blob: 41b71ed63a42eb2a9570fc91cbdccd8e91df2dc0 [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 }
61 uint64_t value = rdeGetUnsignedInteger(bytes, numOfBytes);
62 uint8_t bitsInVal = numOfBytes * 8;
63 // Since numOfBytes > 0, bitsInVal is non negative.
64 uint64_t mask = (uint64_t)1 << (uint8_t)(bitsInVal - 1);
65 return (value ^ mask) - mask;
66}
67
68/**
kasunath64cb1972022-05-13 12:54:23 -070069 * @brief Get offsets of SFLV fields with respect to the enSegment start.
70 *
71 * @param[in] enSegment - a valid pointer to a start of a SFLV bejTuple.
72 * @param[out] offsets - this will hold the local offsets.
73 */
74static void bejGetLocalBejSFLVOffsets(const uint8_t* enSegment,
75 struct BejSFLVOffset* offsets)
76{
77 // Structure of the SFLV.
78 // [Number of bytes need to represent the sequence number] - uint8_t
79 // [SequenceNumber] - multi byte
80 // [Format] - uint8_t
81 // [Number of bytes need to represent the value length] - uint8_t
82 // [Value length] - multi byte
83
84 // Number of bytes need to represent the sequence number.
85 const uint8_t seqSize = *enSegment;
86 // Start of format.
87 const uint32_t formatOffset = sizeof(uint8_t) + seqSize;
88 // Start of length of the value-length bytes.
89 const uint32_t valueLenNnintOffset = formatOffset + sizeof(uint8_t);
90 // Number of bytes need to represent the value length.
91 const uint8_t valueLengthSize = *(enSegment + valueLenNnintOffset);
92 // Start of the Value.
93 const uint32_t valueOffset =
94 valueLenNnintOffset + sizeof(uint8_t) + valueLengthSize;
95
96 offsets->formatOffset = formatOffset;
97 offsets->valueLenNnintOffset = valueLenNnintOffset;
98 offsets->valueOffset = valueOffset;
99}
100
101/**
102 * @brief Initialize sflv struct in params struct.
103 *
104 * @param[inout] params - a valid BejHandleTypeFuncParam struct with
105 * params->state.encodedSubStream pointing to the start of the encoded stream
106 * and params->state.encodedStreamOffset pointing to the current bejTuple.
107 */
108static void bejInitSFLVStruct(struct BejHandleTypeFuncParam* params)
109{
110 struct BejSFLVOffset localOffset;
111 // Get offsets of different SFLV fields with respect to start of the encoded
112 // segment.
113 bejGetLocalBejSFLVOffsets(params->state.encodedSubStream, &localOffset);
114 struct BejSFLV* sflv = &params->sflv;
115 const uint32_t valueLength = (uint32_t)(rdeGetNnint(
116 params->state.encodedSubStream + localOffset.valueLenNnintOffset));
117 // Sequence number itself should be 16bits. Using 32bits for
118 // [sequence_number + schema_type].
119 uint32_t tupleS = (uint32_t)(rdeGetNnint(params->state.encodedSubStream));
120 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 +
150 rdeGetNnintSize(params->sflv.value);
151}
152
153/**
154 * @brief Get the correct property and the dictionary it belongs to.
155 *
156 * @param[in] params - a BejHandleTypeFuncParam struct pointing to valid
157 * dictionaries.
158 * @param[in] schemaType - indicate whether to use the annotation dictionary or
159 * the main schema dictionary.
160 * @param[in] sequenceNumber - sequence number to use for property search. Not
161 * using the params->sflv.tupleS.sequenceNumber from the provided params struct.
162 * @param[out] dictionary - if the function is successful, this will point to a
163 * valid dictionary to be used.
164 * @param[out] prop - if the function is successful, this will point to a valid
165 * property in a dictionary.
166 * @return 0 if successful.
167 */
168static int
169 bejGetDictionaryAndProperty(const struct BejHandleTypeFuncParam* params,
170 uint8_t schemaType, uint32_t sequenceNumber,
171 const uint8_t** dictionary,
172 const struct BejDictionaryProperty** prop)
173{
174 uint16_t dictPropOffset;
175 // We need to pick the correct dictionary.
176 if (schemaType == bejPrimary)
177 {
178 *dictionary = params->mainDictionary;
179 dictPropOffset = params->state.mainDictPropOffset;
180 }
181 else if (schemaType == bejAnnotation)
182 {
183 *dictionary = params->annotDictionary;
184 dictPropOffset = params->state.annoDictPropOffset;
185 }
186 else
187 {
188 fprintf(stderr, "Failed to select a dictionary. schema type: %u\n",
189 schemaType);
190 return bejErrorInvalidSchemaType;
191 }
192
193 int ret =
194 bejDictGetProperty(*dictionary, dictPropOffset, sequenceNumber, prop);
195 if (ret != 0)
196 {
197 fprintf(stderr, "Failed to get dictionary property for offset: %u\n",
198 dictPropOffset);
199 return ret;
200 }
201 return 0;
202}
203
204/**
kasunathe2260cd2022-05-17 16:12:54 -0700205 * @brief Find and return the property name of the current encoded segment. If
206 * the params->state.addPropertyName is false, this will return an empty string.
kasunath34a096d2022-05-17 11:36:14 -0700207 *
208 * @param[in] params - a valid populated BejHandleTypeFuncParam.
209 * @return 0 if successful.
210 */
kasunathe2260cd2022-05-17 16:12:54 -0700211static const char* bejGetPropName(struct BejHandleTypeFuncParam* params)
kasunath34a096d2022-05-17 11:36:14 -0700212{
213 const uint8_t* dictionary;
214 const struct BejDictionaryProperty* prop;
kasunathe2260cd2022-05-17 16:12:54 -0700215 if (!params->state.addPropertyName ||
216 (bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
217 params->sflv.tupleS.sequenceNumber,
218 &dictionary, &prop) != 0))
kasunath34a096d2022-05-17 11:36:14 -0700219 {
220 return "";
221 }
222 return bejDictGetPropertyName(dictionary, prop->nameOffset,
223 prop->nameLength);
224}
225
226/**
227 * @brief Look for section endings.
228 *
229 * This figures out whether the current encoded segment marks a section
230 * ending. If so, this function will update the decoder state and pop the stack
231 * used to memorize endings. This function should be called after updating the
232 * encodedStreamOffset to the end of decoded SFLV tuple.
233 *
234 * @param[in] params - a valid BejHandleTypeFuncParam which contains the decoder
235 * state.
236 * @param[in] canBeEmpty - if true, the stack being empty is not an error. If
237 * false, stack cannot be empty.
238 * @return 0 if successful.
239 */
240static int bejProcessEnding(struct BejHandleTypeFuncParam* params,
241 bool canBeEmpty)
242{
243 if (params->stackCallback->stackEmpty(params->stackDataPtr) && !canBeEmpty)
244 {
245 // If bejProcessEnding has been called after adding an appropriate JSON
246 // property, then stack cannot be empty.
247 fprintf(stderr, "Ending stack cannot be empty.\n");
248 return bejErrorUnknown;
249 }
250
251 while (!params->stackCallback->stackEmpty(params->stackDataPtr))
252 {
253 const struct BejStackProperty* const ending =
254 params->stackCallback->stackPeek(params->stackDataPtr);
255 // Check whether the current offset location matches the expected ending
256 // offset. If so, we are done with that section.
257 if (params->state.encodedStreamOffset == ending->streamEndOffset)
258 {
259 // Since we are going out of a section, we need to reset the
260 // dictionary property offsets to this section's parent property
261 // start.
262 params->state.mainDictPropOffset = ending->mainDictPropOffset;
263 params->state.annoDictPropOffset = ending->annoDictPropOffset;
264 params->state.addPropertyName = ending->addPropertyName;
265
266 if (ending->sectionType == bejSectionSet)
267 {
268 RETURN_IF_CALLBACK_IERROR(
269 params->decodedCallback->callbackSetEnd,
270 params->callbacksDataPtr);
271 }
272 else if (ending->sectionType == bejSectionArray)
273 {
274 RETURN_IF_CALLBACK_IERROR(
275 params->decodedCallback->callbackArrayEnd,
276 params->callbacksDataPtr);
277 }
278 params->stackCallback->stackPop(params->stackDataPtr);
279 }
280 else
281 {
282 RETURN_IF_CALLBACK_IERROR(
283 params->decodedCallback->callbackPropertyEnd,
284 params->callbacksDataPtr);
285 // Do not change the parent dictionary property offset since we are
286 // still inside the same section.
287 return 0;
288 }
289 }
290 return 0;
291}
292
293/**
294 * @brief Check whether the current encoded segment being decoded is an array
295 * element.
296 *
297 * @param[in] params - a valid BejHandleTypeFuncParam struct.
298 * @return true if the encoded segment is an array element. Else false.
299 */
300static bool bejIsArrayElement(const struct BejHandleTypeFuncParam* params)
301{
302 // If the encoded segment enters an array section, we are adding a
303 // BejSectionArray to the stack. Therefore if the stack is empty, encoded
304 // segment cannot be an array element.
305 if (params->stackCallback->stackEmpty(params->stackDataPtr))
306 {
307 return false;
308 }
309 const struct BejStackProperty* const ending =
310 params->stackCallback->stackPeek(params->stackDataPtr);
311 // If the stack top element holds a BejSectionArray, encoded segment is
312 // an array element.
313 return ending->sectionType == bejSectionArray;
314}
315
316/**
317 * @brief Decodes a BejSet type SFLV BEJ tuple.
318 *
319 * @param params - a valid BejHandleTypeFuncParam struct.
320 * @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
347 uint64_t elements = rdeGetNnint(params->sflv.value);
348 // If its an empty set, we are done here.
349 if (elements == 0)
350 {
351 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackSetEnd,
352 params->callbacksDataPtr);
353 }
354 else
355 {
356 // Update the states for the next encoding segment.
357 struct BejStackProperty newEnding = {
358 .sectionType = bejSectionSet,
359 .addPropertyName = params->state.addPropertyName,
360 .mainDictPropOffset = params->state.mainDictPropOffset,
361 .annoDictPropOffset = params->state.annoDictPropOffset,
362 .streamEndOffset = params->sflv.valueEndOffset,
363 };
364 RETURN_IF_IERROR(
365 params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
366 params->state.addPropertyName = true;
367 if (params->sflv.tupleS.schema == bejAnnotation)
368 {
369 // Since this set is an annotated type, we need to advance the
370 // annotation dictionary for decoding the next segment.
371 params->state.annoDictPropOffset = prop->childPointerOffset;
372 }
373 else
374 {
375 params->state.mainDictPropOffset = prop->childPointerOffset;
376 }
377 }
378 params->state.encodedStreamOffset = bejGetFirstTupleOffset(params);
379 return 0;
380}
381
382/**
kasunathe2260cd2022-05-17 16:12:54 -0700383 * @brief Decodes a BejArray type SFLV BEJ tuple.
384 *
385 * @param params - a valid BejHandleTypeFuncParam struct.
386 * @return 0 if successful.
387 */
388static int bejHandleBejArray(struct BejHandleTypeFuncParam* params)
389{
390 const uint8_t* dictionary;
391 const struct BejDictionaryProperty* prop;
392 RETURN_IF_IERROR(bejGetDictionaryAndProperty(
393 params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber,
394 &dictionary, &prop));
395
396 const char* propName = "";
397 if (params->state.addPropertyName)
398 {
399 propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
400 prop->nameLength);
401 }
402
403 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayStart,
404 propName, params->callbacksDataPtr);
405
406 uint64_t elements = rdeGetNnint(params->sflv.value);
407 // If its an empty array, we are done here.
408 if (elements == 0)
409 {
410 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayEnd,
411 params->callbacksDataPtr);
412 }
413 else
414 {
415 // Update the state for next segment decoding.
416 struct BejStackProperty newEnding = {
417 .sectionType = bejSectionArray,
418 .addPropertyName = params->state.addPropertyName,
419 .mainDictPropOffset = params->state.mainDictPropOffset,
420 .annoDictPropOffset = params->state.annoDictPropOffset,
421 .streamEndOffset = params->sflv.valueEndOffset,
422 };
423 RETURN_IF_IERROR(
424 params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
425 // We do not add property names for array elements.
426 params->state.addPropertyName = false;
427 if (params->sflv.tupleS.schema == bejAnnotation)
428 {
429 // Since this array is an annotated type, we need to advance the
430 // annotation dictionary for decoding the next segment.
431 params->state.annoDictPropOffset = prop->childPointerOffset;
432 }
433 else
434 {
435 params->state.mainDictPropOffset = prop->childPointerOffset;
436 }
437 }
438 params->state.encodedStreamOffset = bejGetFirstTupleOffset(params);
439 return 0;
440}
441
442/**
443 * @brief Decodes a BejNull type SFLV BEJ tuple.
444 *
445 * @param params - a valid BejHandleTypeFuncParam struct.
446 * @return 0 if successful.
447 */
448static int bejHandleBejNull(struct BejHandleTypeFuncParam* params)
449{
450 const char* propName = bejGetPropName(params);
451 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, propName,
452 params->callbacksDataPtr);
453 params->state.encodedStreamOffset = params->sflv.valueEndOffset;
454 return bejProcessEnding(params, /*canBeEmpty=*/false);
455}
456
457/**
kasunath34a096d2022-05-17 11:36:14 -0700458 * @brief Decodes a BejInteger type SFLV BEJ tuple.
459 *
460 * @param params - a valid BejHandleTypeFuncParam struct.
461 * @return 0 if successful.
462 */
463static int bejHandleBejInteger(struct BejHandleTypeFuncParam* params)
464{
kasunathe2260cd2022-05-17 16:12:54 -0700465 const char* propName = bejGetPropName(params);
kasunath34a096d2022-05-17 11:36:14 -0700466
467 if (params->sflv.valueLength == 0)
468 {
469 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
470 propName, params->callbacksDataPtr);
471 }
472 else
473 {
474 RETURN_IF_CALLBACK_IERROR(
475 params->decodedCallback->callbackInteger, propName,
476 bejGetIntegerValue(params->sflv.value, params->sflv.valueLength),
477 params->callbacksDataPtr);
478 }
479 params->state.encodedStreamOffset = params->sflv.valueEndOffset;
480 return bejProcessEnding(params, /*canBeEmpty=*/false);
481}
482
483/**
kasunathe2260cd2022-05-17 16:12:54 -0700484 * @brief Decodes a BejString type SFLV BEJ tuple.
485 *
486 * @param params - a valid BejHandleTypeFuncParam struct.
487 * @return 0 if successful.
488 */
489static int bejHandleBejString(struct BejHandleTypeFuncParam* params)
490{
491 // TODO: Handle deferred bindings.
492 const char* propName = bejGetPropName(params);
493
494 if (params->sflv.valueLength == 0)
495 {
496 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
497 propName, params->callbacksDataPtr);
498 }
499 else
500 {
501 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackString,
502 propName, (const char*)(params->sflv.value),
503 params->callbacksDataPtr);
504 }
505 params->state.encodedStreamOffset = params->sflv.valueEndOffset;
506 return bejProcessEnding(params, /*canBeEmpty=*/false);
507}
508
509/**
510 * @brief Decodes a BejBoolean type SFLV BEJ tuple.
511 *
512 * @param params - a valid BejHandleTypeFuncParam struct.
513 * @return 0 if successful.
514 */
515static int bejHandleBejBoolean(struct BejHandleTypeFuncParam* params)
516{
517 const char* propName = bejGetPropName(params);
518
519 if (params->sflv.valueLength == 0)
520 {
521 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
522 propName, params->callbacksDataPtr);
523 }
524 else
525 {
526 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackBool,
527 propName, *(params->sflv.value) > 0,
528 params->callbacksDataPtr);
529 }
530 params->state.encodedStreamOffset = params->sflv.valueEndOffset;
531 return bejProcessEnding(params, /*canBeEmpty=*/false);
532}
533
534/**
kasunath64cb1972022-05-13 12:54:23 -0700535 * @brief Decodes an encoded bej stream.
536 *
537 * @param[in] schemaDictionary - main schema dictionary to use.
538 * @param[in] annotationDictionary - annotation dictionary
539 * @param[in] enStream - encoded stream without the PLDM header.
540 * @param[in] streamLen - length of the enStream.
541 * @param[in] stackCallback - callbacks for stack handlers.
542 * @param[in] decodedCallback - callbacks for extracting decoded properties.
543 * @param[in] callbacksDataPtr - data pointer to pass to decoded callbacks. This
544 * can be used pass additional data.
545 * @param[in] stackDataPtr - data pointer to pass to stack callbacks. This can
546 * be used pass additional data.
547 *
548 * @return 0 if successful.
549 */
550static int bejDecode(const uint8_t* schemaDictionary,
551 const uint8_t* annotationDictionary,
552 const uint8_t* enStream, uint32_t streamLen,
553 const struct BejStackCallback* stackCallback,
554 const struct BejDecodedCallback* decodedCallback,
555 void* callbacksDataPtr, void* stackDataPtr)
556{
557 struct BejHandleTypeFuncParam params = {
558 .state =
559 {
560 // We only add names of set properties. We don't use names for
561 // array
562 // properties. Here we are omitting the name of the root set.
563 .addPropertyName = false,
564 // At start, parent property from the main dictionary is the
565 // first property.
566 .mainDictPropOffset = bejDictGetPropertyHeadOffset(),
567 .annoDictPropOffset = bejDictGetFirstAnnotatedPropertyOffset(),
568 // Current location of the encoded segment we are processing.
569 .encodedStreamOffset = 0,
570 .encodedSubStream = enStream,
571 },
572 .mainDictionary = schemaDictionary,
573 .annotDictionary = annotationDictionary,
574 .decodedCallback = decodedCallback,
575 .stackCallback = stackCallback,
576 .callbacksDataPtr = callbacksDataPtr,
577 .stackDataPtr = stackDataPtr,
578 };
579
580 while (params.state.encodedStreamOffset < streamLen)
581 {
582 // Go to the next encoded segment in the encoded stream.
583 params.state.encodedSubStream =
584 enStream + params.state.encodedStreamOffset;
585 bejInitSFLVStruct(&params);
586
587 if (params.sflv.format.readOnlyProperty)
588 {
589 RETURN_IF_CALLBACK_IERROR(
590 params.decodedCallback->callbackReadonlyProperty,
591 params.sflv.tupleS.sequenceNumber, params.callbacksDataPtr);
592 }
593
594 // TODO: Handle nullable property types. These are indicated by
595 // params.sflv.format.nullableProperty
596 switch (params.sflv.format.principalDataType)
597 {
598 case bejSet:
kasunath34a096d2022-05-17 11:36:14 -0700599 RETURN_IF_IERROR(bejHandleBejSet(&params));
kasunath64cb1972022-05-13 12:54:23 -0700600 break;
601 case bejArray:
kasunathe2260cd2022-05-17 16:12:54 -0700602 RETURN_IF_IERROR(bejHandleBejArray(&params));
kasunath64cb1972022-05-13 12:54:23 -0700603 break;
604 case bejNull:
kasunathe2260cd2022-05-17 16:12:54 -0700605 RETURN_IF_IERROR(bejHandleBejNull(&params));
kasunath64cb1972022-05-13 12:54:23 -0700606 break;
607 case bejInteger:
kasunath34a096d2022-05-17 11:36:14 -0700608 RETURN_IF_IERROR(bejHandleBejInteger(&params));
kasunath64cb1972022-05-13 12:54:23 -0700609 break;
610 case bejEnum:
611 // TODO: Add support for BejEnum decoding.
612 fprintf(stderr, "No BejEnum support\n");
613 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
614 break;
615 case bejString:
kasunathe2260cd2022-05-17 16:12:54 -0700616 RETURN_IF_IERROR(bejHandleBejString(&params));
kasunath64cb1972022-05-13 12:54:23 -0700617 break;
618 case bejReal:
619 // TODO: Add support for BejReal decoding.
620 fprintf(stderr, "No BejReal support\n");
621 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
622 break;
623 case bejBoolean:
kasunathe2260cd2022-05-17 16:12:54 -0700624 RETURN_IF_IERROR(bejHandleBejBoolean(&params));
kasunath64cb1972022-05-13 12:54:23 -0700625 break;
626 case bejBytestring:
627 // TODO: Add support for BejBytestring decoding.
628 fprintf(stderr, "No BejBytestring support\n");
629 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
630 break;
631 case bejChoice:
632 // TODO: Add support for BejChoice decoding.
633 fprintf(stderr, "No BejChoice support\n");
634 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
635 break;
636 case bejPropertyAnnotation:
637 // TODO: Add support for BejPropertyAnnotation decoding.
638 fprintf(stderr, "No BejPropertyAnnotation support\n");
639 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
640 break;
641 case bejResourceLink:
642 // TODO: Add support for BejResourceLink decoding.
643 fprintf(stderr, "No BejResourceLink support\n");
644 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
645 break;
646 case bejResourceLinkExpansion:
647 // TODO: Add support for BejResourceLinkExpansion decoding.
648 fprintf(stderr, "No BejResourceLinkExpansion support\n");
649 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
650 break;
651 default:
652 break;
653 }
654 }
kasunath34a096d2022-05-17 11:36:14 -0700655 RETURN_IF_IERROR(bejProcessEnding(&params, /*canBeEmpty=*/true));
kasunath64cb1972022-05-13 12:54:23 -0700656 if (!params.stackCallback->stackEmpty(params.stackDataPtr))
657 {
658 fprintf(stderr, "Ending stack should be empty but its not. Something "
659 "must have gone wrong with the encoding\n");
660 return bejErrorUnknown;
661 }
662 return 0;
663}
664
665/**
666 * @brief Check if a bej version is supported by this decoder
667 *
668 * @param bejVersion[in] - the bej version in the received encoded stream
669 * @return true if supported.
670 */
671static bool bejIsSupported(uint32_t bejVersion)
672{
673 for (uint32_t i = 0; i < sizeof(supportedBejVersions) / sizeof(uint32_t);
674 ++i)
675 {
676 if (bejVersion == supportedBejVersions[i])
677 {
678 return true;
679 }
680 }
681 return false;
682}
683
684int bejDecodePldmBlock(const struct BejDictionaries* dictionaries,
685 const uint8_t* encodedPldmBlock, uint32_t blockLength,
686 const struct BejStackCallback* stackCallback,
687 const struct BejDecodedCallback* decodedCallback,
688 void* callbacksDataPtr, void* stackDataPtr)
689{
kasunath34a096d2022-05-17 11:36:14 -0700690 NULL_CHECK(dictionaries, "dictionaries");
691 NULL_CHECK(dictionaries->schemaDictionary, "schemaDictionary");
692 NULL_CHECK(dictionaries->annotationDictionary, "annotationDictionary");
693
694 NULL_CHECK(encodedPldmBlock, "encodedPldmBlock");
695
696 NULL_CHECK(stackCallback, "stackCallback");
697 NULL_CHECK(stackCallback->stackEmpty, "stackEmpty");
698 NULL_CHECK(stackCallback->stackPeek, "stackPeek");
699 NULL_CHECK(stackCallback->stackPop, "stackPop");
700 NULL_CHECK(stackCallback->stackPush, "stackPush");
701
702 NULL_CHECK(decodedCallback, "decodedCallback");
703
kasunath64cb1972022-05-13 12:54:23 -0700704 uint32_t pldmHeaderSize = sizeof(struct BejPldmBlockHeader);
705 if (blockLength < pldmHeaderSize)
706 {
707 fprintf(stderr, "Invalid pldm block size: %u\n", blockLength);
708 return bejErrorInvalidSize;
709 }
710
711 const struct BejPldmBlockHeader* pldmHeader =
712 (const struct BejPldmBlockHeader*)encodedPldmBlock;
713
714 if (!bejIsSupported(pldmHeader->bejVersion))
715 {
716 fprintf(stderr, "Bej decoder doesn't support the bej version: %u\n",
717 pldmHeader->bejVersion);
718 return bejErrorNotSuppoted;
719 }
720
721 if (pldmHeader->schemaClass == bejAnnotationSchemaClass)
722 {
723 fprintf(stderr,
724 "Encoder schema class cannot be BejAnnotationSchemaClass\n");
725 return bejErrorNotSuppoted;
726 }
727 // TODO: Add support for CollectionMemberType schema class.
728 if (pldmHeader->schemaClass == bejCollectionMemberTypeSchemaClass)
729 {
730 fprintf(stderr, "Decoder doesn't support "
kasunath34a096d2022-05-17 11:36:14 -0700731 "bejCollectionMemberTypeSchemaClass yet.\n");
kasunath64cb1972022-05-13 12:54:23 -0700732 return bejErrorNotSuppoted;
733 }
734 // TODO: Add support for Error schema class.
735 if (pldmHeader->schemaClass == bejErrorSchemaClass)
736 {
737 fprintf(stderr, "Decoder doesn't support BejErrorSchemaClass yet.\n");
738 return bejErrorNotSuppoted;
739 }
740
741 // Skip the PLDM header.
742 const uint8_t* enStream = encodedPldmBlock + pldmHeaderSize;
743 uint32_t streamLen = blockLength - pldmHeaderSize;
744 return bejDecode(dictionaries->schemaDictionary,
745 dictionaries->annotationDictionary, enStream, streamLen,
746 stackCallback, decodedCallback, callbacksDataPtr,
747 stackDataPtr);
748}