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