Add BEJ tree API.

This API allows us to represent a JSON object with a linked list
representation.

Signed-off-by: Kasun Athukorala <kasunath@google.com>
Change-Id: I7894e7c10e4db2ad02a79b01bb4d7a48d5a197cd
diff --git a/include/libbej/bej_tree.h b/include/libbej/bej_tree.h
new file mode 100644
index 0000000..2539b00
--- /dev/null
+++ b/include/libbej/bej_tree.h
@@ -0,0 +1,190 @@
+#pragma once
+
+#include "bej_common.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+    /**
+     * @brief Holds info needed to encode a parent in the JSON tree.
+     */
+    struct BejEncoderParentMetaData
+    {
+        // Starting dictionary index of the children properties.
+        uint16_t childrenDictPropOffset;
+        // Index of the child pointed by nextChild. Used to store the array
+        // indexes of bejArray elements.
+        uint16_t nextChildIndex;
+        // BEJ sequence number of the property.
+        uint32_t sequenceNumber;
+        // Size needed to encode Sequence number, Format and Length of the
+        // value.
+        size_t sflSize;
+        // Size of the value.
+        size_t vSize;
+        // Dictionary used for this parent.
+        const uint8_t* dictionary;
+        // Points to the next node which is need to process.
+        void* nextChild;
+    };
+
+    /**
+     * @brief Holds info needed to encode a leaf type in the JSON tree.
+     */
+    struct BejEncoderLeafMetaData
+    {
+        // BEJ sequence number of the property.
+        uint32_t sequenceNumber;
+        // Size needed to encode Sequence number, Format and Length of the
+        // value.
+        size_t sflSize;
+        // Size of the value.
+        size_t vSize;
+    };
+
+    /**
+     * @brief Common attributes of a JSON property.
+     */
+    struct RedfishPropertyNode
+    {
+        const char* name;
+        struct BejTupleF format;
+        // Properties belonging to the same set or
+        // array and has the same depth.
+        void* sibling;
+    };
+
+    /**
+     * @brief Used to store parent type property info.
+     *
+     * bejArray, bejSet and bejPropertyAnnotation are the parent type nodes.
+     */
+    struct RedfishPropertyParent
+    {
+        // Common property attributes.
+        struct RedfishPropertyNode nodeAttr;
+        // Number of children in the case of bejSet
+        // or bejArray.
+        size_t nChildren;
+        //  Points to the first child.
+        void* firstChild;
+        // Points to the last child. Technically we only need the firstChild
+        // pointer to add a new element to the list. But to support bejArray
+        // type, we need to traverse the linked list in the same order as the
+        // elements in the array. So we need a pointer to the first child and
+        // keep adding new nodes using the lastChild.
+        void* lastChild;
+        // Metadata used during encoding.
+        struct BejEncoderParentMetaData metaData;
+    };
+
+    /**
+     * @brief Used to store leaf type property info.
+     *
+     * Every type that doesn't belong to parent type are considered as a leaf
+     * property within a JSON tree. Each specific leaf type has its own struct.
+     * They should include this struct as the first member of their struct.
+     */
+    struct RedfishPropertyLeaf
+    {
+        struct RedfishPropertyNode nodeAttr;
+        struct BejEncoderLeafMetaData metaData;
+    };
+
+    /**
+     * @brief bejInteger type property node.
+     */
+    struct RedfishPropertyLeafInt
+    {
+        struct RedfishPropertyLeaf leaf;
+        int64_t value;
+    };
+
+    /**
+     * @brief bejEnum type property node.
+     */
+    struct RedfishPropertyLeafEnum
+    {
+        struct RedfishPropertyLeaf leaf;
+        // A string representation of the enum value.
+        const char* value;
+        // Sequence number of the enum value. Populated during bej encoding.
+        uint16_t enumValueSeq;
+    };
+
+    /**
+     * @brief Initialize a bejSet type node.
+     *
+     * @param[in] node - pointer to a RedfishPropertyParent struct.
+     * @param[in] name - name of the node.
+     */
+    void bejTreeInitSet(struct RedfishPropertyParent* node, const char* name);
+
+    /**
+     * @brief Initialize a bejArray type node.
+     *
+     * @param[in] node - pointer to a RedfishPropertyParent struct.
+     * @param[in] name - name of the node.
+     */
+    void bejTreeInitArray(struct RedfishPropertyParent* node, const char* name);
+
+    /**
+     * @brief Initialize a bejPropertyAnnotation type node.
+     *
+     * @param[in] node - pointer to a RedfishPropertyParent struct.
+     * @param[in] name - name of the node.
+     */
+    void bejTreeInitPropertyAnnotated(struct RedfishPropertyParent* node,
+                                      const char* name);
+
+    /**
+     * @brief Add a bejInteger type node to a parent node.
+     *
+     * @param[in] parent - a pointer to an initialized parent struct.
+     * @param[in] child - a pointer to an uninitialized bejInteger type node.
+     * @param[in] name - name of the bejInteger type property.
+     * @param[in] value - value of the bejInteger type property.
+     */
+    void bejTreeAddInteger(struct RedfishPropertyParent* parent,
+                           struct RedfishPropertyLeafInt* child,
+                           const char* name, int64_t value);
+
+    /**
+     * @brief Set a new value in bejInteger type node.
+     *
+     * @param[in] node - initialized bejInteger type node.
+     * @param[in] newValue - new integer value.
+     */
+    void bejTreeSetInteger(struct RedfishPropertyLeafInt* node,
+                           int64_t newValue);
+
+    /**
+     * @brief Add a bejEnum type node to a parent node.
+     *
+     * @param[in] parent - a pointer to an initialized parent struct.
+     * @param[in] child - a pointer to an uninitialized bejEnum type node.
+     * @param[in] name - name of the bejEnum type property.
+     * @param[in] value - value of the bejEnum type property.
+     */
+    void bejTreeAddEnum(struct RedfishPropertyParent* parent,
+                        struct RedfishPropertyLeafEnum* child, const char* name,
+                        const char* value);
+
+    /**
+     * @brief Link a node to its parent.
+     *
+     * @param[in] parent  - a pointer to an initialized parent struct.
+     * @param[in] child - a pointer to an initialized child struct.
+     */
+    void bejTreeLinkChildToParent(struct RedfishPropertyParent* parent,
+                                  void* child);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/bej_tree.c b/src/bej_tree.c
new file mode 100644
index 0000000..5aeda4f
--- /dev/null
+++ b/src/bej_tree.c
@@ -0,0 +1,82 @@
+#include "bej_tree.h"
+
+static void bejTreeInitParent(struct RedfishPropertyParent* node,
+                              const char* name, enum BejPrincipalDataType type)
+{
+    node->nodeAttr.name = name;
+    node->nodeAttr.format.principalDataType = type;
+    node->nodeAttr.format.deferredBinding = 0;
+    node->nodeAttr.format.readOnlyProperty = 0;
+    node->nodeAttr.format.nullableProperty = 0;
+    node->nodeAttr.sibling = NULL;
+    node->nChildren = 0;
+    node->firstChild = NULL;
+    node->lastChild = NULL;
+}
+
+void bejTreeInitSet(struct RedfishPropertyParent* node, const char* name)
+{
+    bejTreeInitParent(node, name, bejSet);
+}
+
+void bejTreeInitArray(struct RedfishPropertyParent* node, const char* name)
+{
+    bejTreeInitParent(node, name, bejArray);
+}
+
+void bejTreeInitPropertyAnnotated(struct RedfishPropertyParent* node,
+                                  const char* name)
+{
+    bejTreeInitParent(node, name, bejPropertyAnnotation);
+}
+
+static void bejTreeInitChildNode(struct RedfishPropertyLeaf* node,
+                                 const char* name,
+                                 enum BejPrincipalDataType type)
+{
+    node->nodeAttr.name = name;
+    node->nodeAttr.format.principalDataType = type;
+    node->nodeAttr.format.deferredBinding = 0;
+    node->nodeAttr.format.readOnlyProperty = 0;
+    node->nodeAttr.format.nullableProperty = 0;
+    node->nodeAttr.sibling = NULL;
+}
+
+void bejTreeAddInteger(struct RedfishPropertyParent* parent,
+                       struct RedfishPropertyLeafInt* child, const char* name,
+                       int64_t value)
+{
+    bejTreeInitChildNode((struct RedfishPropertyLeaf*)child, name, bejInteger);
+    child->value = value;
+    bejTreeLinkChildToParent(parent, child);
+}
+
+void bejTreeSetInteger(struct RedfishPropertyLeafInt* node, int64_t newValue)
+{
+    node->value = newValue;
+}
+
+void bejTreeAddEnum(struct RedfishPropertyParent* parent,
+                    struct RedfishPropertyLeafEnum* child, const char* name,
+                    const char* value)
+{
+    bejTreeInitChildNode((struct RedfishPropertyLeaf*)child, name, bejEnum);
+    child->value = value;
+    bejTreeLinkChildToParent(parent, child);
+}
+
+void bejTreeLinkChildToParent(struct RedfishPropertyParent* parent, void* child)
+{
+    // A new node is added at the end of the list.
+    if (parent->firstChild == NULL)
+    {
+        parent->firstChild = child;
+    }
+    else
+    {
+        struct RedfishPropertyNode* lastChild = parent->lastChild;
+        lastChild->sibling = child;
+    }
+    parent->lastChild = child;
+    parent->nChildren += 1;
+}
diff --git a/src/meson.build b/src/meson.build
index c09268e..051b397 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -3,6 +3,7 @@
   'bej_decoder_core.c',
   'bej_common.c',
   'bej_dictionary.c',
+  'bej_tree.c',
   'bej_decoder_json.cpp',
   include_directories : libbej_incs,
   implicit_include_directories: false,
diff --git a/test/bej_tree_test.cpp b/test/bej_tree_test.cpp
new file mode 100644
index 0000000..82d763b
--- /dev/null
+++ b/test/bej_tree_test.cpp
@@ -0,0 +1,137 @@
+#include "bej_tree.h"
+
+#include <gmock/gmock-matchers.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace libbej
+{
+
+TEST(BejTreeTest, InitSet)
+{
+    const char* name = "SomeProperty";
+    struct RedfishPropertyParent node;
+    bejTreeInitSet(&node, name);
+
+    EXPECT_THAT(node.nodeAttr.name, name);
+    EXPECT_THAT(node.nodeAttr.format.principalDataType, bejSet);
+    EXPECT_THAT(node.nodeAttr.format.deferredBinding, 0);
+    EXPECT_THAT(node.nodeAttr.format.readOnlyProperty, 0);
+    EXPECT_THAT(node.nodeAttr.format.nullableProperty, 0);
+    EXPECT_THAT(node.nodeAttr.sibling, nullptr);
+    EXPECT_THAT(node.nChildren, 0);
+    EXPECT_THAT(node.firstChild, nullptr);
+    EXPECT_THAT(node.lastChild, nullptr);
+}
+
+TEST(BejTreeTest, InitArray)
+{
+    const char* name = "SomeProperty";
+    struct RedfishPropertyParent node;
+    bejTreeInitArray(&node, name);
+
+    EXPECT_THAT(node.nodeAttr.name, name);
+    EXPECT_THAT(node.nodeAttr.format.principalDataType, bejArray);
+    EXPECT_THAT(node.nodeAttr.format.deferredBinding, 0);
+    EXPECT_THAT(node.nodeAttr.format.readOnlyProperty, 0);
+    EXPECT_THAT(node.nodeAttr.format.nullableProperty, 0);
+    EXPECT_THAT(node.nodeAttr.sibling, nullptr);
+    EXPECT_THAT(node.nChildren, 0);
+    EXPECT_THAT(node.firstChild, nullptr);
+    EXPECT_THAT(node.lastChild, nullptr);
+}
+
+TEST(BejTreeTest, InitAnnotatedProp)
+{
+    const char* name = "SomeProperty";
+    struct RedfishPropertyParent node;
+    bejTreeInitPropertyAnnotated(&node, name);
+
+    EXPECT_THAT(node.nodeAttr.name, name);
+    EXPECT_THAT(node.nodeAttr.format.principalDataType, bejPropertyAnnotation);
+    EXPECT_THAT(node.nodeAttr.format.deferredBinding, 0);
+    EXPECT_THAT(node.nodeAttr.format.readOnlyProperty, 0);
+    EXPECT_THAT(node.nodeAttr.format.nullableProperty, 0);
+    EXPECT_THAT(node.nodeAttr.sibling, nullptr);
+    EXPECT_THAT(node.nChildren, 0);
+    EXPECT_THAT(node.firstChild, nullptr);
+    EXPECT_THAT(node.lastChild, nullptr);
+}
+
+TEST(BejTreeTest, ChildLinking)
+{
+    struct RedfishPropertyParent parent;
+    struct RedfishPropertyLeafInt child1;
+    struct RedfishPropertyLeafInt child2;
+
+    bejTreeInitSet(&parent, nullptr);
+    EXPECT_THAT(parent.nChildren, 0);
+    EXPECT_THAT(parent.firstChild, nullptr);
+    EXPECT_THAT(parent.lastChild, nullptr);
+
+    bejTreeAddInteger(&parent, &child1, nullptr, 1024);
+    EXPECT_THAT(parent.nChildren, 1);
+    EXPECT_THAT(parent.firstChild, &child1);
+    EXPECT_THAT(parent.lastChild, &child1);
+
+    bejTreeAddInteger(&parent, &child2, nullptr, 20);
+    EXPECT_THAT(parent.nChildren, 2);
+    EXPECT_THAT(parent.firstChild, &child1);
+    EXPECT_THAT(parent.lastChild, &child2);
+
+    // child2 should be a sibling of child1.
+    EXPECT_THAT(child1.leaf.nodeAttr.sibling, &child2);
+}
+
+TEST(BejTreeTest, AddInteger)
+{
+    const char* name = "SomeProperty";
+    struct RedfishPropertyParent parent;
+    struct RedfishPropertyLeafInt child;
+
+    bejTreeInitSet(&parent, nullptr);
+    bejTreeAddInteger(&parent, &child, name, 1024);
+
+    EXPECT_THAT(child.leaf.nodeAttr.name, name);
+    EXPECT_THAT(child.leaf.nodeAttr.format.principalDataType, bejInteger);
+    EXPECT_THAT(child.leaf.nodeAttr.format.deferredBinding, 0);
+    EXPECT_THAT(child.leaf.nodeAttr.format.readOnlyProperty, 0);
+    EXPECT_THAT(child.leaf.nodeAttr.format.nullableProperty, 0);
+    EXPECT_THAT(child.leaf.nodeAttr.sibling, nullptr);
+    EXPECT_THAT(child.value, 1024);
+}
+
+TEST(BejTreeTest, SetInteger)
+{
+    const char* name = "SomeProperty";
+    struct RedfishPropertyParent parent;
+    struct RedfishPropertyLeafInt child;
+
+    bejTreeInitSet(&parent, nullptr);
+    bejTreeAddInteger(&parent, &child, name, 1024);
+
+    EXPECT_THAT(child.value, 1024);
+    bejTreeSetInteger(&child, 20);
+    EXPECT_THAT(child.value, 20);
+}
+
+TEST(BejTreeTest, AddEnum)
+{
+    const char* name = "SomeProperty";
+    const char* enumValue = "EnumValue";
+    struct RedfishPropertyParent parent;
+    struct RedfishPropertyLeafEnum child;
+
+    bejTreeInitSet(&parent, nullptr);
+    bejTreeAddEnum(&parent, &child, name, enumValue);
+
+    EXPECT_THAT(child.leaf.nodeAttr.name, name);
+    EXPECT_THAT(child.leaf.nodeAttr.format.principalDataType, bejEnum);
+    EXPECT_THAT(child.leaf.nodeAttr.format.deferredBinding, 0);
+    EXPECT_THAT(child.leaf.nodeAttr.format.readOnlyProperty, 0);
+    EXPECT_THAT(child.leaf.nodeAttr.format.nullableProperty, 0);
+    EXPECT_THAT(child.leaf.nodeAttr.sibling, nullptr);
+    EXPECT_THAT(child.value, enumValue);
+}
+
+} // namespace libbej
diff --git a/test/meson.build b/test/meson.build
index 8178dc1..07807c4 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -19,6 +19,7 @@
   'bej_decoder',
   'bej_common',
   'bej_dictionary',
+  'bej_tree',
 ]
 foreach t : gtests
   test(t, executable(t.underscorify(), t + '_test.cpp',