1 #include "bej_encoder_metadata.h" 2 3 #include "bej_common.h" 4 #include "bej_dictionary.h" 5 6 #include <math.h> 7 #include <stdint.h> 8 #include <stdio.h> 9 #include <string.h> 10 11 /** 12 * @brief Maximum digits supported in the fractional part of a real number. 13 */ 14 #define BEJ_REAL_PRECISION 16 15 16 /** 17 * @brief bejTupleL size of an integer. 18 * 19 * Maximum bytes possible for an integer is 8. Therefore to encode the length of 20 * an integer using a nnint, we only need two bytes. [byte1: nnint length, 21 * byte2: integer length [0-8]] 22 */ 23 #define BEJ_TUPLE_L_SIZE_FOR_BEJ_INTEGER 2 24 25 /** 26 * @brief bejTupleL size of a bool. 27 * 28 * 1byte for the nnint length and 1 byte for the value. 29 */ 30 #define BEJ_TUPLE_L_SIZE_FOR_BEJ_BOOL 2 31 32 /** 33 * @brief bejTupleF size. 34 */ 35 #define BEJ_TUPLE_F_SIZE 1 36 37 /** 38 * @brief Check the name is an annotation type name. 39 * 40 * @param[in] name - property name. 41 * @return true for annotation name, false otherwise. 42 */ 43 static bool bejIsAnnotation(const char* name) 44 { 45 if (name == NULL) 46 { 47 return false; 48 } 49 return name[0] == '@'; 50 } 51 52 /** 53 * @brief Get the dictionary for the provided node. 54 * 55 * @param[in] dictionaries - available dictionaries for encoding. 56 * @param[in] parentDictionary - dictionary used for the parent of this node. 57 * @param[in] nodeName - name of the interested node. Can be NULL if the node 58 * doesn't have a name. 59 * @return a pointer to the dictionary to be used. 60 */ 61 static const uint8_t* 62 bejGetRelatedDictionary(const struct BejDictionaries* dictionaries, 63 const uint8_t* parentDictionary, 64 const char* nodeName) 65 { 66 // If the node name is NULL, we have to use parent dictionary. 67 if (nodeName == NULL) 68 { 69 return parentDictionary; 70 } 71 72 // If the parent is using annotation dictionary, that means the parent is an 73 // annotation. Therefore the child (this node) should be an annotation too 74 // (Could this be false?). Therefore we should use the annotation dictionary 75 // for this node as well. 76 if (parentDictionary == dictionaries->annotationDictionary) 77 { 78 return dictionaries->annotationDictionary; 79 } 80 return bejIsAnnotation(nodeName) ? dictionaries->annotationDictionary 81 : dictionaries->schemaDictionary; 82 } 83 84 /** 85 * @brief Get dictionary data for the given node. 86 * 87 * @param[in] dictionaries - available dictionaries. 88 * @param[in] parentDictionary - the dictionary used by the provided node's 89 * parent. 90 * @param[in] node - node that caller is interested in. 91 * @param[in] nodeIndex - index of this node within its parent. 92 * @param[in] dictStartingOffset - starting dictionary child offset value of 93 * this node's parent. 94 * @param[out] sequenceNumber - sequence number of the node. bit0 specifies the 95 * dictionary schema type: [major|annotation]. 96 * @param[out] nodeDictionary - if not NULL, return a pointer to the dictionary 97 * used for the node. 98 * @param[out] childEntryOffset - if not NULL, return the dictionary starting 99 * offset used for this nodes children. If this node is not supposed to have 100 * children, caller should ignore this value. 101 * @return 0 if successful. 102 */ 103 static int bejFindSeqNumAndChildDictOffset( 104 const struct BejDictionaries* dictionaries, const uint8_t* parentDictionary, 105 struct RedfishPropertyNode* node, uint16_t nodeIndex, 106 uint16_t dictStartingOffset, uint32_t* sequenceNumber, 107 const uint8_t** nodeDictionary, uint16_t* childEntryOffset) 108 { 109 // If the node doesn't have a name, we can't use a dictionary. So we can use 110 // its parent's info. 111 if (node->name == NULL || node->name[0] == '\0') 112 { 113 if (nodeDictionary != NULL) 114 { 115 *nodeDictionary = parentDictionary; 116 } 117 118 if (childEntryOffset != NULL) 119 { 120 *childEntryOffset = dictStartingOffset; 121 } 122 123 // If the property doesn't have a name, it has to be an element of an 124 // array. In that case, sequence number is the array index. 125 *sequenceNumber = (uint32_t)nodeIndex << 1; 126 if (dictionaries->annotationDictionary == parentDictionary) 127 { 128 *sequenceNumber |= 1; 129 } 130 return 0; 131 } 132 133 // If we are here, the property has a name. 134 const uint8_t* dictionary = 135 bejGetRelatedDictionary(dictionaries, parentDictionary, node->name); 136 bool isAnnotation = dictionary == dictionaries->annotationDictionary; 137 // If this node's dictionary and its parent's dictionary is different, 138 // this node should start searching from the beginning of its 139 // dictionary. This should only happen for property annotations of form 140 // property@annotation_class.annotation_name. 141 if (dictionary != parentDictionary) 142 { 143 // Redundancy check. 144 if (!isAnnotation) 145 { 146 fprintf(stderr, 147 "Dictionary for property %s should be the annotation " 148 "dictionary. Might be a encoding failure. Maybe the " 149 "JSON tree is not created correctly.", 150 node->name); 151 return -1; 152 } 153 dictStartingOffset = bejDictGetFirstAnnotatedPropertyOffset(); 154 } 155 156 const struct BejDictionaryProperty* property; 157 int ret = bejDictGetPropertyByName(dictionary, dictStartingOffset, 158 node->name, &property, NULL); 159 if (ret != 0) 160 { 161 fprintf(stderr, 162 "Failed to find dictionary entry for name %s. Search started " 163 "at offset: %u. ret: %d\n", 164 node->name, dictStartingOffset, ret); 165 return ret; 166 } 167 168 if (nodeDictionary != NULL) 169 { 170 *nodeDictionary = dictionary; 171 } 172 173 if (childEntryOffset != NULL) 174 { 175 *childEntryOffset = property->childPointerOffset; 176 } 177 178 *sequenceNumber = (uint32_t)(property->sequenceNumber) << 1; 179 if (isAnnotation) 180 { 181 *sequenceNumber |= 1; 182 } 183 return 0; 184 } 185 186 static int bejUpdateIntMetaData(const struct BejDictionaries* dictionaries, 187 const uint8_t* parentDictionary, 188 struct RedfishPropertyLeafInt* node, 189 uint16_t nodeIndex, uint16_t dictStartingOffset) 190 { 191 uint32_t sequenceNumber; 192 RETURN_IF_IERROR(bejFindSeqNumAndChildDictOffset( 193 dictionaries, parentDictionary, &node->leaf.nodeAttr, nodeIndex, 194 dictStartingOffset, &sequenceNumber, NULL, NULL)); 195 node->leaf.metaData.sequenceNumber = sequenceNumber; 196 197 // Calculate the size for encoding this in a SFLV tuple. 198 // S: Size needed for encoding sequence number. 199 node->leaf.metaData.sflSize = bejNnintEncodingSizeOfUInt(sequenceNumber); 200 // F: Size of the format byte is 1. 201 node->leaf.metaData.sflSize += BEJ_TUPLE_F_SIZE; 202 // L: Length needed for the value. 203 node->leaf.metaData.sflSize += BEJ_TUPLE_L_SIZE_FOR_BEJ_INTEGER; 204 // V: Bytes used for the value. 205 node->leaf.metaData.vSize = bejIntLengthOfValue(node->value); 206 return 0; 207 } 208 209 static int bejUpdateStringMetaData(const struct BejDictionaries* dictionaries, 210 const uint8_t* parentDictionary, 211 struct RedfishPropertyLeafString* node, 212 uint16_t nodeIndex, 213 uint16_t dictStartingOffset) 214 { 215 uint32_t sequenceNumber; 216 RETURN_IF_IERROR(bejFindSeqNumAndChildDictOffset( 217 dictionaries, parentDictionary, &(node->leaf.nodeAttr), nodeIndex, 218 dictStartingOffset, &sequenceNumber, NULL, NULL)); 219 node->leaf.metaData.sequenceNumber = sequenceNumber; 220 221 // Calculate the size for encoding this in a SFLV tuple. 222 // S: Size needed for encoding sequence number. 223 node->leaf.metaData.sflSize = bejNnintEncodingSizeOfUInt(sequenceNumber); 224 // F: Size of the format byte is 1. 225 node->leaf.metaData.sflSize += BEJ_TUPLE_F_SIZE; 226 // L: Length needed for the string including the NULL character. Length is 227 // in nnint format. 228 size_t strLenWithNull = strlen(node->value) + 1; 229 node->leaf.metaData.sflSize += bejNnintEncodingSizeOfUInt(strLenWithNull); 230 // V: Bytes used for the value. 231 node->leaf.metaData.vSize = strLenWithNull; 232 return 0; 233 } 234 235 static int bejUpdateRealMetaData(const struct BejDictionaries* dictionaries, 236 const uint8_t* parentDictionary, 237 struct RedfishPropertyLeafReal* node, 238 uint16_t nodeIndex, 239 uint16_t dictStartingOffset) 240 { 241 uint32_t sequenceNumber; 242 RETURN_IF_IERROR(bejFindSeqNumAndChildDictOffset( 243 dictionaries, parentDictionary, &(node->leaf.nodeAttr), nodeIndex, 244 dictStartingOffset, &sequenceNumber, NULL, NULL)); 245 node->leaf.metaData.sequenceNumber = sequenceNumber; 246 247 if (node->value > (double)INT64_MAX) 248 { 249 // TODO: We should use the exponent. 250 fprintf( 251 stderr, 252 "Need to add support to encode double value larger than INT64_MAX\n"); 253 return -1; 254 } 255 256 // Calculate the size for encoding this in a SFLV tuple. 257 // S: Size needed for encoding sequence number. 258 node->leaf.metaData.sflSize = bejNnintEncodingSizeOfUInt(sequenceNumber); 259 // F: Size of the format byte is 1. 260 node->leaf.metaData.sflSize += BEJ_TUPLE_F_SIZE; 261 // We need to breakdown the real number to bejReal type to determine the 262 // length. We are not gonna add an exponent. It will only be the whole part 263 // and the fraction part. Get the whole part 264 double originalWhole; 265 double originalFract = modf(node->value, &originalWhole); 266 267 // Convert the fraction to a whole value for encoding. 268 // Create a new value by multiplying the original fraction by 10. Do this 269 // until the fraction of the new value is 0 or we reach the precision. Eg 270 // 0.00105: This fraction value has two leading zeros. We will keep 271 // multiplying this by 10 until the fraction of the result of that 272 // multiplication is 0. 273 double originalFactConvertedToWhole = fabs(originalFract); 274 double fract = originalFract; 275 double intPart; 276 uint32_t leadingZeros = 0; 277 uint32_t precision = 0; 278 while (fract != 0 && precision < BEJ_REAL_PRECISION) 279 { 280 originalFactConvertedToWhole = originalFactConvertedToWhole * 10; 281 fract = modf(originalFactConvertedToWhole, &intPart); 282 // If the integer portion is 0, that means we still have leading zeros. 283 if (intPart == 0) 284 { 285 ++leadingZeros; 286 } 287 ++precision; 288 } 289 node->bejReal.whole = (int64_t)originalWhole; 290 node->bejReal.zeroCount = leadingZeros; 291 node->bejReal.fract = (int64_t)originalFactConvertedToWhole; 292 // We are omitting exp. So the exp length should be 0. 293 node->bejReal.expLen = 0; 294 node->bejReal.exp = 0; 295 296 // Calculate the sizes needed for storing bejReal fields. 297 // nnint for the length of the "whole" value. 298 node->leaf.metaData.vSize = BEJ_TUPLE_L_SIZE_FOR_BEJ_INTEGER; 299 // Length needed for the "whole" value. 300 node->leaf.metaData.vSize += bejIntLengthOfValue((int64_t)originalWhole); 301 // nnint for leading zero count. 302 node->leaf.metaData.vSize += bejNnintEncodingSizeOfUInt(leadingZeros); 303 // nnint for the factional part. 304 node->leaf.metaData.vSize += 305 bejNnintEncodingSizeOfUInt((int64_t)originalFactConvertedToWhole); 306 // nnint for the exp length. We are omitting exp. So the exp length should 307 // be 0. 308 node->leaf.metaData.vSize += bejNnintEncodingSizeOfUInt(0); 309 310 // L: nnint for the size needed for encoding the bejReal value. 311 node->leaf.metaData.sflSize += 312 bejNnintEncodingSizeOfUInt(node->leaf.metaData.vSize); 313 return 0; 314 } 315 316 static int bejUpdateEnumMetaData(const struct BejDictionaries* dictionaries, 317 const uint8_t* parentDictionary, 318 struct RedfishPropertyLeafEnum* node, 319 uint16_t nodeIndex, 320 uint16_t dictStartingOffset) 321 { 322 const uint8_t* nodeDictionary; 323 uint16_t childEntryOffset; 324 uint32_t sequenceNumber; 325 // If the enum property doesn't have a name, this will simply return the 326 // nodeIndex encoded as the sequence number. If not, this will return the 327 // sequence number in the dictionary and the starting dictionary index for 328 // the enum values. 329 RETURN_IF_IERROR(bejFindSeqNumAndChildDictOffset( 330 dictionaries, parentDictionary, &(node->leaf.nodeAttr), nodeIndex, 331 dictStartingOffset, &sequenceNumber, &nodeDictionary, 332 &childEntryOffset)); 333 // Update the sequence number of the property. 334 node->leaf.metaData.sequenceNumber = sequenceNumber; 335 336 // Get the sequence number for the Enum value. 337 if (node->leaf.nodeAttr.name != NULL && node->leaf.nodeAttr.name[0] != '\0') 338 { 339 dictStartingOffset = childEntryOffset; 340 } 341 const struct BejDictionaryProperty* enumValueProperty; 342 int ret = bejDictGetPropertyByName(nodeDictionary, dictStartingOffset, 343 node->value, &enumValueProperty, NULL); 344 if (ret != 0) 345 { 346 fprintf( 347 stderr, 348 "Failed to find dictionary entry for enum value %s. Search started " 349 "at offset: %u. ret: %d\n", 350 node->value, dictStartingOffset, ret); 351 return ret; 352 } 353 node->enumValueSeq = enumValueProperty->sequenceNumber; 354 355 // Calculate the size for encoding this in a SFLV tuple. 356 // S: Size needed for encoding sequence number. 357 node->leaf.metaData.sflSize = bejNnintEncodingSizeOfUInt(sequenceNumber); 358 // F: Size of the format byte is 1. 359 node->leaf.metaData.sflSize += BEJ_TUPLE_F_SIZE; 360 // V: Bytes used for the value. 361 node->leaf.metaData.vSize = 362 bejNnintEncodingSizeOfUInt(enumValueProperty->sequenceNumber); 363 // L: Length needed for the value nnint. 364 node->leaf.metaData.sflSize += 365 bejNnintEncodingSizeOfUInt(node->leaf.metaData.vSize); 366 return 0; 367 } 368 369 static int bejUpdateBoolMetaData(const struct BejDictionaries* dictionaries, 370 const uint8_t* parentDictionary, 371 struct RedfishPropertyLeafBool* node, 372 uint16_t nodeIndex, 373 uint16_t dictStartingOffset) 374 { 375 uint32_t sequenceNumber; 376 RETURN_IF_IERROR(bejFindSeqNumAndChildDictOffset( 377 dictionaries, parentDictionary, &node->leaf.nodeAttr, nodeIndex, 378 dictStartingOffset, &sequenceNumber, NULL, NULL)); 379 node->leaf.metaData.sequenceNumber = sequenceNumber; 380 381 // Calculate the size for encoding this in a SFLV tuple. 382 // S: Size needed for encoding sequence number. 383 node->leaf.metaData.sflSize = bejNnintEncodingSizeOfUInt(sequenceNumber); 384 // F: Size of the format byte is 1. 385 node->leaf.metaData.sflSize += BEJ_TUPLE_F_SIZE; 386 // L: Length needed for the value. 387 node->leaf.metaData.sflSize += BEJ_TUPLE_L_SIZE_FOR_BEJ_BOOL; 388 // V: Bytes used for the value; 0x00 or 0xFF. 389 node->leaf.metaData.vSize = 1; 390 return 0; 391 } 392 393 /** 394 * @brief Update metadata of leaf nodes. 395 * 396 * @param dictionaries - dictionaries needed for encoding. 397 * @param parentDictionary - dictionary used by this node's parent. 398 * @param childPtr - a pointer to the leaf node. 399 * @param childIndex - if this node is an array element, this is the array 400 * index. 401 * @param dictStartingOffset - starting dictionary child offset value of this 402 * node's parent. 403 * @return 0 if successful. 404 */ 405 static int bejUpdateLeafNodeMetaData(const struct BejDictionaries* dictionaries, 406 const uint8_t* parentDictionary, 407 void* childPtr, uint16_t childIndex, 408 uint16_t dictStartingOffset) 409 { 410 struct RedfishPropertyLeaf* chNode = childPtr; 411 412 switch (chNode->nodeAttr.format.principalDataType) 413 { 414 case bejInteger: 415 RETURN_IF_IERROR( 416 bejUpdateIntMetaData(dictionaries, parentDictionary, childPtr, 417 childIndex, dictStartingOffset)); 418 break; 419 case bejString: 420 RETURN_IF_IERROR(bejUpdateStringMetaData( 421 dictionaries, parentDictionary, childPtr, childIndex, 422 dictStartingOffset)); 423 break; 424 case bejReal: 425 RETURN_IF_IERROR( 426 bejUpdateRealMetaData(dictionaries, parentDictionary, childPtr, 427 childIndex, dictStartingOffset)); 428 break; 429 case bejEnum: 430 RETURN_IF_IERROR( 431 bejUpdateEnumMetaData(dictionaries, parentDictionary, childPtr, 432 childIndex, dictStartingOffset)); 433 break; 434 case bejBoolean: 435 RETURN_IF_IERROR( 436 bejUpdateBoolMetaData(dictionaries, parentDictionary, childPtr, 437 childIndex, dictStartingOffset)); 438 break; 439 default: 440 fprintf(stderr, "Child type %u not supported\n", 441 chNode->nodeAttr.format.principalDataType); 442 return -1; 443 } 444 return 0; 445 } 446 447 /** 448 * @brief Update metadata of a parent node. 449 * 450 * @param dictionaries - dictionaries needed for encoding. 451 * @param parentDictionary - dictionary used by this node's parent. 452 * @param dictStartingOffset - starting dictionary child offset value of this 453 * node's parent. 454 * @param node - a pointer to the parent node. 455 * @param nodeIndex - If this node is an array element, this is the array index. 456 * @return 0 if successful. 457 */ 458 static int bejUpdateParentMetaData(const struct BejDictionaries* dictionaries, 459 const uint8_t* parentDictionary, 460 uint16_t dictStartingOffset, 461 struct RedfishPropertyParent* node, 462 uint16_t nodeIndex) 463 { 464 const uint8_t* nodeDictionary; 465 uint16_t childEntryOffset; 466 uint32_t sequenceNumber; 467 468 // Get the dictionary related data from the node. 469 RETURN_IF_IERROR(bejFindSeqNumAndChildDictOffset( 470 dictionaries, parentDictionary, &node->nodeAttr, nodeIndex, 471 dictStartingOffset, &sequenceNumber, &nodeDictionary, 472 &childEntryOffset)); 473 474 node->metaData.sequenceNumber = sequenceNumber; 475 node->metaData.childrenDictPropOffset = childEntryOffset; 476 node->metaData.nextChild = node->firstChild; 477 node->metaData.nextChildIndex = 0; 478 node->metaData.dictionary = nodeDictionary; 479 node->metaData.vSize = 0; 480 481 // S: Size needed for encoding sequence number. 482 node->metaData.sflSize = 483 bejNnintEncodingSizeOfUInt(node->metaData.sequenceNumber); 484 // F: Size of the format byte is 1. 485 node->metaData.sflSize += 1; 486 // V: Only for bejArray and bejSet types, value size should include the 487 // children count. We need to add the size needs to encode all the children 488 // later. 489 if (node->nodeAttr.format.principalDataType != bejPropertyAnnotation) 490 { 491 node->metaData.vSize = bejNnintEncodingSizeOfUInt(node->nChildren); 492 } 493 return 0; 494 } 495 496 /** 497 * @brief Update metadata of child nodes. 498 * 499 * If a child node contains its own child nodes, it will be added to the stack 500 * and function will return. 501 * 502 * @param dictionaries - dictionaries needed for encoding. 503 * @param parent - parent node. 504 * @param stack - stack holding parent nodes. 505 * @return 0 if successful. 506 */ 507 static int bejProcessChildNodes(const struct BejDictionaries* dictionaries, 508 struct RedfishPropertyParent* parent, 509 struct BejPointerStackCallback* stack) 510 { 511 // Get the next child of the parent. 512 void* childPtr = parent->metaData.nextChild; 513 514 // Process all the children belongs to the parent. 515 while (childPtr != NULL) 516 { 517 // If we find a child with its own child nodes, add it to the stack and 518 // return. 519 if (bejTreeIsParentType(childPtr)) 520 { 521 RETURN_IF_IERROR(bejUpdateParentMetaData( 522 dictionaries, parent->metaData.dictionary, 523 parent->metaData.childrenDictPropOffset, childPtr, 524 parent->metaData.nextChildIndex)); 525 526 RETURN_IF_IERROR(stack->stackPush(childPtr, stack->stackContext)); 527 bejParentGoToNextChild(parent, childPtr); 528 return 0; 529 } 530 531 RETURN_IF_IERROR( 532 bejUpdateLeafNodeMetaData(dictionaries, parent->metaData.dictionary, 533 childPtr, parent->metaData.nextChildIndex, 534 parent->metaData.childrenDictPropOffset)); 535 // Use the child value size to update the parent value size. 536 struct RedfishPropertyLeaf* leafChild = childPtr; 537 // V: Include the child size in parent's value size. 538 parent->metaData.vSize += 539 (leafChild->metaData.sflSize + leafChild->metaData.vSize); 540 541 // Get the next child belongs to the parent. 542 childPtr = bejParentGoToNextChild(parent, childPtr); 543 } 544 return 0; 545 } 546 547 int bejUpdateNodeMetadata(const struct BejDictionaries* dictionaries, 548 uint16_t majorSchemaStartingOffset, 549 struct RedfishPropertyParent* root, 550 struct BejPointerStackCallback* stack) 551 { 552 // Decide the starting property offset of the dictionary. 553 uint16_t dictOffset = bejDictGetPropertyHeadOffset(); 554 if (majorSchemaStartingOffset != BEJ_DICTIONARY_START_AT_HEAD) 555 { 556 dictOffset = majorSchemaStartingOffset; 557 } 558 559 // Initialize root node metadata. 560 RETURN_IF_IERROR( 561 bejUpdateParentMetaData(dictionaries, dictionaries->schemaDictionary, 562 dictOffset, root, /*childIndex=*/0)); 563 564 // Push the root to the stack. Because we are not done with the parent node 565 // yet. Need to figure out all bytes need to encode children of this parent, 566 // and save it in the parent metadata. 567 RETURN_IF_IERROR(stack->stackPush(root, stack->stackContext)); 568 569 while (!stack->stackEmpty(stack->stackContext)) 570 { 571 // Get the parent at the top of the stack. Stack is only popped if the 572 // parent stack entry has no pending children; That is 573 // parent->metaData.nextChild == NULL. 574 struct RedfishPropertyParent* parent = 575 stack->stackPeek(stack->stackContext); 576 577 // Calculate metadata of all the child nodes of the current parent node. 578 // If one of these child nodes has its own child nodes, that child node 579 // will be added to the stack and this function will return. 580 RETURN_IF_IERROR(bejProcessChildNodes(dictionaries, parent, stack)); 581 582 // If a new node hasn't been added to the stack, we know that this 583 // parent's child nodes have been processed. If not, do not pop the 584 // stack. 585 if (parent != stack->stackPeek(stack->stackContext)) 586 { 587 continue; 588 } 589 590 // If we are here; 591 // Then "parent" is the top element of the stack. 592 // All the children of "parent" has been processed. 593 594 // Remove the "parent" from the stack. 595 parent = stack->stackPop(stack->stackContext); 596 // L: Add the length needed to store the number of bytes used for the 597 // parent's value. 598 parent->metaData.sflSize += 599 bejNnintEncodingSizeOfUInt(parent->metaData.vSize); 600 601 // Since we now know the total size needs to encode the node pointed by 602 // "parent" variable, we should add that to the value size of this 603 // node's parent. Since we already popped this node from the stack, top 604 // of the stack element is this nodes's parent. "parentsParent" can be 605 // NULL if the node pointed by "parent" variable is the root. 606 struct RedfishPropertyParent* parentsParent = 607 stack->stackPeek(stack->stackContext); 608 if (parentsParent != NULL) 609 { 610 // V: Include the total size to encode the current parent in its 611 // parent's value size. 612 parentsParent->metaData.vSize += 613 (parent->metaData.sflSize + parent->metaData.vSize); 614 } 615 } 616 return 0; 617 } 618