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 /** 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 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 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 /** 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; 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 */ 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 } 365