From 2413b768249ae6c298529d1519ef7dcf8f9a6772 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/ocpayload.h | 79 +++++++++ resource/csdk/stack/include/octypes.h | 19 +++ resource/csdk/stack/include/payload_logging.h | 10 ++ resource/csdk/stack/src/ocpayload.c | 187 ++++++++++++++++++++++ resource/csdk/stack/src/ocpayloadconvert.c | 15 ++ resource/csdk/stack/src/ocpayloadparse.c | 40 +++++ resource/csdk/stack/test/SConscript | 3 +- resource/csdk/stack/test/cbortests.cpp | 222 ++++++++++++++++++++++++++ 8 files changed, 574 insertions(+), 1 deletion(-) create mode 100644 resource/csdk/stack/test/cbortests.cpp diff --git a/resource/csdk/stack/include/ocpayload.h b/resource/csdk/stack/include/ocpayload.h index d645d2b..66c086e 100755 --- a/resource/csdk/stack/include/ocpayload.h +++ b/resource/csdk/stack/include/ocpayload.h @@ -62,6 +62,43 @@ bool OCRepPayloadGetPropInt(const OCRepPayload* payload, const char* name, int64 bool OCRepPayloadSetPropDouble(OCRepPayload* payload, const char* name, double value); bool OCRepPayloadGetPropDouble(const OCRepPayload* payload, const char* name, double* value); +/** + * This function allocates memory for the byte string and sets it in the payload. + * + * @param payload Pointer to the payload to which byte string needs to be added. + * @param name Name of the byte string. + * @param value Byte string and it's length. + * + * @return true on success, false upon failure. + */ +bool OCRepPayloadSetPropByteString(OCRepPayload* payload, const char* name, OCByteString value); + +/** + * This function sets the byte string in the payload. + * + * @param payload Pointer to the payload to which byte string needs to be added. + * @param name Name of the byte string. + * @param value Byte string and it's length. + * + * @return true on success, false upon failure. + */ +bool OCRepPayloadSetPropByteStringAsOwner(OCRepPayload* payload, const char* name, + OCByteString* value); + +/** + * This function gets the byte string from the payload. + * + * @param payload Pointer to the payload from which byte string needs to be retrieved. + * @param name Name of the byte string. + * @param value Byte string and it's length. + * + * @note: Caller needs to invoke OCFree on value.bytes after it is finished using the byte string. + * + * @return true on success, false upon failure. + */ +bool OCRepPayloadGetPropByteString(const OCRepPayload* payload, const char* name, + OCByteString* value); + bool OCRepPayloadSetPropString(OCRepPayload* payload, const char* name, const char* value); bool OCRepPayloadSetPropStringAsOwner(OCRepPayload* payload, const char* name, char* value); bool OCRepPayloadGetPropString(const OCRepPayload* payload, const char* name, char** value); @@ -74,6 +111,48 @@ bool OCRepPayloadSetPropObjectAsOwner(OCRepPayload* payload, const char* name, OCRepPayload* value); bool OCRepPayloadGetPropObject(const OCRepPayload* payload, const char* name, OCRepPayload** value); +/** + * This function allocates memory for the byte string array and sets it in the payload. + * + * @param payload Pointer to the payload to which byte string array needs to be added. + * @param name Name of the byte string. + * @param array Byte string array. + * @param dimensions Number of byte strings in above array. + * + * @return true on success, false upon failure. + */ +bool OCRepPayloadSetByteStringArrayAsOwner(OCRepPayload* payload, const char* name, + OCByteString* array, size_t dimensions[MAX_REP_ARRAY_DEPTH]); + +/** + * This function sets the byte string array in the payload. + * + * @param payload Pointer to the payload to which byte string array needs to be added. + * @param name Name of the byte string. + * @param array Byte string array. + * @param dimensions Number of byte strings in above array. + * + * @return true on success, false upon failure. + */ +bool OCRepPayloadSetByteStringArray(OCRepPayload* payload, const char* name, + const OCByteString* array, size_t dimensions[MAX_REP_ARRAY_DEPTH]); + +/** + * This function gets the byte string array from the payload. + * + * @param payload Pointer to the payload from which byte string array needs to be retrieved. + * @param name Name of the byte string array. + * @param value Byte string array. + * @param dimensions Number of byte strings in above array. + * + * @note: Caller needs to invoke OICFree on 'bytes' field of all array elements after it is + * finished using the byte string array. + * + * @return true on success, false upon failure. + */ +bool OCRepPayloadGetByteStringArray(const OCRepPayload* payload, const char* name, + OCByteString** array, size_t dimensions[MAX_REP_ARRAY_DEPTH]); + bool OCRepPayloadSetIntArrayAsOwner(OCRepPayload* payload, const char* name, int64_t* array, size_t dimensions[MAX_REP_ARRAY_DEPTH]); bool OCRepPayloadSetIntArray(OCRepPayload* payload, const char* name, diff --git a/resource/csdk/stack/include/octypes.h b/resource/csdk/stack/include/octypes.h index 63c9c23..9c15e35 100644 --- a/resource/csdk/stack/include/octypes.h +++ b/resource/csdk/stack/include/octypes.h @@ -943,10 +943,21 @@ typedef enum OCREP_PROP_DOUBLE, OCREP_PROP_BOOL, OCREP_PROP_STRING, + OCREP_PROP_BYTE_STRING, OCREP_PROP_OBJECT, OCREP_PROP_ARRAY }OCRepPayloadPropType; +/** This structure will be used to represent a binary string for CBOR payloads.*/ +typedef struct +{ + /** pointer to data bytes.*/ + uint8_t* bytes; + + /** number of data bytes.*/ + size_t len; +} OCByteString; + #define MAX_REP_ARRAY_DEPTH 3 typedef struct { @@ -959,6 +970,10 @@ typedef struct double* dArray; bool* bArray; char** strArray; + + /** pointer to ByteString array.*/ + OCByteString* ocByteStrArray; + struct OCRepPayload** objArray; }; } OCRepPayloadValueArray; @@ -973,6 +988,10 @@ typedef struct OCRepPayloadValue double d; bool b; char* str; + + /** ByteString object.*/ + OCByteString ocByteStr; + struct OCRepPayload* obj; OCRepPayloadValueArray arr; }; diff --git a/resource/csdk/stack/include/payload_logging.h b/resource/csdk/stack/include/payload_logging.h index 3938c17..2c181cd 100644 --- a/resource/csdk/stack/include/payload_logging.h +++ b/resource/csdk/stack/include/payload_logging.h @@ -94,6 +94,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! @@ -126,6 +130,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):%zu x %zu x %zu", val->name, diff --git a/resource/csdk/stack/src/ocpayload.c b/resource/csdk/stack/src/ocpayload.c index cf7d754..43694bb 100755 --- a/resource/csdk/stack/src/ocpayload.c +++ b/resource/csdk/stack/src/ocpayload.c @@ -197,6 +197,10 @@ static void OCFreeRepPayloadValueContents(OCRepPayloadValue* val) { OICFree(val->str); } + else if(val->type == OCREP_PROP_BYTE_STRING) + { + OICFree(val->ocByteStr.bytes); + } else if (val->type == OCREP_PROP_OBJECT) { OCRepPayloadDestroy(val->obj); @@ -220,6 +224,13 @@ static void OCFreeRepPayloadValueContents(OCRepPayloadValue* val) } OICFree(val->arr.strArray); break; + case OCREP_PROP_BYTE_STRING: + for (size_t i = 0; i< dimTotal; ++i) + { + OICFree(val->arr.ocByteStrArray[i].bytes); + } + OICFree(val->arr.ocByteStrArray); + break; case OCREP_PROP_OBJECT: for(size_t i = 0; i< dimTotal;++i) { @@ -479,6 +490,9 @@ static bool OCRepPayloadSetProp(OCRepPayload* payload, const char* name, case OCREP_PROP_STRING: val->str = (char*)value; return val->str != NULL; + case OCREP_PROP_BYTE_STRING: + val->ocByteStr = *(OCByteString*)value; + break; case OCREP_PROP_NULL: return val != NULL; case OCREP_PROP_ARRAY: @@ -562,6 +576,62 @@ bool OCRepPayloadGetPropString(const OCRepPayload* payload, const char* name, ch return *value != NULL; } +bool OCRepPayloadSetPropByteString(OCRepPayload* payload, const char* name, OCByteString value) +{ + if (!value.bytes || !value.len) + { + return false; + } + + OCByteString ocByteStr = { + .bytes = (uint8_t*)OICMalloc(value.len * sizeof(uint8_t)), + .len = value.len }; + + if(!ocByteStr.bytes) + { + return false; + } + memcpy(ocByteStr.bytes, value.bytes, ocByteStr.len); + + bool b = OCRepPayloadSetPropByteStringAsOwner(payload, name, &ocByteStr); + + if(!b) + { + OICFree(ocByteStr.bytes); + } + return b; +} + +bool OCRepPayloadSetPropByteStringAsOwner(OCRepPayload* payload, const char* name, OCByteString* value) +{ + return OCRepPayloadSetProp(payload, name, value, OCREP_PROP_BYTE_STRING); +} + +bool OCRepPayloadGetPropByteString(const OCRepPayload* payload, const char* name, OCByteString* value) +{ + OCRepPayloadValue* val = OCRepPayloadFindValue(payload, name); + + if (!val || val->type != OCREP_PROP_BYTE_STRING) + { + return false; + } + + if (!value) + { + return false; + } + + value->bytes = (uint8_t*)OICMalloc(val->ocByteStr.len * sizeof(uint8_t)); + if (!value->bytes) + { + return false; + } + value->len = val->ocByteStr.len; + memcpy(value->bytes, val->ocByteStr.bytes, value->len); + + return true; +} + bool OCRepPayloadSetPropBool(OCRepPayload* payload, const char* name, bool value) { @@ -626,6 +696,123 @@ size_t calcDimTotal(const size_t dimensions[MAX_REP_ARRAY_DEPTH]) return total; } + +bool OCRepPayloadSetByteStringArrayAsOwner(OCRepPayload* payload, const char* name, + OCByteString* array, size_t dimensions[MAX_REP_ARRAY_DEPTH]) +{ + OCRepPayloadValue* val = OCRepPayloadFindAndSetValue(payload, name, OCREP_PROP_ARRAY); + + if (!val) + { + return false; + } + + val->arr.type = OCREP_PROP_BYTE_STRING; + memcpy(val->arr.dimensions, dimensions, MAX_REP_ARRAY_DEPTH * sizeof(size_t)); + val->arr.ocByteStrArray = array; + + return true; +} + +bool OCRepPayloadSetByteStringArray(OCRepPayload* payload, const char* name, + const OCByteString* array, size_t dimensions[MAX_REP_ARRAY_DEPTH]) +{ + if (!array) + { + return NULL; + } + + size_t dimTotal = calcDimTotal(dimensions); + if (dimTotal == 0) + { + return false; + } + + OCByteString* newArray = (OCByteString*)OICCalloc(dimTotal, sizeof(OCByteString)); + + if (!newArray) + { + return false; + } + + for (size_t i = 0; i < dimTotal; ++i) + { + newArray[i].bytes = (uint8_t*)OICMalloc(array[i].len * sizeof(uint8_t)); + if (NULL == newArray[i].bytes) + { + for (size_t j = 0; j < i; ++j) + { + OICFree(newArray[j].bytes); + } + + OICFree(newArray); + return false; + } + newArray[i].len = array[i].len; + memcpy(newArray[i].bytes, array[i].bytes, newArray[i].len); + } + + bool b = OCRepPayloadSetByteStringArrayAsOwner(payload, name, newArray, dimensions); + if (!b) + { + for (size_t i = 0; i < dimTotal; ++i) + { + OICFree(newArray[i].bytes); + } + + OICFree(newArray); + } + return b; +} + +bool OCRepPayloadGetByteStringArray(const OCRepPayload* payload, const char* name, + OCByteString** array, size_t dimensions[MAX_REP_ARRAY_DEPTH]) +{ + OCRepPayloadValue* val = OCRepPayloadFindValue(payload, name); + + if (!val || val->type != OCREP_PROP_ARRAY || val->arr.type != OCREP_PROP_BYTE_STRING + || !val->arr.ocByteStrArray) + { + return false; + } + + size_t dimTotal = calcDimTotal(val->arr.dimensions); + if (dimTotal == 0) + { + return false; + } + + *array = (OCByteString*)OICCalloc(dimTotal, sizeof(OCByteString)); + if (!*array) + { + return false; + } + + for (size_t i = 0; i < dimTotal; ++i) + { + OCByteString* tmp = &(*array)[i]; + tmp->bytes = (uint8_t*)OICMalloc(val->arr.ocByteStrArray[i].len * sizeof(uint8_t)); + if (NULL == tmp->bytes) + { + for (size_t j = 0; j < i; ++j) + { + OCByteString* tmp = &(*array)[j]; + OICFree(tmp->bytes); + } + OICFree(*array); + *array = NULL; + + return false; + } + tmp->len = val->arr.ocByteStrArray[i].len; + memcpy(tmp->bytes, val->arr.ocByteStrArray[i].bytes, tmp->len); + } + + memcpy(dimensions, val->arr.dimensions, MAX_REP_ARRAY_DEPTH * sizeof(size_t)); + return true; +} + + bool OCRepPayloadSetIntArrayAsOwner(OCRepPayload* payload, const char* name, int64_t* array, size_t dimensions[MAX_REP_ARRAY_DEPTH]) { diff --git a/resource/csdk/stack/src/ocpayloadconvert.c b/resource/csdk/stack/src/ocpayloadconvert.c index 062df00..4e80fc0 100644 --- a/resource/csdk/stack/src/ocpayloadconvert.c +++ b/resource/csdk/stack/src/ocpayloadconvert.c @@ -578,6 +578,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]) { @@ -735,6 +746,10 @@ static int64_t OCConvertSingleRepPayload(CborEncoder* parent, const OCRepPayload err = err | cbor_encode_text_string(&repMap, value->str, strlen(value->str)); break; + case OCREP_PROP_BYTE_STRING: + err = err | cbor_encode_byte_string(&repMap, + value->ocByteStr.bytes, value->ocByteStr.len); + break; case OCREP_PROP_OBJECT: err = err | OCConvertSingleRepPayload(&repMap, value->obj); break; diff --git a/resource/csdk/stack/src/ocpayloadparse.c b/resource/csdk/stack/src/ocpayloadparse.c index 94dece9..e33dda4 100644 --- a/resource/csdk/stack/src/ocpayloadparse.c +++ b/resource/csdk/stack/src/ocpayloadparse.c @@ -723,6 +723,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: @@ -803,6 +805,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: @@ -828,6 +832,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; @@ -900,6 +905,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) { @@ -993,6 +1013,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)) { @@ -1111,6 +1142,7 @@ static bool OCParseSingleRepPayload(OCRepPayload** outPayload, CborValue* repPar int64_t intval = 0; bool boolval = false; char* strval = NULL; + uint8_t* bytestrval = NULL; double doubleval = 0; OCRepPayload* pl; @@ -1147,6 +1179,14 @@ static bool OCParseSingleRepPayload(OCRepPayload** outPayload, CborValue* repPar err = !OCRepPayloadSetPropStringAsOwner(curPayload, name, strval); } break; + case CborByteStringType: + err = err || cbor_value_dup_byte_string(&repMap, &bytestrval, &len, NULL); + if (!err) + { + OCByteString tmp = {.bytes = bytestrval, .len = len}; + err = !OCRepPayloadSetPropByteStringAsOwner(curPayload, name, &tmp); + } + break; case CborMapType: err = err || OCParseSingleRepPayload(&pl, &repMap); if (!err) diff --git a/resource/csdk/stack/test/SConscript b/resource/csdk/stack/test/SConscript index ade8f3f..6654610 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