blob: 73f67c90486176806f8a74e5f5afdc377419f6cf [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 */
Kasun Athukorala6fd03fb2025-07-15 05:19:39 +000093static bool bejInitSFLVStruct(struct BejHandleTypeFuncParam* params)
kasunath64cb1972022-05-13 12:54:23 -070094{
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;
Kasun Athukorala6fd03fb2025-07-15 05:19:39 +0000112
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;
kasunath64cb1972022-05-13 12:54:23 -0700134 sflv->value = params->state.encodedSubStream + localOffset.valueOffset;
Kasun Athukorala6fd03fb2025-07-15 05:19:39 +0000135 return true;
kasunath64cb1972022-05-13 12:54:23 -0700136}
137
138/**
kasunath34a096d2022-05-17 11:36:14 -0700139 * @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 */
Patrick Williams045a2d62025-02-01 08:23:55 -0500148static uint32_t bejGetFirstTupleOffset(
149 const struct BejHandleTypeFuncParam* params)
kasunath34a096d2022-05-17 11:36:14 -0700150{
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 +
kasunath0aa36d82022-11-23 14:24:15 -0800156 bejGetNnintSize(params->sflv.value);
kasunath34a096d2022-05-17 11:36:14 -0700157}
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 */
Patrick Williamsbe27f2e2024-08-16 15:22:35 -0400174static int bejGetDictionaryAndProperty(
175 const struct BejHandleTypeFuncParam* params, uint8_t schemaType,
176 uint32_t sequenceNumber, const uint8_t** dictionary,
177 const struct BejDictionaryProperty** prop)
kasunath34a096d2022-05-17 11:36:14 -0700178{
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
Patrick Williamsbe27f2e2024-08-16 15:22:35 -0400198 int ret =
199 bejDictGetProperty(*dictionary, dictPropOffset, sequenceNumber, prop);
kasunath34a096d2022-05-17 11:36:14 -0700200 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/**
kasunathe2260cd2022-05-17 16:12:54 -0700210 * @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.
kasunath34a096d2022-05-17 11:36:14 -0700212 *
213 * @param[in] params - a valid populated BejHandleTypeFuncParam.
214 * @return 0 if successful.
215 */
kasunathe2260cd2022-05-17 16:12:54 -0700216static const char* bejGetPropName(struct BejHandleTypeFuncParam* params)
kasunath34a096d2022-05-17 11:36:14 -0700217{
218 const uint8_t* dictionary;
219 const struct BejDictionaryProperty* prop;
kasunathe2260cd2022-05-17 16:12:54 -0700220 if (!params->state.addPropertyName ||
221 (bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
222 params->sflv.tupleS.sequenceNumber,
223 &dictionary, &prop) != 0))
kasunath34a096d2022-05-17 11:36:14 -0700224 {
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 */
245static 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 */
305static 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 *
kasunathb8ca2652022-05-17 16:34:35 -0700324 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunath34a096d2022-05-17 11:36:14 -0700325 * @return 0 if successful.
326 */
327static 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
kasunathc14fab62022-11-23 16:18:21 -0800352 // 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
kasunath0aa36d82022-11-23 14:24:15 -0800356 uint64_t elements = bejGetNnint(params->sflv.value);
kasunath34a096d2022-05-17 11:36:14 -0700357 // 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);
kasunathc14fab62022-11-23 16:18:21 -0800362 // 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;
kasunath34a096d2022-05-17 11:36:14 -0700385 }
386 else
387 {
kasunathc14fab62022-11-23 16:18:21 -0800388 params->state.mainDictPropOffset = prop->childPointerOffset;
kasunath34a096d2022-05-17 11:36:14 -0700389 }
kasunath34a096d2022-05-17 11:36:14 -0700390 return 0;
391}
392
393/**
kasunathe2260cd2022-05-17 16:12:54 -0700394 * @brief Decodes a BejArray type SFLV BEJ tuple.
395 *
kasunathb8ca2652022-05-17 16:34:35 -0700396 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunathe2260cd2022-05-17 16:12:54 -0700397 * @return 0 if successful.
398 */
399static 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
kasunathc14fab62022-11-23 16:18:21 -0800417 // 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
kasunath0aa36d82022-11-23 14:24:15 -0800421 uint64_t elements = bejGetNnint(params->sflv.value);
kasunathe2260cd2022-05-17 16:12:54 -0700422 // 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);
kasunathc14fab62022-11-23 16:18:21 -0800427 // 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;
kasunathe2260cd2022-05-17 16:12:54 -0700451 }
452 else
453 {
kasunathc14fab62022-11-23 16:18:21 -0800454 params->state.mainDictPropOffset = prop->childPointerOffset;
kasunathe2260cd2022-05-17 16:12:54 -0700455 }
kasunathe2260cd2022-05-17 16:12:54 -0700456 return 0;
457}
458
459/**
460 * @brief Decodes a BejNull type SFLV BEJ tuple.
461 *
kasunathb8ca2652022-05-17 16:34:35 -0700462 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunathe2260cd2022-05-17 16:12:54 -0700463 * @return 0 if successful.
464 */
465static 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/**
kasunath34a096d2022-05-17 11:36:14 -0700475 * @brief Decodes a BejInteger type SFLV BEJ tuple.
476 *
kasunathb8ca2652022-05-17 16:34:35 -0700477 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunath34a096d2022-05-17 11:36:14 -0700478 * @return 0 if successful.
479 */
480static int bejHandleBejInteger(struct BejHandleTypeFuncParam* params)
481{
kasunathe2260cd2022-05-17 16:12:54 -0700482 const char* propName = bejGetPropName(params);
kasunath34a096d2022-05-17 11:36:14 -0700483
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/**
kasunathb8ca2652022-05-17 16:34:35 -0700501 * @brief Decodes a BejEnum type SFLV BEJ tuple.
502 *
503 * @param[in] params - a valid BejHandleTypeFuncParam struct.
504 * @return 0 if successful.
505 */
506static 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 =
kasunath0aa36d82022-11-23 14:24:15 -0800535 (uint16_t)(bejGetNnint(params->sflv.value));
kasunathb8ca2652022-05-17 16:34:35 -0700536 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/**
kasunathe2260cd2022-05-17 16:12:54 -0700553 * @brief Decodes a BejString type SFLV BEJ tuple.
554 *
kasunathb8ca2652022-05-17 16:34:35 -0700555 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunathe2260cd2022-05-17 16:12:54 -0700556 * @return 0 if successful.
557 */
558static 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 {
Kasun Athukorala485044b2025-07-15 00:59:36 +0000570 RETURN_IF_CALLBACK_IERROR(
571 params->decodedCallback->callbackString, propName,
572 (const char*)(params->sflv.value), params->sflv.valueLength,
573 params->callbacksDataPtr);
kasunathe2260cd2022-05-17 16:12:54 -0700574 }
575 params->state.encodedStreamOffset = params->sflv.valueEndOffset;
576 return bejProcessEnding(params, /*canBeEmpty=*/false);
577}
578
579/**
kasunathb8ca2652022-05-17 16:34:35 -0700580 * @brief Decodes a BejReal type SFLV BEJ tuple.
581 *
582 * @param[in] params - a valid BejHandleTypeFuncParam struct.
583 * @return 0 if successful.
584 */
585static 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)
kasunath0aa36d82022-11-23 14:24:15 -0800603 uint8_t wholeByteLen = (uint8_t)bejGetNnint(params->sflv.value);
Patrick Williamsbe27f2e2024-08-16 15:22:35 -0400604 const uint8_t* wholeBejInt =
605 params->sflv.value + bejGetNnintSize(params->sflv.value);
kasunathb8ca2652022-05-17 16:34:35 -0700606 const uint8_t* fractZeroCountNnint = wholeBejInt + wholeByteLen;
Patrick Williamsbe27f2e2024-08-16 15:22:35 -0400607 const uint8_t* fractNnint =
608 fractZeroCountNnint + bejGetNnintSize(fractZeroCountNnint);
kasunath0aa36d82022-11-23 14:24:15 -0800609 const uint8_t* lenExpNnint = fractNnint + bejGetNnintSize(fractNnint);
610 const uint8_t* expBejInt = lenExpNnint + bejGetNnintSize(lenExpNnint);
kasunathb8ca2652022-05-17 16:34:35 -0700611
612 struct BejReal realValue;
613 realValue.whole = bejGetIntegerValue(wholeBejInt, wholeByteLen);
kasunath0aa36d82022-11-23 14:24:15 -0800614 realValue.zeroCount = bejGetNnint(fractZeroCountNnint);
615 realValue.fract = bejGetNnint(fractNnint);
616 realValue.expLen = (uint8_t)bejGetNnint(lenExpNnint);
kasunathb8ca2652022-05-17 16:34:35 -0700617 if (realValue.expLen != 0)
618 {
619 realValue.exp = bejGetIntegerValue(
kasunath0aa36d82022-11-23 14:24:15 -0800620 expBejInt, (uint8_t)bejGetNnint(lenExpNnint));
kasunathb8ca2652022-05-17 16:34:35 -0700621 }
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/**
kasunathe2260cd2022-05-17 16:12:54 -0700631 * @brief Decodes a BejBoolean type SFLV BEJ tuple.
632 *
kasunathb8ca2652022-05-17 16:34:35 -0700633 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunathe2260cd2022-05-17 16:12:54 -0700634 * @return 0 if successful.
635 */
636static 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/**
kasunathb8ca2652022-05-17 16:34:35 -0700656 * @brief Decodes a BejPropertyAnnotation type SFLV BEJ tuple.
657 *
658 * @param[in] params - a valid BejHandleTypeFuncParam struct.
659 * @return 0 if successful.
660 */
661static 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.
Patrick Williamsbe27f2e2024-08-16 15:22:35 -0400693 params->state.encodedStreamOffset =
694 params->sflv.valueEndOffset - params->sflv.valueLength;
kasunathb8ca2652022-05-17 16:34:35 -0700695 return 0;
696}
697
698/**
kasunath64cb1972022-05-13 12:54:23 -0700699 * @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 */
714static 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
Brandon Kim07abbf82025-06-23 21:03:07 +0000744 uint64_t maxOperations = 1000000;
745 uint64_t operationCount = 0;
746
kasunath64cb1972022-05-13 12:54:23 -0700747 while (params.state.encodedStreamOffset < streamLen)
748 {
Brandon Kim07abbf82025-06-23 21:03:07 +0000749 if (++operationCount > maxOperations)
750 {
751 fprintf(stderr, "BEJ decoding exceeded max operations\n");
Brandon Kimef01d4c2025-06-23 21:13:04 +0000752 return bejErrorNotSupported;
Brandon Kim07abbf82025-06-23 21:03:07 +0000753 }
kasunath64cb1972022-05-13 12:54:23 -0700754 // Go to the next encoded segment in the encoded stream.
Patrick Williamsbe27f2e2024-08-16 15:22:35 -0400755 params.state.encodedSubStream =
756 enStream + params.state.encodedStreamOffset;
Kasun Athukorala6fd03fb2025-07-15 05:19:39 +0000757 if (!bejInitSFLVStruct(&params))
758 {
759 return bejErrorInvalidSize;
760 }
761
762 // Make sure that the next value segment (SFLV) is within the streamLen
763 if (params.sflv.valueEndOffset > streamLen)
764 {
765 fprintf(
766 stderr,
767 "Value goes beyond stream length. SFLV Offset: %u, valueEndOffset: %u, streamLen: %u\n",
768 params.state.encodedStreamOffset, params.sflv.valueEndOffset,
769 streamLen);
770 return bejErrorInvalidSize;
771 }
kasunath64cb1972022-05-13 12:54:23 -0700772
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:
kasunath34a096d2022-05-17 11:36:14 -0700785 RETURN_IF_IERROR(bejHandleBejSet(&params));
kasunath64cb1972022-05-13 12:54:23 -0700786 break;
787 case bejArray:
kasunathe2260cd2022-05-17 16:12:54 -0700788 RETURN_IF_IERROR(bejHandleBejArray(&params));
kasunath64cb1972022-05-13 12:54:23 -0700789 break;
790 case bejNull:
kasunathe2260cd2022-05-17 16:12:54 -0700791 RETURN_IF_IERROR(bejHandleBejNull(&params));
kasunath64cb1972022-05-13 12:54:23 -0700792 break;
793 case bejInteger:
kasunath34a096d2022-05-17 11:36:14 -0700794 RETURN_IF_IERROR(bejHandleBejInteger(&params));
kasunath64cb1972022-05-13 12:54:23 -0700795 break;
796 case bejEnum:
kasunathb8ca2652022-05-17 16:34:35 -0700797 RETURN_IF_IERROR(bejHandleBejEnum(&params));
kasunath64cb1972022-05-13 12:54:23 -0700798 break;
799 case bejString:
kasunathe2260cd2022-05-17 16:12:54 -0700800 RETURN_IF_IERROR(bejHandleBejString(&params));
kasunath64cb1972022-05-13 12:54:23 -0700801 break;
802 case bejReal:
kasunathb8ca2652022-05-17 16:34:35 -0700803 RETURN_IF_IERROR(bejHandleBejReal(&params));
kasunath64cb1972022-05-13 12:54:23 -0700804 break;
805 case bejBoolean:
kasunathe2260cd2022-05-17 16:12:54 -0700806 RETURN_IF_IERROR(bejHandleBejBoolean(&params));
kasunath64cb1972022-05-13 12:54:23 -0700807 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:
kasunathb8ca2652022-05-17 16:34:35 -0700819 RETURN_IF_IERROR(bejHandleBejPropertyAnnotation(&params));
kasunath64cb1972022-05-13 12:54:23 -0700820 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 }
kasunath34a096d2022-05-17 11:36:14 -0700835 RETURN_IF_IERROR(bejProcessEnding(&params, /*canBeEmpty=*/true));
kasunath64cb1972022-05-13 12:54:23 -0700836 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 *
kasunathb8ca2652022-05-17 16:34:35 -0700848 * @param[in] bejVersion - the bej version in the received encoded stream
kasunath64cb1972022-05-13 12:54:23 -0700849 * @return true if supported.
850 */
851static 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
864int 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{
kasunath34a096d2022-05-17 11:36:14 -0700870 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
kasunath64cb1972022-05-13 12:54:23 -0700884 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);
Brandon Kimef01d4c2025-06-23 21:13:04 +0000898 return bejErrorNotSupported;
kasunath64cb1972022-05-13 12:54:23 -0700899 }
900
901 if (pldmHeader->schemaClass == bejAnnotationSchemaClass)
902 {
903 fprintf(stderr,
904 "Encoder schema class cannot be BejAnnotationSchemaClass\n");
Brandon Kimef01d4c2025-06-23 21:13:04 +0000905 return bejErrorNotSupported;
kasunath64cb1972022-05-13 12:54:23 -0700906 }
907 // TODO: Add support for CollectionMemberType schema class.
908 if (pldmHeader->schemaClass == bejCollectionMemberTypeSchemaClass)
909 {
910 fprintf(stderr, "Decoder doesn't support "
kasunath34a096d2022-05-17 11:36:14 -0700911 "bejCollectionMemberTypeSchemaClass yet.\n");
Brandon Kimef01d4c2025-06-23 21:13:04 +0000912 return bejErrorNotSupported;
kasunath64cb1972022-05-13 12:54:23 -0700913 }
914 // TODO: Add support for Error schema class.
915 if (pldmHeader->schemaClass == bejErrorSchemaClass)
916 {
917 fprintf(stderr, "Decoder doesn't support BejErrorSchemaClass yet.\n");
Brandon Kimef01d4c2025-06-23 21:13:04 +0000918 return bejErrorNotSupported;
kasunath64cb1972022-05-13 12:54:23 -0700919 }
920
Kasun Athukorala7c1be8d2025-07-15 05:44:04 +0000921 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
kasunath64cb1972022-05-13 12:54:23 -0700959 // 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}