xref: /openbmc/libbej/src/bej_encoder_core.c (revision 6df0c486)
1 #include "bej_encoder_core.h"
2 
3 #include "bej_common.h"
4 #include "bej_encoder_metadata.h"
5 
6 #include <stdio.h>
7 #include <string.h>
8 
9 /**
10  * @brief Encode a unsigned value with nnint format.
11  */
bejEncodeNnint(uint64_t value,struct BejEncoderOutputHandler * output)12 static int bejEncodeNnint(uint64_t value,
13                           struct BejEncoderOutputHandler* output)
14 {
15     // The length of the value bytes in nnint.
16     uint8_t nnintLengthByte = bejNnintLengthFieldOfUInt(value);
17     RETURN_IF_IERROR(output->recvOutput(&nnintLengthByte, sizeof(uint8_t),
18                                         output->handlerContext));
19     // Write the nnint value bytes.
20     return output->recvOutput(&value, nnintLengthByte, output->handlerContext);
21 }
22 
23 /**
24  * @brief Encode a BejTupleF type.
25  */
bejEncodeFormat(const struct BejTupleF * format,struct BejEncoderOutputHandler * output)26 static int bejEncodeFormat(const struct BejTupleF* format,
27                            struct BejEncoderOutputHandler* output)
28 {
29     return output->recvOutput(format, sizeof(struct BejTupleF),
30                               output->handlerContext);
31 }
32 
33 /**
34  * @brief Encode a BejSet or BejArray type.
35  */
bejEncodeBejSetOrArray(struct RedfishPropertyParent * node,struct BejEncoderOutputHandler * output)36 static int bejEncodeBejSetOrArray(struct RedfishPropertyParent* node,
37                                   struct BejEncoderOutputHandler* output)
38 {
39     // Encode Sequence number.
40     RETURN_IF_IERROR(bejEncodeNnint(node->metaData.sequenceNumber, output));
41     // Add the format.
42     RETURN_IF_IERROR(bejEncodeFormat(&node->nodeAttr.format, output));
43     // Encode the value length.
44     RETURN_IF_IERROR(bejEncodeNnint(node->metaData.vSize, output));
45     // Encode the child count
46     return bejEncodeNnint(node->nChildren, output);
47 }
48 
49 /**
50  * @brief Encode an integer to bejInteger type.
51  */
bejEncodeInteger(int64_t val,struct BejEncoderOutputHandler * output)52 static uint8_t bejEncodeInteger(int64_t val,
53                                 struct BejEncoderOutputHandler* output)
54 {
55     uint8_t copyLength = bejIntLengthOfValue(val);
56     return output->recvOutput(&val, copyLength, output->handlerContext);
57 }
58 
59 /**
60  * @brief Encode a BejInteger type.
61  */
bejEncodeBejInteger(struct RedfishPropertyLeafInt * node,struct BejEncoderOutputHandler * output)62 int bejEncodeBejInteger(struct RedfishPropertyLeafInt* node,
63                         struct BejEncoderOutputHandler* output)
64 {
65     // Encode Sequence number.
66     RETURN_IF_IERROR(
67         bejEncodeNnint(node->leaf.metaData.sequenceNumber, output));
68     // Add the format.
69     RETURN_IF_IERROR(bejEncodeFormat(&node->leaf.nodeAttr.format, output));
70     // Encode the value length.
71     RETURN_IF_IERROR(bejEncodeNnint(node->leaf.metaData.vSize, output));
72     // Encode the value.
73     return bejEncodeInteger(node->value, output);
74 }
75 
76 /**
77  * @brief Encode a BejEnum type.
78  */
bejEncodeBejEnum(struct RedfishPropertyLeafEnum * node,struct BejEncoderOutputHandler * output)79 int bejEncodeBejEnum(struct RedfishPropertyLeafEnum* node,
80                      struct BejEncoderOutputHandler* output)
81 {
82     // S: Encode Sequence number.
83     RETURN_IF_IERROR(
84         bejEncodeNnint(node->leaf.metaData.sequenceNumber, output));
85     // F: Add the format.
86     RETURN_IF_IERROR(bejEncodeFormat(&node->leaf.nodeAttr.format, output));
87     // L: Encode the value length.
88     RETURN_IF_IERROR(bejEncodeNnint(node->leaf.metaData.vSize, output));
89     // V: Encode the value.
90     return bejEncodeNnint(node->enumValueSeq, output);
91 }
92 
bejEncodeBejString(struct RedfishPropertyLeafString * node,struct BejEncoderOutputHandler * output)93 int bejEncodeBejString(struct RedfishPropertyLeafString* node,
94                        struct BejEncoderOutputHandler* output)
95 {
96     // S: Encode Sequence number.
97     RETURN_IF_IERROR(
98         bejEncodeNnint(node->leaf.metaData.sequenceNumber, output));
99     // F: Add the format.
100     RETURN_IF_IERROR(bejEncodeFormat(&node->leaf.nodeAttr.format, output));
101     // L: Encode the value length.
102     RETURN_IF_IERROR(bejEncodeNnint(node->leaf.metaData.vSize, output));
103     // V: Encode the value.
104     return output->recvOutput((void*)node->value, node->leaf.metaData.vSize,
105                               output->handlerContext);
106 }
107 
bejEncodeBejReal(struct RedfishPropertyLeafReal * node,struct BejEncoderOutputHandler * output)108 int bejEncodeBejReal(struct RedfishPropertyLeafReal* node,
109                      struct BejEncoderOutputHandler* output)
110 {
111     // S: Encode Sequence number.
112     RETURN_IF_IERROR(
113         bejEncodeNnint(node->leaf.metaData.sequenceNumber, output));
114     // F: Add the format.
115     RETURN_IF_IERROR(bejEncodeFormat(&node->leaf.nodeAttr.format, output));
116     // L: Encode the value length.
117     RETURN_IF_IERROR(bejEncodeNnint(node->leaf.metaData.vSize, output));
118     // V: Encode the value.
119     // Length of the "whole" value as nnint.
120     RETURN_IF_IERROR(
121         bejEncodeNnint(bejIntLengthOfValue(node->bejReal.whole), output));
122     // Add the "whole" value.
123     RETURN_IF_IERROR(bejEncodeInteger(node->bejReal.whole, output));
124     // Leading zero count as a nnint.
125     RETURN_IF_IERROR(bejEncodeNnint(node->bejReal.zeroCount, output));
126     // Fraction as a nnint.
127     RETURN_IF_IERROR(bejEncodeNnint(node->bejReal.fract, output));
128     // Exp length as a nnint.
129     RETURN_IF_IERROR(bejEncodeNnint(node->bejReal.expLen, output));
130     if (node->bejReal.expLen > 0)
131     {
132         // Exp length as a nnint.
133         RETURN_IF_IERROR(bejEncodeNnint(node->bejReal.expLen, output));
134         RETURN_IF_IERROR(bejEncodeInteger(node->bejReal.exp, output));
135     }
136     return 0;
137 }
138 
bejEncodeBejBool(struct RedfishPropertyLeafBool * node,struct BejEncoderOutputHandler * output)139 int bejEncodeBejBool(struct RedfishPropertyLeafBool* node,
140                      struct BejEncoderOutputHandler* output)
141 {
142     // S: Encode Sequence number.
143     RETURN_IF_IERROR(
144         bejEncodeNnint(node->leaf.metaData.sequenceNumber, output));
145     // F: Add the format.
146     RETURN_IF_IERROR(bejEncodeFormat(&node->leaf.nodeAttr.format, output));
147     // L: Encode the value length.
148     RETURN_IF_IERROR(bejEncodeNnint(node->leaf.metaData.vSize, output));
149     // V: Encode the value.
150     uint8_t value = node->value ? 0xFF : 0x00;
151     return output->recvOutput(&value, /*data_size=*/sizeof(uint8_t),
152                               output->handlerContext);
153 }
154 
bejEncodeBejProAnno(struct RedfishPropertyParent * node,struct BejEncoderOutputHandler * output)155 int bejEncodeBejProAnno(struct RedfishPropertyParent* node,
156                         struct BejEncoderOutputHandler* output)
157 {
158     // Encode Sequence number.
159     RETURN_IF_IERROR(bejEncodeNnint(node->metaData.sequenceNumber, output));
160     // Add the format.
161     RETURN_IF_IERROR(bejEncodeFormat(&node->nodeAttr.format, output));
162     // Encode the value length.
163     return bejEncodeNnint(node->metaData.vSize, output);
164 }
165 
166 /**
167  * @brief Encode a BejNull type.
168  */
bejEncodeBejNull(struct RedfishPropertyLeafNull * node,struct BejEncoderOutputHandler * output)169 int bejEncodeBejNull(struct RedfishPropertyLeafNull* node,
170                      struct BejEncoderOutputHandler* output)
171 {
172     // S: Encode Sequence number.
173     RETURN_IF_IERROR(
174         bejEncodeNnint(node->leaf.metaData.sequenceNumber, output));
175     // F: Add the format.
176     RETURN_IF_IERROR(bejEncodeFormat(&node->leaf.nodeAttr.format, output));
177     // L: Encode the value length.
178     return bejEncodeNnint(node->leaf.metaData.vSize, output);
179 }
180 
181 /**
182  * @brief Encode the provided node.
183  */
bejEncodeNode(void * node,struct BejEncoderOutputHandler * output)184 static int bejEncodeNode(void* node, struct BejEncoderOutputHandler* output)
185 {
186     struct RedfishPropertyNode* nodeInfo = node;
187     switch (nodeInfo->format.principalDataType)
188     {
189         case bejSet:
190             RETURN_IF_IERROR(bejEncodeBejSetOrArray(node, output));
191             break;
192         case bejArray:
193             RETURN_IF_IERROR(bejEncodeBejSetOrArray(node, output));
194             break;
195         case bejNull:
196             RETURN_IF_IERROR(bejEncodeBejNull(node, output));
197             break;
198         case bejInteger:
199             RETURN_IF_IERROR(bejEncodeBejInteger(node, output));
200             break;
201         case bejEnum:
202             RETURN_IF_IERROR(bejEncodeBejEnum(node, output));
203             break;
204         case bejString:
205             RETURN_IF_IERROR(bejEncodeBejString(node, output));
206             break;
207         case bejReal:
208             RETURN_IF_IERROR(bejEncodeBejReal(node, output));
209             break;
210         case bejBoolean:
211             RETURN_IF_IERROR(bejEncodeBejBool(node, output));
212             break;
213         case bejPropertyAnnotation:
214             RETURN_IF_IERROR(bejEncodeBejProAnno(node, output));
215             break;
216         default:
217             fprintf(stderr, "Unsupported node type: %d\n",
218                     nodeInfo->format.principalDataType);
219             return -1;
220     }
221     return 0;
222 }
223 
224 /**
225  * @brief A helper function to add a parent to the stack.
226  */
bejPushParentToStack(struct RedfishPropertyParent * parent,struct BejPointerStackCallback * stack)227 static int bejPushParentToStack(struct RedfishPropertyParent* parent,
228                                 struct BejPointerStackCallback* stack)
229 {
230     // Before pushing the parent node, initialize its nextChild as the first
231     // child.
232     parent->metaData.nextChild = parent->firstChild;
233     return stack->stackPush(parent, stack->stackContext);
234 }
235 
236 /**
237  * @brief Process all the child nodes of a parent.
238  */
bejProcessChildNodes(struct RedfishPropertyParent * parent,struct BejPointerStackCallback * stack,struct BejEncoderOutputHandler * output)239 static int bejProcessChildNodes(struct RedfishPropertyParent* parent,
240                                 struct BejPointerStackCallback* stack,
241                                 struct BejEncoderOutputHandler* output)
242 {
243     // Get the next child of the parent.
244     void* childPtr = parent->metaData.nextChild;
245 
246     while (childPtr != NULL)
247     {
248         // First encode the current child node.
249         RETURN_IF_IERROR(bejEncodeNode(childPtr, output));
250         // If this child node has its own children, add it to the stack and
251         // return. Because we need to encode the children of the newly added
252         // node before continuing to encode the child nodes of the current
253         // parent.
254         if (bejTreeIsParentType(childPtr))
255         {
256             RETURN_IF_IERROR(bejPushParentToStack(childPtr, stack));
257             // Update the next child of the current parent we need to
258             // process.
259             bejParentGoToNextChild(parent, childPtr);
260             return 0;
261         }
262         childPtr = bejParentGoToNextChild(parent, childPtr);
263     }
264     return 0;
265 }
266 
267 /**
268  * @brief Encode the provided JSON tree.
269  *
270  * The node metadata should be initialized before using this function.
271  */
bejEncodeTree(struct RedfishPropertyParent * root,struct BejPointerStackCallback * stack,struct BejEncoderOutputHandler * output)272 static int bejEncodeTree(struct RedfishPropertyParent* root,
273                          struct BejPointerStackCallback* stack,
274                          struct BejEncoderOutputHandler* output)
275 {
276     // We need to encode a parent node before its child nodes. So encoding the
277     // root first.
278     RETURN_IF_IERROR(bejEncodeNode(root, output));
279     // Once the root is encoded, push it to the stack used to traverse the child
280     // nodes. We need to keep a parent in this stack until all the child nodes
281     // of this parent has been encoded. Only then we remove the parent node from
282     // the stack.
283     RETURN_IF_IERROR(bejPushParentToStack(root, stack));
284 
285     while (!stack->stackEmpty(stack->stackContext))
286     {
287         struct RedfishPropertyParent* parent =
288             stack->stackPeek(stack->stackContext);
289 
290         // Encode all the child nodes of the current parent node. If one of
291         // these child nodes has its own child nodes, that child node will be
292         // encoded and added to the stack and this function will return. The
293         // rest of the children of the current parent will be encoded later
294         // (after processing all the nodes under the child node added to the
295         // stack).
296         RETURN_IF_IERROR(bejProcessChildNodes(parent, stack, output));
297 
298         // If a new node hasn't been added to the stack by
299         // bejProcessChildNodes(), we know that this parent's child nodes have
300         // been processed. If a new node has been added, then next we need to
301         // process the children of the newly added node.
302         if (parent != stack->stackPeek(stack->stackContext))
303         {
304             continue;
305         }
306         stack->stackPop(stack->stackContext);
307     }
308     return 0;
309 }
310 
bejEncode(const struct BejDictionaries * dictionaries,uint16_t majorSchemaStartingOffset,enum BejSchemaClass schemaClass,struct RedfishPropertyParent * root,struct BejEncoderOutputHandler * output,struct BejPointerStackCallback * stack)311 int bejEncode(const struct BejDictionaries* dictionaries,
312               uint16_t majorSchemaStartingOffset,
313               enum BejSchemaClass schemaClass,
314               struct RedfishPropertyParent* root,
315               struct BejEncoderOutputHandler* output,
316               struct BejPointerStackCallback* stack)
317 {
318     NULL_CHECK(dictionaries, "dictionaries");
319     NULL_CHECK(dictionaries->schemaDictionary, "schemaDictionary");
320     NULL_CHECK(dictionaries->annotationDictionary, "annotationDictionary");
321 
322     NULL_CHECK(root, "root");
323 
324     NULL_CHECK(output, "output");
325     NULL_CHECK(stack, "stack");
326 
327     // Assert root node.
328     if (root->nodeAttr.format.principalDataType != bejSet)
329     {
330         fprintf(stderr, "Invalid root node\n");
331         return -1;
332     }
333 
334     // First we need to encode a parent node before its child nodes. But before
335     // encoding the parent node, the encoder has to figure out the total size
336     // need to encode the parent's child nodes. Therefore first the encoder need
337     // to visit the child nodes and calculate the size need to encode them
338     // before producing the encoded bytes for the parent node.
339     //
340     // So first the encoder will visit child nodes and calculate the size need
341     // to encode each child node. Then store this information in metadata
342     // properties in each node struct.
343     // Next the encoder will again visit each node starting from the parent
344     // node, and produce the encoded bytes.
345 
346     // First calculate metadata for encoding each node.
347     RETURN_IF_IERROR(bejUpdateNodeMetadata(
348         dictionaries, majorSchemaStartingOffset, root, stack));
349 
350     // Derive the header of the encoded output.
351     // BEJ version
352     uint32_t version = BEJ_VERSION;
353     RETURN_IF_IERROR(
354         output->recvOutput(&version, sizeof(uint32_t), output->handlerContext));
355     uint16_t reserved = 0;
356     RETURN_IF_IERROR(output->recvOutput(&reserved, sizeof(uint16_t),
357                                         output->handlerContext));
358     RETURN_IF_IERROR(output->recvOutput(&schemaClass, sizeof(uint8_t),
359                                         output->handlerContext));
360 
361     // Produce the encoded bytes for the nodes using the previously calculated
362     // metadata.
363     return bejEncodeTree(root, stack, output);
364 }
365