Get metadata for bejReal type

Signed-off-by: Kasun Athukorala <kasunath@google.com>
Change-Id: Ib5de2d4fde4a129cb32988268c3289ba06572b16
diff --git a/src/bej_encoder_metadata.c b/src/bej_encoder_metadata.c
index 3e273a0..098c5e7 100644
--- a/src/bej_encoder_metadata.c
+++ b/src/bej_encoder_metadata.c
@@ -3,11 +3,17 @@
 #include "bej_common.h"
 #include "bej_dictionary.h"
 
+#include <math.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
 
 /**
+ * @brief Maximum digits supported in the fractional part of a real number.
+ */
+#define BEJ_REAL_PRECISION 16
+
+/**
  * @brief bejTupleL size of an integer.
  *
  * Maximum bytes possible for an integer is 8. Therefore to encode the length of
@@ -24,6 +30,11 @@
 #define BEJ_TUPLE_L_SIZE_FOR_BEJ_BOOL 2
 
 /**
+ * @brief bejTupleF size.
+ */
+#define BEJ_TUPLE_F_SIZE 1
+
+/**
  * @brief Check the name is an annotation type name.
  *
  * @param[in] name - property name.
@@ -187,7 +198,7 @@
     // 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;
+    node->leaf.metaData.sflSize += BEJ_TUPLE_F_SIZE;
     // L: Length needed for the value.
     node->leaf.metaData.sflSize += BEJ_TUPLE_L_SIZE_FOR_BEJ_INTEGER;
     // V: Bytes used for the value.
@@ -211,7 +222,7 @@
     // 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;
+    node->leaf.metaData.sflSize += BEJ_TUPLE_F_SIZE;
     // L: Length needed for the string including the NULL character. Length is
     // in nnint format.
     size_t strLenWithNull = strlen(node->value) + 1;
@@ -221,6 +232,87 @@
     return 0;
 }
 
+static int bejUpdateRealMetaData(const struct BejDictionaries* dictionaries,
+                                 const uint8_t* parentDictionary,
+                                 struct RedfishPropertyLeafReal* 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;
+
+    if (node->value > (double)INT64_MAX)
+    {
+        // TODO: We should use the exponent.
+        fprintf(
+            stderr,
+            "Need to add support to encode double value larger than INT64_MAX\n");
+        return -1;
+    }
+
+    // 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 += BEJ_TUPLE_F_SIZE;
+    // We need to breakdown the real number to bejReal type to determine the
+    // length. We are not gonna add an exponent. It will only be the whole part
+    // and the fraction part. Get the whole part
+    double originalWhole;
+    double originalFract = modf(node->value, &originalWhole);
+
+    // Convert the fraction to a whole value for encoding.
+    // Create a new value by multiplying the original fraction by 10. Do this
+    // until the fraction of the new value is 0 or we reach the precision. Eg
+    // 0.00105: This fraction value has two leading zeros. We will keep
+    // multiplying this by 10 until the fraction of the result of that
+    // multiplication is 0.
+    double originalFactConvertedToWhole = fabs(originalFract);
+    double fract = originalFract;
+    double intPart;
+    uint32_t leadingZeros = 0;
+    uint32_t precision = 0;
+    while (fract != 0 && precision < BEJ_REAL_PRECISION)
+    {
+        originalFactConvertedToWhole = originalFactConvertedToWhole * 10;
+        fract = modf(originalFactConvertedToWhole, &intPart);
+        // If the integer portion is 0, that means we still have leading zeros.
+        if (intPart == 0)
+        {
+            ++leadingZeros;
+        }
+        ++precision;
+    }
+    node->bejReal.whole = (int64_t)originalWhole;
+    node->bejReal.zeroCount = leadingZeros;
+    node->bejReal.fract = (int64_t)originalFactConvertedToWhole;
+    // We are omitting exp. So the exp length should be 0.
+    node->bejReal.expLen = 0;
+    node->bejReal.exp = 0;
+
+    // Calculate the sizes needed for storing bejReal fields.
+    // nnint for the length of the "whole" value.
+    node->leaf.metaData.vSize = BEJ_TUPLE_L_SIZE_FOR_BEJ_INTEGER;
+    // Length needed for the "whole" value.
+    node->leaf.metaData.vSize += bejIntLengthOfValue((int64_t)originalWhole);
+    // nnint for leading zero count.
+    node->leaf.metaData.vSize += bejNnintEncodingSizeOfUInt(leadingZeros);
+    // nnint for the factional part.
+    node->leaf.metaData.vSize +=
+        bejNnintEncodingSizeOfUInt((int64_t)originalFactConvertedToWhole);
+    // nnint for the exp length. We are omitting exp. So the exp length should
+    // be 0.
+    node->leaf.metaData.vSize += bejNnintEncodingSizeOfUInt(0);
+
+    // L: nnint for the size needed for encoding the bejReal value.
+    node->leaf.metaData.sflSize +=
+        bejNnintEncodingSizeOfUInt(node->leaf.metaData.vSize);
+    return 0;
+}
+
 static int bejUpdateEnumMetaData(const struct BejDictionaries* dictionaries,
                                  const uint8_t* parentDictionary,
                                  struct RedfishPropertyLeafEnum* node,
@@ -264,7 +356,7 @@
     // 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;
+    node->leaf.metaData.sflSize += BEJ_TUPLE_F_SIZE;
     // V: Bytes used for the value.
     node->leaf.metaData.vSize =
         bejNnintEncodingSizeOfUInt(enumValueProperty->sequenceNumber);
@@ -290,7 +382,7 @@
     // 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;
+    node->leaf.metaData.sflSize += BEJ_TUPLE_F_SIZE;
     // 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.
@@ -329,6 +421,11 @@
                 dictionaries, parentDictionary, childPtr, childIndex,
                 dictStartingOffset));
             break;
+        case bejReal:
+            RETURN_IF_IERROR(
+                bejUpdateRealMetaData(dictionaries, parentDictionary, childPtr,
+                                      childIndex, dictStartingOffset));
+            break;
         case bejEnum:
             RETURN_IF_IERROR(
                 bejUpdateEnumMetaData(dictionaries, parentDictionary, childPtr,