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 = 79 valueLenNnintOffset + sizeof(uint8_t) + 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 bejGetDictionaryAndProperty( 154 const struct BejHandleTypeFuncParam* params, uint8_t schemaType, 155 uint32_t sequenceNumber, const uint8_t** dictionary, 156 const struct BejDictionaryProperty** prop) 157 { 158 uint16_t dictPropOffset; 159 // We need to pick the correct dictionary. 160 if (schemaType == bejPrimary) 161 { 162 *dictionary = params->mainDictionary; 163 dictPropOffset = params->state.mainDictPropOffset; 164 } 165 else if (schemaType == bejAnnotation) 166 { 167 *dictionary = params->annotDictionary; 168 dictPropOffset = params->state.annoDictPropOffset; 169 } 170 else 171 { 172 fprintf(stderr, "Failed to select a dictionary. schema type: %u\n", 173 schemaType); 174 return bejErrorInvalidSchemaType; 175 } 176 177 int ret = 178 bejDictGetProperty(*dictionary, dictPropOffset, sequenceNumber, prop); 179 if (ret != 0) 180 { 181 fprintf(stderr, "Failed to get dictionary property for offset: %u\n", 182 dictPropOffset); 183 return ret; 184 } 185 return 0; 186 } 187 188 /** 189 * @brief Find and return the property name of the current encoded segment. If 190 * the params->state.addPropertyName is false, this will return an empty string. 191 * 192 * @param[in] params - a valid populated BejHandleTypeFuncParam. 193 * @return 0 if successful. 194 */ 195 static const char* bejGetPropName(struct BejHandleTypeFuncParam* params) 196 { 197 const uint8_t* dictionary; 198 const struct BejDictionaryProperty* prop; 199 if (!params->state.addPropertyName || 200 (bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema, 201 params->sflv.tupleS.sequenceNumber, 202 &dictionary, &prop) != 0)) 203 { 204 return ""; 205 } 206 return bejDictGetPropertyName(dictionary, prop->nameOffset, 207 prop->nameLength); 208 } 209 210 /** 211 * @brief Look for section endings. 212 * 213 * This figures out whether the current encoded segment marks a section 214 * ending. If so, this function will update the decoder state and pop the stack 215 * used to memorize endings. This function should be called after updating the 216 * encodedStreamOffset to the end of decoded SFLV tuple. 217 * 218 * @param[in] params - a valid BejHandleTypeFuncParam which contains the decoder 219 * state. 220 * @param[in] canBeEmpty - if true, the stack being empty is not an error. If 221 * false, stack cannot be empty. 222 * @return 0 if successful. 223 */ 224 static int bejProcessEnding(struct BejHandleTypeFuncParam* params, 225 bool canBeEmpty) 226 { 227 if (params->stackCallback->stackEmpty(params->stackDataPtr) && !canBeEmpty) 228 { 229 // If bejProcessEnding has been called after adding an appropriate JSON 230 // property, then stack cannot be empty. 231 fprintf(stderr, "Ending stack cannot be empty.\n"); 232 return bejErrorUnknown; 233 } 234 235 while (!params->stackCallback->stackEmpty(params->stackDataPtr)) 236 { 237 const struct BejStackProperty* const ending = 238 params->stackCallback->stackPeek(params->stackDataPtr); 239 // Check whether the current offset location matches the expected ending 240 // offset. If so, we are done with that section. 241 if (params->state.encodedStreamOffset == ending->streamEndOffset) 242 { 243 // Since we are going out of a section, we need to reset the 244 // dictionary property offsets to this section's parent property 245 // start. 246 params->state.mainDictPropOffset = ending->mainDictPropOffset; 247 params->state.annoDictPropOffset = ending->annoDictPropOffset; 248 params->state.addPropertyName = ending->addPropertyName; 249 250 if (ending->sectionType == bejSectionSet) 251 { 252 RETURN_IF_CALLBACK_IERROR( 253 params->decodedCallback->callbackSetEnd, 254 params->callbacksDataPtr); 255 } 256 else if (ending->sectionType == bejSectionArray) 257 { 258 RETURN_IF_CALLBACK_IERROR( 259 params->decodedCallback->callbackArrayEnd, 260 params->callbacksDataPtr); 261 } 262 params->stackCallback->stackPop(params->stackDataPtr); 263 } 264 else 265 { 266 RETURN_IF_CALLBACK_IERROR( 267 params->decodedCallback->callbackPropertyEnd, 268 params->callbacksDataPtr); 269 // Do not change the parent dictionary property offset since we are 270 // still inside the same section. 271 return 0; 272 } 273 } 274 return 0; 275 } 276 277 /** 278 * @brief Check whether the current encoded segment being decoded is an array 279 * element. 280 * 281 * @param[in] params - a valid BejHandleTypeFuncParam struct. 282 * @return true if the encoded segment is an array element. Else false. 283 */ 284 static bool bejIsArrayElement(const struct BejHandleTypeFuncParam* params) 285 { 286 // If the encoded segment enters an array section, we are adding a 287 // BejSectionArray to the stack. Therefore if the stack is empty, encoded 288 // segment cannot be an array element. 289 if (params->stackCallback->stackEmpty(params->stackDataPtr)) 290 { 291 return false; 292 } 293 const struct BejStackProperty* const ending = 294 params->stackCallback->stackPeek(params->stackDataPtr); 295 // If the stack top element holds a BejSectionArray, encoded segment is 296 // an array element. 297 return ending->sectionType == bejSectionArray; 298 } 299 300 /** 301 * @brief Decodes a BejSet type SFLV BEJ tuple. 302 * 303 * @param[in] params - a valid BejHandleTypeFuncParam struct. 304 * @return 0 if successful. 305 */ 306 static int bejHandleBejSet(struct BejHandleTypeFuncParam* params) 307 { 308 uint16_t sequenceNumber = params->sflv.tupleS.sequenceNumber; 309 // Check whether this BejSet is an array element or not. 310 if (bejIsArrayElement(params)) 311 { 312 // Dictionary only contains an entry for element 0. 313 sequenceNumber = 0; 314 } 315 const uint8_t* dictionary; 316 const struct BejDictionaryProperty* prop; 317 RETURN_IF_IERROR( 318 bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema, 319 sequenceNumber, &dictionary, &prop)); 320 321 const char* propName = ""; 322 if (params->state.addPropertyName) 323 { 324 propName = bejDictGetPropertyName(dictionary, prop->nameOffset, 325 prop->nameLength); 326 } 327 328 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackSetStart, 329 propName, params->callbacksDataPtr); 330 331 // Move the offset to the next SFLV tuple (or end). Make sure that this is 332 // called before calling bejProcessEnding. 333 params->state.encodedStreamOffset = bejGetFirstTupleOffset(params); 334 335 uint64_t elements = bejGetNnint(params->sflv.value); 336 // If its an empty set, we are done here. 337 if (elements == 0) 338 { 339 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackSetEnd, 340 params->callbacksDataPtr); 341 // Since this is an ending of a property (empty array), we should call 342 // bejProcessEnding. Unless the whole JSON object is an empty set (which 343 // shouldn't be the case), stack cannot be empty. 344 bejProcessEnding(params, /*canBeEmpty=*/false); 345 return 0; 346 } 347 348 // Update the states for the next encoding segment. 349 struct BejStackProperty newEnding = { 350 .sectionType = bejSectionSet, 351 .addPropertyName = params->state.addPropertyName, 352 .mainDictPropOffset = params->state.mainDictPropOffset, 353 .annoDictPropOffset = params->state.annoDictPropOffset, 354 .streamEndOffset = params->sflv.valueEndOffset, 355 }; 356 RETURN_IF_IERROR( 357 params->stackCallback->stackPush(&newEnding, params->stackDataPtr)); 358 params->state.addPropertyName = true; 359 if (params->sflv.tupleS.schema == bejAnnotation) 360 { 361 // Since this set is an annotated type, we need to advance the 362 // annotation dictionary for decoding the next segment. 363 params->state.annoDictPropOffset = prop->childPointerOffset; 364 } 365 else 366 { 367 params->state.mainDictPropOffset = prop->childPointerOffset; 368 } 369 return 0; 370 } 371 372 /** 373 * @brief Decodes a BejArray type SFLV BEJ tuple. 374 * 375 * @param[in] params - a valid BejHandleTypeFuncParam struct. 376 * @return 0 if successful. 377 */ 378 static int bejHandleBejArray(struct BejHandleTypeFuncParam* params) 379 { 380 const uint8_t* dictionary; 381 const struct BejDictionaryProperty* prop; 382 RETURN_IF_IERROR(bejGetDictionaryAndProperty( 383 params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber, 384 &dictionary, &prop)); 385 386 const char* propName = ""; 387 if (params->state.addPropertyName) 388 { 389 propName = bejDictGetPropertyName(dictionary, prop->nameOffset, 390 prop->nameLength); 391 } 392 393 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayStart, 394 propName, params->callbacksDataPtr); 395 396 // Move the offset to the next SFLV tuple (or end). Make sure that this is 397 // called before calling bejProcessEnding. 398 params->state.encodedStreamOffset = bejGetFirstTupleOffset(params); 399 400 uint64_t elements = bejGetNnint(params->sflv.value); 401 // If its an empty array, we are done here. 402 if (elements == 0) 403 { 404 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayEnd, 405 params->callbacksDataPtr); 406 // Since this is an ending of a property (empty array), we should call 407 // bejProcessEnding. Stack cannot be empty since there should be at 408 // least 1 parent in the stack. 409 bejProcessEnding(params, /*canBeEmpty=*/false); 410 return 0; 411 } 412 413 // Update the state for next segment decoding. 414 struct BejStackProperty newEnding = { 415 .sectionType = bejSectionArray, 416 .addPropertyName = params->state.addPropertyName, 417 .mainDictPropOffset = params->state.mainDictPropOffset, 418 .annoDictPropOffset = params->state.annoDictPropOffset, 419 .streamEndOffset = params->sflv.valueEndOffset, 420 }; 421 RETURN_IF_IERROR( 422 params->stackCallback->stackPush(&newEnding, params->stackDataPtr)); 423 // We do not add property names for array elements. 424 params->state.addPropertyName = false; 425 if (params->sflv.tupleS.schema == bejAnnotation) 426 { 427 // Since this array is an annotated type, we need to advance the 428 // annotation dictionary for decoding the next segment. 429 params->state.annoDictPropOffset = prop->childPointerOffset; 430 } 431 else 432 { 433 params->state.mainDictPropOffset = prop->childPointerOffset; 434 } 435 return 0; 436 } 437 438 /** 439 * @brief Decodes a BejNull type SFLV BEJ tuple. 440 * 441 * @param[in] params - a valid BejHandleTypeFuncParam struct. 442 * @return 0 if successful. 443 */ 444 static int bejHandleBejNull(struct BejHandleTypeFuncParam* params) 445 { 446 const char* propName = bejGetPropName(params); 447 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, propName, 448 params->callbacksDataPtr); 449 params->state.encodedStreamOffset = params->sflv.valueEndOffset; 450 return bejProcessEnding(params, /*canBeEmpty=*/false); 451 } 452 453 /** 454 * @brief Decodes a BejInteger type SFLV BEJ tuple. 455 * 456 * @param[in] params - a valid BejHandleTypeFuncParam struct. 457 * @return 0 if successful. 458 */ 459 static int bejHandleBejInteger(struct BejHandleTypeFuncParam* params) 460 { 461 const char* propName = bejGetPropName(params); 462 463 if (params->sflv.valueLength == 0) 464 { 465 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, 466 propName, params->callbacksDataPtr); 467 } 468 else 469 { 470 RETURN_IF_CALLBACK_IERROR( 471 params->decodedCallback->callbackInteger, propName, 472 bejGetIntegerValue(params->sflv.value, params->sflv.valueLength), 473 params->callbacksDataPtr); 474 } 475 params->state.encodedStreamOffset = params->sflv.valueEndOffset; 476 return bejProcessEnding(params, /*canBeEmpty=*/false); 477 } 478 479 /** 480 * @brief Decodes a BejEnum type SFLV BEJ tuple. 481 * 482 * @param[in] params - a valid BejHandleTypeFuncParam struct. 483 * @return 0 if successful. 484 */ 485 static int bejHandleBejEnum(struct BejHandleTypeFuncParam* params) 486 { 487 uint16_t sequenceNumber = params->sflv.tupleS.sequenceNumber; 488 if (bejIsArrayElement(params)) 489 { 490 sequenceNumber = 0; 491 } 492 const uint8_t* dictionary; 493 const struct BejDictionaryProperty* prop; 494 RETURN_IF_IERROR( 495 bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema, 496 sequenceNumber, &dictionary, &prop)); 497 498 const char* propName = ""; 499 if (params->state.addPropertyName) 500 { 501 propName = bejDictGetPropertyName(dictionary, prop->nameOffset, 502 prop->nameLength); 503 } 504 505 if (params->sflv.valueLength == 0) 506 { 507 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, 508 propName, params->callbacksDataPtr); 509 } 510 else 511 { 512 // Get the string for enum value. 513 uint16_t enumValueSequenceN = 514 (uint16_t)(bejGetNnint(params->sflv.value)); 515 const struct BejDictionaryProperty* enumValueProp; 516 RETURN_IF_IERROR( 517 bejDictGetProperty(dictionary, prop->childPointerOffset, 518 enumValueSequenceN, &enumValueProp)); 519 const char* enumValueName = bejDictGetPropertyName( 520 dictionary, enumValueProp->nameOffset, enumValueProp->nameLength); 521 522 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackEnum, 523 propName, enumValueName, 524 params->callbacksDataPtr); 525 } 526 // Update the offset to point to the next possible SFLV tuple. 527 params->state.encodedStreamOffset = params->sflv.valueEndOffset; 528 return bejProcessEnding(params, /*canBeEmpty=*/false); 529 } 530 531 /** 532 * @brief Decodes a BejString type SFLV BEJ tuple. 533 * 534 * @param[in] params - a valid BejHandleTypeFuncParam struct. 535 * @return 0 if successful. 536 */ 537 static int bejHandleBejString(struct BejHandleTypeFuncParam* params) 538 { 539 // TODO: Handle deferred bindings. 540 const char* propName = bejGetPropName(params); 541 542 if (params->sflv.valueLength == 0) 543 { 544 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, 545 propName, params->callbacksDataPtr); 546 } 547 else 548 { 549 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackString, 550 propName, (const char*)(params->sflv.value), 551 params->callbacksDataPtr); 552 } 553 params->state.encodedStreamOffset = params->sflv.valueEndOffset; 554 return bejProcessEnding(params, /*canBeEmpty=*/false); 555 } 556 557 /** 558 * @brief Decodes a BejReal type SFLV BEJ tuple. 559 * 560 * @param[in] params - a valid BejHandleTypeFuncParam struct. 561 * @return 0 if successful. 562 */ 563 static int bejHandleBejReal(struct BejHandleTypeFuncParam* params) 564 { 565 const char* propName = bejGetPropName(params); 566 567 if (params->sflv.valueLength == 0) 568 { 569 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, 570 propName, params->callbacksDataPtr); 571 } 572 else 573 { 574 // Real value has the following format. 575 // nnint - Length of whole 576 // bejInteger - whole (includes sign for the overall real number) 577 // nnint - Leading zero count for fract 578 // nnint - fract 579 // nnint - Length of exp 580 // bejInteger - exp (includes sign for the exponent) 581 uint8_t wholeByteLen = (uint8_t)bejGetNnint(params->sflv.value); 582 const uint8_t* wholeBejInt = 583 params->sflv.value + bejGetNnintSize(params->sflv.value); 584 const uint8_t* fractZeroCountNnint = wholeBejInt + wholeByteLen; 585 const uint8_t* fractNnint = 586 fractZeroCountNnint + bejGetNnintSize(fractZeroCountNnint); 587 const uint8_t* lenExpNnint = fractNnint + bejGetNnintSize(fractNnint); 588 const uint8_t* expBejInt = lenExpNnint + bejGetNnintSize(lenExpNnint); 589 590 struct BejReal realValue; 591 realValue.whole = bejGetIntegerValue(wholeBejInt, wholeByteLen); 592 realValue.zeroCount = bejGetNnint(fractZeroCountNnint); 593 realValue.fract = bejGetNnint(fractNnint); 594 realValue.expLen = (uint8_t)bejGetNnint(lenExpNnint); 595 if (realValue.expLen != 0) 596 { 597 realValue.exp = bejGetIntegerValue( 598 expBejInt, (uint8_t)bejGetNnint(lenExpNnint)); 599 } 600 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackReal, 601 propName, &realValue, 602 params->callbacksDataPtr); 603 } 604 params->state.encodedStreamOffset = params->sflv.valueEndOffset; 605 return bejProcessEnding(params, /*canBeEmpty=*/false); 606 } 607 608 /** 609 * @brief Decodes a BejBoolean type SFLV BEJ tuple. 610 * 611 * @param[in] params - a valid BejHandleTypeFuncParam struct. 612 * @return 0 if successful. 613 */ 614 static int bejHandleBejBoolean(struct BejHandleTypeFuncParam* params) 615 { 616 const char* propName = bejGetPropName(params); 617 618 if (params->sflv.valueLength == 0) 619 { 620 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, 621 propName, params->callbacksDataPtr); 622 } 623 else 624 { 625 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackBool, 626 propName, *(params->sflv.value) > 0, 627 params->callbacksDataPtr); 628 } 629 params->state.encodedStreamOffset = params->sflv.valueEndOffset; 630 return bejProcessEnding(params, /*canBeEmpty=*/false); 631 } 632 633 /** 634 * @brief Decodes a BejPropertyAnnotation type SFLV BEJ tuple. 635 * 636 * @param[in] params - a valid BejHandleTypeFuncParam struct. 637 * @return 0 if successful. 638 */ 639 static int bejHandleBejPropertyAnnotation(struct BejHandleTypeFuncParam* params) 640 { 641 // TODO: Handle colon-delimited string values. 642 643 // Property annotation has the form OuterProperty@Annotation. First 644 // processing the outer property name. 645 const uint8_t* outerDictionary; 646 const struct BejDictionaryProperty* outerProp; 647 RETURN_IF_IERROR(bejGetDictionaryAndProperty( 648 params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber, 649 &outerDictionary, &outerProp)); 650 651 const char* propName = bejDictGetPropertyName( 652 outerDictionary, outerProp->nameOffset, outerProp->nameLength); 653 RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackAnnotation, 654 propName, params->callbacksDataPtr); 655 656 // Mark the ending of the property annotation. 657 struct BejStackProperty newEnding = { 658 .sectionType = bejSectionNoType, 659 .addPropertyName = params->state.addPropertyName, 660 .mainDictPropOffset = params->state.mainDictPropOffset, 661 .annoDictPropOffset = params->state.annoDictPropOffset, 662 .streamEndOffset = params->sflv.valueEndOffset, 663 }; 664 // Update the states for the next encoding segment. 665 RETURN_IF_IERROR( 666 params->stackCallback->stackPush(&newEnding, params->stackDataPtr)); 667 params->state.addPropertyName = true; 668 // We might have to change this for nested annotations. 669 params->state.mainDictPropOffset = outerProp->childPointerOffset; 670 // Point to the start of the value for next decoding. 671 params->state.encodedStreamOffset = 672 params->sflv.valueEndOffset - params->sflv.valueLength; 673 return 0; 674 } 675 676 /** 677 * @brief Decodes an encoded bej stream. 678 * 679 * @param[in] schemaDictionary - main schema dictionary to use. 680 * @param[in] annotationDictionary - annotation dictionary 681 * @param[in] enStream - encoded stream without the PLDM header. 682 * @param[in] streamLen - length of the enStream. 683 * @param[in] stackCallback - callbacks for stack handlers. 684 * @param[in] decodedCallback - callbacks for extracting decoded properties. 685 * @param[in] callbacksDataPtr - data pointer to pass to decoded callbacks. This 686 * can be used pass additional data. 687 * @param[in] stackDataPtr - data pointer to pass to stack callbacks. This can 688 * be used pass additional data. 689 * 690 * @return 0 if successful. 691 */ 692 static int bejDecode(const uint8_t* schemaDictionary, 693 const uint8_t* annotationDictionary, 694 const uint8_t* enStream, uint32_t streamLen, 695 const struct BejStackCallback* stackCallback, 696 const struct BejDecodedCallback* decodedCallback, 697 void* callbacksDataPtr, void* stackDataPtr) 698 { 699 struct BejHandleTypeFuncParam params = { 700 .state = 701 { 702 // We only add names of set properties. We don't use names for 703 // array 704 // properties. Here we are omitting the name of the root set. 705 .addPropertyName = false, 706 // At start, parent property from the main dictionary is the 707 // first property. 708 .mainDictPropOffset = bejDictGetPropertyHeadOffset(), 709 .annoDictPropOffset = bejDictGetFirstAnnotatedPropertyOffset(), 710 // Current location of the encoded segment we are processing. 711 .encodedStreamOffset = 0, 712 .encodedSubStream = enStream, 713 }, 714 .mainDictionary = schemaDictionary, 715 .annotDictionary = annotationDictionary, 716 .decodedCallback = decodedCallback, 717 .stackCallback = stackCallback, 718 .callbacksDataPtr = callbacksDataPtr, 719 .stackDataPtr = stackDataPtr, 720 }; 721 722 while (params.state.encodedStreamOffset < streamLen) 723 { 724 // Go to the next encoded segment in the encoded stream. 725 params.state.encodedSubStream = 726 enStream + params.state.encodedStreamOffset; 727 bejInitSFLVStruct(¶ms); 728 729 if (params.sflv.format.readOnlyProperty) 730 { 731 RETURN_IF_CALLBACK_IERROR( 732 params.decodedCallback->callbackReadonlyProperty, 733 params.sflv.tupleS.sequenceNumber, params.callbacksDataPtr); 734 } 735 736 // TODO: Handle nullable property types. These are indicated by 737 // params.sflv.format.nullableProperty 738 switch (params.sflv.format.principalDataType) 739 { 740 case bejSet: 741 RETURN_IF_IERROR(bejHandleBejSet(¶ms)); 742 break; 743 case bejArray: 744 RETURN_IF_IERROR(bejHandleBejArray(¶ms)); 745 break; 746 case bejNull: 747 RETURN_IF_IERROR(bejHandleBejNull(¶ms)); 748 break; 749 case bejInteger: 750 RETURN_IF_IERROR(bejHandleBejInteger(¶ms)); 751 break; 752 case bejEnum: 753 RETURN_IF_IERROR(bejHandleBejEnum(¶ms)); 754 break; 755 case bejString: 756 RETURN_IF_IERROR(bejHandleBejString(¶ms)); 757 break; 758 case bejReal: 759 RETURN_IF_IERROR(bejHandleBejReal(¶ms)); 760 break; 761 case bejBoolean: 762 RETURN_IF_IERROR(bejHandleBejBoolean(¶ms)); 763 break; 764 case bejBytestring: 765 // TODO: Add support for BejBytestring decoding. 766 fprintf(stderr, "No BejBytestring support\n"); 767 params.state.encodedStreamOffset = params.sflv.valueEndOffset; 768 break; 769 case bejChoice: 770 // TODO: Add support for BejChoice decoding. 771 fprintf(stderr, "No BejChoice support\n"); 772 params.state.encodedStreamOffset = params.sflv.valueEndOffset; 773 break; 774 case bejPropertyAnnotation: 775 RETURN_IF_IERROR(bejHandleBejPropertyAnnotation(¶ms)); 776 break; 777 case bejResourceLink: 778 // TODO: Add support for BejResourceLink decoding. 779 fprintf(stderr, "No BejResourceLink support\n"); 780 params.state.encodedStreamOffset = params.sflv.valueEndOffset; 781 break; 782 case bejResourceLinkExpansion: 783 // TODO: Add support for BejResourceLinkExpansion decoding. 784 fprintf(stderr, "No BejResourceLinkExpansion support\n"); 785 params.state.encodedStreamOffset = params.sflv.valueEndOffset; 786 break; 787 default: 788 break; 789 } 790 } 791 RETURN_IF_IERROR(bejProcessEnding(¶ms, /*canBeEmpty=*/true)); 792 if (!params.stackCallback->stackEmpty(params.stackDataPtr)) 793 { 794 fprintf(stderr, "Ending stack should be empty but its not. Something " 795 "must have gone wrong with the encoding\n"); 796 return bejErrorUnknown; 797 } 798 return 0; 799 } 800 801 /** 802 * @brief Check if a bej version is supported by this decoder 803 * 804 * @param[in] bejVersion - the bej version in the received encoded stream 805 * @return true if supported. 806 */ 807 static bool bejIsSupported(uint32_t bejVersion) 808 { 809 for (uint32_t i = 0; i < sizeof(supportedBejVersions) / sizeof(uint32_t); 810 ++i) 811 { 812 if (bejVersion == supportedBejVersions[i]) 813 { 814 return true; 815 } 816 } 817 return false; 818 } 819 820 int bejDecodePldmBlock(const struct BejDictionaries* dictionaries, 821 const uint8_t* encodedPldmBlock, uint32_t blockLength, 822 const struct BejStackCallback* stackCallback, 823 const struct BejDecodedCallback* decodedCallback, 824 void* callbacksDataPtr, void* stackDataPtr) 825 { 826 NULL_CHECK(dictionaries, "dictionaries"); 827 NULL_CHECK(dictionaries->schemaDictionary, "schemaDictionary"); 828 NULL_CHECK(dictionaries->annotationDictionary, "annotationDictionary"); 829 830 NULL_CHECK(encodedPldmBlock, "encodedPldmBlock"); 831 832 NULL_CHECK(stackCallback, "stackCallback"); 833 NULL_CHECK(stackCallback->stackEmpty, "stackEmpty"); 834 NULL_CHECK(stackCallback->stackPeek, "stackPeek"); 835 NULL_CHECK(stackCallback->stackPop, "stackPop"); 836 NULL_CHECK(stackCallback->stackPush, "stackPush"); 837 838 NULL_CHECK(decodedCallback, "decodedCallback"); 839 840 uint32_t pldmHeaderSize = sizeof(struct BejPldmBlockHeader); 841 if (blockLength < pldmHeaderSize) 842 { 843 fprintf(stderr, "Invalid pldm block size: %u\n", blockLength); 844 return bejErrorInvalidSize; 845 } 846 847 const struct BejPldmBlockHeader* pldmHeader = 848 (const struct BejPldmBlockHeader*)encodedPldmBlock; 849 850 if (!bejIsSupported(pldmHeader->bejVersion)) 851 { 852 fprintf(stderr, "Bej decoder doesn't support the bej version: %u\n", 853 pldmHeader->bejVersion); 854 return bejErrorNotSuppoted; 855 } 856 857 if (pldmHeader->schemaClass == bejAnnotationSchemaClass) 858 { 859 fprintf(stderr, 860 "Encoder schema class cannot be BejAnnotationSchemaClass\n"); 861 return bejErrorNotSuppoted; 862 } 863 // TODO: Add support for CollectionMemberType schema class. 864 if (pldmHeader->schemaClass == bejCollectionMemberTypeSchemaClass) 865 { 866 fprintf(stderr, "Decoder doesn't support " 867 "bejCollectionMemberTypeSchemaClass yet.\n"); 868 return bejErrorNotSuppoted; 869 } 870 // TODO: Add support for Error schema class. 871 if (pldmHeader->schemaClass == bejErrorSchemaClass) 872 { 873 fprintf(stderr, "Decoder doesn't support BejErrorSchemaClass yet.\n"); 874 return bejErrorNotSuppoted; 875 } 876 877 // Skip the PLDM header. 878 const uint8_t* enStream = encodedPldmBlock + pldmHeaderSize; 879 uint32_t streamLen = blockLength - pldmHeaderSize; 880 return bejDecode(dictionaries->schemaDictionary, 881 dictionaries->annotationDictionary, enStream, streamLen, 882 stackCallback, decodedCallback, callbacksDataPtr, 883 stackDataPtr); 884 } 885