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 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 */ 55 static 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 /** 69 * @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 */ 74 static 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 */ 108 static 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 = ¶ms->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 /** 133 * @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 */ 142 static 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 */ 168 static 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 /** 205 * @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. 207 * 208 * @param[in] params - a valid populated BejHandleTypeFuncParam. 209 * @return 0 if successful. 210 */ 211 static const char* bejGetPropName(struct BejHandleTypeFuncParam* params) 212 { 213 const uint8_t* dictionary; 214 const struct BejDictionaryProperty* prop; 215 if (!params->state.addPropertyName || 216 (bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema, 217 params->sflv.tupleS.sequenceNumber, 218 &dictionary, &prop) != 0)) 219 { 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 */ 240 static 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 */ 300 static 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[in] params - a valid BejHandleTypeFuncParam struct. 320 * @return 0 if successful. 321 */ 322 static 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 /** 383 * @brief Decodes a BejArray type SFLV BEJ tuple. 384 * 385 * @param[in] params - a valid BejHandleTypeFuncParam struct. 386 * @return 0 if successful. 387 */ 388 static 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[in] params - a valid BejHandleTypeFuncParam struct. 446 * @return 0 if successful. 447 */ 448 static 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 /** 458 * @brief Decodes a BejInteger type SFLV BEJ tuple. 459 * 460 * @param[in] params - a valid BejHandleTypeFuncParam struct. 461 * @return 0 if successful. 462 */ 463 static int bejHandleBejInteger(struct BejHandleTypeFuncParam* params) 464 { 465 const char* propName = bejGetPropName(params); 466 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 /** 484 * @brief Decodes a BejEnum type SFLV BEJ tuple. 485 * 486 * @param[in] params - a valid BejHandleTypeFuncParam struct. 487 * @return 0 if successful. 488 */ 489 static int bejHandleBejEnum(struct BejHandleTypeFuncParam* params) 490 { 491 uint16_t sequenceNumber = params->sflv.tupleS.sequenceNumber; 492 if (bejIsArrayElement(params)) 493 { 494 sequenceNumber = 0; 495 } 496 const uint8_t* dictionary; 497 const struct BejDictionaryProperty* prop; 498 RETURN_IF_IERROR( 499 bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema, 500 sequenceNumber, &dictionary, &prop)); 501 502 const char* propName = ""; 503 if (params->state.addPropertyName) 504 { 505 propName = bejDictGetPropertyName(dictionary, prop->nameOffset, 506 prop->nameLength); 507 } 508 509 if (params->sflv.valueLength == 0) 510 { 511 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, 512 propName, params->callbacksDataPtr); 513 } 514 else 515 { 516 // Get the string for enum value. 517 uint16_t enumValueSequenceN = 518 (uint16_t)(rdeGetNnint(params->sflv.value)); 519 const struct BejDictionaryProperty* enumValueProp; 520 RETURN_IF_IERROR( 521 bejDictGetProperty(dictionary, prop->childPointerOffset, 522 enumValueSequenceN, &enumValueProp)); 523 const char* enumValueName = bejDictGetPropertyName( 524 dictionary, enumValueProp->nameOffset, enumValueProp->nameLength); 525 526 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackEnum, 527 propName, enumValueName, 528 params->callbacksDataPtr); 529 } 530 // Update the offset to point to the next possible SFLV tuple. 531 params->state.encodedStreamOffset = params->sflv.valueEndOffset; 532 return bejProcessEnding(params, /*canBeEmpty=*/false); 533 } 534 535 /** 536 * @brief Decodes a BejString type SFLV BEJ tuple. 537 * 538 * @param[in] params - a valid BejHandleTypeFuncParam struct. 539 * @return 0 if successful. 540 */ 541 static int bejHandleBejString(struct BejHandleTypeFuncParam* params) 542 { 543 // TODO: Handle deferred bindings. 544 const char* propName = bejGetPropName(params); 545 546 if (params->sflv.valueLength == 0) 547 { 548 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, 549 propName, params->callbacksDataPtr); 550 } 551 else 552 { 553 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackString, 554 propName, (const char*)(params->sflv.value), 555 params->callbacksDataPtr); 556 } 557 params->state.encodedStreamOffset = params->sflv.valueEndOffset; 558 return bejProcessEnding(params, /*canBeEmpty=*/false); 559 } 560 561 /** 562 * @brief Decodes a BejReal type SFLV BEJ tuple. 563 * 564 * @param[in] params - a valid BejHandleTypeFuncParam struct. 565 * @return 0 if successful. 566 */ 567 static int bejHandleBejReal(struct BejHandleTypeFuncParam* params) 568 { 569 const char* propName = bejGetPropName(params); 570 571 if (params->sflv.valueLength == 0) 572 { 573 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, 574 propName, params->callbacksDataPtr); 575 } 576 else 577 { 578 // Real value has the following format. 579 // nnint - Length of whole 580 // bejInteger - whole (includes sign for the overall real number) 581 // nnint - Leading zero count for fract 582 // nnint - fract 583 // nnint - Length of exp 584 // bejInteger - exp (includes sign for the exponent) 585 uint8_t wholeByteLen = (uint8_t)rdeGetNnint(params->sflv.value); 586 const uint8_t* wholeBejInt = 587 params->sflv.value + rdeGetNnintSize(params->sflv.value); 588 const uint8_t* fractZeroCountNnint = wholeBejInt + wholeByteLen; 589 const uint8_t* fractNnint = 590 fractZeroCountNnint + rdeGetNnintSize(fractZeroCountNnint); 591 const uint8_t* lenExpNnint = fractNnint + rdeGetNnintSize(fractNnint); 592 const uint8_t* expBejInt = lenExpNnint + rdeGetNnintSize(lenExpNnint); 593 594 struct BejReal realValue; 595 realValue.whole = bejGetIntegerValue(wholeBejInt, wholeByteLen); 596 realValue.zeroCount = rdeGetNnint(fractZeroCountNnint); 597 realValue.fract = rdeGetNnint(fractNnint); 598 realValue.expLen = (uint8_t)rdeGetNnint(lenExpNnint); 599 if (realValue.expLen != 0) 600 { 601 realValue.exp = bejGetIntegerValue( 602 expBejInt, (uint8_t)rdeGetNnint(lenExpNnint)); 603 } 604 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackReal, 605 propName, &realValue, 606 params->callbacksDataPtr); 607 } 608 params->state.encodedStreamOffset = params->sflv.valueEndOffset; 609 return bejProcessEnding(params, /*canBeEmpty=*/false); 610 } 611 612 /** 613 * @brief Decodes a BejBoolean type SFLV BEJ tuple. 614 * 615 * @param[in] params - a valid BejHandleTypeFuncParam struct. 616 * @return 0 if successful. 617 */ 618 static int bejHandleBejBoolean(struct BejHandleTypeFuncParam* params) 619 { 620 const char* propName = bejGetPropName(params); 621 622 if (params->sflv.valueLength == 0) 623 { 624 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, 625 propName, params->callbacksDataPtr); 626 } 627 else 628 { 629 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackBool, 630 propName, *(params->sflv.value) > 0, 631 params->callbacksDataPtr); 632 } 633 params->state.encodedStreamOffset = params->sflv.valueEndOffset; 634 return bejProcessEnding(params, /*canBeEmpty=*/false); 635 } 636 637 /** 638 * @brief Decodes a BejPropertyAnnotation type SFLV BEJ tuple. 639 * 640 * @param[in] params - a valid BejHandleTypeFuncParam struct. 641 * @return 0 if successful. 642 */ 643 static int bejHandleBejPropertyAnnotation(struct BejHandleTypeFuncParam* params) 644 { 645 // TODO: Handle colon-delimited string values. 646 647 // Property annotation has the form OuterProperty@Annotation. First 648 // processing the outer property name. 649 const uint8_t* outerDictionary; 650 const struct BejDictionaryProperty* outerProp; 651 RETURN_IF_IERROR(bejGetDictionaryAndProperty( 652 params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber, 653 &outerDictionary, &outerProp)); 654 655 const char* propName = bejDictGetPropertyName( 656 outerDictionary, outerProp->nameOffset, outerProp->nameLength); 657 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackAnnotation, 658 propName, params->callbacksDataPtr); 659 660 // Mark the ending of the property annotation. 661 struct BejStackProperty newEnding = { 662 .sectionType = bejSectionNoType, 663 .addPropertyName = params->state.addPropertyName, 664 .mainDictPropOffset = params->state.mainDictPropOffset, 665 .annoDictPropOffset = params->state.annoDictPropOffset, 666 .streamEndOffset = params->sflv.valueEndOffset, 667 }; 668 // Update the states for the next encoding segment. 669 RETURN_IF_IERROR( 670 params->stackCallback->stackPush(&newEnding, params->stackDataPtr)); 671 params->state.addPropertyName = true; 672 // We might have to change this for nested annotations. 673 params->state.mainDictPropOffset = outerProp->childPointerOffset; 674 // Point to the start of the value for next decoding. 675 params->state.encodedStreamOffset = 676 params->sflv.valueEndOffset - params->sflv.valueLength; 677 return 0; 678 } 679 680 /** 681 * @brief Decodes an encoded bej stream. 682 * 683 * @param[in] schemaDictionary - main schema dictionary to use. 684 * @param[in] annotationDictionary - annotation dictionary 685 * @param[in] enStream - encoded stream without the PLDM header. 686 * @param[in] streamLen - length of the enStream. 687 * @param[in] stackCallback - callbacks for stack handlers. 688 * @param[in] decodedCallback - callbacks for extracting decoded properties. 689 * @param[in] callbacksDataPtr - data pointer to pass to decoded callbacks. This 690 * can be used pass additional data. 691 * @param[in] stackDataPtr - data pointer to pass to stack callbacks. This can 692 * be used pass additional data. 693 * 694 * @return 0 if successful. 695 */ 696 static int bejDecode(const uint8_t* schemaDictionary, 697 const uint8_t* annotationDictionary, 698 const uint8_t* enStream, uint32_t streamLen, 699 const struct BejStackCallback* stackCallback, 700 const struct BejDecodedCallback* decodedCallback, 701 void* callbacksDataPtr, void* stackDataPtr) 702 { 703 struct BejHandleTypeFuncParam params = { 704 .state = 705 { 706 // We only add names of set properties. We don't use names for 707 // array 708 // properties. Here we are omitting the name of the root set. 709 .addPropertyName = false, 710 // At start, parent property from the main dictionary is the 711 // first property. 712 .mainDictPropOffset = bejDictGetPropertyHeadOffset(), 713 .annoDictPropOffset = bejDictGetFirstAnnotatedPropertyOffset(), 714 // Current location of the encoded segment we are processing. 715 .encodedStreamOffset = 0, 716 .encodedSubStream = enStream, 717 }, 718 .mainDictionary = schemaDictionary, 719 .annotDictionary = annotationDictionary, 720 .decodedCallback = decodedCallback, 721 .stackCallback = stackCallback, 722 .callbacksDataPtr = callbacksDataPtr, 723 .stackDataPtr = stackDataPtr, 724 }; 725 726 while (params.state.encodedStreamOffset < streamLen) 727 { 728 // Go to the next encoded segment in the encoded stream. 729 params.state.encodedSubStream = 730 enStream + params.state.encodedStreamOffset; 731 bejInitSFLVStruct(¶ms); 732 733 if (params.sflv.format.readOnlyProperty) 734 { 735 RETURN_IF_CALLBACK_IERROR( 736 params.decodedCallback->callbackReadonlyProperty, 737 params.sflv.tupleS.sequenceNumber, params.callbacksDataPtr); 738 } 739 740 // TODO: Handle nullable property types. These are indicated by 741 // params.sflv.format.nullableProperty 742 switch (params.sflv.format.principalDataType) 743 { 744 case bejSet: 745 RETURN_IF_IERROR(bejHandleBejSet(¶ms)); 746 break; 747 case bejArray: 748 RETURN_IF_IERROR(bejHandleBejArray(¶ms)); 749 break; 750 case bejNull: 751 RETURN_IF_IERROR(bejHandleBejNull(¶ms)); 752 break; 753 case bejInteger: 754 RETURN_IF_IERROR(bejHandleBejInteger(¶ms)); 755 break; 756 case bejEnum: 757 RETURN_IF_IERROR(bejHandleBejEnum(¶ms)); 758 break; 759 case bejString: 760 RETURN_IF_IERROR(bejHandleBejString(¶ms)); 761 break; 762 case bejReal: 763 RETURN_IF_IERROR(bejHandleBejReal(¶ms)); 764 break; 765 case bejBoolean: 766 RETURN_IF_IERROR(bejHandleBejBoolean(¶ms)); 767 break; 768 case bejBytestring: 769 // TODO: Add support for BejBytestring decoding. 770 fprintf(stderr, "No BejBytestring support\n"); 771 params.state.encodedStreamOffset = params.sflv.valueEndOffset; 772 break; 773 case bejChoice: 774 // TODO: Add support for BejChoice decoding. 775 fprintf(stderr, "No BejChoice support\n"); 776 params.state.encodedStreamOffset = params.sflv.valueEndOffset; 777 break; 778 case bejPropertyAnnotation: 779 RETURN_IF_IERROR(bejHandleBejPropertyAnnotation(¶ms)); 780 break; 781 case bejResourceLink: 782 // TODO: Add support for BejResourceLink decoding. 783 fprintf(stderr, "No BejResourceLink support\n"); 784 params.state.encodedStreamOffset = params.sflv.valueEndOffset; 785 break; 786 case bejResourceLinkExpansion: 787 // TODO: Add support for BejResourceLinkExpansion decoding. 788 fprintf(stderr, "No BejResourceLinkExpansion support\n"); 789 params.state.encodedStreamOffset = params.sflv.valueEndOffset; 790 break; 791 default: 792 break; 793 } 794 } 795 RETURN_IF_IERROR(bejProcessEnding(¶ms, /*canBeEmpty=*/true)); 796 if (!params.stackCallback->stackEmpty(params.stackDataPtr)) 797 { 798 fprintf(stderr, "Ending stack should be empty but its not. Something " 799 "must have gone wrong with the encoding\n"); 800 return bejErrorUnknown; 801 } 802 return 0; 803 } 804 805 /** 806 * @brief Check if a bej version is supported by this decoder 807 * 808 * @param[in] bejVersion - the bej version in the received encoded stream 809 * @return true if supported. 810 */ 811 static bool bejIsSupported(uint32_t bejVersion) 812 { 813 for (uint32_t i = 0; i < sizeof(supportedBejVersions) / sizeof(uint32_t); 814 ++i) 815 { 816 if (bejVersion == supportedBejVersions[i]) 817 { 818 return true; 819 } 820 } 821 return false; 822 } 823 824 int bejDecodePldmBlock(const struct BejDictionaries* dictionaries, 825 const uint8_t* encodedPldmBlock, uint32_t blockLength, 826 const struct BejStackCallback* stackCallback, 827 const struct BejDecodedCallback* decodedCallback, 828 void* callbacksDataPtr, void* stackDataPtr) 829 { 830 NULL_CHECK(dictionaries, "dictionaries"); 831 NULL_CHECK(dictionaries->schemaDictionary, "schemaDictionary"); 832 NULL_CHECK(dictionaries->annotationDictionary, "annotationDictionary"); 833 834 NULL_CHECK(encodedPldmBlock, "encodedPldmBlock"); 835 836 NULL_CHECK(stackCallback, "stackCallback"); 837 NULL_CHECK(stackCallback->stackEmpty, "stackEmpty"); 838 NULL_CHECK(stackCallback->stackPeek, "stackPeek"); 839 NULL_CHECK(stackCallback->stackPop, "stackPop"); 840 NULL_CHECK(stackCallback->stackPush, "stackPush"); 841 842 NULL_CHECK(decodedCallback, "decodedCallback"); 843 844 uint32_t pldmHeaderSize = sizeof(struct BejPldmBlockHeader); 845 if (blockLength < pldmHeaderSize) 846 { 847 fprintf(stderr, "Invalid pldm block size: %u\n", blockLength); 848 return bejErrorInvalidSize; 849 } 850 851 const struct BejPldmBlockHeader* pldmHeader = 852 (const struct BejPldmBlockHeader*)encodedPldmBlock; 853 854 if (!bejIsSupported(pldmHeader->bejVersion)) 855 { 856 fprintf(stderr, "Bej decoder doesn't support the bej version: %u\n", 857 pldmHeader->bejVersion); 858 return bejErrorNotSuppoted; 859 } 860 861 if (pldmHeader->schemaClass == bejAnnotationSchemaClass) 862 { 863 fprintf(stderr, 864 "Encoder schema class cannot be BejAnnotationSchemaClass\n"); 865 return bejErrorNotSuppoted; 866 } 867 // TODO: Add support for CollectionMemberType schema class. 868 if (pldmHeader->schemaClass == bejCollectionMemberTypeSchemaClass) 869 { 870 fprintf(stderr, "Decoder doesn't support " 871 "bejCollectionMemberTypeSchemaClass yet.\n"); 872 return bejErrorNotSuppoted; 873 } 874 // TODO: Add support for Error schema class. 875 if (pldmHeader->schemaClass == bejErrorSchemaClass) 876 { 877 fprintf(stderr, "Decoder doesn't support BejErrorSchemaClass yet.\n"); 878 return bejErrorNotSuppoted; 879 } 880 881 // Skip the PLDM header. 882 const uint8_t* enStream = encodedPldmBlock + pldmHeaderSize; 883 uint32_t streamLen = blockLength - pldmHeaderSize; 884 return bejDecode(dictionaries->schemaDictionary, 885 dictionaries->annotationDictionary, enStream, streamLen, 886 stackCallback, decodedCallback, callbacksDataPtr, 887 stackDataPtr); 888 } 889