blob: 2536ed16642ad0f4cfe50be6a37857b9a16a8e42 [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 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 */
40static int64_t bejGetIntegerValue(const uint8_t* bytes, uint8_t numOfBytes)
41{
42 if (numOfBytes == 0)
43 {
44 return 0;
45 }
kasunath0aa36d82022-11-23 14:24:15 -080046 uint64_t value = bejGetUnsignedInteger(bytes, numOfBytes);
kasunath34a096d2022-05-17 11:36:14 -070047 uint8_t bitsInVal = numOfBytes * 8;
48 // Since numOfBytes > 0, bitsInVal is non negative.
49 uint64_t mask = (uint64_t)1 << (uint8_t)(bitsInVal - 1);
kasunath7daa5f82022-06-09 14:08:10 -070050 return (int64_t)((value ^ mask) - mask);
kasunath34a096d2022-05-17 11:36:14 -070051}
52
53/**
kasunath64cb1972022-05-13 12:54:23 -070054 * @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 */
59static 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.
Patrick Williamsbe27f2e2024-08-16 15:22:35 -040078 const uint32_t valueOffset =
79 valueLenNnintOffset + sizeof(uint8_t) + valueLengthSize;
kasunath64cb1972022-05-13 12:54:23 -070080
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 */
93static void bejInitSFLVStruct(struct BejHandleTypeFuncParam* params)
94{
95 struct BejSFLVOffset localOffset;
96 // Get offsets of different SFLV fields with respect to start of the encoded
97 // segment.
98 bejGetLocalBejSFLVOffsets(params->state.encodedSubStream, &localOffset);
99 struct BejSFLV* sflv = &params->sflv;
kasunath0aa36d82022-11-23 14:24:15 -0800100 const uint32_t valueLength = (uint32_t)(bejGetNnint(
kasunath64cb1972022-05-13 12:54:23 -0700101 params->state.encodedSubStream + localOffset.valueLenNnintOffset));
102 // Sequence number itself should be 16bits. Using 32bits for
103 // [sequence_number + schema_type].
kasunath0aa36d82022-11-23 14:24:15 -0800104 uint32_t tupleS = (uint32_t)(bejGetNnint(params->state.encodedSubStream));
kasunath64cb1972022-05-13 12:54:23 -0700105 sflv->tupleS.schema = (uint8_t)(tupleS & DICTIONARY_TYPE_MASK);
106 sflv->tupleS.sequenceNumber =
107 (uint16_t)((tupleS & (~DICTIONARY_TYPE_MASK)) >>
108 DICTIONARY_SEQ_NUM_SHIFT);
109 sflv->format = *(struct BejTupleF*)(params->state.encodedSubStream +
110 localOffset.formatOffset);
111 sflv->valueLength = valueLength;
112 sflv->valueEndOffset = params->state.encodedStreamOffset +
113 localOffset.valueOffset + valueLength;
114 sflv->value = params->state.encodedSubStream + localOffset.valueOffset;
115}
116
117/**
kasunath34a096d2022-05-17 11:36:14 -0700118 * @brief Get the offset to the first tuple of a bejArray or bejSet.
119 *
120 * The first part of the value of a bejArray or a bejSet contains an nnint
121 * providing the number of elements/tuples. Offset is with respect to the start
122 * of the encoded stream.
123 *
124 * @param[in] params - a valid BejHandleTypeFuncParam struct.
125 * @return offset with respect to the start of the encoded stream.
126 */
Patrick Williams045a2d62025-02-01 08:23:55 -0500127static uint32_t bejGetFirstTupleOffset(
128 const struct BejHandleTypeFuncParam* params)
kasunath34a096d2022-05-17 11:36:14 -0700129{
130 struct BejSFLVOffset localOffset;
131 // Get the offset of the value with respect to the current encoded segment
132 // being decoded.
133 bejGetLocalBejSFLVOffsets(params->state.encodedSubStream, &localOffset);
134 return params->state.encodedStreamOffset + localOffset.valueOffset +
kasunath0aa36d82022-11-23 14:24:15 -0800135 bejGetNnintSize(params->sflv.value);
kasunath34a096d2022-05-17 11:36:14 -0700136}
137
138/**
139 * @brief Get the correct property and the dictionary it belongs to.
140 *
141 * @param[in] params - a BejHandleTypeFuncParam struct pointing to valid
142 * dictionaries.
143 * @param[in] schemaType - indicate whether to use the annotation dictionary or
144 * the main schema dictionary.
145 * @param[in] sequenceNumber - sequence number to use for property search. Not
146 * using the params->sflv.tupleS.sequenceNumber from the provided params struct.
147 * @param[out] dictionary - if the function is successful, this will point to a
148 * valid dictionary to be used.
149 * @param[out] prop - if the function is successful, this will point to a valid
150 * property in a dictionary.
151 * @return 0 if successful.
152 */
Patrick Williamsbe27f2e2024-08-16 15:22:35 -0400153static int bejGetDictionaryAndProperty(
154 const struct BejHandleTypeFuncParam* params, uint8_t schemaType,
155 uint32_t sequenceNumber, const uint8_t** dictionary,
156 const struct BejDictionaryProperty** prop)
kasunath34a096d2022-05-17 11:36:14 -0700157{
158 uint16_t dictPropOffset;
159 // We need to pick the correct dictionary.
160 if (schemaType == bejPrimary)
161 {
162 *dictionary = params->mainDictionary;
163 dictPropOffset = params->state.mainDictPropOffset;
164 }
165 else if (schemaType == bejAnnotation)
166 {
167 *dictionary = params->annotDictionary;
168 dictPropOffset = params->state.annoDictPropOffset;
169 }
170 else
171 {
172 fprintf(stderr, "Failed to select a dictionary. schema type: %u\n",
173 schemaType);
174 return bejErrorInvalidSchemaType;
175 }
176
Patrick Williamsbe27f2e2024-08-16 15:22:35 -0400177 int ret =
178 bejDictGetProperty(*dictionary, dictPropOffset, sequenceNumber, prop);
kasunath34a096d2022-05-17 11:36:14 -0700179 if (ret != 0)
180 {
181 fprintf(stderr, "Failed to get dictionary property for offset: %u\n",
182 dictPropOffset);
183 return ret;
184 }
185 return 0;
186}
187
188/**
kasunathe2260cd2022-05-17 16:12:54 -0700189 * @brief Find and return the property name of the current encoded segment. If
190 * the params->state.addPropertyName is false, this will return an empty string.
kasunath34a096d2022-05-17 11:36:14 -0700191 *
192 * @param[in] params - a valid populated BejHandleTypeFuncParam.
193 * @return 0 if successful.
194 */
kasunathe2260cd2022-05-17 16:12:54 -0700195static const char* bejGetPropName(struct BejHandleTypeFuncParam* params)
kasunath34a096d2022-05-17 11:36:14 -0700196{
197 const uint8_t* dictionary;
198 const struct BejDictionaryProperty* prop;
kasunathe2260cd2022-05-17 16:12:54 -0700199 if (!params->state.addPropertyName ||
200 (bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
201 params->sflv.tupleS.sequenceNumber,
202 &dictionary, &prop) != 0))
kasunath34a096d2022-05-17 11:36:14 -0700203 {
204 return "";
205 }
206 return bejDictGetPropertyName(dictionary, prop->nameOffset,
207 prop->nameLength);
208}
209
210/**
211 * @brief Look for section endings.
212 *
213 * This figures out whether the current encoded segment marks a section
214 * ending. If so, this function will update the decoder state and pop the stack
215 * used to memorize endings. This function should be called after updating the
216 * encodedStreamOffset to the end of decoded SFLV tuple.
217 *
218 * @param[in] params - a valid BejHandleTypeFuncParam which contains the decoder
219 * state.
220 * @param[in] canBeEmpty - if true, the stack being empty is not an error. If
221 * false, stack cannot be empty.
222 * @return 0 if successful.
223 */
224static int bejProcessEnding(struct BejHandleTypeFuncParam* params,
225 bool canBeEmpty)
226{
227 if (params->stackCallback->stackEmpty(params->stackDataPtr) && !canBeEmpty)
228 {
229 // If bejProcessEnding has been called after adding an appropriate JSON
230 // property, then stack cannot be empty.
231 fprintf(stderr, "Ending stack cannot be empty.\n");
232 return bejErrorUnknown;
233 }
234
235 while (!params->stackCallback->stackEmpty(params->stackDataPtr))
236 {
237 const struct BejStackProperty* const ending =
238 params->stackCallback->stackPeek(params->stackDataPtr);
239 // Check whether the current offset location matches the expected ending
240 // offset. If so, we are done with that section.
241 if (params->state.encodedStreamOffset == ending->streamEndOffset)
242 {
243 // Since we are going out of a section, we need to reset the
244 // dictionary property offsets to this section's parent property
245 // start.
246 params->state.mainDictPropOffset = ending->mainDictPropOffset;
247 params->state.annoDictPropOffset = ending->annoDictPropOffset;
248 params->state.addPropertyName = ending->addPropertyName;
249
250 if (ending->sectionType == bejSectionSet)
251 {
252 RETURN_IF_CALLBACK_IERROR(
253 params->decodedCallback->callbackSetEnd,
254 params->callbacksDataPtr);
255 }
256 else if (ending->sectionType == bejSectionArray)
257 {
258 RETURN_IF_CALLBACK_IERROR(
259 params->decodedCallback->callbackArrayEnd,
260 params->callbacksDataPtr);
261 }
262 params->stackCallback->stackPop(params->stackDataPtr);
263 }
264 else
265 {
266 RETURN_IF_CALLBACK_IERROR(
267 params->decodedCallback->callbackPropertyEnd,
268 params->callbacksDataPtr);
269 // Do not change the parent dictionary property offset since we are
270 // still inside the same section.
271 return 0;
272 }
273 }
274 return 0;
275}
276
277/**
278 * @brief Check whether the current encoded segment being decoded is an array
279 * element.
280 *
281 * @param[in] params - a valid BejHandleTypeFuncParam struct.
282 * @return true if the encoded segment is an array element. Else false.
283 */
284static bool bejIsArrayElement(const struct BejHandleTypeFuncParam* params)
285{
286 // If the encoded segment enters an array section, we are adding a
287 // BejSectionArray to the stack. Therefore if the stack is empty, encoded
288 // segment cannot be an array element.
289 if (params->stackCallback->stackEmpty(params->stackDataPtr))
290 {
291 return false;
292 }
293 const struct BejStackProperty* const ending =
294 params->stackCallback->stackPeek(params->stackDataPtr);
295 // If the stack top element holds a BejSectionArray, encoded segment is
296 // an array element.
297 return ending->sectionType == bejSectionArray;
298}
299
300/**
301 * @brief Decodes a BejSet type SFLV BEJ tuple.
302 *
kasunathb8ca2652022-05-17 16:34:35 -0700303 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunath34a096d2022-05-17 11:36:14 -0700304 * @return 0 if successful.
305 */
306static int bejHandleBejSet(struct BejHandleTypeFuncParam* params)
307{
308 uint16_t sequenceNumber = params->sflv.tupleS.sequenceNumber;
309 // Check whether this BejSet is an array element or not.
310 if (bejIsArrayElement(params))
311 {
312 // Dictionary only contains an entry for element 0.
313 sequenceNumber = 0;
314 }
315 const uint8_t* dictionary;
316 const struct BejDictionaryProperty* prop;
317 RETURN_IF_IERROR(
318 bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
319 sequenceNumber, &dictionary, &prop));
320
321 const char* propName = "";
322 if (params->state.addPropertyName)
323 {
324 propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
325 prop->nameLength);
326 }
327
328 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackSetStart,
329 propName, params->callbacksDataPtr);
330
kasunathc14fab62022-11-23 16:18:21 -0800331 // Move the offset to the next SFLV tuple (or end). Make sure that this is
332 // called before calling bejProcessEnding.
333 params->state.encodedStreamOffset = bejGetFirstTupleOffset(params);
334
kasunath0aa36d82022-11-23 14:24:15 -0800335 uint64_t elements = bejGetNnint(params->sflv.value);
kasunath34a096d2022-05-17 11:36:14 -0700336 // If its an empty set, we are done here.
337 if (elements == 0)
338 {
339 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackSetEnd,
340 params->callbacksDataPtr);
kasunathc14fab62022-11-23 16:18:21 -0800341 // Since this is an ending of a property (empty array), we should call
342 // bejProcessEnding. Unless the whole JSON object is an empty set (which
343 // shouldn't be the case), stack cannot be empty.
344 bejProcessEnding(params, /*canBeEmpty=*/false);
345 return 0;
346 }
347
348 // Update the states for the next encoding segment.
349 struct BejStackProperty newEnding = {
350 .sectionType = bejSectionSet,
351 .addPropertyName = params->state.addPropertyName,
352 .mainDictPropOffset = params->state.mainDictPropOffset,
353 .annoDictPropOffset = params->state.annoDictPropOffset,
354 .streamEndOffset = params->sflv.valueEndOffset,
355 };
356 RETURN_IF_IERROR(
357 params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
358 params->state.addPropertyName = true;
359 if (params->sflv.tupleS.schema == bejAnnotation)
360 {
361 // Since this set is an annotated type, we need to advance the
362 // annotation dictionary for decoding the next segment.
363 params->state.annoDictPropOffset = prop->childPointerOffset;
kasunath34a096d2022-05-17 11:36:14 -0700364 }
365 else
366 {
kasunathc14fab62022-11-23 16:18:21 -0800367 params->state.mainDictPropOffset = prop->childPointerOffset;
kasunath34a096d2022-05-17 11:36:14 -0700368 }
kasunath34a096d2022-05-17 11:36:14 -0700369 return 0;
370}
371
372/**
kasunathe2260cd2022-05-17 16:12:54 -0700373 * @brief Decodes a BejArray type SFLV BEJ tuple.
374 *
kasunathb8ca2652022-05-17 16:34:35 -0700375 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunathe2260cd2022-05-17 16:12:54 -0700376 * @return 0 if successful.
377 */
378static int bejHandleBejArray(struct BejHandleTypeFuncParam* params)
379{
380 const uint8_t* dictionary;
381 const struct BejDictionaryProperty* prop;
382 RETURN_IF_IERROR(bejGetDictionaryAndProperty(
383 params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber,
384 &dictionary, &prop));
385
386 const char* propName = "";
387 if (params->state.addPropertyName)
388 {
389 propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
390 prop->nameLength);
391 }
392
393 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayStart,
394 propName, params->callbacksDataPtr);
395
kasunathc14fab62022-11-23 16:18:21 -0800396 // Move the offset to the next SFLV tuple (or end). Make sure that this is
397 // called before calling bejProcessEnding.
398 params->state.encodedStreamOffset = bejGetFirstTupleOffset(params);
399
kasunath0aa36d82022-11-23 14:24:15 -0800400 uint64_t elements = bejGetNnint(params->sflv.value);
kasunathe2260cd2022-05-17 16:12:54 -0700401 // If its an empty array, we are done here.
402 if (elements == 0)
403 {
404 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayEnd,
405 params->callbacksDataPtr);
kasunathc14fab62022-11-23 16:18:21 -0800406 // Since this is an ending of a property (empty array), we should call
407 // bejProcessEnding. Stack cannot be empty since there should be at
408 // least 1 parent in the stack.
409 bejProcessEnding(params, /*canBeEmpty=*/false);
410 return 0;
411 }
412
413 // Update the state for next segment decoding.
414 struct BejStackProperty newEnding = {
415 .sectionType = bejSectionArray,
416 .addPropertyName = params->state.addPropertyName,
417 .mainDictPropOffset = params->state.mainDictPropOffset,
418 .annoDictPropOffset = params->state.annoDictPropOffset,
419 .streamEndOffset = params->sflv.valueEndOffset,
420 };
421 RETURN_IF_IERROR(
422 params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
423 // We do not add property names for array elements.
424 params->state.addPropertyName = false;
425 if (params->sflv.tupleS.schema == bejAnnotation)
426 {
427 // Since this array is an annotated type, we need to advance the
428 // annotation dictionary for decoding the next segment.
429 params->state.annoDictPropOffset = prop->childPointerOffset;
kasunathe2260cd2022-05-17 16:12:54 -0700430 }
431 else
432 {
kasunathc14fab62022-11-23 16:18:21 -0800433 params->state.mainDictPropOffset = prop->childPointerOffset;
kasunathe2260cd2022-05-17 16:12:54 -0700434 }
kasunathe2260cd2022-05-17 16:12:54 -0700435 return 0;
436}
437
438/**
439 * @brief Decodes a BejNull type SFLV BEJ tuple.
440 *
kasunathb8ca2652022-05-17 16:34:35 -0700441 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunathe2260cd2022-05-17 16:12:54 -0700442 * @return 0 if successful.
443 */
444static int bejHandleBejNull(struct BejHandleTypeFuncParam* params)
445{
446 const char* propName = bejGetPropName(params);
447 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, propName,
448 params->callbacksDataPtr);
449 params->state.encodedStreamOffset = params->sflv.valueEndOffset;
450 return bejProcessEnding(params, /*canBeEmpty=*/false);
451}
452
453/**
kasunath34a096d2022-05-17 11:36:14 -0700454 * @brief Decodes a BejInteger type SFLV BEJ tuple.
455 *
kasunathb8ca2652022-05-17 16:34:35 -0700456 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunath34a096d2022-05-17 11:36:14 -0700457 * @return 0 if successful.
458 */
459static int bejHandleBejInteger(struct BejHandleTypeFuncParam* params)
460{
kasunathe2260cd2022-05-17 16:12:54 -0700461 const char* propName = bejGetPropName(params);
kasunath34a096d2022-05-17 11:36:14 -0700462
463 if (params->sflv.valueLength == 0)
464 {
465 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
466 propName, params->callbacksDataPtr);
467 }
468 else
469 {
470 RETURN_IF_CALLBACK_IERROR(
471 params->decodedCallback->callbackInteger, propName,
472 bejGetIntegerValue(params->sflv.value, params->sflv.valueLength),
473 params->callbacksDataPtr);
474 }
475 params->state.encodedStreamOffset = params->sflv.valueEndOffset;
476 return bejProcessEnding(params, /*canBeEmpty=*/false);
477}
478
479/**
kasunathb8ca2652022-05-17 16:34:35 -0700480 * @brief Decodes a BejEnum type SFLV BEJ tuple.
481 *
482 * @param[in] params - a valid BejHandleTypeFuncParam struct.
483 * @return 0 if successful.
484 */
485static int bejHandleBejEnum(struct BejHandleTypeFuncParam* params)
486{
487 uint16_t sequenceNumber = params->sflv.tupleS.sequenceNumber;
488 if (bejIsArrayElement(params))
489 {
490 sequenceNumber = 0;
491 }
492 const uint8_t* dictionary;
493 const struct BejDictionaryProperty* prop;
494 RETURN_IF_IERROR(
495 bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
496 sequenceNumber, &dictionary, &prop));
497
498 const char* propName = "";
499 if (params->state.addPropertyName)
500 {
501 propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
502 prop->nameLength);
503 }
504
505 if (params->sflv.valueLength == 0)
506 {
507 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
508 propName, params->callbacksDataPtr);
509 }
510 else
511 {
512 // Get the string for enum value.
513 uint16_t enumValueSequenceN =
kasunath0aa36d82022-11-23 14:24:15 -0800514 (uint16_t)(bejGetNnint(params->sflv.value));
kasunathb8ca2652022-05-17 16:34:35 -0700515 const struct BejDictionaryProperty* enumValueProp;
516 RETURN_IF_IERROR(
517 bejDictGetProperty(dictionary, prop->childPointerOffset,
518 enumValueSequenceN, &enumValueProp));
519 const char* enumValueName = bejDictGetPropertyName(
520 dictionary, enumValueProp->nameOffset, enumValueProp->nameLength);
521
522 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackEnum,
523 propName, enumValueName,
524 params->callbacksDataPtr);
525 }
526 // Update the offset to point to the next possible SFLV tuple.
527 params->state.encodedStreamOffset = params->sflv.valueEndOffset;
528 return bejProcessEnding(params, /*canBeEmpty=*/false);
529}
530
531/**
kasunathe2260cd2022-05-17 16:12:54 -0700532 * @brief Decodes a BejString type SFLV BEJ tuple.
533 *
kasunathb8ca2652022-05-17 16:34:35 -0700534 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunathe2260cd2022-05-17 16:12:54 -0700535 * @return 0 if successful.
536 */
537static int bejHandleBejString(struct BejHandleTypeFuncParam* params)
538{
539 // TODO: Handle deferred bindings.
540 const char* propName = bejGetPropName(params);
541
542 if (params->sflv.valueLength == 0)
543 {
544 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
545 propName, params->callbacksDataPtr);
546 }
547 else
548 {
Kasun Athukorala485044b2025-07-15 00:59:36 +0000549 RETURN_IF_CALLBACK_IERROR(
550 params->decodedCallback->callbackString, propName,
551 (const char*)(params->sflv.value), params->sflv.valueLength,
552 params->callbacksDataPtr);
kasunathe2260cd2022-05-17 16:12:54 -0700553 }
554 params->state.encodedStreamOffset = params->sflv.valueEndOffset;
555 return bejProcessEnding(params, /*canBeEmpty=*/false);
556}
557
558/**
kasunathb8ca2652022-05-17 16:34:35 -0700559 * @brief Decodes a BejReal type SFLV BEJ tuple.
560 *
561 * @param[in] params - a valid BejHandleTypeFuncParam struct.
562 * @return 0 if successful.
563 */
564static int bejHandleBejReal(struct BejHandleTypeFuncParam* params)
565{
566 const char* propName = bejGetPropName(params);
567
568 if (params->sflv.valueLength == 0)
569 {
570 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
571 propName, params->callbacksDataPtr);
572 }
573 else
574 {
575 // Real value has the following format.
576 // nnint - Length of whole
577 // bejInteger - whole (includes sign for the overall real number)
578 // nnint - Leading zero count for fract
579 // nnint - fract
580 // nnint - Length of exp
581 // bejInteger - exp (includes sign for the exponent)
kasunath0aa36d82022-11-23 14:24:15 -0800582 uint8_t wholeByteLen = (uint8_t)bejGetNnint(params->sflv.value);
Patrick Williamsbe27f2e2024-08-16 15:22:35 -0400583 const uint8_t* wholeBejInt =
584 params->sflv.value + bejGetNnintSize(params->sflv.value);
kasunathb8ca2652022-05-17 16:34:35 -0700585 const uint8_t* fractZeroCountNnint = wholeBejInt + wholeByteLen;
Patrick Williamsbe27f2e2024-08-16 15:22:35 -0400586 const uint8_t* fractNnint =
587 fractZeroCountNnint + bejGetNnintSize(fractZeroCountNnint);
kasunath0aa36d82022-11-23 14:24:15 -0800588 const uint8_t* lenExpNnint = fractNnint + bejGetNnintSize(fractNnint);
589 const uint8_t* expBejInt = lenExpNnint + bejGetNnintSize(lenExpNnint);
kasunathb8ca2652022-05-17 16:34:35 -0700590
591 struct BejReal realValue;
592 realValue.whole = bejGetIntegerValue(wholeBejInt, wholeByteLen);
kasunath0aa36d82022-11-23 14:24:15 -0800593 realValue.zeroCount = bejGetNnint(fractZeroCountNnint);
594 realValue.fract = bejGetNnint(fractNnint);
595 realValue.expLen = (uint8_t)bejGetNnint(lenExpNnint);
kasunathb8ca2652022-05-17 16:34:35 -0700596 if (realValue.expLen != 0)
597 {
598 realValue.exp = bejGetIntegerValue(
kasunath0aa36d82022-11-23 14:24:15 -0800599 expBejInt, (uint8_t)bejGetNnint(lenExpNnint));
kasunathb8ca2652022-05-17 16:34:35 -0700600 }
601 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackReal,
602 propName, &realValue,
603 params->callbacksDataPtr);
604 }
605 params->state.encodedStreamOffset = params->sflv.valueEndOffset;
606 return bejProcessEnding(params, /*canBeEmpty=*/false);
607}
608
609/**
kasunathe2260cd2022-05-17 16:12:54 -0700610 * @brief Decodes a BejBoolean type SFLV BEJ tuple.
611 *
kasunathb8ca2652022-05-17 16:34:35 -0700612 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunathe2260cd2022-05-17 16:12:54 -0700613 * @return 0 if successful.
614 */
615static int bejHandleBejBoolean(struct BejHandleTypeFuncParam* params)
616{
617 const char* propName = bejGetPropName(params);
618
619 if (params->sflv.valueLength == 0)
620 {
621 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
622 propName, params->callbacksDataPtr);
623 }
624 else
625 {
626 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackBool,
627 propName, *(params->sflv.value) > 0,
628 params->callbacksDataPtr);
629 }
630 params->state.encodedStreamOffset = params->sflv.valueEndOffset;
631 return bejProcessEnding(params, /*canBeEmpty=*/false);
632}
633
634/**
kasunathb8ca2652022-05-17 16:34:35 -0700635 * @brief Decodes a BejPropertyAnnotation type SFLV BEJ tuple.
636 *
637 * @param[in] params - a valid BejHandleTypeFuncParam struct.
638 * @return 0 if successful.
639 */
640static int bejHandleBejPropertyAnnotation(struct BejHandleTypeFuncParam* params)
641{
642 // TODO: Handle colon-delimited string values.
643
644 // Property annotation has the form OuterProperty@Annotation. First
645 // processing the outer property name.
646 const uint8_t* outerDictionary;
647 const struct BejDictionaryProperty* outerProp;
648 RETURN_IF_IERROR(bejGetDictionaryAndProperty(
649 params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber,
650 &outerDictionary, &outerProp));
651
652 const char* propName = bejDictGetPropertyName(
653 outerDictionary, outerProp->nameOffset, outerProp->nameLength);
654 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackAnnotation,
655 propName, params->callbacksDataPtr);
656
657 // Mark the ending of the property annotation.
658 struct BejStackProperty newEnding = {
659 .sectionType = bejSectionNoType,
660 .addPropertyName = params->state.addPropertyName,
661 .mainDictPropOffset = params->state.mainDictPropOffset,
662 .annoDictPropOffset = params->state.annoDictPropOffset,
663 .streamEndOffset = params->sflv.valueEndOffset,
664 };
665 // Update the states for the next encoding segment.
666 RETURN_IF_IERROR(
667 params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
668 params->state.addPropertyName = true;
669 // We might have to change this for nested annotations.
670 params->state.mainDictPropOffset = outerProp->childPointerOffset;
671 // Point to the start of the value for next decoding.
Patrick Williamsbe27f2e2024-08-16 15:22:35 -0400672 params->state.encodedStreamOffset =
673 params->sflv.valueEndOffset - params->sflv.valueLength;
kasunathb8ca2652022-05-17 16:34:35 -0700674 return 0;
675}
676
677/**
kasunath64cb1972022-05-13 12:54:23 -0700678 * @brief Decodes an encoded bej stream.
679 *
680 * @param[in] schemaDictionary - main schema dictionary to use.
681 * @param[in] annotationDictionary - annotation dictionary
682 * @param[in] enStream - encoded stream without the PLDM header.
683 * @param[in] streamLen - length of the enStream.
684 * @param[in] stackCallback - callbacks for stack handlers.
685 * @param[in] decodedCallback - callbacks for extracting decoded properties.
686 * @param[in] callbacksDataPtr - data pointer to pass to decoded callbacks. This
687 * can be used pass additional data.
688 * @param[in] stackDataPtr - data pointer to pass to stack callbacks. This can
689 * be used pass additional data.
690 *
691 * @return 0 if successful.
692 */
693static int bejDecode(const uint8_t* schemaDictionary,
694 const uint8_t* annotationDictionary,
695 const uint8_t* enStream, uint32_t streamLen,
696 const struct BejStackCallback* stackCallback,
697 const struct BejDecodedCallback* decodedCallback,
698 void* callbacksDataPtr, void* stackDataPtr)
699{
700 struct BejHandleTypeFuncParam params = {
701 .state =
702 {
703 // We only add names of set properties. We don't use names for
704 // array
705 // properties. Here we are omitting the name of the root set.
706 .addPropertyName = false,
707 // At start, parent property from the main dictionary is the
708 // first property.
709 .mainDictPropOffset = bejDictGetPropertyHeadOffset(),
710 .annoDictPropOffset = bejDictGetFirstAnnotatedPropertyOffset(),
711 // Current location of the encoded segment we are processing.
712 .encodedStreamOffset = 0,
713 .encodedSubStream = enStream,
714 },
715 .mainDictionary = schemaDictionary,
716 .annotDictionary = annotationDictionary,
717 .decodedCallback = decodedCallback,
718 .stackCallback = stackCallback,
719 .callbacksDataPtr = callbacksDataPtr,
720 .stackDataPtr = stackDataPtr,
721 };
722
Brandon Kim07abbf82025-06-23 21:03:07 +0000723 uint64_t maxOperations = 1000000;
724 uint64_t operationCount = 0;
725
kasunath64cb1972022-05-13 12:54:23 -0700726 while (params.state.encodedStreamOffset < streamLen)
727 {
Brandon Kim07abbf82025-06-23 21:03:07 +0000728 if (++operationCount > maxOperations)
729 {
730 fprintf(stderr, "BEJ decoding exceeded max operations\n");
Brandon Kimef01d4c2025-06-23 21:13:04 +0000731 return bejErrorNotSupported;
Brandon Kim07abbf82025-06-23 21:03:07 +0000732 }
kasunath64cb1972022-05-13 12:54:23 -0700733 // Go to the next encoded segment in the encoded stream.
Patrick Williamsbe27f2e2024-08-16 15:22:35 -0400734 params.state.encodedSubStream =
735 enStream + params.state.encodedStreamOffset;
kasunath64cb1972022-05-13 12:54:23 -0700736 bejInitSFLVStruct(&params);
737
738 if (params.sflv.format.readOnlyProperty)
739 {
740 RETURN_IF_CALLBACK_IERROR(
741 params.decodedCallback->callbackReadonlyProperty,
742 params.sflv.tupleS.sequenceNumber, params.callbacksDataPtr);
743 }
744
745 // TODO: Handle nullable property types. These are indicated by
746 // params.sflv.format.nullableProperty
747 switch (params.sflv.format.principalDataType)
748 {
749 case bejSet:
kasunath34a096d2022-05-17 11:36:14 -0700750 RETURN_IF_IERROR(bejHandleBejSet(&params));
kasunath64cb1972022-05-13 12:54:23 -0700751 break;
752 case bejArray:
kasunathe2260cd2022-05-17 16:12:54 -0700753 RETURN_IF_IERROR(bejHandleBejArray(&params));
kasunath64cb1972022-05-13 12:54:23 -0700754 break;
755 case bejNull:
kasunathe2260cd2022-05-17 16:12:54 -0700756 RETURN_IF_IERROR(bejHandleBejNull(&params));
kasunath64cb1972022-05-13 12:54:23 -0700757 break;
758 case bejInteger:
kasunath34a096d2022-05-17 11:36:14 -0700759 RETURN_IF_IERROR(bejHandleBejInteger(&params));
kasunath64cb1972022-05-13 12:54:23 -0700760 break;
761 case bejEnum:
kasunathb8ca2652022-05-17 16:34:35 -0700762 RETURN_IF_IERROR(bejHandleBejEnum(&params));
kasunath64cb1972022-05-13 12:54:23 -0700763 break;
764 case bejString:
kasunathe2260cd2022-05-17 16:12:54 -0700765 RETURN_IF_IERROR(bejHandleBejString(&params));
kasunath64cb1972022-05-13 12:54:23 -0700766 break;
767 case bejReal:
kasunathb8ca2652022-05-17 16:34:35 -0700768 RETURN_IF_IERROR(bejHandleBejReal(&params));
kasunath64cb1972022-05-13 12:54:23 -0700769 break;
770 case bejBoolean:
kasunathe2260cd2022-05-17 16:12:54 -0700771 RETURN_IF_IERROR(bejHandleBejBoolean(&params));
kasunath64cb1972022-05-13 12:54:23 -0700772 break;
773 case bejBytestring:
774 // TODO: Add support for BejBytestring decoding.
775 fprintf(stderr, "No BejBytestring support\n");
776 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
777 break;
778 case bejChoice:
779 // TODO: Add support for BejChoice decoding.
780 fprintf(stderr, "No BejChoice support\n");
781 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
782 break;
783 case bejPropertyAnnotation:
kasunathb8ca2652022-05-17 16:34:35 -0700784 RETURN_IF_IERROR(bejHandleBejPropertyAnnotation(&params));
kasunath64cb1972022-05-13 12:54:23 -0700785 break;
786 case bejResourceLink:
787 // TODO: Add support for BejResourceLink decoding.
788 fprintf(stderr, "No BejResourceLink support\n");
789 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
790 break;
791 case bejResourceLinkExpansion:
792 // TODO: Add support for BejResourceLinkExpansion decoding.
793 fprintf(stderr, "No BejResourceLinkExpansion support\n");
794 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
795 break;
796 default:
797 break;
798 }
799 }
kasunath34a096d2022-05-17 11:36:14 -0700800 RETURN_IF_IERROR(bejProcessEnding(&params, /*canBeEmpty=*/true));
kasunath64cb1972022-05-13 12:54:23 -0700801 if (!params.stackCallback->stackEmpty(params.stackDataPtr))
802 {
803 fprintf(stderr, "Ending stack should be empty but its not. Something "
804 "must have gone wrong with the encoding\n");
805 return bejErrorUnknown;
806 }
807 return 0;
808}
809
810/**
811 * @brief Check if a bej version is supported by this decoder
812 *
kasunathb8ca2652022-05-17 16:34:35 -0700813 * @param[in] bejVersion - the bej version in the received encoded stream
kasunath64cb1972022-05-13 12:54:23 -0700814 * @return true if supported.
815 */
816static bool bejIsSupported(uint32_t bejVersion)
817{
818 for (uint32_t i = 0; i < sizeof(supportedBejVersions) / sizeof(uint32_t);
819 ++i)
820 {
821 if (bejVersion == supportedBejVersions[i])
822 {
823 return true;
824 }
825 }
826 return false;
827}
828
829int bejDecodePldmBlock(const struct BejDictionaries* dictionaries,
830 const uint8_t* encodedPldmBlock, uint32_t blockLength,
831 const struct BejStackCallback* stackCallback,
832 const struct BejDecodedCallback* decodedCallback,
833 void* callbacksDataPtr, void* stackDataPtr)
834{
kasunath34a096d2022-05-17 11:36:14 -0700835 NULL_CHECK(dictionaries, "dictionaries");
836 NULL_CHECK(dictionaries->schemaDictionary, "schemaDictionary");
837 NULL_CHECK(dictionaries->annotationDictionary, "annotationDictionary");
838
839 NULL_CHECK(encodedPldmBlock, "encodedPldmBlock");
840
841 NULL_CHECK(stackCallback, "stackCallback");
842 NULL_CHECK(stackCallback->stackEmpty, "stackEmpty");
843 NULL_CHECK(stackCallback->stackPeek, "stackPeek");
844 NULL_CHECK(stackCallback->stackPop, "stackPop");
845 NULL_CHECK(stackCallback->stackPush, "stackPush");
846
847 NULL_CHECK(decodedCallback, "decodedCallback");
848
kasunath64cb1972022-05-13 12:54:23 -0700849 uint32_t pldmHeaderSize = sizeof(struct BejPldmBlockHeader);
850 if (blockLength < pldmHeaderSize)
851 {
852 fprintf(stderr, "Invalid pldm block size: %u\n", blockLength);
853 return bejErrorInvalidSize;
854 }
855
856 const struct BejPldmBlockHeader* pldmHeader =
857 (const struct BejPldmBlockHeader*)encodedPldmBlock;
858
859 if (!bejIsSupported(pldmHeader->bejVersion))
860 {
861 fprintf(stderr, "Bej decoder doesn't support the bej version: %u\n",
862 pldmHeader->bejVersion);
Brandon Kimef01d4c2025-06-23 21:13:04 +0000863 return bejErrorNotSupported;
kasunath64cb1972022-05-13 12:54:23 -0700864 }
865
866 if (pldmHeader->schemaClass == bejAnnotationSchemaClass)
867 {
868 fprintf(stderr,
869 "Encoder schema class cannot be BejAnnotationSchemaClass\n");
Brandon Kimef01d4c2025-06-23 21:13:04 +0000870 return bejErrorNotSupported;
kasunath64cb1972022-05-13 12:54:23 -0700871 }
872 // TODO: Add support for CollectionMemberType schema class.
873 if (pldmHeader->schemaClass == bejCollectionMemberTypeSchemaClass)
874 {
875 fprintf(stderr, "Decoder doesn't support "
kasunath34a096d2022-05-17 11:36:14 -0700876 "bejCollectionMemberTypeSchemaClass yet.\n");
Brandon Kimef01d4c2025-06-23 21:13:04 +0000877 return bejErrorNotSupported;
kasunath64cb1972022-05-13 12:54:23 -0700878 }
879 // TODO: Add support for Error schema class.
880 if (pldmHeader->schemaClass == bejErrorSchemaClass)
881 {
882 fprintf(stderr, "Decoder doesn't support BejErrorSchemaClass yet.\n");
Brandon Kimef01d4c2025-06-23 21:13:04 +0000883 return bejErrorNotSupported;
kasunath64cb1972022-05-13 12:54:23 -0700884 }
885
886 // Skip the PLDM header.
887 const uint8_t* enStream = encodedPldmBlock + pldmHeaderSize;
888 uint32_t streamLen = blockLength - pldmHeaderSize;
889 return bejDecode(dictionaries->schemaDictionary,
890 dictionaries->annotationDictionary, enStream, streamLen,
891 stackCallback, decodedCallback, callbacksDataPtr,
892 stackDataPtr);
893}