Changed CBOR array format
authorErich Keane <erich.keane@intel.com>
Mon, 14 Sep 2015 17:18:09 +0000 (10:18 -0700)
committerPatrick Lankswert <patrick.lankswert@intel.com>
Wed, 16 Sep 2015 19:31:26 +0000 (19:31 +0000)
Previously, a CBOR array was predicated by its type and dimensions.
In order to be closer to spec compliant, this fix removes these two, and
encodes by using recursive arrays.

Change-Id: Ib644d59c44e0897479ff1473f6b57eacfc9b4da3
Signed-off-by: Erich Keane <erich.keane@intel.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/2527
Tested-by: jenkins-iotivity <jenkins-iotivity@opendaylight.org>
Reviewed-by: Patrick Lankswert <patrick.lankswert@intel.com>
resource/csdk/stack/src/ocpayloadconvert.c
resource/csdk/stack/src/ocpayloadparse.c

index 9a51f67..5584a0b 100644 (file)
@@ -463,65 +463,98 @@ static int64_t OCConvertPlatformPayload(OCPlatformPayload* payload, uint8_t* out
     return checkError(err, &encoder, outPayload, size);
 }
 
-static int64_t OCConvertArray(CborEncoder* parent, const OCRepPayloadValueArray* valArray)
+static int64_t OCConvertArrayItem(CborEncoder* array, const OCRepPayloadValueArray* valArray,
+        size_t index)
 {
-    CborEncoder array;
     int64_t err = 0;
-
-    err = err | cbor_encoder_create_array(parent, &array, CborIndefiniteLength);
-    err = err | cbor_encode_uint(&array, valArray->type);
-    for(int i = 0; i < MAX_REP_ARRAY_DEPTH; ++i)
+    switch (valArray->type)
     {
-        err = err | cbor_encode_uint(&array, valArray->dimensions[i]);
+        case OCREP_PROP_NULL:
+            OC_LOG(ERROR, TAG, "ConvertArray Invalid NULL");
+            err = CborUnknownError;
+            break;
+        case OCREP_PROP_INT:
+            err = err | cbor_encode_int(array, valArray->iArray[index]);
+            break;
+        case OCREP_PROP_DOUBLE:
+            err = err | cbor_encode_double(array, valArray->dArray[index]);
+            break;
+        case OCREP_PROP_BOOL:
+            err = err | cbor_encode_boolean(array, valArray->bArray[index]);
+            break;
+        case OCREP_PROP_STRING:
+            if (!valArray->strArray[index])
+            {
+                err = err | cbor_encode_null(array);
+            }
+            else
+            {
+                err = err | cbor_encode_text_string(array, valArray->strArray[index],
+                        strlen(valArray->strArray[index]));
+            }
+            break;
+        case OCREP_PROP_OBJECT:
+            if (!valArray->objArray[index])
+            {
+                err = err | cbor_encode_null(array);
+            }
+            else
+            {
+                err = OCConvertSingleRepPayload(array, valArray->objArray[index]);
+            }
+            break;
+        case OCREP_PROP_ARRAY:
+            OC_LOG(ERROR, TAG, "ConvertArray Invalid child array");
+            err = CborUnknownError;
+            break;
     }
 
-    size_t dimTotal = calcDimTotal(valArray->dimensions);
+    return err;
+}
+static int64_t OCConvertArray(CborEncoder* parent, const OCRepPayloadValueArray* valArray)
+{
+    CborEncoder array;
+    int64_t err = 0;
+
+    err = err | cbor_encoder_create_array(parent, &array, valArray->dimensions[0]);
 
-    for(size_t i = 0; i < dimTotal; ++i)
+    for (size_t i = 0; i < valArray->dimensions[0];++i)
     {
-        switch(valArray->type)
+        if (valArray->dimensions[1] != 0)
         {
-            case OCREP_PROP_NULL:
-                OC_LOG(ERROR, TAG, "ConvertArray Invalid NULL");
-                err = CborUnknownError;
-                break;
-            case OCREP_PROP_INT:
-                err = err | cbor_encode_int(&array, valArray->iArray[i]);
-                break;
-            case OCREP_PROP_DOUBLE:
-                err = err | cbor_encode_double(&array, valArray->dArray[i]);
-                break;
-            case OCREP_PROP_BOOL:
-                err = err | cbor_encode_boolean(&array, valArray->bArray[i]);
-                break;
-            case OCREP_PROP_STRING:
-                if (!valArray->strArray[i])
-                {
-                    err = err | cbor_encode_null(&array);
-                }
-                else
-                {
-                    err = err | cbor_encode_text_string(&array, valArray->strArray[i],
-                            strlen(valArray->strArray[i]));
-                }
-                break;
-            case OCREP_PROP_OBJECT:
-                if (!valArray->objArray[i])
+            CborEncoder array2;
+            err = err | cbor_encoder_create_array(&array, &array2, valArray->dimensions[1]);
+
+            for (size_t j = 0; j < valArray->dimensions[1]; ++j)
+            {
+                if (valArray->dimensions[2] != 0)
                 {
-                    err = err | cbor_encode_null(&array);
+                    CborEncoder array3;
+                    err = err | cbor_encoder_create_array(&array2, &array3,
+                            valArray->dimensions[2]);
+
+                    for(size_t k = 0; k < valArray->dimensions[2]; ++k)
+                    {
+                        OCConvertArrayItem(&array3, valArray,
+                            j * valArray->dimensions[2] +
+                            i * valArray->dimensions[2] * valArray->dimensions[1] +
+                            k);
+                    }
+                    err = err | cbor_encoder_close_container(&array2, &array3);
                 }
                 else
                 {
-                    err = OCConvertSingleRepPayload(&array, valArray->objArray[i]);
+                    OCConvertArrayItem(&array2, valArray,
+                            i * valArray->dimensions[1] + j);
                 }
-                break;
-            case OCREP_PROP_ARRAY:
-                OC_LOG(ERROR, TAG, "ConvertArray Invalid child array");
-                err = CborUnknownError;
-                break;
+            }
+            err = err | cbor_encoder_close_container(&array, &array2);
+        }
+        else
+        {
+            OCConvertArrayItem(&array, valArray, i);
         }
     }
-
     err = err | cbor_encoder_close_container(parent, &array);
     return err;
 }
index e3d4ea2..d7ed58a 100644 (file)
@@ -558,142 +558,298 @@ static OCStackResult OCParsePlatformPayload(OCPayload** outPayload, CborValue* a
     }
 }
 
-static bool OCParseArray(OCRepPayload* out, const char* name, CborValue* container)
+static OCRepPayloadPropType DecodeCborType(CborType type)
+{
+    switch (type)
+    {
+            case CborNullType:
+                return OCREP_PROP_NULL;
+            case CborIntegerType:
+                return OCREP_PROP_INT;
+            case CborDoubleType:
+                return OCREP_PROP_DOUBLE;
+            case CborBooleanType:
+                return OCREP_PROP_BOOL;
+            case CborTextStringType:
+                return OCREP_PROP_STRING;
+            case CborMapType:
+                return OCREP_PROP_OBJECT;
+            case CborArrayType:
+                return OCREP_PROP_ARRAY;
+            default:
+                return OCREP_PROP_NULL;
+    }
+}
+static bool OCParseArrayFindDimensionsAndType(const CborValue* parent, size_t dimensions[MAX_REP_ARRAY_DEPTH],
+        OCRepPayloadPropType* type)
 {
+    bool err = false;
     CborValue insideArray;
+    *type = OCREP_PROP_NULL;
+    dimensions[0] = dimensions[1] = dimensions[2] = 0;
+
+    err = err || cbor_value_enter_container(parent, &insideArray);
+
+    while (cbor_value_is_valid(&insideArray))
+    {
+        OCRepPayloadPropType tempType = DecodeCborType(cbor_value_get_type(&insideArray));
+
+        if (tempType == OCREP_PROP_ARRAY)
+        {
+            size_t subdim[MAX_REP_ARRAY_DEPTH];
+            tempType = OCREP_PROP_NULL;
+            err = err || OCParseArrayFindDimensionsAndType(&insideArray, subdim, &tempType);
+
+            if (subdim[2] != 0)
+            {
+                OC_LOG(ERROR, TAG, "Parse array helper, sub-array too deep");
+            }
+
+            dimensions[1] = dimensions[1] >= subdim[0] ? dimensions[1] : subdim[0];
+            dimensions[2] = dimensions[2] >= subdim[1] ? dimensions[2] : subdim[1];
+
+            if (*type != OCREP_PROP_NULL && tempType != OCREP_PROP_NULL
+                    && *type != tempType)
+            {
+                OC_LOG(ERROR, TAG, "Array parse failed, mixed arrays not allowed (subtype)");
+                return true;
+            }
+            else if (*type == OCREP_PROP_NULL)
+            {
+                // We don't know the type of this array yet, so the assignment is OK
+                *type = tempType;
+            }
+        }
+        else if (*type == OCREP_PROP_NULL)
+        {
+            // We don't know the type of this array yet, so the assignment is OK
+            *type = tempType;
+        }
+        // tempType is allowed to be NULL, since it might now know the answer yet
+        else if (tempType != OCREP_PROP_NULL && *type != tempType)
+        {
+            // this is an invalid situation!
+            OC_LOG(ERROR, TAG, "Array parse failed, mixed arrays not allowed");
+            return true;
+        }
+
+        ++dimensions[0];
+        cbor_value_advance(&insideArray);
+    }
+
+    return err;
+}
+
+static size_t getAllocSize(OCRepPayloadPropType type)
+{
+    switch (type)
+    {
+        case OCREP_PROP_INT:
+            return sizeof (int64_t);
+        case OCREP_PROP_DOUBLE:
+            return sizeof (double);
+        case OCREP_PROP_BOOL:
+            return sizeof (bool);
+        case OCREP_PROP_STRING:
+            return sizeof (char*);
+        case OCREP_PROP_OBJECT:
+            return sizeof (OCRepPayload*);
+        default:
+            return 0;
+    }
+}
+
+static size_t arrayStep(size_t dimensions[MAX_REP_ARRAY_DEPTH], size_t elementNum)
+{
+    return
+        (dimensions[1] == 0 ? 1 : dimensions[1]) *
+        (dimensions[2] == 0 ? 1 : dimensions[2]) *
+        elementNum;
+}
+
+static bool OCParseArrayFillArray(const CborValue* parent, size_t dimensions[MAX_REP_ARRAY_DEPTH],
+        OCRepPayloadPropType type, void* targetArray)
+{
     bool err = false;
-    uint64_t tempInt = 0;
+    CborValue insideArray;
+
+    err = err || cbor_value_enter_container(parent, &insideArray);
+
+    size_t i = 0;
+    char* tempStr = NULL;
+    size_t tempLen = 0;
+    OCRepPayload* tempPl = NULL;
+
+    size_t newdim[MAX_REP_ARRAY_DEPTH];
+    newdim[0] = dimensions[1];
+    newdim[1] = dimensions[2];
+    newdim[2] = 0;
+
+    while (!err && i < dimensions[0] && cbor_value_is_valid(&insideArray))
+    {
+        if (cbor_value_get_type(&insideArray) != CborNullType)
+        {
+            switch (type)
+            {
+                case OCREP_PROP_INT:
+                    if (dimensions[1] == 0)
+                    {
+                        err = err || cbor_value_get_int64(&insideArray,
+                                &(((int64_t*)targetArray)[i]));
+                    }
+                    else
+                    {
+                        err = err || OCParseArrayFillArray(&insideArray, newdim,
+                            type,
+                            &(((int64_t*)targetArray)[arrayStep(dimensions, i)])
+                            );
+                    }
+                    break;
+                case OCREP_PROP_DOUBLE:
+                    if (dimensions[1] == 0)
+                    {
+                        err = err || cbor_value_get_double(&insideArray,
+                                &(((double*)targetArray)[i]));
+                    }
+                    else
+                    {
+                        err = err || OCParseArrayFillArray(&insideArray, newdim,
+                            type,
+                            &(((double*)targetArray)[arrayStep(dimensions, i)])
+                            );
+                    }
+                    break;
+                case OCREP_PROP_BOOL:
+                    if (dimensions[1] == 0)
+                    {
+                        err = err || cbor_value_get_boolean(&insideArray,
+                                &(((bool*)targetArray)[i]));
+                    }
+                    else
+                    {
+                        err = err || OCParseArrayFillArray(&insideArray, newdim,
+                            type,
+                            &(((bool*)targetArray)[arrayStep(dimensions, i)])
+                            );
+                    }
+                    break;
+                case OCREP_PROP_STRING:
+                    if (dimensions[1] == 0)
+                    {
+                        err = err || cbor_value_dup_text_string(&insideArray,
+                                &tempStr, &tempLen, NULL);
+                        ((char**)targetArray)[i] = tempStr;
+                        tempStr = NULL;
+                    }
+                    else
+                    {
+                        err = err || OCParseArrayFillArray(&insideArray, newdim,
+                            type,
+                            &(((char**)targetArray)[arrayStep(dimensions, i)])
+                            );
+                    }
+                    break;
+                case OCREP_PROP_OBJECT:
+                    if (dimensions[1] == 0)
+                    {
+                        err = err || OCParseSingleRepPayload(&tempPl, &insideArray);
+                        ((OCRepPayload**)targetArray)[i] = tempPl;
+                        tempPl = NULL;
+                    }
+                    else
+                    {
+                        err = err || OCParseArrayFillArray(&insideArray, newdim,
+                            type,
+                            &(((OCRepPayload**)targetArray)[arrayStep(dimensions, i)])
+                            );
+                    }
+                    break;
+                default:
+                    OC_LOG(ERROR, TAG, "Invalid Array type in Parse Array");
+                    err = true;
+                    break;
+            }
+        }
+        ++i;
+        err = err || cbor_value_advance(&insideArray);
+    }
+
+    return err;
+}
+
+static bool OCParseArray(OCRepPayload* out, const char* name, CborValue* container)
+{
     OCRepPayloadPropType type;
     size_t dimensions[MAX_REP_ARRAY_DEPTH];
-    err = err || cbor_value_enter_container(container, &insideArray);
+    bool err = OCParseArrayFindDimensionsAndType(container, dimensions, &type);
 
-    err = err || cbor_value_get_uint64(&insideArray, &tempInt);
-    err = err || cbor_value_advance_fixed(&insideArray);
-    type = (OCRepPayloadPropType)tempInt;
+    if (err)
+    {
+        OC_LOG(ERROR, TAG, "Array details weren't clear");
+        return err;
+    }
 
-    for(int i = 0; i < MAX_REP_ARRAY_DEPTH; ++ i)
+    if (type == OCREP_PROP_NULL)
     {
-         err = err || cbor_value_get_uint64(&insideArray, &tempInt);
-         err = err || cbor_value_advance_fixed(&insideArray);
-        dimensions[i] = tempInt;
+        err = err || OCRepPayloadSetNull(out, name);
+        err = err || cbor_value_advance(container);
+        return err;
     }
 
     size_t dimTotal = calcDimTotal(dimensions);
+    size_t allocSize = getAllocSize(type);
+    void* arr = OICCalloc(dimTotal, allocSize);
 
-    void* arr = NULL;
-    char* tempStr;
-    size_t len;
-    OCRepPayload* pl;
-    switch(type)
+    if (!arr)
+    {
+        OC_LOG(ERROR, TAG, "Array Parse allocation failed");
+        return true;
+    }
+
+    err = err || OCParseArrayFillArray(container, dimensions, type, arr);
+
+    switch (type)
     {
         case OCREP_PROP_INT:
-            arr = (int64_t*)OICMalloc(dimTotal * sizeof(int64_t));
-            if (arr)
-            {
-                for(size_t i = 0; i < dimTotal && !err; ++i)
-                {
-                     err = err || cbor_value_get_int64(&insideArray, &(((int64_t*)arr)[i]));
-                     err = err || cbor_value_advance_fixed(&insideArray);
-                }
-                if(err || !OCRepPayloadSetIntArrayAsOwner(out, name, (int64_t*)arr, dimensions))
-                {
-                    OICFree(arr);
-                    err = true;
-                }
-            }
-            else
+            if (err || !OCRepPayloadSetIntArrayAsOwner(out, name, (int64_t*)arr, dimensions))
             {
+                OICFree(arr);
                 err = true;
             }
             break;
         case OCREP_PROP_DOUBLE:
-            arr = (double*)OICMalloc(dimTotal * sizeof(double));
-            if(arr)
-            {
-                for(size_t i = 0; i < dimTotal && !err; ++i)
-                {
-                     err = err || cbor_value_get_double(&insideArray, &(((double*)arr)[i]));
-                     err = err || cbor_value_advance_fixed(&insideArray);
-                }
-                if(err || !OCRepPayloadSetDoubleArrayAsOwner(out, name, (double*)arr, dimensions))
-                {
-                    OICFree(arr);
-                    err = true;
-                }
-            }
-            else
+            if (err || !OCRepPayloadSetDoubleArrayAsOwner(out, name, (double*)arr, dimensions))
             {
+                OICFree(arr);
                 err = true;
             }
             break;
         case OCREP_PROP_BOOL:
-            arr = (bool*)OICMalloc(dimTotal * sizeof(bool));
-            if(arr)
-            {
-                for(size_t i = 0; i < dimTotal && !err; ++i)
-                {
-                     err = err || cbor_value_get_boolean(&insideArray, &(((bool*)arr)[i]));
-                     err = err || cbor_value_advance_fixed(&insideArray);
-                }
-                if(err || !OCRepPayloadSetBoolArrayAsOwner(out, name, (bool*)arr, dimensions))
-                {
-                    OICFree(arr);
-                    err = true;
-                }
-            }
-            else
+            if (err || !OCRepPayloadSetBoolArrayAsOwner(out, name, (bool*)arr, dimensions))
             {
+                OICFree(arr);
                 err = true;
             }
             break;
         case OCREP_PROP_STRING:
-            arr = (char**)OICCalloc(dimTotal, sizeof(char*));
-            if(arr)
+            if (err || !OCRepPayloadSetStringArrayAsOwner(out, name, (char**)arr, dimensions))
             {
-                for(size_t i = 0; i < dimTotal && !err; ++i)
-                {
-                    if (!cbor_type_is_null(&insideArray))
-                    {
-                        err = err || cbor_value_dup_text_string(&insideArray, &tempStr,
-                                &len, NULL);
-                        ((char**)arr)[i] = tempStr;
-                    }
-                    err = err || cbor_value_advance(&insideArray);
-                }
-                if(err || !OCRepPayloadSetStringArrayAsOwner(out, name, (char**)arr, dimensions))
+                for(size_t i = 0; i < dimTotal; ++i)
                 {
-                    OICFree(arr);
-                    err = true;
+                    OICFree(((char**)arr)[i]);
                 }
-            }
-            else
-            {
+                OICFree(arr);
                 err = true;
             }
             break;
         case OCREP_PROP_OBJECT:
-            arr = (OCRepPayload**)OICCalloc(dimTotal, sizeof(OCRepPayload*));
-            if(arr)
+            if (err || !OCRepPayloadSetPropObjectArrayAsOwner(out, name, (OCRepPayload**)arr, dimensions))
             {
-                for(size_t i = 0; i < dimTotal && !err; ++i)
-                {
-                    if (!cbor_type_is_null(&insideArray))
-                    {
-                        pl = NULL;
-                        err = err || OCParseSingleRepPayload(&pl, &insideArray);
-                        ((OCRepPayload**)arr)[i] = pl;
-                    }
-                    err = err || cbor_value_advance(&insideArray);
-                }
-                if(err || !OCRepPayloadSetPropObjectArrayAsOwner(out, name,
-                        (OCRepPayload**)arr, dimensions))
+                for(size_t i = 0; i < dimTotal; ++i)
                 {
-                    OICFree(arr);
-                    err = true;
+                    OCRepPayloadDestroy(((OCRepPayload**)arr)[i]);
                 }
-            }
-            else
-            {
+                OICFree(arr);
                 err = true;
             }
             break;