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',