blob: 3e273a0caa606cdbce413263d6872273aff24a1f [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
kasunathb250dd52023-08-01 18:13:55 -0700224static 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
kasunath061fbc62023-08-01 18:09:08 -0700277static 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
kasunathf68be952023-08-01 17:29:58 -0700301/**
kasunath99bd6c92023-07-30 18:19:00 -0700302 * @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 */
313static int bejUpdateLeafNodeMetaData(const struct BejDictionaries* dictionaries,
314 const uint8_t* parentDictionary,
315 void* childPtr, uint16_t childIndex,
316 uint16_t dictStartingOffset)
317{
kasunath99bd6c92023-07-30 18:19:00 -0700318 struct RedfishPropertyLeaf* chNode = childPtr;
kasunath061fbc62023-08-01 18:09:08 -0700319
kasunath99bd6c92023-07-30 18:19:00 -0700320 switch (chNode->nodeAttr.format.principalDataType)
321 {
kasunath061fbc62023-08-01 18:09:08 -0700322 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;
kasunathb250dd52023-08-01 18:13:55 -0700332 case bejEnum:
333 RETURN_IF_IERROR(
334 bejUpdateEnumMetaData(dictionaries, parentDictionary, childPtr,
335 childIndex, dictStartingOffset));
336 break;
kasunath061fbc62023-08-01 18:09:08 -0700337 case bejBoolean:
338 RETURN_IF_IERROR(
339 bejUpdateBoolMetaData(dictionaries, parentDictionary, childPtr,
340 childIndex, dictStartingOffset));
341 break;
kasunath99bd6c92023-07-30 18:19:00 -0700342 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 */
361static int bejUpdateParentMetaData(const struct BejDictionaries* dictionaries,
362 const uint8_t* parentDictionary,
363 uint16_t dictStartingOffset,
364 struct RedfishPropertyParent* node,
365 uint16_t nodeIndex)
366{
kasunathf68be952023-08-01 17:29:58 -0700367 const uint8_t* nodeDictionary;
368 uint16_t childEntryOffset;
369 uint32_t sequenceNumber;
kasunath99bd6c92023-07-30 18:19:00 -0700370
kasunathf68be952023-08-01 17:29:58 -0700371 // 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;
kasunath99bd6c92023-07-30 18:19:00 -0700397}
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 */
410static 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
450int 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}