From d38beaea16946810ff07a4bdd3b3bca67127d460 Mon Sep 17 00:00:00 2001 From: Sachin Agrawal Date: Thu, 8 Oct 2015 09:20:54 -0700 Subject: [PATCH] Add ByteString support in CBOR wrapper in OIC stack 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 Reviewed-on: https://gerrit.iotivity.org/gerrit/2337 Tested-by: jenkins-iotivity --- resource/csdk/stack/include/payload_logging.h | 10 ++ resource/csdk/stack/src/ocpayloadconvert.c | 11 ++ resource/csdk/stack/src/ocpayloadparse.c | 31 ++++ resource/csdk/stack/test/SConscript | 3 +- resource/csdk/stack/test/cbortests.cpp | 222 ++++++++++++++++++++++++++ 5 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 resource/csdk/stack/test/cbortests.cpp diff --git a/resource/csdk/stack/include/payload_logging.h b/resource/csdk/stack/include/payload_logging.h index 4c4dfbb..69cb45f 100644 --- a/resource/csdk/stack/include/payload_logging.h +++ b/resource/csdk/stack/include/payload_logging.h @@ -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, diff --git a/resource/csdk/stack/src/ocpayloadconvert.c b/resource/csdk/stack/src/ocpayloadconvert.c index 7b46b36..85a36ce 100644 --- a/resource/csdk/stack/src/ocpayloadconvert.c +++ b/resource/csdk/stack/src/ocpayloadconvert.c @@ -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]) { diff --git a/resource/csdk/stack/src/ocpayloadparse.c b/resource/csdk/stack/src/ocpayloadparse.c index 4dc71ee..c045e14 100644 --- a/resource/csdk/stack/src/ocpayloadparse.c +++ b/resource/csdk/stack/src/ocpayloadparse.c @@ -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)) { diff --git a/resource/csdk/stack/test/SConscript b/resource/csdk/stack/test/SConscript index d75d459..507f011 100644 --- a/resource/csdk/stack/test/SConscript +++ b/resource/csdk/stack/test/SConscript @@ -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 index 0000000..2e4a202 --- /dev/null +++ b/resource/csdk/stack/test/cbortests.cpp @@ -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 +#include +#include +#include +#include +#include + +//----------------------------------------------------------------------------- +// Includes +//----------------------------------------------------------------------------- +#include +#include + +#include +#include + +#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); +} -- 2.7.4