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