A small memory footprint AMQP implimentation

Dependents:   iothub_client_sample_amqp remote_monitoring simplesample_amqp

Revision:
43:4c1e4e94cdd3
Parent:
29:4a11413cf217
Child:
46:01f7ca900e07
--- a/amqpvalue.c	Fri May 04 13:24:52 2018 -0700
+++ b/amqpvalue.c	Mon Jun 11 15:39:52 2018 -0700
@@ -1799,16 +1799,21 @@
     return result;
 }
 
+/* Codes_SRS_AMQPVALUE_01_397: [1.6.24 array A sequence of values of a single type.] */
 AMQP_VALUE amqpvalue_create_array(void)
 {
     AMQP_VALUE result = REFCOUNT_TYPE_CREATE(AMQP_VALUE_DATA);
     if (result == NULL)
     {
+        /* Codes_SRS_AMQPVALUE_01_405: [ If allocating memory for the array fails, then `amqpvalue_create_array` shall return NULL. ] */
         LogError("Could not allocate memory for AMQP value");
     }
     else
     {
+        /* Codes_SRS_AMQPVALUE_01_404: [ `amqpvalue_create_array` shall return a handle to an AMQP_VALUE that stores an array. ] */
         result->type = AMQP_TYPE_ARRAY;
+
+        /* Codes_SRS_AMQPVALUE_01_406: [ The array shall have an initial size of zero. ] */
         result->value.array_value.items = NULL;
         result->value.array_value.count = 0;
     }
@@ -1816,21 +1821,23 @@
     return result;
 }
 
-int amqpvalue_get_array_item_count(AMQP_VALUE value, uint32_t* size)
+int amqpvalue_get_array_item_count(AMQP_VALUE value, uint32_t* count)
 {
     int result;
 
+    /* Tests_SRS_AMQPVALUE_01_421: [ If any of the arguments is NULL, `amqpvalue_get_array_item_count` shall fail and return a non-zero value. ]*/
     if ((value == NULL) ||
-        (size == NULL))
-    {
-        LogError("Bad arguments: value = %p, size = %p",
-            value, size);
+        (count == NULL))
+    {
+        LogError("Bad arguments: value = %p, count = %p",
+            value, count);
         result = __FAILURE__;
     }
     else
     {
         AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value;
 
+        /* Codes_SRS_AMQPVALUE_01_422: [ If the array argument is not an AMQP value created with the `amqpvalue_create_array` function then `amqpvalue_get_array_item_count` shall fail and return a non-zero value. ]*/
         if (value_data->type != AMQP_TYPE_ARRAY)
         {
             LogError("Value is not of type ARRAY");
@@ -1838,7 +1845,10 @@
         }
         else
         {
-            *size = value_data->value.array_value.count;
+            /* Codes_SRS_AMQPVALUE_01_419: [ `amqpvalue_get_array_item_count` shall return in `count` the number of items in the array. ]*/
+            *count = value_data->value.array_value.count;
+
+            /* Codes_SRS_AMQPVALUE_01_420: [ On success `amqpvalue_get_array_item_count` shall return 0. ]*/
             result = 0;
         }
     }
@@ -1850,6 +1860,7 @@
 {
     int result;
 
+    /* Codes_SRS_AMQPVALUE_01_409: [ If `value` or `array_item_value` is NULL, amqpvalue_add_array_item shall fail and return a non-zero value. ]*/
     if (value == NULL)
     {
         LogError("NULL value");
@@ -1857,6 +1868,7 @@
     }
     else
     {
+        /* Codes_SRS_AMQPVALUE_01_413: [ If the `value` argument is not an AMQP array created with the `amqpvalue_create_array` function than `amqpvalue_add_array_item` shall fail and return a non-zero value. ] */
         AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value;
         if (value_data->type != AMQP_TYPE_ARRAY)
         {
@@ -1865,6 +1877,7 @@
         }
         else
         {
+            /* Codes_SRS_AMQPVALUE_01_425: [ If the type of `array_item_value` does not match that of items already in the array then `amqpvalue_add_array_item` shall fail and return a non-zero value. ] */
             AMQP_VALUE_DATA* array_item_value_data = (AMQP_VALUE_DATA*)array_item_value;
             if ((value_data->value.array_value.count > 0) &&
                 (array_item_value_data->type != value_data->value.array_value.items[0]->type))
@@ -1874,9 +1887,12 @@
             }
             else
             {
+                /* Codes_SRS_AMQPVALUE_01_410: [ The item stored at the n-th position in the array shall be a clone of `array_item_value`. ] */
                 AMQP_VALUE cloned_item = amqpvalue_clone(array_item_value);
                 if (cloned_item == NULL)
                 {
+                    /* Codes_SRS_AMQPVALUE_01_423: [ When `amqpvalue_add_array_item` fails due to not being able to clone the item or grow the array, the array shall not be altered. ] */
+                    /* Codes_SRS_AMQPVALUE_01_412: [ If cloning the item fails, `amqpvalue_add_array_item` shall fail and return a non-zero value. ]*/
                     LogError("Cannot clone value to put in the array");
                     result = __FAILURE__;
                 }
@@ -1885,6 +1901,8 @@
                     AMQP_VALUE* new_array = (AMQP_VALUE*)realloc(value_data->value.array_value.items, (value_data->value.array_value.count + 1) * sizeof(AMQP_VALUE));
                     if (new_array == NULL)
                     {
+                        /* Codes_SRS_AMQPVALUE_01_423: [ When `amqpvalue_add_array_item` fails due to not being able to clone the item or grow the array, the array shall not be altered. ] */
+                        /* Codes_SRS_AMQPVALUE_01_424: [ If growing the array fails, then `amqpvalue_add_array_item` shall fail and return a non-zero value. ] */
                         amqpvalue_destroy(cloned_item);
                         LogError("Cannot resize array");
                         result = __FAILURE__;
@@ -1893,9 +1911,11 @@
                     {
                         value_data->value.array_value.items = new_array;
 
+                        /* Codes_SRS_AMQPVALUE_01_407: [ `amqpvalue_add_array_item` shall add the AMQP_VALUE specified by `array_item_value` at the 0 based n-th position in the array. ]*/
                         value_data->value.array_value.items[value_data->value.array_value.count] = cloned_item;
                         value_data->value.array_value.count++;
 
+                        /* Codes_SRS_AMQPVALUE_01_408: [ On success `amqpvalue_add_array_item` shall return 0. ]*/
                         result = 0;
                     }
                 }
@@ -1912,6 +1932,7 @@
 
     if (value == NULL)
     {
+        /* Codes_SRS_AMQPVALUE_01_416: [ If the `value` argument is NULL, `amqpvalue_get_array_item` shall fail and return NULL. ] */
         LogError("NULL value");
         result = NULL;
     }
@@ -1919,11 +1940,13 @@
     {
         AMQP_VALUE_DATA* value_data = (AMQP_VALUE_DATA*)value;
 
+        /* Codes_SRS_AMQPVALUE_01_418: [ If value is not an array then `amqpvalue_get_array_item` shall fail and return NULL. ] */
         if (value_data->type != AMQP_TYPE_ARRAY)
         {
             LogError("Value is not of type ARRAY");
             result = NULL;
         }
+        /* Codes_SRS_AMQPVALUE_01_417: [ If `index` is greater or equal to the number of items in the array then `amqpvalue_get_array_item` shall fail and return NULL. ] */
         else if (value_data->value.array_value.count <= index)
         {
             LogError("Index out of range: %u", (unsigned int)index);
@@ -1931,6 +1954,8 @@
         }
         else
         {
+            /* Codes_SRS_AMQPVALUE_01_414: [ `amqpvalue_get_array_item` shall return a copy of the AMQP_VALUE stored at the 0 based position `index` in the array identified by `value`. ] */
+            /* Codes_SRS_AMQPVALUE_01_426: [ If cloning the item at position `index` fails, then `amqpvalue_get_array_item` shall fail and return NULL. ] */
             result = amqpvalue_clone(value_data->value.array_value.items[index]);
         }
     }
@@ -2122,6 +2147,31 @@
 
                 break;
             }
+            case AMQP_TYPE_ARRAY:
+            {
+                /* Codes_SRS_AMQPVALUE_01_427: [- array: compare array item count and each element. ] */
+                if (value1_data->value.array_value.count != value2_data->value.array_value.count)
+                {
+                    result = false;
+                }
+                else
+                {
+                    uint32_t i;
+
+                    for (i = 0; i < value1_data->value.array_value.count; i++)
+                    {
+                        /* Codes_SRS_AMQPVALUE_01_428: [ Nesting shall be considered in comparison. ] */
+                        if (!amqpvalue_are_equal(value1_data->value.array_value.items[i], value2_data->value.array_value.items[i]))
+                        {
+                            break;
+                        }
+                    }
+
+                    result = (i == value1_data->value.array_value.count);
+                }
+
+                break;
+            }
             case AMQP_TYPE_MAP:
             {
                 /* Codes_SRS_AMQPVALUE_01_233: [- map: compare map pair count and each key/value pair.] */
@@ -3047,6 +3097,115 @@
     return result;
 }
 
+static int encode_array(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context, uint32_t count, AMQP_VALUE* items)
+{
+    size_t i;
+    int result;
+
+    uint32_t size = 0;
+
+    /* get the size of all items in the array */
+    for (i = 0; i < count; i++)
+    {
+        size_t item_size;
+        if (amqpvalue_get_encoded_size(items[i], &item_size) != 0)
+        {
+            LogError("Could not get encoded size for element %u of the array", (unsigned int)i);
+            break;
+        }
+
+        if ((item_size > UINT32_MAX) ||
+            size + (uint32_t)item_size < size)
+        {
+            LogError("Overflow in array size computation");
+            break;
+        }
+        
+        size = (uint32_t)(size + item_size);
+
+    }
+
+    if (i < count)
+    {
+        /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */
+        result = __FAILURE__;
+    }
+    else
+    {
+        if ((count <= 255) && (size < 255))
+        {
+            size++;
+
+            /* Codes_SRS_AMQPVALUE_01_306: [<encoding name="map8" code="0xE0" category="compound" width="1" label="up to 2^8 - 1 octets of encoded map data"/>] */
+            if ((output_byte(encoder_output, context, 0xE0) != 0) ||
+                /* size */
+                (output_byte(encoder_output, context, (size & 0xFF)) != 0) ||
+                /* count */
+                (output_byte(encoder_output, context, (count & 0xFF)) != 0))
+            {
+                /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */
+                LogError("Could not encode map header");
+                result = __FAILURE__;
+            }
+            else
+            {
+                /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */
+                result = 0;
+            }
+        }
+        else
+        {
+            size += 4;
+
+            /* Codes_SRS_AMQPVALUE_01_307: [<encoding name="map32" code="0xF0" category="compound" width="4" label="up to 2^32 - 1 octets of encoded map data"/>] */
+            if ((output_byte(encoder_output, context, 0xF0) != 0) ||
+                /* size */
+                (output_byte(encoder_output, context, (size >> 24) & 0xFF) != 0) ||
+                (output_byte(encoder_output, context, (size >> 16) & 0xFF) != 0) ||
+                (output_byte(encoder_output, context, (size >> 8) & 0xFF) != 0) ||
+                (output_byte(encoder_output, context, size & 0xFF) != 0) ||
+                /* count */
+                (output_byte(encoder_output, context, (count >> 24) & 0xFF) != 0) ||
+                (output_byte(encoder_output, context, (count >> 16) & 0xFF) != 0) ||
+                (output_byte(encoder_output, context, (count >> 8) & 0xFF) != 0) ||
+                (output_byte(encoder_output, context, count & 0xFF) != 0))
+            {
+                /* Codes_SRS_AMQPVALUE_01_274: [When the encoder output function fails, amqpvalue_encode shall fail and return a non-zero value.] */
+                LogError("Could not encode array");
+                result = __FAILURE__;
+            }
+            else
+            {
+                /* Codes_SRS_AMQPVALUE_01_266: [On success amqpvalue_encode shall return 0.] */
+                result = 0;
+            }
+        }
+
+        if (result == 0)
+        {
+            for (i = 0; i < count; i++)
+            {
+                if (amqpvalue_encode(items[i], encoder_output, context) != 0)
+                {
+                    break;
+                }
+            }
+
+            if (i < count)
+            {
+                LogError("Failed encoding element %u of the array", (unsigned int)i);
+                result = __FAILURE__;
+            }
+            else
+            {
+                result = 0;
+            }
+        }
+    }
+
+    return result;
+}
+
 static int encode_descriptor_header(AMQPVALUE_ENCODER_OUTPUT encoder_output, void* context)
 {
     int result;
@@ -3163,6 +3322,10 @@
             result = encode_list(encoder_output, context, value_data->value.list_value.count, value_data->value.list_value.items);
             break;
 
+        case AMQP_TYPE_ARRAY:
+            result = encode_array(encoder_output, context, value_data->value.array_value.count, value_data->value.array_value.items);
+            break;
+
         case AMQP_TYPE_MAP:
             result = encode_map(encoder_output, context, value_data->value.map_value.pair_count, value_data->value.map_value.pairs);
             break;