Browse Source

allow deep numeric ranges to reach into scalars containing an array (#777)

(variants, strings, ...)
Julius Pfrommer 8 years ago
parent
commit
a771272e65
3 changed files with 255 additions and 165 deletions
  1. 7 4
      include/ua_types.h
  2. 59 55
      src/server/ua_services_attribute.c
  3. 189 106
      src/ua_types.c

+ 7 - 4
include/ua_types.h

@@ -209,12 +209,15 @@ void UA_EXPORT UA_Array_delete(void *p, size_t size, const UA_DataType *type);
  * wire, it only exists as an encoded string, such as "1:2,0:3,5". The colon
  * separates min/max index and the comma separates dimensions. A single value
  * indicates a range with a single element (min==max). */
+
+typedef struct {
+    UA_UInt32 min;
+    UA_UInt32 max;
+} UA_NumericRangeDimension;
+    
 typedef struct {
     size_t dimensionsSize;
-    struct UA_NumericRangeDimension {
-        UA_UInt32 min;
-        UA_UInt32 max;
-    } *dimensions;
+    UA_NumericRangeDimension *dimensions;
 } UA_NumericRange;
 
 /**

+ 59 - 55
src/server/ua_services_attribute.c

@@ -22,7 +22,7 @@ readNumber(UA_Byte *buf, size_t buflen, UA_UInt32 *number) {
 }
 
 static size_t
-readDimension(UA_Byte *buf, size_t buflen, struct UA_NumericRangeDimension *dim) {
+readDimension(UA_Byte *buf, size_t buflen, UA_NumericRangeDimension *dim) {
     size_t progress = readNumber(buf, buflen, &dim->min);
     if(progress == 0)
         return 0;
@@ -49,14 +49,14 @@ static
 UA_StatusCode parse_numericrange(const UA_String *str, UA_NumericRange *range) {
     size_t idx = 0;
     size_t dimensionsMax = 0;
-    struct UA_NumericRangeDimension *dimensions = NULL;
+    UA_NumericRangeDimension *dimensions = NULL;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     size_t offset = 0;
     while(true) {
         /* alloc dimensions */
         if(idx >= dimensionsMax) {
-            struct UA_NumericRangeDimension *newds;
-            size_t newdssize = sizeof(struct UA_NumericRangeDimension) * (dimensionsMax + 2);
+            UA_NumericRangeDimension *newds;
+            size_t newdssize = sizeof(UA_NumericRangeDimension) * (dimensionsMax + 2);
             newds = UA_realloc(dimensions, newdssize);
             if(!newds) {
                 retval = UA_STATUSCODE_BADOUTOFMEMORY;
@@ -582,22 +582,20 @@ UA_StatusCode UA_Server_editNode(UA_Server *server, UA_Session *session,
     }
 
 static UA_StatusCode
-Service_Write_single_ValueDataSource(UA_Server *server, UA_Session *session,
-                                     const UA_VariableNode *node, const UA_WriteValue *wvalue)
-{
+WriteToDataSource(UA_Server *server, UA_Session *session,
+                  const UA_VariableNode *node, const UA_WriteValue *wvalue) {
     UA_assert(wvalue->attributeId == UA_ATTRIBUTEID_VALUE);
     UA_assert(node->nodeClass == UA_NODECLASS_VARIABLE ||
               node->nodeClass == UA_NODECLASS_VARIABLETYPE);
     UA_assert(node->valueSource == UA_VALUESOURCE_DATASOURCE);
 
-    if(node->value.dataSource.write == NULL)
+    if(!node->value.dataSource.write)
         return UA_STATUSCODE_BADWRITENOTSUPPORTED;
 
-    UA_StatusCode retval;
-    if(wvalue->indexRange.length <= 0) {
-        retval = node->value.dataSource.write(node->value.dataSource.handle, node->nodeId,
-                                              &wvalue->value.value, NULL);
-    } else {
+
+    if(wvalue->indexRange.length > 0) {
+        /* write with an indexrange */
+        UA_StatusCode retval;
         UA_NumericRange range;
         retval = parse_numericrange(&wvalue->indexRange, &range);
         if(retval != UA_STATUSCODE_GOOD)
@@ -605,8 +603,12 @@ Service_Write_single_ValueDataSource(UA_Server *server, UA_Session *session,
         retval = node->value.dataSource.write(node->value.dataSource.handle, node->nodeId,
                                               &wvalue->value.value, &range);
         UA_free(range.dimensions);
+        return retval;
     }
-    return retval;
+
+    /* write normal */
+    return node->value.dataSource.write(node->value.dataSource.handle,
+                                        node->nodeId, &wvalue->value.value, NULL);
 }
 
 enum type_equivalence {
@@ -626,7 +628,7 @@ static enum type_equivalence typeEquivalence(const UA_DataType *t) {
 }
 
 static UA_StatusCode
-CopyValueIntoNode(UA_VariableNode *node, const UA_WriteValue *wvalue) {
+CopyIntoValueAttribute(UA_VariableNode *node, const UA_WriteValue *wvalue) {
     UA_assert(wvalue->attributeId == UA_ATTRIBUTEID_VALUE);
     UA_assert(node->nodeClass == UA_NODECLASS_VARIABLE ||
               node->nodeClass == UA_NODECLASS_VARIABLETYPE);
@@ -644,7 +646,8 @@ CopyValueIntoNode(UA_VariableNode *node, const UA_WriteValue *wvalue) {
     }
 
     /* The nodeid on the wire may be != the nodeid in the node: opaque types,
-       enums and bytestrings. nodeV contains the correct type definition. */
+       enums and bytestrings. newV contains the correct type definition after
+       the following paragraph */
     UA_Variant *oldV = &node->value.variant.value;
     const UA_Variant *newV = &wvalue->value.value;
     UA_Variant cast_v;
@@ -660,41 +663,43 @@ CopyValueIntoNode(UA_VariableNode *node, const UA_WriteValue *wvalue) {
         } else if(oldV->type == &UA_TYPES[UA_TYPES_BYTE] && !UA_Variant_isScalar(oldV) &&
                   newV->type == &UA_TYPES[UA_TYPES_BYTESTRING] && UA_Variant_isScalar(newV)) {
             /* a string is written to a byte array */
-            UA_ByteString *str = (UA_ByteString*) newV->data;
+            UA_ByteString *str = (UA_ByteString*)newV->data;
+            cast_v.type = &UA_TYPES[UA_TYPES_BYTE];
             cast_v.arrayLength = str->length;
             cast_v.data = str->data;
-            cast_v.type = &UA_TYPES[UA_TYPES_BYTE];
         } else {
-            if(rangeptr)
-                UA_free(range.dimensions);
-            return UA_STATUSCODE_BADTYPEMISMATCH;
+            retval = UA_STATUSCODE_BADTYPEMISMATCH;
+            goto cleanup;
         }
     }
 
+    /* write the value */
     if(!rangeptr) {
         UA_Variant_deleteMembers(&node->value.variant.value);
         UA_Variant_copy(newV, &node->value.variant.value);
     } else
         retval = UA_Variant_setRangeCopy(&node->value.variant.value, newV->data,
                                          newV->arrayLength, range);
+
+    /* post-write callback */
     if(node->value.variant.callback.onWrite)
-        node->value.variant.callback.onWrite(node->value.variant.callback.handle, node->nodeId,
-                                             &node->value.variant.value, rangeptr);
+        node->value.variant.callback.onWrite(node->value.variant.callback.handle,
+                                             node->nodeId, &node->value.variant.value,
+                                             rangeptr);
+ cleanup:
     if(rangeptr)
         UA_free(range.dimensions);
     return retval;
 }
 
+/* this function implements the main part of the write service */
 static UA_StatusCode
 CopyAttributeIntoNode(UA_Server *server, UA_Session *session,
                       UA_Node *node, const UA_WriteValue *wvalue) {
     if(!wvalue->value.hasValue)
         return UA_STATUSCODE_BADTYPEMISMATCH;
 
-    void *value = wvalue->value.value.data;
-    void *target = NULL;
-    const UA_DataType *attr_type = NULL;
-
+    const void *value = wvalue->value.value.data;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     switch(wvalue->attributeId) {
     case UA_ATTRIBUTEID_NODEID:
@@ -704,101 +709,100 @@ CopyAttributeIntoNode(UA_Server *server, UA_Session *session,
         break;
     case UA_ATTRIBUTEID_BROWSENAME:
         CHECK_DATATYPE(QUALIFIEDNAME);
-        target = &node->browseName;
-        attr_type = &UA_TYPES[UA_TYPES_QUALIFIEDNAME];
+        UA_QualifiedName_deleteMembers(&node->browseName);
+        UA_QualifiedName_copy(value, &node->browseName);
         break;
     case UA_ATTRIBUTEID_DISPLAYNAME:
         CHECK_DATATYPE(LOCALIZEDTEXT);
-        target = &node->displayName;
-        attr_type = &UA_TYPES[UA_TYPES_LOCALIZEDTEXT];
+        UA_LocalizedText_deleteMembers(&node->displayName);
+        UA_LocalizedText_copy(value, &node->displayName);
         break;
     case UA_ATTRIBUTEID_DESCRIPTION:
         CHECK_DATATYPE(LOCALIZEDTEXT);
-        target = &node->description;
-        attr_type = &UA_TYPES[UA_TYPES_LOCALIZEDTEXT];
+        UA_LocalizedText_deleteMembers(&node->description);
+        UA_LocalizedText_copy(value, &node->description);
         break;
     case UA_ATTRIBUTEID_WRITEMASK:
         CHECK_DATATYPE(UINT32);
-        node->writeMask = *(UA_UInt32*)value;
+        node->writeMask = *(const UA_UInt32*)value;
         break;
     case UA_ATTRIBUTEID_USERWRITEMASK:
         CHECK_DATATYPE(UINT32);
-        node->userWriteMask = *(UA_UInt32*)value;
+        node->userWriteMask = *(const UA_UInt32*)value;
         break;
     case UA_ATTRIBUTEID_ISABSTRACT:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_REFERENCETYPE |
                               UA_NODECLASS_VARIABLETYPE | UA_NODECLASS_DATATYPE);
         CHECK_DATATYPE(BOOLEAN);
-        ((UA_ObjectTypeNode*)node)->isAbstract = *(UA_Boolean*)value;
+        ((UA_ObjectTypeNode*)node)->isAbstract = *(const UA_Boolean*)value;
         break;
     case UA_ATTRIBUTEID_SYMMETRIC:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
         CHECK_DATATYPE(BOOLEAN);
-        ((UA_ReferenceTypeNode*)node)->symmetric = *(UA_Boolean*)value;
+        ((UA_ReferenceTypeNode*)node)->symmetric = *(const UA_Boolean*)value;
         break;
     case UA_ATTRIBUTEID_INVERSENAME:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
         CHECK_DATATYPE(LOCALIZEDTEXT);
-        target = &((UA_ReferenceTypeNode*)node)->inverseName;
-        attr_type = &UA_TYPES[UA_TYPES_LOCALIZEDTEXT];
+        UA_LocalizedText_deleteMembers(&((UA_ReferenceTypeNode*)node)->inverseName);
+        UA_LocalizedText_copy(value, &((UA_ReferenceTypeNode*)node)->inverseName);
         break;
     case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW);
         CHECK_DATATYPE(BOOLEAN);
-        ((UA_ViewNode*)node)->containsNoLoops = *(UA_Boolean*)value;
+        ((UA_ViewNode*)node)->containsNoLoops = *(const UA_Boolean*)value;
         break;
     case UA_ATTRIBUTEID_EVENTNOTIFIER:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT);
         CHECK_DATATYPE(BYTE);
-        ((UA_ViewNode*)node)->eventNotifier = *(UA_Byte*)value;
+        ((UA_ViewNode*)node)->eventNotifier = *(const UA_Byte*)value;
         break;
     case UA_ATTRIBUTEID_VALUE:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
         if(((const UA_VariableNode*)node)->valueSource == UA_VALUESOURCE_VARIANT)
-            retval = CopyValueIntoNode((UA_VariableNode*)node, wvalue);
+            retval = CopyIntoValueAttribute((UA_VariableNode*)node, wvalue);
         else
-            retval = Service_Write_single_ValueDataSource(server, session,
-                                                          (const UA_VariableNode*)node,
-                                                          wvalue);
+            /* TODO: Don't make a copy of the node in the multithreaded case */
+            retval = WriteToDataSource(server, session,
+                                       (const UA_VariableNode*)node, wvalue);
         break;
     case UA_ATTRIBUTEID_ACCESSLEVEL:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
         CHECK_DATATYPE(BYTE);
-        ((UA_VariableNode*)node)->accessLevel = *(UA_Byte*)value;
+        ((UA_VariableNode*)node)->accessLevel = *(const UA_Byte*)value;
         break;
     case UA_ATTRIBUTEID_USERACCESSLEVEL:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
         CHECK_DATATYPE(BYTE);
-        ((UA_VariableNode*)node)->userAccessLevel = *(UA_Byte*)value;
+        ((UA_VariableNode*)node)->userAccessLevel = *(const UA_Byte*)value;
         break;
     case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
         CHECK_DATATYPE(DOUBLE);
-        ((UA_VariableNode*)node)->minimumSamplingInterval = *(UA_Double*)value;
+        ((UA_VariableNode*)node)->minimumSamplingInterval = *(const UA_Double*)value;
         break;
     case UA_ATTRIBUTEID_HISTORIZING:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
         CHECK_DATATYPE(BOOLEAN);
-        ((UA_VariableNode*)node)->historizing = *(UA_Boolean*)value;
+        ((UA_VariableNode*)node)->historizing = *(const UA_Boolean*)value;
         break;
     case UA_ATTRIBUTEID_EXECUTABLE:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_METHOD);
         CHECK_DATATYPE(BOOLEAN);
-        ((UA_MethodNode*)node)->executable = *(UA_Boolean*)value;
+        ((UA_MethodNode*)node)->executable = *(const UA_Boolean*)value;
         break;
     case UA_ATTRIBUTEID_USEREXECUTABLE:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_METHOD);
         CHECK_DATATYPE(BOOLEAN);
-        ((UA_MethodNode*)node)->userExecutable = *(UA_Boolean*)value;
+        ((UA_MethodNode*)node)->userExecutable = *(const UA_Boolean*)value;
         break;
     default:
         retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
         break;
     }
-    if(attr_type) {
-        UA_deleteMembers(target, attr_type);
-        retval = UA_copy(value, target, attr_type);
-    }
+    if(retval != UA_STATUSCODE_GOOD)
+        UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                             "WriteRequest returned status code 0x%08x", retval);
     return retval;
 }
 

+ 189 - 106
src/ua_types.c

@@ -351,17 +351,62 @@ Variant_copy(UA_Variant const *src, UA_Variant *dst, const UA_DataType *_) {
     return UA_STATUSCODE_GOOD;
 }
 
-/**
- * Test if a range is compatible with a variant. If yes, the following values are set:
+void
+UA_Variant_setScalar(UA_Variant *v, void * UA_RESTRICT p,
+                     const UA_DataType *type) {
+    UA_Variant_init(v);
+    v->type = type;
+    v->arrayLength = 0;
+    v->data = p;
+}
+
+UA_StatusCode
+UA_Variant_setScalarCopy(UA_Variant *v, const void *p,
+                         const UA_DataType *type) {
+    void *new = UA_malloc(type->memSize);
+    if(!new)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    UA_StatusCode retval = UA_copy(p, new, type);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_free(new);
+        //cppcheck-suppress memleak
+        return retval;
+    }
+    UA_Variant_setScalar(v, new, type);
+    //cppcheck-suppress memleak
+    return UA_STATUSCODE_GOOD;
+}
+
+void UA_Variant_setArray(UA_Variant *v, void * UA_RESTRICT array,
+                         size_t arraySize, const UA_DataType *type) {
+    UA_Variant_init(v);
+    v->data = array;
+    v->arrayLength = arraySize;
+    v->type = type;
+}
+
+UA_StatusCode
+UA_Variant_setArrayCopy(UA_Variant *v, const void *array,
+                        size_t arraySize, const UA_DataType *type) {
+    UA_Variant_init(v);
+    UA_StatusCode retval = UA_Array_copy(array, arraySize, &v->data, type);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+    v->arrayLength = arraySize;
+    v->type = type;
+    return UA_STATUSCODE_GOOD;
+}
+
+/* Range-wise access to Variants */
+
+/* Test if a range is compatible with a variant. If yes, the following values are set:
  * - total: how many elements are in the range
  * - block: how big is each contiguous block of elements in the variant that maps into the range
  * - stride: how many elements are between the blocks (beginning to beginning)
- * - first: where does the first block begin
- */
+ * - first: where does the first block begin */
 static UA_StatusCode
-processRangeDefinition(const UA_Variant *v, const UA_NumericRange range,
-                       size_t *total, size_t *block, size_t *stride,
-                       size_t *first) {
+computeStrides(const UA_Variant *v, const UA_NumericRange range,
+               size_t *total, size_t *block, size_t *stride, size_t *first) {
     /* Test the integrity of the source variant dimensions */
     size_t dims_count = 1;
     UA_UInt32 elements = 1;
@@ -418,14 +463,76 @@ processRangeDefinition(const UA_Variant *v, const UA_NumericRange range,
     return UA_STATUSCODE_GOOD;
 }
 
+/* Is the type string-like? */
+static UA_Boolean
+isStringLike(const UA_DataType *type) {
+    if(type->membersSize == 1 && type->members[0].isArray &&
+       type->members[0].namespaceZero &&
+       type->members[0].memberTypeIndex == UA_TYPES_BYTE)
+        return true;
+    return false;
+}
+
+static UA_StatusCode
+copySubString(const UA_String *src, UA_String *dst,
+              const UA_NumericRangeDimension *dim) {
+    if(dim->min > dim->max)
+        return UA_STATUSCODE_BADINDEXRANGEINVALID;
+    if(dim->max >= src->length)
+        return UA_STATUSCODE_BADINDEXRANGENODATA;
+
+    size_t length = dim->max - dim->min + 1;
+    UA_StatusCode retval = UA_ByteString_allocBuffer(dst, length);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    memcpy(dst->data, &src->data[dim->min], length);
+    return UA_STATUSCODE_GOOD;
+}
+
 UA_StatusCode
-UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst,
+UA_Variant_copyRange(const UA_Variant *orig_src, UA_Variant *dst,
                      const UA_NumericRange range) {
+    UA_Boolean isScalar = UA_Variant_isScalar(orig_src);
+    UA_Boolean stringLike = isStringLike(orig_src->type);
+    const UA_Variant *src = orig_src;
+    UA_Variant arraySrc;
+
+    /* Extract the range for copying at this level. The remaining range is dealt
+     * with in the "scalar" type that may define an array by itself (string,
+     * variant, ...). */
+    UA_NumericRange thisrange, nextrange;
+    UA_NumericRangeDimension scalarThisDimension = (UA_NumericRangeDimension){
+        .min = 0, .max = 0}; /* a single entry */
+    if(isScalar) {
+        /* Replace scalar src with array of length 1 */
+        arraySrc = *src;
+        arraySrc.arrayLength = 1;
+        src = &arraySrc;
+        /* Deal with all range dimensions within the scalar */
+        thisrange.dimensions = &scalarThisDimension;
+        thisrange.dimensionsSize = 1;
+        nextrange = range;
+    } else {
+        /* Deal with as many range dimensions as possible right now */
+        size_t dims = src->arrayDimensionsSize;
+        if(dims == 0)
+            dims = 1;
+        if(dims > range.dimensionsSize)
+            return UA_STATUSCODE_BADINDEXRANGEINVALID;
+       thisrange = range;
+       thisrange.dimensionsSize = dims;
+       nextrange.dimensions = &range.dimensions[dims];
+       nextrange.dimensionsSize = range.dimensionsSize - dims;
+    }
+        
+    /* Compute the strides */
     size_t count, block, stride, first;
-    UA_StatusCode retval = processRangeDefinition(src, range, &count, &block,
-                                                  &stride, &first);
+    UA_StatusCode retval = computeStrides(src, thisrange, &count, &block, &stride, &first);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
+
+    /* Allocate the array */
     UA_Variant_init(dst);
     size_t elem_size = src->type->memSize;
     dst->data = UA_malloc(elem_size * count);
@@ -436,94 +543,98 @@ UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst,
     size_t block_count = count / block;
     uintptr_t nextdst = (uintptr_t)dst->data;
     uintptr_t nextsrc = (uintptr_t)src->data + (elem_size * first);
-    if(src->type->fixedSize) {
-        for(size_t i = 0; i < block_count; i++) {
-            memcpy((void*)nextdst, (void*)nextsrc, elem_size * block);
-            nextdst += block * elem_size;
-            nextsrc += stride * elem_size;
+    if(nextrange.dimensionsSize == 0) {
+        /* no nextrange */
+        if(src->type->fixedSize) {
+            for(size_t i = 0; i < block_count; i++) {
+                memcpy((void*)nextdst, (void*)nextsrc, elem_size * block);
+                nextdst += block * elem_size;
+                nextsrc += stride * elem_size;
+            }
+        } else {
+            for(size_t i = 0; i < block_count; i++) {
+                for(size_t j = 0; j < block && retval == UA_STATUSCODE_GOOD; j++) {
+                    retval = UA_copy((const void*)nextsrc, (void*)nextdst, src->type);
+                    nextdst += elem_size;
+                    nextsrc += elem_size;
+                }
+                nextsrc += (stride - block) * elem_size;
+            }
         }
     } else {
+        /* nextrange can only be used for variants and stringlike with remaining
+         * range of dimension 1 */
+        if(src->type != &UA_TYPES[UA_TYPES_VARIANT]) {
+            if(!stringLike)
+                retval = UA_STATUSCODE_BADINDEXRANGENODATA;
+            if(nextrange.dimensionsSize != 1)
+                retval = UA_STATUSCODE_BADINDEXRANGENODATA;
+        }
+
+        /* copy the content */
         for(size_t i = 0; i < block_count; i++) {
             for(size_t j = 0; j < block && retval == UA_STATUSCODE_GOOD; j++) {
-                retval = UA_copy((const void*)nextsrc, (void*)nextdst, src->type);
+                if(stringLike)
+                    retval = copySubString((const UA_String*)nextsrc,
+                                           (UA_String*)nextdst, nextrange.dimensions);
+                else
+                    retval = UA_Variant_copyRange((const UA_Variant*)nextsrc,
+                                                  (UA_Variant*)nextdst, nextrange);
                 nextdst += elem_size;
                 nextsrc += elem_size;
             }
             nextsrc += (stride - block) * elem_size;
         }
-        if(retval != UA_STATUSCODE_GOOD) {
-            size_t copied = ((nextdst - elem_size) - (uintptr_t)dst->data) / elem_size;
-            UA_Array_delete(dst->data, copied, src->type);
-            dst->data = NULL;
-            return retval;
-        }
     }
-    dst->arrayLength = count;
+
+    /* Clean up if copying failed */
+    if(retval != UA_STATUSCODE_GOOD) {
+        size_t copied = ((nextdst - elem_size) - (uintptr_t)dst->data) / elem_size;
+        UA_Array_delete(dst->data, copied, src->type);
+        dst->data = NULL;
+        return retval;
+    }
+
+    /* Done if scalar */
     dst->type = src->type;
+    if(isScalar)
+        return retval;
 
-    /* Copy the range dimensions */
+    /* Finish the array */
+    dst->arrayLength = count;
     if(src->arrayDimensionsSize > 0) {
-        dst->arrayDimensions = UA_Array_new(src->arrayDimensionsSize,
-                                            &UA_TYPES[UA_TYPES_UINT32]);
+        dst->arrayDimensions = UA_Array_new(thisrange.dimensionsSize, &UA_TYPES[UA_TYPES_UINT32]);
         if(!dst->arrayDimensions) {
             Variant_deletemembers(dst, NULL);
-            memset(dst, 0, sizeof(UA_Variant)); /* init */
             return UA_STATUSCODE_BADOUTOFMEMORY;
         }
-        dst->arrayDimensionsSize = src->arrayDimensionsSize;
-        for(size_t k = 0; k < src->arrayDimensionsSize; k++)
+        dst->arrayDimensionsSize = thisrange.dimensionsSize;
+        for(size_t k = 0; k < thisrange.dimensionsSize; k++)
             dst->arrayDimensions[k] =
-                (UA_Int32)(range.dimensions[k].max - range.dimensions[k].min + 1);
+                (UA_Int32)(thisrange.dimensions[k].max - thisrange.dimensions[k].min + 1);
     }
     return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode
-UA_Variant_setRange(UA_Variant *v, void * UA_RESTRICT array, size_t arraySize,
-                    const UA_NumericRange range) {
-    size_t count, block, stride, first;
-    UA_StatusCode retval = processRangeDefinition(v, range, &count, &block,
-                                                  &stride, &first);
-    if(retval != UA_STATUSCODE_GOOD)
-        return retval;
-    if(count != arraySize)
-        return UA_STATUSCODE_BADINDEXRANGEINVALID;
-
-    size_t block_count = count / block;
-    size_t elem_size = v->type->memSize;
-    uintptr_t nextdst = (uintptr_t)v->data + (first * elem_size);
-    uintptr_t nextsrc = (uintptr_t)array;
-    for(size_t i = 0; i < block_count; i++) {
-        if(!v->type->fixedSize) {
-            for(size_t j = 0; j < block; j++) {
-                UA_deleteMembers_noInit((void*)nextdst, v->type);
-                nextdst += elem_size;
-            }
-            nextdst -= block * elem_size;
-        }
-        memcpy((void*)nextdst, (void*)nextsrc, elem_size * block);
-        nextsrc += block * elem_size;
-        nextdst += stride * elem_size;
-    }
-    return UA_STATUSCODE_GOOD;
-}
-
-UA_StatusCode
-UA_Variant_setRangeCopy(UA_Variant *v, const void *array, size_t arraySize,
-                        const UA_NumericRange range) {
+/* TODO: Allow ranges to reach inside a scalars that are array-like, e.g.
+   variant and strings. This is already possible for reading... */
+static UA_StatusCode
+Variant_setRange(UA_Variant *v, void *array, size_t arraySize,
+                 const UA_NumericRange range, UA_Boolean copy) {
+    /* Compute the strides */
     size_t count, block, stride, first;
-    UA_StatusCode retval = processRangeDefinition(v, range, &count, &block,
-                                                  &stride, &first);
+    UA_StatusCode retval = computeStrides(v, range, &count, &block, &stride, &first);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
     if(count != arraySize)
         return UA_STATUSCODE_BADINDEXRANGEINVALID;
 
+    /* Transfer the content */
     size_t block_count = count / block;
     size_t elem_size = v->type->memSize;
     uintptr_t nextdst = (uintptr_t)v->data + (first * elem_size);
     uintptr_t nextsrc = (uintptr_t)array;
-    if(v->type->fixedSize) {
+    if(v->type->fixedSize || !copy) {
         for(size_t i = 0; i < block_count; i++) {
             memcpy((void*)nextdst, (void*)nextsrc, elem_size * block);
             nextsrc += block * elem_size;
@@ -540,53 +651,25 @@ UA_Variant_setRangeCopy(UA_Variant *v, const void *array, size_t arraySize,
             nextdst += (stride - block) * elem_size;
         }
     }
-    return retval;
-}
 
-void
-UA_Variant_setScalar(UA_Variant *v, void * UA_RESTRICT p,
-                     const UA_DataType *type) {
-    UA_Variant_init(v);
-    v->type = type;
-    v->arrayLength = 0;
-    v->data = p;
-}
+    /* If pointers were transferred, initialize original array to prevent
+     * reuse */
+    if(!copy && !v->type->fixedSize)
+        memset(array, 0, sizeof(elem_size)*arraySize);
 
-UA_StatusCode
-UA_Variant_setScalarCopy(UA_Variant *v, const void *p,
-                         const UA_DataType *type) {
-    void *new = UA_malloc(type->memSize);
-    if(!new)
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-    UA_StatusCode retval = UA_copy(p, new, type);
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_free(new);
-        //cppcheck-suppress memleak
-        return retval;
-    }
-    UA_Variant_setScalar(v, new, type);
-    //cppcheck-suppress memleak
-    return UA_STATUSCODE_GOOD;
+    return retval;
 }
 
-void UA_Variant_setArray(UA_Variant *v, void * UA_RESTRICT array,
-                         size_t arraySize, const UA_DataType *type) {
-    UA_Variant_init(v);
-    v->data = array;
-    v->arrayLength = arraySize;
-    v->type = type;
+UA_StatusCode
+UA_Variant_setRange(UA_Variant *v, void * UA_RESTRICT array, size_t arraySize,
+                    const UA_NumericRange range) {
+    return Variant_setRange(v, array, arraySize, range, false);
 }
 
 UA_StatusCode
-UA_Variant_setArrayCopy(UA_Variant *v, const void *array,
-                        size_t arraySize, const UA_DataType *type) {
-    UA_Variant_init(v);
-    UA_StatusCode retval = UA_Array_copy(array, arraySize, &v->data, type);
-    if(retval != UA_STATUSCODE_GOOD)
-        return retval;
-    v->arrayLength = arraySize;
-    v->type = type;
-    return UA_STATUSCODE_GOOD;
+UA_Variant_setRangeCopy(UA_Variant *v, const void *array, size_t arraySize,
+                        const UA_NumericRange range) {
+    return Variant_setRange(v, (void*)(uintptr_t)array, arraySize, range, true);
 }
 
 /* LocalizedText */