xref: /openbmc/libbej/src/bej_decoder_core.c (revision be27f2e9)
1 #include "bej_decoder_core.h"
2 
3 #include "bej_dictionary.h"
4 #include "stdio.h"
5 
6 #include <inttypes.h>
7 #include <stdbool.h>
8 #include <stdlib.h>
9 #include <string.h>
10 
11 // TODO: Support nested annotations for version 0xF1F1F000
12 const uint32_t supportedBejVersions[] = {0xF1F0F000};
13 
14 /**
15  * @brief Call a callback function. If the callback function is NULL, this will
16  * not do anything. If the callback function returns a non-zero value, this will
17  * cause the caller to return with the non-zero status.
18  */
19 #define RETURN_IF_CALLBACK_IERROR(function, ...)                               \
20     do                                                                         \
21     {                                                                          \
22         if ((function) != NULL)                                                \
23         {                                                                      \
24             int __status = ((function)(__VA_ARGS__));                          \
25             if (__status != 0)                                                 \
26             {                                                                  \
27                 return __status;                                               \
28             }                                                                  \
29         }                                                                      \
30     } while (0)
31 
32 /**
33  * @brief Get the integer value from BEJ byte stream.
34  *
35  * @param[in] bytes - valid pointer to a byte stream in little-endian format.
36  * @param[in] numOfBytes - number of bytes belongs to the value. Maximum value
37  * supported is 8 bytes.
38  * @return signed 64bit representation of the value.
39  */
bejGetIntegerValue(const uint8_t * bytes,uint8_t numOfBytes)40 static int64_t bejGetIntegerValue(const uint8_t* bytes, uint8_t numOfBytes)
41 {
42     if (numOfBytes == 0)
43     {
44         return 0;
45     }
46     uint64_t value = bejGetUnsignedInteger(bytes, numOfBytes);
47     uint8_t bitsInVal = numOfBytes * 8;
48     // Since numOfBytes > 0, bitsInVal is non negative.
49     uint64_t mask = (uint64_t)1 << (uint8_t)(bitsInVal - 1);
50     return (int64_t)((value ^ mask) - mask);
51 }
52 
53 /**
54  * @brief Get offsets of SFLV fields with respect to the enSegment start.
55  *
56  * @param[in] enSegment - a valid pointer to a start of a SFLV bejTuple.
57  * @param[out] offsets - this will hold the local offsets.
58  */
bejGetLocalBejSFLVOffsets(const uint8_t * enSegment,struct BejSFLVOffset * offsets)59 static void bejGetLocalBejSFLVOffsets(const uint8_t* enSegment,
60                                       struct BejSFLVOffset* offsets)
61 {
62     // Structure of the SFLV.
63     //   [Number of bytes need to represent the sequence number] - uint8_t
64     //   [SequenceNumber] - multi byte
65     //   [Format] - uint8_t
66     //   [Number of bytes need to represent the value length] - uint8_t
67     //   [Value length] - multi byte
68 
69     // Number of bytes need to represent the sequence number.
70     const uint8_t seqSize = *enSegment;
71     // Start of format.
72     const uint32_t formatOffset = sizeof(uint8_t) + seqSize;
73     // Start of length of the value-length bytes.
74     const uint32_t valueLenNnintOffset = formatOffset + sizeof(uint8_t);
75     // Number of bytes need to represent the value length.
76     const uint8_t valueLengthSize = *(enSegment + valueLenNnintOffset);
77     // Start of the Value.
78     const uint32_t valueOffset =
79         valueLenNnintOffset + sizeof(uint8_t) + valueLengthSize;
80 
81     offsets->formatOffset = formatOffset;
82     offsets->valueLenNnintOffset = valueLenNnintOffset;
83     offsets->valueOffset = valueOffset;
84 }
85 
86 /**
87  * @brief Initialize sflv struct in params struct.
88  *
89  * @param[inout] params - a valid BejHandleTypeFuncParam struct with
90  * params->state.encodedSubStream pointing to the start of the encoded stream
91  * and params->state.encodedStreamOffset pointing to the current bejTuple.
92  */
bejInitSFLVStruct(struct BejHandleTypeFuncParam * params)93 static void bejInitSFLVStruct(struct BejHandleTypeFuncParam* params)
94 {
95     struct BejSFLVOffset localOffset;
96     // Get offsets of different SFLV fields with respect to start of the encoded
97     // segment.
98     bejGetLocalBejSFLVOffsets(params->state.encodedSubStream, &localOffset);
99     struct BejSFLV* sflv = &params->sflv;
100     const uint32_t valueLength = (uint32_t)(bejGetNnint(
101         params->state.encodedSubStream + localOffset.valueLenNnintOffset));
102     // Sequence number itself should be 16bits. Using 32bits for
103     // [sequence_number + schema_type].
104     uint32_t tupleS = (uint32_t)(bejGetNnint(params->state.encodedSubStream));
105     sflv->tupleS.schema = (uint8_t)(tupleS & DICTIONARY_TYPE_MASK);
106     sflv->tupleS.sequenceNumber =
107         (uint16_t)((tupleS & (~DICTIONARY_TYPE_MASK)) >>
108                    DICTIONARY_SEQ_NUM_SHIFT);
109     sflv->format = *(struct BejTupleF*)(params->state.encodedSubStream +
110                                         localOffset.formatOffset);
111     sflv->valueLength = valueLength;
112     sflv->valueEndOffset = params->state.encodedStreamOffset +
113                            localOffset.valueOffset + valueLength;
114     sflv->value = params->state.encodedSubStream + localOffset.valueOffset;
115 }
116 
117 /**
118  * @brief Get the offset to the first tuple of a bejArray or bejSet.
119  *
120  * The first part of the value of a bejArray or a bejSet contains an nnint
121  * providing the number of elements/tuples. Offset is with respect to the start
122  * of the encoded stream.
123  *
124  * @param[in] params - a valid BejHandleTypeFuncParam struct.
125  * @return offset with respect to the start of the encoded stream.
126  */
127 static uint32_t
bejGetFirstTupleOffset(const struct BejHandleTypeFuncParam * params)128     bejGetFirstTupleOffset(const struct BejHandleTypeFuncParam* params)
129 {
130     struct BejSFLVOffset localOffset;
131     // Get the offset of the value with respect to the current encoded segment
132     // being decoded.
133     bejGetLocalBejSFLVOffsets(params->state.encodedSubStream, &localOffset);
134     return params->state.encodedStreamOffset + localOffset.valueOffset +
135            bejGetNnintSize(params->sflv.value);
136 }
137 
138 /**
139  * @brief Get the correct property and the dictionary it belongs to.
140  *
141  * @param[in] params - a BejHandleTypeFuncParam struct pointing to valid
142  * dictionaries.
143  * @param[in] schemaType - indicate whether to use the annotation dictionary or
144  * the main schema dictionary.
145  * @param[in] sequenceNumber - sequence number to use for property search. Not
146  * using the params->sflv.tupleS.sequenceNumber from the provided params struct.
147  * @param[out] dictionary - if the function is successful, this will point to a
148  * valid dictionary to be used.
149  * @param[out] prop - if the function is successful, this will point to a valid
150  * property in a dictionary.
151  * @return 0 if successful.
152  */
bejGetDictionaryAndProperty(const struct BejHandleTypeFuncParam * params,uint8_t schemaType,uint32_t sequenceNumber,const uint8_t ** dictionary,const struct BejDictionaryProperty ** prop)153 static int bejGetDictionaryAndProperty(
154     const struct BejHandleTypeFuncParam* params, uint8_t schemaType,
155     uint32_t sequenceNumber, const uint8_t** dictionary,
156     const struct BejDictionaryProperty** prop)
157 {
158     uint16_t dictPropOffset;
159     // We need to pick the correct dictionary.
160     if (schemaType == bejPrimary)
161     {
162         *dictionary = params->mainDictionary;
163         dictPropOffset = params->state.mainDictPropOffset;
164     }
165     else if (schemaType == bejAnnotation)
166     {
167         *dictionary = params->annotDictionary;
168         dictPropOffset = params->state.annoDictPropOffset;
169     }
170     else
171     {
172         fprintf(stderr, "Failed to select a dictionary. schema type: %u\n",
173                 schemaType);
174         return bejErrorInvalidSchemaType;
175     }
176 
177     int ret =
178         bejDictGetProperty(*dictionary, dictPropOffset, sequenceNumber, prop);
179     if (ret != 0)
180     {
181         fprintf(stderr, "Failed to get dictionary property for offset: %u\n",
182                 dictPropOffset);
183         return ret;
184     }
185     return 0;
186 }
187 
188 /**
189  * @brief Find and return the property name of the current encoded segment. If
190  * the params->state.addPropertyName is false, this will return an empty string.
191  *
192  * @param[in] params - a valid populated BejHandleTypeFuncParam.
193  * @return 0 if successful.
194  */
bejGetPropName(struct BejHandleTypeFuncParam * params)195 static const char* bejGetPropName(struct BejHandleTypeFuncParam* params)
196 {
197     const uint8_t* dictionary;
198     const struct BejDictionaryProperty* prop;
199     if (!params->state.addPropertyName ||
200         (bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
201                                      params->sflv.tupleS.sequenceNumber,
202                                      &dictionary, &prop) != 0))
203     {
204         return "";
205     }
206     return bejDictGetPropertyName(dictionary, prop->nameOffset,
207                                   prop->nameLength);
208 }
209 
210 /**
211  * @brief Look for section endings.
212  *
213  * This figures out whether the current encoded segment marks a section
214  * ending. If so, this function will update the decoder state and pop the stack
215  * used to memorize endings. This function should be called after updating the
216  * encodedStreamOffset to the end of decoded SFLV tuple.
217  *
218  * @param[in] params - a valid BejHandleTypeFuncParam which contains the decoder
219  * state.
220  * @param[in] canBeEmpty - if true, the stack being empty is not an error. If
221  * false, stack cannot be empty.
222  * @return 0 if successful.
223  */
bejProcessEnding(struct BejHandleTypeFuncParam * params,bool canBeEmpty)224 static int bejProcessEnding(struct BejHandleTypeFuncParam* params,
225                             bool canBeEmpty)
226 {
227     if (params->stackCallback->stackEmpty(params->stackDataPtr) && !canBeEmpty)
228     {
229         // If bejProcessEnding has been called after adding an appropriate JSON
230         // property, then stack cannot be empty.
231         fprintf(stderr, "Ending stack cannot be empty.\n");
232         return bejErrorUnknown;
233     }
234 
235     while (!params->stackCallback->stackEmpty(params->stackDataPtr))
236     {
237         const struct BejStackProperty* const ending =
238             params->stackCallback->stackPeek(params->stackDataPtr);
239         // Check whether the current offset location matches the expected ending
240         // offset. If so, we are done with that section.
241         if (params->state.encodedStreamOffset == ending->streamEndOffset)
242         {
243             // Since we are going out of a section, we need to reset the
244             // dictionary property offsets to this section's parent property
245             // start.
246             params->state.mainDictPropOffset = ending->mainDictPropOffset;
247             params->state.annoDictPropOffset = ending->annoDictPropOffset;
248             params->state.addPropertyName = ending->addPropertyName;
249 
250             if (ending->sectionType == bejSectionSet)
251             {
252                 RETURN_IF_CALLBACK_IERROR(
253                     params->decodedCallback->callbackSetEnd,
254                     params->callbacksDataPtr);
255             }
256             else if (ending->sectionType == bejSectionArray)
257             {
258                 RETURN_IF_CALLBACK_IERROR(
259                     params->decodedCallback->callbackArrayEnd,
260                     params->callbacksDataPtr);
261             }
262             params->stackCallback->stackPop(params->stackDataPtr);
263         }
264         else
265         {
266             RETURN_IF_CALLBACK_IERROR(
267                 params->decodedCallback->callbackPropertyEnd,
268                 params->callbacksDataPtr);
269             // Do not change the parent dictionary property offset since we are
270             // still inside the same section.
271             return 0;
272         }
273     }
274     return 0;
275 }
276 
277 /**
278  * @brief Check whether the current encoded segment being decoded is an array
279  * element.
280  *
281  * @param[in] params - a valid BejHandleTypeFuncParam struct.
282  * @return true if the encoded segment is an array element. Else false.
283  */
bejIsArrayElement(const struct BejHandleTypeFuncParam * params)284 static bool bejIsArrayElement(const struct BejHandleTypeFuncParam* params)
285 {
286     // If the encoded segment enters an array section, we are adding a
287     // BejSectionArray to the stack. Therefore if the stack is empty, encoded
288     // segment cannot be an array element.
289     if (params->stackCallback->stackEmpty(params->stackDataPtr))
290     {
291         return false;
292     }
293     const struct BejStackProperty* const ending =
294         params->stackCallback->stackPeek(params->stackDataPtr);
295     // If the stack top element holds a BejSectionArray, encoded segment is
296     // an array element.
297     return ending->sectionType == bejSectionArray;
298 }
299 
300 /**
301  * @brief Decodes a BejSet type SFLV BEJ tuple.
302  *
303  * @param[in] params - a valid BejHandleTypeFuncParam struct.
304  * @return 0 if successful.
305  */
bejHandleBejSet(struct BejHandleTypeFuncParam * params)306 static int bejHandleBejSet(struct BejHandleTypeFuncParam* params)
307 {
308     uint16_t sequenceNumber = params->sflv.tupleS.sequenceNumber;
309     // Check whether this BejSet is an array element or not.
310     if (bejIsArrayElement(params))
311     {
312         // Dictionary only contains an entry for element 0.
313         sequenceNumber = 0;
314     }
315     const uint8_t* dictionary;
316     const struct BejDictionaryProperty* prop;
317     RETURN_IF_IERROR(
318         bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
319                                     sequenceNumber, &dictionary, &prop));
320 
321     const char* propName = "";
322     if (params->state.addPropertyName)
323     {
324         propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
325                                           prop->nameLength);
326     }
327 
328     RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackSetStart,
329                               propName, params->callbacksDataPtr);
330 
331     // Move the offset to the next SFLV tuple (or end). Make sure that this is
332     // called before calling bejProcessEnding.
333     params->state.encodedStreamOffset = bejGetFirstTupleOffset(params);
334 
335     uint64_t elements = bejGetNnint(params->sflv.value);
336     // If its an empty set, we are done here.
337     if (elements == 0)
338     {
339         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackSetEnd,
340                                   params->callbacksDataPtr);
341         // Since this is an ending of a property (empty array), we should call
342         // bejProcessEnding. Unless the whole JSON object is an empty set (which
343         // shouldn't be the case), stack cannot be empty.
344         bejProcessEnding(params, /*canBeEmpty=*/false);
345         return 0;
346     }
347 
348     // Update the states for the next encoding segment.
349     struct BejStackProperty newEnding = {
350         .sectionType = bejSectionSet,
351         .addPropertyName = params->state.addPropertyName,
352         .mainDictPropOffset = params->state.mainDictPropOffset,
353         .annoDictPropOffset = params->state.annoDictPropOffset,
354         .streamEndOffset = params->sflv.valueEndOffset,
355     };
356     RETURN_IF_IERROR(
357         params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
358     params->state.addPropertyName = true;
359     if (params->sflv.tupleS.schema == bejAnnotation)
360     {
361         // Since this set is an annotated type, we need to advance the
362         // annotation dictionary for decoding the next segment.
363         params->state.annoDictPropOffset = prop->childPointerOffset;
364     }
365     else
366     {
367         params->state.mainDictPropOffset = prop->childPointerOffset;
368     }
369     return 0;
370 }
371 
372 /**
373  * @brief Decodes a BejArray type SFLV BEJ tuple.
374  *
375  * @param[in] params - a valid BejHandleTypeFuncParam struct.
376  * @return 0 if successful.
377  */
bejHandleBejArray(struct BejHandleTypeFuncParam * params)378 static int bejHandleBejArray(struct BejHandleTypeFuncParam* params)
379 {
380     const uint8_t* dictionary;
381     const struct BejDictionaryProperty* prop;
382     RETURN_IF_IERROR(bejGetDictionaryAndProperty(
383         params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber,
384         &dictionary, &prop));
385 
386     const char* propName = "";
387     if (params->state.addPropertyName)
388     {
389         propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
390                                           prop->nameLength);
391     }
392 
393     RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayStart,
394                               propName, params->callbacksDataPtr);
395 
396     // Move the offset to the next SFLV tuple (or end). Make sure that this is
397     // called before calling bejProcessEnding.
398     params->state.encodedStreamOffset = bejGetFirstTupleOffset(params);
399 
400     uint64_t elements = bejGetNnint(params->sflv.value);
401     // If its an empty array, we are done here.
402     if (elements == 0)
403     {
404         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayEnd,
405                                   params->callbacksDataPtr);
406         // Since this is an ending of a property (empty array), we should call
407         // bejProcessEnding. Stack cannot be empty since there should be at
408         // least 1 parent in the stack.
409         bejProcessEnding(params, /*canBeEmpty=*/false);
410         return 0;
411     }
412 
413     // Update the state for next segment decoding.
414     struct BejStackProperty newEnding = {
415         .sectionType = bejSectionArray,
416         .addPropertyName = params->state.addPropertyName,
417         .mainDictPropOffset = params->state.mainDictPropOffset,
418         .annoDictPropOffset = params->state.annoDictPropOffset,
419         .streamEndOffset = params->sflv.valueEndOffset,
420     };
421     RETURN_IF_IERROR(
422         params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
423     // We do not add property names for array elements.
424     params->state.addPropertyName = false;
425     if (params->sflv.tupleS.schema == bejAnnotation)
426     {
427         // Since this array is an annotated type, we need to advance the
428         // annotation dictionary for decoding the next segment.
429         params->state.annoDictPropOffset = prop->childPointerOffset;
430     }
431     else
432     {
433         params->state.mainDictPropOffset = prop->childPointerOffset;
434     }
435     return 0;
436 }
437 
438 /**
439  * @brief Decodes a BejNull type SFLV BEJ tuple.
440  *
441  * @param[in] params - a valid BejHandleTypeFuncParam struct.
442  * @return 0 if successful.
443  */
bejHandleBejNull(struct BejHandleTypeFuncParam * params)444 static int bejHandleBejNull(struct BejHandleTypeFuncParam* params)
445 {
446     const char* propName = bejGetPropName(params);
447     RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, propName,
448                               params->callbacksDataPtr);
449     params->state.encodedStreamOffset = params->sflv.valueEndOffset;
450     return bejProcessEnding(params, /*canBeEmpty=*/false);
451 }
452 
453 /**
454  * @brief Decodes a BejInteger type SFLV BEJ tuple.
455  *
456  * @param[in] params - a valid BejHandleTypeFuncParam struct.
457  * @return 0 if successful.
458  */
bejHandleBejInteger(struct BejHandleTypeFuncParam * params)459 static int bejHandleBejInteger(struct BejHandleTypeFuncParam* params)
460 {
461     const char* propName = bejGetPropName(params);
462 
463     if (params->sflv.valueLength == 0)
464     {
465         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
466                                   propName, params->callbacksDataPtr);
467     }
468     else
469     {
470         RETURN_IF_CALLBACK_IERROR(
471             params->decodedCallback->callbackInteger, propName,
472             bejGetIntegerValue(params->sflv.value, params->sflv.valueLength),
473             params->callbacksDataPtr);
474     }
475     params->state.encodedStreamOffset = params->sflv.valueEndOffset;
476     return bejProcessEnding(params, /*canBeEmpty=*/false);
477 }
478 
479 /**
480  * @brief Decodes a BejEnum type SFLV BEJ tuple.
481  *
482  * @param[in] params - a valid BejHandleTypeFuncParam struct.
483  * @return 0 if successful.
484  */
bejHandleBejEnum(struct BejHandleTypeFuncParam * params)485 static int bejHandleBejEnum(struct BejHandleTypeFuncParam* params)
486 {
487     uint16_t sequenceNumber = params->sflv.tupleS.sequenceNumber;
488     if (bejIsArrayElement(params))
489     {
490         sequenceNumber = 0;
491     }
492     const uint8_t* dictionary;
493     const struct BejDictionaryProperty* prop;
494     RETURN_IF_IERROR(
495         bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
496                                     sequenceNumber, &dictionary, &prop));
497 
498     const char* propName = "";
499     if (params->state.addPropertyName)
500     {
501         propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
502                                           prop->nameLength);
503     }
504 
505     if (params->sflv.valueLength == 0)
506     {
507         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
508                                   propName, params->callbacksDataPtr);
509     }
510     else
511     {
512         // Get the string for enum value.
513         uint16_t enumValueSequenceN =
514             (uint16_t)(bejGetNnint(params->sflv.value));
515         const struct BejDictionaryProperty* enumValueProp;
516         RETURN_IF_IERROR(
517             bejDictGetProperty(dictionary, prop->childPointerOffset,
518                                enumValueSequenceN, &enumValueProp));
519         const char* enumValueName = bejDictGetPropertyName(
520             dictionary, enumValueProp->nameOffset, enumValueProp->nameLength);
521 
522         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackEnum,
523                                   propName, enumValueName,
524                                   params->callbacksDataPtr);
525     }
526     // Update the offset to point to the next possible SFLV tuple.
527     params->state.encodedStreamOffset = params->sflv.valueEndOffset;
528     return bejProcessEnding(params, /*canBeEmpty=*/false);
529 }
530 
531 /**
532  * @brief Decodes a BejString type SFLV BEJ tuple.
533  *
534  * @param[in] params - a valid BejHandleTypeFuncParam struct.
535  * @return 0 if successful.
536  */
bejHandleBejString(struct BejHandleTypeFuncParam * params)537 static int bejHandleBejString(struct BejHandleTypeFuncParam* params)
538 {
539     // TODO: Handle deferred bindings.
540     const char* propName = bejGetPropName(params);
541 
542     if (params->sflv.valueLength == 0)
543     {
544         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
545                                   propName, params->callbacksDataPtr);
546     }
547     else
548     {
549         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackString,
550                                   propName, (const char*)(params->sflv.value),
551                                   params->callbacksDataPtr);
552     }
553     params->state.encodedStreamOffset = params->sflv.valueEndOffset;
554     return bejProcessEnding(params, /*canBeEmpty=*/false);
555 }
556 
557 /**
558  * @brief Decodes a BejReal type SFLV BEJ tuple.
559  *
560  * @param[in] params - a valid BejHandleTypeFuncParam struct.
561  * @return 0 if successful.
562  */
bejHandleBejReal(struct BejHandleTypeFuncParam * params)563 static int bejHandleBejReal(struct BejHandleTypeFuncParam* params)
564 {
565     const char* propName = bejGetPropName(params);
566 
567     if (params->sflv.valueLength == 0)
568     {
569         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
570                                   propName, params->callbacksDataPtr);
571     }
572     else
573     {
574         // Real value has the following format.
575         // nnint      - Length of whole
576         // bejInteger - whole (includes sign for the overall real number)
577         // nnint      - Leading zero count for fract
578         // nnint      - fract
579         // nnint      - Length of exp
580         // bejInteger - exp (includes sign for the exponent)
581         uint8_t wholeByteLen = (uint8_t)bejGetNnint(params->sflv.value);
582         const uint8_t* wholeBejInt =
583             params->sflv.value + bejGetNnintSize(params->sflv.value);
584         const uint8_t* fractZeroCountNnint = wholeBejInt + wholeByteLen;
585         const uint8_t* fractNnint =
586             fractZeroCountNnint + bejGetNnintSize(fractZeroCountNnint);
587         const uint8_t* lenExpNnint = fractNnint + bejGetNnintSize(fractNnint);
588         const uint8_t* expBejInt = lenExpNnint + bejGetNnintSize(lenExpNnint);
589 
590         struct BejReal realValue;
591         realValue.whole = bejGetIntegerValue(wholeBejInt, wholeByteLen);
592         realValue.zeroCount = bejGetNnint(fractZeroCountNnint);
593         realValue.fract = bejGetNnint(fractNnint);
594         realValue.expLen = (uint8_t)bejGetNnint(lenExpNnint);
595         if (realValue.expLen != 0)
596         {
597             realValue.exp = bejGetIntegerValue(
598                 expBejInt, (uint8_t)bejGetNnint(lenExpNnint));
599         }
600         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackReal,
601                                   propName, &realValue,
602                                   params->callbacksDataPtr);
603     }
604     params->state.encodedStreamOffset = params->sflv.valueEndOffset;
605     return bejProcessEnding(params, /*canBeEmpty=*/false);
606 }
607 
608 /**
609  * @brief Decodes a BejBoolean type SFLV BEJ tuple.
610  *
611  * @param[in] params - a valid BejHandleTypeFuncParam struct.
612  * @return 0 if successful.
613  */
bejHandleBejBoolean(struct BejHandleTypeFuncParam * params)614 static int bejHandleBejBoolean(struct BejHandleTypeFuncParam* params)
615 {
616     const char* propName = bejGetPropName(params);
617 
618     if (params->sflv.valueLength == 0)
619     {
620         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
621                                   propName, params->callbacksDataPtr);
622     }
623     else
624     {
625         RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackBool,
626                                   propName, *(params->sflv.value) > 0,
627                                   params->callbacksDataPtr);
628     }
629     params->state.encodedStreamOffset = params->sflv.valueEndOffset;
630     return bejProcessEnding(params, /*canBeEmpty=*/false);
631 }
632 
633 /**
634  * @brief Decodes a BejPropertyAnnotation type SFLV BEJ tuple.
635  *
636  * @param[in] params - a valid BejHandleTypeFuncParam struct.
637  * @return 0 if successful.
638  */
bejHandleBejPropertyAnnotation(struct BejHandleTypeFuncParam * params)639 static int bejHandleBejPropertyAnnotation(struct BejHandleTypeFuncParam* params)
640 {
641     // TODO: Handle colon-delimited string values.
642 
643     // Property annotation has the form OuterProperty@Annotation. First
644     // processing the outer property name.
645     const uint8_t* outerDictionary;
646     const struct BejDictionaryProperty* outerProp;
647     RETURN_IF_IERROR(bejGetDictionaryAndProperty(
648         params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber,
649         &outerDictionary, &outerProp));
650 
651     const char* propName = bejDictGetPropertyName(
652         outerDictionary, outerProp->nameOffset, outerProp->nameLength);
653     RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackAnnotation,
654                               propName, params->callbacksDataPtr);
655 
656     // Mark the ending of the property annotation.
657     struct BejStackProperty newEnding = {
658         .sectionType = bejSectionNoType,
659         .addPropertyName = params->state.addPropertyName,
660         .mainDictPropOffset = params->state.mainDictPropOffset,
661         .annoDictPropOffset = params->state.annoDictPropOffset,
662         .streamEndOffset = params->sflv.valueEndOffset,
663     };
664     // Update the states for the next encoding segment.
665     RETURN_IF_IERROR(
666         params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
667     params->state.addPropertyName = true;
668     // We might have to change this for nested annotations.
669     params->state.mainDictPropOffset = outerProp->childPointerOffset;
670     // Point to the start of the value for next decoding.
671     params->state.encodedStreamOffset =
672         params->sflv.valueEndOffset - params->sflv.valueLength;
673     return 0;
674 }
675 
676 /**
677  * @brief Decodes an encoded bej stream.
678  *
679  * @param[in] schemaDictionary - main schema dictionary to use.
680  * @param[in] annotationDictionary - annotation dictionary
681  * @param[in] enStream - encoded stream without the PLDM header.
682  * @param[in] streamLen - length of the enStream.
683  * @param[in] stackCallback - callbacks for stack handlers.
684  * @param[in] decodedCallback - callbacks for extracting decoded properties.
685  * @param[in] callbacksDataPtr - data pointer to pass to decoded callbacks. This
686  * can be used pass additional data.
687  * @param[in] stackDataPtr - data pointer to pass to stack callbacks. This can
688  * be used pass additional data.
689  *
690  * @return 0 if successful.
691  */
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)692 static int bejDecode(const uint8_t* schemaDictionary,
693                      const uint8_t* annotationDictionary,
694                      const uint8_t* enStream, uint32_t streamLen,
695                      const struct BejStackCallback* stackCallback,
696                      const struct BejDecodedCallback* decodedCallback,
697                      void* callbacksDataPtr, void* stackDataPtr)
698 {
699     struct BejHandleTypeFuncParam params = {
700         .state =
701             {
702                 // We only add names of set properties. We don't use names for
703                 // array
704                 // properties. Here we are omitting the name of the root set.
705                 .addPropertyName = false,
706                 // At start, parent property from the main dictionary is the
707                 // first property.
708                 .mainDictPropOffset = bejDictGetPropertyHeadOffset(),
709                 .annoDictPropOffset = bejDictGetFirstAnnotatedPropertyOffset(),
710                 // Current location of the encoded segment we are processing.
711                 .encodedStreamOffset = 0,
712                 .encodedSubStream = enStream,
713             },
714         .mainDictionary = schemaDictionary,
715         .annotDictionary = annotationDictionary,
716         .decodedCallback = decodedCallback,
717         .stackCallback = stackCallback,
718         .callbacksDataPtr = callbacksDataPtr,
719         .stackDataPtr = stackDataPtr,
720     };
721 
722     while (params.state.encodedStreamOffset < streamLen)
723     {
724         // Go to the next encoded segment in the encoded stream.
725         params.state.encodedSubStream =
726             enStream + params.state.encodedStreamOffset;
727         bejInitSFLVStruct(&params);
728 
729         if (params.sflv.format.readOnlyProperty)
730         {
731             RETURN_IF_CALLBACK_IERROR(
732                 params.decodedCallback->callbackReadonlyProperty,
733                 params.sflv.tupleS.sequenceNumber, params.callbacksDataPtr);
734         }
735 
736         // TODO: Handle nullable property types. These are indicated by
737         // params.sflv.format.nullableProperty
738         switch (params.sflv.format.principalDataType)
739         {
740             case bejSet:
741                 RETURN_IF_IERROR(bejHandleBejSet(&params));
742                 break;
743             case bejArray:
744                 RETURN_IF_IERROR(bejHandleBejArray(&params));
745                 break;
746             case bejNull:
747                 RETURN_IF_IERROR(bejHandleBejNull(&params));
748                 break;
749             case bejInteger:
750                 RETURN_IF_IERROR(bejHandleBejInteger(&params));
751                 break;
752             case bejEnum:
753                 RETURN_IF_IERROR(bejHandleBejEnum(&params));
754                 break;
755             case bejString:
756                 RETURN_IF_IERROR(bejHandleBejString(&params));
757                 break;
758             case bejReal:
759                 RETURN_IF_IERROR(bejHandleBejReal(&params));
760                 break;
761             case bejBoolean:
762                 RETURN_IF_IERROR(bejHandleBejBoolean(&params));
763                 break;
764             case bejBytestring:
765                 // TODO: Add support for BejBytestring decoding.
766                 fprintf(stderr, "No BejBytestring support\n");
767                 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
768                 break;
769             case bejChoice:
770                 // TODO: Add support for BejChoice decoding.
771                 fprintf(stderr, "No BejChoice support\n");
772                 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
773                 break;
774             case bejPropertyAnnotation:
775                 RETURN_IF_IERROR(bejHandleBejPropertyAnnotation(&params));
776                 break;
777             case bejResourceLink:
778                 // TODO: Add support for BejResourceLink decoding.
779                 fprintf(stderr, "No BejResourceLink support\n");
780                 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
781                 break;
782             case bejResourceLinkExpansion:
783                 // TODO: Add support for BejResourceLinkExpansion decoding.
784                 fprintf(stderr, "No BejResourceLinkExpansion support\n");
785                 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
786                 break;
787             default:
788                 break;
789         }
790     }
791     RETURN_IF_IERROR(bejProcessEnding(&params, /*canBeEmpty=*/true));
792     if (!params.stackCallback->stackEmpty(params.stackDataPtr))
793     {
794         fprintf(stderr, "Ending stack should be empty but its not. Something "
795                         "must have gone wrong with the encoding\n");
796         return bejErrorUnknown;
797     }
798     return 0;
799 }
800 
801 /**
802  * @brief Check if a bej version is supported by this decoder
803  *
804  * @param[in] bejVersion - the bej version in the received encoded stream
805  * @return true if supported.
806  */
bejIsSupported(uint32_t bejVersion)807 static bool bejIsSupported(uint32_t bejVersion)
808 {
809     for (uint32_t i = 0; i < sizeof(supportedBejVersions) / sizeof(uint32_t);
810          ++i)
811     {
812         if (bejVersion == supportedBejVersions[i])
813         {
814             return true;
815         }
816     }
817     return false;
818 }
819 
bejDecodePldmBlock(const struct BejDictionaries * dictionaries,const uint8_t * encodedPldmBlock,uint32_t blockLength,const struct BejStackCallback * stackCallback,const struct BejDecodedCallback * decodedCallback,void * callbacksDataPtr,void * stackDataPtr)820 int bejDecodePldmBlock(const struct BejDictionaries* dictionaries,
821                        const uint8_t* encodedPldmBlock, uint32_t blockLength,
822                        const struct BejStackCallback* stackCallback,
823                        const struct BejDecodedCallback* decodedCallback,
824                        void* callbacksDataPtr, void* stackDataPtr)
825 {
826     NULL_CHECK(dictionaries, "dictionaries");
827     NULL_CHECK(dictionaries->schemaDictionary, "schemaDictionary");
828     NULL_CHECK(dictionaries->annotationDictionary, "annotationDictionary");
829 
830     NULL_CHECK(encodedPldmBlock, "encodedPldmBlock");
831 
832     NULL_CHECK(stackCallback, "stackCallback");
833     NULL_CHECK(stackCallback->stackEmpty, "stackEmpty");
834     NULL_CHECK(stackCallback->stackPeek, "stackPeek");
835     NULL_CHECK(stackCallback->stackPop, "stackPop");
836     NULL_CHECK(stackCallback->stackPush, "stackPush");
837 
838     NULL_CHECK(decodedCallback, "decodedCallback");
839 
840     uint32_t pldmHeaderSize = sizeof(struct BejPldmBlockHeader);
841     if (blockLength < pldmHeaderSize)
842     {
843         fprintf(stderr, "Invalid pldm block size: %u\n", blockLength);
844         return bejErrorInvalidSize;
845     }
846 
847     const struct BejPldmBlockHeader* pldmHeader =
848         (const struct BejPldmBlockHeader*)encodedPldmBlock;
849 
850     if (!bejIsSupported(pldmHeader->bejVersion))
851     {
852         fprintf(stderr, "Bej decoder doesn't support the bej version: %u\n",
853                 pldmHeader->bejVersion);
854         return bejErrorNotSuppoted;
855     }
856 
857     if (pldmHeader->schemaClass == bejAnnotationSchemaClass)
858     {
859         fprintf(stderr,
860                 "Encoder schema class cannot be BejAnnotationSchemaClass\n");
861         return bejErrorNotSuppoted;
862     }
863     // TODO: Add support for CollectionMemberType schema class.
864     if (pldmHeader->schemaClass == bejCollectionMemberTypeSchemaClass)
865     {
866         fprintf(stderr, "Decoder doesn't support "
867                         "bejCollectionMemberTypeSchemaClass yet.\n");
868         return bejErrorNotSuppoted;
869     }
870     // TODO: Add support for Error schema class.
871     if (pldmHeader->schemaClass == bejErrorSchemaClass)
872     {
873         fprintf(stderr, "Decoder doesn't support BejErrorSchemaClass yet.\n");
874         return bejErrorNotSuppoted;
875     }
876 
877     // Skip the PLDM header.
878     const uint8_t* enStream = encodedPldmBlock + pldmHeaderSize;
879     uint32_t streamLen = blockLength - pldmHeaderSize;
880     return bejDecode(dictionaries->schemaDictionary,
881                      dictionaries->annotationDictionary, enStream, streamLen,
882                      stackCallback, decodedCallback, callbacksDataPtr,
883                      stackDataPtr);
884 }
885