blob: 3c32f804af1ff2b3d4a48614ecc47d29ee84c1a3 [file] [log] [blame]
kasunath99bd6c92023-07-30 18:19:00 -07001#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/**
kasunath061fbc62023-08-01 18:09:08 -070011 * @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/**
kasunathf68be952023-08-01 17:29:58 -070027 * @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 */
32static 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 */
50static 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 */
92static 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
kasunath061fbc62023-08-01 18:09:08 -0700175static 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
198static 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
224static int bejUpdateBoolMetaData(const struct BejDictionaries* dictionaries,
225 const uint8_t* parentDictionary,
226 struct RedfishPropertyLeafBool* node,
227 uint16_t nodeIndex,
228 uint16_t dictStartingOffset)
229{
230 uint32_t sequenceNumber;
231 RETURN_IF_IERROR(bejFindSeqNumAndChildDictOffset(
232 dictionaries, parentDictionary, &node->leaf.nodeAttr, nodeIndex,
233 dictStartingOffset, &sequenceNumber, NULL, NULL));
234 node->leaf.metaData.sequenceNumber = sequenceNumber;
235
236 // Calculate the size for encoding this in a SFLV tuple.
237 // S: Size needed for encoding sequence number.
238 node->leaf.metaData.sflSize = bejNnintEncodingSizeOfUInt(sequenceNumber);
239 // F: Size of the format byte is 1.
240 node->leaf.metaData.sflSize += 1;
241 // L: Length needed for the value.
242 node->leaf.metaData.sflSize += BEJ_TUPLE_L_SIZE_FOR_BEJ_BOOL;
243 // V: Bytes used for the value; 0x00 or 0xFF.
244 node->leaf.metaData.vSize = 1;
245 return 0;
246}
247
kasunathf68be952023-08-01 17:29:58 -0700248/**
kasunath99bd6c92023-07-30 18:19:00 -0700249 * @brief Update metadata of leaf nodes.
250 *
251 * @param dictionaries - dictionaries needed for encoding.
252 * @param parentDictionary - dictionary used by this node's parent.
253 * @param childPtr - a pointer to the leaf node.
254 * @param childIndex - if this node is an array element, this is the array
255 * index.
256 * @param dictStartingOffset - starting dictionary child offset value of this
257 * node's parent.
258 * @return 0 if successful.
259 */
260static int bejUpdateLeafNodeMetaData(const struct BejDictionaries* dictionaries,
261 const uint8_t* parentDictionary,
262 void* childPtr, uint16_t childIndex,
263 uint16_t dictStartingOffset)
264{
kasunath99bd6c92023-07-30 18:19:00 -0700265 struct RedfishPropertyLeaf* chNode = childPtr;
kasunath061fbc62023-08-01 18:09:08 -0700266
kasunath99bd6c92023-07-30 18:19:00 -0700267 switch (chNode->nodeAttr.format.principalDataType)
268 {
kasunath061fbc62023-08-01 18:09:08 -0700269 case bejInteger:
270 RETURN_IF_IERROR(
271 bejUpdateIntMetaData(dictionaries, parentDictionary, childPtr,
272 childIndex, dictStartingOffset));
273 break;
274 case bejString:
275 RETURN_IF_IERROR(bejUpdateStringMetaData(
276 dictionaries, parentDictionary, childPtr, childIndex,
277 dictStartingOffset));
278 break;
279 case bejBoolean:
280 RETURN_IF_IERROR(
281 bejUpdateBoolMetaData(dictionaries, parentDictionary, childPtr,
282 childIndex, dictStartingOffset));
283 break;
kasunath99bd6c92023-07-30 18:19:00 -0700284 default:
285 fprintf(stderr, "Child type %u not supported\n",
286 chNode->nodeAttr.format.principalDataType);
287 return -1;
288 }
289 return 0;
290}
291
292/**
293 * @brief Update metadata of a parent node.
294 *
295 * @param dictionaries - dictionaries needed for encoding.
296 * @param parentDictionary - dictionary used by this node's parent.
297 * @param dictStartingOffset - starting dictionary child offset value of this
298 * node's parent.
299 * @param node - a pointer to the parent node.
300 * @param nodeIndex - If this node is an array element, this is the array index.
301 * @return 0 if successful.
302 */
303static int bejUpdateParentMetaData(const struct BejDictionaries* dictionaries,
304 const uint8_t* parentDictionary,
305 uint16_t dictStartingOffset,
306 struct RedfishPropertyParent* node,
307 uint16_t nodeIndex)
308{
kasunathf68be952023-08-01 17:29:58 -0700309 const uint8_t* nodeDictionary;
310 uint16_t childEntryOffset;
311 uint32_t sequenceNumber;
kasunath99bd6c92023-07-30 18:19:00 -0700312
kasunathf68be952023-08-01 17:29:58 -0700313 // Get the dictionary related data from the node.
314 RETURN_IF_IERROR(bejFindSeqNumAndChildDictOffset(
315 dictionaries, parentDictionary, &node->nodeAttr, nodeIndex,
316 dictStartingOffset, &sequenceNumber, &nodeDictionary,
317 &childEntryOffset));
318
319 node->metaData.sequenceNumber = sequenceNumber;
320 node->metaData.childrenDictPropOffset = childEntryOffset;
321 node->metaData.nextChild = node->firstChild;
322 node->metaData.nextChildIndex = 0;
323 node->metaData.dictionary = nodeDictionary;
324 node->metaData.vSize = 0;
325
326 // S: Size needed for encoding sequence number.
327 node->metaData.sflSize =
328 bejNnintEncodingSizeOfUInt(node->metaData.sequenceNumber);
329 // F: Size of the format byte is 1.
330 node->metaData.sflSize += 1;
331 // V: Only for bejArray and bejSet types, value size should include the
332 // children count. We need to add the size needs to encode all the children
333 // later.
334 if (node->nodeAttr.format.principalDataType != bejPropertyAnnotation)
335 {
336 node->metaData.vSize = bejNnintEncodingSizeOfUInt(node->nChildren);
337 }
338 return 0;
kasunath99bd6c92023-07-30 18:19:00 -0700339}
340
341/**
342 * @brief Update metadata of child nodes.
343 *
344 * If a child node contains its own child nodes, it will be added to the stack
345 * and function will return.
346 *
347 * @param dictionaries - dictionaries needed for encoding.
348 * @param parent - parent node.
349 * @param stack - stack holding parent nodes.
350 * @return 0 if successful.
351 */
352static int bejProcessChildNodes(const struct BejDictionaries* dictionaries,
353 struct RedfishPropertyParent* parent,
354 struct BejPointerStackCallback* stack)
355{
356 // Get the next child of the parent.
357 void* childPtr = parent->metaData.nextChild;
358
359 // Process all the children belongs to the parent.
360 while (childPtr != NULL)
361 {
362 // If we find a child with its own child nodes, add it to the stack and
363 // return.
364 if (bejTreeIsParentType(childPtr))
365 {
366 RETURN_IF_IERROR(bejUpdateParentMetaData(
367 dictionaries, parent->metaData.dictionary,
368 parent->metaData.childrenDictPropOffset, childPtr,
369 parent->metaData.nextChildIndex));
370
371 RETURN_IF_IERROR(stack->stackPush(childPtr, stack->stackContext));
372 bejParentGoToNextChild(parent, childPtr);
373 return 0;
374 }
375
376 RETURN_IF_IERROR(
377 bejUpdateLeafNodeMetaData(dictionaries, parent->metaData.dictionary,
378 childPtr, parent->metaData.nextChildIndex,
379 parent->metaData.childrenDictPropOffset));
380 // Use the child value size to update the parent value size.
381 struct RedfishPropertyLeaf* leafChild = childPtr;
382 // V: Include the child size in parent's value size.
383 parent->metaData.vSize +=
384 (leafChild->metaData.sflSize + leafChild->metaData.vSize);
385
386 // Get the next child belongs to the parent.
387 childPtr = bejParentGoToNextChild(parent, childPtr);
388 }
389 return 0;
390}
391
392int bejUpdateNodeMetadata(const struct BejDictionaries* dictionaries,
393 uint16_t majorSchemaStartingOffset,
394 struct RedfishPropertyParent* root,
395 struct BejPointerStackCallback* stack)
396{
397 // Decide the starting property offset of the dictionary.
398 uint16_t dictOffset = bejDictGetPropertyHeadOffset();
399 if (majorSchemaStartingOffset != BEJ_DICTIONARY_START_AT_HEAD)
400 {
401 dictOffset = majorSchemaStartingOffset;
402 }
403
404 // Initialize root node metadata.
405 RETURN_IF_IERROR(
406 bejUpdateParentMetaData(dictionaries, dictionaries->schemaDictionary,
407 dictOffset, root, /*childIndex=*/0));
408
409 // Push the root to the stack. Because we are not done with the parent node
410 // yet. Need to figure out all bytes need to encode children of this parent,
411 // and save it in the parent metadata.
412 RETURN_IF_IERROR(stack->stackPush(root, stack->stackContext));
413
414 while (!stack->stackEmpty(stack->stackContext))
415 {
416 // Get the parent at the top of the stack. Stack is only popped if the
417 // parent stack entry has no pending children; That is
418 // parent->metaData.nextChild == NULL.
419 struct RedfishPropertyParent* parent =
420 stack->stackPeek(stack->stackContext);
421
422 // Calculate metadata of all the child nodes of the current parent node.
423 // If one of these child nodes has its own child nodes, that child node
424 // will be added to the stack and this function will return.
425 RETURN_IF_IERROR(bejProcessChildNodes(dictionaries, parent, stack));
426
427 // If a new node hasn't been added to the stack, we know that this
428 // parent's child nodes have been processed. If not, do not pop the
429 // stack.
430 if (parent != stack->stackPeek(stack->stackContext))
431 {
432 continue;
433 }
434
435 // If we are here;
436 // Then "parent" is the top element of the stack.
437 // All the children of "parent" has been processed.
438
439 // Remove the "parent" from the stack.
440 parent = stack->stackPop(stack->stackContext);
441 // L: Add the length needed to store the number of bytes used for the
442 // parent's value.
443 parent->metaData.sflSize +=
444 bejNnintEncodingSizeOfUInt(parent->metaData.vSize);
445
446 // Since we now know the total size needs to encode the node pointed by
447 // "parent" variable, we should add that to the value size of this
448 // node's parent. Since we already popped this node from the stack, top
449 // of the stack element is this nodes's parent. "parentsParent" can be
450 // NULL if the node pointed by "parent" variable is the root.
451 struct RedfishPropertyParent* parentsParent =
452 stack->stackPeek(stack->stackContext);
453 if (parentsParent != NULL)
454 {
455 // V: Include the total size to encode the current parent in its
456 // parent's value size.
457 parentsParent->metaData.vSize +=
458 (parent->metaData.sflSize + parent->metaData.vSize);
459 }
460 }
461 return 0;
462}