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 /** 94 * @brief Encode a BejNull type. 95 */ 96 int bejEncodeBejNull(struct RedfishPropertyLeafNull* node, 97 struct BejEncoderOutputHandler* output) 98 { 99 // S: Encode Sequence number. 100 RETURN_IF_IERROR( 101 bejEncodeNnint(node->leaf.metaData.sequenceNumber, output)); 102 // F: Add the format. 103 RETURN_IF_IERROR(bejEncodeFormat(&node->leaf.nodeAttr.format, output)); 104 // L: Encode the value length. 105 return bejEncodeNnint(node->leaf.metaData.vSize, output); 106 } 107 108 /** 109 * @brief Encode the provided node. 110 */ 111 static int bejEncodeNode(void* node, struct BejEncoderOutputHandler* output) 112 { 113 struct RedfishPropertyNode* nodeInfo = node; 114 switch (nodeInfo->format.principalDataType) 115 { 116 case bejSet: 117 RETURN_IF_IERROR(bejEncodeBejSetOrArray(node, output)); 118 break; 119 case bejArray: 120 RETURN_IF_IERROR(bejEncodeBejSetOrArray(node, output)); 121 break; 122 case bejNull: 123 RETURN_IF_IERROR(bejEncodeBejNull(node, output)); 124 break; 125 case bejInteger: 126 RETURN_IF_IERROR(bejEncodeBejInteger(node, output)); 127 break; 128 case bejEnum: 129 RETURN_IF_IERROR(bejEncodeBejEnum(node, output)); 130 break; 131 default: 132 fprintf(stderr, "Unsupported node type: %d\n", 133 nodeInfo->format.principalDataType); 134 return -1; 135 } 136 return 0; 137 } 138 139 /** 140 * @brief A helper function to add a parent to the stack. 141 */ 142 static int bejPushParentToStack(struct RedfishPropertyParent* parent, 143 struct BejPointerStackCallback* stack) 144 { 145 // Before pushing the parent node, initialize its nextChild as the first 146 // child. 147 parent->metaData.nextChild = parent->firstChild; 148 return stack->stackPush(parent, stack->stackContext); 149 } 150 151 /** 152 * @brief Process all the child nodes of a parent. 153 */ 154 static int bejProcessChildNodes(struct RedfishPropertyParent* parent, 155 struct BejPointerStackCallback* stack, 156 struct BejEncoderOutputHandler* output) 157 { 158 // Get the next child of the parent. 159 void* childPtr = parent->metaData.nextChild; 160 161 while (childPtr != NULL) 162 { 163 // First encode the current child node. 164 RETURN_IF_IERROR(bejEncodeNode(childPtr, output)); 165 // If this child node has its own children, add it to the stack and 166 // return. Because we need to encode the children of the newly added 167 // node before continuing to encode the child nodes of the current 168 // parent. 169 if (bejTreeIsParentType(childPtr)) 170 { 171 RETURN_IF_IERROR(bejPushParentToStack(childPtr, stack)); 172 // Update the next child of the current parent we need to 173 // process. 174 bejParentGoToNextChild(parent, childPtr); 175 return 0; 176 } 177 childPtr = bejParentGoToNextChild(parent, childPtr); 178 } 179 return 0; 180 } 181 182 /** 183 * @brief Encode the provided JSON tree. 184 * 185 * The node metadata should be initialized before using this function. 186 */ 187 static int bejEncodeTree(struct RedfishPropertyParent* root, 188 struct BejPointerStackCallback* stack, 189 struct BejEncoderOutputHandler* output) 190 { 191 // We need to encode a parent node before its child nodes. So encoding the 192 // root first. 193 RETURN_IF_IERROR(bejEncodeNode(root, output)); 194 // Once the root is encoded, push it to the stack used to traverse the child 195 // nodes. We need to keep a parent in this stack until all the child nodes 196 // of this parent has been encoded. Only then we remove the parent node from 197 // the stack. 198 RETURN_IF_IERROR(bejPushParentToStack(root, stack)); 199 200 while (!stack->stackEmpty(stack->stackContext)) 201 { 202 struct RedfishPropertyParent* parent = 203 stack->stackPeek(stack->stackContext); 204 205 // Encode all the child nodes of the current parent node. If one of 206 // these child nodes has its own child nodes, that child node will be 207 // encoded and added to the stack and this function will return. The 208 // rest of the children of the current parent will be encoded later 209 // (after processing all the nodes under the child node added to the 210 // stack). 211 RETURN_IF_IERROR(bejProcessChildNodes(parent, stack, output)); 212 213 // If a new node hasn't been added to the stack by 214 // bejProcessChildNodes(), we know that this parent's child nodes have 215 // been processed. If a new node has been added, then next we need to 216 // process the children of the newly added node. 217 if (parent != stack->stackPeek(stack->stackContext)) 218 { 219 continue; 220 } 221 stack->stackPop(stack->stackContext); 222 } 223 return 0; 224 } 225 226 int bejEncode(const struct BejDictionaries* dictionaries, 227 uint16_t majorSchemaStartingOffset, 228 enum BejSchemaClass schemaClass, 229 struct RedfishPropertyParent* root, 230 struct BejEncoderOutputHandler* output, 231 struct BejPointerStackCallback* stack) 232 { 233 NULL_CHECK(dictionaries, "dictionaries"); 234 NULL_CHECK(dictionaries->schemaDictionary, "schemaDictionary"); 235 NULL_CHECK(dictionaries->annotationDictionary, "annotationDictionary"); 236 237 NULL_CHECK(root, "root"); 238 239 NULL_CHECK(output, "output"); 240 NULL_CHECK(stack, "stack"); 241 242 // Assert root node. 243 if (root->nodeAttr.format.principalDataType != bejSet) 244 { 245 fprintf(stderr, "Invalid root node\n"); 246 return -1; 247 } 248 249 // First we need to encode a parent node before its child nodes. But before 250 // encoding the parent node, the encoder has to figure out the total size 251 // need to encode the parent's child nodes. Therefore first the encoder need 252 // to visit the child nodes and calculate the size need to encode them 253 // before producing the encoded bytes for the parent node. 254 // 255 // So first the encoder will visit child nodes and calculate the size need 256 // to encode each child node. Then store this information in metadata 257 // properties in each node struct. 258 // Next the encoder will again visit each node starting from the parent 259 // node, and produce the encoded bytes. 260 261 // First calculate metadata for encoding each node. 262 RETURN_IF_IERROR(bejUpdateNodeMetadata( 263 dictionaries, majorSchemaStartingOffset, root, stack)); 264 265 // Derive the header of the encoded output. 266 // BEJ version 267 uint32_t version = BEJ_VERSION; 268 RETURN_IF_IERROR( 269 output->recvOutput(&version, sizeof(uint32_t), output->handlerContext)); 270 uint16_t reserved = 0; 271 RETURN_IF_IERROR(output->recvOutput(&reserved, sizeof(uint16_t), 272 output->handlerContext)); 273 RETURN_IF_IERROR(output->recvOutput(&schemaClass, sizeof(uint8_t), 274 output->handlerContext)); 275 276 // Produce the encoded bytes for the nodes using the previously calculated 277 // metadata. 278 return bejEncodeTree(root, stack, output); 279 } 280