Get metadata for BEJ integer, string and bool

This change adds the support to derive bejInteger, bejString and
bejBool node metadata.

Signed-off-by: Kasun Athukorala <kasunath@google.com>
Change-Id: Ib6efcd29bf7cb2acc9913f95756e60ee65f16c6d
diff --git a/include/libbej/bej_common.h b/include/libbej/bej_common.h
index 96f1a66..6db5fd1 100644
--- a/include/libbej/bej_common.h
+++ b/include/libbej/bej_common.h
@@ -246,6 +246,17 @@
     uint8_t bejGetNnintSize(const uint8_t* nnint);
 
     /**
+     * @brief Get the bytes needed represent the value as a bejInteger.
+     *
+     * This will return the number of bytes needed to encode the signed value
+     * into a bejInteger type.
+     *
+     * @param val - signed value needed to encode.
+     * @return size of the bejInteger.
+     */
+    uint8_t bejIntLengthOfValue(int64_t val);
+
+    /**
      * @brief Get the total bytes needed to encode an unsigned value using nnint
      * format.
      *
diff --git a/src/bej_common.c b/src/bej_common.c
index 0e1cf10..ca4a849 100644
--- a/src/bej_common.c
+++ b/src/bej_common.c
@@ -24,6 +24,45 @@
     return *nnint + sizeof(uint8_t);
 }
 
+uint8_t bejIntLengthOfValue(int64_t val)
+{
+    // Only need to encode 0x00 or 0xFF
+    if (val == 0 || val == -1)
+    {
+        return 1;
+    }
+
+    // Starts at the MSB. LSB index is 0.
+    uint8_t byteIndex = sizeof(uint64_t) - 1;
+    const uint8_t bitsPerByte = 8;
+    // The current byte being looked at. Starts at MSB.
+    uint8_t currentByte = (val >> (bitsPerByte * byteIndex)) & 0xFF;
+    uint8_t byteLength = sizeof(int64_t);
+
+    while ((val > 0 && currentByte == 0) || (val < 0 && currentByte == 0xFF))
+    {
+        byteLength--;
+        byteIndex--;
+        currentByte = (val >> (bitsPerByte * byteIndex)) & 0xFF;
+    }
+
+    // If the value is positive and encoded MSBbit is 1 we need to add 0x00 to
+    // the encoded value as padding.
+    if (val > 0 && (currentByte & 0x80))
+    {
+        byteLength++;
+    }
+
+    // If the value is negative and encoded MSBbit is 0 we need to add 0xFF to
+    // the encoded value as padding.
+    if (val < 0 && !(currentByte & 0x80))
+    {
+        byteLength++;
+    }
+
+    return byteLength;
+}
+
 uint8_t bejNnintEncodingSizeOfUInt(uint64_t val)
 {
     uint8_t bytes = 0;
diff --git a/src/bej_encoder_metadata.c b/src/bej_encoder_metadata.c
index bb08874..3c32f80 100644
--- a/src/bej_encoder_metadata.c
+++ b/src/bej_encoder_metadata.c
@@ -8,6 +8,22 @@
 #include <string.h>
 
 /**
+ * @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.
@@ -156,6 +172,79 @@
     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 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.
  *
@@ -173,15 +262,25 @@
                                      void* childPtr, uint16_t childIndex,
                                      uint16_t dictStartingOffset)
 {
-    // TODO: Implement this
-    (void)dictionaries;
-    (void)parentDictionary;
-    (void)childIndex;
-    (void)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 bejBoolean:
+            RETURN_IF_IERROR(
+                bejUpdateBoolMetaData(dictionaries, parentDictionary, childPtr,
+                                      childIndex, dictStartingOffset));
+            break;
         default:
             fprintf(stderr, "Child type %u not supported\n",
                     chNode->nodeAttr.format.principalDataType);