Add helper functions for BEJ dictionaries
Signed-off-by: Kasun Athukorala <kasunath@google.com>
Change-Id: I50f2ccec155434639dfecd96193608cf6c5a1787
diff --git a/include/bej_dictionary.h b/include/bej_dictionary.h
new file mode 100644
index 0000000..13d979e
--- /dev/null
+++ b/include/bej_dictionary.h
@@ -0,0 +1,101 @@
+#pragma once
+
+#include "rde_common.h"
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * @brief Mask for the type of the dictionary within a bejTupleS.
+ */
+#define DICTIONARY_TYPE_MASK 0x01
+
+/**
+ * @brief Number of bits needed to shift to get the sequence number from a
+ * bejTupleS nnint value.
+ */
+#define DICTIONARY_SEQ_NUM_SHIFT 1
+
+ /**
+ * @brief BEJ dictionary type.
+ */
+ enum BejDictionaryType
+ {
+ bejPrimary = 0,
+ bejAnnotation = 1,
+ };
+
+ /**
+ * @brief Dictionary property header.
+ */
+ struct BejDictionaryProperty
+ {
+ struct BejTupleF format;
+ uint16_t sequenceNumber;
+ uint16_t childPointerOffset;
+ uint16_t childCount;
+ uint8_t nameLength;
+ uint16_t nameOffset;
+ } __attribute__((__packed__));
+
+ struct BejDictionaryHeader
+ {
+ uint8_t versionTag;
+ uint8_t truncationFlag : 1;
+ uint8_t reservedFlags : 7;
+ uint16_t entryCount;
+ uint32_t schemaVersion;
+ uint32_t dictionarySize;
+ } __attribute__((__packed__));
+
+ /**
+ * @brief Get the offset of the first property in a dictionary.
+ *
+ * @return the offset to the first property.
+ */
+ uint16_t bejDictGetPropertyHeadOffset();
+
+ /**
+ * @brief Get the offset of the first annotated property in an annoation
+ * dictionary.
+ *
+ * @return the offset to the first annotated property in an annoation
+ * dictionary.
+ */
+ uint16_t bejDictGetFirstAnnotatedPropertyOffset();
+
+ /**
+ * @brief Get the property related to the given sequence number.
+ *
+ * @param[in] dictionary - dictionary containing the sequence number.
+ * @param[in] startingPropertyOffset - offset of the starting property for
+ * the search.
+ * @param[in] sequenceNumber - sequence number of the property.
+ * @param[out] property - if the search is successful, this will point to a
+ * valid property.
+ * @return 0 if successful.
+ */
+ int bejDictGetProperty(const uint8_t* dictionary,
+ uint16_t startingPropertyOffset,
+ uint16_t sequenceNumber,
+ const struct BejDictionaryProperty** property);
+
+ /**
+ * @brief Get the name of a property.
+ *
+ * @param[in] dictionary - dictionary containing the property.
+ * @param[in] nameOffset - dictionary offset of the name.
+ * @param[in] nameLength - length of the name.
+ * @return a NULL terminated string. If the nameLength is 0, this will
+ * return an empty string.
+ */
+ const char* bejDictGetPropertyName(const uint8_t* dictionary,
+ uint16_t nameOffset, uint8_t nameLength);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/include/rde_common.h b/include/rde_common.h
index bb55a37..f377507 100644
--- a/include/rde_common.h
+++ b/include/rde_common.h
@@ -27,13 +27,13 @@
*/
enum BejError
{
- BejErrorNoError = 0,
- BejErrorUnknown,
- BejErrorInvalidSize,
- BejErrorNotSuppoted,
- BejErrorUnknownProperty,
- BejErrorInvalidSchemaType,
- BejErrorInvalidPropertyOffset,
+ bejErrorNoError = 0,
+ bejErrorUnknown,
+ bejErrorInvalidSize,
+ bejErrorNotSuppoted,
+ bejErrorUnknownProperty,
+ bejErrorInvalidSchemaType,
+ bejErrorInvalidPropertyOffset,
};
/**
@@ -41,11 +41,11 @@
*/
enum BejSchemaClass
{
- BejMajorSchemaClass = 0,
- BejEventSchemaClass = 1,
- BejAnnotationSchemaClass = 2,
- BejCollectionMemberTypeSchemaClass = 3,
- BejErrorSchemaClass = 4,
+ bejMajorSchemaClass = 0,
+ bejEventSchemaClass = 1,
+ bejAnnotationSchemaClass = 2,
+ bejCollectionMemberTypeSchemaClass = 3,
+ bejErrorSchemaClass = 4,
};
/**
@@ -53,22 +53,22 @@
*/
enum BejPrincipalDataType
{
- BejSet = 0,
- BejArray = 1,
- BejNull = 2,
- BejInteger = 3,
- BejEnum = 4,
- BejString = 5,
- BejReal = 6,
- BejBoolean = 7,
- BejBytestring = 8,
- BejChoice = 9,
- BejPropertyAnnotation = 10,
- Reserved1 = 11,
- Reserved2 = 12,
- Reserved3 = 13,
- BejResourceLink = 14,
- BejResourceLinkExpansion = 15,
+ bejSet = 0,
+ bejArray = 1,
+ bejNull = 2,
+ bejInteger = 3,
+ bejEnum = 4,
+ bejString = 5,
+ bejReal = 6,
+ bejBoolean = 7,
+ bejBytestring = 8,
+ bejChoice = 9,
+ bejPropertyAnnotation = 10,
+ bejPrincipalDataReserved1 = 11,
+ bejPrincipalDataReserved2 = 12,
+ bejPrincipalDataReserved3 = 13,
+ bejResourceLink = 14,
+ bejResourceLinkExpansion = 15,
};
/**
@@ -145,21 +145,21 @@
enum RdeOperationInitType
{
- RdeOpInitOperationHead = 0,
- RdeOpInitOperationRead = 1,
- RdeOpInitOperationCreate = 2,
- RdeOpInitOperationDelete = 3,
- RdeOpInitOperationUpdate = 4,
- RdeOpInitOperationReplace = 5,
- RdeOpInitOperationAction = 6,
+ rdeOpInitOperationHead = 0,
+ rdeOpInitOperationRead = 1,
+ rdeOpInitOperationCreate = 2,
+ rdeOpInitOperationDelete = 3,
+ rdeOpInitOperationUpdate = 4,
+ rdeOpInitOperationReplace = 5,
+ rdeOpInitOperationAction = 6,
};
enum RdeMultiReceiveTransferFlag
{
- RdeMRecFlagStart = 0,
- RdeMRecFlagMiddle = 1,
- RdeMRecFlagEnd = 2,
- RdeMRecFlagStartAndEnd = 3,
+ rdeMRecFlagStart = 0,
+ rdeMRecFlagMiddle = 1,
+ rdeMRecFlagEnd = 2,
+ rdeMRecFlagStartAndEnd = 3,
};
struct RdeOperationInitReqHeader
@@ -190,9 +190,11 @@
/**
* @brief Get the unsigned integer value from provided bytes.
*
- * @param bytes - valid pointer to a byte stream in little-endian format.
- * @param numOfBytes - number of bytes belongs to the value. Maximum number
- * of bytes supported is 8. If numOfBytes > 8, the result is undefined.
+ * @param[in] bytes - valid pointer to a byte stream in little-endian
+ * format.
+ * @param[in] numOfBytes - number of bytes belongs to the value. Maximum
+ * number of bytes supported is 8. If numOfBytes > 8, the result is
+ * undefined.
* @return unsigend 64bit representation of the value.
*/
uint64_t rdeGetUnsignedInteger(const uint8_t* bytes, uint8_t numOfBytes);
@@ -200,7 +202,7 @@
/**
* @brief Get the value from nnint type.
*
- * @param nnint - nnint should be pointing to a valid nnint.
+ * @param[in] nnint - nnint should be pointing to a valid nnint.
* @return unsigend 64bit representation of the value.
*/
uint64_t rdeGetNnint(const uint8_t* nnint);
@@ -208,7 +210,7 @@
/**
* @brief Get the size of the complete nnint.
*
- * @param nnint - pointer to a valid nnint.
+ * @param[in] nnint - pointer to a valid nnint.
* @return size of the complete nnint.
*/
uint8_t rdeGetNnintSize(const uint8_t* nnint);
diff --git a/src/bej_dictionary.c b/src/bej_dictionary.c
new file mode 100644
index 0000000..a86720b
--- /dev/null
+++ b/src/bej_dictionary.c
@@ -0,0 +1,100 @@
+#include "bej_dictionary.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+
+/**
+ * @brief Get the index for a property offset. First property will be at index
+ * 0.
+ *
+ * @param[in] propertyOffset - a valid property offset.
+ * @return index of the property.
+ */
+static uint16_t bejGetPropertyEntryIndex(uint16_t propertyOffset)
+{
+ return (propertyOffset - bejDictGetPropertyHeadOffset()) /
+ sizeof(struct BejDictionaryProperty);
+}
+
+/**
+ * @brief Validate a property offset.
+ *
+ * @param[in] propertyOffset - offset needed to be validated.
+ * @return true if propertyOffset is a valid offset.
+ */
+static bool bejValidatePropertyOffset(uint16_t propertyOffset)
+{
+ // propertyOffset should be greater than or equal to first property offset.
+ if (propertyOffset < bejDictGetPropertyHeadOffset())
+ {
+ fprintf(
+ stderr,
+ "Invalid property offset. Pointing to Dictionary header data\n");
+ return false;
+ }
+
+ // propertyOffset should be a multiple of sizeof(BejDictionaryProperty)
+ // starting from first property within the dictionary.
+ if ((propertyOffset - bejDictGetPropertyHeadOffset()) %
+ sizeof(struct BejDictionaryProperty))
+ {
+ fprintf(stderr, "Invalid property offset. Does not point to beginning "
+ "of property\n");
+ return false;
+ }
+
+ return true;
+}
+
+uint16_t bejDictGetPropertyHeadOffset()
+{
+ // First property is present soon after the dictionary header.
+ return sizeof(struct BejDictionaryHeader);
+}
+
+uint16_t bejDictGetFirstAnnotatedPropertyOffset()
+{
+ // The first property available is the "Annotations" set which is the parent
+ // for all properties. Next immediate property is the first property we
+ // need.
+ return sizeof(struct BejDictionaryHeader) +
+ sizeof(struct BejDictionaryProperty);
+}
+
+int bejDictGetProperty(const uint8_t* dictionary,
+ uint16_t startingPropertyOffset, uint16_t sequenceNumber,
+ const struct BejDictionaryProperty** property)
+{
+ uint16_t propertyOffset = startingPropertyOffset;
+ const struct BejDictionaryHeader* header =
+ (const struct BejDictionaryHeader*)dictionary;
+
+ if (!bejValidatePropertyOffset(propertyOffset))
+ {
+ return bejErrorInvalidPropertyOffset;
+ }
+ uint16_t propertyIndex = bejGetPropertyEntryIndex(propertyOffset);
+
+ for (uint16_t index = propertyIndex; index < header->entryCount; ++index)
+ {
+ const struct BejDictionaryProperty* p =
+ (const struct BejDictionaryProperty*)(dictionary + propertyOffset);
+ if (p->sequenceNumber == sequenceNumber)
+ {
+ *property = p;
+ return 0;
+ }
+ propertyOffset += sizeof(struct BejDictionaryProperty);
+ }
+ return bejErrorUnknownProperty;
+}
+
+const char* bejDictGetPropertyName(const uint8_t* dictionary,
+ uint16_t nameOffset, uint8_t nameLength)
+{
+ if (nameLength == 0)
+ {
+ return "";
+ }
+ return (const char*)(dictionary + nameOffset);
+}
diff --git a/src/meson.build b/src/meson.build
index 0d2703d..e8d9958 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,6 +1,7 @@
libbej_lib = static_library(
'libbej',
'rde_common.c',
+ 'bej_dictionary.c',
include_directories : libbej_incs,
implicit_include_directories: false
)
diff --git a/test/bej_dictionary_test.cpp b/test/bej_dictionary_test.cpp
new file mode 100644
index 0000000..0ca9361
--- /dev/null
+++ b/test/bej_dictionary_test.cpp
@@ -0,0 +1,107 @@
+#include "bej_dictionary.h"
+
+#include <array>
+#include <string_view>
+#include <tuple>
+
+#include <gmock/gmock-matchers.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace rde
+{
+
+/**
+ * @brief A valid dictionary.
+ */
+constexpr std::array<uint8_t, 279> dummySimpleDict{
+ {0x0, 0x0, 0xc, 0x0, 0x0, 0xf0, 0xf0, 0xf1, 0x17, 0x1, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x16, 0x0, 0x5, 0x0, 0xc, 0x84, 0x0, 0x14, 0x0,
+ 0x0, 0x48, 0x0, 0x1, 0x0, 0x13, 0x90, 0x0, 0x56, 0x1, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x3, 0xa3, 0x0, 0x74, 0x2, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x16, 0xa6, 0x0, 0x34, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16,
+ 0xbc, 0x0, 0x64, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x13, 0xd2, 0x0,
+ 0x0, 0x0, 0x0, 0x52, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x74, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0xe5, 0x0, 0x46, 0x1, 0x0, 0x66,
+ 0x0, 0x3, 0x0, 0xb, 0xf4, 0x0, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x9, 0xff, 0x0, 0x50, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7,
+ 0x8, 0x1, 0x50, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xf, 0x1,
+ 0x44, 0x75, 0x6d, 0x6d, 0x79, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x0,
+ 0x43, 0x68, 0x69, 0x6c, 0x64, 0x41, 0x72, 0x72, 0x61, 0x79, 0x50, 0x72,
+ 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x0, 0x49, 0x64, 0x0, 0x53, 0x61,
+ 0x6d, 0x70, 0x6c, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x50,
+ 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x0, 0x53, 0x61, 0x6d, 0x70,
+ 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x50, 0x72, 0x6f,
+ 0x70, 0x65, 0x72, 0x74, 0x79, 0x0, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+ 0x52, 0x65, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79,
+ 0x0, 0x41, 0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x6c,
+ 0x65, 0x61, 0x6e, 0x0, 0x4c, 0x69, 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74,
+ 0x75, 0x73, 0x0, 0x4c, 0x69, 0x6e, 0x6b, 0x44, 0x6f, 0x77, 0x6e, 0x0,
+ 0x4c, 0x69, 0x6e, 0x6b, 0x55, 0x70, 0x0, 0x4e, 0x6f, 0x4c, 0x69, 0x6e,
+ 0x6b, 0x0, 0x0}};
+
+/**
+ * @brief Property names and sequence numbers in dummySimpleDict.
+ * Order here is the same as the order in the dummySimpleDict.
+ */
+constexpr std::array<std::tuple<std::string_view, int>, 12> propertyNameSeq{{
+ {"DummySimple", 0},
+ {"ChildArrayProperty", 0},
+ {"Id", 1},
+ {"SampleEnabledProperty", 2},
+ {"SampleIntegerProperty", 3},
+ {"SampleRealProperty", 4},
+ {"", 0},
+ {"AnotherBoolean", 0},
+ {"LinkStatus", 1},
+ {"LinkDown", 0},
+ {"LinkUp", 1},
+ {"NoLink", 2},
+}};
+
+TEST(BejDictionaryTest, PropertyHeadOffsetTest)
+{
+ EXPECT_THAT(bejDictGetPropertyHeadOffset(), sizeof(BejDictionaryHeader));
+}
+
+TEST(BejDictionaryTest, AnnotationPropertyHeadOffsetTest)
+{
+ EXPECT_THAT(bejDictGetFirstAnnotatedPropertyOffset(),
+ sizeof(BejDictionaryHeader) + sizeof(BejDictionaryProperty));
+}
+
+TEST(BejDictionaryTest, ValidPropertyTest)
+{
+ const struct BejDictionaryHeader* header =
+ (const struct BejDictionaryHeader*)dummySimpleDict.data();
+ uint16_t propHead = bejDictGetPropertyHeadOffset();
+ // Read each property in the dictionary and verify that the property name is
+ // correct.
+ for (uint16_t index = 0; index < header->entryCount; ++index)
+ {
+ uint16_t offset = propHead + sizeof(BejDictionaryProperty) * index;
+ const struct BejDictionaryProperty* property;
+ EXPECT_THAT(bejDictGetProperty(dummySimpleDict.data(), offset,
+ std::get<1>(propertyNameSeq[index]),
+ &property),
+ 0);
+ EXPECT_THAT(bejDictGetPropertyName(dummySimpleDict.data(),
+ property->nameOffset,
+ property->nameLength),
+ std::get<0>(propertyNameSeq[index]));
+ }
+}
+
+TEST(BejDictionaryTest, invalidPropertyOffsetTest)
+{
+ const struct BejDictionaryProperty* property;
+ EXPECT_THAT(bejDictGetProperty(dummySimpleDict.data(), /*offset=*/0,
+ /*sequenceNumber=*/0, &property),
+ bejErrorInvalidPropertyOffset);
+ uint16_t propHead = bejDictGetPropertyHeadOffset();
+ EXPECT_THAT(bejDictGetProperty(dummySimpleDict.data(), propHead + 1,
+ /*sequenceNumber=*/0, &property),
+ bejErrorInvalidPropertyOffset);
+}
+
+} // namespace rde
diff --git a/test/meson.build b/test/meson.build
index a775a0c..8e406a7 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -17,6 +17,7 @@
gtests = [
'rde_common',
+ 'bej_dictionary',
]
foreach t : gtests
test(t, executable(t.underscorify(), t + '_test.cpp',