From 2c341d2da1492f469c64ddbed7c77620750723ff Mon Sep 17 00:00:00 2001 From: "Timothy Rule (VM/EMT3)" Date: Mon, 21 Oct 2024 12:21:41 +0200 Subject: [PATCH] Marshal library support for binary and string types. Signed-off-by: Timothy Rule (VM/EMT3) --- dse/clib/data/marshal.c | 211 +++++++++++++++-- dse/clib/data/marshal.h | 36 ++- tests/data/CMakeLists.txt | 1 + tests/data/test_marshal.c | 477 +++++++++++++++++++++++++++++++++++++- 4 files changed, 701 insertions(+), 24 deletions(-) diff --git a/dse/clib/data/marshal.c b/dse/clib/data/marshal.c index 4efd05d..390e49e 100644 --- a/dse/clib/data/marshal.c +++ b/dse/clib/data/marshal.c @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -23,6 +24,40 @@ typedef struct IndexItem { } IndexItem; +static char* _default_string_encode(const char* source, size_t len) +{ + if (source == NULL || len == 0) return NULL; + + size_t _len = strlen(source); + if (_len > (len - 1)) _len = len - 1; + if (_len) { + char* s = calloc(_len + 1, sizeof(char)); + memcpy(s, source, _len); + return s; + } else { + return NULL; + } +} + + +static char* _default_string_decode(const char* source, size_t* len) +{ + if (len == NULL) return NULL; + *len = 0; + + size_t _len = 0; + if (source) _len = strlen(source); + if (_len) { + char* s = calloc(_len + 1, sizeof(char)); + memcpy(s, source, _len); + *len = _len + 1; + return s; + } else { + return NULL; + } +} + + static inline void _marshal_scalar_out(MarshalGroup* mg) { for (size_t i = 0; i < mg->count; i++) { @@ -89,6 +124,96 @@ static inline void _marshal_scalar_in(MarshalGroup* mg) } +static inline void _marshal_binary_out(MarshalGroup* mg) +{ + for (size_t i = 0; i < mg->count; i++) { + switch (mg->type) { + case MARSHAL_TYPE_STRING: { + char* source = (char*)mg->source.binary[mg->source.offset + i]; + size_t source_len = mg->source.binary_len[mg->source.offset + i]; + char* target = (char*)mg->target._string[i]; + // Free previous allocated source (from this function). + free(target); + target = NULL; + // Decode the source string. + if (source && source_len) { + if (mg->functions.string_encode && + mg->functions.string_encode[i]) { + target = mg->functions.string_encode[i](source, source_len); + } else { + target = _default_string_encode(source, source_len); + } + } + mg->target._string[i] = target; + } break; + case MARSHAL_TYPE_BINARY: { + char* source = (char*)mg->source.binary[mg->source.offset + i]; + size_t source_len = mg->source.binary_len[mg->source.offset + i]; + char* target = (char*)mg->target._binary[i]; + size_t target_len = 0; + // Free previous allocated source (from this function). + free(target); + target = NULL; + // Allocate and copy to target. + if (source && source_len) { + target = malloc(source_len); + memcpy(target, source, source_len); + target_len = source_len; + } + mg->target._binary[i] = target; + mg->target._binary_len[i] = target_len; + } break; + default: + break; + } + } +} + + +static inline void _marshal_binary_in(MarshalGroup* mg) +{ + for (size_t i = 0; i < mg->count; i++) { + switch (mg->type) { + case MARSHAL_TYPE_STRING: { + char* source = (char*)mg->source.binary[mg->source.offset + i]; + size_t source_len = 0; + char* target = (char*)mg->target._string[i]; + // Free previous allocated source (from this function). + free(source); + source = NULL; + // Decode the target string. + if (mg->functions.string_decode && mg->functions.string_decode[i]) { + source = mg->functions.string_decode[i](target, &source_len); + } else { + source = _default_string_decode(target, &source_len); + } + mg->source.binary[mg->source.offset + i] = source; + mg->source.binary_len[mg->source.offset + i] = source_len; + } break; + case MARSHAL_TYPE_BINARY: { + char* source = (char*)mg->source.binary[mg->source.offset + i]; + size_t source_len = 0; + char* target = (char*)mg->target._binary[i]; + size_t target_len = mg->target._binary_len[i]; + // Free previous allocated source (from this function). + free(source); + source = NULL; + // Allocate and copy to source. + if (target && target_len) { + source = malloc(target_len); + memcpy(source, target, target_len); + source_len = target_len; + } + mg->source.binary[mg->source.offset + i] = source; + mg->source.binary_len[mg->source.offset + i] = source_len; + } break; + default: + break; + } + } +} + + /** marshal_type_size ================= @@ -127,6 +252,9 @@ size_t marshal_type_size(MarshalType type) case MARSHAL_TYPE_DOUBLE: case MARSHAL_TYPE_BYTE8: return 8; + case MARSHAL_TYPE_STRING: + case MARSHAL_TYPE_BINARY: + return 8; default: return 0; } @@ -151,7 +279,16 @@ void marshal_group_out(MarshalGroup* mg_table) case MARSHAL_DIRECTION_TXRX: case MARSHAL_DIRECTION_TXONLY: case MARSHAL_DIRECTION_PARAMETER: - if (mg->kind == MARSHAL_KIND_PRIMITIVE) _marshal_scalar_out(mg); + switch (mg->kind) { + case MARSHAL_KIND_PRIMITIVE: + _marshal_scalar_out(mg); + break; + case MARSHAL_KIND_BINARY: + _marshal_binary_out(mg); + break; + default: + break; + } break; default: continue; @@ -179,7 +316,16 @@ void marshal_group_in(MarshalGroup* mg_table) case MARSHAL_DIRECTION_RXONLY: case MARSHAL_DIRECTION_PARAMETER: case MARSHAL_DIRECTION_LOCAL: - if (mg->kind == MARSHAL_KIND_PRIMITIVE) _marshal_scalar_in(mg); + switch (mg->kind) { + case MARSHAL_KIND_PRIMITIVE: + _marshal_scalar_in(mg); + break; + case MARSHAL_KIND_BINARY: + _marshal_binary_in(mg); + break; + default: + break; + } break; default: continue; @@ -205,6 +351,9 @@ void marshal_group_destroy(MarshalGroup* mg_table) if (mg->name) free(mg->name); if (mg->target.ref) free(mg->target.ref); if (mg->target.ptr) free(mg->target.ptr); + if (mg->target._binary_len) free(mg->target._binary_len); + if (mg->functions.string_encode) free(mg->functions.string_encode); + if (mg->functions.string_decode) free(mg->functions.string_decode); } if (mg_table) free(mg_table); } @@ -252,11 +401,12 @@ MarshalSignalMap* marshal_generate_signalmap(MarshalMapSpec signal, /* If a set is provided, signal mappings are unique. */ if (ex_signals) { /* Match. */ - if (set_contains(ex_signals, signal.signal[i]) == SET_TRUE) { + if (set_contains(ex_signals, signal.signal[i]) == + SET_TRUE) { /* Signal already mapped. */ errno = -EINVAL; for (uint32_t i = 0; i < hashlist_length(&index_list); - i++) { + i++) { free(hashlist_at(&index_list, i)); } hashlist_destroy(&index_list); @@ -287,8 +437,12 @@ MarshalSignalMap* marshal_generate_signalmap(MarshalMapSpec signal, msm->count = count; if (is_binary) { - msm->signal.ncodec = signal.ncodec; - msm->source.ncodec = source.ncodec; + msm->signal.binary = signal.binary; + msm->signal.binary_len = signal.binary_len; + msm->signal.binary_buffer_size = signal.binary_buffer_size; + msm->source.binary = source.binary; + msm->source.binary_len = source.binary_len; + msm->source.binary_buffer_size = source.binary_buffer_size; msm->is_binary = true; } else { msm->signal.scalar = signal.scalar; @@ -317,6 +471,8 @@ marshal_signalmap_out Marshal a `MarshalSignalMap` outwards (towards the marshal target). + Signal -[marshal_signalmap_out()]-> Source -> Target + Parameters ---------- map (MarshalSignalMap*) @@ -326,15 +482,24 @@ void marshal_signalmap_out(MarshalSignalMap* map) { for (MarshalSignalMap* msm = map; msm && msm->name; msm++) { for (size_t i = 0; i < msm->count; i++) { + size_t sig_idx = msm->signal.index[i]; + size_t src_idx = msm->source.index[i]; + if (msm->is_binary) { - void* src_binary = *(msm->source.ncodec); - void* sig_binary = *(msm->signal.ncodec); - memcpy(src_binary, sig_binary, sizeof(void*)); + void** src_binary = (msm->source.binary); + uint32_t* src_binary_len = (msm->source.binary_len); + void** sig_binary = (msm->signal.binary); + uint32_t* sig_binary_len = (msm->signal.binary_len); + + // Reference (shallow copy) signal -> source. + // Note: Signal owns memory. + // Note: Source is managed in source-target marshal code. + src_binary[src_idx] = sig_binary[sig_idx]; + src_binary_len[src_idx] = sig_binary_len[sig_idx]; } else { double* src_scalar = *(msm->source.scalar); double* sig_scalar = *(msm->signal.scalar); - src_scalar[msm->source.index[i]] = - sig_scalar[msm->signal.index[i]]; + src_scalar[src_idx] = sig_scalar[sig_idx]; } } } @@ -347,6 +512,8 @@ marshal_signalmap_in Marshal a `MarshalGroup` inwards (from the marshal target). + Signal <-[marshal_signalmap_in()]- Source -> Target + Parameters ---------- map (MarshalSignalMap*) @@ -356,15 +523,27 @@ void marshal_signalmap_in(MarshalSignalMap* map) { for (MarshalSignalMap* msm = map; msm && msm->name; msm++) { for (size_t i = 0; i < msm->count; i++) { + size_t sig_idx = msm->signal.index[i]; + size_t src_idx = msm->source.index[i]; + if (msm->is_binary) { - void* src_binary = *(msm->source.ncodec); - void* sig_binary = *(msm->signal.ncodec); - memcpy(sig_binary, src_binary, sizeof(void*)); + void** src_binary = (msm->source.binary); + uint32_t* src_binary_len = (msm->source.binary_len); + void** sig_binary = (msm->signal.binary); + uint32_t* sig_binary_len = (msm->signal.binary_len); + uint32_t* sig_binary_buffer_size = + (msm->signal.binary_buffer_size); + + // Append (deep copy) source -> signal + // Note. Signal owns memory. + // Note: Source is managed in source-target marshal code. + dse_buffer_append(&sig_binary[sig_idx], + &sig_binary_len[sig_idx], &sig_binary_buffer_size[sig_idx], + src_binary[src_idx], src_binary_len[src_idx]); } else { double* src_scalar = *(msm->source.scalar); double* sig_scalar = *(msm->signal.scalar); - sig_scalar[msm->signal.index[i]] = - src_scalar[msm->source.index[i]]; + sig_scalar[sig_idx] = src_scalar[src_idx]; } } } diff --git a/dse/clib/data/marshal.h b/dse/clib/data/marshal.h index da77a01..3a23b56 100644 --- a/dse/clib/data/marshal.h +++ b/dse/clib/data/marshal.h @@ -60,12 +60,16 @@ center footer Dynamic Simulation Environment */ +typedef char* (*MarshalStringEncode)(const char* source, size_t len); +typedef char* (*MarshalStringDecode)(const char* source, size_t* len); + + typedef enum MarshalKind { MARSHAL_KIND_NONE = 0, MARSHAL_KIND_PRIMITIVE, + MARSHAL_KIND_BINARY, MARSHAL_KIND_ARRAY, MARSHAL_KIND_STRUCT, - MARSHAL_KIND_BINARY, __MARSHAL_KIND_SIZE__, } MarshalKind; @@ -107,6 +111,9 @@ typedef enum MarshalType { MARSHAL_TYPE_BYTE8, /* Imaginary types. */ MARSHAL_TYPE_BOOL, + /* Binary types. */ + MARSHAL_TYPE_STRING, + MARSHAL_TYPE_BINARY, __MARSHAL_TYPE_SIZE__, } MarshalType; @@ -126,9 +133,12 @@ typedef struct MarshalGroup { int32_t* _int32; uint64_t* _uint64; double* _double; + char** _string; + void** _binary; /* Pointer member (for calls to free()). */ void* ptr; }; + uint32_t* _binary_len; } target; /* Marshal Source. */ struct { @@ -137,8 +147,18 @@ typedef struct MarshalGroup { union { /* (reference, allocated elsewhere) */ double* scalar; + void** binary; }; + /* (reference, allocated elsewhere) */ + uint32_t* binary_len; + uint32_t* binary_buffer_size; } source; + /* Marshal supporting functions. */ + struct { + /* (allocated with 'count' elements, access as array) */ + MarshalStringEncode* string_encode; + MarshalStringDecode* string_decode; + } functions; } MarshalGroup; @@ -166,6 +186,8 @@ typedef struct MarshalStruct { double* scalar; void** binary; // ?? or codec object or sv object. }; + uint32_t* binary_len; + uint32_t* binary_buffer_size; } source; } MarshalStruct; @@ -177,8 +199,10 @@ typedef struct MarshalMapSpec { const char** signal; union { double** scalar; - void** ncodec; + void** binary; }; + uint32_t* binary_len; + uint32_t* binary_buffer_size; } MarshalMapSpec; @@ -193,8 +217,10 @@ typedef struct MarshalSignalMap { union { /* (reference, allocated elsewhere) */ double** scalar; - void** ncodec; + void** binary; }; + uint32_t* binary_len; + uint32_t* binary_buffer_size; } signal; /* Marshal Source (represents Target). */ struct { @@ -203,8 +229,10 @@ typedef struct MarshalSignalMap { union { /* (reference, allocated elsewhere) */ double** scalar; - void** ncodec; + void** binary; }; + uint32_t* binary_len; + uint32_t* binary_buffer_size; } source; } MarshalSignalMap; diff --git a/tests/data/CMakeLists.txt b/tests/data/CMakeLists.txt index f48ed27..447071a 100644 --- a/tests/data/CMakeLists.txt +++ b/tests/data/CMakeLists.txt @@ -10,6 +10,7 @@ add_executable(test_data ${DSE_CLIB_SOURCE_DIR}/collections/hashmap.c ${DSE_CLIB_SOURCE_DIR}/collections/set.c ${DSE_CLIB_SOURCE_DIR}/data/marshal.c + ${DSE_CLIB_SOURCE_DIR}/util/binary.c ) target_include_directories(test_data PRIVATE diff --git a/tests/data/test_marshal.c b/tests/data/test_marshal.c index 2707b37..bed4425 100644 --- a/tests/data/test_marshal.c +++ b/tests/data/test_marshal.c @@ -57,6 +57,9 @@ void test_marshal__type_size(void** state) { .type = MARSHAL_TYPE_BOOL, .size = 4 }, + { .type = MARSHAL_TYPE_STRING, .size = 8 }, + { .type = MARSHAL_TYPE_BINARY, .size = 8 }, + { .type = MARSHAL_TYPE_NONE, .size = 0 }, { .type = __MARSHAL_TYPE_SIZE__, .size = 0 }, { .type = __MARSHAL_TYPE_SIZE__ + 100, .size = 0 }, @@ -86,6 +89,7 @@ void test_marshal_group__primitive(void** state) { UNUSED(state); + // clang-format off MGKP_TC tc[] = { { .type = MARSHAL_TYPE_INT32, @@ -109,6 +113,7 @@ void test_marshal_group__primitive(void** state) ._int32 = { -5, 0, 5, 9 }, // Note that BOOL is alias of int32. }, }; + // clang-format on MarshalGroup* mg_table = calloc(ARRAY_SIZE(tc) + 1, sizeof(MarshalGroup)); for (size_t i = 0; i < ARRAY_SIZE(tc); i++) { MGKP_TC* t = &tc[i]; @@ -229,6 +234,291 @@ void test_marshal_group__primitive(void** state) } +static char* _string_encode(const char* source, size_t len) +{ + if (source == NULL || len == 0) return NULL; + + size_t _len = strlen(source); + if (_len > (len - 1)) _len = len - 1; + if (_len) { + char* s = calloc(_len + 1, sizeof(char)); + for (size_t i = 0; i < _len; i++) { + s[i] = source[_len - i - 1]; + } + return s; + } else { + return NULL; + } +} + +static char* _string_decode(const char* source, size_t* len) +{ + if (len == NULL) return NULL; + *len = 0; + + size_t _len = 0; + if (source) _len = strlen(source); + if (_len) { + char* s = calloc(_len + 1, sizeof(char)); + for (size_t i = 0; i < _len; i++) { + s[i] = source[_len - i - 1]; + } + *len = _len + 1; + return s; + } else { + return NULL; + } +} + +typedef struct MGKB_TC { + MarshalType type; + size_t offset; + size_t count; + struct { + struct { + // Test condition (copied to source, marshal to target). + const void* binary[10]; + uint32_t binary_len[10]; + } source; + struct { + // Test condition (copied to target, marshal to source). + const char* string[10]; + const void* binary[10]; + uint32_t binary_len[10]; + } target; + } condition; + struct { + struct { + void* binary[10]; + uint32_t binary_len[10]; + } source; + struct { + MarshalStringEncode string_encode[10]; + MarshalStringDecode string_decode[10]; + } target; + } storage; +} MGKB_TC; + +void test_marshal_group__binary(void** state) +{ + UNUSED(state); + + // clang-format off + MGKB_TC tc[] = { + { + .type = MARSHAL_TYPE_STRING, + .offset = 1, + .count = 3, + .condition.source.binary = { "foo", "bar", "fubar" }, + .condition.source.binary_len = { 4, 4, 6 }, + .condition.target.string = { "foo", "rab", "fubar" }, + .storage.target.string_encode = { NULL, _string_encode, NULL }, + .storage.target.string_decode = { NULL, _string_decode, NULL }, + }, + { + .type = MARSHAL_TYPE_BINARY, + .offset = 1, + .count = 3, + .condition.source.binary = { "foo", "foo\0bar", "fubar" }, + .condition.source.binary_len = { 4, 8, 6 }, + .condition.target.binary = { "foo", "foo\0bar", "fubar" }, + .condition.target.binary_len = { 4, 8, 6 }, + .storage.target.string_encode = { NULL, NULL, NULL }, + .storage.target.string_decode = { NULL, NULL, NULL }, + }, + }; + // clang-format on + MarshalGroup* mg_table = calloc(ARRAY_SIZE(tc) + 1, sizeof(MarshalGroup)); + for (size_t i = 0; i < ARRAY_SIZE(tc); i++) { + MGKB_TC* t = &tc[i]; + MarshalGroup* mg = &mg_table[i]; + + mg->name = strdup("MG"); + mg->kind = MARSHAL_KIND_BINARY; + mg->type = t->type; + mg->count = t->count; + mg->source.offset = t->offset; + mg->source.binary = t->storage.source.binary; + mg->source.binary_len = t->storage.source.binary_len; + mg->target.ptr = calloc(t->count, sizeof(void*)); + mg->target._binary_len = calloc(t->count, sizeof(uint32_t)); + mg->functions.string_encode = + calloc(t->count, sizeof(MarshalStringEncode)); + mg->functions.string_decode = + calloc(t->count, sizeof(MarshalStringDecode)); + } + for (MarshalDir dir = MARSHAL_DIRECTION_NONE; + dir < __MARSHAL_DIRECTION_SIZE__; dir++) { + log_trace("Direction %d", dir); + + /* Group OUT ... */ + for (size_t i = 0; i < ARRAY_SIZE(tc); i++) { + MGKB_TC* t = &tc[i]; + MarshalGroup* mg = &mg_table[i]; + mg->dir = dir; + for (size_t i = 0; i < t->count; i++) { + mg->source.binary[t->offset + i] = + malloc(t->condition.source.binary_len[i]); + memcpy(mg->source.binary[t->offset + i], + t->condition.source.binary[i], + t->condition.source.binary_len[i]); + mg->source.binary_len[t->offset + i] = + t->condition.source.binary_len[i]; + mg->target._string[i] = NULL; + mg->target._binary[i] = NULL; + mg->target._binary_len[i] = 0; + mg->functions.string_encode[i] = + t->storage.target.string_encode[i]; + mg->functions.string_decode[i] = + t->storage.target.string_decode[i]; + } + } + marshal_group_out(mg_table); + for (size_t i = 0; i < ARRAY_SIZE(tc); i++) { + MGKB_TC* t = &tc[i]; + MarshalGroup* mg = &mg_table[i]; + log_trace("Type %d", mg->type); + + for (size_t i = 0; i < t->count; i++) { + switch (mg->dir) { + case MARSHAL_DIRECTION_TXRX: + case MARSHAL_DIRECTION_TXONLY: + case MARSHAL_DIRECTION_PARAMETER: + switch (mg->type) { + case MARSHAL_TYPE_STRING: + log_trace("index %d: condition %s -> %s", i, + mg->source.binary[t->offset + i], + t->condition.target.string[i]); + assert_non_null(mg->target._string[i]); + assert_non_null(t->condition.target.string[i]); + assert_string_equal(mg->target._string[i], + t->condition.target.string[i]); + assert_int_equal(mg->target._binary_len[i], 0); + break; + case MARSHAL_TYPE_BINARY: + log_trace("index %d: condition %s -> %s (%d)", i, + mg->source.binary[t->offset + i], + t->condition.target.binary[i], + t->condition.target.binary_len[i]); + assert_non_null(mg->target._binary[i]); + assert_non_null(t->condition.target.binary[i]); + assert_memory_equal(mg->target._binary[i], + t->condition.target.binary[i], + t->condition.target.binary_len[i]); + assert_int_equal(mg->target._binary_len[i], + t->condition.target.binary_len[i]); + break; + default: + fail_msg("unsupported type"); + } + break; + default: + assert_null(mg->target._string[i]); + assert_null(mg->target._binary[i]); + assert_int_equal(mg->target._binary_len[i], 0); + } + } + } + for (size_t i = 0; i < ARRAY_SIZE(tc); i++) { + MGKB_TC* t = &tc[i]; + MarshalGroup* mg = &mg_table[i]; + for (size_t i = 0; i < t->count; i++) { + free(mg->target._binary[i]); + mg->target._binary[i] = NULL; + mg->target._binary_len[i] = 0; + free(mg->source.binary[t->offset + i]); + mg->source.binary[t->offset + i] = NULL; + mg->source.binary_len[t->offset + i] = 0; + } + } + + /* Group IN ... */ + for (size_t i = 0; i < ARRAY_SIZE(tc); i++) { + MGKB_TC* t = &tc[i]; + MarshalGroup* mg = &mg_table[i]; + mg->dir = dir; + for (size_t i = 0; i < t->count; i++) { + switch (mg->type) { + case MARSHAL_TYPE_STRING: + mg->target._string[i] = + strdup(t->condition.target.string[i]); + mg->target._binary_len[i] = 0; + break; + case MARSHAL_TYPE_BINARY: + mg->target._binary[i] = + strdup(t->condition.target.binary[i]); + mg->target._binary_len[i] = + t->condition.target.binary_len[i]; + break; + default: + fail_msg("unsupported type"); + } + mg->functions.string_encode[i] = + t->storage.target.string_encode[i]; + mg->functions.string_decode[i] = + t->storage.target.string_decode[i]; + } + } + marshal_group_in(mg_table); + for (size_t i = 0; i < ARRAY_SIZE(tc); i++) { + MGKB_TC* t = &tc[i]; + MarshalGroup* mg = &mg_table[i]; + + for (size_t i = 0; i < t->count; i++) { + switch (mg->dir) { + case MARSHAL_DIRECTION_TXRX: + case MARSHAL_DIRECTION_RXONLY: + case MARSHAL_DIRECTION_PARAMETER: + case MARSHAL_DIRECTION_LOCAL: + switch (mg->type) { + case MARSHAL_TYPE_STRING: + log_trace("index %d: condition %s <- %s", i, + mg->source.binary[t->offset + i], + t->condition.target.string[i]); + assert_non_null(mg->source.binary[t->offset + i]); + assert_non_null(t->condition.source.binary[i]); + assert_string_equal(mg->source.binary[t->offset + i], + t->condition.source.binary[i]); + assert_int_equal(mg->source.binary_len[t->offset + i], + strlen(t->condition.target.string[i]) + 1); + break; + case MARSHAL_TYPE_BINARY: + log_trace("index %d: condition %s <- %s (%d)", i, + mg->source.binary[t->offset + i], + t->condition.target.binary[i], + t->condition.target.binary_len[i]); + assert_non_null(mg->source.binary[t->offset + i]); + assert_non_null(t->condition.source.binary[i]); + assert_string_equal(mg->source.binary[t->offset + i], + t->condition.source.binary[i]); + assert_int_equal(mg->source.binary_len[t->offset + i], + t->condition.target.binary_len[i]); + break; + + default: + fail_msg("unsupported type"); + } + break; + default: + assert_null(mg->source.binary[t->offset + i]); + assert_int_equal(mg->source.binary_len[t->offset + i], 0); + } + } + } + for (size_t i = 0; i < ARRAY_SIZE(tc); i++) { + MGKB_TC* t = &tc[i]; + MarshalGroup* mg = &mg_table[i]; + for (size_t i = 0; i < t->count; i++) { + free(mg->source.binary[t->offset + i]); + free(mg->target._binary[i]); + } + } + } + + marshal_group_destroy(mg_table); +} + + typedef struct SM_TC { struct { const char* ex_signals[10]; @@ -444,9 +734,25 @@ typedef struct MSM_TC { size_t source_idx[10]; double source_scalar[10]; double expected[10]; + struct { + struct { + void* binary[10]; + uint32_t binary_len[10]; + uint32_t binary_buffer_size[10]; + } signal; + struct { + void* binary[10]; + uint32_t binary_len[10]; + } source; + struct { + void* binary[10]; + uint32_t binary_len[10]; + uint32_t binary_buffer_size[10]; + } expected; + } binary; } MSM_TC; -void test_marshal__signalmap_in(void** state) +void test_marshal__signalmap_scalar_out(void** state) { UNUSED(state); @@ -514,7 +820,7 @@ void test_marshal__signalmap_in(void** state) } -void test_marshal__signalmap_out(void** state) +void test_marshal__signalmap_scalar_in(void** state) { UNUSED(state); @@ -582,6 +888,162 @@ void test_marshal__signalmap_out(void** state) } +void test_marshal__signalmap_binary_out(void** state) +{ + UNUSED(state); + + MSM_TC tc[] = { + { + .name = "foo", + .count = 2, + .signal_idx = { 0, 1 }, + .source_idx = { 0, 1 }, + .binary.signal.binary = { strdup("foo"), strdup("bar") }, + .binary.signal.binary_len = { 4, 4 }, + .binary.signal.binary_buffer_size = { 4, 4 }, + .binary.source.binary = { NULL, NULL }, + .binary.source.binary_len = { 0, 0 }, + .binary.expected.binary = { strdup("foo"), strdup("bar") }, + .binary.expected.binary_len = { 4, 4 }, + }, + { + .name = "bar", + .count = 3, + .signal_idx = { 0, 1, 2 }, + .source_idx = { 0, 1, 2 }, + .binary.signal.binary = { strdup("foo"), strdup("fubar"), + strdup("bar") }, + .binary.signal.binary_len = { 4, 6, 4 }, + .binary.signal.binary_buffer_size = { 4, 6, 4 }, + .binary.source.binary = { NULL, NULL, NULL }, + .binary.source.binary_len = { 0, 0, 0 }, + .binary.expected.binary = { strdup("foo"), strdup("fubar"), + strdup("bar") }, + .binary.expected.binary_len = { 4, 6, 4 }, + }, + }; + + /* Check every test case. */ + for (size_t i = 0; i < ARRAY_SIZE(tc); i++) { + // Setup the MSM structure (NTL). + MarshalSignalMap* msm = calloc(2, sizeof(MarshalSignalMap)); + msm[0].name = (char*)tc[i].name; + msm[0].count = tc[i].count; + msm[0].is_binary = true; + msm[0].signal.index = tc[i].signal_idx; + msm[0].signal.binary = (void**)&tc[i].binary.signal.binary; + msm[0].signal.binary_len = (uint32_t*)&tc[i].binary.signal.binary_len; + msm[0].signal.binary_buffer_size = + (uint32_t*)&tc[i].binary.signal.binary_buffer_size; + msm[0].source.index = tc[i].source_idx; + msm[0].source.binary = (void**)&tc[i].binary.source.binary; + msm[0].source.binary_len = (uint32_t*)&tc[i].binary.source.binary_len; + for (size_t j = 0; j < msm[0].count; j++) { + assert_ptr_equal(tc[i].binary.source.binary[j], NULL); + } + + // Marshal and check results: signal -> source + // (reference only, no copy). + marshal_signalmap_out(msm); + for (size_t j = 0; j < msm[0].count; j++) { + assert_int_equal(tc[i].binary.expected.binary_len[j], + msm[0].source.binary_len[j]); + assert_ptr_equal(tc[i].binary.signal.binary[j], + tc[i].binary.source.binary[j]); // Reference copy. + assert_memory_equal(tc[i].binary.expected.binary[j], + msm[0].source.binary[j], tc[i].binary.expected.binary_len[j]); + } + + /* Cleanup. */ + for (size_t j = 0; j < msm[0].count; j++) { + free(tc[i].binary.signal.binary[j]); + free(tc[i].binary.expected.binary[j]); + } + free(msm); + } +} + + +void test_marshal__signalmap_binary_in(void** state) +{ + UNUSED(state); + + MSM_TC tc[] = { + { + .name = "foo", + .count = 2, + .signal_idx = { 0, 1 }, + .source_idx = { 0, 1 }, + .binary.signal.binary = { NULL, NULL }, + .binary.signal.binary_len = { 0, 0 }, + .binary.signal.binary_buffer_size = { 0, 0 }, + .binary.source.binary = { strdup("foo"), strdup("bar") }, + .binary.source.binary_len = { 4, 4 }, + .binary.expected.binary = { strdup("foo"), strdup("bar") }, + .binary.expected.binary_len = { 4, 4 }, + .binary.expected.binary_buffer_size = { 4, 4 }, + }, + { + .name = "bar", + .count = 3, + .signal_idx = { 0, 1, 2 }, + .source_idx = { 0, 1, 2 }, + .binary.signal.binary = { NULL, NULL, NULL }, + .binary.signal.binary_len = { 0, 0, 0 }, + .binary.signal.binary_buffer_size = { 0, 0, 0 }, + .binary.source.binary = { strdup("foo"), strdup("fubar"), + strdup("bar") }, + .binary.source.binary_len = { 4, 6, 4 }, + .binary.expected.binary = { strdup("foo"), strdup("fubar"), + strdup("bar") }, + .binary.expected.binary_len = { 4, 6, 4 }, + .binary.expected.binary_buffer_size = { 4, 6, 4 }, + }, + }; + + /* Check every test case. */ + for (size_t i = 0; i < ARRAY_SIZE(tc); i++) { + // Setup the MSM structure (NTL). + MarshalSignalMap* msm = calloc(2, sizeof(MarshalSignalMap)); + msm[0].name = (char*)tc[i].name; + msm[0].count = tc[i].count; + msm[0].is_binary = true; + msm[0].signal.index = tc[i].signal_idx; + msm[0].signal.binary = (void**)&tc[i].binary.signal.binary; + msm[0].signal.binary_len = (uint32_t*)&tc[i].binary.signal.binary_len; + msm[0].signal.binary_buffer_size = + (uint32_t*)&tc[i].binary.signal.binary_buffer_size; + msm[0].source.index = tc[i].source_idx; + msm[0].source.binary = (void**)&tc[i].binary.source.binary; + msm[0].source.binary_len = (uint32_t*)&tc[i].binary.source.binary_len; + for (size_t j = 0; j < msm[0].count; j++) { + assert_ptr_equal(tc[i].binary.signal.binary[j], NULL); + } + + // Marshal and check results: source -> signal (append, copy). + marshal_signalmap_in(msm); + for (size_t j = 0; j < msm[0].count; j++) { + assert_int_equal(tc[i].binary.expected.binary_len[j], + msm[0].signal.binary_len[j]); + assert_int_equal(tc[i].binary.expected.binary_buffer_size[j], + msm[0].signal.binary_buffer_size[j]); + assert_ptr_not_equal(tc[i].binary.signal.binary[j], + tc[i].binary.source.binary[j]); // Append / Deep copy. + assert_memory_equal(tc[i].binary.expected.binary[j], + msm[0].signal.binary[j], tc[i].binary.expected.binary_len[j]); + } + + /* Cleanup. */ + for (size_t j = 0; j < msm[0].count; j++) { + free(tc[i].binary.signal.binary[j]); + free(tc[i].binary.source.binary[j]); + free(tc[i].binary.expected.binary[j]); + } + free(msm); + } +} + + int run_marshal_tests(void) { void* s = test_setup; @@ -590,9 +1052,16 @@ int run_marshal_tests(void) const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown(test_marshal__type_size, s, t), cmocka_unit_test_setup_teardown(test_marshal_group__primitive, s, t), + cmocka_unit_test_setup_teardown(test_marshal_group__binary, s, t), cmocka_unit_test_setup_teardown(test_marshal__signalmap_generate, s, t), - cmocka_unit_test_setup_teardown(test_marshal__signalmap_in, s, t), - cmocka_unit_test_setup_teardown(test_marshal__signalmap_out, s, t), + cmocka_unit_test_setup_teardown( + test_marshal__signalmap_scalar_out, s, t), + cmocka_unit_test_setup_teardown( + test_marshal__signalmap_scalar_in, s, t), + cmocka_unit_test_setup_teardown( + test_marshal__signalmap_binary_out, s, t), + cmocka_unit_test_setup_teardown( + test_marshal__signalmap_binary_in, s, t), }; return cmocka_run_group_tests_name("MARSHAL", tests, NULL, NULL);