1 #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 12 const 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 /** 33 * @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 */ 40 static int64_t bejGetIntegerValue(const uint8_t* bytes, uint8_t numOfBytes) 41 { 42 if (numOfBytes == 0) 43 { 44 return 0; 45 } 46 uint64_t value = bejGetUnsignedInteger(bytes, numOfBytes); 47 uint8_t bitsInVal = numOfBytes * 8; 48 // Since numOfBytes > 0, bitsInVal is non negative. 49 uint64_t mask = (uint64_t)1 << (uint8_t)(bitsInVal - 1); 50 return (int64_t)((value ^ mask) - mask); 51 } 52 53 /** 54 * @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 */ 59 static 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. 78 const uint32_t valueOffset = valueLenNnintOffset + sizeof(uint8_t) + 79 valueLengthSize; 80 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 */ 93 static 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 = ¶ms->sflv; 100 const uint32_t valueLength = (uint32_t)(bejGetNnint( 101 params->state.encodedSubStream + localOffset.valueLenNnintOffset)); 102 // Sequence number itself should be 16bits. Using 32bits for 103 // [sequence_number + schema_type]. 104 uint32_t tupleS = (uint32_t)(bejGetNnint(params->state.encodedSubStream)); 105 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 /** 118 * @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 */ 127 static 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 + 135 bejGetNnintSize(params->sflv.value); 136 } 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 */ 153 static 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 178 int ret = bejDictGetProperty(*dictionary, dictPropOffset, sequenceNumber, 179 prop); 180 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 /** 190 * @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. 192 * 193 * @param[in] params - a valid populated BejHandleTypeFuncParam. 194 * @return 0 if successful. 195 */ 196 static const char* bejGetPropName(struct BejHandleTypeFuncParam* params) 197 { 198 const uint8_t* dictionary; 199 const struct BejDictionaryProperty* prop; 200 if (!params->state.addPropertyName || 201 (bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema, 202 params->sflv.tupleS.sequenceNumber, 203 &dictionary, &prop) != 0)) 204 { 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 */ 225 static 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 */ 285 static 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 * 304 * @param[in] params - a valid BejHandleTypeFuncParam struct. 305 * @return 0 if successful. 306 */ 307 static 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 332 // 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 336 uint64_t elements = bejGetNnint(params->sflv.value); 337 // 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); 342 // 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; 365 } 366 else 367 { 368 params->state.mainDictPropOffset = prop->childPointerOffset; 369 } 370 return 0; 371 } 372 373 /** 374 * @brief Decodes a BejArray type SFLV BEJ tuple. 375 * 376 * @param[in] params - a valid BejHandleTypeFuncParam struct. 377 * @return 0 if successful. 378 */ 379 static 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 397 // 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 401 uint64_t elements = bejGetNnint(params->sflv.value); 402 // 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); 407 // 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; 431 } 432 else 433 { 434 params->state.mainDictPropOffset = prop->childPointerOffset; 435 } 436 return 0; 437 } 438 439 /** 440 * @brief Decodes a BejNull type SFLV BEJ tuple. 441 * 442 * @param[in] params - a valid BejHandleTypeFuncParam struct. 443 * @return 0 if successful. 444 */ 445 static 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 /** 455 * @brief Decodes a BejInteger type SFLV BEJ tuple. 456 * 457 * @param[in] params - a valid BejHandleTypeFuncParam struct. 458 * @return 0 if successful. 459 */ 460 static int bejHandleBejInteger(struct BejHandleTypeFuncParam* params) 461 { 462 const char* propName = bejGetPropName(params); 463 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 /** 481 * @brief Decodes a BejEnum type SFLV BEJ tuple. 482 * 483 * @param[in] params - a valid BejHandleTypeFuncParam struct. 484 * @return 0 if successful. 485 */ 486 static 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 = 515 (uint16_t)(bejGetNnint(params->sflv.value)); 516 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 /** 533 * @brief Decodes a BejString type SFLV BEJ tuple. 534 * 535 * @param[in] params - a valid BejHandleTypeFuncParam struct. 536 * @return 0 if successful. 537 */ 538 static 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 /** 559 * @brief Decodes a BejReal type SFLV BEJ tuple. 560 * 561 * @param[in] params - a valid BejHandleTypeFuncParam struct. 562 * @return 0 if successful. 563 */ 564 static 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) 582 uint8_t wholeByteLen = (uint8_t)bejGetNnint(params->sflv.value); 583 const uint8_t* wholeBejInt = params->sflv.value + 584 bejGetNnintSize(params->sflv.value); 585 const uint8_t* fractZeroCountNnint = wholeBejInt + wholeByteLen; 586 const uint8_t* fractNnint = fractZeroCountNnint + 587 bejGetNnintSize(fractZeroCountNnint); 588 const uint8_t* lenExpNnint = fractNnint + bejGetNnintSize(fractNnint); 589 const uint8_t* expBejInt = lenExpNnint + bejGetNnintSize(lenExpNnint); 590 591 struct BejReal realValue; 592 realValue.whole = bejGetIntegerValue(wholeBejInt, wholeByteLen); 593 realValue.zeroCount = bejGetNnint(fractZeroCountNnint); 594 realValue.fract = bejGetNnint(fractNnint); 595 realValue.expLen = (uint8_t)bejGetNnint(lenExpNnint); 596 if (realValue.expLen != 0) 597 { 598 realValue.exp = bejGetIntegerValue( 599 expBejInt, (uint8_t)bejGetNnint(lenExpNnint)); 600 } 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 /** 610 * @brief Decodes a BejBoolean type SFLV BEJ tuple. 611 * 612 * @param[in] params - a valid BejHandleTypeFuncParam struct. 613 * @return 0 if successful. 614 */ 615 static 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 /** 635 * @brief Decodes a BejPropertyAnnotation type SFLV BEJ tuple. 636 * 637 * @param[in] params - a valid BejHandleTypeFuncParam struct. 638 * @return 0 if successful. 639 */ 640 static 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. 672 params->state.encodedStreamOffset = params->sflv.valueEndOffset - 673 params->sflv.valueLength; 674 return 0; 675 } 676 677 /** 678 * @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 */ 693 static 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. 726 params.state.encodedSubStream = enStream + 727 params.state.encodedStreamOffset; 728 bejInitSFLVStruct(¶ms); 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: 742 RETURN_IF_IERROR(bejHandleBejSet(¶ms)); 743 break; 744 case bejArray: 745 RETURN_IF_IERROR(bejHandleBejArray(¶ms)); 746 break; 747 case bejNull: 748 RETURN_IF_IERROR(bejHandleBejNull(¶ms)); 749 break; 750 case bejInteger: 751 RETURN_IF_IERROR(bejHandleBejInteger(¶ms)); 752 break; 753 case bejEnum: 754 RETURN_IF_IERROR(bejHandleBejEnum(¶ms)); 755 break; 756 case bejString: 757 RETURN_IF_IERROR(bejHandleBejString(¶ms)); 758 break; 759 case bejReal: 760 RETURN_IF_IERROR(bejHandleBejReal(¶ms)); 761 break; 762 case bejBoolean: 763 RETURN_IF_IERROR(bejHandleBejBoolean(¶ms)); 764 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: 776 RETURN_IF_IERROR(bejHandleBejPropertyAnnotation(¶ms)); 777 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 } 792 RETURN_IF_IERROR(bejProcessEnding(¶ms, /*canBeEmpty=*/true)); 793 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 * 805 * @param[in] bejVersion - the bej version in the received encoded stream 806 * @return true if supported. 807 */ 808 static 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 821 int 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 { 827 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 841 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 " 868 "bejCollectionMemberTypeSchemaClass yet.\n"); 869 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 } 886