#include "bej_decoder_json.hpp" namespace libbej { /** * @brief This structure is used to pass additional data to callback functions. */ struct BejJsonParam { bool* isPrevAnnotated; std::string* output; }; /** * @brief Add a property name to output buffer. * * @param[in] params - a valid BejJsonParam struct. * @param[in] propertyName - a NULL terminated string. */ static void addPropertyNameToOutput(struct BejJsonParam* params, const char* propertyName) { if (propertyName[0] == '\0') { return; } if (!(*params->isPrevAnnotated)) { params->output->push_back('\"'); } params->output->append(propertyName); params->output->append("\":"); } /** * @brief Callback for bejSet start. * * @param[in] propertyName - a NULL terminated string. * @param[in] dataPtr - pointing to a valid BejJsonParam struct. * @return 0 if successful. */ static int callbackSetStart(const char* propertyName, void* dataPtr) { struct BejJsonParam* params = reinterpret_cast(dataPtr); addPropertyNameToOutput(params, propertyName); params->output->push_back('{'); *params->isPrevAnnotated = false; return 0; } /** * @brief Callback for bejSet end. * * @param[in] dataPtr - pointing to a valid BejJsonParam struct. * @return 0 if successful. */ static int callbackSetEnd(void* dataPtr) { struct BejJsonParam* params = reinterpret_cast(dataPtr); params->output->push_back('}'); return 0; } /** * @brief Callback for bejArray start. * * @param[in] propertyName - a NULL terminated string. * @param[in] dataPtr - pointing to a valid BejJsonParam struct. * @return 0 if successful. */ static int callbackArrayStart(const char* propertyName, void* dataPtr) { struct BejJsonParam* params = reinterpret_cast(dataPtr); addPropertyNameToOutput(params, propertyName); params->output->push_back('['); *params->isPrevAnnotated = false; return 0; } /** * @brief Callback for bejArray end. * * @param[in] dataPtr - pointing to a valid BejJsonParam struct. * @return 0 if successful. */ static int callbackArrayEnd(void* dataPtr) { struct BejJsonParam* params = reinterpret_cast(dataPtr); params->output->push_back(']'); return 0; } /** * @brief Callback when an end of a property is detected. * * @param[in] dataPtr - pointing to a valid BejJsonParam struct. * @return 0 if successful. */ static int callbackPropertyEnd(void* dataPtr) { struct BejJsonParam* params = reinterpret_cast(dataPtr); // Not a section ending. So add a comma. params->output->push_back(','); return 0; } /** * @brief Callback for bejNull type. * * @param[in] propertyName - a NULL terminated string. * @param[in] dataPtr - pointing to a valid BejJsonParam struct. * @return 0 if successful. */ static int callbackNull(const char* propertyName, void* dataPtr) { struct BejJsonParam* params = reinterpret_cast(dataPtr); addPropertyNameToOutput(params, propertyName); params->output->append("null"); *params->isPrevAnnotated = false; return 0; } /** * @brief Callback for bejInteger type. * * @param[in] propertyName - a NULL terminated string. * @param[in] value - integer value. * @param[in] dataPtr - pointing to a valid BejJsonParam struct. * @return 0 if successful. */ static int callbackInteger(const char* propertyName, int64_t value, void* dataPtr) { struct BejJsonParam* params = reinterpret_cast(dataPtr); addPropertyNameToOutput(params, propertyName); params->output->append(std::to_string(value)); *params->isPrevAnnotated = false; return 0; } /** * @brief Callback for bejEnum type. * * @param[in] propertyName - a NULL terminated string. * @param[in] value - a NULL terminated string. * @param[in] dataPtr - pointing to a valid BejJsonParam struct. * @return 0 if successful. */ static int callbackEnum(const char* propertyName, const char* value, void* dataPtr) { struct BejJsonParam* params = reinterpret_cast(dataPtr); addPropertyNameToOutput(params, propertyName); params->output->push_back('\"'); params->output->append(value); params->output->push_back('\"'); *params->isPrevAnnotated = false; return 0; } /** * @brief Callback for bejString type. * * @param[in] propertyName - a NULL terminated string. * @param[in] value - a NULL terminated string. * @param[in] dataPtr - pointing to a valid BejJsonParam struct. * @return 0 if successful. */ static int callbackString(const char* propertyName, const char* value, void* dataPtr) { struct BejJsonParam* params = reinterpret_cast(dataPtr); addPropertyNameToOutput(params, propertyName); params->output->push_back('\"'); params->output->append(value); params->output->push_back('\"'); *params->isPrevAnnotated = false; return 0; } /** * @brief Callback for bejReal type. * * @param[in] propertyName - a NULL terminated string. * @param[in] value - pointing to a valid BejReal. * @param[in] dataPtr - pointing to a valid BejJsonParam struct. * @return 0 if successful. */ static int callbackReal(const char* propertyName, const struct BejReal* value, void* dataPtr) { struct BejJsonParam* params = reinterpret_cast(dataPtr); addPropertyNameToOutput(params, propertyName); params->output->append(std::to_string(value->whole)); params->output->push_back('.'); params->output->insert(params->output->cend(), value->zeroCount, '0'); params->output->append(std::to_string(value->fract)); if (value->expLen != 0) { params->output->push_back('e'); params->output->append(std::to_string(value->exp)); } *params->isPrevAnnotated = false; return 0; } /** * @brief Callback for bejBoolean type. * * @param[in] propertyName - a NULL terminated string. * @param[in] value - boolean value. * @param[in] dataPtr - pointing to a valid BejJsonParam struct. * @return 0 if successful. */ static int callbackBool(const char* propertyName, bool value, void* dataPtr) { struct BejJsonParam* params = reinterpret_cast(dataPtr); addPropertyNameToOutput(params, propertyName); params->output->append(value ? "true" : "false"); *params->isPrevAnnotated = false; return 0; } /** * @brief Callback for bejPropertyAnnotation type. * * @param[in] propertyName - a NULL terminated string. * @param[in] dataPtr - pointing to a valid BejJsonParam struct. * @return 0 if successful. */ static int callbackAnnotation(const char* propertyName, void* dataPtr) { struct BejJsonParam* params = reinterpret_cast(dataPtr); params->output->push_back('\"'); params->output->append(propertyName); // bejPropertyAnnotation type has the form "Status@Message.ExtendedInfo". // First the decoder will see "Status" part of the annotated property. This // will be in its own SFLV tuple. The remainder of the property name, // @Message.ExtendedInfo will be contained in the next bej SFLV tuple. // Therefore to add the inverted commas to the complete property name, // Status@Message.ExtendedInfo, we need to know that the previous property // we processed is a start to an annotation property. We can use // isPrevAnnotated to pass this information. // Here we are adding: "propertyName // If isPrevAnnotated is true, next property should add: propertyNameNext" *params->isPrevAnnotated = true; return 0; } /** * @brief Callback for stackEmpty. * * @param[in] dataPtr - pointer to a valid std::vector * @return true if the stack is empty. */ static bool stackEmpty(void* dataPtr) { std::vector* stack = reinterpret_cast*>(dataPtr); return stack->empty(); } /** * @brief Callback for stackPeek. * * @param[in] dataPtr - pointer to a valid std::vector * @return a const reference to the stack top. */ static const struct BejStackProperty* stackPeek(void* dataPtr) { std::vector* stack = reinterpret_cast*>(dataPtr); if (stack->empty()) { return nullptr; } return &(stack->back()); } /** * @brief Callback for stackPop. Remove the top element from the stack. * * @param[in] dataPtr - pointer to a valid std::vector */ static void stackPop(void* dataPtr) { std::vector* stack = reinterpret_cast*>(dataPtr); if (stack->empty()) { return; } stack->pop_back(); } /** * @brief Callback for stackPush. Push a new element to the top of the stack. * * @param[in] property - property to push. * @param[in] dataPtr - pointer to a valid std::vector * @return 0 if successful. */ static int stackPush(const struct BejStackProperty* const property, void* dataPtr) { std::vector* stack = reinterpret_cast*>(dataPtr); stack->push_back(*property); return 0; } int BejDecoderJson::decode(const BejDictionaries& dictionaries, const std::span encodedPldmBlock) { // Clear the previous output if any. output.clear(); // The dictionaries have to be traversed in a depth first manner. This is // using a stack to implement it non-recursively. Going into a set or an // array or a property annotation section means that we have to jump to the // child dictionary offset start point but needs to retrieve the parent // dictionary offset start once all the children are processed. This stack // will hold the parent dictionary offsets and endings for each section. stack.clear(); struct BejStackCallback stackCallback = { .stackEmpty = stackEmpty, .stackPeek = stackPeek, .stackPop = stackPop, .stackPush = stackPush, }; struct BejDecodedCallback decodedCallback = { .callbackSetStart = callbackSetStart, .callbackSetEnd = callbackSetEnd, .callbackArrayStart = callbackArrayStart, .callbackArrayEnd = callbackArrayEnd, .callbackPropertyEnd = callbackPropertyEnd, .callbackNull = callbackNull, .callbackInteger = callbackInteger, .callbackEnum = callbackEnum, .callbackString = callbackString, .callbackReal = callbackReal, .callbackBool = callbackBool, .callbackAnnotation = callbackAnnotation, .callbackReadonlyProperty = nullptr, }; isPrevAnnotated = false; struct BejJsonParam callbackData = { .isPrevAnnotated = &isPrevAnnotated, .output = &output, }; return bejDecodePldmBlock( &dictionaries, encodedPldmBlock.data(), encodedPldmBlock.size_bytes(), &stackCallback, &decodedCallback, (void*)(&callbackData), (void*)(&stack)); } std::string BejDecoderJson::getOutput() { return output; } } // namespace libbej