#include "bej_decoder_core.h" #include "bej_dictionary.h" #include "stdio.h" #include #include #include #include // TODO: Support nested annotations for version 0xF1F1F000 const uint32_t supportedBejVersions[] = {0xF1F0F000}; /** * @brief Call a callback function. If the callback function is NULL, this will * not do anything. If the callback function returns a non-zero value, this will * cause the caller to return with the non-zero status. */ #define RETURN_IF_CALLBACK_IERROR(function, ...) \ do \ { \ if ((function) != NULL) \ { \ int __status = ((function)(__VA_ARGS__)); \ if (__status != 0) \ { \ return __status; \ } \ } \ } while (0) /** * @brief Get the integer value from BEJ byte stream. * * @param[in] bytes - valid pointer to a byte stream in little-endian format. * @param[in] numOfBytes - number of bytes belongs to the value. Maximum value * supported is 8 bytes. * @return signed 64bit representation of the value. */ static int64_t bejGetIntegerValue(const uint8_t* bytes, uint8_t numOfBytes) { if (numOfBytes == 0) { return 0; } uint64_t value = bejGetUnsignedInteger(bytes, numOfBytes); uint8_t bitsInVal = numOfBytes * 8; // Since numOfBytes > 0, bitsInVal is non negative. uint64_t mask = (uint64_t)1 << (uint8_t)(bitsInVal - 1); return (int64_t)((value ^ mask) - mask); } /** * @brief Get offsets of SFLV fields with respect to the enSegment start. * * @param[in] enSegment - a valid pointer to a start of a SFLV bejTuple. * @param[out] offsets - this will hold the local offsets. */ static void bejGetLocalBejSFLVOffsets(const uint8_t* enSegment, struct BejSFLVOffset* offsets) { // Structure of the SFLV. // [Number of bytes need to represent the sequence number] - uint8_t // [SequenceNumber] - multi byte // [Format] - uint8_t // [Number of bytes need to represent the value length] - uint8_t // [Value length] - multi byte // Number of bytes need to represent the sequence number. const uint8_t seqSize = *enSegment; // Start of format. const uint32_t formatOffset = sizeof(uint8_t) + seqSize; // Start of length of the value-length bytes. const uint32_t valueLenNnintOffset = formatOffset + sizeof(uint8_t); // Number of bytes need to represent the value length. const uint8_t valueLengthSize = *(enSegment + valueLenNnintOffset); // Start of the Value. const uint32_t valueOffset = valueLenNnintOffset + sizeof(uint8_t) + valueLengthSize; offsets->formatOffset = formatOffset; offsets->valueLenNnintOffset = valueLenNnintOffset; offsets->valueOffset = valueOffset; } /** * @brief Initialize sflv struct in params struct. * * @param[inout] params - a valid BejHandleTypeFuncParam struct with * params->state.encodedSubStream pointing to the start of the encoded stream * and params->state.encodedStreamOffset pointing to the current bejTuple. */ static void bejInitSFLVStruct(struct BejHandleTypeFuncParam* params) { struct BejSFLVOffset localOffset; // Get offsets of different SFLV fields with respect to start of the encoded // segment. bejGetLocalBejSFLVOffsets(params->state.encodedSubStream, &localOffset); struct BejSFLV* sflv = ¶ms->sflv; const uint32_t valueLength = (uint32_t)(bejGetNnint( params->state.encodedSubStream + localOffset.valueLenNnintOffset)); // Sequence number itself should be 16bits. Using 32bits for // [sequence_number + schema_type]. uint32_t tupleS = (uint32_t)(bejGetNnint(params->state.encodedSubStream)); sflv->tupleS.schema = (uint8_t)(tupleS & DICTIONARY_TYPE_MASK); sflv->tupleS.sequenceNumber = (uint16_t)((tupleS & (~DICTIONARY_TYPE_MASK)) >> DICTIONARY_SEQ_NUM_SHIFT); sflv->format = *(struct BejTupleF*)(params->state.encodedSubStream + localOffset.formatOffset); sflv->valueLength = valueLength; sflv->valueEndOffset = params->state.encodedStreamOffset + localOffset.valueOffset + valueLength; sflv->value = params->state.encodedSubStream + localOffset.valueOffset; } /** * @brief Get the offset to the first tuple of a bejArray or bejSet. * * The first part of the value of a bejArray or a bejSet contains an nnint * providing the number of elements/tuples. Offset is with respect to the start * of the encoded stream. * * @param[in] params - a valid BejHandleTypeFuncParam struct. * @return offset with respect to the start of the encoded stream. */ static uint32_t bejGetFirstTupleOffset(const struct BejHandleTypeFuncParam* params) { struct BejSFLVOffset localOffset; // Get the offset of the value with respect to the current encoded segment // being decoded. bejGetLocalBejSFLVOffsets(params->state.encodedSubStream, &localOffset); return params->state.encodedStreamOffset + localOffset.valueOffset + bejGetNnintSize(params->sflv.value); } /** * @brief Get the correct property and the dictionary it belongs to. * * @param[in] params - a BejHandleTypeFuncParam struct pointing to valid * dictionaries. * @param[in] schemaType - indicate whether to use the annotation dictionary or * the main schema dictionary. * @param[in] sequenceNumber - sequence number to use for property search. Not * using the params->sflv.tupleS.sequenceNumber from the provided params struct. * @param[out] dictionary - if the function is successful, this will point to a * valid dictionary to be used. * @param[out] prop - if the function is successful, this will point to a valid * property in a dictionary. * @return 0 if successful. */ static int bejGetDictionaryAndProperty( const struct BejHandleTypeFuncParam* params, uint8_t schemaType, uint32_t sequenceNumber, const uint8_t** dictionary, const struct BejDictionaryProperty** prop) { uint16_t dictPropOffset; // We need to pick the correct dictionary. if (schemaType == bejPrimary) { *dictionary = params->mainDictionary; dictPropOffset = params->state.mainDictPropOffset; } else if (schemaType == bejAnnotation) { *dictionary = params->annotDictionary; dictPropOffset = params->state.annoDictPropOffset; } else { fprintf(stderr, "Failed to select a dictionary. schema type: %u\n", schemaType); return bejErrorInvalidSchemaType; } int ret = bejDictGetProperty(*dictionary, dictPropOffset, sequenceNumber, prop); if (ret != 0) { fprintf(stderr, "Failed to get dictionary property for offset: %u\n", dictPropOffset); return ret; } return 0; } /** * @brief Find and return the property name of the current encoded segment. If * the params->state.addPropertyName is false, this will return an empty string. * * @param[in] params - a valid populated BejHandleTypeFuncParam. * @return 0 if successful. */ static const char* bejGetPropName(struct BejHandleTypeFuncParam* params) { const uint8_t* dictionary; const struct BejDictionaryProperty* prop; if (!params->state.addPropertyName || (bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber, &dictionary, &prop) != 0)) { return ""; } return bejDictGetPropertyName(dictionary, prop->nameOffset, prop->nameLength); } /** * @brief Look for section endings. * * This figures out whether the current encoded segment marks a section * ending. If so, this function will update the decoder state and pop the stack * used to memorize endings. This function should be called after updating the * encodedStreamOffset to the end of decoded SFLV tuple. * * @param[in] params - a valid BejHandleTypeFuncParam which contains the decoder * state. * @param[in] canBeEmpty - if true, the stack being empty is not an error. If * false, stack cannot be empty. * @return 0 if successful. */ static int bejProcessEnding(struct BejHandleTypeFuncParam* params, bool canBeEmpty) { if (params->stackCallback->stackEmpty(params->stackDataPtr) && !canBeEmpty) { // If bejProcessEnding has been called after adding an appropriate JSON // property, then stack cannot be empty. fprintf(stderr, "Ending stack cannot be empty.\n"); return bejErrorUnknown; } while (!params->stackCallback->stackEmpty(params->stackDataPtr)) { const struct BejStackProperty* const ending = params->stackCallback->stackPeek(params->stackDataPtr); // Check whether the current offset location matches the expected ending // offset. If so, we are done with that section. if (params->state.encodedStreamOffset == ending->streamEndOffset) { // Since we are going out of a section, we need to reset the // dictionary property offsets to this section's parent property // start. params->state.mainDictPropOffset = ending->mainDictPropOffset; params->state.annoDictPropOffset = ending->annoDictPropOffset; params->state.addPropertyName = ending->addPropertyName; if (ending->sectionType == bejSectionSet) { RETURN_IF_CALLBACK_IERROR( params->decodedCallback->callbackSetEnd, params->callbacksDataPtr); } else if (ending->sectionType == bejSectionArray) { RETURN_IF_CALLBACK_IERROR( params->decodedCallback->callbackArrayEnd, params->callbacksDataPtr); } params->stackCallback->stackPop(params->stackDataPtr); } else { RETURN_IF_CALLBACK_IERROR( params->decodedCallback->callbackPropertyEnd, params->callbacksDataPtr); // Do not change the parent dictionary property offset since we are // still inside the same section. return 0; } } return 0; } /** * @brief Check whether the current encoded segment being decoded is an array * element. * * @param[in] params - a valid BejHandleTypeFuncParam struct. * @return true if the encoded segment is an array element. Else false. */ static bool bejIsArrayElement(const struct BejHandleTypeFuncParam* params) { // If the encoded segment enters an array section, we are adding a // BejSectionArray to the stack. Therefore if the stack is empty, encoded // segment cannot be an array element. if (params->stackCallback->stackEmpty(params->stackDataPtr)) { return false; } const struct BejStackProperty* const ending = params->stackCallback->stackPeek(params->stackDataPtr); // If the stack top element holds a BejSectionArray, encoded segment is // an array element. return ending->sectionType == bejSectionArray; } /** * @brief Decodes a BejSet type SFLV BEJ tuple. * * @param[in] params - a valid BejHandleTypeFuncParam struct. * @return 0 if successful. */ static int bejHandleBejSet(struct BejHandleTypeFuncParam* params) { uint16_t sequenceNumber = params->sflv.tupleS.sequenceNumber; // Check whether this BejSet is an array element or not. if (bejIsArrayElement(params)) { // Dictionary only contains an entry for element 0. sequenceNumber = 0; } const uint8_t* dictionary; const struct BejDictionaryProperty* prop; RETURN_IF_IERROR( bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema, sequenceNumber, &dictionary, &prop)); const char* propName = ""; if (params->state.addPropertyName) { propName = bejDictGetPropertyName(dictionary, prop->nameOffset, prop->nameLength); } RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackSetStart, propName, params->callbacksDataPtr); // Move the offset to the next SFLV tuple (or end). Make sure that this is // called before calling bejProcessEnding. params->state.encodedStreamOffset = bejGetFirstTupleOffset(params); uint64_t elements = bejGetNnint(params->sflv.value); // If its an empty set, we are done here. if (elements == 0) { RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackSetEnd, params->callbacksDataPtr); // Since this is an ending of a property (empty array), we should call // bejProcessEnding. Unless the whole JSON object is an empty set (which // shouldn't be the case), stack cannot be empty. bejProcessEnding(params, /*canBeEmpty=*/false); return 0; } // Update the states for the next encoding segment. struct BejStackProperty newEnding = { .sectionType = bejSectionSet, .addPropertyName = params->state.addPropertyName, .mainDictPropOffset = params->state.mainDictPropOffset, .annoDictPropOffset = params->state.annoDictPropOffset, .streamEndOffset = params->sflv.valueEndOffset, }; RETURN_IF_IERROR( params->stackCallback->stackPush(&newEnding, params->stackDataPtr)); params->state.addPropertyName = true; if (params->sflv.tupleS.schema == bejAnnotation) { // Since this set is an annotated type, we need to advance the // annotation dictionary for decoding the next segment. params->state.annoDictPropOffset = prop->childPointerOffset; } else { params->state.mainDictPropOffset = prop->childPointerOffset; } return 0; } /** * @brief Decodes a BejArray type SFLV BEJ tuple. * * @param[in] params - a valid BejHandleTypeFuncParam struct. * @return 0 if successful. */ static int bejHandleBejArray(struct BejHandleTypeFuncParam* params) { const uint8_t* dictionary; const struct BejDictionaryProperty* prop; RETURN_IF_IERROR(bejGetDictionaryAndProperty( params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber, &dictionary, &prop)); const char* propName = ""; if (params->state.addPropertyName) { propName = bejDictGetPropertyName(dictionary, prop->nameOffset, prop->nameLength); } RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayStart, propName, params->callbacksDataPtr); // Move the offset to the next SFLV tuple (or end). Make sure that this is // called before calling bejProcessEnding. params->state.encodedStreamOffset = bejGetFirstTupleOffset(params); uint64_t elements = bejGetNnint(params->sflv.value); // If its an empty array, we are done here. if (elements == 0) { RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayEnd, params->callbacksDataPtr); // Since this is an ending of a property (empty array), we should call // bejProcessEnding. Stack cannot be empty since there should be at // least 1 parent in the stack. bejProcessEnding(params, /*canBeEmpty=*/false); return 0; } // Update the state for next segment decoding. struct BejStackProperty newEnding = { .sectionType = bejSectionArray, .addPropertyName = params->state.addPropertyName, .mainDictPropOffset = params->state.mainDictPropOffset, .annoDictPropOffset = params->state.annoDictPropOffset, .streamEndOffset = params->sflv.valueEndOffset, }; RETURN_IF_IERROR( params->stackCallback->stackPush(&newEnding, params->stackDataPtr)); // We do not add property names for array elements. params->state.addPropertyName = false; if (params->sflv.tupleS.schema == bejAnnotation) { // Since this array is an annotated type, we need to advance the // annotation dictionary for decoding the next segment. params->state.annoDictPropOffset = prop->childPointerOffset; } else { params->state.mainDictPropOffset = prop->childPointerOffset; } return 0; } /** * @brief Decodes a BejNull type SFLV BEJ tuple. * * @param[in] params - a valid BejHandleTypeFuncParam struct. * @return 0 if successful. */ static int bejHandleBejNull(struct BejHandleTypeFuncParam* params) { const char* propName = bejGetPropName(params); RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, propName, params->callbacksDataPtr); params->state.encodedStreamOffset = params->sflv.valueEndOffset; return bejProcessEnding(params, /*canBeEmpty=*/false); } /** * @brief Decodes a BejInteger type SFLV BEJ tuple. * * @param[in] params - a valid BejHandleTypeFuncParam struct. * @return 0 if successful. */ static int bejHandleBejInteger(struct BejHandleTypeFuncParam* params) { const char* propName = bejGetPropName(params); if (params->sflv.valueLength == 0) { RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, propName, params->callbacksDataPtr); } else { RETURN_IF_CALLBACK_IERROR( params->decodedCallback->callbackInteger, propName, bejGetIntegerValue(params->sflv.value, params->sflv.valueLength), params->callbacksDataPtr); } params->state.encodedStreamOffset = params->sflv.valueEndOffset; return bejProcessEnding(params, /*canBeEmpty=*/false); } /** * @brief Decodes a BejEnum type SFLV BEJ tuple. * * @param[in] params - a valid BejHandleTypeFuncParam struct. * @return 0 if successful. */ static int bejHandleBejEnum(struct BejHandleTypeFuncParam* params) { uint16_t sequenceNumber = params->sflv.tupleS.sequenceNumber; if (bejIsArrayElement(params)) { sequenceNumber = 0; } const uint8_t* dictionary; const struct BejDictionaryProperty* prop; RETURN_IF_IERROR( bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema, sequenceNumber, &dictionary, &prop)); const char* propName = ""; if (params->state.addPropertyName) { propName = bejDictGetPropertyName(dictionary, prop->nameOffset, prop->nameLength); } if (params->sflv.valueLength == 0) { RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, propName, params->callbacksDataPtr); } else { // Get the string for enum value. uint16_t enumValueSequenceN = (uint16_t)(bejGetNnint(params->sflv.value)); const struct BejDictionaryProperty* enumValueProp; RETURN_IF_IERROR( bejDictGetProperty(dictionary, prop->childPointerOffset, enumValueSequenceN, &enumValueProp)); const char* enumValueName = bejDictGetPropertyName( dictionary, enumValueProp->nameOffset, enumValueProp->nameLength); RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackEnum, propName, enumValueName, params->callbacksDataPtr); } // Update the offset to point to the next possible SFLV tuple. params->state.encodedStreamOffset = params->sflv.valueEndOffset; return bejProcessEnding(params, /*canBeEmpty=*/false); } /** * @brief Decodes a BejString type SFLV BEJ tuple. * * @param[in] params - a valid BejHandleTypeFuncParam struct. * @return 0 if successful. */ static int bejHandleBejString(struct BejHandleTypeFuncParam* params) { // TODO: Handle deferred bindings. const char* propName = bejGetPropName(params); if (params->sflv.valueLength == 0) { RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, propName, params->callbacksDataPtr); } else { RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackString, propName, (const char*)(params->sflv.value), params->callbacksDataPtr); } params->state.encodedStreamOffset = params->sflv.valueEndOffset; return bejProcessEnding(params, /*canBeEmpty=*/false); } /** * @brief Decodes a BejReal type SFLV BEJ tuple. * * @param[in] params - a valid BejHandleTypeFuncParam struct. * @return 0 if successful. */ static int bejHandleBejReal(struct BejHandleTypeFuncParam* params) { const char* propName = bejGetPropName(params); if (params->sflv.valueLength == 0) { RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, propName, params->callbacksDataPtr); } else { // Real value has the following format. // nnint - Length of whole // bejInteger - whole (includes sign for the overall real number) // nnint - Leading zero count for fract // nnint - fract // nnint - Length of exp // bejInteger - exp (includes sign for the exponent) uint8_t wholeByteLen = (uint8_t)bejGetNnint(params->sflv.value); const uint8_t* wholeBejInt = params->sflv.value + bejGetNnintSize(params->sflv.value); const uint8_t* fractZeroCountNnint = wholeBejInt + wholeByteLen; const uint8_t* fractNnint = fractZeroCountNnint + bejGetNnintSize(fractZeroCountNnint); const uint8_t* lenExpNnint = fractNnint + bejGetNnintSize(fractNnint); const uint8_t* expBejInt = lenExpNnint + bejGetNnintSize(lenExpNnint); struct BejReal realValue; realValue.whole = bejGetIntegerValue(wholeBejInt, wholeByteLen); realValue.zeroCount = bejGetNnint(fractZeroCountNnint); realValue.fract = bejGetNnint(fractNnint); realValue.expLen = (uint8_t)bejGetNnint(lenExpNnint); if (realValue.expLen != 0) { realValue.exp = bejGetIntegerValue( expBejInt, (uint8_t)bejGetNnint(lenExpNnint)); } RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackReal, propName, &realValue, params->callbacksDataPtr); } params->state.encodedStreamOffset = params->sflv.valueEndOffset; return bejProcessEnding(params, /*canBeEmpty=*/false); } /** * @brief Decodes a BejBoolean type SFLV BEJ tuple. * * @param[in] params - a valid BejHandleTypeFuncParam struct. * @return 0 if successful. */ static int bejHandleBejBoolean(struct BejHandleTypeFuncParam* params) { const char* propName = bejGetPropName(params); if (params->sflv.valueLength == 0) { RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, propName, params->callbacksDataPtr); } else { RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackBool, propName, *(params->sflv.value) > 0, params->callbacksDataPtr); } params->state.encodedStreamOffset = params->sflv.valueEndOffset; return bejProcessEnding(params, /*canBeEmpty=*/false); } /** * @brief Decodes a BejPropertyAnnotation type SFLV BEJ tuple. * * @param[in] params - a valid BejHandleTypeFuncParam struct. * @return 0 if successful. */ static int bejHandleBejPropertyAnnotation(struct BejHandleTypeFuncParam* params) { // TODO: Handle colon-delimited string values. // Property annotation has the form OuterProperty@Annotation. First // processing the outer property name. const uint8_t* outerDictionary; const struct BejDictionaryProperty* outerProp; RETURN_IF_IERROR(bejGetDictionaryAndProperty( params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber, &outerDictionary, &outerProp)); const char* propName = bejDictGetPropertyName( outerDictionary, outerProp->nameOffset, outerProp->nameLength); RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackAnnotation, propName, params->callbacksDataPtr); // Mark the ending of the property annotation. struct BejStackProperty newEnding = { .sectionType = bejSectionNoType, .addPropertyName = params->state.addPropertyName, .mainDictPropOffset = params->state.mainDictPropOffset, .annoDictPropOffset = params->state.annoDictPropOffset, .streamEndOffset = params->sflv.valueEndOffset, }; // Update the states for the next encoding segment. RETURN_IF_IERROR( params->stackCallback->stackPush(&newEnding, params->stackDataPtr)); params->state.addPropertyName = true; // We might have to change this for nested annotations. params->state.mainDictPropOffset = outerProp->childPointerOffset; // Point to the start of the value for next decoding. params->state.encodedStreamOffset = params->sflv.valueEndOffset - params->sflv.valueLength; return 0; } /** * @brief Decodes an encoded bej stream. * * @param[in] schemaDictionary - main schema dictionary to use. * @param[in] annotationDictionary - annotation dictionary * @param[in] enStream - encoded stream without the PLDM header. * @param[in] streamLen - length of the enStream. * @param[in] stackCallback - callbacks for stack handlers. * @param[in] decodedCallback - callbacks for extracting decoded properties. * @param[in] callbacksDataPtr - data pointer to pass to decoded callbacks. This * can be used pass additional data. * @param[in] stackDataPtr - data pointer to pass to stack callbacks. This can * be used pass additional data. * * @return 0 if successful. */ static int bejDecode(const uint8_t* schemaDictionary, const uint8_t* annotationDictionary, const uint8_t* enStream, uint32_t streamLen, const struct BejStackCallback* stackCallback, const struct BejDecodedCallback* decodedCallback, void* callbacksDataPtr, void* stackDataPtr) { struct BejHandleTypeFuncParam params = { .state = { // We only add names of set properties. We don't use names for // array // properties. Here we are omitting the name of the root set. .addPropertyName = false, // At start, parent property from the main dictionary is the // first property. .mainDictPropOffset = bejDictGetPropertyHeadOffset(), .annoDictPropOffset = bejDictGetFirstAnnotatedPropertyOffset(), // Current location of the encoded segment we are processing. .encodedStreamOffset = 0, .encodedSubStream = enStream, }, .mainDictionary = schemaDictionary, .annotDictionary = annotationDictionary, .decodedCallback = decodedCallback, .stackCallback = stackCallback, .callbacksDataPtr = callbacksDataPtr, .stackDataPtr = stackDataPtr, }; while (params.state.encodedStreamOffset < streamLen) { // Go to the next encoded segment in the encoded stream. params.state.encodedSubStream = enStream + params.state.encodedStreamOffset; bejInitSFLVStruct(¶ms); if (params.sflv.format.readOnlyProperty) { RETURN_IF_CALLBACK_IERROR( params.decodedCallback->callbackReadonlyProperty, params.sflv.tupleS.sequenceNumber, params.callbacksDataPtr); } // TODO: Handle nullable property types. These are indicated by // params.sflv.format.nullableProperty switch (params.sflv.format.principalDataType) { case bejSet: RETURN_IF_IERROR(bejHandleBejSet(¶ms)); break; case bejArray: RETURN_IF_IERROR(bejHandleBejArray(¶ms)); break; case bejNull: RETURN_IF_IERROR(bejHandleBejNull(¶ms)); break; case bejInteger: RETURN_IF_IERROR(bejHandleBejInteger(¶ms)); break; case bejEnum: RETURN_IF_IERROR(bejHandleBejEnum(¶ms)); break; case bejString: RETURN_IF_IERROR(bejHandleBejString(¶ms)); break; case bejReal: RETURN_IF_IERROR(bejHandleBejReal(¶ms)); break; case bejBoolean: RETURN_IF_IERROR(bejHandleBejBoolean(¶ms)); break; case bejBytestring: // TODO: Add support for BejBytestring decoding. fprintf(stderr, "No BejBytestring support\n"); params.state.encodedStreamOffset = params.sflv.valueEndOffset; break; case bejChoice: // TODO: Add support for BejChoice decoding. fprintf(stderr, "No BejChoice support\n"); params.state.encodedStreamOffset = params.sflv.valueEndOffset; break; case bejPropertyAnnotation: RETURN_IF_IERROR(bejHandleBejPropertyAnnotation(¶ms)); break; case bejResourceLink: // TODO: Add support for BejResourceLink decoding. fprintf(stderr, "No BejResourceLink support\n"); params.state.encodedStreamOffset = params.sflv.valueEndOffset; break; case bejResourceLinkExpansion: // TODO: Add support for BejResourceLinkExpansion decoding. fprintf(stderr, "No BejResourceLinkExpansion support\n"); params.state.encodedStreamOffset = params.sflv.valueEndOffset; break; default: break; } } RETURN_IF_IERROR(bejProcessEnding(¶ms, /*canBeEmpty=*/true)); if (!params.stackCallback->stackEmpty(params.stackDataPtr)) { fprintf(stderr, "Ending stack should be empty but its not. Something " "must have gone wrong with the encoding\n"); return bejErrorUnknown; } return 0; } /** * @brief Check if a bej version is supported by this decoder * * @param[in] bejVersion - the bej version in the received encoded stream * @return true if supported. */ static bool bejIsSupported(uint32_t bejVersion) { for (uint32_t i = 0; i < sizeof(supportedBejVersions) / sizeof(uint32_t); ++i) { if (bejVersion == supportedBejVersions[i]) { return true; } } return false; } int bejDecodePldmBlock(const struct BejDictionaries* dictionaries, const uint8_t* encodedPldmBlock, uint32_t blockLength, const struct BejStackCallback* stackCallback, const struct BejDecodedCallback* decodedCallback, void* callbacksDataPtr, void* stackDataPtr) { NULL_CHECK(dictionaries, "dictionaries"); NULL_CHECK(dictionaries->schemaDictionary, "schemaDictionary"); NULL_CHECK(dictionaries->annotationDictionary, "annotationDictionary"); NULL_CHECK(encodedPldmBlock, "encodedPldmBlock"); NULL_CHECK(stackCallback, "stackCallback"); NULL_CHECK(stackCallback->stackEmpty, "stackEmpty"); NULL_CHECK(stackCallback->stackPeek, "stackPeek"); NULL_CHECK(stackCallback->stackPop, "stackPop"); NULL_CHECK(stackCallback->stackPush, "stackPush"); NULL_CHECK(decodedCallback, "decodedCallback"); uint32_t pldmHeaderSize = sizeof(struct BejPldmBlockHeader); if (blockLength < pldmHeaderSize) { fprintf(stderr, "Invalid pldm block size: %u\n", blockLength); return bejErrorInvalidSize; } const struct BejPldmBlockHeader* pldmHeader = (const struct BejPldmBlockHeader*)encodedPldmBlock; if (!bejIsSupported(pldmHeader->bejVersion)) { fprintf(stderr, "Bej decoder doesn't support the bej version: %u\n", pldmHeader->bejVersion); return bejErrorNotSuppoted; } if (pldmHeader->schemaClass == bejAnnotationSchemaClass) { fprintf(stderr, "Encoder schema class cannot be BejAnnotationSchemaClass\n"); return bejErrorNotSuppoted; } // TODO: Add support for CollectionMemberType schema class. if (pldmHeader->schemaClass == bejCollectionMemberTypeSchemaClass) { fprintf(stderr, "Decoder doesn't support " "bejCollectionMemberTypeSchemaClass yet.\n"); return bejErrorNotSuppoted; } // TODO: Add support for Error schema class. if (pldmHeader->schemaClass == bejErrorSchemaClass) { fprintf(stderr, "Decoder doesn't support BejErrorSchemaClass yet.\n"); return bejErrorNotSuppoted; } // Skip the PLDM header. const uint8_t* enStream = encodedPldmBlock + pldmHeaderSize; uint32_t streamLen = blockLength - pldmHeaderSize; return bejDecode(dictionaries->schemaDictionary, dictionaries->annotationDictionary, enStream, streamLen, stackCallback, decodedCallback, callbacksDataPtr, stackDataPtr); }