xref: /openbmc/libbej/src/bej_decoder_core.c (revision 99bd6c90)
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.
78*435526a5SPatrick Williams     const uint32_t valueOffset = valueLenNnintOffset + sizeof(uint8_t) +
79*435526a5SPatrick Williams                                  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  */
12734a096dbSkasunath static uint32_t
bejGetFirstTupleOffset(const struct BejHandleTypeFuncParam * params)12834a096dbSkasunath     bejGetFirstTupleOffset(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  */
15334a096dbSkasunath static int
bejGetDictionaryAndProperty(const struct BejHandleTypeFuncParam * params,uint8_t schemaType,uint32_t sequenceNumber,const uint8_t ** dictionary,const struct BejDictionaryProperty ** prop)15434a096dbSkasunath     bejGetDictionaryAndProperty(const struct BejHandleTypeFuncParam* params,
15534a096dbSkasunath                                 uint8_t schemaType, uint32_t sequenceNumber,
15634a096dbSkasunath                                 const uint8_t** dictionary,
15734a096dbSkasunath                                 const struct BejDictionaryProperty** prop)
15834a096dbSkasunath {
15934a096dbSkasunath     uint16_t dictPropOffset;
16034a096dbSkasunath     // We need to pick the correct dictionary.
16134a096dbSkasunath     if (schemaType == bejPrimary)
16234a096dbSkasunath     {
16334a096dbSkasunath         *dictionary = params->mainDictionary;
16434a096dbSkasunath         dictPropOffset = params->state.mainDictPropOffset;
16534a096dbSkasunath     }
16634a096dbSkasunath     else if (schemaType == bejAnnotation)
16734a096dbSkasunath     {
16834a096dbSkasunath         *dictionary = params->annotDictionary;
16934a096dbSkasunath         dictPropOffset = params->state.annoDictPropOffset;
17034a096dbSkasunath     }
17134a096dbSkasunath     else
17234a096dbSkasunath     {
17334a096dbSkasunath         fprintf(stderr, "Failed to select a dictionary. schema type: %u\n",
17434a096dbSkasunath                 schemaType);
17534a096dbSkasunath         return bejErrorInvalidSchemaType;
17634a096dbSkasunath     }
17734a096dbSkasunath 
178*435526a5SPatrick Williams     int ret = bejDictGetProperty(*dictionary, dictPropOffset, sequenceNumber,
179*435526a5SPatrick Williams                                  prop);
18034a096dbSkasunath     if (ret != 0)
18134a096dbSkasunath     {
18234a096dbSkasunath         fprintf(stderr, "Failed to get dictionary property for offset: %u\n",
18334a096dbSkasunath                 dictPropOffset);
18434a096dbSkasunath         return ret;
18534a096dbSkasunath     }
18634a096dbSkasunath     return 0;
18734a096dbSkasunath }
18834a096dbSkasunath 
18934a096dbSkasunath /**
190e2260cd6Skasunath  * @brief Find and return the property name of the current encoded segment. If
191e2260cd6Skasunath  * the params->state.addPropertyName is false, this will return an empty string.
19234a096dbSkasunath  *
19334a096dbSkasunath  * @param[in] params - a valid populated BejHandleTypeFuncParam.
19434a096dbSkasunath  * @return 0 if successful.
19534a096dbSkasunath  */
bejGetPropName(struct BejHandleTypeFuncParam * params)196e2260cd6Skasunath static const char* bejGetPropName(struct BejHandleTypeFuncParam* params)
19734a096dbSkasunath {
19834a096dbSkasunath     const uint8_t* dictionary;
19934a096dbSkasunath     const struct BejDictionaryProperty* prop;
200e2260cd6Skasunath     if (!params->state.addPropertyName ||
201e2260cd6Skasunath         (bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
20234a096dbSkasunath                                      params->sflv.tupleS.sequenceNumber,
203e2260cd6Skasunath                                      &dictionary, &prop) != 0))
20434a096dbSkasunath     {
20534a096dbSkasunath         return "";
20634a096dbSkasunath     }
20734a096dbSkasunath     return bejDictGetPropertyName(dictionary, prop->nameOffset,
20834a096dbSkasunath                                   prop->nameLength);
20934a096dbSkasunath }
21034a096dbSkasunath 
21134a096dbSkasunath /**
21234a096dbSkasunath  * @brief Look for section endings.
21334a096dbSkasunath  *
21434a096dbSkasunath  * This figures out whether the current encoded segment marks a section
21534a096dbSkasunath  * ending. If so, this function will update the decoder state and pop the stack
21634a096dbSkasunath  * used to memorize endings. This function should be called after updating the
21734a096dbSkasunath  * encodedStreamOffset to the end of decoded SFLV tuple.
21834a096dbSkasunath  *
21934a096dbSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam which contains the decoder
22034a096dbSkasunath  * state.
22134a096dbSkasunath  * @param[in] canBeEmpty - if true, the stack being empty is not an error. If
22234a096dbSkasunath  * false, stack cannot be empty.
22334a096dbSkasunath  * @return 0 if successful.
22434a096dbSkasunath  */
bejProcessEnding(struct BejHandleTypeFuncParam * params,bool canBeEmpty)22534a096dbSkasunath static int bejProcessEnding(struct BejHandleTypeFuncParam* params,
22634a096dbSkasunath                             bool canBeEmpty)
22734a096dbSkasunath {
22834a096dbSkasunath     if (params->stackCallback->stackEmpty(params->stackDataPtr) && !canBeEmpty)
22934a096dbSkasunath     {
23034a096dbSkasunath         // If bejProcessEnding has been called after adding an appropriate JSON
23134a096dbSkasunath         // property, then stack cannot be empty.
23234a096dbSkasunath         fprintf(stderr, "Ending stack cannot be empty.\n");
23334a096dbSkasunath         return bejErrorUnknown;
23434a096dbSkasunath     }
23534a096dbSkasunath 
23634a096dbSkasunath     while (!params->stackCallback->stackEmpty(params->stackDataPtr))
23734a096dbSkasunath     {
23834a096dbSkasunath         const struct BejStackProperty* const ending =
23934a096dbSkasunath             params->stackCallback->stackPeek(params->stackDataPtr);
24034a096dbSkasunath         // Check whether the current offset location matches the expected ending
24134a096dbSkasunath         // offset. If so, we are done with that section.
24234a096dbSkasunath         if (params->state.encodedStreamOffset == ending->streamEndOffset)
24334a096dbSkasunath         {
24434a096dbSkasunath             // Since we are going out of a section, we need to reset the
24534a096dbSkasunath             // dictionary property offsets to this section's parent property
24634a096dbSkasunath             // start.
24734a096dbSkasunath             params->state.mainDictPropOffset = ending->mainDictPropOffset;
24834a096dbSkasunath             params->state.annoDictPropOffset = ending->annoDictPropOffset;
24934a096dbSkasunath             params->state.addPropertyName = ending->addPropertyName;
25034a096dbSkasunath 
25134a096dbSkasunath             if (ending->sectionType == bejSectionSet)
25234a096dbSkasunath             {
25334a096dbSkasunath                 RETURN_IF_CALLBACK_IERROR(
25434a096dbSkasunath                     params->decodedCallback->callbackSetEnd,
25534a096dbSkasunath                     params->callbacksDataPtr);
25634a096dbSkasunath             }
25734a096dbSkasunath             else if (ending->sectionType == bejSectionArray)
25834a096dbSkasunath             {
25934a096dbSkasunath                 RETURN_IF_CALLBACK_IERROR(
26034a096dbSkasunath                     params->decodedCallback->callbackArrayEnd,
26134a096dbSkasunath                     params->callbacksDataPtr);
26234a096dbSkasunath             }
26334a096dbSkasunath             params->stackCallback->stackPop(params->stackDataPtr);
26434a096dbSkasunath         }
26534a096dbSkasunath         else
26634a096dbSkasunath         {
26734a096dbSkasunath             RETURN_IF_CALLBACK_IERROR(
26834a096dbSkasunath                 params->decodedCallback->callbackPropertyEnd,
26934a096dbSkasunath                 params->callbacksDataPtr);
27034a096dbSkasunath             // Do not change the parent dictionary property offset since we are
27134a096dbSkasunath             // still inside the same section.
27234a096dbSkasunath             return 0;
27334a096dbSkasunath         }
27434a096dbSkasunath     }
27534a096dbSkasunath     return 0;
27634a096dbSkasunath }
27734a096dbSkasunath 
27834a096dbSkasunath /**
27934a096dbSkasunath  * @brief Check whether the current encoded segment being decoded is an array
28034a096dbSkasunath  * element.
28134a096dbSkasunath  *
28234a096dbSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
28334a096dbSkasunath  * @return true if the encoded segment is an array element. Else false.
28434a096dbSkasunath  */
bejIsArrayElement(const struct BejHandleTypeFuncParam * params)28534a096dbSkasunath static bool bejIsArrayElement(const struct BejHandleTypeFuncParam* params)
28634a096dbSkasunath {
28734a096dbSkasunath     // If the encoded segment enters an array section, we are adding a
28834a096dbSkasunath     // BejSectionArray to the stack. Therefore if the stack is empty, encoded
28934a096dbSkasunath     // segment cannot be an array element.
29034a096dbSkasunath     if (params->stackCallback->stackEmpty(params->stackDataPtr))
29134a096dbSkasunath     {
29234a096dbSkasunath         return false;
29334a096dbSkasunath     }
29434a096dbSkasunath     const struct BejStackProperty* const ending =
29534a096dbSkasunath         params->stackCallback->stackPeek(params->stackDataPtr);
29634a096dbSkasunath     // If the stack top element holds a BejSectionArray, encoded segment is
29734a096dbSkasunath     // an array element.
29834a096dbSkasunath     return ending->sectionType == bejSectionArray;
29934a096dbSkasunath }
30034a096dbSkasunath 
30134a096dbSkasunath /**
30234a096dbSkasunath  * @brief Decodes a BejSet type SFLV BEJ tuple.
30334a096dbSkasunath  *
304b8ca265aSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
30534a096dbSkasunath  * @return 0 if successful.
30634a096dbSkasunath  */
bejHandleBejSet(struct BejHandleTypeFuncParam * params)30734a096dbSkasunath static int bejHandleBejSet(struct BejHandleTypeFuncParam* params)
30834a096dbSkasunath {
30934a096dbSkasunath     uint16_t sequenceNumber = params->sflv.tupleS.sequenceNumber;
31034a096dbSkasunath     // Check whether this BejSet is an array element or not.
31134a096dbSkasunath     if (bejIsArrayElement(params))
31234a096dbSkasunath     {
31334a096dbSkasunath         // Dictionary only contains an entry for element 0.
31434a096dbSkasunath         sequenceNumber = 0;
31534a096dbSkasunath     }
31634a096dbSkasunath     const uint8_t* dictionary;
31734a096dbSkasunath     const struct BejDictionaryProperty* prop;
31834a096dbSkasunath     RETURN_IF_IERROR(
31934a096dbSkasunath         bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
32034a096dbSkasunath                                     sequenceNumber, &dictionary, &prop));
32134a096dbSkasunath 
32234a096dbSkasunath     const char* propName = "";
32334a096dbSkasunath     if (params->state.addPropertyName)
32434a096dbSkasunath     {
32534a096dbSkasunath         propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
32634a096dbSkasunath                                           prop->nameLength);
32734a096dbSkasunath     }
32834a096dbSkasunath 
32934a096dbSkasunath     RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackSetStart,
33034a096dbSkasunath                               propName, params->callbacksDataPtr);
33134a096dbSkasunath 
332c14fab6eSkasunath     // Move the offset to the next SFLV tuple (or end). Make sure that this is
333c14fab6eSkasunath     // called before calling bejProcessEnding.
334c14fab6eSkasunath     params->state.encodedStreamOffset = bejGetFirstTupleOffset(params);
335c14fab6eSkasunath 
3360aa36d82Skasunath     uint64_t elements = bejGetNnint(params->sflv.value);
33734a096dbSkasunath     // If its an empty set, we are done here.
33834a096dbSkasunath     if (elements == 0)
33934a096dbSkasunath     {
34034a096dbSkasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackSetEnd,
34134a096dbSkasunath                                   params->callbacksDataPtr);
342c14fab6eSkasunath         // Since this is an ending of a property (empty array), we should call
343c14fab6eSkasunath         // bejProcessEnding. Unless the whole JSON object is an empty set (which
344c14fab6eSkasunath         // shouldn't be the case), stack cannot be empty.
345c14fab6eSkasunath         bejProcessEnding(params, /*canBeEmpty=*/false);
346c14fab6eSkasunath         return 0;
34734a096dbSkasunath     }
348c14fab6eSkasunath 
34934a096dbSkasunath     // Update the states for the next encoding segment.
35034a096dbSkasunath     struct BejStackProperty newEnding = {
35134a096dbSkasunath         .sectionType = bejSectionSet,
35234a096dbSkasunath         .addPropertyName = params->state.addPropertyName,
35334a096dbSkasunath         .mainDictPropOffset = params->state.mainDictPropOffset,
35434a096dbSkasunath         .annoDictPropOffset = params->state.annoDictPropOffset,
35534a096dbSkasunath         .streamEndOffset = params->sflv.valueEndOffset,
35634a096dbSkasunath     };
35734a096dbSkasunath     RETURN_IF_IERROR(
35834a096dbSkasunath         params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
35934a096dbSkasunath     params->state.addPropertyName = true;
36034a096dbSkasunath     if (params->sflv.tupleS.schema == bejAnnotation)
36134a096dbSkasunath     {
36234a096dbSkasunath         // Since this set is an annotated type, we need to advance the
36334a096dbSkasunath         // annotation dictionary for decoding the next segment.
36434a096dbSkasunath         params->state.annoDictPropOffset = prop->childPointerOffset;
36534a096dbSkasunath     }
36634a096dbSkasunath     else
36734a096dbSkasunath     {
36834a096dbSkasunath         params->state.mainDictPropOffset = prop->childPointerOffset;
36934a096dbSkasunath     }
37034a096dbSkasunath     return 0;
37134a096dbSkasunath }
37234a096dbSkasunath 
37334a096dbSkasunath /**
374e2260cd6Skasunath  * @brief Decodes a BejArray type SFLV BEJ tuple.
375e2260cd6Skasunath  *
376b8ca265aSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
377e2260cd6Skasunath  * @return 0 if successful.
378e2260cd6Skasunath  */
bejHandleBejArray(struct BejHandleTypeFuncParam * params)379e2260cd6Skasunath static int bejHandleBejArray(struct BejHandleTypeFuncParam* params)
380e2260cd6Skasunath {
381e2260cd6Skasunath     const uint8_t* dictionary;
382e2260cd6Skasunath     const struct BejDictionaryProperty* prop;
383e2260cd6Skasunath     RETURN_IF_IERROR(bejGetDictionaryAndProperty(
384e2260cd6Skasunath         params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber,
385e2260cd6Skasunath         &dictionary, &prop));
386e2260cd6Skasunath 
387e2260cd6Skasunath     const char* propName = "";
388e2260cd6Skasunath     if (params->state.addPropertyName)
389e2260cd6Skasunath     {
390e2260cd6Skasunath         propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
391e2260cd6Skasunath                                           prop->nameLength);
392e2260cd6Skasunath     }
393e2260cd6Skasunath 
394e2260cd6Skasunath     RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayStart,
395e2260cd6Skasunath                               propName, params->callbacksDataPtr);
396e2260cd6Skasunath 
397c14fab6eSkasunath     // Move the offset to the next SFLV tuple (or end). Make sure that this is
398c14fab6eSkasunath     // called before calling bejProcessEnding.
399c14fab6eSkasunath     params->state.encodedStreamOffset = bejGetFirstTupleOffset(params);
400c14fab6eSkasunath 
4010aa36d82Skasunath     uint64_t elements = bejGetNnint(params->sflv.value);
402e2260cd6Skasunath     // If its an empty array, we are done here.
403e2260cd6Skasunath     if (elements == 0)
404e2260cd6Skasunath     {
405e2260cd6Skasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayEnd,
406e2260cd6Skasunath                                   params->callbacksDataPtr);
407c14fab6eSkasunath         // Since this is an ending of a property (empty array), we should call
408c14fab6eSkasunath         // bejProcessEnding. Stack cannot be empty since there should be at
409c14fab6eSkasunath         // least 1 parent in the stack.
410c14fab6eSkasunath         bejProcessEnding(params, /*canBeEmpty=*/false);
411c14fab6eSkasunath         return 0;
412e2260cd6Skasunath     }
413c14fab6eSkasunath 
414e2260cd6Skasunath     // Update the state for next segment decoding.
415e2260cd6Skasunath     struct BejStackProperty newEnding = {
416e2260cd6Skasunath         .sectionType = bejSectionArray,
417e2260cd6Skasunath         .addPropertyName = params->state.addPropertyName,
418e2260cd6Skasunath         .mainDictPropOffset = params->state.mainDictPropOffset,
419e2260cd6Skasunath         .annoDictPropOffset = params->state.annoDictPropOffset,
420e2260cd6Skasunath         .streamEndOffset = params->sflv.valueEndOffset,
421e2260cd6Skasunath     };
422e2260cd6Skasunath     RETURN_IF_IERROR(
423e2260cd6Skasunath         params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
424e2260cd6Skasunath     // We do not add property names for array elements.
425e2260cd6Skasunath     params->state.addPropertyName = false;
426e2260cd6Skasunath     if (params->sflv.tupleS.schema == bejAnnotation)
427e2260cd6Skasunath     {
428e2260cd6Skasunath         // Since this array is an annotated type, we need to advance the
429e2260cd6Skasunath         // annotation dictionary for decoding the next segment.
430e2260cd6Skasunath         params->state.annoDictPropOffset = prop->childPointerOffset;
431e2260cd6Skasunath     }
432e2260cd6Skasunath     else
433e2260cd6Skasunath     {
434e2260cd6Skasunath         params->state.mainDictPropOffset = prop->childPointerOffset;
435e2260cd6Skasunath     }
436e2260cd6Skasunath     return 0;
437e2260cd6Skasunath }
438e2260cd6Skasunath 
439e2260cd6Skasunath /**
440e2260cd6Skasunath  * @brief Decodes a BejNull type SFLV BEJ tuple.
441e2260cd6Skasunath  *
442b8ca265aSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
443e2260cd6Skasunath  * @return 0 if successful.
444e2260cd6Skasunath  */
bejHandleBejNull(struct BejHandleTypeFuncParam * params)445e2260cd6Skasunath static int bejHandleBejNull(struct BejHandleTypeFuncParam* params)
446e2260cd6Skasunath {
447e2260cd6Skasunath     const char* propName = bejGetPropName(params);
448e2260cd6Skasunath     RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, propName,
449e2260cd6Skasunath                               params->callbacksDataPtr);
450e2260cd6Skasunath     params->state.encodedStreamOffset = params->sflv.valueEndOffset;
451e2260cd6Skasunath     return bejProcessEnding(params, /*canBeEmpty=*/false);
452e2260cd6Skasunath }
453e2260cd6Skasunath 
454e2260cd6Skasunath /**
45534a096dbSkasunath  * @brief Decodes a BejInteger type SFLV BEJ tuple.
45634a096dbSkasunath  *
457b8ca265aSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
45834a096dbSkasunath  * @return 0 if successful.
45934a096dbSkasunath  */
bejHandleBejInteger(struct BejHandleTypeFuncParam * params)46034a096dbSkasunath static int bejHandleBejInteger(struct BejHandleTypeFuncParam* params)
46134a096dbSkasunath {
462e2260cd6Skasunath     const char* propName = bejGetPropName(params);
46334a096dbSkasunath 
46434a096dbSkasunath     if (params->sflv.valueLength == 0)
46534a096dbSkasunath     {
46634a096dbSkasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
46734a096dbSkasunath                                   propName, params->callbacksDataPtr);
46834a096dbSkasunath     }
46934a096dbSkasunath     else
47034a096dbSkasunath     {
47134a096dbSkasunath         RETURN_IF_CALLBACK_IERROR(
47234a096dbSkasunath             params->decodedCallback->callbackInteger, propName,
47334a096dbSkasunath             bejGetIntegerValue(params->sflv.value, params->sflv.valueLength),
47434a096dbSkasunath             params->callbacksDataPtr);
47534a096dbSkasunath     }
47634a096dbSkasunath     params->state.encodedStreamOffset = params->sflv.valueEndOffset;
47734a096dbSkasunath     return bejProcessEnding(params, /*canBeEmpty=*/false);
47834a096dbSkasunath }
47934a096dbSkasunath 
48034a096dbSkasunath /**
481b8ca265aSkasunath  * @brief Decodes a BejEnum type SFLV BEJ tuple.
482b8ca265aSkasunath  *
483b8ca265aSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
484b8ca265aSkasunath  * @return 0 if successful.
485b8ca265aSkasunath  */
bejHandleBejEnum(struct BejHandleTypeFuncParam * params)486b8ca265aSkasunath static int bejHandleBejEnum(struct BejHandleTypeFuncParam* params)
487b8ca265aSkasunath {
488b8ca265aSkasunath     uint16_t sequenceNumber = params->sflv.tupleS.sequenceNumber;
489b8ca265aSkasunath     if (bejIsArrayElement(params))
490b8ca265aSkasunath     {
491b8ca265aSkasunath         sequenceNumber = 0;
492b8ca265aSkasunath     }
493b8ca265aSkasunath     const uint8_t* dictionary;
494b8ca265aSkasunath     const struct BejDictionaryProperty* prop;
495b8ca265aSkasunath     RETURN_IF_IERROR(
496b8ca265aSkasunath         bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
497b8ca265aSkasunath                                     sequenceNumber, &dictionary, &prop));
498b8ca265aSkasunath 
499b8ca265aSkasunath     const char* propName = "";
500b8ca265aSkasunath     if (params->state.addPropertyName)
501b8ca265aSkasunath     {
502b8ca265aSkasunath         propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
503b8ca265aSkasunath                                           prop->nameLength);
504b8ca265aSkasunath     }
505b8ca265aSkasunath 
506b8ca265aSkasunath     if (params->sflv.valueLength == 0)
507b8ca265aSkasunath     {
508b8ca265aSkasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
509b8ca265aSkasunath                                   propName, params->callbacksDataPtr);
510b8ca265aSkasunath     }
511b8ca265aSkasunath     else
512b8ca265aSkasunath     {
513b8ca265aSkasunath         // Get the string for enum value.
514b8ca265aSkasunath         uint16_t enumValueSequenceN =
5150aa36d82Skasunath             (uint16_t)(bejGetNnint(params->sflv.value));
516b8ca265aSkasunath         const struct BejDictionaryProperty* enumValueProp;
517b8ca265aSkasunath         RETURN_IF_IERROR(
518b8ca265aSkasunath             bejDictGetProperty(dictionary, prop->childPointerOffset,
519b8ca265aSkasunath                                enumValueSequenceN, &enumValueProp));
520b8ca265aSkasunath         const char* enumValueName = bejDictGetPropertyName(
521b8ca265aSkasunath             dictionary, enumValueProp->nameOffset, enumValueProp->nameLength);
522b8ca265aSkasunath 
523b8ca265aSkasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackEnum,
524b8ca265aSkasunath                                   propName, enumValueName,
525b8ca265aSkasunath                                   params->callbacksDataPtr);
526b8ca265aSkasunath     }
527b8ca265aSkasunath     // Update the offset to point to the next possible SFLV tuple.
528b8ca265aSkasunath     params->state.encodedStreamOffset = params->sflv.valueEndOffset;
529b8ca265aSkasunath     return bejProcessEnding(params, /*canBeEmpty=*/false);
530b8ca265aSkasunath }
531b8ca265aSkasunath 
532b8ca265aSkasunath /**
533e2260cd6Skasunath  * @brief Decodes a BejString type SFLV BEJ tuple.
534e2260cd6Skasunath  *
535b8ca265aSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
536e2260cd6Skasunath  * @return 0 if successful.
537e2260cd6Skasunath  */
bejHandleBejString(struct BejHandleTypeFuncParam * params)538e2260cd6Skasunath static int bejHandleBejString(struct BejHandleTypeFuncParam* params)
539e2260cd6Skasunath {
540e2260cd6Skasunath     // TODO: Handle deferred bindings.
541e2260cd6Skasunath     const char* propName = bejGetPropName(params);
542e2260cd6Skasunath 
543e2260cd6Skasunath     if (params->sflv.valueLength == 0)
544e2260cd6Skasunath     {
545e2260cd6Skasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
546e2260cd6Skasunath                                   propName, params->callbacksDataPtr);
547e2260cd6Skasunath     }
548e2260cd6Skasunath     else
549e2260cd6Skasunath     {
550e2260cd6Skasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackString,
551e2260cd6Skasunath                                   propName, (const char*)(params->sflv.value),
552e2260cd6Skasunath                                   params->callbacksDataPtr);
553e2260cd6Skasunath     }
554e2260cd6Skasunath     params->state.encodedStreamOffset = params->sflv.valueEndOffset;
555e2260cd6Skasunath     return bejProcessEnding(params, /*canBeEmpty=*/false);
556e2260cd6Skasunath }
557e2260cd6Skasunath 
558e2260cd6Skasunath /**
559b8ca265aSkasunath  * @brief Decodes a BejReal type SFLV BEJ tuple.
560b8ca265aSkasunath  *
561b8ca265aSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
562b8ca265aSkasunath  * @return 0 if successful.
563b8ca265aSkasunath  */
bejHandleBejReal(struct BejHandleTypeFuncParam * params)564b8ca265aSkasunath static int bejHandleBejReal(struct BejHandleTypeFuncParam* params)
565b8ca265aSkasunath {
566b8ca265aSkasunath     const char* propName = bejGetPropName(params);
567b8ca265aSkasunath 
568b8ca265aSkasunath     if (params->sflv.valueLength == 0)
569b8ca265aSkasunath     {
570b8ca265aSkasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
571b8ca265aSkasunath                                   propName, params->callbacksDataPtr);
572b8ca265aSkasunath     }
573b8ca265aSkasunath     else
574b8ca265aSkasunath     {
575b8ca265aSkasunath         // Real value has the following format.
576b8ca265aSkasunath         // nnint      - Length of whole
577b8ca265aSkasunath         // bejInteger - whole (includes sign for the overall real number)
578b8ca265aSkasunath         // nnint      - Leading zero count for fract
579b8ca265aSkasunath         // nnint      - fract
580b8ca265aSkasunath         // nnint      - Length of exp
581b8ca265aSkasunath         // bejInteger - exp (includes sign for the exponent)
5820aa36d82Skasunath         uint8_t wholeByteLen = (uint8_t)bejGetNnint(params->sflv.value);
583*435526a5SPatrick Williams         const uint8_t* wholeBejInt = params->sflv.value +
584*435526a5SPatrick Williams                                      bejGetNnintSize(params->sflv.value);
585b8ca265aSkasunath         const uint8_t* fractZeroCountNnint = wholeBejInt + wholeByteLen;
586*435526a5SPatrick Williams         const uint8_t* fractNnint = fractZeroCountNnint +
587*435526a5SPatrick Williams                                     bejGetNnintSize(fractZeroCountNnint);
5880aa36d82Skasunath         const uint8_t* lenExpNnint = fractNnint + bejGetNnintSize(fractNnint);
5890aa36d82Skasunath         const uint8_t* expBejInt = lenExpNnint + bejGetNnintSize(lenExpNnint);
590b8ca265aSkasunath 
591b8ca265aSkasunath         struct BejReal realValue;
592b8ca265aSkasunath         realValue.whole = bejGetIntegerValue(wholeBejInt, wholeByteLen);
5930aa36d82Skasunath         realValue.zeroCount = bejGetNnint(fractZeroCountNnint);
5940aa36d82Skasunath         realValue.fract = bejGetNnint(fractNnint);
5950aa36d82Skasunath         realValue.expLen = (uint8_t)bejGetNnint(lenExpNnint);
596b8ca265aSkasunath         if (realValue.expLen != 0)
597b8ca265aSkasunath         {
598b8ca265aSkasunath             realValue.exp = bejGetIntegerValue(
5990aa36d82Skasunath                 expBejInt, (uint8_t)bejGetNnint(lenExpNnint));
600b8ca265aSkasunath         }
601b8ca265aSkasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackReal,
602b8ca265aSkasunath                                   propName, &realValue,
603b8ca265aSkasunath                                   params->callbacksDataPtr);
604b8ca265aSkasunath     }
605b8ca265aSkasunath     params->state.encodedStreamOffset = params->sflv.valueEndOffset;
606b8ca265aSkasunath     return bejProcessEnding(params, /*canBeEmpty=*/false);
607b8ca265aSkasunath }
608b8ca265aSkasunath 
609b8ca265aSkasunath /**
610e2260cd6Skasunath  * @brief Decodes a BejBoolean type SFLV BEJ tuple.
611e2260cd6Skasunath  *
612b8ca265aSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
613e2260cd6Skasunath  * @return 0 if successful.
614e2260cd6Skasunath  */
bejHandleBejBoolean(struct BejHandleTypeFuncParam * params)615e2260cd6Skasunath static int bejHandleBejBoolean(struct BejHandleTypeFuncParam* params)
616e2260cd6Skasunath {
617e2260cd6Skasunath     const char* propName = bejGetPropName(params);
618e2260cd6Skasunath 
619e2260cd6Skasunath     if (params->sflv.valueLength == 0)
620e2260cd6Skasunath     {
621e2260cd6Skasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
622e2260cd6Skasunath                                   propName, params->callbacksDataPtr);
623e2260cd6Skasunath     }
624e2260cd6Skasunath     else
625e2260cd6Skasunath     {
626e2260cd6Skasunath         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackBool,
627e2260cd6Skasunath                                   propName, *(params->sflv.value) > 0,
628e2260cd6Skasunath                                   params->callbacksDataPtr);
629e2260cd6Skasunath     }
630e2260cd6Skasunath     params->state.encodedStreamOffset = params->sflv.valueEndOffset;
631e2260cd6Skasunath     return bejProcessEnding(params, /*canBeEmpty=*/false);
632e2260cd6Skasunath }
633e2260cd6Skasunath 
634e2260cd6Skasunath /**
635b8ca265aSkasunath  * @brief Decodes a BejPropertyAnnotation type SFLV BEJ tuple.
636b8ca265aSkasunath  *
637b8ca265aSkasunath  * @param[in] params - a valid BejHandleTypeFuncParam struct.
638b8ca265aSkasunath  * @return 0 if successful.
639b8ca265aSkasunath  */
bejHandleBejPropertyAnnotation(struct BejHandleTypeFuncParam * params)640b8ca265aSkasunath static int bejHandleBejPropertyAnnotation(struct BejHandleTypeFuncParam* params)
641b8ca265aSkasunath {
642b8ca265aSkasunath     // TODO: Handle colon-delimited string values.
643b8ca265aSkasunath 
644b8ca265aSkasunath     // Property annotation has the form OuterProperty@Annotation. First
645b8ca265aSkasunath     // processing the outer property name.
646b8ca265aSkasunath     const uint8_t* outerDictionary;
647b8ca265aSkasunath     const struct BejDictionaryProperty* outerProp;
648b8ca265aSkasunath     RETURN_IF_IERROR(bejGetDictionaryAndProperty(
649b8ca265aSkasunath         params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber,
650b8ca265aSkasunath         &outerDictionary, &outerProp));
651b8ca265aSkasunath 
652b8ca265aSkasunath     const char* propName = bejDictGetPropertyName(
653b8ca265aSkasunath         outerDictionary, outerProp->nameOffset, outerProp->nameLength);
654b8ca265aSkasunath     RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackAnnotation,
655b8ca265aSkasunath                               propName, params->callbacksDataPtr);
656b8ca265aSkasunath 
657b8ca265aSkasunath     // Mark the ending of the property annotation.
658b8ca265aSkasunath     struct BejStackProperty newEnding = {
659b8ca265aSkasunath         .sectionType = bejSectionNoType,
660b8ca265aSkasunath         .addPropertyName = params->state.addPropertyName,
661b8ca265aSkasunath         .mainDictPropOffset = params->state.mainDictPropOffset,
662b8ca265aSkasunath         .annoDictPropOffset = params->state.annoDictPropOffset,
663b8ca265aSkasunath         .streamEndOffset = params->sflv.valueEndOffset,
664b8ca265aSkasunath     };
665b8ca265aSkasunath     // Update the states for the next encoding segment.
666b8ca265aSkasunath     RETURN_IF_IERROR(
667b8ca265aSkasunath         params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
668b8ca265aSkasunath     params->state.addPropertyName = true;
669b8ca265aSkasunath     // We might have to change this for nested annotations.
670b8ca265aSkasunath     params->state.mainDictPropOffset = outerProp->childPointerOffset;
671b8ca265aSkasunath     // Point to the start of the value for next decoding.
672*435526a5SPatrick Williams     params->state.encodedStreamOffset = params->sflv.valueEndOffset -
673*435526a5SPatrick Williams                                         params->sflv.valueLength;
674b8ca265aSkasunath     return 0;
675b8ca265aSkasunath }
676b8ca265aSkasunath 
677b8ca265aSkasunath /**
67864cb1973Skasunath  * @brief Decodes an encoded bej stream.
67964cb1973Skasunath  *
68064cb1973Skasunath  * @param[in] schemaDictionary - main schema dictionary to use.
68164cb1973Skasunath  * @param[in] annotationDictionary - annotation dictionary
68264cb1973Skasunath  * @param[in] enStream - encoded stream without the PLDM header.
68364cb1973Skasunath  * @param[in] streamLen - length of the enStream.
68464cb1973Skasunath  * @param[in] stackCallback - callbacks for stack handlers.
68564cb1973Skasunath  * @param[in] decodedCallback - callbacks for extracting decoded properties.
68664cb1973Skasunath  * @param[in] callbacksDataPtr - data pointer to pass to decoded callbacks. This
68764cb1973Skasunath  * can be used pass additional data.
68864cb1973Skasunath  * @param[in] stackDataPtr - data pointer to pass to stack callbacks. This can
68964cb1973Skasunath  * be used pass additional data.
69064cb1973Skasunath  *
69164cb1973Skasunath  * @return 0 if successful.
69264cb1973Skasunath  */
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)69364cb1973Skasunath static int bejDecode(const uint8_t* schemaDictionary,
69464cb1973Skasunath                      const uint8_t* annotationDictionary,
69564cb1973Skasunath                      const uint8_t* enStream, uint32_t streamLen,
69664cb1973Skasunath                      const struct BejStackCallback* stackCallback,
69764cb1973Skasunath                      const struct BejDecodedCallback* decodedCallback,
69864cb1973Skasunath                      void* callbacksDataPtr, void* stackDataPtr)
69964cb1973Skasunath {
70064cb1973Skasunath     struct BejHandleTypeFuncParam params = {
70164cb1973Skasunath         .state =
70264cb1973Skasunath             {
70364cb1973Skasunath                 // We only add names of set properties. We don't use names for
70464cb1973Skasunath                 // array
70564cb1973Skasunath                 // properties. Here we are omitting the name of the root set.
70664cb1973Skasunath                 .addPropertyName = false,
70764cb1973Skasunath                 // At start, parent property from the main dictionary is the
70864cb1973Skasunath                 // first property.
70964cb1973Skasunath                 .mainDictPropOffset = bejDictGetPropertyHeadOffset(),
71064cb1973Skasunath                 .annoDictPropOffset = bejDictGetFirstAnnotatedPropertyOffset(),
71164cb1973Skasunath                 // Current location of the encoded segment we are processing.
71264cb1973Skasunath                 .encodedStreamOffset = 0,
71364cb1973Skasunath                 .encodedSubStream = enStream,
71464cb1973Skasunath             },
71564cb1973Skasunath         .mainDictionary = schemaDictionary,
71664cb1973Skasunath         .annotDictionary = annotationDictionary,
71764cb1973Skasunath         .decodedCallback = decodedCallback,
71864cb1973Skasunath         .stackCallback = stackCallback,
71964cb1973Skasunath         .callbacksDataPtr = callbacksDataPtr,
72064cb1973Skasunath         .stackDataPtr = stackDataPtr,
72164cb1973Skasunath     };
72264cb1973Skasunath 
72364cb1973Skasunath     while (params.state.encodedStreamOffset < streamLen)
72464cb1973Skasunath     {
72564cb1973Skasunath         // Go to the next encoded segment in the encoded stream.
726*435526a5SPatrick Williams         params.state.encodedSubStream = enStream +
727*435526a5SPatrick Williams                                         params.state.encodedStreamOffset;
72864cb1973Skasunath         bejInitSFLVStruct(&params);
72964cb1973Skasunath 
73064cb1973Skasunath         if (params.sflv.format.readOnlyProperty)
73164cb1973Skasunath         {
73264cb1973Skasunath             RETURN_IF_CALLBACK_IERROR(
73364cb1973Skasunath                 params.decodedCallback->callbackReadonlyProperty,
73464cb1973Skasunath                 params.sflv.tupleS.sequenceNumber, params.callbacksDataPtr);
73564cb1973Skasunath         }
73664cb1973Skasunath 
73764cb1973Skasunath         // TODO: Handle nullable property types. These are indicated by
73864cb1973Skasunath         // params.sflv.format.nullableProperty
73964cb1973Skasunath         switch (params.sflv.format.principalDataType)
74064cb1973Skasunath         {
74164cb1973Skasunath             case bejSet:
74234a096dbSkasunath                 RETURN_IF_IERROR(bejHandleBejSet(&params));
74364cb1973Skasunath                 break;
74464cb1973Skasunath             case bejArray:
745e2260cd6Skasunath                 RETURN_IF_IERROR(bejHandleBejArray(&params));
74664cb1973Skasunath                 break;
74764cb1973Skasunath             case bejNull:
748e2260cd6Skasunath                 RETURN_IF_IERROR(bejHandleBejNull(&params));
74964cb1973Skasunath                 break;
75064cb1973Skasunath             case bejInteger:
75134a096dbSkasunath                 RETURN_IF_IERROR(bejHandleBejInteger(&params));
75264cb1973Skasunath                 break;
75364cb1973Skasunath             case bejEnum:
754b8ca265aSkasunath                 RETURN_IF_IERROR(bejHandleBejEnum(&params));
75564cb1973Skasunath                 break;
75664cb1973Skasunath             case bejString:
757e2260cd6Skasunath                 RETURN_IF_IERROR(bejHandleBejString(&params));
75864cb1973Skasunath                 break;
75964cb1973Skasunath             case bejReal:
760b8ca265aSkasunath                 RETURN_IF_IERROR(bejHandleBejReal(&params));
76164cb1973Skasunath                 break;
76264cb1973Skasunath             case bejBoolean:
763e2260cd6Skasunath                 RETURN_IF_IERROR(bejHandleBejBoolean(&params));
76464cb1973Skasunath                 break;
76564cb1973Skasunath             case bejBytestring:
76664cb1973Skasunath                 // TODO: Add support for BejBytestring decoding.
76764cb1973Skasunath                 fprintf(stderr, "No BejBytestring support\n");
76864cb1973Skasunath                 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
76964cb1973Skasunath                 break;
77064cb1973Skasunath             case bejChoice:
77164cb1973Skasunath                 // TODO: Add support for BejChoice decoding.
77264cb1973Skasunath                 fprintf(stderr, "No BejChoice support\n");
77364cb1973Skasunath                 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
77464cb1973Skasunath                 break;
77564cb1973Skasunath             case bejPropertyAnnotation:
776b8ca265aSkasunath                 RETURN_IF_IERROR(bejHandleBejPropertyAnnotation(&params));
77764cb1973Skasunath                 break;
77864cb1973Skasunath             case bejResourceLink:
77964cb1973Skasunath                 // TODO: Add support for BejResourceLink decoding.
78064cb1973Skasunath                 fprintf(stderr, "No BejResourceLink support\n");
78164cb1973Skasunath                 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
78264cb1973Skasunath                 break;
78364cb1973Skasunath             case bejResourceLinkExpansion:
78464cb1973Skasunath                 // TODO: Add support for BejResourceLinkExpansion decoding.
78564cb1973Skasunath                 fprintf(stderr, "No BejResourceLinkExpansion support\n");
78664cb1973Skasunath                 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
78764cb1973Skasunath                 break;
78864cb1973Skasunath             default:
78964cb1973Skasunath                 break;
79064cb1973Skasunath         }
79164cb1973Skasunath     }
79234a096dbSkasunath     RETURN_IF_IERROR(bejProcessEnding(&params, /*canBeEmpty=*/true));
79364cb1973Skasunath     if (!params.stackCallback->stackEmpty(params.stackDataPtr))
79464cb1973Skasunath     {
79564cb1973Skasunath         fprintf(stderr, "Ending stack should be empty but its not. Something "
79664cb1973Skasunath                         "must have gone wrong with the encoding\n");
79764cb1973Skasunath         return bejErrorUnknown;
79864cb1973Skasunath     }
79964cb1973Skasunath     return 0;
80064cb1973Skasunath }
80164cb1973Skasunath 
80264cb1973Skasunath /**
80364cb1973Skasunath  * @brief Check if a bej version is supported by this decoder
80464cb1973Skasunath  *
805b8ca265aSkasunath  * @param[in] bejVersion - the bej version in the received encoded stream
80664cb1973Skasunath  * @return true if supported.
80764cb1973Skasunath  */
bejIsSupported(uint32_t bejVersion)80864cb1973Skasunath static bool bejIsSupported(uint32_t bejVersion)
80964cb1973Skasunath {
81064cb1973Skasunath     for (uint32_t i = 0; i < sizeof(supportedBejVersions) / sizeof(uint32_t);
81164cb1973Skasunath          ++i)
81264cb1973Skasunath     {
81364cb1973Skasunath         if (bejVersion == supportedBejVersions[i])
81464cb1973Skasunath         {
81564cb1973Skasunath             return true;
81664cb1973Skasunath         }
81764cb1973Skasunath     }
81864cb1973Skasunath     return false;
81964cb1973Skasunath }
82064cb1973Skasunath 
bejDecodePldmBlock(const struct BejDictionaries * dictionaries,const uint8_t * encodedPldmBlock,uint32_t blockLength,const struct BejStackCallback * stackCallback,const struct BejDecodedCallback * decodedCallback,void * callbacksDataPtr,void * stackDataPtr)82164cb1973Skasunath int bejDecodePldmBlock(const struct BejDictionaries* dictionaries,
82264cb1973Skasunath                        const uint8_t* encodedPldmBlock, uint32_t blockLength,
82364cb1973Skasunath                        const struct BejStackCallback* stackCallback,
82464cb1973Skasunath                        const struct BejDecodedCallback* decodedCallback,
82564cb1973Skasunath                        void* callbacksDataPtr, void* stackDataPtr)
82664cb1973Skasunath {
82734a096dbSkasunath     NULL_CHECK(dictionaries, "dictionaries");
82834a096dbSkasunath     NULL_CHECK(dictionaries->schemaDictionary, "schemaDictionary");
82934a096dbSkasunath     NULL_CHECK(dictionaries->annotationDictionary, "annotationDictionary");
83034a096dbSkasunath 
83134a096dbSkasunath     NULL_CHECK(encodedPldmBlock, "encodedPldmBlock");
83234a096dbSkasunath 
83334a096dbSkasunath     NULL_CHECK(stackCallback, "stackCallback");
83434a096dbSkasunath     NULL_CHECK(stackCallback->stackEmpty, "stackEmpty");
83534a096dbSkasunath     NULL_CHECK(stackCallback->stackPeek, "stackPeek");
83634a096dbSkasunath     NULL_CHECK(stackCallback->stackPop, "stackPop");
83734a096dbSkasunath     NULL_CHECK(stackCallback->stackPush, "stackPush");
83834a096dbSkasunath 
83934a096dbSkasunath     NULL_CHECK(decodedCallback, "decodedCallback");
84034a096dbSkasunath 
84164cb1973Skasunath     uint32_t pldmHeaderSize = sizeof(struct BejPldmBlockHeader);
84264cb1973Skasunath     if (blockLength < pldmHeaderSize)
84364cb1973Skasunath     {
84464cb1973Skasunath         fprintf(stderr, "Invalid pldm block size: %u\n", blockLength);
84564cb1973Skasunath         return bejErrorInvalidSize;
84664cb1973Skasunath     }
84764cb1973Skasunath 
84864cb1973Skasunath     const struct BejPldmBlockHeader* pldmHeader =
84964cb1973Skasunath         (const struct BejPldmBlockHeader*)encodedPldmBlock;
85064cb1973Skasunath 
85164cb1973Skasunath     if (!bejIsSupported(pldmHeader->bejVersion))
85264cb1973Skasunath     {
85364cb1973Skasunath         fprintf(stderr, "Bej decoder doesn't support the bej version: %u\n",
85464cb1973Skasunath                 pldmHeader->bejVersion);
85564cb1973Skasunath         return bejErrorNotSuppoted;
85664cb1973Skasunath     }
85764cb1973Skasunath 
85864cb1973Skasunath     if (pldmHeader->schemaClass == bejAnnotationSchemaClass)
85964cb1973Skasunath     {
86064cb1973Skasunath         fprintf(stderr,
86164cb1973Skasunath                 "Encoder schema class cannot be BejAnnotationSchemaClass\n");
86264cb1973Skasunath         return bejErrorNotSuppoted;
86364cb1973Skasunath     }
86464cb1973Skasunath     // TODO: Add support for CollectionMemberType schema class.
86564cb1973Skasunath     if (pldmHeader->schemaClass == bejCollectionMemberTypeSchemaClass)
86664cb1973Skasunath     {
86764cb1973Skasunath         fprintf(stderr, "Decoder doesn't support "
86834a096dbSkasunath                         "bejCollectionMemberTypeSchemaClass yet.\n");
86964cb1973Skasunath         return bejErrorNotSuppoted;
87064cb1973Skasunath     }
87164cb1973Skasunath     // TODO: Add support for Error schema class.
87264cb1973Skasunath     if (pldmHeader->schemaClass == bejErrorSchemaClass)
87364cb1973Skasunath     {
87464cb1973Skasunath         fprintf(stderr, "Decoder doesn't support BejErrorSchemaClass yet.\n");
87564cb1973Skasunath         return bejErrorNotSuppoted;
87664cb1973Skasunath     }
87764cb1973Skasunath 
87864cb1973Skasunath     // Skip the PLDM header.
87964cb1973Skasunath     const uint8_t* enStream = encodedPldmBlock + pldmHeaderSize;
88064cb1973Skasunath     uint32_t streamLen = blockLength - pldmHeaderSize;
88164cb1973Skasunath     return bejDecode(dictionaries->schemaDictionary,
88264cb1973Skasunath                      dictionaries->annotationDictionary, enStream, streamLen,
88364cb1973Skasunath                      stackCallback, decodedCallback, callbacksDataPtr,
88464cb1973Skasunath                      stackDataPtr);
88564cb1973Skasunath }
886