xref: /openbmc/libbej/src/bej_decoder_core.c (revision 045a2d6561c91b8521b5e08d6b18df4d10a1708b)
164cb1973Skasunath #include "bej_decoder_core.h"
264cb1973Skasunath 
364cb1973Skasunath #include "bej_dictionary.h"
464cb1973Skasunath #include "stdio.h"
564cb1973Skasunath 
664cb1973Skasunath #include <inttypes.h>
764cb1973Skasunath #include <stdbool.h>
864cb1973Skasunath #include <stdlib.h>
964cb1973Skasunath #include <string.h>
1064cb1973Skasunath 
1164cb1973Skasunath // TODO: Support nested annotations for version 0xF1F1F000
1264cb1973Skasunath const uint32_t supportedBejVersions[] = {0xF1F0F000};
1364cb1973Skasunath 
1464cb1973Skasunath /**
1564cb1973Skasunath  * @brief Call a callback function. If the callback function is NULL, this will
1664cb1973Skasunath  * not do anything. If the callback function returns a non-zero value, this will
1764cb1973Skasunath  * cause the caller to return with the non-zero status.
1864cb1973Skasunath  */
1964cb1973Skasunath #define RETURN_IF_CALLBACK_IERROR(function, ...)                               \
2064cb1973Skasunath     do                                                                         \
2164cb1973Skasunath     {                                                                          \
2264cb1973Skasunath         if ((function) != NULL)                                                \
2364cb1973Skasunath         {                                                                      \
2464cb1973Skasunath             int __status = ((function)(__VA_ARGS__));                          \
2564cb1973Skasunath             if (__status != 0)                                                 \
2664cb1973Skasunath             {                                                                  \
2764cb1973Skasunath                 return __status;                                               \
2864cb1973Skasunath             }                                                                  \
2964cb1973Skasunath         }                                                                      \
3064cb1973Skasunath     } while (0)
3164cb1973Skasunath 
3264cb1973Skasunath /**
3334a096dbSkasunath  * @brief Get the integer value from BEJ byte stream.
3434a096dbSkasunath  *
3534a096dbSkasunath  * @param[in] bytes - valid pointer to a byte stream in little-endian format.
3634a096dbSkasunath  * @param[in] numOfBytes - number of bytes belongs to the value. Maximum value
3734a096dbSkasunath  * supported is 8 bytes.
3834a096dbSkasunath  * @return signed 64bit representation of the value.
3934a096dbSkasunath  */
bejGetIntegerValue(const uint8_t * bytes,uint8_t numOfBytes)4034a096dbSkasunath static int64_t bejGetIntegerValue(const uint8_t* bytes, uint8_t numOfBytes)
4134a096dbSkasunath {
4234a096dbSkasunath     if (numOfBytes == 0)
4334a096dbSkasunath     {
4434a096dbSkasunath         return 0;
4534a096dbSkasunath     }
460aa36d82Skasunath     uint64_t value = bejGetUnsignedInteger(bytes, numOfBytes);
4734a096dbSkasunath     uint8_t bitsInVal = numOfBytes * 8;
4834a096dbSkasunath     // Since numOfBytes > 0, bitsInVal is non negative.
4934a096dbSkasunath     uint64_t mask = (uint64_t)1 << (uint8_t)(bitsInVal - 1);
507daa5f8eSkasunath     return (int64_t)((value ^ mask) - mask);
5134a096dbSkasunath }
5234a096dbSkasunath 
5334a096dbSkasunath /**
5464cb1973Skasunath  * @brief Get offsets of SFLV fields with respect to the enSegment start.
5564cb1973Skasunath  *
5664cb1973Skasunath  * @param[in] enSegment - a valid pointer to a start of a SFLV bejTuple.
5764cb1973Skasunath  * @param[out] offsets - this will hold the local offsets.
5864cb1973Skasunath  */
bejGetLocalBejSFLVOffsets(const uint8_t * enSegment,struct BejSFLVOffset * offsets)5964cb1973Skasunath static void bejGetLocalBejSFLVOffsets(const uint8_t* enSegment,
6064cb1973Skasunath                                       struct BejSFLVOffset* offsets)
6164cb1973Skasunath {
6264cb1973Skasunath     // Structure of the SFLV.
6364cb1973Skasunath     //   [Number of bytes need to represent the sequence number] - uint8_t
6464cb1973Skasunath     //   [SequenceNumber] - multi byte
6564cb1973Skasunath     //   [Format] - uint8_t
6664cb1973Skasunath     //   [Number of bytes need to represent the value length] - uint8_t
6764cb1973Skasunath     //   [Value length] - multi byte
6864cb1973Skasunath 
6964cb1973Skasunath     // Number of bytes need to represent the sequence number.
7064cb1973Skasunath     const uint8_t seqSize = *enSegment;
7164cb1973Skasunath     // Start of format.
7264cb1973Skasunath     const uint32_t formatOffset = sizeof(uint8_t) + seqSize;
7364cb1973Skasunath     // Start of length of the value-length bytes.
7464cb1973Skasunath     const uint32_t valueLenNnintOffset = formatOffset + sizeof(uint8_t);
7564cb1973Skasunath     // Number of bytes need to represent the value length.
7664cb1973Skasunath     const uint8_t valueLengthSize = *(enSegment + valueLenNnintOffset);
7764cb1973Skasunath     // Start of the Value.
78be27f2e9SPatrick Williams     const uint32_t valueOffset =
79be27f2e9SPatrick Williams         valueLenNnintOffset + sizeof(uint8_t) + valueLengthSize;
8064cb1973Skasunath 
8164cb1973Skasunath     offsets->formatOffset = formatOffset;
8264cb1973Skasunath     offsets->valueLenNnintOffset = valueLenNnintOffset;
8364cb1973Skasunath     offsets->valueOffset = valueOffset;
8464cb1973Skasunath }
8564cb1973Skasunath 
8664cb1973Skasunath /**
8764cb1973Skasunath  * @brief Initialize sflv struct in params struct.
8864cb1973Skasunath  *
8964cb1973Skasunath  * @param[inout] params - a valid BejHandleTypeFuncParam struct with
9064cb1973Skasunath  * params->state.encodedSubStream pointing to the start of the encoded stream
9164cb1973Skasunath  * and params->state.encodedStreamOffset pointing to the current bejTuple.
9264cb1973Skasunath  */
bejInitSFLVStruct(struct BejHandleTypeFuncParam * params)9364cb1973Skasunath static void bejInitSFLVStruct(struct BejHandleTypeFuncParam* params)
9464cb1973Skasunath {
9564cb1973Skasunath     struct BejSFLVOffset localOffset;
9664cb1973Skasunath     // Get offsets of different SFLV fields with respect to start of the encoded
9764cb1973Skasunath     // segment.
9864cb1973Skasunath     bejGetLocalBejSFLVOffsets(params->state.encodedSubStream, &localOffset);
9964cb1973Skasunath     struct BejSFLV* sflv = &params->sflv;
1000aa36d82Skasunath     const uint32_t valueLength = (uint32_t)(bejGetNnint(
10164cb1973Skasunath         params->state.encodedSubStream + localOffset.valueLenNnintOffset));
10264cb1973Skasunath     // Sequence number itself should be 16bits. Using 32bits for
10364cb1973Skasunath     // [sequence_number + schema_type].
1040aa36d82Skasunath     uint32_t tupleS = (uint32_t)(bejGetNnint(params->state.encodedSubStream));
10564cb1973Skasunath     sflv->tupleS.schema = (uint8_t)(tupleS & DICTIONARY_TYPE_MASK);
10664cb1973Skasunath     sflv->tupleS.sequenceNumber =
10764cb1973Skasunath         (uint16_t)((tupleS & (~DICTIONARY_TYPE_MASK)) >>
10864cb1973Skasunath                    DICTIONARY_SEQ_NUM_SHIFT);
10964cb1973Skasunath     sflv->format = *(struct BejTupleF*)(params->state.encodedSubStream +
11064cb1973Skasunath                                         localOffset.formatOffset);
11164cb1973Skasunath     sflv->valueLength = valueLength;
11264cb1973Skasunath     sflv->valueEndOffset = params->state.encodedStreamOffset +
11364cb1973Skasunath                            localOffset.valueOffset + valueLength;
11464cb1973Skasunath     sflv->value = params->state.encodedSubStream + localOffset.valueOffset;
11564cb1973Skasunath }
11664cb1973Skasunath 
11764cb1973Skasunath /**
11834a096dbSkasunath  * @brief Get the offset to the first tuple of a bejArray or bejSet.
11934a096dbSkasunath  *
12034a096dbSkasunath  * The first part of the value of a bejArray or a bejSet contains an nnint
12134a096dbSkasunath  * providing the number of elements/tuples. Offset is with respect to the start
12234a096dbSkasunath  * of the encoded stream.
12334a096dbSkasunath  *
12434a096dbSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
12534a096dbSkasunath  * @return offset with respect to the start of the encoded stream.
12634a096dbSkasunath  */
bejGetFirstTupleOffset(const struct BejHandleTypeFuncParam * params)127*045a2d65SPatrick Williams static uint32_t bejGetFirstTupleOffset(
128*045a2d65SPatrick Williams     const struct BejHandleTypeFuncParam* params)
12934a096dbSkasunath {
13034a096dbSkasunath     struct BejSFLVOffset localOffset;
13134a096dbSkasunath     // Get the offset of the value with respect to the current encoded segment
13234a096dbSkasunath     // being decoded.
13334a096dbSkasunath     bejGetLocalBejSFLVOffsets(params->state.encodedSubStream, &localOffset);
13434a096dbSkasunath     return params->state.encodedStreamOffset + localOffset.valueOffset +
1350aa36d82Skasunath            bejGetNnintSize(params->sflv.value);
13634a096dbSkasunath }
13734a096dbSkasunath 
13834a096dbSkasunath /**
13934a096dbSkasunath  * @brief Get the correct property and the dictionary it belongs to.
14034a096dbSkasunath  *
14134a096dbSkasunath  * @param[in] params - a BejHandleTypeFuncParam struct pointing to valid
14234a096dbSkasunath  * dictionaries.
14334a096dbSkasunath  * @param[in] schemaType - indicate whether to use the annotation dictionary or
14434a096dbSkasunath  * the main schema dictionary.
14534a096dbSkasunath  * @param[in] sequenceNumber - sequence number to use for property search. Not
14634a096dbSkasunath  * using the params->sflv.tupleS.sequenceNumber from the provided params struct.
14734a096dbSkasunath  * @param[out] dictionary - if the function is successful, this will point to a
14834a096dbSkasunath  * valid dictionary to be used.
14934a096dbSkasunath  * @param[out] prop - if the function is successful, this will point to a valid
15034a096dbSkasunath  * property in a dictionary.
15134a096dbSkasunath  * @return 0 if successful.
15234a096dbSkasunath  */
bejGetDictionaryAndProperty(const struct BejHandleTypeFuncParam * params,uint8_t schemaType,uint32_t sequenceNumber,const uint8_t ** dictionary,const struct BejDictionaryProperty ** prop)153be27f2e9SPatrick Williams static int bejGetDictionaryAndProperty(
154be27f2e9SPatrick Williams     const struct BejHandleTypeFuncParam* params, uint8_t schemaType,
155be27f2e9SPatrick Williams     uint32_t sequenceNumber, const uint8_t** dictionary,
15634a096dbSkasunath     const struct BejDictionaryProperty** prop)
15734a096dbSkasunath {
15834a096dbSkasunath     uint16_t dictPropOffset;
15934a096dbSkasunath     // We need to pick the correct dictionary.
16034a096dbSkasunath     if (schemaType == bejPrimary)
16134a096dbSkasunath     {
16234a096dbSkasunath         *dictionary = params->mainDictionary;
16334a096dbSkasunath         dictPropOffset = params->state.mainDictPropOffset;
16434a096dbSkasunath     }
16534a096dbSkasunath     else if (schemaType == bejAnnotation)
16634a096dbSkasunath     {
16734a096dbSkasunath         *dictionary = params->annotDictionary;
16834a096dbSkasunath         dictPropOffset = params->state.annoDictPropOffset;
16934a096dbSkasunath     }
17034a096dbSkasunath     else
17134a096dbSkasunath     {
17234a096dbSkasunath         fprintf(stderr, "Failed to select a dictionary. schema type: %u\n",
17334a096dbSkasunath                 schemaType);
17434a096dbSkasunath         return bejErrorInvalidSchemaType;
17534a096dbSkasunath     }
17634a096dbSkasunath 
177be27f2e9SPatrick Williams     int ret =
178be27f2e9SPatrick Williams         bejDictGetProperty(*dictionary, dictPropOffset, sequenceNumber, prop);
17934a096dbSkasunath     if (ret != 0)
18034a096dbSkasunath     {
18134a096dbSkasunath         fprintf(stderr, "Failed to get dictionary property for offset: %u\n",
18234a096dbSkasunath                 dictPropOffset);
18334a096dbSkasunath         return ret;
18434a096dbSkasunath     }
18534a096dbSkasunath     return 0;
18634a096dbSkasunath }
18734a096dbSkasunath 
18834a096dbSkasunath /**
189e2260cd6Skasunath  * @brief Find and return the property name of the current encoded segment. If
190e2260cd6Skasunath  * the params->state.addPropertyName is false, this will return an empty string.
19134a096dbSkasunath  *
19234a096dbSkasunath  * @param[in] params - a valid populated BejHandleTypeFuncParam.
19334a096dbSkasunath  * @return 0 if successful.
19434a096dbSkasunath  */
bejGetPropName(struct BejHandleTypeFuncParam * params)195e2260cd6Skasunath static const char* bejGetPropName(struct BejHandleTypeFuncParam* params)
19634a096dbSkasunath {
19734a096dbSkasunath     const uint8_t* dictionary;
19834a096dbSkasunath     const struct BejDictionaryProperty* prop;
199e2260cd6Skasunath     if (!params->state.addPropertyName ||
200e2260cd6Skasunath         (bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
20134a096dbSkasunath                                      params->sflv.tupleS.sequenceNumber,
202e2260cd6Skasunath                                      &dictionary, &prop) != 0))
20334a096dbSkasunath     {
20434a096dbSkasunath         return "";
20534a096dbSkasunath     }
20634a096dbSkasunath     return bejDictGetPropertyName(dictionary, prop->nameOffset,
20734a096dbSkasunath                                   prop->nameLength);
20834a096dbSkasunath }
20934a096dbSkasunath 
21034a096dbSkasunath /**
21134a096dbSkasunath  * @brief Look for section endings.
21234a096dbSkasunath  *
21334a096dbSkasunath  * This figures out whether the current encoded segment marks a section
21434a096dbSkasunath  * ending. If so, this function will update the decoder state and pop the stack
21534a096dbSkasunath  * used to memorize endings. This function should be called after updating the
21634a096dbSkasunath  * encodedStreamOffset to the end of decoded SFLV tuple.
21734a096dbSkasunath  *
21834a096dbSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam which contains the decoder
21934a096dbSkasunath  * state.
22034a096dbSkasunath  * @param[in] canBeEmpty - if true, the stack being empty is not an error. If
22134a096dbSkasunath  * false, stack cannot be empty.
22234a096dbSkasunath  * @return 0 if successful.
22334a096dbSkasunath  */
bejProcessEnding(struct BejHandleTypeFuncParam * params,bool canBeEmpty)22434a096dbSkasunath static int bejProcessEnding(struct BejHandleTypeFuncParam* params,
22534a096dbSkasunath                             bool canBeEmpty)
22634a096dbSkasunath {
22734a096dbSkasunath     if (params->stackCallback->stackEmpty(params->stackDataPtr) && !canBeEmpty)
22834a096dbSkasunath     {
22934a096dbSkasunath         // If bejProcessEnding has been called after adding an appropriate JSON
23034a096dbSkasunath         // property, then stack cannot be empty.
23134a096dbSkasunath         fprintf(stderr, "Ending stack cannot be empty.\n");
23234a096dbSkasunath         return bejErrorUnknown;
23334a096dbSkasunath     }
23434a096dbSkasunath 
23534a096dbSkasunath     while (!params->stackCallback->stackEmpty(params->stackDataPtr))
23634a096dbSkasunath     {
23734a096dbSkasunath         const struct BejStackProperty* const ending =
23834a096dbSkasunath             params->stackCallback->stackPeek(params->stackDataPtr);
23934a096dbSkasunath         // Check whether the current offset location matches the expected ending
24034a096dbSkasunath         // offset. If so, we are done with that section.
24134a096dbSkasunath         if (params->state.encodedStreamOffset == ending->streamEndOffset)
24234a096dbSkasunath         {
24334a096dbSkasunath             // Since we are going out of a section, we need to reset the
24434a096dbSkasunath             // dictionary property offsets to this section's parent property
24534a096dbSkasunath             // start.
24634a096dbSkasunath             params->state.mainDictPropOffset = ending->mainDictPropOffset;
24734a096dbSkasunath             params->state.annoDictPropOffset = ending->annoDictPropOffset;
24834a096dbSkasunath             params->state.addPropertyName = ending->addPropertyName;
24934a096dbSkasunath 
25034a096dbSkasunath             if (ending->sectionType == bejSectionSet)
25134a096dbSkasunath             {
25234a096dbSkasunath                 RETURN_IF_CALLBACK_IERROR(
25334a096dbSkasunath                     params->decodedCallback->callbackSetEnd,
25434a096dbSkasunath                     params->callbacksDataPtr);
25534a096dbSkasunath             }
25634a096dbSkasunath             else if (ending->sectionType == bejSectionArray)
25734a096dbSkasunath             {
25834a096dbSkasunath                 RETURN_IF_CALLBACK_IERROR(
25934a096dbSkasunath                     params->decodedCallback->callbackArrayEnd,
26034a096dbSkasunath                     params->callbacksDataPtr);
26134a096dbSkasunath             }
26234a096dbSkasunath             params->stackCallback->stackPop(params->stackDataPtr);
26334a096dbSkasunath         }
26434a096dbSkasunath         else
26534a096dbSkasunath         {
26634a096dbSkasunath             RETURN_IF_CALLBACK_IERROR(
26734a096dbSkasunath                 params->decodedCallback->callbackPropertyEnd,
26834a096dbSkasunath                 params->callbacksDataPtr);
26934a096dbSkasunath             // Do not change the parent dictionary property offset since we are
27034a096dbSkasunath             // still inside the same section.
27134a096dbSkasunath             return 0;
27234a096dbSkasunath         }
27334a096dbSkasunath     }
27434a096dbSkasunath     return 0;
27534a096dbSkasunath }
27634a096dbSkasunath 
27734a096dbSkasunath /**
27834a096dbSkasunath  * @brief Check whether the current encoded segment being decoded is an array
27934a096dbSkasunath  * element.
28034a096dbSkasunath  *
28134a096dbSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
28234a096dbSkasunath  * @return true if the encoded segment is an array element. Else false.
28334a096dbSkasunath  */
bejIsArrayElement(const struct BejHandleTypeFuncParam * params)28434a096dbSkasunath static bool bejIsArrayElement(const struct BejHandleTypeFuncParam* params)
28534a096dbSkasunath {
28634a096dbSkasunath     // If the encoded segment enters an array section, we are adding a
28734a096dbSkasunath     // BejSectionArray to the stack. Therefore if the stack is empty, encoded
28834a096dbSkasunath     // segment cannot be an array element.
28934a096dbSkasunath     if (params->stackCallback->stackEmpty(params->stackDataPtr))
29034a096dbSkasunath     {
29134a096dbSkasunath         return false;
29234a096dbSkasunath     }
29334a096dbSkasunath     const struct BejStackProperty* const ending =
29434a096dbSkasunath         params->stackCallback->stackPeek(params->stackDataPtr);
29534a096dbSkasunath     // If the stack top element holds a BejSectionArray, encoded segment is
29634a096dbSkasunath     // an array element.
29734a096dbSkasunath     return ending->sectionType == bejSectionArray;
29834a096dbSkasunath }
29934a096dbSkasunath 
30034a096dbSkasunath /**
30134a096dbSkasunath  * @brief Decodes a BejSet type SFLV BEJ tuple.
30234a096dbSkasunath  *
303b8ca265aSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
30434a096dbSkasunath  * @return 0 if successful.
30534a096dbSkasunath  */
bejHandleBejSet(struct BejHandleTypeFuncParam * params)30634a096dbSkasunath static int bejHandleBejSet(struct BejHandleTypeFuncParam* params)
30734a096dbSkasunath {
30834a096dbSkasunath     uint16_t sequenceNumber = params->sflv.tupleS.sequenceNumber;
30934a096dbSkasunath     // Check whether this BejSet is an array element or not.
31034a096dbSkasunath     if (bejIsArrayElement(params))
31134a096dbSkasunath     {
31234a096dbSkasunath         // Dictionary only contains an entry for element 0.
31334a096dbSkasunath         sequenceNumber = 0;
31434a096dbSkasunath     }
31534a096dbSkasunath     const uint8_t* dictionary;
31634a096dbSkasunath     const struct BejDictionaryProperty* prop;
31734a096dbSkasunath     RETURN_IF_IERROR(
31834a096dbSkasunath         bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
31934a096dbSkasunath                                     sequenceNumber, &dictionary, &prop));
32034a096dbSkasunath 
32134a096dbSkasunath     const char* propName = "";
32234a096dbSkasunath     if (params->state.addPropertyName)
32334a096dbSkasunath     {
32434a096dbSkasunath         propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
32534a096dbSkasunath                                           prop->nameLength);
32634a096dbSkasunath     }
32734a096dbSkasunath 
32834a096dbSkasunath     RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackSetStart,
32934a096dbSkasunath                               propName, params->callbacksDataPtr);
33034a096dbSkasunath 
331c14fab6eSkasunath     // Move the offset to the next SFLV tuple (or end). Make sure that this is
332c14fab6eSkasunath     // called before calling bejProcessEnding.
333c14fab6eSkasunath     params->state.encodedStreamOffset = bejGetFirstTupleOffset(params);
334c14fab6eSkasunath 
3350aa36d82Skasunath     uint64_t elements = bejGetNnint(params->sflv.value);
33634a096dbSkasunath     // If its an empty set, we are done here.
33734a096dbSkasunath     if (elements == 0)
33834a096dbSkasunath     {
33934a096dbSkasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackSetEnd,
34034a096dbSkasunath                                   params->callbacksDataPtr);
341c14fab6eSkasunath         // Since this is an ending of a property (empty array), we should call
342c14fab6eSkasunath         // bejProcessEnding. Unless the whole JSON object is an empty set (which
343c14fab6eSkasunath         // shouldn't be the case), stack cannot be empty.
344c14fab6eSkasunath         bejProcessEnding(params, /*canBeEmpty=*/false);
345c14fab6eSkasunath         return 0;
34634a096dbSkasunath     }
347c14fab6eSkasunath 
34834a096dbSkasunath     // Update the states for the next encoding segment.
34934a096dbSkasunath     struct BejStackProperty newEnding = {
35034a096dbSkasunath         .sectionType = bejSectionSet,
35134a096dbSkasunath         .addPropertyName = params->state.addPropertyName,
35234a096dbSkasunath         .mainDictPropOffset = params->state.mainDictPropOffset,
35334a096dbSkasunath         .annoDictPropOffset = params->state.annoDictPropOffset,
35434a096dbSkasunath         .streamEndOffset = params->sflv.valueEndOffset,
35534a096dbSkasunath     };
35634a096dbSkasunath     RETURN_IF_IERROR(
35734a096dbSkasunath         params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
35834a096dbSkasunath     params->state.addPropertyName = true;
35934a096dbSkasunath     if (params->sflv.tupleS.schema == bejAnnotation)
36034a096dbSkasunath     {
36134a096dbSkasunath         // Since this set is an annotated type, we need to advance the
36234a096dbSkasunath         // annotation dictionary for decoding the next segment.
36334a096dbSkasunath         params->state.annoDictPropOffset = prop->childPointerOffset;
36434a096dbSkasunath     }
36534a096dbSkasunath     else
36634a096dbSkasunath     {
36734a096dbSkasunath         params->state.mainDictPropOffset = prop->childPointerOffset;
36834a096dbSkasunath     }
36934a096dbSkasunath     return 0;
37034a096dbSkasunath }
37134a096dbSkasunath 
37234a096dbSkasunath /**
373e2260cd6Skasunath  * @brief Decodes a BejArray type SFLV BEJ tuple.
374e2260cd6Skasunath  *
375b8ca265aSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
376e2260cd6Skasunath  * @return 0 if successful.
377e2260cd6Skasunath  */
bejHandleBejArray(struct BejHandleTypeFuncParam * params)378e2260cd6Skasunath static int bejHandleBejArray(struct BejHandleTypeFuncParam* params)
379e2260cd6Skasunath {
380e2260cd6Skasunath     const uint8_t* dictionary;
381e2260cd6Skasunath     const struct BejDictionaryProperty* prop;
382e2260cd6Skasunath     RETURN_IF_IERROR(bejGetDictionaryAndProperty(
383e2260cd6Skasunath         params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber,
384e2260cd6Skasunath         &dictionary, &prop));
385e2260cd6Skasunath 
386e2260cd6Skasunath     const char* propName = "";
387e2260cd6Skasunath     if (params->state.addPropertyName)
388e2260cd6Skasunath     {
389e2260cd6Skasunath         propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
390e2260cd6Skasunath                                           prop->nameLength);
391e2260cd6Skasunath     }
392e2260cd6Skasunath 
393e2260cd6Skasunath     RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayStart,
394e2260cd6Skasunath                               propName, params->callbacksDataPtr);
395e2260cd6Skasunath 
396c14fab6eSkasunath     // Move the offset to the next SFLV tuple (or end). Make sure that this is
397c14fab6eSkasunath     // called before calling bejProcessEnding.
398c14fab6eSkasunath     params->state.encodedStreamOffset = bejGetFirstTupleOffset(params);
399c14fab6eSkasunath 
4000aa36d82Skasunath     uint64_t elements = bejGetNnint(params->sflv.value);
401e2260cd6Skasunath     // If its an empty array, we are done here.
402e2260cd6Skasunath     if (elements == 0)
403e2260cd6Skasunath     {
404e2260cd6Skasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayEnd,
405e2260cd6Skasunath                                   params->callbacksDataPtr);
406c14fab6eSkasunath         // Since this is an ending of a property (empty array), we should call
407c14fab6eSkasunath         // bejProcessEnding. Stack cannot be empty since there should be at
408c14fab6eSkasunath         // least 1 parent in the stack.
409c14fab6eSkasunath         bejProcessEnding(params, /*canBeEmpty=*/false);
410c14fab6eSkasunath         return 0;
411e2260cd6Skasunath     }
412c14fab6eSkasunath 
413e2260cd6Skasunath     // Update the state for next segment decoding.
414e2260cd6Skasunath     struct BejStackProperty newEnding = {
415e2260cd6Skasunath         .sectionType = bejSectionArray,
416e2260cd6Skasunath         .addPropertyName = params->state.addPropertyName,
417e2260cd6Skasunath         .mainDictPropOffset = params->state.mainDictPropOffset,
418e2260cd6Skasunath         .annoDictPropOffset = params->state.annoDictPropOffset,
419e2260cd6Skasunath         .streamEndOffset = params->sflv.valueEndOffset,
420e2260cd6Skasunath     };
421e2260cd6Skasunath     RETURN_IF_IERROR(
422e2260cd6Skasunath         params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
423e2260cd6Skasunath     // We do not add property names for array elements.
424e2260cd6Skasunath     params->state.addPropertyName = false;
425e2260cd6Skasunath     if (params->sflv.tupleS.schema == bejAnnotation)
426e2260cd6Skasunath     {
427e2260cd6Skasunath         // Since this array is an annotated type, we need to advance the
428e2260cd6Skasunath         // annotation dictionary for decoding the next segment.
429e2260cd6Skasunath         params->state.annoDictPropOffset = prop->childPointerOffset;
430e2260cd6Skasunath     }
431e2260cd6Skasunath     else
432e2260cd6Skasunath     {
433e2260cd6Skasunath         params->state.mainDictPropOffset = prop->childPointerOffset;
434e2260cd6Skasunath     }
435e2260cd6Skasunath     return 0;
436e2260cd6Skasunath }
437e2260cd6Skasunath 
438e2260cd6Skasunath /**
439e2260cd6Skasunath  * @brief Decodes a BejNull type SFLV BEJ tuple.
440e2260cd6Skasunath  *
441b8ca265aSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
442e2260cd6Skasunath  * @return 0 if successful.
443e2260cd6Skasunath  */
bejHandleBejNull(struct BejHandleTypeFuncParam * params)444e2260cd6Skasunath static int bejHandleBejNull(struct BejHandleTypeFuncParam* params)
445e2260cd6Skasunath {
446e2260cd6Skasunath     const char* propName = bejGetPropName(params);
447e2260cd6Skasunath     RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, propName,
448e2260cd6Skasunath                               params->callbacksDataPtr);
449e2260cd6Skasunath     params->state.encodedStreamOffset = params->sflv.valueEndOffset;
450e2260cd6Skasunath     return bejProcessEnding(params, /*canBeEmpty=*/false);
451e2260cd6Skasunath }
452e2260cd6Skasunath 
453e2260cd6Skasunath /**
45434a096dbSkasunath  * @brief Decodes a BejInteger type SFLV BEJ tuple.
45534a096dbSkasunath  *
456b8ca265aSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
45734a096dbSkasunath  * @return 0 if successful.
45834a096dbSkasunath  */
bejHandleBejInteger(struct BejHandleTypeFuncParam * params)45934a096dbSkasunath static int bejHandleBejInteger(struct BejHandleTypeFuncParam* params)
46034a096dbSkasunath {
461e2260cd6Skasunath     const char* propName = bejGetPropName(params);
46234a096dbSkasunath 
46334a096dbSkasunath     if (params->sflv.valueLength == 0)
46434a096dbSkasunath     {
46534a096dbSkasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
46634a096dbSkasunath                                   propName, params->callbacksDataPtr);
46734a096dbSkasunath     }
46834a096dbSkasunath     else
46934a096dbSkasunath     {
47034a096dbSkasunath         RETURN_IF_CALLBACK_IERROR(
47134a096dbSkasunath             params->decodedCallback->callbackInteger, propName,
47234a096dbSkasunath             bejGetIntegerValue(params->sflv.value, params->sflv.valueLength),
47334a096dbSkasunath             params->callbacksDataPtr);
47434a096dbSkasunath     }
47534a096dbSkasunath     params->state.encodedStreamOffset = params->sflv.valueEndOffset;
47634a096dbSkasunath     return bejProcessEnding(params, /*canBeEmpty=*/false);
47734a096dbSkasunath }
47834a096dbSkasunath 
47934a096dbSkasunath /**
480b8ca265aSkasunath  * @brief Decodes a BejEnum type SFLV BEJ tuple.
481b8ca265aSkasunath  *
482b8ca265aSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
483b8ca265aSkasunath  * @return 0 if successful.
484b8ca265aSkasunath  */
bejHandleBejEnum(struct BejHandleTypeFuncParam * params)485b8ca265aSkasunath static int bejHandleBejEnum(struct BejHandleTypeFuncParam* params)
486b8ca265aSkasunath {
487b8ca265aSkasunath     uint16_t sequenceNumber = params->sflv.tupleS.sequenceNumber;
488b8ca265aSkasunath     if (bejIsArrayElement(params))
489b8ca265aSkasunath     {
490b8ca265aSkasunath         sequenceNumber = 0;
491b8ca265aSkasunath     }
492b8ca265aSkasunath     const uint8_t* dictionary;
493b8ca265aSkasunath     const struct BejDictionaryProperty* prop;
494b8ca265aSkasunath     RETURN_IF_IERROR(
495b8ca265aSkasunath         bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
496b8ca265aSkasunath                                     sequenceNumber, &dictionary, &prop));
497b8ca265aSkasunath 
498b8ca265aSkasunath     const char* propName = "";
499b8ca265aSkasunath     if (params->state.addPropertyName)
500b8ca265aSkasunath     {
501b8ca265aSkasunath         propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
502b8ca265aSkasunath                                           prop->nameLength);
503b8ca265aSkasunath     }
504b8ca265aSkasunath 
505b8ca265aSkasunath     if (params->sflv.valueLength == 0)
506b8ca265aSkasunath     {
507b8ca265aSkasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
508b8ca265aSkasunath                                   propName, params->callbacksDataPtr);
509b8ca265aSkasunath     }
510b8ca265aSkasunath     else
511b8ca265aSkasunath     {
512b8ca265aSkasunath         // Get the string for enum value.
513b8ca265aSkasunath         uint16_t enumValueSequenceN =
5140aa36d82Skasunath             (uint16_t)(bejGetNnint(params->sflv.value));
515b8ca265aSkasunath         const struct BejDictionaryProperty* enumValueProp;
516b8ca265aSkasunath         RETURN_IF_IERROR(
517b8ca265aSkasunath             bejDictGetProperty(dictionary, prop->childPointerOffset,
518b8ca265aSkasunath                                enumValueSequenceN, &enumValueProp));
519b8ca265aSkasunath         const char* enumValueName = bejDictGetPropertyName(
520b8ca265aSkasunath             dictionary, enumValueProp->nameOffset, enumValueProp->nameLength);
521b8ca265aSkasunath 
522b8ca265aSkasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackEnum,
523b8ca265aSkasunath                                   propName, enumValueName,
524b8ca265aSkasunath                                   params->callbacksDataPtr);
525b8ca265aSkasunath     }
526b8ca265aSkasunath     // Update the offset to point to the next possible SFLV tuple.
527b8ca265aSkasunath     params->state.encodedStreamOffset = params->sflv.valueEndOffset;
528b8ca265aSkasunath     return bejProcessEnding(params, /*canBeEmpty=*/false);
529b8ca265aSkasunath }
530b8ca265aSkasunath 
531b8ca265aSkasunath /**
532e2260cd6Skasunath  * @brief Decodes a BejString type SFLV BEJ tuple.
533e2260cd6Skasunath  *
534b8ca265aSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
535e2260cd6Skasunath  * @return 0 if successful.
536e2260cd6Skasunath  */
bejHandleBejString(struct BejHandleTypeFuncParam * params)537e2260cd6Skasunath static int bejHandleBejString(struct BejHandleTypeFuncParam* params)
538e2260cd6Skasunath {
539e2260cd6Skasunath     // TODO: Handle deferred bindings.
540e2260cd6Skasunath     const char* propName = bejGetPropName(params);
541e2260cd6Skasunath 
542e2260cd6Skasunath     if (params->sflv.valueLength == 0)
543e2260cd6Skasunath     {
544e2260cd6Skasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
545e2260cd6Skasunath                                   propName, params->callbacksDataPtr);
546e2260cd6Skasunath     }
547e2260cd6Skasunath     else
548e2260cd6Skasunath     {
549e2260cd6Skasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackString,
550e2260cd6Skasunath                                   propName, (const char*)(params->sflv.value),
551e2260cd6Skasunath                                   params->callbacksDataPtr);
552e2260cd6Skasunath     }
553e2260cd6Skasunath     params->state.encodedStreamOffset = params->sflv.valueEndOffset;
554e2260cd6Skasunath     return bejProcessEnding(params, /*canBeEmpty=*/false);
555e2260cd6Skasunath }
556e2260cd6Skasunath 
557e2260cd6Skasunath /**
558b8ca265aSkasunath  * @brief Decodes a BejReal type SFLV BEJ tuple.
559b8ca265aSkasunath  *
560b8ca265aSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
561b8ca265aSkasunath  * @return 0 if successful.
562b8ca265aSkasunath  */
bejHandleBejReal(struct BejHandleTypeFuncParam * params)563b8ca265aSkasunath static int bejHandleBejReal(struct BejHandleTypeFuncParam* params)
564b8ca265aSkasunath {
565b8ca265aSkasunath     const char* propName = bejGetPropName(params);
566b8ca265aSkasunath 
567b8ca265aSkasunath     if (params->sflv.valueLength == 0)
568b8ca265aSkasunath     {
569b8ca265aSkasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
570b8ca265aSkasunath                                   propName, params->callbacksDataPtr);
571b8ca265aSkasunath     }
572b8ca265aSkasunath     else
573b8ca265aSkasunath     {
574b8ca265aSkasunath         // Real value has the following format.
575b8ca265aSkasunath         // nnint      - Length of whole
576b8ca265aSkasunath         // bejInteger - whole (includes sign for the overall real number)
577b8ca265aSkasunath         // nnint      - Leading zero count for fract
578b8ca265aSkasunath         // nnint      - fract
579b8ca265aSkasunath         // nnint      - Length of exp
580b8ca265aSkasunath         // bejInteger - exp (includes sign for the exponent)
5810aa36d82Skasunath         uint8_t wholeByteLen = (uint8_t)bejGetNnint(params->sflv.value);
582be27f2e9SPatrick Williams         const uint8_t* wholeBejInt =
583be27f2e9SPatrick Williams             params->sflv.value + bejGetNnintSize(params->sflv.value);
584b8ca265aSkasunath         const uint8_t* fractZeroCountNnint = wholeBejInt + wholeByteLen;
585be27f2e9SPatrick Williams         const uint8_t* fractNnint =
586be27f2e9SPatrick Williams             fractZeroCountNnint + bejGetNnintSize(fractZeroCountNnint);
5870aa36d82Skasunath         const uint8_t* lenExpNnint = fractNnint + bejGetNnintSize(fractNnint);
5880aa36d82Skasunath         const uint8_t* expBejInt = lenExpNnint + bejGetNnintSize(lenExpNnint);
589b8ca265aSkasunath 
590b8ca265aSkasunath         struct BejReal realValue;
591b8ca265aSkasunath         realValue.whole = bejGetIntegerValue(wholeBejInt, wholeByteLen);
5920aa36d82Skasunath         realValue.zeroCount = bejGetNnint(fractZeroCountNnint);
5930aa36d82Skasunath         realValue.fract = bejGetNnint(fractNnint);
5940aa36d82Skasunath         realValue.expLen = (uint8_t)bejGetNnint(lenExpNnint);
595b8ca265aSkasunath         if (realValue.expLen != 0)
596b8ca265aSkasunath         {
597b8ca265aSkasunath             realValue.exp = bejGetIntegerValue(
5980aa36d82Skasunath                 expBejInt, (uint8_t)bejGetNnint(lenExpNnint));
599b8ca265aSkasunath         }
600b8ca265aSkasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackReal,
601b8ca265aSkasunath                                   propName, &realValue,
602b8ca265aSkasunath                                   params->callbacksDataPtr);
603b8ca265aSkasunath     }
604b8ca265aSkasunath     params->state.encodedStreamOffset = params->sflv.valueEndOffset;
605b8ca265aSkasunath     return bejProcessEnding(params, /*canBeEmpty=*/false);
606b8ca265aSkasunath }
607b8ca265aSkasunath 
608b8ca265aSkasunath /**
609e2260cd6Skasunath  * @brief Decodes a BejBoolean type SFLV BEJ tuple.
610e2260cd6Skasunath  *
611b8ca265aSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
612e2260cd6Skasunath  * @return 0 if successful.
613e2260cd6Skasunath  */
bejHandleBejBoolean(struct BejHandleTypeFuncParam * params)614e2260cd6Skasunath static int bejHandleBejBoolean(struct BejHandleTypeFuncParam* params)
615e2260cd6Skasunath {
616e2260cd6Skasunath     const char* propName = bejGetPropName(params);
617e2260cd6Skasunath 
618e2260cd6Skasunath     if (params->sflv.valueLength == 0)
619e2260cd6Skasunath     {
620e2260cd6Skasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
621e2260cd6Skasunath                                   propName, params->callbacksDataPtr);
622e2260cd6Skasunath     }
623e2260cd6Skasunath     else
624e2260cd6Skasunath     {
625e2260cd6Skasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackBool,
626e2260cd6Skasunath                                   propName, *(params->sflv.value) > 0,
627e2260cd6Skasunath                                   params->callbacksDataPtr);
628e2260cd6Skasunath     }
629e2260cd6Skasunath     params->state.encodedStreamOffset = params->sflv.valueEndOffset;
630e2260cd6Skasunath     return bejProcessEnding(params, /*canBeEmpty=*/false);
631e2260cd6Skasunath }
632e2260cd6Skasunath 
633e2260cd6Skasunath /**
634b8ca265aSkasunath  * @brief Decodes a BejPropertyAnnotation type SFLV BEJ tuple.
635b8ca265aSkasunath  *
636b8ca265aSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
637b8ca265aSkasunath  * @return 0 if successful.
638b8ca265aSkasunath  */
bejHandleBejPropertyAnnotation(struct BejHandleTypeFuncParam * params)639b8ca265aSkasunath static int bejHandleBejPropertyAnnotation(struct BejHandleTypeFuncParam* params)
640b8ca265aSkasunath {
641b8ca265aSkasunath     // TODO: Handle colon-delimited string values.
642b8ca265aSkasunath 
643b8ca265aSkasunath     // Property annotation has the form OuterProperty@Annotation. First
644b8ca265aSkasunath     // processing the outer property name.
645b8ca265aSkasunath     const uint8_t* outerDictionary;
646b8ca265aSkasunath     const struct BejDictionaryProperty* outerProp;
647b8ca265aSkasunath     RETURN_IF_IERROR(bejGetDictionaryAndProperty(
648b8ca265aSkasunath         params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber,
649b8ca265aSkasunath         &outerDictionary, &outerProp));
650b8ca265aSkasunath 
651b8ca265aSkasunath     const char* propName = bejDictGetPropertyName(
652b8ca265aSkasunath         outerDictionary, outerProp->nameOffset, outerProp->nameLength);
653b8ca265aSkasunath     RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackAnnotation,
654b8ca265aSkasunath                               propName, params->callbacksDataPtr);
655b8ca265aSkasunath 
656b8ca265aSkasunath     // Mark the ending of the property annotation.
657b8ca265aSkasunath     struct BejStackProperty newEnding = {
658b8ca265aSkasunath         .sectionType = bejSectionNoType,
659b8ca265aSkasunath         .addPropertyName = params->state.addPropertyName,
660b8ca265aSkasunath         .mainDictPropOffset = params->state.mainDictPropOffset,
661b8ca265aSkasunath         .annoDictPropOffset = params->state.annoDictPropOffset,
662b8ca265aSkasunath         .streamEndOffset = params->sflv.valueEndOffset,
663b8ca265aSkasunath     };
664b8ca265aSkasunath     // Update the states for the next encoding segment.
665b8ca265aSkasunath     RETURN_IF_IERROR(
666b8ca265aSkasunath         params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
667b8ca265aSkasunath     params->state.addPropertyName = true;
668b8ca265aSkasunath     // We might have to change this for nested annotations.
669b8ca265aSkasunath     params->state.mainDictPropOffset = outerProp->childPointerOffset;
670b8ca265aSkasunath     // Point to the start of the value for next decoding.
671be27f2e9SPatrick Williams     params->state.encodedStreamOffset =
672be27f2e9SPatrick Williams         params->sflv.valueEndOffset - params->sflv.valueLength;
673b8ca265aSkasunath     return 0;
674b8ca265aSkasunath }
675b8ca265aSkasunath 
676b8ca265aSkasunath /**
67764cb1973Skasunath  * @brief Decodes an encoded bej stream.
67864cb1973Skasunath  *
67964cb1973Skasunath  * @param[in] schemaDictionary - main schema dictionary to use.
68064cb1973Skasunath  * @param[in] annotationDictionary - annotation dictionary
68164cb1973Skasunath  * @param[in] enStream - encoded stream without the PLDM header.
68264cb1973Skasunath  * @param[in] streamLen - length of the enStream.
68364cb1973Skasunath  * @param[in] stackCallback - callbacks for stack handlers.
68464cb1973Skasunath  * @param[in] decodedCallback - callbacks for extracting decoded properties.
68564cb1973Skasunath  * @param[in] callbacksDataPtr - data pointer to pass to decoded callbacks. This
68664cb1973Skasunath  * can be used pass additional data.
68764cb1973Skasunath  * @param[in] stackDataPtr - data pointer to pass to stack callbacks. This can
68864cb1973Skasunath  * be used pass additional data.
68964cb1973Skasunath  *
69064cb1973Skasunath  * @return 0 if successful.
69164cb1973Skasunath  */
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)69264cb1973Skasunath static int bejDecode(const uint8_t* schemaDictionary,
69364cb1973Skasunath                      const uint8_t* annotationDictionary,
69464cb1973Skasunath                      const uint8_t* enStream, uint32_t streamLen,
69564cb1973Skasunath                      const struct BejStackCallback* stackCallback,
69664cb1973Skasunath                      const struct BejDecodedCallback* decodedCallback,
69764cb1973Skasunath                      void* callbacksDataPtr, void* stackDataPtr)
69864cb1973Skasunath {
69964cb1973Skasunath     struct BejHandleTypeFuncParam params = {
70064cb1973Skasunath         .state =
70164cb1973Skasunath             {
70264cb1973Skasunath                 // We only add names of set properties. We don't use names for
70364cb1973Skasunath                 // array
70464cb1973Skasunath                 // properties. Here we are omitting the name of the root set.
70564cb1973Skasunath                 .addPropertyName = false,
70664cb1973Skasunath                 // At start, parent property from the main dictionary is the
70764cb1973Skasunath                 // first property.
70864cb1973Skasunath                 .mainDictPropOffset = bejDictGetPropertyHeadOffset(),
70964cb1973Skasunath                 .annoDictPropOffset = bejDictGetFirstAnnotatedPropertyOffset(),
71064cb1973Skasunath                 // Current location of the encoded segment we are processing.
71164cb1973Skasunath                 .encodedStreamOffset = 0,
71264cb1973Skasunath                 .encodedSubStream = enStream,
71364cb1973Skasunath             },
71464cb1973Skasunath         .mainDictionary = schemaDictionary,
71564cb1973Skasunath         .annotDictionary = annotationDictionary,
71664cb1973Skasunath         .decodedCallback = decodedCallback,
71764cb1973Skasunath         .stackCallback = stackCallback,
71864cb1973Skasunath         .callbacksDataPtr = callbacksDataPtr,
71964cb1973Skasunath         .stackDataPtr = stackDataPtr,
72064cb1973Skasunath     };
72164cb1973Skasunath 
72264cb1973Skasunath     while (params.state.encodedStreamOffset < streamLen)
72364cb1973Skasunath     {
72464cb1973Skasunath         // Go to the next encoded segment in the encoded stream.
725be27f2e9SPatrick Williams         params.state.encodedSubStream =
726be27f2e9SPatrick Williams             enStream + params.state.encodedStreamOffset;
72764cb1973Skasunath         bejInitSFLVStruct(&params);
72864cb1973Skasunath 
72964cb1973Skasunath         if (params.sflv.format.readOnlyProperty)
73064cb1973Skasunath         {
73164cb1973Skasunath             RETURN_IF_CALLBACK_IERROR(
73264cb1973Skasunath                 params.decodedCallback->callbackReadonlyProperty,
73364cb1973Skasunath                 params.sflv.tupleS.sequenceNumber, params.callbacksDataPtr);
73464cb1973Skasunath         }
73564cb1973Skasunath 
73664cb1973Skasunath         // TODO: Handle nullable property types. These are indicated by
73764cb1973Skasunath         // params.sflv.format.nullableProperty
73864cb1973Skasunath         switch (params.sflv.format.principalDataType)
73964cb1973Skasunath         {
74064cb1973Skasunath             case bejSet:
74134a096dbSkasunath                 RETURN_IF_IERROR(bejHandleBejSet(&params));
74264cb1973Skasunath                 break;
74364cb1973Skasunath             case bejArray:
744e2260cd6Skasunath                 RETURN_IF_IERROR(bejHandleBejArray(&params));
74564cb1973Skasunath                 break;
74664cb1973Skasunath             case bejNull:
747e2260cd6Skasunath                 RETURN_IF_IERROR(bejHandleBejNull(&params));
74864cb1973Skasunath                 break;
74964cb1973Skasunath             case bejInteger:
75034a096dbSkasunath                 RETURN_IF_IERROR(bejHandleBejInteger(&params));
75164cb1973Skasunath                 break;
75264cb1973Skasunath             case bejEnum:
753b8ca265aSkasunath                 RETURN_IF_IERROR(bejHandleBejEnum(&params));
75464cb1973Skasunath                 break;
75564cb1973Skasunath             case bejString:
756e2260cd6Skasunath                 RETURN_IF_IERROR(bejHandleBejString(&params));
75764cb1973Skasunath                 break;
75864cb1973Skasunath             case bejReal:
759b8ca265aSkasunath                 RETURN_IF_IERROR(bejHandleBejReal(&params));
76064cb1973Skasunath                 break;
76164cb1973Skasunath             case bejBoolean:
762e2260cd6Skasunath                 RETURN_IF_IERROR(bejHandleBejBoolean(&params));
76364cb1973Skasunath                 break;
76464cb1973Skasunath             case bejBytestring:
76564cb1973Skasunath                 // TODO: Add support for BejBytestring decoding.
76664cb1973Skasunath                 fprintf(stderr, "No BejBytestring support\n");
76764cb1973Skasunath                 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
76864cb1973Skasunath                 break;
76964cb1973Skasunath             case bejChoice:
77064cb1973Skasunath                 // TODO: Add support for BejChoice decoding.
77164cb1973Skasunath                 fprintf(stderr, "No BejChoice support\n");
77264cb1973Skasunath                 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
77364cb1973Skasunath                 break;
77464cb1973Skasunath             case bejPropertyAnnotation:
775b8ca265aSkasunath                 RETURN_IF_IERROR(bejHandleBejPropertyAnnotation(&params));
77664cb1973Skasunath                 break;
77764cb1973Skasunath             case bejResourceLink:
77864cb1973Skasunath                 // TODO: Add support for BejResourceLink decoding.
77964cb1973Skasunath                 fprintf(stderr, "No BejResourceLink support\n");
78064cb1973Skasunath                 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
78164cb1973Skasunath                 break;
78264cb1973Skasunath             case bejResourceLinkExpansion:
78364cb1973Skasunath                 // TODO: Add support for BejResourceLinkExpansion decoding.
78464cb1973Skasunath                 fprintf(stderr, "No BejResourceLinkExpansion support\n");
78564cb1973Skasunath                 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
78664cb1973Skasunath                 break;
78764cb1973Skasunath             default:
78864cb1973Skasunath                 break;
78964cb1973Skasunath         }
79064cb1973Skasunath     }
79134a096dbSkasunath     RETURN_IF_IERROR(bejProcessEnding(&params, /*canBeEmpty=*/true));
79264cb1973Skasunath     if (!params.stackCallback->stackEmpty(params.stackDataPtr))
79364cb1973Skasunath     {
79464cb1973Skasunath         fprintf(stderr, "Ending stack should be empty but its not. Something "
79564cb1973Skasunath                         "must have gone wrong with the encoding\n");
79664cb1973Skasunath         return bejErrorUnknown;
79764cb1973Skasunath     }
79864cb1973Skasunath     return 0;
79964cb1973Skasunath }
80064cb1973Skasunath 
80164cb1973Skasunath /**
80264cb1973Skasunath  * @brief Check if a bej version is supported by this decoder
80364cb1973Skasunath  *
804b8ca265aSkasunath  * @param[in] bejVersion - the bej version in the received encoded stream
80564cb1973Skasunath  * @return true if supported.
80664cb1973Skasunath  */
bejIsSupported(uint32_t bejVersion)80764cb1973Skasunath static bool bejIsSupported(uint32_t bejVersion)
80864cb1973Skasunath {
80964cb1973Skasunath     for (uint32_t i = 0; i < sizeof(supportedBejVersions) / sizeof(uint32_t);
81064cb1973Skasunath          ++i)
81164cb1973Skasunath     {
81264cb1973Skasunath         if (bejVersion == supportedBejVersions[i])
81364cb1973Skasunath         {
81464cb1973Skasunath             return true;
81564cb1973Skasunath         }
81664cb1973Skasunath     }
81764cb1973Skasunath     return false;
81864cb1973Skasunath }
81964cb1973Skasunath 
bejDecodePldmBlock(const struct BejDictionaries * dictionaries,const uint8_t * encodedPldmBlock,uint32_t blockLength,const struct BejStackCallback * stackCallback,const struct BejDecodedCallback * decodedCallback,void * callbacksDataPtr,void * stackDataPtr)82064cb1973Skasunath int bejDecodePldmBlock(const struct BejDictionaries* dictionaries,
82164cb1973Skasunath                        const uint8_t* encodedPldmBlock, uint32_t blockLength,
82264cb1973Skasunath                        const struct BejStackCallback* stackCallback,
82364cb1973Skasunath                        const struct BejDecodedCallback* decodedCallback,
82464cb1973Skasunath                        void* callbacksDataPtr, void* stackDataPtr)
82564cb1973Skasunath {
82634a096dbSkasunath     NULL_CHECK(dictionaries, "dictionaries");
82734a096dbSkasunath     NULL_CHECK(dictionaries->schemaDictionary, "schemaDictionary");
82834a096dbSkasunath     NULL_CHECK(dictionaries->annotationDictionary, "annotationDictionary");
82934a096dbSkasunath 
83034a096dbSkasunath     NULL_CHECK(encodedPldmBlock, "encodedPldmBlock");
83134a096dbSkasunath 
83234a096dbSkasunath     NULL_CHECK(stackCallback, "stackCallback");
83334a096dbSkasunath     NULL_CHECK(stackCallback->stackEmpty, "stackEmpty");
83434a096dbSkasunath     NULL_CHECK(stackCallback->stackPeek, "stackPeek");
83534a096dbSkasunath     NULL_CHECK(stackCallback->stackPop, "stackPop");
83634a096dbSkasunath     NULL_CHECK(stackCallback->stackPush, "stackPush");
83734a096dbSkasunath 
83834a096dbSkasunath     NULL_CHECK(decodedCallback, "decodedCallback");
83934a096dbSkasunath 
84064cb1973Skasunath     uint32_t pldmHeaderSize = sizeof(struct BejPldmBlockHeader);
84164cb1973Skasunath     if (blockLength < pldmHeaderSize)
84264cb1973Skasunath     {
84364cb1973Skasunath         fprintf(stderr, "Invalid pldm block size: %u\n", blockLength);
84464cb1973Skasunath         return bejErrorInvalidSize;
84564cb1973Skasunath     }
84664cb1973Skasunath 
84764cb1973Skasunath     const struct BejPldmBlockHeader* pldmHeader =
84864cb1973Skasunath         (const struct BejPldmBlockHeader*)encodedPldmBlock;
84964cb1973Skasunath 
85064cb1973Skasunath     if (!bejIsSupported(pldmHeader->bejVersion))
85164cb1973Skasunath     {
85264cb1973Skasunath         fprintf(stderr, "Bej decoder doesn't support the bej version: %u\n",
85364cb1973Skasunath                 pldmHeader->bejVersion);
85464cb1973Skasunath         return bejErrorNotSuppoted;
85564cb1973Skasunath     }
85664cb1973Skasunath 
85764cb1973Skasunath     if (pldmHeader->schemaClass == bejAnnotationSchemaClass)
85864cb1973Skasunath     {
85964cb1973Skasunath         fprintf(stderr,
86064cb1973Skasunath                 "Encoder schema class cannot be BejAnnotationSchemaClass\n");
86164cb1973Skasunath         return bejErrorNotSuppoted;
86264cb1973Skasunath     }
86364cb1973Skasunath     // TODO: Add support for CollectionMemberType schema class.
86464cb1973Skasunath     if (pldmHeader->schemaClass == bejCollectionMemberTypeSchemaClass)
86564cb1973Skasunath     {
86664cb1973Skasunath         fprintf(stderr, "Decoder doesn't support "
86734a096dbSkasunath                         "bejCollectionMemberTypeSchemaClass yet.\n");
86864cb1973Skasunath         return bejErrorNotSuppoted;
86964cb1973Skasunath     }
87064cb1973Skasunath     // TODO: Add support for Error schema class.
87164cb1973Skasunath     if (pldmHeader->schemaClass == bejErrorSchemaClass)
87264cb1973Skasunath     {
87364cb1973Skasunath         fprintf(stderr, "Decoder doesn't support BejErrorSchemaClass yet.\n");
87464cb1973Skasunath         return bejErrorNotSuppoted;
87564cb1973Skasunath     }
87664cb1973Skasunath 
87764cb1973Skasunath     // Skip the PLDM header.
87864cb1973Skasunath     const uint8_t* enStream = encodedPldmBlock + pldmHeaderSize;
87964cb1973Skasunath     uint32_t streamLen = blockLength - pldmHeaderSize;
88064cb1973Skasunath     return bejDecode(dictionaries->schemaDictionary,
88164cb1973Skasunath                      dictionaries->annotationDictionary, enStream, streamLen,
88264cb1973Skasunath                      stackCallback, decodedCallback, callbacksDataPtr,
88364cb1973Skasunath                      stackDataPtr);
88464cb1973Skasunath }
885