xref: /openbmc/libbej/src/bej_encoder_metadata.c (revision 2bc745a3)
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 static int bejUpdateNullMetaData(const struct BejDictionaries* dictionaries,
394                                  const uint8_t* parentDictionary,
395                                  struct RedfishPropertyLeafNull* node,
396                                  uint16_t nodeIndex,
397                                  uint16_t dictStartingOffset)
398 {
399     uint32_t sequenceNumber;
400     RETURN_IF_IERROR(bejFindSeqNumAndChildDictOffset(
401         dictionaries, parentDictionary, &node->leaf.nodeAttr, nodeIndex,
402         dictStartingOffset, &sequenceNumber, NULL, NULL));
403     node->leaf.metaData.sequenceNumber = sequenceNumber;
404 
405     // Calculate the size for encoding this in a SFLV tuple.
406     // S: Size needed for encoding sequence number.
407     node->leaf.metaData.sflSize = bejNnintEncodingSizeOfUInt(sequenceNumber);
408     // F: Size of the format byte is 1.
409     node->leaf.metaData.sflSize += BEJ_TUPLE_F_SIZE;
410     // L: Length needed for the value. Value length for NULL type is 0. So we
411     // need to encode [0x01 0x00]
412     node->leaf.metaData.sflSize += BEJ_TUPLE_L_SIZE_FOR_BEJ_INTEGER;
413     node->leaf.metaData.vSize = 0;
414     return 0;
415 }
416 
417 /**
418  * @brief Update metadata of leaf nodes.
419  *
420  * @param dictionaries - dictionaries needed for encoding.
421  * @param parentDictionary - dictionary used by this node's parent.
422  * @param childPtr - a pointer to the leaf node.
423  * @param childIndex - if this node is an array element, this is the array
424  * index.
425  * @param dictStartingOffset - starting dictionary child offset value of this
426  * node's parent.
427  * @return 0 if successful.
428  */
429 static int bejUpdateLeafNodeMetaData(const struct BejDictionaries* dictionaries,
430                                      const uint8_t* parentDictionary,
431                                      void* childPtr, uint16_t childIndex,
432                                      uint16_t dictStartingOffset)
433 {
434     struct RedfishPropertyLeaf* chNode = childPtr;
435 
436     switch (chNode->nodeAttr.format.principalDataType)
437     {
438         case bejInteger:
439             RETURN_IF_IERROR(
440                 bejUpdateIntMetaData(dictionaries, parentDictionary, childPtr,
441                                      childIndex, dictStartingOffset));
442             break;
443         case bejString:
444             RETURN_IF_IERROR(bejUpdateStringMetaData(
445                 dictionaries, parentDictionary, childPtr, childIndex,
446                 dictStartingOffset));
447             break;
448         case bejReal:
449             RETURN_IF_IERROR(
450                 bejUpdateRealMetaData(dictionaries, parentDictionary, childPtr,
451                                       childIndex, dictStartingOffset));
452             break;
453         case bejEnum:
454             RETURN_IF_IERROR(
455                 bejUpdateEnumMetaData(dictionaries, parentDictionary, childPtr,
456                                       childIndex, dictStartingOffset));
457             break;
458         case bejBoolean:
459             RETURN_IF_IERROR(
460                 bejUpdateBoolMetaData(dictionaries, parentDictionary, childPtr,
461                                       childIndex, dictStartingOffset));
462             break;
463         case bejNull:
464             RETURN_IF_IERROR(
465                 bejUpdateNullMetaData(dictionaries, parentDictionary, childPtr,
466                                       childIndex, dictStartingOffset));
467             break;
468         default:
469             fprintf(stderr, "Child type %u not supported\n",
470                     chNode->nodeAttr.format.principalDataType);
471             return -1;
472     }
473     return 0;
474 }
475 
476 /**
477  * @brief Update metadata of a parent node.
478  *
479  * @param dictionaries - dictionaries needed for encoding.
480  * @param parentDictionary - dictionary used by this node's parent.
481  * @param dictStartingOffset - starting dictionary child offset value of this
482  * node's parent.
483  * @param node - a pointer to the parent node.
484  * @param nodeIndex - If this node is an array element, this is the array index.
485  * @return 0 if successful.
486  */
487 static int bejUpdateParentMetaData(const struct BejDictionaries* dictionaries,
488                                    const uint8_t* parentDictionary,
489                                    uint16_t dictStartingOffset,
490                                    struct RedfishPropertyParent* node,
491                                    uint16_t nodeIndex)
492 {
493     const uint8_t* nodeDictionary;
494     uint16_t childEntryOffset;
495     uint32_t sequenceNumber;
496 
497     // Get the dictionary related data from the node.
498     RETURN_IF_IERROR(bejFindSeqNumAndChildDictOffset(
499         dictionaries, parentDictionary, &node->nodeAttr, nodeIndex,
500         dictStartingOffset, &sequenceNumber, &nodeDictionary,
501         &childEntryOffset));
502 
503     node->metaData.sequenceNumber = sequenceNumber;
504     node->metaData.childrenDictPropOffset = childEntryOffset;
505     node->metaData.nextChild = node->firstChild;
506     node->metaData.nextChildIndex = 0;
507     node->metaData.dictionary = nodeDictionary;
508     node->metaData.vSize = 0;
509 
510     // S: Size needed for encoding sequence number.
511     node->metaData.sflSize =
512         bejNnintEncodingSizeOfUInt(node->metaData.sequenceNumber);
513     // F: Size of the format byte is 1.
514     node->metaData.sflSize += 1;
515     // V: Only for bejArray and bejSet types, value size should include the
516     // children count. We need to add the size needs to encode all the children
517     // later.
518     if (node->nodeAttr.format.principalDataType != bejPropertyAnnotation)
519     {
520         node->metaData.vSize = bejNnintEncodingSizeOfUInt(node->nChildren);
521     }
522     return 0;
523 }
524 
525 /**
526  * @brief Update metadata of child nodes.
527  *
528  * If a child node contains its own child nodes, it will be added to the stack
529  * and function will return.
530  *
531  * @param dictionaries - dictionaries needed for encoding.
532  * @param parent - parent node.
533  * @param stack - stack holding parent nodes.
534  * @return 0 if successful.
535  */
536 static int bejProcessChildNodes(const struct BejDictionaries* dictionaries,
537                                 struct RedfishPropertyParent* parent,
538                                 struct BejPointerStackCallback* stack)
539 {
540     // Get the next child of the parent.
541     void* childPtr = parent->metaData.nextChild;
542 
543     // Process all the children belongs to the parent.
544     while (childPtr != NULL)
545     {
546         // If we find a child with its own child nodes, add it to the stack and
547         // return.
548         if (bejTreeIsParentType(childPtr))
549         {
550             RETURN_IF_IERROR(bejUpdateParentMetaData(
551                 dictionaries, parent->metaData.dictionary,
552                 parent->metaData.childrenDictPropOffset, childPtr,
553                 parent->metaData.nextChildIndex));
554 
555             RETURN_IF_IERROR(stack->stackPush(childPtr, stack->stackContext));
556             bejParentGoToNextChild(parent, childPtr);
557             return 0;
558         }
559 
560         RETURN_IF_IERROR(
561             bejUpdateLeafNodeMetaData(dictionaries, parent->metaData.dictionary,
562                                       childPtr, parent->metaData.nextChildIndex,
563                                       parent->metaData.childrenDictPropOffset));
564         // Use the child value size to update the parent value size.
565         struct RedfishPropertyLeaf* leafChild = childPtr;
566         // V: Include the child size in parent's value size.
567         parent->metaData.vSize +=
568             (leafChild->metaData.sflSize + leafChild->metaData.vSize);
569 
570         // Get the next child belongs to the parent.
571         childPtr = bejParentGoToNextChild(parent, childPtr);
572     }
573     return 0;
574 }
575 
576 int bejUpdateNodeMetadata(const struct BejDictionaries* dictionaries,
577                           uint16_t majorSchemaStartingOffset,
578                           struct RedfishPropertyParent* root,
579                           struct BejPointerStackCallback* stack)
580 {
581     // Decide the starting property offset of the dictionary.
582     uint16_t dictOffset = bejDictGetPropertyHeadOffset();
583     if (majorSchemaStartingOffset != BEJ_DICTIONARY_START_AT_HEAD)
584     {
585         dictOffset = majorSchemaStartingOffset;
586     }
587 
588     // Initialize root node metadata.
589     RETURN_IF_IERROR(
590         bejUpdateParentMetaData(dictionaries, dictionaries->schemaDictionary,
591                                 dictOffset, root, /*childIndex=*/0));
592 
593     // Push the root to the stack. Because we are not done with the parent node
594     // yet. Need to figure out all bytes need to encode children of this parent,
595     // and save it in the parent metadata.
596     RETURN_IF_IERROR(stack->stackPush(root, stack->stackContext));
597 
598     while (!stack->stackEmpty(stack->stackContext))
599     {
600         // Get the parent at the top of the stack. Stack is only popped if the
601         // parent stack entry has no pending children; That is
602         // parent->metaData.nextChild == NULL.
603         struct RedfishPropertyParent* parent =
604             stack->stackPeek(stack->stackContext);
605 
606         // Calculate metadata of all the child nodes of the current parent node.
607         // If one of these child nodes has its own child nodes, that child node
608         // will be added to the stack and this function will return.
609         RETURN_IF_IERROR(bejProcessChildNodes(dictionaries, parent, stack));
610 
611         // If a new node hasn't been added to the stack, we know that this
612         // parent's child nodes have been processed. If not, do not pop the
613         // stack.
614         if (parent != stack->stackPeek(stack->stackContext))
615         {
616             continue;
617         }
618 
619         // If we are here;
620         // Then "parent" is the top element of the stack.
621         // All the children of "parent" has been processed.
622 
623         // Remove the "parent" from the stack.
624         parent = stack->stackPop(stack->stackContext);
625         // L: Add the length needed to store the number of bytes used for the
626         // parent's value.
627         parent->metaData.sflSize +=
628             bejNnintEncodingSizeOfUInt(parent->metaData.vSize);
629 
630         // Since we now know the total size needs to encode the node pointed by
631         // "parent" variable, we should add that to the value size of this
632         // node's parent. Since we already popped this node from the stack, top
633         // of the stack element is this nodes's parent. "parentsParent" can be
634         // NULL if the node pointed by "parent" variable is the root.
635         struct RedfishPropertyParent* parentsParent =
636             stack->stackPeek(stack->stackContext);
637         if (parentsParent != NULL)
638         {
639             // V: Include the total size to encode the current parent in its
640             // parent's value size.
641             parentsParent->metaData.vSize +=
642                 (parent->metaData.sflSize + parent->metaData.vSize);
643         }
644     }
645     return 0;
646 }
647