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