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