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