#include "bej_encoder_metadata.h" #include "bej_common.h" #include "bej_dictionary.h" #include #include #include /** * @brief bejTupleL size of an integer. * * Maximum bytes possible for an integer is 8. Therefore to encode the length of * an integer using a nnint, we only need two bytes. [byte1: nnint length, * byte2: integer length [0-8]] */ #define BEJ_TUPLE_L_SIZE_FOR_BEJ_INTEGER 2 /** * @brief bejTupleL size of a bool. * * 1byte for the nnint length and 1 byte for the value. */ #define BEJ_TUPLE_L_SIZE_FOR_BEJ_BOOL 2 /** * @brief Check the name is an annotation type name. * * @param[in] name - property name. * @return true for annotation name, false otherwise. */ static bool bejIsAnnotation(const char* name) { if (name == NULL) { return false; } return name[0] == '@'; } /** * @brief Get the dictionary for the provided node. * * @param[in] dictionaries - available dictionaries for encoding. * @param[in] parentDictionary - dictionary used for the parent of this node. * @param[in] nodeName - name of the interested node. Can be NULL if the node * doesn't have a name. * @return a pointer to the dictionary to be used. */ static const uint8_t* bejGetRelatedDictionary(const struct BejDictionaries* dictionaries, const uint8_t* parentDictionary, const char* nodeName) { // If the node name is NULL, we have to use parent dictionary. if (nodeName == NULL) { return parentDictionary; } // If the parent is using annotation dictionary, that means the parent is an // annotation. Therefore the child (this node) should be an annotation too // (Could this be false?). Therefore we should use the annotation dictionary // for this node as well. if (parentDictionary == dictionaries->annotationDictionary) { return dictionaries->annotationDictionary; } return bejIsAnnotation(nodeName) ? dictionaries->annotationDictionary : dictionaries->schemaDictionary; } /** * @brief Get dictionary data for the given node. * * @param[in] dictionaries - available dictionaries. * @param[in] parentDictionary - the dictionary used by the provided node's * parent. * @param[in] node - node that caller is interested in. * @param[in] nodeIndex - index of this node within its parent. * @param[in] dictStartingOffset - starting dictionary child offset value of * this node's parent. * @param[out] sequenceNumber - sequence number of the node. bit0 specifies the * dictionary schema type: [major|annotation]. * @param[out] nodeDictionary - if not NULL, return a pointer to the dictionary * used for the node. * @param[out] childEntryOffset - if not NULL, return the dictionary starting * offset used for this nodes children. If this node is not supposed to have * children, caller should ignore this value. * @return 0 if successful. */ static int bejFindSeqNumAndChildDictOffset( const struct BejDictionaries* dictionaries, const uint8_t* parentDictionary, struct RedfishPropertyNode* node, uint16_t nodeIndex, uint16_t dictStartingOffset, uint32_t* sequenceNumber, const uint8_t** nodeDictionary, uint16_t* childEntryOffset) { // If the node doesn't have a name, we can't use a dictionary. So we can use // its parent's info. if (node->name == NULL || node->name[0] == '\0') { if (nodeDictionary != NULL) { *nodeDictionary = parentDictionary; } if (childEntryOffset != NULL) { *childEntryOffset = dictStartingOffset; } // If the property doesn't have a name, it has to be an element of an // array. In that case, sequence number is the array index. *sequenceNumber = (uint32_t)nodeIndex << 1; if (dictionaries->annotationDictionary == parentDictionary) { *sequenceNumber |= 1; } return 0; } // If we are here, the property has a name. const uint8_t* dictionary = bejGetRelatedDictionary(dictionaries, parentDictionary, node->name); bool isAnnotation = dictionary == dictionaries->annotationDictionary; // If this node's dictionary and its parent's dictionary is different, // this node should start searching from the beginning of its // dictionary. This should only happen for property annotations of form // property@annotation_class.annotation_name. if (dictionary != parentDictionary) { // Redundancy check. if (!isAnnotation) { fprintf(stderr, "Dictionary for property %s should be the annotation " "dictionary. Might be a encoding failure. Maybe the " "JSON tree is not created correctly.", node->name); return -1; } dictStartingOffset = bejDictGetFirstAnnotatedPropertyOffset(); } const struct BejDictionaryProperty* property; int ret = bejDictGetPropertyByName(dictionary, dictStartingOffset, node->name, &property, NULL); if (ret != 0) { fprintf(stderr, "Failed to find dictionary entry for name %s. Search started " "at offset: %u. ret: %d\n", node->name, dictStartingOffset, ret); return ret; } if (nodeDictionary != NULL) { *nodeDictionary = dictionary; } if (childEntryOffset != NULL) { *childEntryOffset = property->childPointerOffset; } *sequenceNumber = (uint32_t)(property->sequenceNumber) << 1; if (isAnnotation) { *sequenceNumber |= 1; } return 0; } static int bejUpdateIntMetaData(const struct BejDictionaries* dictionaries, const uint8_t* parentDictionary, struct RedfishPropertyLeafInt* node, uint16_t nodeIndex, uint16_t dictStartingOffset) { uint32_t sequenceNumber; RETURN_IF_IERROR(bejFindSeqNumAndChildDictOffset( dictionaries, parentDictionary, &node->leaf.nodeAttr, nodeIndex, dictStartingOffset, &sequenceNumber, NULL, NULL)); node->leaf.metaData.sequenceNumber = sequenceNumber; // Calculate the size for encoding this in a SFLV tuple. // S: Size needed for encoding sequence number. node->leaf.metaData.sflSize = bejNnintEncodingSizeOfUInt(sequenceNumber); // F: Size of the format byte is 1. node->leaf.metaData.sflSize += 1; // L: Length needed for the value. node->leaf.metaData.sflSize += BEJ_TUPLE_L_SIZE_FOR_BEJ_INTEGER; // V: Bytes used for the value. node->leaf.metaData.vSize = bejIntLengthOfValue(node->value); return 0; } static int bejUpdateStringMetaData(const struct BejDictionaries* dictionaries, const uint8_t* parentDictionary, struct RedfishPropertyLeafString* node, uint16_t nodeIndex, uint16_t dictStartingOffset) { uint32_t sequenceNumber; RETURN_IF_IERROR(bejFindSeqNumAndChildDictOffset( dictionaries, parentDictionary, &(node->leaf.nodeAttr), nodeIndex, dictStartingOffset, &sequenceNumber, NULL, NULL)); node->leaf.metaData.sequenceNumber = sequenceNumber; // Calculate the size for encoding this in a SFLV tuple. // S: Size needed for encoding sequence number. node->leaf.metaData.sflSize = bejNnintEncodingSizeOfUInt(sequenceNumber); // F: Size of the format byte is 1. node->leaf.metaData.sflSize += 1; // L: Length needed for the string including the NULL character. Length is // in nnint format. size_t strLenWithNull = strlen(node->value) + 1; node->leaf.metaData.sflSize += bejNnintEncodingSizeOfUInt(strLenWithNull); // V: Bytes used for the value. node->leaf.metaData.vSize = strLenWithNull; return 0; } static int bejUpdateEnumMetaData(const struct BejDictionaries* dictionaries, const uint8_t* parentDictionary, struct RedfishPropertyLeafEnum* node, uint16_t nodeIndex, uint16_t dictStartingOffset) { const uint8_t* nodeDictionary; uint16_t childEntryOffset; uint32_t sequenceNumber; // If the enum property doesn't have a name, this will simply return the // nodeIndex encoded as the sequence number. If not, this will return the // sequence number in the dictionary and the starting dictionary index for // the enum values. RETURN_IF_IERROR(bejFindSeqNumAndChildDictOffset( dictionaries, parentDictionary, &(node->leaf.nodeAttr), nodeIndex, dictStartingOffset, &sequenceNumber, &nodeDictionary, &childEntryOffset)); // Update the sequence number of the property. node->leaf.metaData.sequenceNumber = sequenceNumber; // Get the sequence number for the Enum value. if (node->leaf.nodeAttr.name != NULL && node->leaf.nodeAttr.name[0] != '\0') { dictStartingOffset = childEntryOffset; } const struct BejDictionaryProperty* enumValueProperty; int ret = bejDictGetPropertyByName(nodeDictionary, dictStartingOffset, node->value, &enumValueProperty, NULL); if (ret != 0) { fprintf( stderr, "Failed to find dictionary entry for enum value %s. Search started " "at offset: %u. ret: %d\n", node->value, dictStartingOffset, ret); return ret; } node->enumValueSeq = enumValueProperty->sequenceNumber; // Calculate the size for encoding this in a SFLV tuple. // S: Size needed for encoding sequence number. node->leaf.metaData.sflSize = bejNnintEncodingSizeOfUInt(sequenceNumber); // F: Size of the format byte is 1. node->leaf.metaData.sflSize += 1; // V: Bytes used for the value. node->leaf.metaData.vSize = bejNnintEncodingSizeOfUInt(enumValueProperty->sequenceNumber); // L: Length needed for the value nnint. node->leaf.metaData.sflSize += bejNnintEncodingSizeOfUInt(node->leaf.metaData.vSize); return 0; } static int bejUpdateBoolMetaData(const struct BejDictionaries* dictionaries, const uint8_t* parentDictionary, struct RedfishPropertyLeafBool* node, uint16_t nodeIndex, uint16_t dictStartingOffset) { uint32_t sequenceNumber; RETURN_IF_IERROR(bejFindSeqNumAndChildDictOffset( dictionaries, parentDictionary, &node->leaf.nodeAttr, nodeIndex, dictStartingOffset, &sequenceNumber, NULL, NULL)); node->leaf.metaData.sequenceNumber = sequenceNumber; // Calculate the size for encoding this in a SFLV tuple. // S: Size needed for encoding sequence number. node->leaf.metaData.sflSize = bejNnintEncodingSizeOfUInt(sequenceNumber); // F: Size of the format byte is 1. node->leaf.metaData.sflSize += 1; // L: Length needed for the value. node->leaf.metaData.sflSize += BEJ_TUPLE_L_SIZE_FOR_BEJ_BOOL; // V: Bytes used for the value; 0x00 or 0xFF. node->leaf.metaData.vSize = 1; return 0; } /** * @brief Update metadata of leaf nodes. * * @param dictionaries - dictionaries needed for encoding. * @param parentDictionary - dictionary used by this node's parent. * @param childPtr - a pointer to the leaf node. * @param childIndex - if this node is an array element, this is the array * index. * @param dictStartingOffset - starting dictionary child offset value of this * node's parent. * @return 0 if successful. */ static int bejUpdateLeafNodeMetaData(const struct BejDictionaries* dictionaries, const uint8_t* parentDictionary, void* childPtr, uint16_t childIndex, uint16_t dictStartingOffset) { struct RedfishPropertyLeaf* chNode = childPtr; switch (chNode->nodeAttr.format.principalDataType) { case bejInteger: RETURN_IF_IERROR( bejUpdateIntMetaData(dictionaries, parentDictionary, childPtr, childIndex, dictStartingOffset)); break; case bejString: RETURN_IF_IERROR(bejUpdateStringMetaData( dictionaries, parentDictionary, childPtr, childIndex, dictStartingOffset)); break; case bejEnum: RETURN_IF_IERROR( bejUpdateEnumMetaData(dictionaries, parentDictionary, childPtr, childIndex, dictStartingOffset)); break; case bejBoolean: RETURN_IF_IERROR( bejUpdateBoolMetaData(dictionaries, parentDictionary, childPtr, childIndex, dictStartingOffset)); break; default: fprintf(stderr, "Child type %u not supported\n", chNode->nodeAttr.format.principalDataType); return -1; } return 0; } /** * @brief Update metadata of a parent node. * * @param dictionaries - dictionaries needed for encoding. * @param parentDictionary - dictionary used by this node's parent. * @param dictStartingOffset - starting dictionary child offset value of this * node's parent. * @param node - a pointer to the parent node. * @param nodeIndex - If this node is an array element, this is the array index. * @return 0 if successful. */ static int bejUpdateParentMetaData(const struct BejDictionaries* dictionaries, const uint8_t* parentDictionary, uint16_t dictStartingOffset, struct RedfishPropertyParent* node, uint16_t nodeIndex) { const uint8_t* nodeDictionary; uint16_t childEntryOffset; uint32_t sequenceNumber; // Get the dictionary related data from the node. RETURN_IF_IERROR(bejFindSeqNumAndChildDictOffset( dictionaries, parentDictionary, &node->nodeAttr, nodeIndex, dictStartingOffset, &sequenceNumber, &nodeDictionary, &childEntryOffset)); node->metaData.sequenceNumber = sequenceNumber; node->metaData.childrenDictPropOffset = childEntryOffset; node->metaData.nextChild = node->firstChild; node->metaData.nextChildIndex = 0; node->metaData.dictionary = nodeDictionary; node->metaData.vSize = 0; // S: Size needed for encoding sequence number. node->metaData.sflSize = bejNnintEncodingSizeOfUInt(node->metaData.sequenceNumber); // F: Size of the format byte is 1. node->metaData.sflSize += 1; // V: Only for bejArray and bejSet types, value size should include the // children count. We need to add the size needs to encode all the children // later. if (node->nodeAttr.format.principalDataType != bejPropertyAnnotation) { node->metaData.vSize = bejNnintEncodingSizeOfUInt(node->nChildren); } return 0; } /** * @brief Update metadata of child nodes. * * If a child node contains its own child nodes, it will be added to the stack * and function will return. * * @param dictionaries - dictionaries needed for encoding. * @param parent - parent node. * @param stack - stack holding parent nodes. * @return 0 if successful. */ static int bejProcessChildNodes(const struct BejDictionaries* dictionaries, struct RedfishPropertyParent* parent, struct BejPointerStackCallback* stack) { // Get the next child of the parent. void* childPtr = parent->metaData.nextChild; // Process all the children belongs to the parent. while (childPtr != NULL) { // If we find a child with its own child nodes, add it to the stack and // return. if (bejTreeIsParentType(childPtr)) { RETURN_IF_IERROR(bejUpdateParentMetaData( dictionaries, parent->metaData.dictionary, parent->metaData.childrenDictPropOffset, childPtr, parent->metaData.nextChildIndex)); RETURN_IF_IERROR(stack->stackPush(childPtr, stack->stackContext)); bejParentGoToNextChild(parent, childPtr); return 0; } RETURN_IF_IERROR( bejUpdateLeafNodeMetaData(dictionaries, parent->metaData.dictionary, childPtr, parent->metaData.nextChildIndex, parent->metaData.childrenDictPropOffset)); // Use the child value size to update the parent value size. struct RedfishPropertyLeaf* leafChild = childPtr; // V: Include the child size in parent's value size. parent->metaData.vSize += (leafChild->metaData.sflSize + leafChild->metaData.vSize); // Get the next child belongs to the parent. childPtr = bejParentGoToNextChild(parent, childPtr); } return 0; } int bejUpdateNodeMetadata(const struct BejDictionaries* dictionaries, uint16_t majorSchemaStartingOffset, struct RedfishPropertyParent* root, struct BejPointerStackCallback* stack) { // Decide the starting property offset of the dictionary. uint16_t dictOffset = bejDictGetPropertyHeadOffset(); if (majorSchemaStartingOffset != BEJ_DICTIONARY_START_AT_HEAD) { dictOffset = majorSchemaStartingOffset; } // Initialize root node metadata. RETURN_IF_IERROR( bejUpdateParentMetaData(dictionaries, dictionaries->schemaDictionary, dictOffset, root, /*childIndex=*/0)); // Push the root to the stack. Because we are not done with the parent node // yet. Need to figure out all bytes need to encode children of this parent, // and save it in the parent metadata. RETURN_IF_IERROR(stack->stackPush(root, stack->stackContext)); while (!stack->stackEmpty(stack->stackContext)) { // Get the parent at the top of the stack. Stack is only popped if the // parent stack entry has no pending children; That is // parent->metaData.nextChild == NULL. struct RedfishPropertyParent* parent = stack->stackPeek(stack->stackContext); // Calculate metadata of all the child nodes of the current parent node. // If one of these child nodes has its own child nodes, that child node // will be added to the stack and this function will return. RETURN_IF_IERROR(bejProcessChildNodes(dictionaries, parent, stack)); // If a new node hasn't been added to the stack, we know that this // parent's child nodes have been processed. If not, do not pop the // stack. if (parent != stack->stackPeek(stack->stackContext)) { continue; } // If we are here; // Then "parent" is the top element of the stack. // All the children of "parent" has been processed. // Remove the "parent" from the stack. parent = stack->stackPop(stack->stackContext); // L: Add the length needed to store the number of bytes used for the // parent's value. parent->metaData.sflSize += bejNnintEncodingSizeOfUInt(parent->metaData.vSize); // Since we now know the total size needs to encode the node pointed by // "parent" variable, we should add that to the value size of this // node's parent. Since we already popped this node from the stack, top // of the stack element is this nodes's parent. "parentsParent" can be // NULL if the node pointed by "parent" variable is the root. struct RedfishPropertyParent* parentsParent = stack->stackPeek(stack->stackContext); if (parentsParent != NULL) { // V: Include the total size to encode the current parent in its // parent's value size. parentsParent->metaData.vSize += (parent->metaData.sflSize + parent->metaData.vSize); } } return 0; }