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 the provided node. 51 */ 52 static int bejEncodeNode(void* node, struct BejEncoderOutputHandler* output) 53 { 54 struct RedfishPropertyNode* nodeInfo = node; 55 switch (nodeInfo->format.principalDataType) 56 { 57 case bejSet: 58 RETURN_IF_IERROR(bejEncodeBejSetOrArray(node, output)); 59 break; 60 default: 61 fprintf(stderr, "Unsupported node type: %d\n", 62 nodeInfo->format.principalDataType); 63 return -1; 64 } 65 return 0; 66 } 67 68 /** 69 * @brief A helper function to add a parent to the stack. 70 */ 71 static int bejPushParentToStack(struct RedfishPropertyParent* parent, 72 struct BejPointerStackCallback* stack) 73 { 74 // Before pushing the parent node, initialize its nextChild as the first 75 // child. 76 parent->metaData.nextChild = parent->firstChild; 77 return stack->stackPush(parent, stack->stackContext); 78 } 79 80 /** 81 * @brief Process all the child nodes of a parent. 82 */ 83 static int bejProcessChildNodes(struct RedfishPropertyParent* parent, 84 struct BejPointerStackCallback* stack, 85 struct BejEncoderOutputHandler* output) 86 { 87 // Get the next child of the parent. 88 void* childPtr = parent->metaData.nextChild; 89 90 while (childPtr != NULL) 91 { 92 // First encode the current child node. 93 RETURN_IF_IERROR(bejEncodeNode(childPtr, output)); 94 // If this child node has its own children, add it to the stack and 95 // return. Because we need to encode the children of the newly added 96 // node before continuing to encode the child nodes of the current 97 // parent. 98 if (bejTreeIsParentType(childPtr)) 99 { 100 RETURN_IF_IERROR(bejPushParentToStack(childPtr, stack)); 101 // Update the next child of the current parent we need to 102 // process. 103 bejParentGoToNextChild(parent, childPtr); 104 return 0; 105 } 106 childPtr = bejParentGoToNextChild(parent, childPtr); 107 } 108 return 0; 109 } 110 111 /** 112 * @brief Encode the provided JSON tree. 113 * 114 * The node metadata should be initialized before using this function. 115 */ 116 static int bejEncodeTree(struct RedfishPropertyParent* root, 117 struct BejPointerStackCallback* stack, 118 struct BejEncoderOutputHandler* output) 119 { 120 // We need to encode a parent node before its child nodes. So encoding the 121 // root first. 122 RETURN_IF_IERROR(bejEncodeNode(root, output)); 123 // Once the root is encoded, push it to the stack used to traverse the child 124 // nodes. We need to keep a parent in this stack until all the child nodes 125 // of this parent has been encoded. Only then we remove the parent node from 126 // the stack. 127 RETURN_IF_IERROR(bejPushParentToStack(root, stack)); 128 129 while (!stack->stackEmpty(stack->stackContext)) 130 { 131 struct RedfishPropertyParent* parent = 132 stack->stackPeek(stack->stackContext); 133 134 // Encode all the child nodes of the current parent node. If one of 135 // these child nodes has its own child nodes, that child node will be 136 // encoded and added to the stack and this function will return. The 137 // rest of the children of the current parent will be encoded later 138 // (after processing all the nodes under the child node added to the 139 // stack). 140 RETURN_IF_IERROR(bejProcessChildNodes(parent, stack, output)); 141 142 // If a new node hasn't been added to the stack by 143 // bejProcessChildNodes(), we know that this parent's child nodes have 144 // been processed. If a new node has been added, then next we need to 145 // process the children of the newly added node. 146 if (parent != stack->stackPeek(stack->stackContext)) 147 { 148 continue; 149 } 150 stack->stackPop(stack->stackContext); 151 } 152 return 0; 153 } 154 155 int bejEncode(const struct BejDictionaries* dictionaries, 156 uint16_t majorSchemaStartingOffset, 157 enum BejSchemaClass schemaClass, 158 struct RedfishPropertyParent* root, 159 struct BejEncoderOutputHandler* output, 160 struct BejPointerStackCallback* stack) 161 { 162 NULL_CHECK(dictionaries, "dictionaries"); 163 NULL_CHECK(dictionaries->schemaDictionary, "schemaDictionary"); 164 NULL_CHECK(dictionaries->annotationDictionary, "annotationDictionary"); 165 166 NULL_CHECK(root, "root"); 167 168 NULL_CHECK(output, "output"); 169 NULL_CHECK(stack, "stack"); 170 171 // Assert root node. 172 if (root->nodeAttr.format.principalDataType != bejSet) 173 { 174 fprintf(stderr, "Invalid root node\n"); 175 return -1; 176 } 177 178 // First we need to encode a parent node before its child nodes. But before 179 // encoding the parent node, the encoder has to figure out the total size 180 // need to encode the parent's child nodes. Therefore first the encoder need 181 // to visit the child nodes and calculate the size need to encode them 182 // before producing the encoded bytes for the parent node. 183 // 184 // So first the encoder will visit child nodes and calculate the size need 185 // to encode each child node. Then store this information in metadata 186 // properties in each node struct. 187 // Next the encoder will again visit each node starting from the parent 188 // node, and produce the encoded bytes. 189 190 // First calculate metadata for encoding each node. 191 RETURN_IF_IERROR(bejUpdateNodeMetadata( 192 dictionaries, majorSchemaStartingOffset, root, stack)); 193 194 // Derive the header of the encoded output. 195 // BEJ version 196 uint32_t version = BEJ_VERSION; 197 RETURN_IF_IERROR( 198 output->recvOutput(&version, sizeof(uint32_t), output->handlerContext)); 199 uint16_t reserved = 0; 200 RETURN_IF_IERROR(output->recvOutput(&reserved, sizeof(uint16_t), 201 output->handlerContext)); 202 RETURN_IF_IERROR(output->recvOutput(&schemaClass, sizeof(uint8_t), 203 output->handlerContext)); 204 205 // Produce the encoded bytes for the nodes using the previously calculated 206 // metadata. 207 return bejEncodeTree(root, stack, output); 208 } 209