blob: f1134f18853ad7d85daee65a47f2ddc376f5822e [file] [log] [blame]
kasunath99bd6c92023-07-30 18:19:00 -07001#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 */
12static 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 */
26static 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 */
36static 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/**
kasunath0a291932023-08-18 11:26:32 -070050 * @brief Encode an integer to bejInteger type.
51 */
52static 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 */
62int 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 */
79int 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
kasunath6df0c482023-08-18 11:48:22 -070093int 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
108int 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
139int 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
155int 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
kasunath0a291932023-08-18 11:26:32 -0700166/**
167 * @brief Encode a BejNull type.
168 */
169int 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/**
kasunath99bd6c92023-07-30 18:19:00 -0700182 * @brief Encode the provided node.
183 */
184static 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;
kasunath0a291932023-08-18 11:26:32 -0700192 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;
kasunath6df0c482023-08-18 11:48:22 -0700204 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;
kasunath99bd6c92023-07-30 18:19:00 -0700216 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 */
227static 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 */
239static 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 */
272static 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
311int 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}