blob: efed4fc38689842cc543f8d7550afe0d2579b0ed [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.
Patrick Williams435526a2023-05-10 14:44:12 -050093 const uint32_t valueOffset = valueLenNnintOffset + sizeof(uint8_t) +
94 valueLengthSize;
kasunath64cb1972022-05-13 12:54:23 -070095
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
Patrick Williams435526a2023-05-10 14:44:12 -0500193 int ret = bejDictGetProperty(*dictionary, dictPropOffset, sequenceNumber,
194 prop);
kasunath34a096d2022-05-17 11:36:14 -0700195 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
kasunathc14fab62022-11-23 16:18:21 -0800347 // Move the offset to the next SFLV tuple (or end). Make sure that this is
348 // called before calling bejProcessEnding.
349 params->state.encodedStreamOffset = bejGetFirstTupleOffset(params);
350
kasunath0aa36d82022-11-23 14:24:15 -0800351 uint64_t elements = bejGetNnint(params->sflv.value);
kasunath34a096d2022-05-17 11:36:14 -0700352 // If its an empty set, we are done here.
353 if (elements == 0)
354 {
355 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackSetEnd,
356 params->callbacksDataPtr);
kasunathc14fab62022-11-23 16:18:21 -0800357 // Since this is an ending of a property (empty array), we should call
358 // bejProcessEnding. Unless the whole JSON object is an empty set (which
359 // shouldn't be the case), stack cannot be empty.
360 bejProcessEnding(params, /*canBeEmpty=*/false);
361 return 0;
362 }
363
364 // Update the states for the next encoding segment.
365 struct BejStackProperty newEnding = {
366 .sectionType = bejSectionSet,
367 .addPropertyName = params->state.addPropertyName,
368 .mainDictPropOffset = params->state.mainDictPropOffset,
369 .annoDictPropOffset = params->state.annoDictPropOffset,
370 .streamEndOffset = params->sflv.valueEndOffset,
371 };
372 RETURN_IF_IERROR(
373 params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
374 params->state.addPropertyName = true;
375 if (params->sflv.tupleS.schema == bejAnnotation)
376 {
377 // Since this set is an annotated type, we need to advance the
378 // annotation dictionary for decoding the next segment.
379 params->state.annoDictPropOffset = prop->childPointerOffset;
kasunath34a096d2022-05-17 11:36:14 -0700380 }
381 else
382 {
kasunathc14fab62022-11-23 16:18:21 -0800383 params->state.mainDictPropOffset = prop->childPointerOffset;
kasunath34a096d2022-05-17 11:36:14 -0700384 }
kasunath34a096d2022-05-17 11:36:14 -0700385 return 0;
386}
387
388/**
kasunathe2260cd2022-05-17 16:12:54 -0700389 * @brief Decodes a BejArray type SFLV BEJ tuple.
390 *
kasunathb8ca2652022-05-17 16:34:35 -0700391 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunathe2260cd2022-05-17 16:12:54 -0700392 * @return 0 if successful.
393 */
394static int bejHandleBejArray(struct BejHandleTypeFuncParam* params)
395{
396 const uint8_t* dictionary;
397 const struct BejDictionaryProperty* prop;
398 RETURN_IF_IERROR(bejGetDictionaryAndProperty(
399 params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber,
400 &dictionary, &prop));
401
402 const char* propName = "";
403 if (params->state.addPropertyName)
404 {
405 propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
406 prop->nameLength);
407 }
408
409 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayStart,
410 propName, params->callbacksDataPtr);
411
kasunathc14fab62022-11-23 16:18:21 -0800412 // Move the offset to the next SFLV tuple (or end). Make sure that this is
413 // called before calling bejProcessEnding.
414 params->state.encodedStreamOffset = bejGetFirstTupleOffset(params);
415
kasunath0aa36d82022-11-23 14:24:15 -0800416 uint64_t elements = bejGetNnint(params->sflv.value);
kasunathe2260cd2022-05-17 16:12:54 -0700417 // If its an empty array, we are done here.
418 if (elements == 0)
419 {
420 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayEnd,
421 params->callbacksDataPtr);
kasunathc14fab62022-11-23 16:18:21 -0800422 // Since this is an ending of a property (empty array), we should call
423 // bejProcessEnding. Stack cannot be empty since there should be at
424 // least 1 parent in the stack.
425 bejProcessEnding(params, /*canBeEmpty=*/false);
426 return 0;
427 }
428
429 // Update the state for next segment decoding.
430 struct BejStackProperty newEnding = {
431 .sectionType = bejSectionArray,
432 .addPropertyName = params->state.addPropertyName,
433 .mainDictPropOffset = params->state.mainDictPropOffset,
434 .annoDictPropOffset = params->state.annoDictPropOffset,
435 .streamEndOffset = params->sflv.valueEndOffset,
436 };
437 RETURN_IF_IERROR(
438 params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
439 // We do not add property names for array elements.
440 params->state.addPropertyName = false;
441 if (params->sflv.tupleS.schema == bejAnnotation)
442 {
443 // Since this array is an annotated type, we need to advance the
444 // annotation dictionary for decoding the next segment.
445 params->state.annoDictPropOffset = prop->childPointerOffset;
kasunathe2260cd2022-05-17 16:12:54 -0700446 }
447 else
448 {
kasunathc14fab62022-11-23 16:18:21 -0800449 params->state.mainDictPropOffset = prop->childPointerOffset;
kasunathe2260cd2022-05-17 16:12:54 -0700450 }
kasunathe2260cd2022-05-17 16:12:54 -0700451 return 0;
452}
453
454/**
455 * @brief Decodes a BejNull type SFLV BEJ tuple.
456 *
kasunathb8ca2652022-05-17 16:34:35 -0700457 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunathe2260cd2022-05-17 16:12:54 -0700458 * @return 0 if successful.
459 */
460static int bejHandleBejNull(struct BejHandleTypeFuncParam* params)
461{
462 const char* propName = bejGetPropName(params);
463 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, propName,
464 params->callbacksDataPtr);
465 params->state.encodedStreamOffset = params->sflv.valueEndOffset;
466 return bejProcessEnding(params, /*canBeEmpty=*/false);
467}
468
469/**
kasunath34a096d2022-05-17 11:36:14 -0700470 * @brief Decodes a BejInteger type SFLV BEJ tuple.
471 *
kasunathb8ca2652022-05-17 16:34:35 -0700472 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunath34a096d2022-05-17 11:36:14 -0700473 * @return 0 if successful.
474 */
475static int bejHandleBejInteger(struct BejHandleTypeFuncParam* params)
476{
kasunathe2260cd2022-05-17 16:12:54 -0700477 const char* propName = bejGetPropName(params);
kasunath34a096d2022-05-17 11:36:14 -0700478
479 if (params->sflv.valueLength == 0)
480 {
481 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
482 propName, params->callbacksDataPtr);
483 }
484 else
485 {
486 RETURN_IF_CALLBACK_IERROR(
487 params->decodedCallback->callbackInteger, propName,
488 bejGetIntegerValue(params->sflv.value, params->sflv.valueLength),
489 params->callbacksDataPtr);
490 }
491 params->state.encodedStreamOffset = params->sflv.valueEndOffset;
492 return bejProcessEnding(params, /*canBeEmpty=*/false);
493}
494
495/**
kasunathb8ca2652022-05-17 16:34:35 -0700496 * @brief Decodes a BejEnum type SFLV BEJ tuple.
497 *
498 * @param[in] params - a valid BejHandleTypeFuncParam struct.
499 * @return 0 if successful.
500 */
501static int bejHandleBejEnum(struct BejHandleTypeFuncParam* params)
502{
503 uint16_t sequenceNumber = params->sflv.tupleS.sequenceNumber;
504 if (bejIsArrayElement(params))
505 {
506 sequenceNumber = 0;
507 }
508 const uint8_t* dictionary;
509 const struct BejDictionaryProperty* prop;
510 RETURN_IF_IERROR(
511 bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
512 sequenceNumber, &dictionary, &prop));
513
514 const char* propName = "";
515 if (params->state.addPropertyName)
516 {
517 propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
518 prop->nameLength);
519 }
520
521 if (params->sflv.valueLength == 0)
522 {
523 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
524 propName, params->callbacksDataPtr);
525 }
526 else
527 {
528 // Get the string for enum value.
529 uint16_t enumValueSequenceN =
kasunath0aa36d82022-11-23 14:24:15 -0800530 (uint16_t)(bejGetNnint(params->sflv.value));
kasunathb8ca2652022-05-17 16:34:35 -0700531 const struct BejDictionaryProperty* enumValueProp;
532 RETURN_IF_IERROR(
533 bejDictGetProperty(dictionary, prop->childPointerOffset,
534 enumValueSequenceN, &enumValueProp));
535 const char* enumValueName = bejDictGetPropertyName(
536 dictionary, enumValueProp->nameOffset, enumValueProp->nameLength);
537
538 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackEnum,
539 propName, enumValueName,
540 params->callbacksDataPtr);
541 }
542 // Update the offset to point to the next possible SFLV tuple.
543 params->state.encodedStreamOffset = params->sflv.valueEndOffset;
544 return bejProcessEnding(params, /*canBeEmpty=*/false);
545}
546
547/**
kasunathe2260cd2022-05-17 16:12:54 -0700548 * @brief Decodes a BejString type SFLV BEJ tuple.
549 *
kasunathb8ca2652022-05-17 16:34:35 -0700550 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunathe2260cd2022-05-17 16:12:54 -0700551 * @return 0 if successful.
552 */
553static int bejHandleBejString(struct BejHandleTypeFuncParam* params)
554{
555 // TODO: Handle deferred bindings.
556 const char* propName = bejGetPropName(params);
557
558 if (params->sflv.valueLength == 0)
559 {
560 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
561 propName, params->callbacksDataPtr);
562 }
563 else
564 {
565 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackString,
566 propName, (const char*)(params->sflv.value),
567 params->callbacksDataPtr);
568 }
569 params->state.encodedStreamOffset = params->sflv.valueEndOffset;
570 return bejProcessEnding(params, /*canBeEmpty=*/false);
571}
572
573/**
kasunathb8ca2652022-05-17 16:34:35 -0700574 * @brief Decodes a BejReal type SFLV BEJ tuple.
575 *
576 * @param[in] params - a valid BejHandleTypeFuncParam struct.
577 * @return 0 if successful.
578 */
579static int bejHandleBejReal(struct BejHandleTypeFuncParam* params)
580{
581 const char* propName = bejGetPropName(params);
582
583 if (params->sflv.valueLength == 0)
584 {
585 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
586 propName, params->callbacksDataPtr);
587 }
588 else
589 {
590 // Real value has the following format.
591 // nnint - Length of whole
592 // bejInteger - whole (includes sign for the overall real number)
593 // nnint - Leading zero count for fract
594 // nnint - fract
595 // nnint - Length of exp
596 // bejInteger - exp (includes sign for the exponent)
kasunath0aa36d82022-11-23 14:24:15 -0800597 uint8_t wholeByteLen = (uint8_t)bejGetNnint(params->sflv.value);
Patrick Williams435526a2023-05-10 14:44:12 -0500598 const uint8_t* wholeBejInt = params->sflv.value +
599 bejGetNnintSize(params->sflv.value);
kasunathb8ca2652022-05-17 16:34:35 -0700600 const uint8_t* fractZeroCountNnint = wholeBejInt + wholeByteLen;
Patrick Williams435526a2023-05-10 14:44:12 -0500601 const uint8_t* fractNnint = fractZeroCountNnint +
602 bejGetNnintSize(fractZeroCountNnint);
kasunath0aa36d82022-11-23 14:24:15 -0800603 const uint8_t* lenExpNnint = fractNnint + bejGetNnintSize(fractNnint);
604 const uint8_t* expBejInt = lenExpNnint + bejGetNnintSize(lenExpNnint);
kasunathb8ca2652022-05-17 16:34:35 -0700605
606 struct BejReal realValue;
607 realValue.whole = bejGetIntegerValue(wholeBejInt, wholeByteLen);
kasunath0aa36d82022-11-23 14:24:15 -0800608 realValue.zeroCount = bejGetNnint(fractZeroCountNnint);
609 realValue.fract = bejGetNnint(fractNnint);
610 realValue.expLen = (uint8_t)bejGetNnint(lenExpNnint);
kasunathb8ca2652022-05-17 16:34:35 -0700611 if (realValue.expLen != 0)
612 {
613 realValue.exp = bejGetIntegerValue(
kasunath0aa36d82022-11-23 14:24:15 -0800614 expBejInt, (uint8_t)bejGetNnint(lenExpNnint));
kasunathb8ca2652022-05-17 16:34:35 -0700615 }
616 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackReal,
617 propName, &realValue,
618 params->callbacksDataPtr);
619 }
620 params->state.encodedStreamOffset = params->sflv.valueEndOffset;
621 return bejProcessEnding(params, /*canBeEmpty=*/false);
622}
623
624/**
kasunathe2260cd2022-05-17 16:12:54 -0700625 * @brief Decodes a BejBoolean type SFLV BEJ tuple.
626 *
kasunathb8ca2652022-05-17 16:34:35 -0700627 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunathe2260cd2022-05-17 16:12:54 -0700628 * @return 0 if successful.
629 */
630static int bejHandleBejBoolean(struct BejHandleTypeFuncParam* params)
631{
632 const char* propName = bejGetPropName(params);
633
634 if (params->sflv.valueLength == 0)
635 {
636 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
637 propName, params->callbacksDataPtr);
638 }
639 else
640 {
641 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackBool,
642 propName, *(params->sflv.value) > 0,
643 params->callbacksDataPtr);
644 }
645 params->state.encodedStreamOffset = params->sflv.valueEndOffset;
646 return bejProcessEnding(params, /*canBeEmpty=*/false);
647}
648
649/**
kasunathb8ca2652022-05-17 16:34:35 -0700650 * @brief Decodes a BejPropertyAnnotation type SFLV BEJ tuple.
651 *
652 * @param[in] params - a valid BejHandleTypeFuncParam struct.
653 * @return 0 if successful.
654 */
655static int bejHandleBejPropertyAnnotation(struct BejHandleTypeFuncParam* params)
656{
657 // TODO: Handle colon-delimited string values.
658
659 // Property annotation has the form OuterProperty@Annotation. First
660 // processing the outer property name.
661 const uint8_t* outerDictionary;
662 const struct BejDictionaryProperty* outerProp;
663 RETURN_IF_IERROR(bejGetDictionaryAndProperty(
664 params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber,
665 &outerDictionary, &outerProp));
666
667 const char* propName = bejDictGetPropertyName(
668 outerDictionary, outerProp->nameOffset, outerProp->nameLength);
669 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackAnnotation,
670 propName, params->callbacksDataPtr);
671
672 // Mark the ending of the property annotation.
673 struct BejStackProperty newEnding = {
674 .sectionType = bejSectionNoType,
675 .addPropertyName = params->state.addPropertyName,
676 .mainDictPropOffset = params->state.mainDictPropOffset,
677 .annoDictPropOffset = params->state.annoDictPropOffset,
678 .streamEndOffset = params->sflv.valueEndOffset,
679 };
680 // Update the states for the next encoding segment.
681 RETURN_IF_IERROR(
682 params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
683 params->state.addPropertyName = true;
684 // We might have to change this for nested annotations.
685 params->state.mainDictPropOffset = outerProp->childPointerOffset;
686 // Point to the start of the value for next decoding.
Patrick Williams435526a2023-05-10 14:44:12 -0500687 params->state.encodedStreamOffset = params->sflv.valueEndOffset -
688 params->sflv.valueLength;
kasunathb8ca2652022-05-17 16:34:35 -0700689 return 0;
690}
691
692/**
kasunath64cb1972022-05-13 12:54:23 -0700693 * @brief Decodes an encoded bej stream.
694 *
695 * @param[in] schemaDictionary - main schema dictionary to use.
696 * @param[in] annotationDictionary - annotation dictionary
697 * @param[in] enStream - encoded stream without the PLDM header.
698 * @param[in] streamLen - length of the enStream.
699 * @param[in] stackCallback - callbacks for stack handlers.
700 * @param[in] decodedCallback - callbacks for extracting decoded properties.
701 * @param[in] callbacksDataPtr - data pointer to pass to decoded callbacks. This
702 * can be used pass additional data.
703 * @param[in] stackDataPtr - data pointer to pass to stack callbacks. This can
704 * be used pass additional data.
705 *
706 * @return 0 if successful.
707 */
708static int bejDecode(const uint8_t* schemaDictionary,
709 const uint8_t* annotationDictionary,
710 const uint8_t* enStream, uint32_t streamLen,
711 const struct BejStackCallback* stackCallback,
712 const struct BejDecodedCallback* decodedCallback,
713 void* callbacksDataPtr, void* stackDataPtr)
714{
715 struct BejHandleTypeFuncParam params = {
716 .state =
717 {
718 // We only add names of set properties. We don't use names for
719 // array
720 // properties. Here we are omitting the name of the root set.
721 .addPropertyName = false,
722 // At start, parent property from the main dictionary is the
723 // first property.
724 .mainDictPropOffset = bejDictGetPropertyHeadOffset(),
725 .annoDictPropOffset = bejDictGetFirstAnnotatedPropertyOffset(),
726 // Current location of the encoded segment we are processing.
727 .encodedStreamOffset = 0,
728 .encodedSubStream = enStream,
729 },
730 .mainDictionary = schemaDictionary,
731 .annotDictionary = annotationDictionary,
732 .decodedCallback = decodedCallback,
733 .stackCallback = stackCallback,
734 .callbacksDataPtr = callbacksDataPtr,
735 .stackDataPtr = stackDataPtr,
736 };
737
738 while (params.state.encodedStreamOffset < streamLen)
739 {
740 // Go to the next encoded segment in the encoded stream.
Patrick Williams435526a2023-05-10 14:44:12 -0500741 params.state.encodedSubStream = enStream +
742 params.state.encodedStreamOffset;
kasunath64cb1972022-05-13 12:54:23 -0700743 bejInitSFLVStruct(&params);
744
745 if (params.sflv.format.readOnlyProperty)
746 {
747 RETURN_IF_CALLBACK_IERROR(
748 params.decodedCallback->callbackReadonlyProperty,
749 params.sflv.tupleS.sequenceNumber, params.callbacksDataPtr);
750 }
751
752 // TODO: Handle nullable property types. These are indicated by
753 // params.sflv.format.nullableProperty
754 switch (params.sflv.format.principalDataType)
755 {
756 case bejSet:
kasunath34a096d2022-05-17 11:36:14 -0700757 RETURN_IF_IERROR(bejHandleBejSet(&params));
kasunath64cb1972022-05-13 12:54:23 -0700758 break;
759 case bejArray:
kasunathe2260cd2022-05-17 16:12:54 -0700760 RETURN_IF_IERROR(bejHandleBejArray(&params));
kasunath64cb1972022-05-13 12:54:23 -0700761 break;
762 case bejNull:
kasunathe2260cd2022-05-17 16:12:54 -0700763 RETURN_IF_IERROR(bejHandleBejNull(&params));
kasunath64cb1972022-05-13 12:54:23 -0700764 break;
765 case bejInteger:
kasunath34a096d2022-05-17 11:36:14 -0700766 RETURN_IF_IERROR(bejHandleBejInteger(&params));
kasunath64cb1972022-05-13 12:54:23 -0700767 break;
768 case bejEnum:
kasunathb8ca2652022-05-17 16:34:35 -0700769 RETURN_IF_IERROR(bejHandleBejEnum(&params));
kasunath64cb1972022-05-13 12:54:23 -0700770 break;
771 case bejString:
kasunathe2260cd2022-05-17 16:12:54 -0700772 RETURN_IF_IERROR(bejHandleBejString(&params));
kasunath64cb1972022-05-13 12:54:23 -0700773 break;
774 case bejReal:
kasunathb8ca2652022-05-17 16:34:35 -0700775 RETURN_IF_IERROR(bejHandleBejReal(&params));
kasunath64cb1972022-05-13 12:54:23 -0700776 break;
777 case bejBoolean:
kasunathe2260cd2022-05-17 16:12:54 -0700778 RETURN_IF_IERROR(bejHandleBejBoolean(&params));
kasunath64cb1972022-05-13 12:54:23 -0700779 break;
780 case bejBytestring:
781 // TODO: Add support for BejBytestring decoding.
782 fprintf(stderr, "No BejBytestring support\n");
783 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
784 break;
785 case bejChoice:
786 // TODO: Add support for BejChoice decoding.
787 fprintf(stderr, "No BejChoice support\n");
788 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
789 break;
790 case bejPropertyAnnotation:
kasunathb8ca2652022-05-17 16:34:35 -0700791 RETURN_IF_IERROR(bejHandleBejPropertyAnnotation(&params));
kasunath64cb1972022-05-13 12:54:23 -0700792 break;
793 case bejResourceLink:
794 // TODO: Add support for BejResourceLink decoding.
795 fprintf(stderr, "No BejResourceLink support\n");
796 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
797 break;
798 case bejResourceLinkExpansion:
799 // TODO: Add support for BejResourceLinkExpansion decoding.
800 fprintf(stderr, "No BejResourceLinkExpansion support\n");
801 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
802 break;
803 default:
804 break;
805 }
806 }
kasunath34a096d2022-05-17 11:36:14 -0700807 RETURN_IF_IERROR(bejProcessEnding(&params, /*canBeEmpty=*/true));
kasunath64cb1972022-05-13 12:54:23 -0700808 if (!params.stackCallback->stackEmpty(params.stackDataPtr))
809 {
810 fprintf(stderr, "Ending stack should be empty but its not. Something "
811 "must have gone wrong with the encoding\n");
812 return bejErrorUnknown;
813 }
814 return 0;
815}
816
817/**
818 * @brief Check if a bej version is supported by this decoder
819 *
kasunathb8ca2652022-05-17 16:34:35 -0700820 * @param[in] bejVersion - the bej version in the received encoded stream
kasunath64cb1972022-05-13 12:54:23 -0700821 * @return true if supported.
822 */
823static bool bejIsSupported(uint32_t bejVersion)
824{
825 for (uint32_t i = 0; i < sizeof(supportedBejVersions) / sizeof(uint32_t);
826 ++i)
827 {
828 if (bejVersion == supportedBejVersions[i])
829 {
830 return true;
831 }
832 }
833 return false;
834}
835
836int bejDecodePldmBlock(const struct BejDictionaries* dictionaries,
837 const uint8_t* encodedPldmBlock, uint32_t blockLength,
838 const struct BejStackCallback* stackCallback,
839 const struct BejDecodedCallback* decodedCallback,
840 void* callbacksDataPtr, void* stackDataPtr)
841{
kasunath34a096d2022-05-17 11:36:14 -0700842 NULL_CHECK(dictionaries, "dictionaries");
843 NULL_CHECK(dictionaries->schemaDictionary, "schemaDictionary");
844 NULL_CHECK(dictionaries->annotationDictionary, "annotationDictionary");
845
846 NULL_CHECK(encodedPldmBlock, "encodedPldmBlock");
847
848 NULL_CHECK(stackCallback, "stackCallback");
849 NULL_CHECK(stackCallback->stackEmpty, "stackEmpty");
850 NULL_CHECK(stackCallback->stackPeek, "stackPeek");
851 NULL_CHECK(stackCallback->stackPop, "stackPop");
852 NULL_CHECK(stackCallback->stackPush, "stackPush");
853
854 NULL_CHECK(decodedCallback, "decodedCallback");
855
kasunath64cb1972022-05-13 12:54:23 -0700856 uint32_t pldmHeaderSize = sizeof(struct BejPldmBlockHeader);
857 if (blockLength < pldmHeaderSize)
858 {
859 fprintf(stderr, "Invalid pldm block size: %u\n", blockLength);
860 return bejErrorInvalidSize;
861 }
862
863 const struct BejPldmBlockHeader* pldmHeader =
864 (const struct BejPldmBlockHeader*)encodedPldmBlock;
865
866 if (!bejIsSupported(pldmHeader->bejVersion))
867 {
868 fprintf(stderr, "Bej decoder doesn't support the bej version: %u\n",
869 pldmHeader->bejVersion);
870 return bejErrorNotSuppoted;
871 }
872
873 if (pldmHeader->schemaClass == bejAnnotationSchemaClass)
874 {
875 fprintf(stderr,
876 "Encoder schema class cannot be BejAnnotationSchemaClass\n");
877 return bejErrorNotSuppoted;
878 }
879 // TODO: Add support for CollectionMemberType schema class.
880 if (pldmHeader->schemaClass == bejCollectionMemberTypeSchemaClass)
881 {
882 fprintf(stderr, "Decoder doesn't support "
kasunath34a096d2022-05-17 11:36:14 -0700883 "bejCollectionMemberTypeSchemaClass yet.\n");
kasunath64cb1972022-05-13 12:54:23 -0700884 return bejErrorNotSuppoted;
885 }
886 // TODO: Add support for Error schema class.
887 if (pldmHeader->schemaClass == bejErrorSchemaClass)
888 {
889 fprintf(stderr, "Decoder doesn't support BejErrorSchemaClass yet.\n");
890 return bejErrorNotSuppoted;
891 }
892
893 // Skip the PLDM header.
894 const uint8_t* enStream = encodedPldmBlock + pldmHeaderSize;
895 uint32_t streamLen = blockLength - pldmHeaderSize;
896 return bejDecode(dictionaries->schemaDictionary,
897 dictionaries->annotationDictionary, enStream, streamLen,
898 stackCallback, decodedCallback, callbacksDataPtr,
899 stackDataPtr);
900}