浏览代码

improve range handling

Julius Pfrommer 10 年之前
父节点
当前提交
a7482ed0a6
共有 4 个文件被更改,包括 76 次插入57 次删除
  1. 14 12
      include/ua_types.h
  2. 11 11
      src/server/ua_server_binary.c
  3. 13 12
      src/server/ua_services_attribute.c
  4. 38 22
      src/ua_types.c

+ 14 - 12
include/ua_types.h

@@ -477,33 +477,35 @@ UA_StatusCode UA_EXPORT UA_Variant_setArrayCopy(UA_Variant *v, const void *array
                                                 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.
+ * Copy the variant, but use only a subset of the (multidimensional) array into a variant. Returns
+ * an error code if the variant is not an 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).
+ * Insert a range of data into an existing variant. The data array can't be reused afterwards if it
+ * contains types without a fixed size (e.g. strings) since they take on the lifetime of the
+ * variant.
  *
  * @param v The variant
- * @param data The data array. Obviously the type must match the variant and the length the range.
+ * @param dataArray The data array. The type must match the variant
+ * @param dataarraySize The length of the data array. This is checked to match the range size.
  * @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);
+UA_StatusCode UA_EXPORT UA_Variant_setRange(UA_Variant *v, void *dataArray, UA_Int32 dataArraySize,
+                                            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).
+ * Deep-copy a range of data into an existing variant.
  *
- * @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 v The variant
+ * @param dataArray The data array. The type must match the variant
+ * @param dataarraySize The length of the data array. This is checked to match the range size.
  * @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,
+UA_StatusCode UA_EXPORT UA_Variant_setRangeCopy(UA_Variant *v, const void *dataArray, UA_Int32 dataArraySize,
                                                 const UA_NumericRange range);
 
 /****************************/

+ 11 - 11
src/server/ua_server_binary.c

@@ -143,22 +143,22 @@ static void invoke_service(UA_Server *server, UA_SecureChannel *channel,
 }
 
 #define INVOKE_SERVICE(TYPE) do {                                       \
-        UA_##TYPE##Request p;                                           \
-        UA_##TYPE##Response r;                                          \
-        if(UA_##TYPE##Request_decodeBinary(msg, pos, &p))               \
-            return;                                                     \
+        UA_##TYPE##Request p;                                           \
+        UA_##TYPE##Response r;                                          \
+        if(UA_##TYPE##Request_decodeBinary(msg, pos, &p))               \
+            return;                                                     \
         UA_##TYPE##Response_init(&r);                                   \
         invoke_service(server, clientChannel, &p.requestHeader,         \
                        &r.responseHeader,                               \
                        (void (*)(UA_Server*, UA_Session*, void*,void*))Service_##TYPE); \
         UA_##TYPE##Request_deleteMembers(&p);                           \
-        retval = connection->getBuffer(connection, &message,            \
-                     headerSize + UA_##TYPE##Response_calcSizeBinary(&r)); \
-        if(retval != UA_STATUSCODE_GOOD) {                              \
-            UA_##TYPE##Response_deleteMembers(&r);                      \
-            return;                                                     \
-        }                                                               \
-        UA_##TYPE##Response_encodeBinary(&r, &message, &messagePos);    \
+        retval = connection->getBuffer(connection, &message,            \
+                     headerSize + UA_##TYPE##Response_calcSizeBinary(&r)); \
+        if(retval != UA_STATUSCODE_GOOD) {                              \
+            UA_##TYPE##Response_deleteMembers(&r);                      \
+            return;                                                     \
+        }                                                               \
+        UA_##TYPE##Response_encodeBinary(&r, &message, &messagePos);    \
         UA_##TYPE##Response_deleteMembers(&r);                          \
 } while(0)
 

+ 13 - 12
src/server/ua_services_attribute.c

@@ -92,10 +92,11 @@ static void handleSourceTimestamps(UA_TimestampsToReturn timestamps, UA_DataValu
 /** Reads a single attribute from a node in the nodestore. */
 static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
                       const UA_ReadValueId *id, UA_DataValue *v) {
-
+    UA_String binEncoding = UA_STRING("DefaultBinary");
+    UA_String xmlEncoding = UA_STRING("DefaultXml");
 	if(id->dataEncoding.name.length >= 0){
-		if(memcmp(id->dataEncoding.name.data, "DefaultBinary", 13) != 0 &&
-           memcmp(id->dataEncoding.name.data, "DefaultXml", 10) != 0) {
+		if(!UA_String_equal(&binEncoding, &id->dataEncoding.name) &&
+           !UA_String_equal(&xmlEncoding, &id->dataEncoding.name)) {
 			v->hasStatus = UA_TRUE;
 			v->status = UA_STATUSCODE_BADDATAENCODINGINVALID;
 			return;
@@ -493,10 +494,9 @@ static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
                     /* An enum was sent as an int32, or an opaque type as a bytestring. This is
                        detected with the typeIndex indicated the "true" datatype. */
                     wvalue->value.value.type = oldV->type;
-                else if(oldV->type == &UA_TYPES[UA_TYPES_BYTE] &&
-                        (!oldV->data || vn->value.variant.arrayLength > -1) /* isArray */ &&
+                else if(oldV->type == &UA_TYPES[UA_TYPES_BYTE] && !UA_Variant_isScalar(oldV) &&
                         wvalue->value.value.type == &UA_TYPES[UA_TYPES_BYTESTRING] &&
-                        wvalue->value.value.data && wvalue->value.value.arrayLength == -1 /* isScalar */) {
+                        UA_Variant_isScalar(&wvalue->value.value)) {
                     /* a string is written to a byte array */
                     UA_ByteString *str = (UA_ByteString*) wvalue->value.value.data;
                     wvalue->value.value.arrayLength = str->length;
@@ -521,16 +521,17 @@ static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
             if(retval != UA_STATUSCODE_GOOD)
                 goto clean_up;
                 
-            /* insert the new value*/
+            /* insert the new value */
             if(hasRange)
-                retval = UA_Variant_setRange(&newVn->value.variant, wvalue->value.value.data, range);
+                retval = UA_Variant_setRangeCopy(&newVn->value.variant, wvalue->value.value.data,
+                                                 wvalue->value.value.arrayLength, range);
             else {
                 UA_Variant_deleteMembers(&newVn->value.variant);
                 retval = UA_Variant_copy(&wvalue->value.value, &newVn->value.variant);
             }
 
-            if(retval == UA_STATUSCODE_GOOD &&
-                UA_NodeStore_replace(server->nodestore, node, (UA_Node*)newVn, UA_NULL) == UA_STATUSCODE_GOOD) {
+            if(retval == UA_STATUSCODE_GOOD && UA_NodeStore_replace(server->nodestore, node,
+                                                   (UA_Node*)newVn, UA_NULL) == UA_STATUSCODE_GOOD) {
                 if(hasRange)
                     UA_free(range.dimensions);
                 done = UA_TRUE;
@@ -570,8 +571,8 @@ static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
     return retval;
 }
 
-void Service_Write(UA_Server *server, UA_Session *session,
-                   const UA_WriteRequest *request, UA_WriteResponse *response) {
+void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request,
+                   UA_WriteResponse *response) {
     UA_assert(server != UA_NULL && session != UA_NULL && request != UA_NULL && response != UA_NULL);
 
     if(request->nodesToWriteSize <= 0){

+ 38 - 22
src/ua_types.c

@@ -680,7 +680,7 @@ testRangeWithVariant(const UA_Variant *v, const UA_NumericRange range, size_t *t
     /* Test the integrity of the range */
     size_t count = 1;
     if(range.dimensionsSize != dims_count)
-        return UA_STATUSCODE_BADINTERNALERROR;
+        return UA_STATUSCODE_BADINDEXRANGEINVALID;
     for(UA_Int32 i = 0; i < dims_count; i++) {
         if(range.dimensions[i].min > range.dimensions[i].max)
             return UA_STATUSCODE_BADINDEXRANGEINVALID;
@@ -766,47 +766,63 @@ UA_StatusCode UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst, const
     return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode UA_Variant_setRange(UA_Variant *v, void *data, const UA_NumericRange range) {
+UA_StatusCode UA_Variant_setRange(UA_Variant *v, void *dataArray, UA_Int32 dataArraySize,
+                                  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;
+    if((UA_Int32)count != dataArraySize)
+        return UA_STATUSCODE_BADINDEXRANGEINVALID;
 
     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++) {
+    uintptr_t nextsrc = (uintptr_t)dataArray;
+    for(size_t i = 0; i < block_count; i++) {
+        if(!v->type->fixedSize) {
             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;
         }
+        memcpy((void*)nextdst, (void*)nextsrc, elem_size * block_size);
+        nextsrc += block_size * elem_size;
+        nextdst += 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);
+UA_StatusCode UA_EXPORT UA_Variant_setRangeCopy(UA_Variant *v, const void *dataArray, UA_Int32 dataArraySize,
+                                                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;
-    retval = UA_Variant_setRange(dst, data, range);
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_Variant_deleteMembers(dst);
-        return retval;
+    if((UA_Int32)count != dataArraySize)
+        return UA_STATUSCODE_BADINDEXRANGEINVALID;
+
+    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)dataArray;
+    if(v->type->fixedSize) {
+        for(size_t i = 0; i < block_count; i++) {
+            memcpy((void*)nextdst, (void*)nextsrc, elem_size * block_size);
+            nextsrc += block_size * elem_size;
+            nextdst += 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);
+                UA_copy((void*)nextsrc, (void*)nextdst, v->type);
+                nextdst += elem_size;
+                nextsrc += elem_size;
+            }
+            nextdst += (block_distance - block_size) * elem_size;
+        }
     }
     return UA_STATUSCODE_GOOD;
 }