kasunath | 99bd6c9 | 2023-07-30 18:19:00 -0700 | [diff] [blame] | 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 | */ |
| 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 | */ |
| 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 | */ |
| 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 | /** |
kasunath | 0a29193 | 2023-08-18 11:26:32 -0700 | [diff] [blame] | 50 | * @brief Encode an integer to bejInteger type. |
| 51 | */ |
| 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 | */ |
| 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 | */ |
| 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 | |
kasunath | 6df0c48 | 2023-08-18 11:48:22 -0700 | [diff] [blame^] | 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 | |
| 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 | |
| 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 | |
| 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 | |
kasunath | 0a29193 | 2023-08-18 11:26:32 -0700 | [diff] [blame] | 166 | /** |
| 167 | * @brief Encode a BejNull type. |
| 168 | */ |
| 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 | /** |
kasunath | 99bd6c9 | 2023-07-30 18:19:00 -0700 | [diff] [blame] | 182 | * @brief Encode the provided node. |
| 183 | */ |
| 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; |
kasunath | 0a29193 | 2023-08-18 11:26:32 -0700 | [diff] [blame] | 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; |
kasunath | 6df0c48 | 2023-08-18 11:48:22 -0700 | [diff] [blame^] | 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; |
kasunath | 99bd6c9 | 2023-07-30 18:19:00 -0700 | [diff] [blame] | 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 | */ |
| 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 | */ |
| 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 | */ |
| 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 | |
| 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 | } |