Bladeren bron

Types: Add UA_DataTypeArray as a linked list for client/server

Julius Pfrommer 5 jaren geleden
bovenliggende
commit
49e0a0bb1e

+ 5 - 2
examples/custom_datatype/client_types_custom.c

@@ -10,8 +10,11 @@ int main(void) {
     /* Make your custom datatype known to the stack */
     UA_DataType types[1];
     types[0] = PointType;
-    config.customDataTypes = types;
-    config.customDataTypesSize = 1;
+
+    /* Attention! Here the custom datatypes are allocated on the stack. So they
+     * cannot be accessed from parallel (worker) threads. */
+    UA_DataTypeArray customDataTypes = {config.customDataTypes, 1, types};
+    config.customDataTypes = &customDataTypes;
 
     UA_Client *client = UA_Client_new(config);
     UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");

+ 5 - 2
examples/custom_datatype/server_types_custom.c

@@ -68,8 +68,11 @@ int main(void) {
     members[2] = Point_members[2];
     types[0] = PointType;
     types[0].members = members;
-    config->customDataTypes = types;
-    config->customDataTypesSize = 1;
+
+    /* Attention! Here the custom datatypes are allocated on the stack. So they
+     * cannot be accessed from parallel (worker) threads. */
+    UA_DataTypeArray customDataTypes = {config->customDataTypes, 1, types};
+    config->customDataTypes = &customDataTypes;
 
     UA_Server *server = UA_Server_new(config);
 

+ 3 - 3
include/ua_client_config.h

@@ -60,9 +60,9 @@ typedef struct {
     UA_ConnectClientConnection initConnectionFunc;
     void (*pollConnectionFunc)(UA_Client *client, void *context);
 
-    /* Custom DataTypes */
-    size_t customDataTypesSize;
-    const UA_DataType *customDataTypes;
+    /* Custom DataTypes. Attention! Custom datatypes are not cleaned up together
+     * with the configuration. So it is possible to allocate them on ROM. */
+    const UA_DataTypeArray *customDataTypes;
 
     /* Callback for state changes */
     void (*stateCallback)(UA_Client *client, UA_ClientState clientState);

+ 4 - 3
include/ua_server_config.h

@@ -77,9 +77,10 @@ struct UA_ServerConfig {
     UA_String *serverCapabilities;
 #endif
 
-    /* Custom DataTypes */
-    size_t customDataTypesSize;
-    UA_DataType *customDataTypes;
+    /* Custom DataTypes. Attention! Custom datatypes are not cleaned up together
+     * with the configuration. So it is possible to allocate them on ROM. */
+    const UA_DataTypeArray *customDataTypes;
+
     /**
      * .. note:: See the section on :ref:`generic-types`. Examples for working
      *    with custom data types are provided in

+ 11 - 0
include/ua_types.h

@@ -937,6 +937,17 @@ UA_Guid UA_EXPORT UA_Guid_random(void);     /* no cryptographic entropy */
 # define UA_TYPENAME(name)
 #endif
 
+/* Datatype arrays with custom type definitions can be added in a linked list to
+ * the client or server configuration. Datatype members can point to types in
+ * the same array via the ``memberTypeIndex``. If ``namespaceZero`` is set to
+ * true, the member datatype is looked up in the array of builtin datatypes
+ * instead. */
+typedef struct UA_DataTypeArray {
+    const struct UA_DataTypeArray *next;
+    const size_t typesSize;
+    const UA_DataType *types;
+} UA_DataTypeArray;
+
 /**
  *
  * .. toctree::

+ 2 - 8
plugins/ua_config_default.c

@@ -620,13 +620,7 @@ UA_ServerConfig_delete(UA_ServerConfig *config) {
         config->nodestore.deleteNodestore(config->nodestore.context);
 
     /* Custom DataTypes */
-    if(config->customDataTypesSize > 0) {
-        for(size_t i = 0; i < config->customDataTypesSize; ++i)
-            UA_free(config->customDataTypes[i].members);
-        UA_free(config->customDataTypes);
-        config->customDataTypes = NULL;
-        config->customDataTypesSize = 0;
-    }
+    /* nothing to do */
 
     /* Networking */
     for(size_t i = 0; i < config->networkLayersSize; ++i)
@@ -715,7 +709,7 @@ const UA_ClientConfig UA_ClientConfig_default = {
     UA_ClientConnectionTCP, /* .connectionFunc (for sync connection) */
     UA_ClientConnectionTCP_init, /* .initConnectionFunc (for async client) */
     UA_ClientConnectionTCP_poll_callback, /* .pollConnectionFunc (for async connection) */
-    0,    /* .customDataTypesSize */
+
     NULL, /* .customDataTypes */
 
     NULL, /* .stateCallback */

+ 2 - 4
src/client/ua_client.c

@@ -326,8 +326,7 @@ processAsyncResponse(UA_Client *client, UA_UInt32 requestId, const UA_NodeId *re
     }
 
     /* Decode the response */
-    retval = UA_decodeBinary(responseMessage, offset, response,
-                             responseType, 0, NULL);
+    retval = UA_decodeBinary(responseMessage, offset, response, responseType, NULL);
 
  process:
     if(retval != UA_STATUSCODE_GOOD) {
@@ -392,7 +391,7 @@ processServiceResponse(void *application, UA_SecureChannel *channel,
                          "Received a ServiceFault response");
             UA_init(rd->response, rd->responseType);
             retval = UA_decodeBinary(message, &offset, rd->response,
-                                     &UA_TYPES[UA_TYPES_SERVICEFAULT], 0, NULL);
+                                     &UA_TYPES[UA_TYPES_SERVICEFAULT], NULL);
         } else {
             /* Close the connection */
             UA_LOG_ERROR(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
@@ -412,7 +411,6 @@ processServiceResponse(void *application, UA_SecureChannel *channel,
 
     /* Decode the response */
     retval = UA_decodeBinary(message, &offset, rd->response, rd->responseType,
-                             rd->client->config.customDataTypesSize,
                              rd->client->config.customDataTypes);
 
 finish:

+ 1 - 1
src/server/ua_nodes.c

@@ -331,7 +331,7 @@ copyCommonVariableAttributes(UA_VariableNode *node,
                 for(size_t i=0; i<attr->value.arrayLength; i++) {
                     size_t offset =0;
                     const UA_ExtensionObject *curr = &((const UA_ExtensionObject *)attr->value.data)[i];
-                    UA_StatusCode ret = UA_decodeBinary(&curr->content.encoded.body, &offset, tmpPos, type, 0, NULL);
+                    UA_StatusCode ret = UA_decodeBinary(&curr->content.encoded.body, &offset, tmpPos, type, NULL);
                     if(ret != UA_STATUSCODE_GOOD) {
                         return ret;
                     }

+ 1 - 3
src/server/ua_server_binary.c

@@ -438,9 +438,7 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
     /* Decode the request */
     UA_STACKARRAY(UA_Byte, request, requestType->memSize);
     UA_RequestHeader *requestHeader = (UA_RequestHeader*)request;
-    retval = UA_decodeBinary(msg, &offset, request, requestType,
-                             server->config.customDataTypesSize,
-                             server->config.customDataTypes);
+    retval = UA_decodeBinary(msg, &offset, request, requestType, server->config.customDataTypes);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_DEBUG_CHANNEL(server->config.logger, channel,
                              "Could not decode the request");

+ 3 - 3
src/ua_types.c

@@ -1086,9 +1086,9 @@ UA_Array_delete(void *p, size_t size, const UA_DataType *type) {
 
 UA_Boolean
 isDataTypeNumeric(const UA_DataType *type) {
-    // All data types ids between UA_TYPES_SBYTE and UA_TYPES_DOUBLE are numeric
-    for (int i = UA_TYPES_SBYTE; i <= UA_TYPES_DOUBLE; ++i)
-        if (&UA_TYPES[i] == type)
+    /* All data types between UA_TYPES_SBYTE and UA_TYPES_DOUBLE are numeric */
+    for(size_t i = UA_TYPES_SBYTE; i <= UA_TYPES_DOUBLE; ++i)
+        if(&UA_TYPES[i] == type)
             return true;
     return false;
 }

+ 11 - 14
src/ua_types_encoding_binary.c

@@ -44,8 +44,7 @@ typedef struct {
 
     u16 depth; /* How often did we en-/decoding recurse? */
 
-    size_t customTypesArraySize;
-    const UA_DataType *customTypesArray;
+    const UA_DataTypeArray *customTypes;
 
     UA_exchangeEncodeBuffer exchangeBufferCallback;
     void *exchangeBufferCallbackHandle;
@@ -826,13 +825,14 @@ UA_findDataTypeByBinaryInternal(const UA_NodeId *typeId, Ctx *ctx) {
             return &UA_TYPES[i];
     }
 
-    /* When other namespace look in custom types, too */
-    if(typeId->namespaceIndex != 0) {
-        for(size_t i = 0; i < ctx->customTypesArraySize; ++i) {
-            if(ctx->customTypesArray[i].binaryEncodingId == typeId->identifier.numeric &&
-               ctx->customTypesArray[i].typeId.namespaceIndex == typeId->namespaceIndex)
-                return &ctx->customTypesArray[i];
+    const UA_DataTypeArray *customTypes = ctx->customTypes;
+    while(customTypes) {
+        for(size_t i = 0; i < customTypes->typesSize; ++i) {
+            if(customTypes->types[i].binaryEncodingId == typeId->identifier.numeric &&
+               customTypes->types[i].typeId.namespaceIndex == typeId->namespaceIndex)
+                return &customTypes->types[i];
         }
+        customTypes = customTypes->next;
     }
 
     return NULL;
@@ -841,8 +841,7 @@ UA_findDataTypeByBinaryInternal(const UA_NodeId *typeId, Ctx *ctx) {
 const UA_DataType *
 UA_findDataTypeByBinary(const UA_NodeId *typeId) {
     Ctx ctx;
-    ctx.customTypesArraySize = 0;
-    ctx.customTypesArray = NULL;
+    ctx.customTypes = NULL;
     return UA_findDataTypeByBinaryInternal(typeId, &ctx);
 }
 
@@ -1525,15 +1524,13 @@ decodeBinaryInternal(void *dst, const UA_DataType *type, Ctx *ctx) {
 
 status
 UA_decodeBinary(const UA_ByteString *src, size_t *offset, void *dst,
-                const UA_DataType *type, size_t customTypesSize,
-                const UA_DataType *customTypes) {
+                const UA_DataType *type, const UA_DataTypeArray *customTypes) {
     /* Set up the context */
     Ctx ctx;
     ctx.pos = &src->data[*offset];
     ctx.end = &src->data[src->length];
     ctx.depth = 0;
-    ctx.customTypesArraySize = customTypesSize;
-    ctx.customTypesArray = customTypes;
+    ctx.customTypes = customTypes;
 
     /* Decode */
     memset(dst, 0, type->memSize); /* Initialize the value */

+ 2 - 2
src/ua_types_encoding_binary.h

@@ -63,8 +63,8 @@ UA_encodeBinary(const void *src, const UA_DataType *type,
  * @return Returns a statuscode whether decoding succeeded. */
 UA_StatusCode
 UA_decodeBinary(const UA_ByteString *src, size_t *offset, void *dst,
-                const UA_DataType *type, size_t customTypesSize,
-                const UA_DataType *customTypes) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
+                const UA_DataType *type, const UA_DataTypeArray *customTypes)
+    UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
 /* Returns the number of bytes the value p takes in binary encoding. Returns
  * zero if an error occurs. UA_calcSizeBinary is thread-safe and reentrant since

+ 5 - 3
tests/check_types_custom.c

@@ -73,6 +73,8 @@ static const UA_DataType PointType = {
     members
 };
 
+const UA_DataTypeArray customDataTypes = {NULL, 1, &PointType};
+
 START_TEST(parseCustomScalar) {
     Point p;
     p.x = 1.0;
@@ -96,7 +98,7 @@ START_TEST(parseCustomScalar) {
 
     UA_Variant var2;
     size_t offset = 0;
-    retval = UA_decodeBinary(&buf, &offset, &var2, &UA_TYPES[UA_TYPES_VARIANT], 1, &PointType);
+    retval = UA_decodeBinary(&buf, &offset, &var2, &UA_TYPES[UA_TYPES_VARIANT], &customDataTypes);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     ck_assert(var2.type == &PointType);
 
@@ -132,7 +134,7 @@ START_TEST(parseCustomScalarExtensionObject) {
 
     UA_ExtensionObject eo2;
     size_t offset = 0;
-    retval = UA_decodeBinary(&buf, &offset, &eo2, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], 1, &PointType);
+    retval = UA_decodeBinary(&buf, &offset, &eo2, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], &customDataTypes);
     ck_assert_int_eq(offset, (uintptr_t)(bufPos - buf.data));
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
@@ -171,7 +173,7 @@ START_TEST(parseCustomArray) {
 
     UA_Variant var2;
     size_t offset = 0;
-    retval = UA_decodeBinary(&buf, &offset, &var2, &UA_TYPES[UA_TYPES_VARIANT], 1, &PointType);
+    retval = UA_decodeBinary(&buf, &offset, &var2, &UA_TYPES[UA_TYPES_VARIANT], &customDataTypes);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     ck_assert(var2.type == &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]);
     ck_assert_int_eq(var2.arrayLength, 10);

+ 4 - 4
tests/check_types_memory.c

@@ -130,7 +130,7 @@ START_TEST(encodeShallYieldDecode) {
     // when
     void *obj2 = UA_new(&UA_TYPES[_i]);
     size_t offset = 0;
-    retval = UA_decodeBinary(&msg1, &offset, obj2, &UA_TYPES[_i], 0, NULL);
+    retval = UA_decodeBinary(&msg1, &offset, obj2, &UA_TYPES[_i], NULL);
     ck_assert_msg(retval == UA_STATUSCODE_GOOD, "could not decode idx=%d,nodeid=%i",
                   _i, UA_TYPES[_i].typeId.identifier.numeric);
     ck_assert(!memcmp(obj1, obj2, UA_TYPES[_i].memSize)); // bit identical decoding
@@ -199,7 +199,7 @@ START_TEST(decodeShallFailWithTruncatedBufferButSurvive) {
     // when
     void *obj2 = UA_new(&UA_TYPES[_i]);
     size_t offset = 0;
-    retval = UA_decodeBinary(&msg1, &offset, obj2, &UA_TYPES[_i], 0, NULL);
+    retval = UA_decodeBinary(&msg1, &offset, obj2, &UA_TYPES[_i], NULL);
     ck_assert_int_ne(retval, UA_STATUSCODE_GOOD);
     UA_delete(obj2, &UA_TYPES[_i]);
     UA_ByteString_deleteMembers(&msg1);
@@ -232,7 +232,7 @@ START_TEST(decodeScalarBasicTypeFromRandomBufferShallSucceed) {
         }
         size_t pos = 0;
         obj1 = UA_new(&UA_TYPES[_i]);
-        retval |= UA_decodeBinary(&msg1, &pos, obj1, &UA_TYPES[_i], 0, NULL);
+        retval |= UA_decodeBinary(&msg1, &pos, obj1, &UA_TYPES[_i], NULL);
         //then
         ck_assert_msg(retval == UA_STATUSCODE_GOOD,
                       "Decoding %d from random buffer",
@@ -268,7 +268,7 @@ START_TEST(decodeComplexTypeFromRandomBufferShallSurvive) {
         }
         size_t pos = 0;
         void *obj1 = UA_new(&UA_TYPES[_i]);
-        retval |= UA_decodeBinary(&msg1, &pos, obj1, &UA_TYPES[_i], 0, NULL);
+        retval |= UA_decodeBinary(&msg1, &pos, obj1, &UA_TYPES[_i], NULL);
         UA_delete(obj1, &UA_TYPES[_i]);
     }
 

+ 2 - 2
tests/fuzz/fuzz_binary_decode.cc

@@ -29,7 +29,7 @@ static UA_Boolean tortureEncoding(const uint8_t *data, size_t size, size_t *newO
             (UA_Byte *) (void *) data
     };
 
-    UA_StatusCode ret = UA_decodeBinary(&binary, newOffset, dst, &UA_TYPES[typeIndex], 0, nullptr);
+    UA_StatusCode ret = UA_decodeBinary(&binary, newOffset, dst, &UA_TYPES[typeIndex], NULL);
 
     if (ret == UA_STATUSCODE_GOOD) {
         // copy the datatype to test
@@ -78,7 +78,7 @@ static UA_Boolean tortureExtensionObject(const uint8_t *data, size_t size, size_
     UA_StatusCode ret = UA_STATUSCODE_GOOD;
     if (type) {
         void *dstCopy = UA_new(type);
-        ret = UA_decodeBinary(&obj.content.encoded.body, newOffset, dstCopy, type, 0, NULL);
+        ret = UA_decodeBinary(&obj.content.encoded.body, newOffset, dstCopy, type, NULL);
 
         if (ret == UA_STATUSCODE_GOOD) {
             UA_Variant var;

+ 2 - 2
tests/server/check_server_readspeed.c

@@ -95,9 +95,9 @@ START_TEST(readSpeed) {
     clock_t begin, finish;
     begin = clock();
 
-    for(int i = 0; i < 1000000; i++) {
+    for(size_t i = 0; i < 1000000; i++) {
         size_t offset = 0;
-        retval |= UA_decodeBinary(&request_msg, &offset, &rq, &UA_TYPES[UA_TYPES_READREQUEST], 0, NULL);
+        retval |= UA_decodeBinary(&request_msg, &offset, &rq, &UA_TYPES[UA_TYPES_READREQUEST], NULL);
 
         UA_MessageContext_begin(&mc, &testChannel, 0, UA_MESSAGETYPE_MSG);
         retval |= Service_Read(server, &server->adminSession, &mc, &rq, &rh);

+ 1 - 1
tools/generate_datatypes.py

@@ -177,7 +177,7 @@ class Type(object):
         idName = makeCIdentifier(self.name)
         enc = "static UA_INLINE size_t\nUA_%s_calcSizeBinary(const UA_%s *src) {\n    return UA_calcSizeBinary(src, %s);\n}\n"
         enc += "static UA_INLINE UA_StatusCode\nUA_%s_encodeBinary(const UA_%s *src, UA_Byte **bufPos, const UA_Byte *bufEnd) {\n    return UA_encodeBinary(src, %s, bufPos, &bufEnd, NULL, NULL);\n}\n"
-        enc += "static UA_INLINE UA_StatusCode\nUA_%s_decodeBinary(const UA_ByteString *src, size_t *offset, UA_%s *dst) {\n    return UA_decodeBinary(src, offset, dst, %s, 0, NULL);\n}"
+        enc += "static UA_INLINE UA_StatusCode\nUA_%s_decodeBinary(const UA_ByteString *src, size_t *offset, UA_%s *dst) {\n    return UA_decodeBinary(src, offset, dst, %s, NULL);\n}"
         return enc % tuple(list(itertools.chain(*itertools.repeat([idName, idName, self.datatype_ptr()], 3))))
 
 class BuiltinType(Type):