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