blob: e2890323a575f049c15fd964573ae10480d98fdd [file] [log] [blame]
#include "bej_encoder_core.h"
#include "bej_common.h"
#include "bej_encoder_metadata.h"
#include <stdio.h>
#include <string.h>
/**
* @brief Encode a unsigned value with nnint format.
*/
static int bejEncodeNnint(uint64_t value,
struct BejEncoderOutputHandler* output)
{
// The length of the value bytes in nnint.
uint8_t nnintLengthByte = bejNnintLengthFieldOfUInt(value);
RETURN_IF_IERROR(output->recvOutput(&nnintLengthByte, sizeof(uint8_t),
output->handlerContext));
// Write the nnint value bytes.
return output->recvOutput(&value, nnintLengthByte, output->handlerContext);
}
/**
* @brief Encode a BejTupleF type.
*/
static int bejEncodeFormat(const struct BejTupleF* format,
struct BejEncoderOutputHandler* output)
{
return output->recvOutput(format, sizeof(struct BejTupleF),
output->handlerContext);
}
/**
* @brief Encode a BejSet or BejArray type.
*/
static int bejEncodeBejSetOrArray(struct RedfishPropertyParent* node,
struct BejEncoderOutputHandler* output)
{
// Encode Sequence number.
RETURN_IF_IERROR(bejEncodeNnint(node->metaData.sequenceNumber, output));
// Add the format.
RETURN_IF_IERROR(bejEncodeFormat(&node->nodeAttr.format, output));
// Encode the value length.
RETURN_IF_IERROR(bejEncodeNnint(node->metaData.vSize, output));
// Encode the child count
return bejEncodeNnint(node->nChildren, output);
}
/**
* @brief Encode the provided node.
*/
static int bejEncodeNode(void* node, struct BejEncoderOutputHandler* output)
{
struct RedfishPropertyNode* nodeInfo = node;
switch (nodeInfo->format.principalDataType)
{
case bejSet:
RETURN_IF_IERROR(bejEncodeBejSetOrArray(node, output));
break;
default:
fprintf(stderr, "Unsupported node type: %d\n",
nodeInfo->format.principalDataType);
return -1;
}
return 0;
}
/**
* @brief A helper function to add a parent to the stack.
*/
static int bejPushParentToStack(struct RedfishPropertyParent* parent,
struct BejPointerStackCallback* stack)
{
// Before pushing the parent node, initialize its nextChild as the first
// child.
parent->metaData.nextChild = parent->firstChild;
return stack->stackPush(parent, stack->stackContext);
}
/**
* @brief Process all the child nodes of a parent.
*/
static int bejProcessChildNodes(struct RedfishPropertyParent* parent,
struct BejPointerStackCallback* stack,
struct BejEncoderOutputHandler* output)
{
// Get the next child of the parent.
void* childPtr = parent->metaData.nextChild;
while (childPtr != NULL)
{
// First encode the current child node.
RETURN_IF_IERROR(bejEncodeNode(childPtr, output));
// If this child node has its own children, add it to the stack and
// return. Because we need to encode the children of the newly added
// node before continuing to encode the child nodes of the current
// parent.
if (bejTreeIsParentType(childPtr))
{
RETURN_IF_IERROR(bejPushParentToStack(childPtr, stack));
// Update the next child of the current parent we need to
// process.
bejParentGoToNextChild(parent, childPtr);
return 0;
}
childPtr = bejParentGoToNextChild(parent, childPtr);
}
return 0;
}
/**
* @brief Encode the provided JSON tree.
*
* The node metadata should be initialized before using this function.
*/
static int bejEncodeTree(struct RedfishPropertyParent* root,
struct BejPointerStackCallback* stack,
struct BejEncoderOutputHandler* output)
{
// We need to encode a parent node before its child nodes. So encoding the
// root first.
RETURN_IF_IERROR(bejEncodeNode(root, output));
// Once the root is encoded, push it to the stack used to traverse the child
// nodes. We need to keep a parent in this stack until all the child nodes
// of this parent has been encoded. Only then we remove the parent node from
// the stack.
RETURN_IF_IERROR(bejPushParentToStack(root, stack));
while (!stack->stackEmpty(stack->stackContext))
{
struct RedfishPropertyParent* parent =
stack->stackPeek(stack->stackContext);
// Encode 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
// encoded and added to the stack and this function will return. The
// rest of the children of the current parent will be encoded later
// (after processing all the nodes under the child node added to the
// stack).
RETURN_IF_IERROR(bejProcessChildNodes(parent, stack, output));
// If a new node hasn't been added to the stack by
// bejProcessChildNodes(), we know that this parent's child nodes have
// been processed. If a new node has been added, then next we need to
// process the children of the newly added node.
if (parent != stack->stackPeek(stack->stackContext))
{
continue;
}
stack->stackPop(stack->stackContext);
}
return 0;
}
int bejEncode(const struct BejDictionaries* dictionaries,
uint16_t majorSchemaStartingOffset,
enum BejSchemaClass schemaClass,
struct RedfishPropertyParent* root,
struct BejEncoderOutputHandler* output,
struct BejPointerStackCallback* stack)
{
NULL_CHECK(dictionaries, "dictionaries");
NULL_CHECK(dictionaries->schemaDictionary, "schemaDictionary");
NULL_CHECK(dictionaries->annotationDictionary, "annotationDictionary");
NULL_CHECK(root, "root");
NULL_CHECK(output, "output");
NULL_CHECK(stack, "stack");
// Assert root node.
if (root->nodeAttr.format.principalDataType != bejSet)
{
fprintf(stderr, "Invalid root node\n");
return -1;
}
// First we need to encode a parent node before its child nodes. But before
// encoding the parent node, the encoder has to figure out the total size
// need to encode the parent's child nodes. Therefore first the encoder need
// to visit the child nodes and calculate the size need to encode them
// before producing the encoded bytes for the parent node.
//
// So first the encoder will visit child nodes and calculate the size need
// to encode each child node. Then store this information in metadata
// properties in each node struct.
// Next the encoder will again visit each node starting from the parent
// node, and produce the encoded bytes.
// First calculate metadata for encoding each node.
RETURN_IF_IERROR(bejUpdateNodeMetadata(
dictionaries, majorSchemaStartingOffset, root, stack));
// Derive the header of the encoded output.
// BEJ version
uint32_t version = BEJ_VERSION;
RETURN_IF_IERROR(
output->recvOutput(&version, sizeof(uint32_t), output->handlerContext));
uint16_t reserved = 0;
RETURN_IF_IERROR(output->recvOutput(&reserved, sizeof(uint16_t),
output->handlerContext));
RETURN_IF_IERROR(output->recvOutput(&schemaClass, sizeof(uint8_t),
output->handlerContext));
// Produce the encoded bytes for the nodes using the previously calculated
// metadata.
return bejEncodeTree(root, stack, output);
}