blob: fe6dad6b67e53514d07f2450b1f36e721de6a384 [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 Williams435526a2023-05-10 14:44:12 -050078 const uint32_t valueOffset = valueLenNnintOffset + sizeof(uint8_t) +
79 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 */
127static uint32_t
128 bejGetFirstTupleOffset(const struct BejHandleTypeFuncParam* params)
129{
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 */
153static int
154 bejGetDictionaryAndProperty(const struct BejHandleTypeFuncParam* params,
155 uint8_t schemaType, uint32_t sequenceNumber,
156 const uint8_t** dictionary,
157 const struct BejDictionaryProperty** prop)
158{
159 uint16_t dictPropOffset;
160 // We need to pick the correct dictionary.
161 if (schemaType == bejPrimary)
162 {
163 *dictionary = params->mainDictionary;
164 dictPropOffset = params->state.mainDictPropOffset;
165 }
166 else if (schemaType == bejAnnotation)
167 {
168 *dictionary = params->annotDictionary;
169 dictPropOffset = params->state.annoDictPropOffset;
170 }
171 else
172 {
173 fprintf(stderr, "Failed to select a dictionary. schema type: %u\n",
174 schemaType);
175 return bejErrorInvalidSchemaType;
176 }
177
Patrick Williams435526a2023-05-10 14:44:12 -0500178 int ret = bejDictGetProperty(*dictionary, dictPropOffset, sequenceNumber,
179 prop);
kasunath34a096d2022-05-17 11:36:14 -0700180 if (ret != 0)
181 {
182 fprintf(stderr, "Failed to get dictionary property for offset: %u\n",
183 dictPropOffset);
184 return ret;
185 }
186 return 0;
187}
188
189/**
kasunathe2260cd2022-05-17 16:12:54 -0700190 * @brief Find and return the property name of the current encoded segment. If
191 * the params->state.addPropertyName is false, this will return an empty string.
kasunath34a096d2022-05-17 11:36:14 -0700192 *
193 * @param[in] params - a valid populated BejHandleTypeFuncParam.
194 * @return 0 if successful.
195 */
kasunathe2260cd2022-05-17 16:12:54 -0700196static const char* bejGetPropName(struct BejHandleTypeFuncParam* params)
kasunath34a096d2022-05-17 11:36:14 -0700197{
198 const uint8_t* dictionary;
199 const struct BejDictionaryProperty* prop;
kasunathe2260cd2022-05-17 16:12:54 -0700200 if (!params->state.addPropertyName ||
201 (bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
202 params->sflv.tupleS.sequenceNumber,
203 &dictionary, &prop) != 0))
kasunath34a096d2022-05-17 11:36:14 -0700204 {
205 return "";
206 }
207 return bejDictGetPropertyName(dictionary, prop->nameOffset,
208 prop->nameLength);
209}
210
211/**
212 * @brief Look for section endings.
213 *
214 * This figures out whether the current encoded segment marks a section
215 * ending. If so, this function will update the decoder state and pop the stack
216 * used to memorize endings. This function should be called after updating the
217 * encodedStreamOffset to the end of decoded SFLV tuple.
218 *
219 * @param[in] params - a valid BejHandleTypeFuncParam which contains the decoder
220 * state.
221 * @param[in] canBeEmpty - if true, the stack being empty is not an error. If
222 * false, stack cannot be empty.
223 * @return 0 if successful.
224 */
225static int bejProcessEnding(struct BejHandleTypeFuncParam* params,
226 bool canBeEmpty)
227{
228 if (params->stackCallback->stackEmpty(params->stackDataPtr) && !canBeEmpty)
229 {
230 // If bejProcessEnding has been called after adding an appropriate JSON
231 // property, then stack cannot be empty.
232 fprintf(stderr, "Ending stack cannot be empty.\n");
233 return bejErrorUnknown;
234 }
235
236 while (!params->stackCallback->stackEmpty(params->stackDataPtr))
237 {
238 const struct BejStackProperty* const ending =
239 params->stackCallback->stackPeek(params->stackDataPtr);
240 // Check whether the current offset location matches the expected ending
241 // offset. If so, we are done with that section.
242 if (params->state.encodedStreamOffset == ending->streamEndOffset)
243 {
244 // Since we are going out of a section, we need to reset the
245 // dictionary property offsets to this section's parent property
246 // start.
247 params->state.mainDictPropOffset = ending->mainDictPropOffset;
248 params->state.annoDictPropOffset = ending->annoDictPropOffset;
249 params->state.addPropertyName = ending->addPropertyName;
250
251 if (ending->sectionType == bejSectionSet)
252 {
253 RETURN_IF_CALLBACK_IERROR(
254 params->decodedCallback->callbackSetEnd,
255 params->callbacksDataPtr);
256 }
257 else if (ending->sectionType == bejSectionArray)
258 {
259 RETURN_IF_CALLBACK_IERROR(
260 params->decodedCallback->callbackArrayEnd,
261 params->callbacksDataPtr);
262 }
263 params->stackCallback->stackPop(params->stackDataPtr);
264 }
265 else
266 {
267 RETURN_IF_CALLBACK_IERROR(
268 params->decodedCallback->callbackPropertyEnd,
269 params->callbacksDataPtr);
270 // Do not change the parent dictionary property offset since we are
271 // still inside the same section.
272 return 0;
273 }
274 }
275 return 0;
276}
277
278/**
279 * @brief Check whether the current encoded segment being decoded is an array
280 * element.
281 *
282 * @param[in] params - a valid BejHandleTypeFuncParam struct.
283 * @return true if the encoded segment is an array element. Else false.
284 */
285static bool bejIsArrayElement(const struct BejHandleTypeFuncParam* params)
286{
287 // If the encoded segment enters an array section, we are adding a
288 // BejSectionArray to the stack. Therefore if the stack is empty, encoded
289 // segment cannot be an array element.
290 if (params->stackCallback->stackEmpty(params->stackDataPtr))
291 {
292 return false;
293 }
294 const struct BejStackProperty* const ending =
295 params->stackCallback->stackPeek(params->stackDataPtr);
296 // If the stack top element holds a BejSectionArray, encoded segment is
297 // an array element.
298 return ending->sectionType == bejSectionArray;
299}
300
301/**
302 * @brief Decodes a BejSet type SFLV BEJ tuple.
303 *
kasunathb8ca2652022-05-17 16:34:35 -0700304 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunath34a096d2022-05-17 11:36:14 -0700305 * @return 0 if successful.
306 */
307static int bejHandleBejSet(struct BejHandleTypeFuncParam* params)
308{
309 uint16_t sequenceNumber = params->sflv.tupleS.sequenceNumber;
310 // Check whether this BejSet is an array element or not.
311 if (bejIsArrayElement(params))
312 {
313 // Dictionary only contains an entry for element 0.
314 sequenceNumber = 0;
315 }
316 const uint8_t* dictionary;
317 const struct BejDictionaryProperty* prop;
318 RETURN_IF_IERROR(
319 bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
320 sequenceNumber, &dictionary, &prop));
321
322 const char* propName = "";
323 if (params->state.addPropertyName)
324 {
325 propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
326 prop->nameLength);
327 }
328
329 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackSetStart,
330 propName, params->callbacksDataPtr);
331
kasunathc14fab62022-11-23 16:18:21 -0800332 // Move the offset to the next SFLV tuple (or end). Make sure that this is
333 // called before calling bejProcessEnding.
334 params->state.encodedStreamOffset = bejGetFirstTupleOffset(params);
335
kasunath0aa36d82022-11-23 14:24:15 -0800336 uint64_t elements = bejGetNnint(params->sflv.value);
kasunath34a096d2022-05-17 11:36:14 -0700337 // If its an empty set, we are done here.
338 if (elements == 0)
339 {
340 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackSetEnd,
341 params->callbacksDataPtr);
kasunathc14fab62022-11-23 16:18:21 -0800342 // Since this is an ending of a property (empty array), we should call
343 // bejProcessEnding. Unless the whole JSON object is an empty set (which
344 // shouldn't be the case), stack cannot be empty.
345 bejProcessEnding(params, /*canBeEmpty=*/false);
346 return 0;
347 }
348
349 // Update the states for the next encoding segment.
350 struct BejStackProperty newEnding = {
351 .sectionType = bejSectionSet,
352 .addPropertyName = params->state.addPropertyName,
353 .mainDictPropOffset = params->state.mainDictPropOffset,
354 .annoDictPropOffset = params->state.annoDictPropOffset,
355 .streamEndOffset = params->sflv.valueEndOffset,
356 };
357 RETURN_IF_IERROR(
358 params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
359 params->state.addPropertyName = true;
360 if (params->sflv.tupleS.schema == bejAnnotation)
361 {
362 // Since this set is an annotated type, we need to advance the
363 // annotation dictionary for decoding the next segment.
364 params->state.annoDictPropOffset = prop->childPointerOffset;
kasunath34a096d2022-05-17 11:36:14 -0700365 }
366 else
367 {
kasunathc14fab62022-11-23 16:18:21 -0800368 params->state.mainDictPropOffset = prop->childPointerOffset;
kasunath34a096d2022-05-17 11:36:14 -0700369 }
kasunath34a096d2022-05-17 11:36:14 -0700370 return 0;
371}
372
373/**
kasunathe2260cd2022-05-17 16:12:54 -0700374 * @brief Decodes a BejArray type SFLV BEJ tuple.
375 *
kasunathb8ca2652022-05-17 16:34:35 -0700376 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunathe2260cd2022-05-17 16:12:54 -0700377 * @return 0 if successful.
378 */
379static int bejHandleBejArray(struct BejHandleTypeFuncParam* params)
380{
381 const uint8_t* dictionary;
382 const struct BejDictionaryProperty* prop;
383 RETURN_IF_IERROR(bejGetDictionaryAndProperty(
384 params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber,
385 &dictionary, &prop));
386
387 const char* propName = "";
388 if (params->state.addPropertyName)
389 {
390 propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
391 prop->nameLength);
392 }
393
394 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayStart,
395 propName, params->callbacksDataPtr);
396
kasunathc14fab62022-11-23 16:18:21 -0800397 // Move the offset to the next SFLV tuple (or end). Make sure that this is
398 // called before calling bejProcessEnding.
399 params->state.encodedStreamOffset = bejGetFirstTupleOffset(params);
400
kasunath0aa36d82022-11-23 14:24:15 -0800401 uint64_t elements = bejGetNnint(params->sflv.value);
kasunathe2260cd2022-05-17 16:12:54 -0700402 // If its an empty array, we are done here.
403 if (elements == 0)
404 {
405 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayEnd,
406 params->callbacksDataPtr);
kasunathc14fab62022-11-23 16:18:21 -0800407 // Since this is an ending of a property (empty array), we should call
408 // bejProcessEnding. Stack cannot be empty since there should be at
409 // least 1 parent in the stack.
410 bejProcessEnding(params, /*canBeEmpty=*/false);
411 return 0;
412 }
413
414 // Update the state for next segment decoding.
415 struct BejStackProperty newEnding = {
416 .sectionType = bejSectionArray,
417 .addPropertyName = params->state.addPropertyName,
418 .mainDictPropOffset = params->state.mainDictPropOffset,
419 .annoDictPropOffset = params->state.annoDictPropOffset,
420 .streamEndOffset = params->sflv.valueEndOffset,
421 };
422 RETURN_IF_IERROR(
423 params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
424 // We do not add property names for array elements.
425 params->state.addPropertyName = false;
426 if (params->sflv.tupleS.schema == bejAnnotation)
427 {
428 // Since this array is an annotated type, we need to advance the
429 // annotation dictionary for decoding the next segment.
430 params->state.annoDictPropOffset = prop->childPointerOffset;
kasunathe2260cd2022-05-17 16:12:54 -0700431 }
432 else
433 {
kasunathc14fab62022-11-23 16:18:21 -0800434 params->state.mainDictPropOffset = prop->childPointerOffset;
kasunathe2260cd2022-05-17 16:12:54 -0700435 }
kasunathe2260cd2022-05-17 16:12:54 -0700436 return 0;
437}
438
439/**
440 * @brief Decodes a BejNull type SFLV BEJ tuple.
441 *
kasunathb8ca2652022-05-17 16:34:35 -0700442 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunathe2260cd2022-05-17 16:12:54 -0700443 * @return 0 if successful.
444 */
445static int bejHandleBejNull(struct BejHandleTypeFuncParam* params)
446{
447 const char* propName = bejGetPropName(params);
448 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, propName,
449 params->callbacksDataPtr);
450 params->state.encodedStreamOffset = params->sflv.valueEndOffset;
451 return bejProcessEnding(params, /*canBeEmpty=*/false);
452}
453
454/**
kasunath34a096d2022-05-17 11:36:14 -0700455 * @brief Decodes a BejInteger type SFLV BEJ tuple.
456 *
kasunathb8ca2652022-05-17 16:34:35 -0700457 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunath34a096d2022-05-17 11:36:14 -0700458 * @return 0 if successful.
459 */
460static int bejHandleBejInteger(struct BejHandleTypeFuncParam* params)
461{
kasunathe2260cd2022-05-17 16:12:54 -0700462 const char* propName = bejGetPropName(params);
kasunath34a096d2022-05-17 11:36:14 -0700463
464 if (params->sflv.valueLength == 0)
465 {
466 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
467 propName, params->callbacksDataPtr);
468 }
469 else
470 {
471 RETURN_IF_CALLBACK_IERROR(
472 params->decodedCallback->callbackInteger, propName,
473 bejGetIntegerValue(params->sflv.value, params->sflv.valueLength),
474 params->callbacksDataPtr);
475 }
476 params->state.encodedStreamOffset = params->sflv.valueEndOffset;
477 return bejProcessEnding(params, /*canBeEmpty=*/false);
478}
479
480/**
kasunathb8ca2652022-05-17 16:34:35 -0700481 * @brief Decodes a BejEnum type SFLV BEJ tuple.
482 *
483 * @param[in] params - a valid BejHandleTypeFuncParam struct.
484 * @return 0 if successful.
485 */
486static int bejHandleBejEnum(struct BejHandleTypeFuncParam* params)
487{
488 uint16_t sequenceNumber = params->sflv.tupleS.sequenceNumber;
489 if (bejIsArrayElement(params))
490 {
491 sequenceNumber = 0;
492 }
493 const uint8_t* dictionary;
494 const struct BejDictionaryProperty* prop;
495 RETURN_IF_IERROR(
496 bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
497 sequenceNumber, &dictionary, &prop));
498
499 const char* propName = "";
500 if (params->state.addPropertyName)
501 {
502 propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
503 prop->nameLength);
504 }
505
506 if (params->sflv.valueLength == 0)
507 {
508 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
509 propName, params->callbacksDataPtr);
510 }
511 else
512 {
513 // Get the string for enum value.
514 uint16_t enumValueSequenceN =
kasunath0aa36d82022-11-23 14:24:15 -0800515 (uint16_t)(bejGetNnint(params->sflv.value));
kasunathb8ca2652022-05-17 16:34:35 -0700516 const struct BejDictionaryProperty* enumValueProp;
517 RETURN_IF_IERROR(
518 bejDictGetProperty(dictionary, prop->childPointerOffset,
519 enumValueSequenceN, &enumValueProp));
520 const char* enumValueName = bejDictGetPropertyName(
521 dictionary, enumValueProp->nameOffset, enumValueProp->nameLength);
522
523 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackEnum,
524 propName, enumValueName,
525 params->callbacksDataPtr);
526 }
527 // Update the offset to point to the next possible SFLV tuple.
528 params->state.encodedStreamOffset = params->sflv.valueEndOffset;
529 return bejProcessEnding(params, /*canBeEmpty=*/false);
530}
531
532/**
kasunathe2260cd2022-05-17 16:12:54 -0700533 * @brief Decodes a BejString type SFLV BEJ tuple.
534 *
kasunathb8ca2652022-05-17 16:34:35 -0700535 * @param[in] params - a valid BejHandleTypeFuncParam struct.
kasunathe2260cd2022-05-17 16:12:54 -0700536 * @return 0 if successful.
537 */
538static int bejHandleBejString(struct BejHandleTypeFuncParam* params)
539{
540 // TODO: Handle deferred bindings.
541 const char* propName = bejGetPropName(params);
542
543 if (params->sflv.valueLength == 0)
544 {
545 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
546 propName, params->callbacksDataPtr);
547 }
548 else
549 {
550 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackString,
551 propName, (const char*)(params->sflv.value),
552 params->callbacksDataPtr);
553 }
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 Williams435526a2023-05-10 14:44:12 -0500583 const uint8_t* wholeBejInt = params->sflv.value +
584 bejGetNnintSize(params->sflv.value);
kasunathb8ca2652022-05-17 16:34:35 -0700585 const uint8_t* fractZeroCountNnint = wholeBejInt + wholeByteLen;
Patrick Williams435526a2023-05-10 14:44:12 -0500586 const uint8_t* fractNnint = fractZeroCountNnint +
587 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 Williams435526a2023-05-10 14:44:12 -0500672 params->state.encodedStreamOffset = params->sflv.valueEndOffset -
673 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
723 while (params.state.encodedStreamOffset < streamLen)
724 {
725 // Go to the next encoded segment in the encoded stream.
Patrick Williams435526a2023-05-10 14:44:12 -0500726 params.state.encodedSubStream = enStream +
727 params.state.encodedStreamOffset;
kasunath64cb1972022-05-13 12:54:23 -0700728 bejInitSFLVStruct(&params);
729
730 if (params.sflv.format.readOnlyProperty)
731 {
732 RETURN_IF_CALLBACK_IERROR(
733 params.decodedCallback->callbackReadonlyProperty,
734 params.sflv.tupleS.sequenceNumber, params.callbacksDataPtr);
735 }
736
737 // TODO: Handle nullable property types. These are indicated by
738 // params.sflv.format.nullableProperty
739 switch (params.sflv.format.principalDataType)
740 {
741 case bejSet:
kasunath34a096d2022-05-17 11:36:14 -0700742 RETURN_IF_IERROR(bejHandleBejSet(&params));
kasunath64cb1972022-05-13 12:54:23 -0700743 break;
744 case bejArray:
kasunathe2260cd2022-05-17 16:12:54 -0700745 RETURN_IF_IERROR(bejHandleBejArray(&params));
kasunath64cb1972022-05-13 12:54:23 -0700746 break;
747 case bejNull:
kasunathe2260cd2022-05-17 16:12:54 -0700748 RETURN_IF_IERROR(bejHandleBejNull(&params));
kasunath64cb1972022-05-13 12:54:23 -0700749 break;
750 case bejInteger:
kasunath34a096d2022-05-17 11:36:14 -0700751 RETURN_IF_IERROR(bejHandleBejInteger(&params));
kasunath64cb1972022-05-13 12:54:23 -0700752 break;
753 case bejEnum:
kasunathb8ca2652022-05-17 16:34:35 -0700754 RETURN_IF_IERROR(bejHandleBejEnum(&params));
kasunath64cb1972022-05-13 12:54:23 -0700755 break;
756 case bejString:
kasunathe2260cd2022-05-17 16:12:54 -0700757 RETURN_IF_IERROR(bejHandleBejString(&params));
kasunath64cb1972022-05-13 12:54:23 -0700758 break;
759 case bejReal:
kasunathb8ca2652022-05-17 16:34:35 -0700760 RETURN_IF_IERROR(bejHandleBejReal(&params));
kasunath64cb1972022-05-13 12:54:23 -0700761 break;
762 case bejBoolean:
kasunathe2260cd2022-05-17 16:12:54 -0700763 RETURN_IF_IERROR(bejHandleBejBoolean(&params));
kasunath64cb1972022-05-13 12:54:23 -0700764 break;
765 case bejBytestring:
766 // TODO: Add support for BejBytestring decoding.
767 fprintf(stderr, "No BejBytestring support\n");
768 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
769 break;
770 case bejChoice:
771 // TODO: Add support for BejChoice decoding.
772 fprintf(stderr, "No BejChoice support\n");
773 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
774 break;
775 case bejPropertyAnnotation:
kasunathb8ca2652022-05-17 16:34:35 -0700776 RETURN_IF_IERROR(bejHandleBejPropertyAnnotation(&params));
kasunath64cb1972022-05-13 12:54:23 -0700777 break;
778 case bejResourceLink:
779 // TODO: Add support for BejResourceLink decoding.
780 fprintf(stderr, "No BejResourceLink support\n");
781 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
782 break;
783 case bejResourceLinkExpansion:
784 // TODO: Add support for BejResourceLinkExpansion decoding.
785 fprintf(stderr, "No BejResourceLinkExpansion support\n");
786 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
787 break;
788 default:
789 break;
790 }
791 }
kasunath34a096d2022-05-17 11:36:14 -0700792 RETURN_IF_IERROR(bejProcessEnding(&params, /*canBeEmpty=*/true));
kasunath64cb1972022-05-13 12:54:23 -0700793 if (!params.stackCallback->stackEmpty(params.stackDataPtr))
794 {
795 fprintf(stderr, "Ending stack should be empty but its not. Something "
796 "must have gone wrong with the encoding\n");
797 return bejErrorUnknown;
798 }
799 return 0;
800}
801
802/**
803 * @brief Check if a bej version is supported by this decoder
804 *
kasunathb8ca2652022-05-17 16:34:35 -0700805 * @param[in] bejVersion - the bej version in the received encoded stream
kasunath64cb1972022-05-13 12:54:23 -0700806 * @return true if supported.
807 */
808static bool bejIsSupported(uint32_t bejVersion)
809{
810 for (uint32_t i = 0; i < sizeof(supportedBejVersions) / sizeof(uint32_t);
811 ++i)
812 {
813 if (bejVersion == supportedBejVersions[i])
814 {
815 return true;
816 }
817 }
818 return false;
819}
820
821int bejDecodePldmBlock(const struct BejDictionaries* dictionaries,
822 const uint8_t* encodedPldmBlock, uint32_t blockLength,
823 const struct BejStackCallback* stackCallback,
824 const struct BejDecodedCallback* decodedCallback,
825 void* callbacksDataPtr, void* stackDataPtr)
826{
kasunath34a096d2022-05-17 11:36:14 -0700827 NULL_CHECK(dictionaries, "dictionaries");
828 NULL_CHECK(dictionaries->schemaDictionary, "schemaDictionary");
829 NULL_CHECK(dictionaries->annotationDictionary, "annotationDictionary");
830
831 NULL_CHECK(encodedPldmBlock, "encodedPldmBlock");
832
833 NULL_CHECK(stackCallback, "stackCallback");
834 NULL_CHECK(stackCallback->stackEmpty, "stackEmpty");
835 NULL_CHECK(stackCallback->stackPeek, "stackPeek");
836 NULL_CHECK(stackCallback->stackPop, "stackPop");
837 NULL_CHECK(stackCallback->stackPush, "stackPush");
838
839 NULL_CHECK(decodedCallback, "decodedCallback");
840
kasunath64cb1972022-05-13 12:54:23 -0700841 uint32_t pldmHeaderSize = sizeof(struct BejPldmBlockHeader);
842 if (blockLength < pldmHeaderSize)
843 {
844 fprintf(stderr, "Invalid pldm block size: %u\n", blockLength);
845 return bejErrorInvalidSize;
846 }
847
848 const struct BejPldmBlockHeader* pldmHeader =
849 (const struct BejPldmBlockHeader*)encodedPldmBlock;
850
851 if (!bejIsSupported(pldmHeader->bejVersion))
852 {
853 fprintf(stderr, "Bej decoder doesn't support the bej version: %u\n",
854 pldmHeader->bejVersion);
855 return bejErrorNotSuppoted;
856 }
857
858 if (pldmHeader->schemaClass == bejAnnotationSchemaClass)
859 {
860 fprintf(stderr,
861 "Encoder schema class cannot be BejAnnotationSchemaClass\n");
862 return bejErrorNotSuppoted;
863 }
864 // TODO: Add support for CollectionMemberType schema class.
865 if (pldmHeader->schemaClass == bejCollectionMemberTypeSchemaClass)
866 {
867 fprintf(stderr, "Decoder doesn't support "
kasunath34a096d2022-05-17 11:36:14 -0700868 "bejCollectionMemberTypeSchemaClass yet.\n");
kasunath64cb1972022-05-13 12:54:23 -0700869 return bejErrorNotSuppoted;
870 }
871 // TODO: Add support for Error schema class.
872 if (pldmHeader->schemaClass == bejErrorSchemaClass)
873 {
874 fprintf(stderr, "Decoder doesn't support BejErrorSchemaClass yet.\n");
875 return bejErrorNotSuppoted;
876 }
877
878 // Skip the PLDM header.
879 const uint8_t* enStream = encodedPldmBlock + pldmHeaderSize;
880 uint32_t streamLen = blockLength - pldmHeaderSize;
881 return bejDecode(dictionaries->schemaDictionary,
882 dictionaries->annotationDictionary, enStream, streamLen,
883 stackCallback, decodedCallback, callbacksDataPtr,
884 stackDataPtr);
885}