Add ByteString support in CBOR wrapper in OIC stack accepted/tizen/ivi/20160218.024825 accepted/tizen/mobile/20160115.111531 accepted/tizen/tv/20160115.111550 accepted/tizen/wearable/20160115.111608 submit/tizen/20160115.042718 submit/tizen_common/20160115.042709 submit/tizen_common/20160218.142243 submit/tizen_ivi/20160217.000000 submit/tizen_ivi/20160217.000005
authorSachin Agrawal <sachin.agrawal@intel.com>
Thu, 8 Oct 2015 16:20:54 +0000 (09:20 -0700)
committerYoungjae Shin <yj99.shin@samsung.com>
Fri, 15 Jan 2016 04:26:13 +0000 (13:26 +0900)
Currently byte strings in CBOR wrapper are achieved by using
int arrays. Drawbacks with this approach is in-efficient memory
consumption and 3rd Party tools cannot properly decode the CBOR
generated in this fashion.

Updated code in CBOR wrapper and also added unit tests.

Change-Id: I4ed1adc891be84e800c833df404914a335150ded
Signed-off-by: Sachin Agrawal <sachin.agrawal@intel.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/2337
Tested-by: jenkins-iotivity <jenkins-iotivity@opendaylight.org>
resource/csdk/stack/include/payload_logging.h
resource/csdk/stack/src/ocpayloadconvert.c
resource/csdk/stack/src/ocpayloadparse.c
resource/csdk/stack/test/SConscript
resource/csdk/stack/test/cbortests.cpp [new file with mode: 0644]

index 4c4dfbb..69cb45f 100644 (file)
@@ -91,6 +91,10 @@ static inline void OCPayloadLogRep(LogLevel level, OCRepPayload* payload)
                 case OCREP_PROP_STRING:
                     OC_LOG_V(level, PL_TAG, "\t\t%s(string):%s", val->name, val->str);
                     break;
+                case OCREP_PROP_BYTE_STRING:
+                    OC_LOG_V(level, PL_TAG, "\t\t%s(binary):", val->name);
+                    OC_LOG_BUFFER(level, PL_TAG, val->ocByteStr.bytes, val->ocByteStr.len);
+                    break;
                 case OCREP_PROP_OBJECT:
                     // Note: Only prints the URI (if available), to print further, you'll
                     // need to dig into the object better!
@@ -123,6 +127,12 @@ static inline void OCPayloadLogRep(LogLevel level, OCRepPayload* payload)
                                     val->arr.dimensions[0], val->arr.dimensions[1],
                                     val->arr.dimensions[2]);
                             break;
+                        case OCREP_PROP_BYTE_STRING:
+                            OC_LOG_V(level, PL_TAG, "\t\t%s(byte array):%lld x %lld x %lld",
+                                    val->name,
+                                    val->arr.dimensions[0], val->arr.dimensions[1],
+                                    val->arr.dimensions[2]);
+                            break;
                         case OCREP_PROP_OBJECT:
                             OC_LOG_V(level, PL_TAG, "\t\t%s(OCRep array):%lld x %lld x %lld",
                                     val->name,
index 7b46b36..85a36ce 100644 (file)
@@ -560,6 +560,17 @@ static int64_t OCConvertArrayItem(CborEncoder* array, const OCRepPayloadValueArr
                         strlen(valArray->strArray[index]));
             }
             break;
+        case OCREP_PROP_BYTE_STRING:
+            if (!valArray->strArray[index])
+            {
+                err = err | cbor_encode_null(array);
+            }
+            else
+            {
+                err = err | cbor_encode_byte_string(array, valArray->ocByteStrArray[index].bytes,
+                        valArray->ocByteStrArray[index].len);
+            }
+            break;
         case OCREP_PROP_OBJECT:
             if (!valArray->objArray[index])
             {
index 4dc71ee..c045e14 100644 (file)
@@ -614,6 +614,8 @@ static OCRepPayloadPropType DecodeCborType(CborType type)
                 return OCREP_PROP_BOOL;
             case CborTextStringType:
                 return OCREP_PROP_STRING;
+            case CborByteStringType:
+                return OCREP_PROP_BYTE_STRING;
             case CborMapType:
                 return OCREP_PROP_OBJECT;
             case CborArrayType:
@@ -694,6 +696,8 @@ static size_t getAllocSize(OCRepPayloadPropType type)
             return sizeof (bool);
         case OCREP_PROP_STRING:
             return sizeof (char*);
+        case OCREP_PROP_BYTE_STRING:
+            return sizeof (OCByteString);
         case OCREP_PROP_OBJECT:
             return sizeof (OCRepPayload*);
         default:
@@ -719,6 +723,7 @@ static bool OCParseArrayFillArray(const CborValue* parent, size_t dimensions[MAX
 
     size_t i = 0;
     char* tempStr = NULL;
+    OCByteString ocByteStr = { .bytes = NULL, .len = 0};
     size_t tempLen = 0;
     OCRepPayload* tempPl = NULL;
 
@@ -792,6 +797,21 @@ static bool OCParseArrayFillArray(const CborValue* parent, size_t dimensions[MAX
                             );
                     }
                     break;
+                case OCREP_PROP_BYTE_STRING:
+                    if (dimensions[1] == 0)
+                    {
+                        err = err || cbor_value_dup_byte_string(&insideArray,
+                                &(ocByteStr.bytes), &(ocByteStr.len), NULL);
+                        ((OCByteString*)targetArray)[i] = ocByteStr;
+                    }
+                    else
+                    {
+                        err = err || OCParseArrayFillArray(&insideArray, newdim,
+                                type,
+                                &(((OCByteString*)targetArray)[arrayStep(dimensions, i)])
+                                );
+                    }
+                    break;
                 case OCREP_PROP_OBJECT:
                     if (dimensions[1] == 0)
                     {
@@ -889,6 +909,17 @@ static bool OCParseArray(OCRepPayload* out, const char* name, CborValue* contain
                 err = true;
             }
             break;
+        case OCREP_PROP_BYTE_STRING:
+            if (err || !OCRepPayloadSetByteStringArrayAsOwner(out, name, (OCByteString*)arr, dimensions))
+            {
+                for (size_t i = 0; i < dimTotal; ++i)
+                {
+                    OICFree(((OCByteString*)arr)[i].bytes);
+                }
+                OICFree(arr);
+                err = true;
+            }
+            break;
         case OCREP_PROP_OBJECT:
             if (err || !OCRepPayloadSetPropObjectArrayAsOwner(out, name, (OCRepPayload**)arr, dimensions))
             {
index d75d459..507f011 100644 (file)
@@ -69,8 +69,9 @@ if env.get('LOGGING'):
 # Source files and Targets
 ######################################################################
 stacktests = stacktest_env.Program('stacktests', ['stacktests.cpp'])
+cbortests = stacktest_env.Program('cbortests', ['cbortests.cpp'])
 
-Alias("test", [stacktests])
+Alias("test", [stacktests, cbortests])
 
 env.AppendTarget('test')
 if env.get('TEST') == '1':
diff --git a/resource/csdk/stack/test/cbortests.cpp b/resource/csdk/stack/test/cbortests.cpp
new file mode 100644 (file)
index 0000000..2e4a202
--- /dev/null
@@ -0,0 +1,222 @@
+//******************************************************************
+//
+// Copyright 2015 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+
+extern "C"
+{
+    #include "ocstack.h"
+    #include "ocpayload.h"
+    #include "ocpayloadcbor.h"
+    #include "logger.h"
+    #include "oic_malloc.h"
+}
+
+#include "gtest/gtest.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+//-----------------------------------------------------------------------------
+// Includes
+//-----------------------------------------------------------------------------
+#include <stdio.h>
+#include <string.h>
+
+#include <iostream>
+#include <stdint.h>
+
+#include "gtest_helper.h"
+
+class CborByteStringTest : public ::testing::Test {
+    protected:
+        virtual void SetUp() {
+            // Create Payload
+            payload_in = OCRepPayloadCreate();
+            ASSERT_TRUE(payload_in != NULL);
+        }
+
+        virtual void TearDown() {
+            OCPayloadDestroy((OCPayload*)payload_in);
+        }
+
+        OCRepPayload* payload_in;
+};
+
+TEST_F(CborByteStringTest, ByteStringSetGetTest)
+{
+    OCRepPayloadSetUri(payload_in, "/a/quake_sensor");
+    OCRepPayloadSetPropInt(payload_in, "scale", 4);
+
+    uint8_t binval[] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x0, 0xA, 0xB, 0xC,
+                        0xD, 0xE, 0xF};
+    OCByteString quakedata_in = { binval, sizeof(binval)};
+
+    EXPECT_EQ(true, OCRepPayloadSetPropByteString(payload_in, "quakedata", quakedata_in));
+
+    OCByteString quakedata_out = { NULL, 0};
+    ASSERT_EQ(true, OCRepPayloadGetPropByteString(payload_in, "quakedata", &quakedata_out));
+
+    EXPECT_EQ(quakedata_in.len, quakedata_out.len);
+    EXPECT_EQ(0, memcmp(quakedata_in.bytes, quakedata_out.bytes, quakedata_in.len));
+
+    // Cleanup
+    OICFree(quakedata_out.bytes);
+}
+
+TEST_F(CborByteStringTest, ByteStringConvertParseTest)
+{
+    OCRepPayloadSetUri(payload_in, "/a/quake_sensor");
+    OCRepPayloadSetPropInt(payload_in, "scale", 4);
+
+    uint8_t binval[] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x0, 0xA, 0xB, 0xC,
+                        0xD, 0xE, 0xF};
+    OCByteString quakedata_in = { binval, sizeof(binval)};
+
+    // Set ByteString in Payload
+    EXPECT_EQ(true, OCRepPayloadSetPropByteString(payload_in, "quakedata", quakedata_in));
+
+    // Convert OCPayload to CBOR
+    uint8_t *payload_cbor = NULL;
+    size_t payload_cbor_size = 0;
+    EXPECT_EQ(OC_STACK_OK, OCConvertPayload((OCPayload*) payload_in, &payload_cbor, &payload_cbor_size));
+
+#ifdef CBOR_BIN_STRING_DEBUG
+    FILE *fp = fopen("binstring.cbor", "wb+");
+    if (fp)
+    {
+        fwrite(payload_cbor, 1, payload_cbor_size, fp);
+        fclose(fp);
+    }
+#endif //CBOR_BIN_STRING_DEBUG
+
+    // Parse CBOR back to OCPayload
+    OCPayload* payload_out = NULL;
+    EXPECT_EQ(OC_STACK_OK, OCParsePayload(&payload_out, PAYLOAD_TYPE_REPRESENTATION,
+                 payload_cbor, payload_cbor_size));
+
+    OCByteString quakedata_out = {NULL, 0};
+    ASSERT_EQ(true, OCRepPayloadGetPropByteString((OCRepPayload*)payload_out, "quakedata", &quakedata_out));
+
+    // Compare input and output data
+    EXPECT_EQ(quakedata_in.len, quakedata_out.len);
+    EXPECT_EQ(0, memcmp(quakedata_in.bytes, quakedata_out.bytes, quakedata_in.len));
+
+    // Cleanup
+    OICFree(payload_cbor);
+    OICFree(quakedata_out.bytes);
+    OCPayloadDestroy((OCPayload*)payload_out);
+}
+
+TEST_F(CborByteStringTest, ByteStringArraySetGetTest )
+{
+    OCRepPayloadSetUri(payload_in, "/a/quake_sensor");
+    OCRepPayloadSetPropInt(payload_in, "scale", 4);
+
+    size_t dimensions_in[MAX_REP_ARRAY_DEPTH] = { 3, 0, 0};
+    uint8_t binval1[] = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19};
+    uint8_t binval2[] = {0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29};
+    uint8_t binval3[] = {0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39};
+
+    OCByteString quakedata_in[3] = {{binval1, sizeof(binval1)},
+                                    {binval2, sizeof(binval2)},
+                                    {binval3, sizeof(binval3)}};
+
+    EXPECT_EQ(true, OCRepPayloadSetByteStringArray(payload_in, "quakedata",
+                quakedata_in, dimensions_in));
+
+    OCByteString* quakedata_out = NULL;
+    size_t dimensions_out[MAX_REP_ARRAY_DEPTH] = {0};
+    ASSERT_EQ(true, OCRepPayloadGetByteStringArray(payload_in, "quakedata",
+                &quakedata_out, dimensions_out));
+
+    for(size_t i = 0; i < dimensions_in[0]; i++)
+    {
+        EXPECT_EQ(quakedata_in[i].len, quakedata_out[i].len);
+        EXPECT_EQ(0, memcmp(quakedata_in[i].bytes, quakedata_out[i].bytes, quakedata_in[i].len));
+    }
+
+    // Cleanup
+    for(size_t i = 0; i < dimensions_out[0]; i++)
+    {
+        OICFree(quakedata_out[i].bytes);
+    }
+    OICFree(quakedata_out);
+}
+
+
+TEST_F(CborByteStringTest, ByteStringArrayConvertParseTest )
+{
+    OCRepPayloadSetUri(payload_in, "/a/quake_sensor");
+    OCRepPayloadSetPropInt(payload_in, "scale", 4);
+
+    size_t dimensions_in[MAX_REP_ARRAY_DEPTH] = { 3, 0, 0};
+    uint8_t binval1[] = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19};
+    uint8_t binval2[] = {0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29};
+    uint8_t binval3[] = {0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39};
+
+    OCByteString quakedata_in[3] = {{binval1, sizeof(binval1)},
+                                    {binval2, sizeof(binval2)},
+                                    {binval3, sizeof(binval3)}};
+
+    EXPECT_EQ(true, OCRepPayloadSetByteStringArray(payload_in, "quakedata",
+                quakedata_in, dimensions_in));
+
+    // Convert OCPayload to CBOR
+    uint8_t *payload_cbor = NULL;
+    size_t payload_cbor_size = 0;
+    EXPECT_EQ(OC_STACK_OK, OCConvertPayload((OCPayload*) payload_in, &payload_cbor, &payload_cbor_size));
+#ifdef CBOR_BIN_STRING_DEBUG
+    FILE *fp = fopen("binstringarr.cbor", "wb+");
+    if (fp)
+    {
+        fwrite(payload_cbor, 1, payload_cbor_size, fp);
+        fclose(fp);
+    }
+#endif //CBOR_BIN_STRING_DEBUG
+
+    // Parse CBOR back to OCPayload
+    OCPayload* payload_out = NULL;
+    EXPECT_EQ(OC_STACK_OK, OCParsePayload(&payload_out, PAYLOAD_TYPE_REPRESENTATION,
+                payload_cbor, payload_cbor_size));
+
+    OCByteString* quakedata_out = NULL;
+    size_t dimensions_out[MAX_REP_ARRAY_DEPTH] = {0};
+    ASSERT_EQ(true, OCRepPayloadGetByteStringArray((OCRepPayload*)payload_out, "quakedata",
+                &quakedata_out, dimensions_out));
+
+    for(size_t i = 0; i < dimensions_in[0]; i++)
+    {
+        EXPECT_EQ(quakedata_in[i].len, quakedata_out[i].len);
+        EXPECT_EQ(0, memcmp(quakedata_in[i].bytes, quakedata_out[i].bytes, quakedata_in[i].len));
+    }
+
+    // Cleanup
+    OICFree(payload_cbor);
+    for(size_t i = 0; i < dimensions_out[0]; i++)
+    {
+        OICFree(quakedata_out[i].bytes);
+    }
+    OICFree(quakedata_out);
+
+    OCPayloadDestroy((OCPayload*)payload_out);
+}