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 = ¶ms->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(¶ms))
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(¶ms));
786 break;
787 case bejArray:
788 RETURN_IF_IERROR(bejHandleBejArray(¶ms));
789 break;
790 case bejNull:
791 RETURN_IF_IERROR(bejHandleBejNull(¶ms));
792 break;
793 case bejInteger:
794 RETURN_IF_IERROR(bejHandleBejInteger(¶ms));
795 break;
796 case bejEnum:
797 RETURN_IF_IERROR(bejHandleBejEnum(¶ms));
798 break;
799 case bejString:
800 RETURN_IF_IERROR(bejHandleBejString(¶ms));
801 break;
802 case bejReal:
803 RETURN_IF_IERROR(bejHandleBejReal(¶ms));
804 break;
805 case bejBoolean:
806 RETURN_IF_IERROR(bejHandleBejBoolean(¶ms));
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(¶ms));
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(¶ms, /*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