Add decoding for bejArray, bejNull, bejString and bejBoolean

Tested:
Tested this locally with unit tests. I will submit unit tests for
review once all the necessary data type decoding changes are submitted.

Signed-off-by: Kasun Athukorala <kasunath@google.com>
Change-Id: Id764bdc4728c4ab2a250f3327e2414c31506bf3d
diff --git a/src/bej_decoder_core.c b/src/bej_decoder_core.c
index c226a52..41b71ed 100644
--- a/src/bej_decoder_core.c
+++ b/src/bej_decoder_core.c
@@ -202,18 +202,20 @@
 }
 
 /**
- * @brief Find and return the property name of the current encoded segment.
+ * @brief Find and return the property name of the current encoded segment. If
+ * the params->state.addPropertyName is false, this will return an empty string.
  *
  * @param[in] params - a valid populated BejHandleTypeFuncParam.
  * @return 0 if successful.
  */
-static const char* bejFindPropName(struct BejHandleTypeFuncParam* params)
+static const char* bejGetPropName(struct BejHandleTypeFuncParam* params)
 {
     const uint8_t* dictionary;
     const struct BejDictionaryProperty* prop;
-    if (bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
-                                    params->sflv.tupleS.sequenceNumber,
-                                    &dictionary, &prop) != 0)
+    if (!params->state.addPropertyName ||
+        (bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
+                                     params->sflv.tupleS.sequenceNumber,
+                                     &dictionary, &prop) != 0))
     {
         return "";
     }
@@ -378,6 +380,81 @@
 }
 
 /**
+ * @brief Decodes a BejArray type SFLV BEJ tuple.
+ *
+ * @param params - a valid BejHandleTypeFuncParam struct.
+ * @return 0 if successful.
+ */
+static int bejHandleBejArray(struct BejHandleTypeFuncParam* params)
+{
+    const uint8_t* dictionary;
+    const struct BejDictionaryProperty* prop;
+    RETURN_IF_IERROR(bejGetDictionaryAndProperty(
+        params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber,
+        &dictionary, &prop));
+
+    const char* propName = "";
+    if (params->state.addPropertyName)
+    {
+        propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
+                                          prop->nameLength);
+    }
+
+    RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayStart,
+                              propName, params->callbacksDataPtr);
+
+    uint64_t elements = rdeGetNnint(params->sflv.value);
+    // If its an empty array, we are done here.
+    if (elements == 0)
+    {
+        RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayEnd,
+                                  params->callbacksDataPtr);
+    }
+    else
+    {
+        // Update the state for next segment decoding.
+        struct BejStackProperty newEnding = {
+            .sectionType = bejSectionArray,
+            .addPropertyName = params->state.addPropertyName,
+            .mainDictPropOffset = params->state.mainDictPropOffset,
+            .annoDictPropOffset = params->state.annoDictPropOffset,
+            .streamEndOffset = params->sflv.valueEndOffset,
+        };
+        RETURN_IF_IERROR(
+            params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
+        // We do not add property names for array elements.
+        params->state.addPropertyName = false;
+        if (params->sflv.tupleS.schema == bejAnnotation)
+        {
+            // Since this array is an annotated type, we need to advance the
+            // annotation dictionary for decoding the next segment.
+            params->state.annoDictPropOffset = prop->childPointerOffset;
+        }
+        else
+        {
+            params->state.mainDictPropOffset = prop->childPointerOffset;
+        }
+    }
+    params->state.encodedStreamOffset = bejGetFirstTupleOffset(params);
+    return 0;
+}
+
+/**
+ * @brief Decodes a BejNull type SFLV BEJ tuple.
+ *
+ * @param params - a valid BejHandleTypeFuncParam struct.
+ * @return 0 if successful.
+ */
+static int bejHandleBejNull(struct BejHandleTypeFuncParam* params)
+{
+    const char* propName = bejGetPropName(params);
+    RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, propName,
+                              params->callbacksDataPtr);
+    params->state.encodedStreamOffset = params->sflv.valueEndOffset;
+    return bejProcessEnding(params, /*canBeEmpty=*/false);
+}
+
+/**
  * @brief Decodes a BejInteger type SFLV BEJ tuple.
  *
  * @param params - a valid BejHandleTypeFuncParam struct.
@@ -385,11 +462,7 @@
  */
 static int bejHandleBejInteger(struct BejHandleTypeFuncParam* params)
 {
-    const char* propName = "";
-    if (params->state.addPropertyName)
-    {
-        propName = bejFindPropName(params);
-    }
+    const char* propName = bejGetPropName(params);
 
     if (params->sflv.valueLength == 0)
     {
@@ -408,6 +481,57 @@
 }
 
 /**
+ * @brief Decodes a BejString type SFLV BEJ tuple.
+ *
+ * @param params - a valid BejHandleTypeFuncParam struct.
+ * @return 0 if successful.
+ */
+static int bejHandleBejString(struct BejHandleTypeFuncParam* params)
+{
+    // TODO: Handle deferred bindings.
+    const char* propName = bejGetPropName(params);
+
+    if (params->sflv.valueLength == 0)
+    {
+        RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
+                                  propName, params->callbacksDataPtr);
+    }
+    else
+    {
+        RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackString,
+                                  propName, (const char*)(params->sflv.value),
+                                  params->callbacksDataPtr);
+    }
+    params->state.encodedStreamOffset = params->sflv.valueEndOffset;
+    return bejProcessEnding(params, /*canBeEmpty=*/false);
+}
+
+/**
+ * @brief Decodes a BejBoolean type SFLV BEJ tuple.
+ *
+ * @param params - a valid BejHandleTypeFuncParam struct.
+ * @return 0 if successful.
+ */
+static int bejHandleBejBoolean(struct BejHandleTypeFuncParam* params)
+{
+    const char* propName = bejGetPropName(params);
+
+    if (params->sflv.valueLength == 0)
+    {
+        RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
+                                  propName, params->callbacksDataPtr);
+    }
+    else
+    {
+        RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackBool,
+                                  propName, *(params->sflv.value) > 0,
+                                  params->callbacksDataPtr);
+    }
+    params->state.encodedStreamOffset = params->sflv.valueEndOffset;
+    return bejProcessEnding(params, /*canBeEmpty=*/false);
+}
+
+/**
  * @brief Decodes an encoded bej stream.
  *
  * @param[in] schemaDictionary - main schema dictionary to use.
@@ -475,14 +599,10 @@
                 RETURN_IF_IERROR(bejHandleBejSet(&params));
                 break;
             case bejArray:
-                // TODO: Add support for BejArray decoding.
-                fprintf(stderr, "No BejArray support\n");
-                params.state.encodedStreamOffset = params.sflv.valueEndOffset;
+                RETURN_IF_IERROR(bejHandleBejArray(&params));
                 break;
             case bejNull:
-                // TODO: Add support for BejNull decoding.
-                fprintf(stderr, "No BejNull support\n");
-                params.state.encodedStreamOffset = params.sflv.valueEndOffset;
+                RETURN_IF_IERROR(bejHandleBejNull(&params));
                 break;
             case bejInteger:
                 RETURN_IF_IERROR(bejHandleBejInteger(&params));
@@ -493,9 +613,7 @@
                 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
                 break;
             case bejString:
-                // TODO: Add support for BejString decoding.
-                fprintf(stderr, "No BejString support\n");
-                params.state.encodedStreamOffset = params.sflv.valueEndOffset;
+                RETURN_IF_IERROR(bejHandleBejString(&params));
                 break;
             case bejReal:
                 // TODO: Add support for BejReal decoding.
@@ -503,9 +621,7 @@
                 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
                 break;
             case bejBoolean:
-                // TODO: Add support for BejBoolean decoding.
-                fprintf(stderr, "No BejBoolean support\n");
-                params.state.encodedStreamOffset = params.sflv.valueEndOffset;
+                RETURN_IF_IERROR(bejHandleBejBoolean(&params));
                 break;
             case bejBytestring:
                 // TODO: Add support for BejBytestring decoding.