blob: b1688a9c7650112a3e39b1ad27e40da00c4c080a [file] [log] [blame]
kasunath64cb1972022-05-13 12:54:23 -07001#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
12const 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/**
kasunath34a096d2022-05-17 11:36:14 -070033 * @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 */
55static int64_t bejGetIntegerValue(const uint8_t* bytes, uint8_t numOfBytes)
56{
57 if (numOfBytes == 0)
58 {
59 return 0;
60 }
kasunath0aa36d82022-11-23 14:24:15 -080061 uint64_t value = bejGetUnsignedInteger(bytes, numOfBytes);
kasunath34a096d2022-05-17 11:36:14 -070062 uint8_t bitsInVal = numOfBytes * 8;
63 // Since numOfBytes > 0, bitsInVal is non negative.
64 uint64_t mask = (uint64_t)1 << (uint8_t)(bitsInVal - 1);
kasunath7daa5f82022-06-09 14:08:10 -070065 return (int64_t)((value ^ mask) - mask);
kasunath34a096d2022-05-17 11:36:14 -070066}
67
68/**
kasunath64cb1972022-05-13 12:54:23 -070069 * @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 */
74static 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 */
108static 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;
kasunath0aa36d82022-11-23 14:24:15 -0800115 const uint32_t valueLength = (uint32_t)(bejGetNnint(
kasunath64cb1972022-05-13 12:54:23 -0700116 params->state.encodedSubStream + localOffset.valueLenNnintOffset));
117 // Sequence number itself should be 16bits. Using 32bits for
118 // [sequence_number + schema_type].
kasunath0aa36d82022-11-23 14:24:15 -0800119 uint32_t tupleS = (uint32_t)(bejGetNnint(params->state.encodedSubStream));
kasunath64cb1972022-05-13 12:54:23 -0700120 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/**
kasunath34a096d2022-05-17 11:36:14 -0700133 * @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 */
142static 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 +
kasunath0aa36d82022-11-23 14:24:15 -0800150 bejGetNnintSize(params->sflv.value);
kasunath34a096d2022-05-17 11:36:14 -0700151}
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 */
168static 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/**
kasunathe2260cd2022-05-17 16:12:54 -0700205 * @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.
kasunath34a096d2022-05-17 11:36:14 -0700207 *
208 * @param[in] params - a valid populated BejHandleTypeFuncParam.
209 * @return 0 if successful.
210 */
kasunathe2260cd2022-05-17 16:12:54 -0700211static const char* bejGetPropName(struct BejHandleTypeFuncParam* params)
kasunath34a096d2022-05-17 11:36:14 -0700212{
213 const uint8_t* dictionary;
214 const struct BejDictionaryProperty* prop;
kasunathe2260cd2022-05-17 16:12:54 -0700215 if (!params->state.addPropertyName ||
216 (bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
217 params->sflv.tupleS.sequenceNumber,
218 &dictionary, &prop) != 0))
kasunath34a096d2022-05-17 11:36:14 -0700219 {
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 */
240static 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 */
300static 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 *
kasunathb8ca2652022-05-17 16:34:35 -0700319 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunath34a096d2022-05-17 11:36:14 -0700320 * @return 0 if successful.
321 */
322static 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
kasunath0aa36d82022-11-23 14:24:15 -0800347 uint64_t elements = bejGetNnint(params->sflv.value);
kasunath34a096d2022-05-17 11:36:14 -0700348 // 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/**
kasunathe2260cd2022-05-17 16:12:54 -0700383 * @brief Decodes a BejArray type SFLV BEJ tuple.
384 *
kasunathb8ca2652022-05-17 16:34:35 -0700385 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunathe2260cd2022-05-17 16:12:54 -0700386 * @return 0 if successful.
387 */
388static 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
kasunath0aa36d82022-11-23 14:24:15 -0800406 uint64_t elements = bejGetNnint(params->sflv.value);
kasunathe2260cd2022-05-17 16:12:54 -0700407 // 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 *
kasunathb8ca2652022-05-17 16:34:35 -0700445 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunathe2260cd2022-05-17 16:12:54 -0700446 * @return 0 if successful.
447 */
448static 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/**
kasunath34a096d2022-05-17 11:36:14 -0700458 * @brief Decodes a BejInteger type SFLV BEJ tuple.
459 *
kasunathb8ca2652022-05-17 16:34:35 -0700460 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunath34a096d2022-05-17 11:36:14 -0700461 * @return 0 if successful.
462 */
463static int bejHandleBejInteger(struct BejHandleTypeFuncParam* params)
464{
kasunathe2260cd2022-05-17 16:12:54 -0700465 const char* propName = bejGetPropName(params);
kasunath34a096d2022-05-17 11:36:14 -0700466
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/**
kasunathb8ca2652022-05-17 16:34:35 -0700484 * @brief Decodes a BejEnum type SFLV BEJ tuple.
485 *
486 * @param[in] params - a valid BejHandleTypeFuncParam struct.
487 * @return 0 if successful.
488 */
489static 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 =
kasunath0aa36d82022-11-23 14:24:15 -0800518 (uint16_t)(bejGetNnint(params->sflv.value));
kasunathb8ca2652022-05-17 16:34:35 -0700519 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/**
kasunathe2260cd2022-05-17 16:12:54 -0700536 * @brief Decodes a BejString type SFLV BEJ tuple.
537 *
kasunathb8ca2652022-05-17 16:34:35 -0700538 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunathe2260cd2022-05-17 16:12:54 -0700539 * @return 0 if successful.
540 */
541static 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/**
kasunathb8ca2652022-05-17 16:34:35 -0700562 * @brief Decodes a BejReal type SFLV BEJ tuple.
563 *
564 * @param[in] params - a valid BejHandleTypeFuncParam struct.
565 * @return 0 if successful.
566 */
567static 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)
kasunath0aa36d82022-11-23 14:24:15 -0800585 uint8_t wholeByteLen = (uint8_t)bejGetNnint(params->sflv.value);
kasunathb8ca2652022-05-17 16:34:35 -0700586 const uint8_t* wholeBejInt =
kasunath0aa36d82022-11-23 14:24:15 -0800587 params->sflv.value + bejGetNnintSize(params->sflv.value);
kasunathb8ca2652022-05-17 16:34:35 -0700588 const uint8_t* fractZeroCountNnint = wholeBejInt + wholeByteLen;
589 const uint8_t* fractNnint =
kasunath0aa36d82022-11-23 14:24:15 -0800590 fractZeroCountNnint + bejGetNnintSize(fractZeroCountNnint);
591 const uint8_t* lenExpNnint = fractNnint + bejGetNnintSize(fractNnint);
592 const uint8_t* expBejInt = lenExpNnint + bejGetNnintSize(lenExpNnint);
kasunathb8ca2652022-05-17 16:34:35 -0700593
594 struct BejReal realValue;
595 realValue.whole = bejGetIntegerValue(wholeBejInt, wholeByteLen);
kasunath0aa36d82022-11-23 14:24:15 -0800596 realValue.zeroCount = bejGetNnint(fractZeroCountNnint);
597 realValue.fract = bejGetNnint(fractNnint);
598 realValue.expLen = (uint8_t)bejGetNnint(lenExpNnint);
kasunathb8ca2652022-05-17 16:34:35 -0700599 if (realValue.expLen != 0)
600 {
601 realValue.exp = bejGetIntegerValue(
kasunath0aa36d82022-11-23 14:24:15 -0800602 expBejInt, (uint8_t)bejGetNnint(lenExpNnint));
kasunathb8ca2652022-05-17 16:34:35 -0700603 }
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/**
kasunathe2260cd2022-05-17 16:12:54 -0700613 * @brief Decodes a BejBoolean type SFLV BEJ tuple.
614 *
kasunathb8ca2652022-05-17 16:34:35 -0700615 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunathe2260cd2022-05-17 16:12:54 -0700616 * @return 0 if successful.
617 */
618static 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/**
kasunathb8ca2652022-05-17 16:34:35 -0700638 * @brief Decodes a BejPropertyAnnotation type SFLV BEJ tuple.
639 *
640 * @param[in] params - a valid BejHandleTypeFuncParam struct.
641 * @return 0 if successful.
642 */
643static 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/**
kasunath64cb1972022-05-13 12:54:23 -0700681 * @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 */
696static 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:
kasunath34a096d2022-05-17 11:36:14 -0700745 RETURN_IF_IERROR(bejHandleBejSet(&params));
kasunath64cb1972022-05-13 12:54:23 -0700746 break;
747 case bejArray:
kasunathe2260cd2022-05-17 16:12:54 -0700748 RETURN_IF_IERROR(bejHandleBejArray(&params));
kasunath64cb1972022-05-13 12:54:23 -0700749 break;
750 case bejNull:
kasunathe2260cd2022-05-17 16:12:54 -0700751 RETURN_IF_IERROR(bejHandleBejNull(&params));
kasunath64cb1972022-05-13 12:54:23 -0700752 break;
753 case bejInteger:
kasunath34a096d2022-05-17 11:36:14 -0700754 RETURN_IF_IERROR(bejHandleBejInteger(&params));
kasunath64cb1972022-05-13 12:54:23 -0700755 break;
756 case bejEnum:
kasunathb8ca2652022-05-17 16:34:35 -0700757 RETURN_IF_IERROR(bejHandleBejEnum(&params));
kasunath64cb1972022-05-13 12:54:23 -0700758 break;
759 case bejString:
kasunathe2260cd2022-05-17 16:12:54 -0700760 RETURN_IF_IERROR(bejHandleBejString(&params));
kasunath64cb1972022-05-13 12:54:23 -0700761 break;
762 case bejReal:
kasunathb8ca2652022-05-17 16:34:35 -0700763 RETURN_IF_IERROR(bejHandleBejReal(&params));
kasunath64cb1972022-05-13 12:54:23 -0700764 break;
765 case bejBoolean:
kasunathe2260cd2022-05-17 16:12:54 -0700766 RETURN_IF_IERROR(bejHandleBejBoolean(&params));
kasunath64cb1972022-05-13 12:54:23 -0700767 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:
kasunathb8ca2652022-05-17 16:34:35 -0700779 RETURN_IF_IERROR(bejHandleBejPropertyAnnotation(&params));
kasunath64cb1972022-05-13 12:54:23 -0700780 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 }
kasunath34a096d2022-05-17 11:36:14 -0700795 RETURN_IF_IERROR(bejProcessEnding(&params, /*canBeEmpty=*/true));
kasunath64cb1972022-05-13 12:54:23 -0700796 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 *
kasunathb8ca2652022-05-17 16:34:35 -0700808 * @param[in] bejVersion - the bej version in the received encoded stream
kasunath64cb1972022-05-13 12:54:23 -0700809 * @return true if supported.
810 */
811static 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
824int 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{
kasunath34a096d2022-05-17 11:36:14 -0700830 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
kasunath64cb1972022-05-13 12:54:23 -0700844 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 "
kasunath34a096d2022-05-17 11:36:14 -0700871 "bejCollectionMemberTypeSchemaClass yet.\n");
kasunath64cb1972022-05-13 12:54:23 -0700872 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}