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