xref: /openbmc/libbej/src/bej_encoder_metadata.c (revision f68be959)
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 Check the name is an annotation type name.
12  *
13  * @param[in] name - property name.
14  * @return true for annotation name, false otherwise.
15  */
16 static bool bejIsAnnotation(const char* name)
17 {
18     if (name == NULL)
19     {
20         return false;
21     }
22     return name[0] == '@';
23 }
24 
25 /**
26  * @brief Get the dictionary for the provided node.
27  *
28  * @param[in] dictionaries - available dictionaries for encoding.
29  * @param[in] parentDictionary - dictionary used for the parent of this node.
30  * @param[in] nodeName - name of the interested node. Can be NULL if the node
31  * doesn't have a name.
32  * @return a pointer to the dictionary to be used.
33  */
34 static const uint8_t*
35     bejGetRelatedDictionary(const struct BejDictionaries* dictionaries,
36                             const uint8_t* parentDictionary,
37                             const char* nodeName)
38 {
39     // If the node name is NULL, we have to use parent dictionary.
40     if (nodeName == NULL)
41     {
42         return parentDictionary;
43     }
44 
45     // If the parent is using annotation dictionary, that means the parent is an
46     // annotation. Therefore the child (this node) should be an annotation too
47     // (Could this be false?). Therefore we should use the annotation dictionary
48     // for this node as well.
49     if (parentDictionary == dictionaries->annotationDictionary)
50     {
51         return dictionaries->annotationDictionary;
52     }
53     return bejIsAnnotation(nodeName) ? dictionaries->annotationDictionary
54                                      : dictionaries->schemaDictionary;
55 }
56 
57 /**
58  * @brief Get dictionary data for the given node.
59  *
60  * @param[in] dictionaries - available dictionaries.
61  * @param[in] parentDictionary - the dictionary used by the provided node's
62  * parent.
63  * @param[in] node - node that caller is interested in.
64  * @param[in] nodeIndex - index of this node within its parent.
65  * @param[in] dictStartingOffset - starting dictionary child offset value of
66  * this node's parent.
67  * @param[out] sequenceNumber - sequence number of the node. bit0 specifies the
68  * dictionary schema type: [major|annotation].
69  * @param[out] nodeDictionary - if not NULL, return a pointer to the dictionary
70  * used for the node.
71  * @param[out] childEntryOffset - if not NULL, return the dictionary starting
72  * offset used for this nodes children. If this node is not supposed to have
73  * children, caller should ignore this value.
74  * @return 0 if successful.
75  */
76 static int bejFindSeqNumAndChildDictOffset(
77     const struct BejDictionaries* dictionaries, const uint8_t* parentDictionary,
78     struct RedfishPropertyNode* node, uint16_t nodeIndex,
79     uint16_t dictStartingOffset, uint32_t* sequenceNumber,
80     const uint8_t** nodeDictionary, uint16_t* childEntryOffset)
81 {
82     // If the node doesn't have a name, we can't use a dictionary. So we can use
83     // its parent's info.
84     if (node->name == NULL || node->name[0] == '\0')
85     {
86         if (nodeDictionary != NULL)
87         {
88             *nodeDictionary = parentDictionary;
89         }
90 
91         if (childEntryOffset != NULL)
92         {
93             *childEntryOffset = dictStartingOffset;
94         }
95 
96         // If the property doesn't have a name, it has to be an element of an
97         // array. In that case, sequence number is the array index.
98         *sequenceNumber = (uint32_t)nodeIndex << 1;
99         if (dictionaries->annotationDictionary == parentDictionary)
100         {
101             *sequenceNumber |= 1;
102         }
103         return 0;
104     }
105 
106     // If we are here, the property has a name.
107     const uint8_t* dictionary =
108         bejGetRelatedDictionary(dictionaries, parentDictionary, node->name);
109     bool isAnnotation = dictionary == dictionaries->annotationDictionary;
110     // If this node's dictionary and its parent's dictionary is different,
111     // this node should start searching from the beginning of its
112     // dictionary. This should only happen for property annotations of form
113     // property@annotation_class.annotation_name.
114     if (dictionary != parentDictionary)
115     {
116         // Redundancy check.
117         if (!isAnnotation)
118         {
119             fprintf(stderr,
120                     "Dictionary for property %s should be the annotation "
121                     "dictionary. Might be a encoding failure. Maybe the "
122                     "JSON tree is not created correctly.",
123                     node->name);
124             return -1;
125         }
126         dictStartingOffset = bejDictGetFirstAnnotatedPropertyOffset();
127     }
128 
129     const struct BejDictionaryProperty* property;
130     int ret = bejDictGetPropertyByName(dictionary, dictStartingOffset,
131                                        node->name, &property, NULL);
132     if (ret != 0)
133     {
134         fprintf(stderr,
135                 "Failed to find dictionary entry for name %s. Search started "
136                 "at offset: %u. ret: %d\n",
137                 node->name, dictStartingOffset, ret);
138         return ret;
139     }
140 
141     if (nodeDictionary != NULL)
142     {
143         *nodeDictionary = dictionary;
144     }
145 
146     if (childEntryOffset != NULL)
147     {
148         *childEntryOffset = property->childPointerOffset;
149     }
150 
151     *sequenceNumber = (uint32_t)(property->sequenceNumber) << 1;
152     if (isAnnotation)
153     {
154         *sequenceNumber |= 1;
155     }
156     return 0;
157 }
158 
159 /**
160  * @brief Update metadata of leaf nodes.
161  *
162  * @param dictionaries - dictionaries needed for encoding.
163  * @param parentDictionary - dictionary used by this node's parent.
164  * @param childPtr - a pointer to the leaf node.
165  * @param childIndex - if this node is an array element, this is the array
166  * index.
167  * @param dictStartingOffset - starting dictionary child offset value of this
168  * node's parent.
169  * @return 0 if successful.
170  */
171 static int bejUpdateLeafNodeMetaData(const struct BejDictionaries* dictionaries,
172                                      const uint8_t* parentDictionary,
173                                      void* childPtr, uint16_t childIndex,
174                                      uint16_t dictStartingOffset)
175 {
176     // TODO: Implement this
177     (void)dictionaries;
178     (void)parentDictionary;
179     (void)childIndex;
180     (void)dictStartingOffset;
181 
182     struct RedfishPropertyLeaf* chNode = childPtr;
183     switch (chNode->nodeAttr.format.principalDataType)
184     {
185         default:
186             fprintf(stderr, "Child type %u not supported\n",
187                     chNode->nodeAttr.format.principalDataType);
188             return -1;
189     }
190     return 0;
191 }
192 
193 /**
194  * @brief Update metadata of a parent node.
195  *
196  * @param dictionaries - dictionaries needed for encoding.
197  * @param parentDictionary - dictionary used by this node's parent.
198  * @param dictStartingOffset - starting dictionary child offset value of this
199  * node's parent.
200  * @param node - a pointer to the parent node.
201  * @param nodeIndex - If this node is an array element, this is the array index.
202  * @return 0 if successful.
203  */
204 static int bejUpdateParentMetaData(const struct BejDictionaries* dictionaries,
205                                    const uint8_t* parentDictionary,
206                                    uint16_t dictStartingOffset,
207                                    struct RedfishPropertyParent* node,
208                                    uint16_t nodeIndex)
209 {
210     const uint8_t* nodeDictionary;
211     uint16_t childEntryOffset;
212     uint32_t sequenceNumber;
213 
214     // Get the dictionary related data from the node.
215     RETURN_IF_IERROR(bejFindSeqNumAndChildDictOffset(
216         dictionaries, parentDictionary, &node->nodeAttr, nodeIndex,
217         dictStartingOffset, &sequenceNumber, &nodeDictionary,
218         &childEntryOffset));
219 
220     node->metaData.sequenceNumber = sequenceNumber;
221     node->metaData.childrenDictPropOffset = childEntryOffset;
222     node->metaData.nextChild = node->firstChild;
223     node->metaData.nextChildIndex = 0;
224     node->metaData.dictionary = nodeDictionary;
225     node->metaData.vSize = 0;
226 
227     // S: Size needed for encoding sequence number.
228     node->metaData.sflSize =
229         bejNnintEncodingSizeOfUInt(node->metaData.sequenceNumber);
230     // F: Size of the format byte is 1.
231     node->metaData.sflSize += 1;
232     // V: Only for bejArray and bejSet types, value size should include the
233     // children count. We need to add the size needs to encode all the children
234     // later.
235     if (node->nodeAttr.format.principalDataType != bejPropertyAnnotation)
236     {
237         node->metaData.vSize = bejNnintEncodingSizeOfUInt(node->nChildren);
238     }
239     return 0;
240 }
241 
242 /**
243  * @brief Update metadata of child nodes.
244  *
245  * If a child node contains its own child nodes, it will be added to the stack
246  * and function will return.
247  *
248  * @param dictionaries - dictionaries needed for encoding.
249  * @param parent - parent node.
250  * @param stack - stack holding parent nodes.
251  * @return 0 if successful.
252  */
253 static int bejProcessChildNodes(const struct BejDictionaries* dictionaries,
254                                 struct RedfishPropertyParent* parent,
255                                 struct BejPointerStackCallback* stack)
256 {
257     // Get the next child of the parent.
258     void* childPtr = parent->metaData.nextChild;
259 
260     // Process all the children belongs to the parent.
261     while (childPtr != NULL)
262     {
263         // If we find a child with its own child nodes, add it to the stack and
264         // return.
265         if (bejTreeIsParentType(childPtr))
266         {
267             RETURN_IF_IERROR(bejUpdateParentMetaData(
268                 dictionaries, parent->metaData.dictionary,
269                 parent->metaData.childrenDictPropOffset, childPtr,
270                 parent->metaData.nextChildIndex));
271 
272             RETURN_IF_IERROR(stack->stackPush(childPtr, stack->stackContext));
273             bejParentGoToNextChild(parent, childPtr);
274             return 0;
275         }
276 
277         RETURN_IF_IERROR(
278             bejUpdateLeafNodeMetaData(dictionaries, parent->metaData.dictionary,
279                                       childPtr, parent->metaData.nextChildIndex,
280                                       parent->metaData.childrenDictPropOffset));
281         // Use the child value size to update the parent value size.
282         struct RedfishPropertyLeaf* leafChild = childPtr;
283         // V: Include the child size in parent's value size.
284         parent->metaData.vSize +=
285             (leafChild->metaData.sflSize + leafChild->metaData.vSize);
286 
287         // Get the next child belongs to the parent.
288         childPtr = bejParentGoToNextChild(parent, childPtr);
289     }
290     return 0;
291 }
292 
293 int bejUpdateNodeMetadata(const struct BejDictionaries* dictionaries,
294                           uint16_t majorSchemaStartingOffset,
295                           struct RedfishPropertyParent* root,
296                           struct BejPointerStackCallback* stack)
297 {
298     // Decide the starting property offset of the dictionary.
299     uint16_t dictOffset = bejDictGetPropertyHeadOffset();
300     if (majorSchemaStartingOffset != BEJ_DICTIONARY_START_AT_HEAD)
301     {
302         dictOffset = majorSchemaStartingOffset;
303     }
304 
305     // Initialize root node metadata.
306     RETURN_IF_IERROR(
307         bejUpdateParentMetaData(dictionaries, dictionaries->schemaDictionary,
308                                 dictOffset, root, /*childIndex=*/0));
309 
310     // Push the root to the stack. Because we are not done with the parent node
311     // yet. Need to figure out all bytes need to encode children of this parent,
312     // and save it in the parent metadata.
313     RETURN_IF_IERROR(stack->stackPush(root, stack->stackContext));
314 
315     while (!stack->stackEmpty(stack->stackContext))
316     {
317         // Get the parent at the top of the stack. Stack is only popped if the
318         // parent stack entry has no pending children; That is
319         // parent->metaData.nextChild == NULL.
320         struct RedfishPropertyParent* parent =
321             stack->stackPeek(stack->stackContext);
322 
323         // Calculate metadata of all the child nodes of the current parent node.
324         // If one of these child nodes has its own child nodes, that child node
325         // will be added to the stack and this function will return.
326         RETURN_IF_IERROR(bejProcessChildNodes(dictionaries, parent, stack));
327 
328         // If a new node hasn't been added to the stack, we know that this
329         // parent's child nodes have been processed. If not, do not pop the
330         // stack.
331         if (parent != stack->stackPeek(stack->stackContext))
332         {
333             continue;
334         }
335 
336         // If we are here;
337         // Then "parent" is the top element of the stack.
338         // All the children of "parent" has been processed.
339 
340         // Remove the "parent" from the stack.
341         parent = stack->stackPop(stack->stackContext);
342         // L: Add the length needed to store the number of bytes used for the
343         // parent's value.
344         parent->metaData.sflSize +=
345             bejNnintEncodingSizeOfUInt(parent->metaData.vSize);
346 
347         // Since we now know the total size needs to encode the node pointed by
348         // "parent" variable, we should add that to the value size of this
349         // node's parent. Since we already popped this node from the stack, top
350         // of the stack element is this nodes's parent. "parentsParent" can be
351         // NULL if the node pointed by "parent" variable is the root.
352         struct RedfishPropertyParent* parentsParent =
353             stack->stackPeek(stack->stackContext);
354         if (parentsParent != NULL)
355         {
356             // V: Include the total size to encode the current parent in its
357             // parent's value size.
358             parentsParent->metaData.vSize +=
359                 (parent->metaData.sflSize + parent->metaData.vSize);
360         }
361     }
362     return 0;
363 }
364