xref: /openbmc/libbej/src/bej_encoder_metadata.c (revision be27f2e9bfab32d9281496614e3d15a49a4c6aa9)
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