Browse Source

towards writing of ranges

Julius Pfrommer 10 years ago
parent
commit
bb5e53dcf5
4 changed files with 199 additions and 96 deletions
  1. 19 6
      examples/server_simple.c
  2. 40 16
      include/ua_types.h
  3. 139 74
      src/ua_types.c
  4. 1 0
      tests/CMakeLists.txt

+ 19 - 6
examples/server_simple.c

@@ -80,17 +80,30 @@ int main(int argc, char** argv) {
                               myIntegerNodeId, parentNodeId, parentReferenceNodeId);
     
 #ifdef BENCHMARK
-    UA_UInt32 nodeCount = 500;
-    char str[15];
+    UA_UInt32 nodeCount = 50;
+    char str[32];
     for(UA_UInt32 i = 0;i<nodeCount;i++) {
-        UA_Int32 *data = UA_Int32_new();
-        *data = 42;
+        /* scalar */
+        void *data = UA_new(&UA_TYPES[i]);
         UA_Variant *variant = UA_Variant_new();
-        UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_INT32]);
+        UA_Variant_setScalar(variant, data, &UA_TYPES[i]);
         UA_QualifiedName *nodeName = UA_QualifiedName_new();
         sprintf(str,"%d",i);
         *nodeName = UA_QUALIFIEDNAME(1, str);
-        UA_Server_addVariableNode(server, variant, *nodeName, UA_NODEID_NULL,
+        UA_NodeId id = UA_NODEID_NUMERIC(1, 100 + i);
+        UA_Server_addVariableNode(server, variant, *nodeName, id,
+                                  UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                  UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
+
+        /* array */
+        data = UA_Array_new(&UA_TYPES[i], 10);
+        variant = UA_Variant_new();
+        UA_Variant_setArray(variant, data, 10, &UA_TYPES[i]);
+        nodeName = UA_QualifiedName_new();
+        sprintf(str,"array of %d",i);
+        *nodeName = UA_QUALIFIEDNAME(1, str);
+        id = UA_NODEID_NUMERIC(1, 200 + i);
+        UA_Server_addVariableNode(server, variant, *nodeName, id,
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
     }

+ 40 - 16
include/ua_types.h

@@ -375,22 +375,22 @@ UA_Boolean UA_EXPORT UA_NodeId_isNull(const UA_NodeId *p);
 
 #define UA_NODEID_NUMERIC(NS_INDEX, NUMERICID) (UA_NodeId) {           \
         .namespaceIndex = NS_INDEX,                                    \
-        .identifierType = UA_NODEIDTYPE_NUMERIC,                        \
+        .identifierType = UA_NODEIDTYPE_NUMERIC,                       \
         .identifier.numeric = NUMERICID }
 
 #define UA_NODEID_STRING(NS_INDEX, CHARS) (UA_NodeId) {                \
         .namespaceIndex = NS_INDEX,                                    \
-        .identifierType = UA_NODEIDTYPE_STRING,                         \
+        .identifierType = UA_NODEIDTYPE_STRING,                        \
         .identifier.string = UA_STRING(CHARS) }
 
 #define UA_NODEID_GUID(NS_INDEX, GUID) (UA_NodeId) {                   \
         .namespaceIndex = NS_INDEX,                                    \
-        .identifierType = UA_NODEIDTYPE_GUID,                           \
+        .identifierType = UA_NODEIDTYPE_GUID,                          \
         .identifier.guid = GUID }
 
 #define UA_NODEID_BYTESTRING(NS_INDEX, CHARS) (UA_NodeId) {            \
         .namespaceIndex = NS_INDEX,                                    \
-        .identifierType = UA_NODEIDTYPE_BYTESTRING,                     \
+        .identifierType = UA_NODEIDTYPE_BYTESTRING,                    \
         .identifier.byteString = UA_STRING(CHARS) }
 
 #define UA_NODEID_NULL UA_NODEID_NUMERIC(0,0)
@@ -398,9 +398,9 @@ UA_Boolean UA_EXPORT UA_NodeId_isNull(const UA_NodeId *p);
 /* ExpandedNodeId */
 UA_Boolean UA_EXPORT UA_ExpandedNodeId_isNull(const UA_ExpandedNodeId *p);
 
-#define UA_EXPANDEDNODEID_NUMERIC(NS_INDEX, NUMERICID) (UA_ExpandedNodeId) {                \
+#define UA_EXPANDEDNODEID_NUMERIC(NS_INDEX, NUMERICID) (UA_ExpandedNodeId) {            \
         .nodeId = {.namespaceIndex = NS_INDEX, .identifierType = UA_NODEIDTYPE_NUMERIC, \
-                   .identifier.numeric = NUMERICID },                                    \
+                   .identifier.numeric = NUMERICID },                                   \
         .serverIndex = 0, .namespaceUri = {.data = (UA_Byte*)0, .length = -1} }
     
 /* QualifiedName */
@@ -423,12 +423,6 @@ UA_Boolean UA_EXPORT UA_ExpandedNodeId_isNull(const UA_ExpandedNodeId *p);
  *                      data can be NULL if arrayLength == 0
  */
 
-/**
- * Copy the variant, but use only a subset of the (multidimensional) array. Returns an error code if
- * the variant is no array or if the indicated range does not fit.
- */
-UA_StatusCode UA_EXPORT UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst, UA_NumericRange range);
-
 /**
  * Set the variant to a scalar value that already resides in memory. The value takes on the
  * lifecycle of the variant and is deleted with it.
@@ -460,8 +454,8 @@ UA_StatusCode UA_EXPORT UA_Variant_setScalarCopy(UA_Variant *v, const void *p, c
  * @param type The datatype of the array
  * @return Indicates whether the operation succeeded or returns an error code
  */
-UA_StatusCode UA_EXPORT UA_Variant_setArray(UA_Variant *v, void *array,
-                                            UA_Int32 noElements, const UA_DataType *type);
+UA_StatusCode UA_EXPORT UA_Variant_setArray(UA_Variant *v, void *array, UA_Int32 noElements,
+                                            const UA_DataType *type);
 
 /**
  * Set the variant to an array that is copied from an existing array.
@@ -472,8 +466,38 @@ UA_StatusCode UA_EXPORT UA_Variant_setArray(UA_Variant *v, void *array,
  * @param type The datatype of the array
  * @return Indicates whether the operation succeeded or returns an error code
  */
-UA_StatusCode UA_EXPORT UA_Variant_setArrayCopy(UA_Variant *v, const void *array,
-                                                UA_Int32 noElements, const UA_DataType *type);
+UA_StatusCode UA_EXPORT UA_Variant_setArrayCopy(UA_Variant *v, const void *array, UA_Int32 noElements,
+                                                const UA_DataType *type);
+
+/**
+ * Copy the variant, but use only a subset of the (multidimensional) array. Returns an error code if
+ * the variant is no array or if the indicated range does not fit.
+ */
+UA_StatusCode UA_EXPORT UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst, UA_NumericRange range);
+
+/**
+ * Insert a range of data into an existing variant of the dimensionality. This overwrites data in
+ * the variant. The inserted data is managed by the variant (members are deleted with it).
+ *
+ * @param v The variant
+ * @param data The data array. Obviously the type must match the variant and the length the range.
+ * @param range The range of where the new data is inserted
+ * @return Indicates whether the operation succeeded or returns an error code
+ */
+UA_StatusCode UA_EXPORT UA_Variant_setRange(UA_Variant *v, void *data, const UA_NumericRange range);
+
+/**
+ * Copies the variant and inserts data from the range. The inserted data is managed by the variant
+ * (members are deleted with it).
+ *
+ * @param src The source variant
+ * @param dst The target variant
+ * @param data The data array. Obviously the type must match the variant and the length the range.
+ * @param range The range of where the new data is inserted
+ * @return Indicates whether the operation succeeded or returns an error code
+ */
+UA_StatusCode UA_EXPORT UA_Variant_setCopyRange(const UA_Variant *src, UA_Variant *dst, void *data,
+                                                const UA_NumericRange range);
 
 /****************************/
 /* Structured Type Handling */

+ 139 - 74
src/ua_types.c

@@ -81,12 +81,13 @@ UA_TYPE_DEFAULT(UA_Double)
 
 /* String */
 UA_TYPE_NEW_DEFAULT(UA_String)
+UA_TYPE_DELETE_DEFAULT(UA_String)
+
 void UA_String_init(UA_String *p) {
     p->length = -1;
     p->data   = UA_NULL;
 }
 
-UA_TYPE_DELETE_DEFAULT(UA_String)
 void UA_String_deleteMembers(UA_String *p) {
 	if(p->data) {
 		UA_free(p->data);
@@ -232,6 +233,7 @@ UA_StatusCode UA_DateTime_toString(UA_DateTime atime, UA_String *timeString) {
 }
 
 /* Guid */
+UA_TYPE_NEW_DEFAULT(UA_Guid)
 UA_TYPE_DELETEMEMBERS_NOACTION(UA_Guid)
 UA_TYPE_DELETE_DEFAULT(UA_Guid)
 
@@ -267,7 +269,6 @@ void UA_Guid_init(UA_Guid *p) {
     memset(p->data4, 0, sizeof(UA_Byte)*8);
 }
 
-UA_TYPE_NEW_DEFAULT(UA_Guid)
 UA_StatusCode UA_Guid_copy(UA_Guid const *src, UA_Guid *dst) {
     UA_memcpy((void *)dst, (const void *)src, sizeof(UA_Guid));
     return UA_STATUSCODE_GOOD;
@@ -292,13 +293,14 @@ UA_StatusCode UA_ByteString_newMembers(UA_ByteString *p, UA_Int32 length) {
 /* XmlElement */
 
 /* NodeId */
+UA_TYPE_NEW_DEFAULT(UA_NodeId)
+UA_TYPE_DELETE_DEFAULT(UA_NodeId)
 void UA_NodeId_init(UA_NodeId *p) {
     p->identifierType = UA_NODEIDTYPE_NUMERIC;
     p->namespaceIndex = 0;
     memset(&p->identifier, 0, sizeof(p->identifier));
 }
 
-UA_TYPE_NEW_DEFAULT(UA_NodeId)
 UA_StatusCode UA_NodeId_copy(UA_NodeId const *src, UA_NodeId *dst) {
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     switch(src->identifierType) {
@@ -332,7 +334,6 @@ static UA_Boolean UA_NodeId_isBasicType(UA_NodeId const *id) {
         id ->identifier.numeric <= 25;
 }
 
-UA_TYPE_DELETE_DEFAULT(UA_NodeId)
 void UA_NodeId_deleteMembers(UA_NodeId *p) {
     switch(p->identifierType) {
     case UA_NODEIDTYPE_STRING:
@@ -397,7 +398,9 @@ UA_Boolean UA_NodeId_isNull(const UA_NodeId *p) {
 }
 
 /* ExpandedNodeId */
+UA_TYPE_NEW_DEFAULT(UA_ExpandedNodeId)
 UA_TYPE_DELETE_DEFAULT(UA_ExpandedNodeId)
+
 void UA_ExpandedNodeId_deleteMembers(UA_ExpandedNodeId *p) {
     UA_NodeId_deleteMembers(&p->nodeId);
     UA_String_deleteMembers(&p->namespaceUri);
@@ -409,7 +412,6 @@ void UA_ExpandedNodeId_init(UA_ExpandedNodeId *p) {
     p->serverIndex = 0;
 }
 
-UA_TYPE_NEW_DEFAULT(UA_ExpandedNodeId)
 UA_StatusCode UA_ExpandedNodeId_copy(UA_ExpandedNodeId const *src, UA_ExpandedNodeId *dst) {
     UA_StatusCode retval = UA_NodeId_copy(&src->nodeId, &dst->nodeId);
     retval |= UA_String_copy(&src->namespaceUri, &dst->namespaceUri);
@@ -499,6 +501,7 @@ UA_StatusCode UA_ExtensionObject_copy(UA_ExtensionObject const *src, UA_Extensio
 
 /* DataValue */
 UA_TYPE_DELETE_DEFAULT(UA_DataValue)
+UA_TYPE_NEW_DEFAULT(UA_DataValue)
 void UA_DataValue_deleteMembers(UA_DataValue *p) {
     UA_Variant_deleteMembers(&p->value);
 }
@@ -513,7 +516,6 @@ void UA_DataValue_init(UA_DataValue *p) {
     UA_Variant_init(&p->value);
 }
 
-UA_TYPE_NEW_DEFAULT(UA_DataValue)
 UA_StatusCode UA_DataValue_copy(UA_DataValue const *src, UA_DataValue *dst) {
     UA_DataValue_init(dst);
     *((UA_Byte*)dst) = *((const UA_Byte*)src); // the bitfield
@@ -532,6 +534,8 @@ UA_StatusCode UA_DataValue_copy(UA_DataValue const *src, UA_DataValue *dst) {
 
 /* Variant */
 UA_TYPE_NEW_DEFAULT(UA_Variant)
+UA_TYPE_DELETE_DEFAULT(UA_Variant)
+
 void UA_Variant_init(UA_Variant *p) {
     p->storageType = UA_VARIANT_DATA;
     p->arrayLength = -1;
@@ -541,20 +545,19 @@ void UA_Variant_init(UA_Variant *p) {
     p->type = &UA_TYPES[UA_TYPES_BOOLEAN];
 }
 
-UA_TYPE_DELETE_DEFAULT(UA_Variant)
 void UA_Variant_deleteMembers(UA_Variant *p) {
-    if(p->storageType == UA_VARIANT_DATA) {
-        if(p->data) {
-            if(p->arrayLength == -1)
-                p->arrayLength = 1;
-            UA_Array_delete(p->data, p->type, p->arrayLength);
-            p->data = UA_NULL;
-            p->arrayLength = -1;
-        }
-        if(p->arrayDimensions) {
-            UA_free(p->arrayDimensions);
-            p->arrayDimensions = UA_NULL;
-        }
+    if(p->storageType != UA_VARIANT_DATA)
+        return;
+    if(p->data) {
+        if(p->arrayLength == -1)
+            p->arrayLength = 1;
+        UA_Array_delete(p->data, p->type, p->arrayLength);
+        p->data = UA_NULL;
+        p->arrayLength = -1;
+    }
+    if(p->arrayDimensions) {
+        UA_free(p->arrayDimensions);
+        p->arrayDimensions = UA_NULL;
     }
 }
 
@@ -583,26 +586,32 @@ UA_StatusCode UA_Variant_copy(UA_Variant const *src, UA_Variant *dst) {
         }
         dst->arrayDimensionsSize = src->arrayDimensionsSize;
     }
-
     return retval;
 }
 
-UA_StatusCode UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst, const UA_NumericRange range) {
-    UA_Variant_init(dst);
-    // test the integrity of the variant dimensions
+/**
+ * Tests if a range is compatible with a variant. If yes, the following values are set:
+ * - total: how many elements are indicated by the range
+ * - block_size: how big is each contiguous block of elements in the variant denoted by the range
+ * - block_distance: how many elements are between the blocks (beginning to beginning)
+ * - first_elem: where does the first block begin
+ */
+static UA_StatusCode testRangeWithVariant(const UA_Variant *v, const UA_NumericRange range, size_t *total,
+                                          size_t *block_size, size_t *block_distance, size_t *first_elem) {
+    /* Test the integrity of the source variant dimensions */
     UA_Int32 dims_count = 1;
-    const UA_Int32 *dims = &src->arrayLength; // default: the array has only one dimension
-    if(src->arrayDimensionsSize > 0) {
-        dims_count = src->arrayDimensionsSize;
-        dims = src->arrayDimensions;
+    const UA_Int32 *dims = &v->arrayLength; // default: the array has only one dimension
+    if(v->arrayDimensionsSize > 0) {
+        dims_count = v->arrayDimensionsSize;
+        dims = v->arrayDimensions;
         UA_Int32 elements = 1;
         for(UA_Int32 i = 0; i < dims_count; i++)
             elements *= dims[i];
-        if(elements != src->arrayLength)
+        if(elements != v->arrayLength)
             return UA_STATUSCODE_BADINTERNALERROR;
     }
 
-    // test the integrity of the range and count objects
+    /* Test the integrity of the range */
     size_t count = 1;
     if(range.dimensionsSize != dims_count)
         return UA_STATUSCODE_BADINTERNALERROR;
@@ -614,67 +623,123 @@ UA_StatusCode UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst, const
         count *= (range.dimensions[i].max - range.dimensions[i].min) + 1;
     }
 
-    dst->data = UA_malloc(src->type->memSize * count);
-    if(!dst->data)
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-    
-    // copy a subset of the tensor with as few calls as possible.
-    // shift from copying single elements to contiguous blocks
-    size_t elem_size = src->type->memSize;
-    uintptr_t nextsrc = (uintptr_t)src->data; // the start ptr of the next copy operation
-    size_t contiguous_elems = src->arrayLength; // the length of the copy operation
-    ptrdiff_t jump_length = elem_size * dims[0]; // how far to jump until the next contiguous copy
-    size_t copy_count = count; // how often to copy
-
-    size_t running_dimssize = 1; // how big is a contiguous block for the dimensions k_max to k
+    /* Compute the block size and the position of the first element */
+    size_t bs, bd, fe = 0;
+    size_t running_dimssize = 1; // elements per block of dimensions k to k_max
     UA_Boolean found_contiguous = UA_FALSE;
     for(UA_Int32 k = dims_count - 1; k >= 0; k--) {
-        if(!found_contiguous) {
-            if(range.dimensions[k].min != 0 || range.dimensions[k].max + 1 != (UA_UInt32)dims[k]) {
-                found_contiguous = UA_TRUE;
-                contiguous_elems = (range.dimensions[k].max - range.dimensions[k].min + 1) * running_dimssize;
-                jump_length = ((dims[k] * running_dimssize) - contiguous_elems) * elem_size;
-                copy_count /= range.dimensions[k].max - range.dimensions[k].min + 1;
-            } else
-                copy_count /= dims[k];
+        if(!found_contiguous && (range.dimensions[k].min != 0 ||
+                                 range.dimensions[k].max + 1 != (UA_UInt32)dims[k])) {
+            found_contiguous = UA_TRUE;
+            bs = (range.dimensions[k].max - range.dimensions[k].min + 1) * running_dimssize;
+            bd = dims[k] * running_dimssize;
         } 
-        nextsrc += running_dimssize * range.dimensions[k].min * elem_size;
+        fe += running_dimssize * range.dimensions[k].min;
         running_dimssize *= dims[k];
     }
+    *total = count;
+    *block_size = bs;
+    *block_distance = bd;
+    *first_elem = fe;
+    return UA_STATUSCODE_GOOD;
+}
 
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+UA_StatusCode UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst, const UA_NumericRange range) {
+    size_t count, block_size, block_distance, first_elem;
+    UA_StatusCode retval = testRangeWithVariant(src, range, &count, &block_size, &block_distance, &first_elem);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    UA_Variant_init(dst);
+    size_t elem_size = src->type->memSize;
+    dst->data = UA_malloc(elem_size * count);
+    if(!dst->data)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    /* Copy the range */
+    size_t block_count = count / block_size;
     uintptr_t nextdst = (uintptr_t)dst->data;
-    size_t copied = 0;
-    for(size_t i = 0; i < copy_count; i++) {
-        if(src->type->fixedSize) {
-            memcpy((void*)nextdst, (void*)nextsrc, elem_size * contiguous_elems);
-        } else {
-            for(size_t j = 0; j < contiguous_elems; j++)
-                retval = UA_copy((const void*)(nextsrc + (j * elem_size)), (void*)(nextdst + (j * elem_size)),
-                                 src->type);
-            if(retval != UA_STATUSCODE_GOOD) {
-                UA_Array_delete(dst->data, src->type, copied);
-                return retval;
+    uintptr_t nextsrc = (uintptr_t)src->data + (elem_size * first_elem);
+    if(src->type->fixedSize) {
+        for(size_t i = 0; i < block_count; i++) {
+            memcpy((void*)nextdst, (void*)nextsrc, elem_size * block_size);
+            nextdst += block_size * elem_size;
+            nextsrc += block_distance * elem_size;
+        }
+    } else {
+        for(size_t i = 0; i < block_count; i++) {
+            for(size_t j = 0; j < block_size && retval == UA_STATUSCODE_GOOD; j++) {
+                retval = UA_copy((const void*)nextsrc, (void*)nextdst, src->type);
+                nextdst += elem_size;
+                nextsrc += elem_size;
             }
-            copied += contiguous_elems;
+            nextsrc += (block_distance - block_size) * elem_size;
         }
-        nextdst += elem_size * contiguous_elems;
-        nextsrc += jump_length;
-    }
-
-    if(src->arrayDimensionsSize > 0) {
-        retval = UA_Array_copy(dims, (void**)&dst->arrayDimensions, &UA_TYPES[UA_TYPES_INT32], dims_count);
         if(retval != UA_STATUSCODE_GOOD) {
+            size_t copied = ((nextdst - elem_size) - (uintptr_t)dst->data) / elem_size;
             UA_Array_delete(dst->data, src->type, copied);
             return retval;
         }
-        for(UA_Int32 k = 0; k < dims_count; k++)
+    }
+
+    /* Copy the range dimensions*/
+    if(src->arrayDimensionsSize > 0) {
+        dst->arrayDimensions = UA_malloc(sizeof(UA_Int32) * src->arrayDimensionsSize);
+        if(!dst->arrayDimensions) {
+            UA_Array_delete(dst->data, src->type, count);
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+        }
+        for(UA_Int32 k = 0; k < src->arrayDimensionsSize; k++)
             dst->arrayDimensions[k] = range.dimensions[k].max - range.dimensions[k].min + 1;
-        dst->arrayDimensionsSize = dims_count;
+        dst->arrayDimensionsSize = src->arrayDimensionsSize;
     }
     dst->arrayLength = count;
     dst->type = src->type;
-    dst->storageType = UA_VARIANT_DATA;
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_StatusCode UA_Variant_setRange(UA_Variant *v, void *data, const UA_NumericRange range) {
+    size_t count, block_size, block_distance, first_elem;
+    UA_StatusCode retval = testRangeWithVariant(v, range, &count, &block_size, &block_distance, &first_elem);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    size_t block_count = count / block_size;
+    size_t elem_size = v->type->memSize;
+    uintptr_t nextdst = (uintptr_t)v->data + (first_elem * elem_size);
+    uintptr_t nextsrc = (uintptr_t)data;
+    if(v->type->fixedSize) {
+        for(size_t i = 0; i < block_count; i++) {
+            memcpy((void*)nextdst, (void*)nextsrc, elem_size * block_size);
+            nextdst += block_size * elem_size;
+            nextsrc += block_distance * elem_size;
+        }
+    } else {
+        for(size_t i = 0; i < block_count; i++) {
+            for(size_t j = 0; j < block_size; j++) {
+                UA_deleteMembers((void*)nextdst, v->type);
+                nextdst += elem_size;
+            }
+            nextdst -= block_size * elem_size;
+            memcpy((void*)nextdst, (void*)nextsrc, elem_size * block_size);
+            nextdst += block_size * elem_size;
+            nextsrc += block_distance * elem_size;
+        }
+    }
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_StatusCode UA_Variant_setCopyRange(const UA_Variant *src, UA_Variant *dst, void *data,
+                                      const UA_NumericRange range) {
+    // todo: don't copy the entire variant but only the retained parts
+    UA_StatusCode retval = UA_Variant_copy(src, dst);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+    retval = UA_Variant_setRange(dst, data, range);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_Variant_deleteMembers(dst);
+        return retval;
+    }
     return UA_STATUSCODE_GOOD;
 }
 
@@ -1098,7 +1163,7 @@ UA_StatusCode UA_Array_copy(const void *src, void **dst, const UA_DataType *data
 
     if(retval != UA_STATUSCODE_GOOD)
         UA_Array_delete(*dst, dataType, noElements);
-        
+
     return retval;
 }
 

+ 1 - 0
tests/CMakeLists.txt

@@ -50,6 +50,7 @@ add_test(nodestore ${CMAKE_CURRENT_BINARY_DIR}/check_nodestore)
 add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/client_HELOPN.bin
                    PRE_BUILD
                    COMMAND python ${PROJECT_SOURCE_DIR}/tools/hex2bin.py ${CMAKE_CURRENT_SOURCE_DIR}/dumps/client_HELOPN.hex
+                                  ${CMAKE_CURRENT_SOURCE_DIR}/dumps/client_CreateActivateSession.hex
                    DEPENDS ${PROJECT_SOURCE_DIR}/tools/hex2bin.py
                            ${CMAKE_CURRENT_SOURCE_DIR}/dumps/client_HELOPN.hex)