1 #include "bej_encoder_metadata.h" 2 3 #include "bej_common.h" 4 #include "bej_dictionary.h" 5 6 #include <stdint.h> 7 #include <stdio.h> 8 #include <string.h> 9 10 /** 11 * @brief Check the name is an annotation type name. 12 * 13 * @param[in] name - property name. 14 * @return true for annotation name, false otherwise. 15 */ 16 static bool bejIsAnnotation(const char* name) 17 { 18 if (name == NULL) 19 { 20 return false; 21 } 22 return name[0] == '@'; 23 } 24 25 /** 26 * @brief Get the dictionary for the provided node. 27 * 28 * @param[in] dictionaries - available dictionaries for encoding. 29 * @param[in] parentDictionary - dictionary used for the parent of this node. 30 * @param[in] nodeName - name of the interested node. Can be NULL if the node 31 * doesn't have a name. 32 * @return a pointer to the dictionary to be used. 33 */ 34 static const uint8_t* 35 bejGetRelatedDictionary(const struct BejDictionaries* dictionaries, 36 const uint8_t* parentDictionary, 37 const char* nodeName) 38 { 39 // If the node name is NULL, we have to use parent dictionary. 40 if (nodeName == NULL) 41 { 42 return parentDictionary; 43 } 44 45 // If the parent is using annotation dictionary, that means the parent is an 46 // annotation. Therefore the child (this node) should be an annotation too 47 // (Could this be false?). Therefore we should use the annotation dictionary 48 // for this node as well. 49 if (parentDictionary == dictionaries->annotationDictionary) 50 { 51 return dictionaries->annotationDictionary; 52 } 53 return bejIsAnnotation(nodeName) ? dictionaries->annotationDictionary 54 : dictionaries->schemaDictionary; 55 } 56 57 /** 58 * @brief Get dictionary data for the given node. 59 * 60 * @param[in] dictionaries - available dictionaries. 61 * @param[in] parentDictionary - the dictionary used by the provided node's 62 * parent. 63 * @param[in] node - node that caller is interested in. 64 * @param[in] nodeIndex - index of this node within its parent. 65 * @param[in] dictStartingOffset - starting dictionary child offset value of 66 * this node's parent. 67 * @param[out] sequenceNumber - sequence number of the node. bit0 specifies the 68 * dictionary schema type: [major|annotation]. 69 * @param[out] nodeDictionary - if not NULL, return a pointer to the dictionary 70 * used for the node. 71 * @param[out] childEntryOffset - if not NULL, return the dictionary starting 72 * offset used for this nodes children. If this node is not supposed to have 73 * children, caller should ignore this value. 74 * @return 0 if successful. 75 */ 76 static int bejFindSeqNumAndChildDictOffset( 77 const struct BejDictionaries* dictionaries, const uint8_t* parentDictionary, 78 struct RedfishPropertyNode* node, uint16_t nodeIndex, 79 uint16_t dictStartingOffset, uint32_t* sequenceNumber, 80 const uint8_t** nodeDictionary, uint16_t* childEntryOffset) 81 { 82 // If the node doesn't have a name, we can't use a dictionary. So we can use 83 // its parent's info. 84 if (node->name == NULL || node->name[0] == '\0') 85 { 86 if (nodeDictionary != NULL) 87 { 88 *nodeDictionary = parentDictionary; 89 } 90 91 if (childEntryOffset != NULL) 92 { 93 *childEntryOffset = dictStartingOffset; 94 } 95 96 // If the property doesn't have a name, it has to be an element of an 97 // array. In that case, sequence number is the array index. 98 *sequenceNumber = (uint32_t)nodeIndex << 1; 99 if (dictionaries->annotationDictionary == parentDictionary) 100 { 101 *sequenceNumber |= 1; 102 } 103 return 0; 104 } 105 106 // If we are here, the property has a name. 107 const uint8_t* dictionary = 108 bejGetRelatedDictionary(dictionaries, parentDictionary, node->name); 109 bool isAnnotation = dictionary == dictionaries->annotationDictionary; 110 // If this node's dictionary and its parent's dictionary is different, 111 // this node should start searching from the beginning of its 112 // dictionary. This should only happen for property annotations of form 113 // property@annotation_class.annotation_name. 114 if (dictionary != parentDictionary) 115 { 116 // Redundancy check. 117 if (!isAnnotation) 118 { 119 fprintf(stderr, 120 "Dictionary for property %s should be the annotation " 121 "dictionary. Might be a encoding failure. Maybe the " 122 "JSON tree is not created correctly.", 123 node->name); 124 return -1; 125 } 126 dictStartingOffset = bejDictGetFirstAnnotatedPropertyOffset(); 127 } 128 129 const struct BejDictionaryProperty* property; 130 int ret = bejDictGetPropertyByName(dictionary, dictStartingOffset, 131 node->name, &property, NULL); 132 if (ret != 0) 133 { 134 fprintf(stderr, 135 "Failed to find dictionary entry for name %s. Search started " 136 "at offset: %u. ret: %d\n", 137 node->name, dictStartingOffset, ret); 138 return ret; 139 } 140 141 if (nodeDictionary != NULL) 142 { 143 *nodeDictionary = dictionary; 144 } 145 146 if (childEntryOffset != NULL) 147 { 148 *childEntryOffset = property->childPointerOffset; 149 } 150 151 *sequenceNumber = (uint32_t)(property->sequenceNumber) << 1; 152 if (isAnnotation) 153 { 154 *sequenceNumber |= 1; 155 } 156 return 0; 157 } 158 159 /** 160 * @brief Update metadata of leaf nodes. 161 * 162 * @param dictionaries - dictionaries needed for encoding. 163 * @param parentDictionary - dictionary used by this node's parent. 164 * @param childPtr - a pointer to the leaf node. 165 * @param childIndex - if this node is an array element, this is the array 166 * index. 167 * @param dictStartingOffset - starting dictionary child offset value of this 168 * node's parent. 169 * @return 0 if successful. 170 */ 171 static int bejUpdateLeafNodeMetaData(const struct BejDictionaries* dictionaries, 172 const uint8_t* parentDictionary, 173 void* childPtr, uint16_t childIndex, 174 uint16_t dictStartingOffset) 175 { 176 // TODO: Implement this 177 (void)dictionaries; 178 (void)parentDictionary; 179 (void)childIndex; 180 (void)dictStartingOffset; 181 182 struct RedfishPropertyLeaf* chNode = childPtr; 183 switch (chNode->nodeAttr.format.principalDataType) 184 { 185 default: 186 fprintf(stderr, "Child type %u not supported\n", 187 chNode->nodeAttr.format.principalDataType); 188 return -1; 189 } 190 return 0; 191 } 192 193 /** 194 * @brief Update metadata of a parent node. 195 * 196 * @param dictionaries - dictionaries needed for encoding. 197 * @param parentDictionary - dictionary used by this node's parent. 198 * @param dictStartingOffset - starting dictionary child offset value of this 199 * node's parent. 200 * @param node - a pointer to the parent node. 201 * @param nodeIndex - If this node is an array element, this is the array index. 202 * @return 0 if successful. 203 */ 204 static int bejUpdateParentMetaData(const struct BejDictionaries* dictionaries, 205 const uint8_t* parentDictionary, 206 uint16_t dictStartingOffset, 207 struct RedfishPropertyParent* node, 208 uint16_t nodeIndex) 209 { 210 const uint8_t* nodeDictionary; 211 uint16_t childEntryOffset; 212 uint32_t sequenceNumber; 213 214 // Get the dictionary related data from the node. 215 RETURN_IF_IERROR(bejFindSeqNumAndChildDictOffset( 216 dictionaries, parentDictionary, &node->nodeAttr, nodeIndex, 217 dictStartingOffset, &sequenceNumber, &nodeDictionary, 218 &childEntryOffset)); 219 220 node->metaData.sequenceNumber = sequenceNumber; 221 node->metaData.childrenDictPropOffset = childEntryOffset; 222 node->metaData.nextChild = node->firstChild; 223 node->metaData.nextChildIndex = 0; 224 node->metaData.dictionary = nodeDictionary; 225 node->metaData.vSize = 0; 226 227 // S: Size needed for encoding sequence number. 228 node->metaData.sflSize = 229 bejNnintEncodingSizeOfUInt(node->metaData.sequenceNumber); 230 // F: Size of the format byte is 1. 231 node->metaData.sflSize += 1; 232 // V: Only for bejArray and bejSet types, value size should include the 233 // children count. We need to add the size needs to encode all the children 234 // later. 235 if (node->nodeAttr.format.principalDataType != bejPropertyAnnotation) 236 { 237 node->metaData.vSize = bejNnintEncodingSizeOfUInt(node->nChildren); 238 } 239 return 0; 240 } 241 242 /** 243 * @brief Update metadata of child nodes. 244 * 245 * If a child node contains its own child nodes, it will be added to the stack 246 * and function will return. 247 * 248 * @param dictionaries - dictionaries needed for encoding. 249 * @param parent - parent node. 250 * @param stack - stack holding parent nodes. 251 * @return 0 if successful. 252 */ 253 static int bejProcessChildNodes(const struct BejDictionaries* dictionaries, 254 struct RedfishPropertyParent* parent, 255 struct BejPointerStackCallback* stack) 256 { 257 // Get the next child of the parent. 258 void* childPtr = parent->metaData.nextChild; 259 260 // Process all the children belongs to the parent. 261 while (childPtr != NULL) 262 { 263 // If we find a child with its own child nodes, add it to the stack and 264 // return. 265 if (bejTreeIsParentType(childPtr)) 266 { 267 RETURN_IF_IERROR(bejUpdateParentMetaData( 268 dictionaries, parent->metaData.dictionary, 269 parent->metaData.childrenDictPropOffset, childPtr, 270 parent->metaData.nextChildIndex)); 271 272 RETURN_IF_IERROR(stack->stackPush(childPtr, stack->stackContext)); 273 bejParentGoToNextChild(parent, childPtr); 274 return 0; 275 } 276 277 RETURN_IF_IERROR( 278 bejUpdateLeafNodeMetaData(dictionaries, parent->metaData.dictionary, 279 childPtr, parent->metaData.nextChildIndex, 280 parent->metaData.childrenDictPropOffset)); 281 // Use the child value size to update the parent value size. 282 struct RedfishPropertyLeaf* leafChild = childPtr; 283 // V: Include the child size in parent's value size. 284 parent->metaData.vSize += 285 (leafChild->metaData.sflSize + leafChild->metaData.vSize); 286 287 // Get the next child belongs to the parent. 288 childPtr = bejParentGoToNextChild(parent, childPtr); 289 } 290 return 0; 291 } 292 293 int bejUpdateNodeMetadata(const struct BejDictionaries* dictionaries, 294 uint16_t majorSchemaStartingOffset, 295 struct RedfishPropertyParent* root, 296 struct BejPointerStackCallback* stack) 297 { 298 // Decide the starting property offset of the dictionary. 299 uint16_t dictOffset = bejDictGetPropertyHeadOffset(); 300 if (majorSchemaStartingOffset != BEJ_DICTIONARY_START_AT_HEAD) 301 { 302 dictOffset = majorSchemaStartingOffset; 303 } 304 305 // Initialize root node metadata. 306 RETURN_IF_IERROR( 307 bejUpdateParentMetaData(dictionaries, dictionaries->schemaDictionary, 308 dictOffset, root, /*childIndex=*/0)); 309 310 // Push the root to the stack. Because we are not done with the parent node 311 // yet. Need to figure out all bytes need to encode children of this parent, 312 // and save it in the parent metadata. 313 RETURN_IF_IERROR(stack->stackPush(root, stack->stackContext)); 314 315 while (!stack->stackEmpty(stack->stackContext)) 316 { 317 // Get the parent at the top of the stack. Stack is only popped if the 318 // parent stack entry has no pending children; That is 319 // parent->metaData.nextChild == NULL. 320 struct RedfishPropertyParent* parent = 321 stack->stackPeek(stack->stackContext); 322 323 // Calculate metadata of all the child nodes of the current parent node. 324 // If one of these child nodes has its own child nodes, that child node 325 // will be added to the stack and this function will return. 326 RETURN_IF_IERROR(bejProcessChildNodes(dictionaries, parent, stack)); 327 328 // If a new node hasn't been added to the stack, we know that this 329 // parent's child nodes have been processed. If not, do not pop the 330 // stack. 331 if (parent != stack->stackPeek(stack->stackContext)) 332 { 333 continue; 334 } 335 336 // If we are here; 337 // Then "parent" is the top element of the stack. 338 // All the children of "parent" has been processed. 339 340 // Remove the "parent" from the stack. 341 parent = stack->stackPop(stack->stackContext); 342 // L: Add the length needed to store the number of bytes used for the 343 // parent's value. 344 parent->metaData.sflSize += 345 bejNnintEncodingSizeOfUInt(parent->metaData.vSize); 346 347 // Since we now know the total size needs to encode the node pointed by 348 // "parent" variable, we should add that to the value size of this 349 // node's parent. Since we already popped this node from the stack, top 350 // of the stack element is this nodes's parent. "parentsParent" can be 351 // NULL if the node pointed by "parent" variable is the root. 352 struct RedfishPropertyParent* parentsParent = 353 stack->stackPeek(stack->stackContext); 354 if (parentsParent != NULL) 355 { 356 // V: Include the total size to encode the current parent in its 357 // parent's value size. 358 parentsParent->metaData.vSize += 359 (parent->metaData.sflSize + parent->metaData.vSize); 360 } 361 } 362 return 0; 363 } 364