Przeglądaj źródła

Merge branch '0.2'

Julius Pfrommer 8 lat temu
rodzic
commit
afde8a0cfc

+ 6 - 6
examples/server_udp.c

@@ -19,17 +19,17 @@ UA_Boolean running = 1;
 
 static void stopHandler(int sign) {
     printf("Received Ctrl-C\n");
-	running = 0;
+    running = 0;
 }
 
 int main(int argc, char** argv) {
-	signal(SIGINT, stopHandler); /* catches ctrl-c */
+    signal(SIGINT, stopHandler); /* catches ctrl-c */
 
-	UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
+    UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
     UA_ServerNetworkLayer *nl = ServerNetworkLayerUDP_new(UA_ConnectionConfig_standard, 16664);
     UA_Server_addNetworkLayer(server, nl);
 
-	// add a variable node to the adresspace
+    // add a variable node to the adresspace
     UA_VariableAttributes attr;
     UA_VariableAttributes_init(&attr);
     UA_Int32 myInteger = 42;
@@ -45,7 +45,7 @@ int main(int argc, char** argv) {
                               UA_NODEID_NULL, attr, NULL);
 
     UA_StatusCode retval = UA_Server_run(server, 1, &running);
-	UA_Server_delete(server);
+    UA_Server_delete(server);
 
-	return (int) retval;
+    return (int) retval;
 }

+ 0 - 8
include/ua_constants.h

@@ -95,14 +95,6 @@ typedef enum {
 #define UA_WRITEMASK_WRITEMASK                  1<<20
 #define UA_WRITEMASK_VALUEFORVARIABLETYPE       1<<21
 
-/**
- * Encoding Offsets
- * ----------------
- * Subtract from the typeid of the encoding nodeids to get to the type
- * definition. */
-#define UA_ENCODINGOFFSET_XML 1
-#define UA_ENCODINGOFFSET_BINARY 2
-
 /**
  * .. _statuscodes:
  *

+ 3 - 6
include/ua_log.h

@@ -107,12 +107,9 @@ typedef void (*UA_Logger)(UA_LogLevel level, UA_LogCategory category,
  * Convenience macros for complex types
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 #define UA_PRINTF_GUID_FORMAT "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}"
-#define UA_PRINTF_GUID_DATA(GUID) (GUID).identifier.guid.data1,     \
-        (GUID).identifier.guid.data2, (GUID).identifier.guid.data3, \
-        (GUID).identifier.guid.data4[0], (GUID).identifier.guid.data4[1], \
-        (GUID).identifier.guid.data4[2], (GUID).identifier.guid.data4[3], \
-        (GUID).identifier.guid.data4[4], (GUID).identifier.guid.data4[5], \
-        (GUID).identifier.guid.data4[6], (GUID).identifier.guid.data4[7]
+#define UA_PRINTF_GUID_DATA(GUID) (GUID).data1, (GUID).data2, (GUID).data3, \
+        (GUID).data4[0], (GUID).data4[1], (GUID).data4[2], (GUID).data4[3], \
+        (GUID).data4[4], (GUID).data4[5], (GUID).data4[6], (GUID).data4[7]
 
 #define UA_PRINTF_STRING_FORMAT "\"%.*s\""
 #define UA_PRINTF_STRING_DATA(STRING) (STRING).length, (STRING).data

+ 2 - 3
include/ua_server.h

@@ -203,8 +203,7 @@ UA_StatusCode UA_EXPORT
 UA_Server_addRepeatedJob(UA_Server *server, UA_Job job,
                          UA_UInt32 interval, UA_Guid *jobId);
 
-/* Remove repeated job. The entry will be removed asynchronously during the next
- * iteration of the server main loop.
+/* Remove repeated job.
  *
  * @param server The server object.
  * @param jobId The id of the job that shall be removed.
@@ -518,7 +517,7 @@ UA_Server_browseNext(UA_Server *server, UA_Boolean releaseContinuationPoint,
  * differently) */
 typedef UA_StatusCode
 (*UA_NodeIteratorCallback)(UA_NodeId childId, UA_Boolean isInverse,
-						   UA_NodeId referenceTypeId, void *handle);
+                           UA_NodeId referenceTypeId, void *handle);
 #endif
 
 UA_StatusCode UA_EXPORT

+ 11 - 11
include/ua_server_external_ns.h

@@ -81,17 +81,17 @@ typedef UA_StatusCode (*UA_ExternalNodeStore_delete)(void *ensHandle);
 
 typedef struct UA_ExternalNodeStore {
     void *ensHandle;
-	UA_ExternalNodeStore_addNodes addNodes;
-	UA_ExternalNodeStore_deleteNodes deleteNodes;
-	UA_ExternalNodeStore_writeNodes writeNodes;
-	UA_ExternalNodeStore_readNodes readNodes;
-	UA_ExternalNodeStore_browseNodes browseNodes;
-	UA_ExternalNodeStore_translateBrowsePathsToNodeIds translateBrowsePathsToNodeIds;
-	UA_ExternalNodeStore_addReferences addReferences;
-	UA_ExternalNodeStore_deleteReferences deleteReferences;
-	UA_ExternalNodeStore_call call;
-	UA_ExternalNodeStore_addOneWayReference addOneWayReference;
-	UA_ExternalNodeStore_delete destroy;
+    UA_ExternalNodeStore_addNodes addNodes;
+    UA_ExternalNodeStore_deleteNodes deleteNodes;
+    UA_ExternalNodeStore_writeNodes writeNodes;
+    UA_ExternalNodeStore_readNodes readNodes;
+    UA_ExternalNodeStore_browseNodes browseNodes;
+    UA_ExternalNodeStore_translateBrowsePathsToNodeIds translateBrowsePathsToNodeIds;
+    UA_ExternalNodeStore_addReferences addReferences;
+    UA_ExternalNodeStore_deleteReferences deleteReferences;
+    UA_ExternalNodeStore_call call;
+    UA_ExternalNodeStore_addOneWayReference addOneWayReference;
+    UA_ExternalNodeStore_delete destroy;
 } UA_ExternalNodeStore;
 
 UA_StatusCode UA_EXPORT

+ 2 - 0
include/ua_types.h

@@ -745,6 +745,8 @@ struct UA_DataType {
                                     pointers */
     UA_Boolean overlayable  : 1; /* The type has the identical memory layout in
                                     memory and on the binary stream. */
+    //UA_UInt16  xmlEncodingId;    /* NodeId of datatype when encoded as XML */
+    UA_UInt16  binaryEncodingId;    /* NodeId of datatype when encoded as binary */
     UA_DataTypeMember *members;
 };
 

+ 5 - 7
src/client/ua_client.c

@@ -212,7 +212,7 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client, UA_Boolean renew)
     asymHeader.securityPolicyUri = UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#None");
 
     /* id of opensecurechannelrequest */
-    UA_NodeId requestType = UA_NODEID_NUMERIC(0, UA_NS0ID_OPENSECURECHANNELREQUEST + UA_ENCODINGOFFSET_BINARY);
+    UA_NodeId requestType = UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST].binaryEncodingId);
 
     UA_OpenSecureChannelRequest opnSecRq;
     UA_OpenSecureChannelRequest_init(&opnSecRq);
@@ -277,8 +277,7 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client, UA_Boolean renew)
     UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(&reply, &offset, &asymHeader);
     UA_SequenceHeader_decodeBinary(&reply, &offset, &seqHeader);
     UA_NodeId_decodeBinary(&reply, &offset, &requestType);
-    UA_NodeId expectedRequest = UA_NODEID_NUMERIC(0, UA_NS0ID_OPENSECURECHANNELRESPONSE +
-                                                  UA_ENCODINGOFFSET_BINARY);
+    UA_NodeId expectedRequest = UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE].binaryEncodingId);
     if(!UA_NodeId_equal(&requestType, &expectedRequest)) {
         UA_ByteString_deleteMembers(&reply);
         UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
@@ -522,7 +521,7 @@ static UA_StatusCode CloseSecureChannel(UA_Client *client) {
     seqHeader.sequenceNumber = ++channel->sendSequenceNumber;
     seqHeader.requestId = ++client->requestId;
 
-    UA_NodeId typeId = UA_NODEID_NUMERIC(0, UA_NS0ID_CLOSESECURECHANNELREQUEST + UA_ENCODINGOFFSET_BINARY);
+    UA_NodeId typeId = UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST].binaryEncodingId);
 
     UA_ByteString message;
     UA_Connection *c = client->connection;
@@ -733,8 +732,7 @@ void __UA_Client_Service(UA_Client *client, const void *r, const UA_DataType *re
     retval |= UA_SequenceHeader_decodeBinary(&reply, &offset, &seqHeader);
     UA_NodeId responseId;
     retval |= UA_NodeId_decodeBinary(&reply, &offset, &responseId);
-    UA_NodeId expectedNodeId = UA_NODEID_NUMERIC(0, responseType->typeId.identifier.numeric +
-                                                 UA_ENCODINGOFFSET_BINARY);
+    UA_NodeId expectedNodeId = UA_NODEID_NUMERIC(0, responseType->binaryEncodingId);
 
     if(retval != UA_STATUSCODE_GOOD)
         goto finish;
@@ -750,7 +748,7 @@ void __UA_Client_Service(UA_Client *client, const void *r, const UA_DataType *re
 
     /* Todo: we need to demux responses since a publish responses may come at any time */
     if(!UA_NodeId_equal(&responseId, &expectedNodeId) || seqHeader.requestId != requestId) {
-        if(responseId.identifier.numeric != UA_NS0ID_SERVICEFAULT + UA_ENCODINGOFFSET_BINARY) {
+        if(responseId.identifier.numeric != UA_TYPES[UA_TYPES_SERVICEFAULT].binaryEncodingId) {
             UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
                          "Reply answers the wrong request. Expected ns=%i,i=%i. But retrieved ns=%i,i=%i",
                          expectedNodeId.namespaceIndex, expectedNodeId.identifier.numeric,

+ 14 - 14
src/server/ua_nodes.c

@@ -122,21 +122,21 @@ UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst) {
         return UA_STATUSCODE_BADINTERNALERROR;
     
     /* copy standard content */
-	UA_StatusCode retval = UA_NodeId_copy(&src->nodeId, &dst->nodeId);
-	dst->nodeClass = src->nodeClass;
-	retval |= UA_QualifiedName_copy(&src->browseName, &dst->browseName);
-	retval |= UA_LocalizedText_copy(&src->displayName, &dst->displayName);
-	retval |= UA_LocalizedText_copy(&src->description, &dst->description);
-	dst->writeMask = src->writeMask;
-	dst->userWriteMask = src->userWriteMask;
-	if(retval != UA_STATUSCODE_GOOD) {
-    	UA_Node_deleteMembersAnyNodeClass(dst);
+    UA_StatusCode retval = UA_NodeId_copy(&src->nodeId, &dst->nodeId);
+    dst->nodeClass = src->nodeClass;
+    retval |= UA_QualifiedName_copy(&src->browseName, &dst->browseName);
+    retval |= UA_LocalizedText_copy(&src->displayName, &dst->displayName);
+    retval |= UA_LocalizedText_copy(&src->description, &dst->description);
+    dst->writeMask = src->writeMask;
+    dst->userWriteMask = src->userWriteMask;
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_Node_deleteMembersAnyNodeClass(dst);
         return retval;
     }
-	retval |= UA_Array_copy(src->references, src->referencesSize, (void**)&dst->references,
+    retval |= UA_Array_copy(src->references, src->referencesSize, (void**)&dst->references,
                             &UA_TYPES[UA_TYPES_REFERENCENODE]);
-	if(retval != UA_STATUSCODE_GOOD) {
-    	UA_Node_deleteMembersAnyNodeClass(dst);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_Node_deleteMembersAnyNodeClass(dst);
         return retval;
     }
     dst->referencesSize = src->referencesSize;
@@ -170,7 +170,7 @@ UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst) {
     default:
         break;
     }
-	if(retval != UA_STATUSCODE_GOOD)
-    	UA_Node_deleteMembersAnyNodeClass(dst);
+    if(retval != UA_STATUSCODE_GOOD)
+        UA_Node_deleteMembersAnyNodeClass(dst);
     return retval;
 }

+ 8 - 8
src/server/ua_nodestore.h

@@ -37,21 +37,21 @@ void UA_NodeStore_delete(UA_NodeStore *ns);
 /* Create an editable node of the given NodeClass. */
 UA_Node * UA_NodeStore_newNode(UA_NodeClass nodeClass);
 #define UA_NodeStore_newObjectNode() \
-	(UA_ObjectNode*)UA_NodeStore_newNode(UA_NODECLASS_OBJECT)
+    (UA_ObjectNode*)UA_NodeStore_newNode(UA_NODECLASS_OBJECT)
 #define UA_NodeStore_newVariableNode() \
-	(UA_VariableNode*)UA_NodeStore_newNode(UA_NODECLASS_VARIABLE)
+    (UA_VariableNode*)UA_NodeStore_newNode(UA_NODECLASS_VARIABLE)
 #define UA_NodeStore_newMethodNode() \
-	(UA_MethodNode*)UA_NodeStore_newNode(UA_NODECLASS_METHOD)
+    (UA_MethodNode*)UA_NodeStore_newNode(UA_NODECLASS_METHOD)
 #define UA_NodeStore_newObjectTypeNode() \
-	(UA_ObjectTypeNode*)UA_NodeStore_newNode(UA_NODECLASS_OBJECTTYPE)
+    (UA_ObjectTypeNode*)UA_NodeStore_newNode(UA_NODECLASS_OBJECTTYPE)
 #define UA_NodeStore_newVariableTypeNode() \
-	(UA_VariableTypeNode*)UA_NodeStore_newNode(UA_NODECLASS_VARIABLETYPE)
+    (UA_VariableTypeNode*)UA_NodeStore_newNode(UA_NODECLASS_VARIABLETYPE)
 #define UA_NodeStore_newReferenceTypeNode() \
-	(UA_ReferenceTypeNode*)UA_NodeStore_newNode(UA_NODECLASS_REFERENCETYPE)
+    (UA_ReferenceTypeNode*)UA_NodeStore_newNode(UA_NODECLASS_REFERENCETYPE)
 #define UA_NodeStore_newDataTypeNode() \
-	(UA_DataTypeNode*)UA_NodeStore_newNode(UA_NODECLASS_DATATYPE)
+    (UA_DataTypeNode*)UA_NodeStore_newNode(UA_NODECLASS_DATATYPE)
 #define UA_NodeStore_newViewNode() \
-	(UA_ViewNode*)UA_NodeStore_newNode(UA_NODECLASS_VIEW)
+    (UA_ViewNode*)UA_NodeStore_newNode(UA_NODECLASS_VIEW)
 
 /* Delete an editable node. */
 void UA_NodeStore_deleteNode(UA_Node *node);

+ 150 - 48
src/server/ua_server_binary.c

@@ -18,7 +18,8 @@ static void init_response_header(const UA_RequestHeader *p, UA_ResponseHeader *r
 }
 
 static void
-sendError(UA_SecureChannel *channel, const UA_ByteString *msg, size_t offset, const UA_DataType *responseType,
+sendError(UA_SecureChannel *channel, const UA_ByteString *msg,
+          size_t offset, const UA_DataType *responseType,
           UA_UInt32 requestId, UA_StatusCode error) {
     UA_RequestHeader requestHeader;
     UA_StatusCode retval = UA_RequestHeader_decodeBinary(msg, &offset, &requestHeader);
@@ -36,10 +37,11 @@ sendError(UA_SecureChannel *channel, const UA_ByteString *msg, size_t offset, co
 
 /* Returns a complete decoded request (without securechannel headers + padding)
    or UA_BYTESTRING_NULL */
-static UA_ByteString processChunk(UA_SecureChannel *channel, UA_Server *server,
-                                  const UA_TcpMessageHeader *messageHeader, UA_UInt32 requestId,
-                                  const UA_ByteString *msg, size_t offset, size_t chunksize,
-                                  UA_Boolean *deleteRequest) {
+static UA_ByteString
+processChunk(UA_SecureChannel *channel, UA_Server *server,
+             const UA_TcpMessageHeader *messageHeader, UA_UInt32 requestId,
+             const UA_ByteString *msg, size_t offset, size_t chunksize,
+             UA_Boolean *deleteRequest) {
     UA_ByteString bytes = UA_BYTESTRING_NULL;
     switch(messageHeader->messageTypeAndChunkType & 0xff000000) {
     case UA_CHUNKTYPE_INTERMEDIATE:
@@ -48,7 +50,8 @@ static UA_ByteString processChunk(UA_SecureChannel *channel, UA_Server *server,
         break;
     case UA_CHUNKTYPE_FINAL:
         UA_LOG_TRACE_CHANNEL(server->config.logger, channel, "Final chunk message");
-        bytes = UA_SecureChannel_finalizeChunk(channel, requestId, msg, offset, chunksize, deleteRequest);
+        bytes = UA_SecureChannel_finalizeChunk(channel, requestId, msg, offset,
+                                               chunksize, deleteRequest);
         break;
     case UA_CHUNKTYPE_ABORT:
         UA_LOG_INFO_CHANNEL(server->config.logger, channel, "Chunk aborted");
@@ -64,14 +67,18 @@ static void
 getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
                    const UA_DataType **responseType, UA_Service *service,
                    UA_Boolean *requiresSession) {
-    switch(requestTypeId - UA_ENCODINGOFFSET_BINARY) {
+    switch(requestTypeId) {
     case UA_NS0ID_GETENDPOINTSREQUEST:
+    case UA_NS0ID_GETENDPOINTSREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_GETENDPOINTSREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_GetEndpoints;
         *requestType = &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE];
         *requiresSession = false;
         break;
     case UA_NS0ID_FINDSERVERSREQUEST:
+    case UA_NS0ID_FINDSERVERSREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_FINDSERVERSREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_FindServers;
         *requestType = &UA_TYPES[UA_TYPES_FINDSERVERSREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_FINDSERVERSRESPONSE];
@@ -85,52 +92,72 @@ getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
         break;
 #endif
     case UA_NS0ID_CREATESESSIONREQUEST:
+    case UA_NS0ID_CREATESESSIONREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_CREATESESSIONREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_CreateSession;
         *requestType = &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE];
         *requiresSession = false;
         break;
     case UA_NS0ID_ACTIVATESESSIONREQUEST:
+    case UA_NS0ID_ACTIVATESESSIONREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_ACTIVATESESSIONREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_ActivateSession;
         *requestType = &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE];
         break;
     case UA_NS0ID_CLOSESESSIONREQUEST:
+    case UA_NS0ID_CLOSESESSIONREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_CLOSESESSIONREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_CloseSession;
         *requestType = &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE];
         break;
     case UA_NS0ID_READREQUEST:
+    case UA_NS0ID_READREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_READREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_Read;
         *requestType = &UA_TYPES[UA_TYPES_READREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_READRESPONSE];
         break;
     case UA_NS0ID_WRITEREQUEST:
+    case UA_NS0ID_WRITEREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_WRITEREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_Write;
         *requestType = &UA_TYPES[UA_TYPES_WRITEREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_WRITERESPONSE];
         break;
     case UA_NS0ID_BROWSEREQUEST:
+    case UA_NS0ID_BROWSEREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_BROWSEREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_Browse;
         *requestType = &UA_TYPES[UA_TYPES_BROWSEREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_BROWSERESPONSE];
         break;
     case UA_NS0ID_BROWSENEXTREQUEST:
+    case UA_NS0ID_BROWSENEXTREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_BROWSENEXTREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_BrowseNext;
         *requestType = &UA_TYPES[UA_TYPES_BROWSENEXTREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_BROWSENEXTRESPONSE];
         break;
     case UA_NS0ID_REGISTERNODESREQUEST:
+    case UA_NS0ID_REGISTERNODESREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_REGISTERNODESREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_RegisterNodes;
         *requestType = &UA_TYPES[UA_TYPES_REGISTERNODESREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_REGISTERNODESRESPONSE];
         break;
     case UA_NS0ID_UNREGISTERNODESREQUEST:
+    case UA_NS0ID_UNREGISTERNODESREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_UNREGISTERNODESREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_UnregisterNodes;
         *requestType = &UA_TYPES[UA_TYPES_UNREGISTERNODESREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_UNREGISTERNODESRESPONSE];
         break;
     case UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSREQUEST:
+    case UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_TranslateBrowsePathsToNodeIds;
         *requestType = &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE];
@@ -138,50 +165,70 @@ getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS
     case UA_NS0ID_CREATESUBSCRIPTIONREQUEST:
+    case UA_NS0ID_CREATESUBSCRIPTIONREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_CREATESUBSCRIPTIONREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_CreateSubscription;
         *requestType = &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONRESPONSE];
         break;
     case UA_NS0ID_PUBLISHREQUEST:
+    case UA_NS0ID_PUBLISHREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_PUBLISHREQUEST_ENCODING_DEFAULTBINARY:
         *requestType = &UA_TYPES[UA_TYPES_PUBLISHREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_PUBLISHRESPONSE];
         break;
     case UA_NS0ID_REPUBLISHREQUEST:
+    case UA_NS0ID_REPUBLISHREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_REPUBLISHREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_Republish;
         *requestType = &UA_TYPES[UA_TYPES_REPUBLISHREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_REPUBLISHRESPONSE];
         break;
     case UA_NS0ID_MODIFYSUBSCRIPTIONREQUEST:
+    case UA_NS0ID_MODIFYSUBSCRIPTIONREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_MODIFYSUBSCRIPTIONREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_ModifySubscription;
         *requestType = &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE];
         break;
     case UA_NS0ID_SETPUBLISHINGMODEREQUEST:
+    case UA_NS0ID_SETPUBLISHINGMODEREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_SETPUBLISHINGMODEREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_SetPublishingMode;
         *requestType = &UA_TYPES[UA_TYPES_SETPUBLISHINGMODEREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_SETPUBLISHINGMODERESPONSE];
         break;
     case UA_NS0ID_DELETESUBSCRIPTIONSREQUEST:
+    case UA_NS0ID_DELETESUBSCRIPTIONSREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_DELETESUBSCRIPTIONSREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_DeleteSubscriptions;
         *requestType = &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSRESPONSE];
         break;
     case UA_NS0ID_CREATEMONITOREDITEMSREQUEST:
+    case UA_NS0ID_CREATEMONITOREDITEMSREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_CREATEMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_CreateMonitoredItems;
         *requestType = &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSRESPONSE];
         break;
     case UA_NS0ID_DELETEMONITOREDITEMSREQUEST:
+    case UA_NS0ID_DELETEMONITOREDITEMSREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_DELETEMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_DeleteMonitoredItems;
         *requestType = &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSRESPONSE];
         break;
     case UA_NS0ID_MODIFYMONITOREDITEMSREQUEST:
+    case UA_NS0ID_MODIFYMONITOREDITEMSREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_MODIFYMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_ModifyMonitoredItems;
         *requestType = &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSRESPONSE];
         break;
     case UA_NS0ID_SETMONITORINGMODEREQUEST:
+    case UA_NS0ID_SETMONITORINGMODEREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_SETMONITORINGMODEREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_SetMonitoringMode;
         *requestType = &UA_TYPES[UA_TYPES_SETMONITORINGMODEREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_SETMONITORINGMODERESPONSE];
@@ -190,6 +237,8 @@ getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
 
 #ifdef UA_ENABLE_METHODCALLS
     case UA_NS0ID_CALLREQUEST:
+    case UA_NS0ID_CALLREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_CALLREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_Call;
         *requestType = &UA_TYPES[UA_TYPES_CALLREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_CALLRESPONSE];
@@ -198,21 +247,29 @@ getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
 
 #ifdef UA_ENABLE_NODEMANAGEMENT
     case UA_NS0ID_ADDNODESREQUEST:
+    case UA_NS0ID_ADDNODESREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_ADDNODESREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_AddNodes;
         *requestType = &UA_TYPES[UA_TYPES_ADDNODESREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_ADDNODESRESPONSE];
         break;
     case UA_NS0ID_ADDREFERENCESREQUEST:
+    case UA_NS0ID_ADDREFERENCESREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_ADDREFERENCESREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_AddReferences;
         *requestType = &UA_TYPES[UA_TYPES_ADDREFERENCESREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_ADDREFERENCESRESPONSE];
         break;
     case UA_NS0ID_DELETENODESREQUEST:
+    case UA_NS0ID_DELETENODESREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_DELETENODESREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_DeleteNodes;
         *requestType = &UA_TYPES[UA_TYPES_DELETENODESREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_DELETENODESRESPONSE];
         break;
     case UA_NS0ID_DELETEREFERENCESREQUEST:
+    case UA_NS0ID_DELETEREFERENCESREQUEST_ENCODING_DEFAULTXML:
+    case UA_NS0ID_DELETEREFERENCESREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_DeleteReferences;
         *requestType = &UA_TYPES[UA_TYPES_DELETEREFERENCESREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_DELETEREFERENCESRESPONSE];
@@ -236,8 +293,9 @@ static void processHEL(UA_Connection *connection, const UA_ByteString *msg, size
         return;
     }
 
-    connection->remoteConf.maxChunkCount = helloMessage.maxChunkCount; /* may be zero -> unlimited */
-    connection->remoteConf.maxMessageSize = helloMessage.maxMessageSize; /* may be zero -> unlimited */
+    /* Parameterize the connection */
+    connection->remoteConf.maxChunkCount = helloMessage.maxChunkCount; /* zero -> unlimited */
+    connection->remoteConf.maxMessageSize = helloMessage.maxMessageSize; /* zero -> unlimited */
     connection->remoteConf.protocolVersion = helloMessage.protocolVersion;
     connection->remoteConf.recvBufferSize = helloMessage.receiveBufferSize;
     if(connection->localConf.sendBufferSize > helloMessage.receiveBufferSize)
@@ -260,11 +318,15 @@ static void processHEL(UA_Connection *connection, const UA_ByteString *msg, size
     ackHeader.messageTypeAndChunkType = UA_MESSAGETYPE_ACK + UA_CHUNKTYPE_FINAL;
     ackHeader.messageSize = 8 + 20; /* ackHeader + ackMessage */
 
+    /* Get the send buffer from the network layer */
     UA_ByteString ack_msg;
     UA_ByteString_init(&ack_msg);
-    if(connection->getSendBuffer(connection, connection->localConf.sendBufferSize, &ack_msg) != UA_STATUSCODE_GOOD)
+    UA_StatusCode retval =
+        connection->getSendBuffer(connection, connection->localConf.sendBufferSize, &ack_msg);
+    if(retval != UA_STATUSCODE_GOOD)
         return;
 
+    /* Encode and send the response */
     size_t tmpPos = 0;
     UA_TcpMessageHeader_encodeBinary(&ackHeader, &ack_msg, &tmpPos);
     UA_TcpAcknowledgeMessage_encodeBinary(&ackMessage, &ack_msg, &tmpPos);
@@ -273,7 +335,9 @@ static void processHEL(UA_Connection *connection, const UA_ByteString *msg, size
 }
 
 /* OPN -> Open up/renew the securechannel */
-static void processOPN(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg, size_t *offset) {
+static void
+processOPN(UA_Connection *connection, UA_Server *server,
+           const UA_ByteString *msg, size_t *offset) {
     if(connection->state != UA_CONNECTION_ESTABLISHED) {
         connection->close(connection);
         return;
@@ -301,6 +365,7 @@ static void processOPN(UA_Connection *connection, UA_Server *server, const UA_By
     UA_OpenSecureChannelRequest r;
     retval |= UA_OpenSecureChannelRequest_decodeBinary(msg, offset, &r);
 
+    /* Could not decode or wrong service type */
     if(retval != UA_STATUSCODE_GOOD || requestType.identifier.numeric != 446) {
         UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
         UA_NodeId_deleteMembers(&requestType);
@@ -309,6 +374,7 @@ static void processOPN(UA_Connection *connection, UA_Server *server, const UA_By
         return;
     }
 
+    /* Call the service */
     UA_OpenSecureChannelResponse p;
     UA_OpenSecureChannelResponse_init(&p);
     Service_OpenSecureChannel(server, connection, &r, &p);
@@ -347,7 +413,7 @@ static void processOPN(UA_Connection *connection, UA_Server *server, const UA_By
 #endif
     retval |= UA_AsymmetricAlgorithmSecurityHeader_encodeBinary(&asymHeader, &resp_msg, &tmpPos); // just mirror back
     retval |= UA_SequenceHeader_encodeBinary(&seqHeader, &resp_msg, &tmpPos);
-    UA_NodeId responseType = UA_NODEID_NUMERIC(0, UA_NS0ID_OPENSECURECHANNELRESPONSE + UA_ENCODINGOFFSET_BINARY);
+    UA_NodeId responseType = UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE].binaryEncodingId);
     retval |= UA_NodeId_encodeBinary(&responseType, &resp_msg, &tmpPos);
     retval |= UA_OpenSecureChannelResponse_encodeBinary(&p, &resp_msg, &tmpPos);
 
@@ -359,7 +425,7 @@ static void processOPN(UA_Connection *connection, UA_Server *server, const UA_By
         return;
     }
 
-    /* Encode the secureconversationmessageheader */
+    /* Encode the secureconversationmessageheader (cannot fail) and send */
     UA_SecureConversationMessageHeader respHeader;
     respHeader.messageHeader.messageTypeAndChunkType = UA_MESSAGETYPE_OPN + UA_CHUNKTYPE_FINAL;
     respHeader.messageHeader.messageSize = (UA_UInt32)tmpPos;
@@ -375,7 +441,8 @@ static void processOPN(UA_Connection *connection, UA_Server *server, const UA_By
 }
 
 static void
-processRequest(UA_SecureChannel *channel, UA_Server *server, UA_UInt32 requestId, const UA_ByteString *msg) {
+processRequest(UA_SecureChannel *channel, UA_Server *server,
+               UA_UInt32 requestId, const UA_ByteString *msg) {
     /* At 0, the nodeid starts... */
     size_t ppos = 0;
     size_t *offset = &ppos;
@@ -393,8 +460,10 @@ processRequest(UA_SecureChannel *channel, UA_Server *server, UA_UInt32 requestId
     if(requestTypeId.identifierType != UA_NODEIDTYPE_NUMERIC ||
        requestTypeId.namespaceIndex != 0) {
         UA_NodeId_deleteMembers(&requestTypeId);
-        UA_LOG_DEBUG_CHANNEL(server->config.logger, channel, "Received a non-numeric message type NodeId");
-        sendError(channel, msg, requestPos, &UA_TYPES[UA_TYPES_SERVICEFAULT], requestId, UA_STATUSCODE_BADSERVICEUNSUPPORTED);
+        UA_LOG_DEBUG_CHANNEL(server->config.logger, channel,
+                             "Received a non-numeric message type NodeId");
+        sendError(channel, msg, requestPos, &UA_TYPES[UA_TYPES_SERVICEFAULT],
+                  requestId, UA_STATUSCODE_BADSERVICEUNSUPPORTED);
     }
 
     /* Get the service pointers */
@@ -402,16 +471,19 @@ processRequest(UA_SecureChannel *channel, UA_Server *server, UA_UInt32 requestId
     const UA_DataType *requestType = NULL;
     const UA_DataType *responseType = NULL;
     UA_Boolean sessionRequired = true;
-    getServicePointers(requestTypeId.identifier.numeric, &requestType, &responseType, &service, &sessionRequired);
+    getServicePointers(requestTypeId.identifier.numeric, &requestType,
+                       &responseType, &service, &sessionRequired);
     if(!requestType) {
         if(requestTypeId.identifier.numeric == 787) {
             UA_LOG_INFO_CHANNEL(server->config.logger, channel,
-                                "Client requested a subscription, but those are not enabled in the build");
+                                "Client requested a subscription, " \
+                                "but those are not enabled in the build");
         } else {
             UA_LOG_INFO_CHANNEL(server->config.logger, channel, "Unknown request %i",
-                                requestTypeId.identifier.numeric - UA_ENCODINGOFFSET_BINARY);
+                                requestTypeId.identifier.numeric);
         }
-        sendError(channel, msg, requestPos, &UA_TYPES[UA_TYPES_SERVICEFAULT], requestId, UA_STATUSCODE_BADSERVICEUNSUPPORTED);
+        sendError(channel, msg, requestPos, &UA_TYPES[UA_TYPES_SERVICEFAULT],
+                  requestId, UA_STATUSCODE_BADSERVICEUNSUPPORTED);
         return;
     }
     UA_assert(responseType);
@@ -434,6 +506,7 @@ processRequest(UA_SecureChannel *channel, UA_Server *server, UA_UInt32 requestId
     /* Prepare the respone */
     void *response = UA_alloca(responseType->memSize);
     UA_init(response, responseType);
+    UA_Session *session = NULL; /* must be initialized before goto send_response */
 
     /* CreateSession doesn't need a session */
     if(requestType == &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST]) {
@@ -442,14 +515,18 @@ processRequest(UA_SecureChannel *channel, UA_Server *server, UA_UInt32 requestId
     }
 
     /* Find the matching session */
-    UA_Session *session = UA_SecureChannel_getSession(channel, &requestHeader->authenticationToken);
+    session = UA_SecureChannel_getSession(channel, &requestHeader->authenticationToken);
     if(!session)
-        session = UA_SessionManager_getSession(&server->sessionManager, &requestHeader->authenticationToken);
+        session = UA_SessionManager_getSession(&server->sessionManager,
+                                               &requestHeader->authenticationToken);
 
     if(requestType == &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST]) {
         if(!session) {
-            UA_LOG_DEBUG_CHANNEL(server->config.logger, channel, "Trying to activate a session that is not known in the server");
-            sendError(channel, msg, requestPos, responseType, requestId, UA_STATUSCODE_BADSESSIONIDINVALID);
+            UA_LOG_DEBUG_CHANNEL(server->config.logger, channel,
+                                 "Trying to activate a session that is " \
+                                 "not known in the server");
+            sendError(channel, msg, requestPos, responseType,
+                      requestId, UA_STATUSCODE_BADSESSIONIDINVALID);
             UA_deleteMembers(request, requestType);
             return;
         }
@@ -461,9 +538,11 @@ processRequest(UA_SecureChannel *channel, UA_Server *server, UA_UInt32 requestId
     UA_Session anonymousSession;
     if(!session) {
         if(sessionRequired) {
-            UA_LOG_INFO_CHANNEL(server->config.logger, channel, "Service request %i without a valid session",
-                                requestTypeId.identifier.numeric - UA_ENCODINGOFFSET_BINARY);
-            sendError(channel, msg, requestPos, responseType, requestId, UA_STATUSCODE_BADSESSIONIDINVALID);
+            UA_LOG_INFO_CHANNEL(server->config.logger, channel,
+                                "Service request %i without a valid session",
+                                requestType->binaryEncodingId);
+            sendError(channel, msg, requestPos, responseType,
+                      requestId, UA_STATUSCODE_BADSESSIONIDINVALID);
             UA_deleteMembers(request, requestType);
             return;
         }
@@ -474,19 +553,24 @@ processRequest(UA_SecureChannel *channel, UA_Server *server, UA_UInt32 requestId
     }
 
     /* Trying to use a non-activated session? */
-    if(!session->activated && sessionRequired) {
-        UA_LOG_INFO_SESSION(server->config.logger, session, "Calling service %i on a non-activated session",
-                            requestTypeId.identifier.numeric - UA_ENCODINGOFFSET_BINARY);
-        sendError(channel, msg, requestPos, responseType, requestId, UA_STATUSCODE_BADSESSIONNOTACTIVATED);
-        UA_SessionManager_removeSession(&server->sessionManager, &session->authenticationToken);
+    if(sessionRequired && !session->activated) {
+        UA_LOG_INFO_SESSION(server->config.logger, session,
+                            "Calling service %i on a non-activated session",
+                            requestType->binaryEncodingId);
+        sendError(channel, msg, requestPos, responseType,
+                  requestId, UA_STATUSCODE_BADSESSIONNOTACTIVATED);
+        UA_SessionManager_removeSession(&server->sessionManager,
+                                        &session->authenticationToken);
         UA_deleteMembers(request, requestType);
         return;
     }
 
     /* The session is bound to another channel */
     if(session->channel != channel) {
-        UA_LOG_DEBUG_CHANNEL(server->config.logger, channel, "Client tries to use an obsolete securechannel");
-        sendError(channel, msg, requestPos, responseType, requestId, UA_STATUSCODE_BADSECURECHANNELIDINVALID);
+        UA_LOG_DEBUG_CHANNEL(server->config.logger, channel,
+                             "Client tries to use an obsolete securechannel");
+        sendError(channel, msg, requestPos, responseType,
+                  requestId, UA_STATUSCODE_BADSECURECHANNELIDINVALID);
         UA_deleteMembers(request, requestType);
         return;
     }
@@ -510,10 +594,15 @@ processRequest(UA_SecureChannel *channel, UA_Server *server, UA_UInt32 requestId
     /* Send the response */
     init_response_header(request, response);
     retval = UA_SecureChannel_sendBinaryMessage(channel, requestId, response, responseType);
+
     if(retval != UA_STATUSCODE_GOOD)
         UA_LOG_INFO_CHANNEL(server->config.logger, channel, "Could not send the message over "
                              "the SecureChannel with error code 0x%08x", retval);
 
+    /* See if we need to return publish requests without a subscription */
+    if(session && requestType == &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST])
+        UA_Session_answerPublishRequestsWithoutSubscription(session);
+
     /* Clean up */
     UA_deleteMembers(request, requestType);
     UA_deleteMembers(response, responseType);
@@ -521,7 +610,8 @@ processRequest(UA_SecureChannel *channel, UA_Server *server, UA_UInt32 requestId
 
 /* MSG -> Normal request */
 static void
-processMSG(UA_Connection *connection, UA_Server *server, const UA_TcpMessageHeader *messageHeader,
+processMSG(UA_Connection *connection, UA_Server *server,
+           const UA_TcpMessageHeader *messageHeader,
            const UA_ByteString *msg, size_t *offset) {
     /* Decode the header */
     UA_UInt32 channelId = 0;
@@ -596,7 +686,8 @@ processMSG(UA_Connection *connection, UA_Server *server, const UA_TcpMessageHead
 
 /* CLO -> Close the secure channel */
 static void
-processCLO(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg, size_t *offset) {
+processCLO(UA_Connection *connection, UA_Server *server,
+           const UA_ByteString *msg, size_t *offset) {
     UA_UInt32 channelId;
     UA_UInt32 tokenId = 0;
     UA_SequenceHeader sequenceHeader;
@@ -620,7 +711,9 @@ processCLO(UA_Connection *connection, UA_Server *server, const UA_ByteString *ms
 /* Process binary message received from Connection dose not modify UA_ByteString
  * you have to free it youself. use of connection->getSendBuffer() and
  * connection->send() to answer Message */
-void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, const UA_ByteString *msg) {
+void
+UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection,
+                               const UA_ByteString *msg) {
     size_t offset= 0;
     UA_TcpMessageHeader tcpMessageHeader;
     do {
@@ -628,13 +721,15 @@ void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection
         UA_StatusCode retval = UA_TcpMessageHeader_decodeBinary(msg, &offset, &tcpMessageHeader);
         if(retval != UA_STATUSCODE_GOOD) {
             UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                        "Decoding of message header failed on Connection %i", connection->sockfd);
+                        "Decoding of message header failed on Connection %i",
+                        connection->sockfd);
             connection->close(connection);
             break;
         }
         if(tcpMessageHeader.messageSize < 16) {
             UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                        "The message is suspiciously small on Connection %i", connection->sockfd);
+                        "The message is suspiciously small on Connection %i",
+                        connection->sockfd);
             connection->close(connection);
             break;
         }
@@ -645,12 +740,14 @@ void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection
         /* Process the message */
         switch(tcpMessageHeader.messageTypeAndChunkType & 0x00ffffff) {
         case UA_MESSAGETYPE_HEL:
-            UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK, "Connection %i | Process a HEL", connection->sockfd);
+            UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
+                         "Connection %i | Process a HEL", connection->sockfd);
             processHEL(connection, msg, &offset);
             break;
 
         case UA_MESSAGETYPE_OPN:
-            UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK, "Connection %i | Process a OPN", connection->sockfd);
+            UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
+                         "Connection %i | Process a OPN", connection->sockfd);
             processOPN(connection, server, msg, &offset);
             break;
 
@@ -658,29 +755,34 @@ void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection
 #ifndef UA_ENABLE_NONSTANDARD_STATELESS
             if(connection->state != UA_CONNECTION_ESTABLISHED) {
                 UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                             "Connection %i | Received a MSG, but the connection is not established", connection->sockfd);
+                             "Connection %i | Received a MSG, but the connection " \
+                             "is not established", connection->sockfd);
                 connection->close(connection);
                 return;
             }
 #endif
-            UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK, "Connection %i | Process a MSG", connection->sockfd);
+            UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
+                         "Connection %i | Process a MSG", connection->sockfd);
             processMSG(connection, server, &tcpMessageHeader, msg, &offset);
             break;
 
         case UA_MESSAGETYPE_CLO:
-            UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK, "Connection %i | Process a CLO", connection->sockfd);
+            UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
+                         "Connection %i | Process a CLO", connection->sockfd);
             processCLO(connection, server, msg, &offset);
             return;
 
         default:
-            UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK, "Connection %i | Unknown chunk type", connection->sockfd);
+            UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
+                         "Connection %i | Unknown chunk type", connection->sockfd);
         }
 
         /* Loop to process the next message in the stream */
         if(offset != targetpos) {
-            UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_NETWORK, "Connection %i | Message was not entirely processed. "
-                         "Skip from position %i to position %i; message length is %i", connection->sockfd, offset, targetpos,
-                         msg->length);
+            UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_NETWORK,
+                         "Connection %i | Message was not entirely processed. " \
+                         "Skip from position %i to position %i; message length is %i",
+                         connection->sockfd, offset, targetpos, msg->length);
             offset = targetpos;
         }
     } while(msg->length > offset);

+ 1 - 1
src/server/ua_server_internal.h

@@ -74,7 +74,7 @@ struct UA_Server {
 #endif
 
     /* Jobs with a repetition interval */
-    LIST_HEAD(RepeatedJobsList, RepeatedJobs) repeatedJobs;
+    LIST_HEAD(RepeatedJobsList, RepeatedJob) repeatedJobs;
 
 #ifdef UA_ENABLE_MULTITHREADING
     /* Dispatch queue head for the worker threads (the tail should not be in the same cache line) */

+ 166 - 256
src/server/ua_server_worker.c

@@ -33,39 +33,36 @@
  */
 
 #define MAXTIMEOUT 50 // max timeout in millisec until the next main loop iteration
-#define BATCHSIZE 20 // max number of jobs that are dispatched at once to workers
 
-static void processJobs(UA_Server *server, UA_Job *jobs, size_t jobsSize) {
+static void
+processJob(UA_Server *server, UA_Job *job) {
     UA_ASSERT_RCU_UNLOCKED();
     UA_RCU_LOCK();
-    for(size_t i = 0; i < jobsSize; i++) {
-        UA_Job *job = &jobs[i];
-        switch(job->type) {
-        case UA_JOBTYPE_NOTHING:
-            break;
-        case UA_JOBTYPE_DETACHCONNECTION:
-            UA_Connection_detachSecureChannel(job->job.closeConnection);
-            break;
-        case UA_JOBTYPE_BINARYMESSAGE_NETWORKLAYER:
-            UA_Server_processBinaryMessage(server, job->job.binaryMessage.connection,
-                                           &job->job.binaryMessage.message);
-            UA_Connection *connection = job->job.binaryMessage.connection;
-            connection->releaseRecvBuffer(connection, &job->job.binaryMessage.message);
-            break;
-        case UA_JOBTYPE_BINARYMESSAGE_ALLOCATED:
-            UA_Server_processBinaryMessage(server, job->job.binaryMessage.connection,
-                                           &job->job.binaryMessage.message);
-            UA_ByteString_deleteMembers(&job->job.binaryMessage.message);
-            break;
-        case UA_JOBTYPE_METHODCALL:
-        case UA_JOBTYPE_METHODCALL_DELAYED:
-            job->job.methodCall.method(server, job->job.methodCall.data);
-            break;
-        default:
-            UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
-                           "Trying to execute a job of unknown type");
-            break;
-        }
+    switch(job->type) {
+    case UA_JOBTYPE_NOTHING:
+        break;
+    case UA_JOBTYPE_DETACHCONNECTION:
+        UA_Connection_detachSecureChannel(job->job.closeConnection);
+        break;
+    case UA_JOBTYPE_BINARYMESSAGE_NETWORKLAYER:
+        UA_Server_processBinaryMessage(server, job->job.binaryMessage.connection,
+                                       &job->job.binaryMessage.message);
+        UA_Connection *connection = job->job.binaryMessage.connection;
+        connection->releaseRecvBuffer(connection, &job->job.binaryMessage.message);
+        break;
+    case UA_JOBTYPE_BINARYMESSAGE_ALLOCATED:
+        UA_Server_processBinaryMessage(server, job->job.binaryMessage.connection,
+                                       &job->job.binaryMessage.message);
+        UA_ByteString_deleteMembers(&job->job.binaryMessage.message);
+        break;
+    case UA_JOBTYPE_METHODCALL:
+    case UA_JOBTYPE_METHODCALL_DELAYED:
+        job->job.methodCall.method(server, job->job.methodCall.data);
+        break;
+    default:
+        UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
+                       "Trying to execute a job of unknown type");
+        break;
     }
     UA_RCU_UNLOCK();
 }
@@ -81,14 +78,13 @@ struct MainLoopJob {
     UA_Job job;
 };
 
-/** Entry in the dispatch queue */
-struct DispatchJobsList {
+struct DispatchJob {
     struct cds_wfcq_node node; // node for the queue
-    size_t jobsSize;
-    UA_Job *jobs;
+    UA_Job job;
 };
 
-static void * workerLoop(UA_Worker *worker) {
+static void *
+workerLoop(UA_Worker *worker) {
     UA_Server *server = worker->server;
     UA_UInt32 *counter = &worker->counter;
     volatile UA_Boolean *running = &worker->running;
@@ -98,21 +94,19 @@ static void * workerLoop(UA_Worker *worker) {
     rcu_register_thread();
 
     pthread_mutex_t mutex; // required for the condition variable
-    pthread_mutex_init(&mutex,0);
+    pthread_mutex_init(&mutex, 0);
     pthread_mutex_lock(&mutex);
 
     while(*running) {
-        struct DispatchJobsList *wln = (struct DispatchJobsList*)
+        struct DispatchJob *dj = (struct DispatchJob*)
             cds_wfcq_dequeue_blocking(&server->dispatchQueue_head, &server->dispatchQueue_tail);
-        if(!wln) {
-            uatomic_inc(counter);
-            /* sleep until a work arrives (and wakes up all worker threads) */
+        if(dj) {
+            processJob(server, &dj->job);
+            UA_free(dj);
+        } else {
+            /* nothing to do. sleep until a job is dispatched (and wakes up all worker threads) */
             pthread_cond_wait(&server->dispatchQueue_condition, &mutex);
-            continue;
         }
-        processJobs(server, wln->jobs, wln->jobsSize);
-        UA_free(wln->jobs);
-        UA_free(wln);
         uatomic_inc(counter);
     }
 
@@ -124,39 +118,21 @@ static void * workerLoop(UA_Worker *worker) {
     return NULL;
 }
 
-/** Dispatch jobs to workers. Slices the job array up if it contains more than
-    BATCHSIZE items. The jobs array is freed in the worker threads. */
-static void dispatchJobs(UA_Server *server, UA_Job *jobs, size_t jobsSize) {
-    size_t startIndex = jobsSize; // start at the end
-    while(jobsSize > 0) {
-        size_t size = BATCHSIZE;
-        if(size > jobsSize)
-            size = jobsSize;
-        startIndex = startIndex - size;
-        struct DispatchJobsList *wln = UA_malloc(sizeof(struct DispatchJobsList));
-        if(startIndex > 0) {
-            wln->jobs = UA_malloc(size * sizeof(UA_Job));
-            memcpy(wln->jobs, &jobs[startIndex], size * sizeof(UA_Job));
-            wln->jobsSize = size;
-        } else {
-            /* forward the original array */
-            wln->jobsSize = size;
-            wln->jobs = jobs;
-        }
-        cds_wfcq_node_init(&wln->node);
-        cds_wfcq_enqueue(&server->dispatchQueue_head, &server->dispatchQueue_tail, &wln->node);
-        jobsSize -= size;
-    }
+static void
+dispatchJob(UA_Server *server, const UA_Job *job) {
+    struct DispatchJob *dj = UA_malloc(sizeof(struct DispatchJob));
+    dj->job = *job;
+    cds_wfcq_node_init(&dj->node);
+    cds_wfcq_enqueue(&server->dispatchQueue_head, &server->dispatchQueue_tail, &dj->node);
 }
 
 static void
 emptyDispatchQueue(UA_Server *server) {
     while(!cds_wfcq_empty(&server->dispatchQueue_head, &server->dispatchQueue_tail)) {
-        struct DispatchJobsList *wln = (struct DispatchJobsList*)
+        struct DispatchJob *dj = (struct DispatchJob*)
             cds_wfcq_dequeue_blocking(&server->dispatchQueue_head, &server->dispatchQueue_tail);
-        processJobs(server, wln->jobs, wln->jobsSize);
-        UA_free(wln->jobs);
-        UA_free(wln);
+        processJob(server, &dj->job);
+        UA_free(dj);
     }
 }
 
@@ -166,183 +142,119 @@ emptyDispatchQueue(UA_Server *server) {
 /* Repeated Jobs */
 /*****************/
 
-struct IdentifiedJob {
-    UA_Job job;
-    UA_Guid id;
-};
-
-/**
- * The RepeatedJobs structure contains an array of jobs that are either executed with the same
- * repetition interval. The linked list is sorted, so we can stop traversing when the first element
- * has nextTime > now.
- */
-struct RepeatedJobs {
-    LIST_ENTRY(RepeatedJobs) pointers; ///> Links to the next list of repeated jobs (with a different) interval
-    UA_DateTime nextTime; ///> The next time when the jobs are to be executed
-    UA_UInt32 interval; ///> Interval in 100ns resolution
-    size_t jobsSize; ///> Number of jobs contained
-    struct IdentifiedJob jobs[]; ///> The jobs. This is not a pointer, instead the struct is variable sized.
-};
-
-/* throwaway struct for the mainloop callback */
-struct AddRepeatedJob {
-    struct IdentifiedJob job;
-    UA_UInt32 interval;
+/* The linked list of jobs is sorted according to the next execution timestamp */
+struct RepeatedJob {
+    LIST_ENTRY(RepeatedJob) next;  /* Next element in the list */
+    UA_DateTime nextTime;          /* The next time when the jobs are to be executed */
+    UA_UInt32 interval;            /* Interval in 100ns resolution */
+    UA_Guid id;                    /* Id of the repeated job */
+    UA_Job job;                    /* The job description itself */
 };
 
 /* internal. call only from the main loop. */
-static UA_StatusCode addRepeatedJob(UA_Server *server, struct AddRepeatedJob * UA_RESTRICT arw) {
-    struct RepeatedJobs *matchingTw = NULL; // add the item here
-    struct RepeatedJobs *lastTw = NULL; // if there is no repeated job, add a new one this entry
-    struct RepeatedJobs *tempTw;
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-
+static void
+addRepeatedJob(UA_Server *server, struct RepeatedJob * UA_RESTRICT rj)
+{
     /* search for matching entry */
-    UA_DateTime firstTime = UA_DateTime_nowMonotonic() + arw->interval;
-    tempTw = LIST_FIRST(&server->repeatedJobs);
-    while(tempTw) {
-        if(arw->interval == tempTw->interval) {
-            matchingTw = tempTw;
-            break;
-        }
-        if(tempTw->nextTime > firstTime)
+    struct RepeatedJob *lastRj = NULL; /* Add after this entry or at LIST_HEAD if NULL */
+    struct RepeatedJob *tempRj = LIST_FIRST(&server->repeatedJobs);
+    while(tempRj) {
+        if(tempRj->nextTime > rj->nextTime)
             break;
-        lastTw = tempTw;
-        tempTw = LIST_NEXT(lastTw, pointers);
-    }
-
-    if(matchingTw) {
-        /* append to matching entry */
-        matchingTw = UA_realloc(matchingTw, sizeof(struct RepeatedJobs) +
-                                (sizeof(struct IdentifiedJob) * (matchingTw->jobsSize + 1)));
-        if(!matchingTw) {
-            retval = UA_STATUSCODE_BADOUTOFMEMORY;
-            goto cleanup;
-        }
-        /* link the reallocated tw into the list */
-        LIST_REPLACE(matchingTw, matchingTw, pointers);
-    } else {
-        /* create a new entry */
-        matchingTw = UA_malloc(sizeof(struct RepeatedJobs) + sizeof(struct IdentifiedJob));
-        if(!matchingTw) {
-            retval = UA_STATUSCODE_BADOUTOFMEMORY;
-            goto cleanup;
-        }
-        matchingTw->jobsSize = 0;
-        matchingTw->nextTime = firstTime;
-        matchingTw->interval = arw->interval;
-        if(lastTw)
-            LIST_INSERT_AFTER(lastTw, matchingTw, pointers);
-        else
-            LIST_INSERT_HEAD(&server->repeatedJobs, matchingTw, pointers);
+        lastRj = tempRj;
+        tempRj = LIST_NEXT(lastRj, next);
     }
-    matchingTw->jobs[matchingTw->jobsSize] = arw->job;
-    matchingTw->jobsSize++;
 
- cleanup:
-#ifdef UA_ENABLE_MULTITHREADING
-    UA_free(arw);
-#endif
-    return retval;
+    /* add the repeated job */
+    if(lastRj)
+        LIST_INSERT_AFTER(lastRj, rj, next);
+    else
+        LIST_INSERT_HEAD(&server->repeatedJobs, rj, next);
 }
 
-UA_StatusCode UA_Server_addRepeatedJob(UA_Server *server, UA_Job job, UA_UInt32 interval, UA_Guid *jobId) {
+UA_StatusCode
+UA_Server_addRepeatedJob(UA_Server *server, UA_Job job,
+                         UA_UInt32 interval, UA_Guid *jobId) {
     /* the interval needs to be at least 5ms */
     if(interval < 5)
         return UA_STATUSCODE_BADINTERNALERROR;
     interval *= (UA_UInt32)UA_MSEC_TO_DATETIME; // from ms to 100ns resolution
 
-#ifdef UA_ENABLE_MULTITHREADING
-    struct AddRepeatedJob *arw = UA_malloc(sizeof(struct AddRepeatedJob));
-    if(!arw)
+    /* Create and fill the repeated job structure */
+    struct RepeatedJob *rj = UA_malloc(sizeof(struct RepeatedJob));
+    if(!rj)
         return UA_STATUSCODE_BADOUTOFMEMORY;
+    rj->nextTime = UA_DateTime_nowMonotonic() + interval;
+    rj->interval = interval;
+    rj->id = UA_Guid_random();
+    rj->job = job;
 
-    arw->interval = interval;
-    arw->job.job = job;
-    if(jobId) {
-        arw->job.id = UA_Guid_random();
-        *jobId = arw->job.id;
-    } else
-        UA_Guid_init(&arw->job.id);
-
+#ifdef UA_ENABLE_MULTITHREADING
+    /* Call addRepeatedJob from the main loop */
     struct MainLoopJob *mlw = UA_malloc(sizeof(struct MainLoopJob));
     if(!mlw) {
-        UA_free(arw);
+        UA_free(rj);
         return UA_STATUSCODE_BADOUTOFMEMORY;
     }
     mlw->job = (UA_Job) {
         .type = UA_JOBTYPE_METHODCALL,
-        .job.methodCall = {.data = arw, .method = (void (*)(UA_Server*, void*))addRepeatedJob}};
+        .job.methodCall = {.data = rj, .method = (void (*)(UA_Server*, void*))addRepeatedJob}};
     cds_lfs_push(&server->mainLoopJobs, &mlw->node);
 #else
-    struct AddRepeatedJob arw;
-    arw.interval = interval;
-    arw.job.job = job;
-    if(jobId) {
-        arw.job.id = UA_Guid_random();
-        *jobId = arw.job.id;
-    } else
-        UA_Guid_init(&arw.job.id);
-    addRepeatedJob(server, &arw);
+    /* Add directly */
+    addRepeatedJob(server, rj);
 #endif
+    if(jobId)
+        *jobId = rj->id;
     return UA_STATUSCODE_GOOD;
 }
 
 /* Returns the next datetime when a repeated job is scheduled */
-static UA_DateTime processRepeatedJobs(UA_Server *server, UA_DateTime current) {
-    struct RepeatedJobs *tw, *tmp_tw;
+static UA_DateTime
+processRepeatedJobs(UA_Server *server, UA_DateTime current) {
+    struct RepeatedJob *rj, *tmp_rj;
     /* Iterate over the list of elements (sorted according to the next execution timestamp) */
-    LIST_FOREACH_SAFE(tw, &server->repeatedJobs, pointers, tmp_tw) {
-        if(tw->nextTime > current)
+    LIST_FOREACH_SAFE(rj, &server->repeatedJobs, next, tmp_rj) {
+        if(rj->nextTime > current)
             break;
 
+        /* Dispatch/process job */
 #ifdef UA_ENABLE_MULTITHREADING
-        // copy the entry and insert at the new location
-        UA_Job *jobsCopy = UA_malloc(sizeof(UA_Job) * tw->jobsSize);
-        if(!jobsCopy) {
-            UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
-                         "Not enough memory to dispatch delayed jobs");
-            break;
-        }
-        for(size_t i=0;i<tw->jobsSize;i++)
-            jobsCopy[i] = tw->jobs[i].job;
-        dispatchJobs(server, jobsCopy, tw->jobsSize); // frees the job pointer
+        dispatchJob(server, &rj->job);
 #else
-        size_t size = tw->jobsSize;
-        for(size_t i = 0; i < size; i++)
-            processJobs(server, &tw->jobs[i].job, 1); // does not free the job ptr
-#endif
-
-        /* Elements are removed only here. Check if empty. */
-        if(tw->jobsSize == 0) {
-            LIST_REMOVE(tw, pointers);
-            UA_free(tw);
-            UA_assert(LIST_FIRST(&server->repeatedJobs) != tw); /* Assert for static code checkers */
+        struct RepeatedJob **previousNext = rj->next.le_prev;
+        processJob(server, &rj->job);
+        /* See if the current job was deleted during processJob. That means the
+           le_next field of the previous repeated job (could also be the list
+           header) does no longer point to the current repeated job */
+        if((void*)*previousNext != (void*)rj) {
+            UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
+                         "The current repeated job removed itself");
             continue;
         }
+#endif
 
         /* Set the time for the next execution */
-        tw->nextTime += tw->interval;
-        if(tw->nextTime < current)
-            tw->nextTime = current;
+        rj->nextTime += rj->interval;
+        if(rj->nextTime < current)
+            rj->nextTime = current;
 
-        /* Reinsert to keep the list sorted */
-        struct RepeatedJobs *prevTw = LIST_FIRST(&server->repeatedJobs);
+        /* Keep the list sorted */
+        struct RepeatedJob *prev_rj = LIST_FIRST(&server->repeatedJobs);
         while(true) {
-            struct RepeatedJobs *n = LIST_NEXT(prevTw, pointers);
-            if(!n || n->nextTime > tw->nextTime)
+            struct RepeatedJob *n = LIST_NEXT(prev_rj, next);
+            if(!n || n->nextTime > rj->nextTime)
                 break;
-            prevTw = n;
+            prev_rj = n;
         }
-        if(prevTw != tw) {
-            LIST_REMOVE(tw, pointers);
-            LIST_INSERT_AFTER(prevTw, tw, pointers);
+        if(prev_rj != rj) {
+            LIST_REMOVE(rj, next);
+            LIST_INSERT_AFTER(prev_rj, rj, next);
         }
     }
 
-    // check if the next repeated job is sooner than the usual timeout
-    // calc in 32 bit must be ok
-    struct RepeatedJobs *first = LIST_FIRST(&server->repeatedJobs);
+    /* Check if the next repeated job is sooner than the usual timeout */
+    struct RepeatedJob *first = LIST_FIRST(&server->repeatedJobs);
     UA_DateTime next = current + (MAXTIMEOUT * UA_MSEC_TO_DATETIME);
     if(first && first->nextTime < next)
         next = first->nextTime;
@@ -350,23 +262,19 @@ static UA_DateTime processRepeatedJobs(UA_Server *server, UA_DateTime current) {
 }
 
 /* Call this function only from the main loop! */
-static void removeRepeatedJob(UA_Server *server, UA_Guid *jobId) {
-    struct RepeatedJobs *tw;
-    LIST_FOREACH(tw, &server->repeatedJobs, pointers) {
-        for(size_t i = 0; i < tw->jobsSize; i++) {
-            if(!UA_Guid_equal(jobId, &tw->jobs[i].id))
-                continue;
-            tw->jobsSize--; /* if size == 0, tw is freed during the next processing */
-            if(tw->jobsSize > 0)
-                tw->jobs[i] = tw->jobs[tw->jobsSize]; // move the last entry to overwrite
-            goto finish;
-        }
+static void
+removeRepeatedJob(UA_Server *server, UA_Guid *jobId) {
+    struct RepeatedJob *rj;
+    LIST_FOREACH(rj, &server->repeatedJobs, next) {
+        if(!UA_Guid_equal(jobId, &rj->id))
+            continue;
+        LIST_REMOVE(rj, next);
+        UA_free(rj);
+        break;
     }
- finish:
 #ifdef UA_ENABLE_MULTITHREADING
     UA_free(jobId);
 #endif
-    return;
 }
 
 UA_StatusCode UA_Server_removeRepeatedJob(UA_Server *server, UA_Guid jobId) {
@@ -388,9 +296,9 @@ UA_StatusCode UA_Server_removeRepeatedJob(UA_Server *server, UA_Guid jobId) {
 }
 
 void UA_Server_deleteAllRepeatedJobs(UA_Server *server) {
-    struct RepeatedJobs *current, *temp;
-    LIST_FOREACH_SAFE(current, &server->repeatedJobs, pointers, temp) {
-        LIST_REMOVE(current, pointers);
+    struct RepeatedJob *current, *temp;
+    LIST_FOREACH_SAFE(current, &server->repeatedJobs, next, temp) {
+        LIST_REMOVE(current, next);
         UA_free(current);
     }
 }
@@ -418,9 +326,9 @@ static void getCounters(UA_Server *server, struct DelayedJobs *delayed) {
     delayed->workerCounters = counters;
 }
 
-// Call from the main thread only. This is the only function that modifies
-// server->delayedWork. processDelayedWorkQueue modifies the "next" (after the
-// head).
+/* Call from the main thread only. This is the only function that modifies */
+/* server->delayedWork. processDelayedWorkQueue modifies the "next" (after the */
+/* head). */
 static void addDelayedJob(UA_Server *server, UA_Job *job) {
     struct DelayedJobs *dj = server->delayedJobs;
     if(!dj || dj->jobsCount >= DELAYEDJOBSSIZE) {
@@ -438,37 +346,29 @@ static void addDelayedJob(UA_Server *server, UA_Job *job) {
 
         /* dispatch a method that sets the counter for the full list that comes afterwards */
         if(dj->next) {
-            UA_Job *setCounter = UA_malloc(sizeof(UA_Job));
-            *setCounter = (UA_Job) {.type = UA_JOBTYPE_METHODCALL, .job.methodCall =
-                                    {.method = (void (*)(UA_Server*, void*))getCounters, .data = dj->next}};
-            dispatchJobs(server, setCounter, 1);
+            UA_Job setCounter = (UA_Job){
+                .type = UA_JOBTYPE_METHODCALL, .job.methodCall =
+                {.method = (void (*)(UA_Server*, void*))getCounters, .data = dj->next}};
+            dispatchJob(server, &setCounter);
         }
     }
     dj->jobs[dj->jobsCount] = *job;
     dj->jobsCount++;
 }
 
-static void addDelayedJobAsync(UA_Server *server, UA_Job *job) {
-    addDelayedJob(server, job);
-    UA_free(job);
-}
-
-static void server_free(UA_Server *server, void *data) {
+static void
+delayed_free(UA_Server *server, void *data) {
     UA_free(data);
 }
 
 UA_StatusCode UA_Server_delayedFree(UA_Server *server, void *data) {
-    UA_Job *j = UA_malloc(sizeof(UA_Job));
-    if(!j)
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-    j->type = UA_JOBTYPE_METHODCALL;
-    j->job.methodCall.data = data;
-    j->job.methodCall.method = server_free;
-    struct MainLoopJob *mlw = UA_malloc(sizeof(struct MainLoopJob));
-    mlw->job = (UA_Job) {.type = UA_JOBTYPE_METHODCALL, .job.methodCall =
-                         {.data = j, .method = (UA_ServerCallback)addDelayedJobAsync}};
-    cds_lfs_push(&server->mainLoopJobs, &mlw->node);
-    return UA_STATUSCODE_GOOD;
+    return UA_Server_delayedCallback(server, delayed_free, data);
+}
+
+static void
+addDelayedJobAsync(UA_Server *server, UA_Job *job) {
+    addDelayedJob(server, job);
+    UA_free(job);
 }
 
 UA_StatusCode
@@ -522,10 +422,11 @@ dispatchDelayedJobs(UA_Server *server, void *_) {
 #endif
     /* process and free all delayed jobs from here on */
     while(dw) {
-        processJobs(server, dw->jobs, dw->jobsCount);
+        for(size_t i = 0; i < dw->jobsCount; i++)
+            processJob(server, &dw->jobs[i]);
         struct DelayedJobs *next = uatomic_xchg(&beforedw->next, NULL);
-        UA_free(dw);
         UA_free(dw->workerCounters);
+        UA_free(dw);
         dw = next;
     }
 #if (__GNUC__ <= 4 && __GNUC_MINOR__ <= 6)
@@ -549,12 +450,11 @@ static void processMainLoopJobs(UA_Server *server) {
     struct MainLoopJob *mlw = (struct MainLoopJob*)&head->node;
     struct MainLoopJob *next;
     do {
-        processJobs(server, &mlw->job, 1);
+        processJob(server, &mlw->job);
         next = (struct MainLoopJob*)mlw->node.next;
         UA_free(mlw);
         //cppcheck-suppress unreadVariable
     } while((mlw = next));
-    //UA_free(head);
 }
 #endif
 
@@ -591,7 +491,10 @@ UA_StatusCode UA_Server_run_startup(UA_Server *server) {
     return result;
 }
 
-static void completeMessages(UA_Server *server, UA_Job *job) {
+/* completeMessages is run synchronous on the jobs returned from the network
+   layer, so that the order for processing TCP packets is never mixed up. */
+static void
+completeMessages(UA_Server *server, UA_Job *job) {
     UA_Boolean realloced = UA_FALSE;
     UA_StatusCode retval = UA_Connection_completeMessages(job->job.binaryMessage.connection,
                                                           &job->job.binaryMessage.message, &realloced);
@@ -653,16 +556,22 @@ UA_UInt16 UA_Server_run_iterate(UA_Server *server, UA_Boolean waitInternal) {
                 completeMessages(server, &jobs[k]);
         }
 
+        /* Dispatch/process jobs */
+        for(size_t j = 0; j < jobsSize; j++) {
 #ifdef UA_ENABLE_MULTITHREADING
-        dispatchJobs(server, jobs, jobsSize);
-        /* Wake up worker threads */
-        if(jobsSize > 0)
-            pthread_cond_broadcast(&server->dispatchQueue_condition);
+            dispatchJob(server, &jobs[j]);
 #else
-        processJobs(server, jobs, jobsSize);
-        if(jobsSize > 0)
-            UA_free(jobs);
+            processJob(server, &jobs[j]);
+#endif
+        }
+
+        if(jobsSize > 0) {
+#ifdef UA_ENABLE_MULTITHREADING
+            /* Wake up worker threads */
+            pthread_cond_broadcast(&server->dispatchQueue_condition);
 #endif
+            UA_free(jobs);
+        }
     }
 
     now = UA_DateTime_nowMonotonic();
@@ -677,7 +586,8 @@ UA_StatusCode UA_Server_run_shutdown(UA_Server *server) {
         UA_ServerNetworkLayer *nl = &server->config.networkLayers[i];
         UA_Job *stopJobs;
         size_t stopJobsSize = nl->stop(nl, &stopJobs);
-        processJobs(server, stopJobs, stopJobsSize);
+        for(size_t j = 0; j < stopJobsSize; j++)
+            processJob(server, &stopJobs[j]);
         UA_free(stopJobs);
     }
 

+ 6 - 6
src/server/ua_services_call.c

@@ -160,11 +160,11 @@ Service_Call_single(UA_Server *server, UA_Session *session, const UA_CallMethodR
     for(size_t i=0;i<methodCalled->referencesSize;i++){
         if (methodCalled->references[i].isInverse && UA_NodeId_equal(&methodCalled->references[i].targetId.nodeId,&withObject->nodeId)){
             //TODO adjust maxDepth to needed tree depth (define a variable in config?)
-    	    isNodeInTree(server->nodestore, &methodCalled->references[i].referenceTypeId, &hasComponentNodeId,
-    	         &hasSubTypeNodeId, 1, 1, &found);
+            isNodeInTree(server->nodestore, &methodCalled->references[i].referenceTypeId, &hasComponentNodeId,
+                 &hasSubTypeNodeId, 1, 1, &found);
             if(found){
                 break;
-    	    }
+            }
         }
     }
     if(!found)
@@ -250,13 +250,13 @@ void Service_Call(UA_Server *server, UA_Session *session, const UA_CallRequest *
                        indices, (UA_UInt32)indexSize, response->results);
     }
 #endif
-	
+    
     for(size_t i = 0; i < request->methodsToCallSize;i++){
 #ifdef UA_ENABLE_EXTERNAL_NAMESPACES
         if(!isExternal[i])
 #endif    
-			Service_Call_single(server, session, &request->methodsToCall[i], &response->results[i]);
-	}
+            Service_Call_single(server, session, &request->methodsToCall[i], &response->results[i]);
+    }
 }
 
 #endif /* UA_ENABLE_METHODCALLS */

+ 1 - 1
src/server/ua_services_session.c

@@ -44,7 +44,7 @@ void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
          return;
     }
     UA_LOG_DEBUG_CHANNEL(server->config.logger, channel, "Session " UA_PRINTF_GUID_FORMAT " created",
-                         UA_PRINTF_GUID_DATA(newSession->sessionId));
+                         UA_PRINTF_GUID_DATA(newSession->sessionId.identifier.guid));
 }
 
 void

+ 62 - 20
src/server/ua_services_subscription.c

@@ -15,8 +15,16 @@ setSubscriptionSettings(UA_Server *server, UA_Subscription *subscription,
                         UA_Double requestedPublishingInterval,
                         UA_UInt32 requestedLifetimeCount,
                         UA_UInt32 requestedMaxKeepAliveCount,
-                        UA_UInt32 maxNotificationsPerPublish, UA_Byte priority) {
-    Subscription_unregisterPublishJob(server, subscription);
+                        UA_UInt32 maxNotificationsPerPublish, UA_Byte priority)
+{
+    /* deregister the job if required */
+    UA_StatusCode retval = Subscription_unregisterPublishJob(server, subscription);
+    if(retval != UA_STATUSCODE_GOOD)
+        UA_LOG_DEBUG_SESSION(server->config.logger, subscription->session, "Subscription %u | "
+                             "Could not unregister publish job with error code 0x%08x",
+                             subscription->subscriptionID, retval);
+
+    /* re-parameterize the subscription */
     subscription->publishingInterval = requestedPublishingInterval;
     UA_BOUNDEDVALUE_SETWBOUNDS(server->config.publishingIntervalLimits,
                                requestedPublishingInterval, subscription->publishingInterval);
@@ -34,30 +42,45 @@ setSubscriptionSettings(UA_Server *server, UA_Subscription *subscription,
        maxNotificationsPerPublish > server->config.maxNotificationsPerPublish)
         subscription->notificationsPerPublish = server->config.maxNotificationsPerPublish;
     subscription->priority = priority;
-    Subscription_registerPublishJob(server, subscription);
+
+    retval = Subscription_registerPublishJob(server, subscription);
+    if(retval != UA_STATUSCODE_GOOD)
+        UA_LOG_DEBUG_SESSION(server->config.logger, subscription->session, "Subscription %u | "
+                             "Could not register publish job with error code 0x%08x",
+                             subscription->subscriptionID, retval);
 }
 
-void Service_CreateSubscription(UA_Server *server, UA_Session *session,
-                                const UA_CreateSubscriptionRequest *request,
-                                UA_CreateSubscriptionResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing CreateSubscriptionRequest");
-    response->subscriptionId = UA_Session_getUniqueSubscriptionID(session);
+void
+Service_CreateSubscription(UA_Server *server, UA_Session *session,
+                           const UA_CreateSubscriptionRequest *request,
+                           UA_CreateSubscriptionResponse *response)
+{
+    /* Create the subscription */
     UA_Subscription *newSubscription = UA_Subscription_new(session, response->subscriptionId);
     if(!newSubscription) {
+        UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing CreateSubscriptionRequest failed");
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
     }
-
+    newSubscription->subscriptionID = UA_Session_getUniqueSubscriptionID(session);
     UA_Session_addSubscription(session, newSubscription);
+
+    /* Set the subscription parameters */
     newSubscription->publishingEnabled = request->publishingEnabled;
+    newSubscription->currentKeepAliveCount = newSubscription->maxKeepAliveCount;
     setSubscriptionSettings(server, newSubscription, request->requestedPublishingInterval,
                             request->requestedLifetimeCount, request->requestedMaxKeepAliveCount,
                             request->maxNotificationsPerPublish, request->priority);
-    /* immediately send the first response */
-    newSubscription->currentKeepAliveCount = newSubscription->maxKeepAliveCount;
+
+    /* Prepare the response */
+    response->subscriptionId = newSubscription->subscriptionID;
     response->revisedPublishingInterval = newSubscription->publishingInterval;
     response->revisedLifetimeCount = newSubscription->lifeTimeCount;
     response->revisedMaxKeepAliveCount = newSubscription->maxKeepAliveCount;
+
+    UA_LOG_DEBUG_SESSION(server->config.logger, session, "CreateSubscriptionRequest: Created Subscription %u "
+                         "with a publishing interval of %f ms", response->subscriptionId,
+                         newSubscription->publishingInterval);
 }
 
 void Service_ModifySubscription(UA_Server *server, UA_Session *session,
@@ -103,8 +126,14 @@ void Service_SetPublishingMode(UA_Server *server, UA_Session *session,
             response->results[i] = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
             continue;
         }
-        sub->publishingEnabled = request->publishingEnabled;
-        sub->currentLifetimeCount = 0; /* Reset the subscription lifetime */
+        if(sub->publishingEnabled != request->publishingEnabled) {
+            sub->publishingEnabled = request->publishingEnabled;
+            sub->currentLifetimeCount = 0; /* Reset the subscription lifetime */
+            if(sub->publishingEnabled)
+                Subscription_registerPublishJob(server, sub);
+            else
+                Subscription_unregisterPublishJob(server, sub);
+        }
     }
 }
 
@@ -290,7 +319,8 @@ void Service_ModifyMonitoredItems(UA_Server *server, UA_Session *session,
     response->resultsSize = request->itemsToModifySize;
 
     for(size_t i = 0; i < request->itemsToModifySize; i++)
-        Service_ModifyMonitoredItems_single(server, session, sub, &request->itemsToModify[i], &response->results[i]);
+        Service_ModifyMonitoredItems_single(server, session, sub, &request->itemsToModify[i],
+                                            &response->results[i]);
 
 }
 
@@ -407,7 +437,8 @@ Service_Publish(UA_Server *server, UA_Session *session,
     UA_Subscription *immediate;
     LIST_FOREACH(immediate, &session->serverSubscriptions, listEntry) {
         if(immediate->state == UA_SUBSCRIPTIONSTATE_LATE) {
-            UA_LOG_DEBUG_SESSION(server->config.logger, session, "Response on a late subscription",
+            UA_LOG_DEBUG_SESSION(server->config.logger, session, "Subscription %u | "
+                                 "Response on a late subscription", immediate->subscriptionID,
                                  session->authenticationToken.identifier.numeric);
             UA_Subscription_publishCallback(server, immediate);
             return;
@@ -415,9 +446,10 @@ Service_Publish(UA_Server *server, UA_Session *session,
     }
 }
 
-void Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
-                                 const UA_DeleteSubscriptionsRequest *request,
-                                 UA_DeleteSubscriptionsResponse *response) {
+void
+Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
+                            const UA_DeleteSubscriptionsRequest *request,
+                            UA_DeleteSubscriptionsResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing DeleteSubscriptionsRequest");
 
     if(request->subscriptionIdsSize == 0){
@@ -432,14 +464,24 @@ void Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
     }
     response->resultsSize = request->subscriptionIdsSize;
 
-    for(size_t i = 0; i < request->subscriptionIdsSize; i++)
+    for(size_t i = 0; i < request->subscriptionIdsSize; i++) {
         response->results[i] = UA_Session_deleteSubscription(server, session, request->subscriptionIds[i]);
+        if(response->results[i] == UA_STATUSCODE_GOOD) {
+            UA_LOG_DEBUG_SESSION(server->config.logger, session, "Subscription %u | "
+                                "Subscription deleted", request->subscriptionIds[i]);
+        } else {
+            UA_LOG_DEBUG_SESSION(server->config.logger, session, "Deleting Subscription with Id "
+                                 "%u failed with error code 0x%08x", request->subscriptionIds[i],
+                                 response->results[i]);
+        }
+    }
 }
 
 void Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session,
                                   const UA_DeleteMonitoredItemsRequest *request,
                                   UA_DeleteMonitoredItemsResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing DeleteMonitoredItemsRequest");
+    UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                         "Processing DeleteMonitoredItemsRequest");
 
     if(request->monitoredItemIdsSize == 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;

+ 2 - 2
src/server/ua_session_manager.c

@@ -46,7 +46,7 @@ UA_SessionManager_getSession(UA_SessionManager *sm, const UA_NodeId *token) {
             if(UA_DateTime_nowMonotonic() > current->session.validTill) {
                 UA_LOG_DEBUG(sm->server->config.logger, UA_LOGCATEGORY_SESSION,
                              "Try to use Session with token " UA_PRINTF_GUID_FORMAT ", but has timed out",
-                             UA_PRINTF_GUID_DATA((*token)));
+                             UA_PRINTF_GUID_DATA(token->identifier.guid));
                 return NULL;
             }
             return &current->session;
@@ -54,7 +54,7 @@ UA_SessionManager_getSession(UA_SessionManager *sm, const UA_NodeId *token) {
     }
     UA_LOG_DEBUG(sm->server->config.logger, UA_LOGCATEGORY_SESSION,
                  "Try to use Session with token " UA_PRINTF_GUID_FORMAT " but is not found",
-                 UA_PRINTF_GUID_DATA((*token)));
+                 UA_PRINTF_GUID_DATA(token->identifier.guid));
     return NULL;
 }
 

+ 16 - 5
src/server/ua_subscription.c

@@ -55,7 +55,8 @@ void UA_MoniteredItem_SampleCallback(UA_Server *server, UA_MonitoredItem *monito
     MonitoredItem_queuedValue *newvalue = UA_malloc(sizeof(MonitoredItem_queuedValue));
     if(!newvalue) {
         UA_LOG_WARNING_SESSION(server->config.logger, sub->session, "Subscription %u | MonitoredItem %i | "
-                               "Skipped a sample due to lack of memory", sub->subscriptionID, monitoredItem->itemId);
+                               "Skipped a sample due to lack of memory", sub->subscriptionID,
+                               monitoredItem->itemId);
         return;
     }
     UA_DataValue_init(&newvalue->value);
@@ -222,6 +223,9 @@ UA_Subscription_deleteMonitoredItem(UA_Server *server, UA_Subscription *sub,
 }
 
 void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
+    UA_LOG_DEBUG_SESSION(server->config.logger, sub->session, "Subscription %u | "
+                         "Publish Callback", sub->subscriptionID);
+
     /* Count the available notifications */
     size_t notifications = 0;
     UA_Boolean moreNotifications = false;
@@ -262,8 +266,8 @@ void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
         } else {
             sub->currentLifetimeCount++;
             if(sub->currentLifetimeCount > sub->lifeTimeCount) {
-                UA_LOG_INFO_SESSION(server->config.logger, sub->session, "Subscription %u | "
-                                    "End of lifetime for subscription", sub->subscriptionID);
+                UA_LOG_DEBUG_SESSION(server->config.logger, sub->session, "Subscription %u | "
+                                     "End of lifetime for subscription", sub->subscriptionID);
                 UA_Session_deleteSubscription(server, sub->session, sub->subscriptionID);
             }
         }
@@ -316,7 +320,8 @@ void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
                 mon_l++;
             }
             UA_LOG_DEBUG_SESSION(server->config.logger, sub->session, "Subscription %u | MonitoredItem %u | " \
-                                 "Adding %u notifications to the publish response. %u notifications remain in the queue",
+                                 "Adding %u notifications to the publish response. " \
+                                 "%u notifications remain in the queue",
                                  sub->subscriptionID, mon->itemId, mon_l, mon->currentQueueSize);
             l += mon_l;
         }
@@ -352,7 +357,8 @@ void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
 
     /* Send the response */
     UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
-                         "Sending out a publish response with %u notifications", (UA_UInt32)notifications);
+                         "Subscription %u | Sending out a publish response with %u notifications",
+                         sub->subscriptionID, (UA_UInt32)notifications);
     UA_SecureChannel_sendBinaryMessage(sub->session->channel, requestId, response,
                                        &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
 
@@ -368,6 +374,11 @@ void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
 }
 
 UA_StatusCode Subscription_registerPublishJob(UA_Server *server, UA_Subscription *sub) {
+    if(sub->publishJobIsRegistered)
+        return UA_STATUSCODE_GOOD;
+    if(!sub->publishingEnabled)
+        return UA_STATUSCODE_GOOD;
+
     UA_Job job = (UA_Job) {.type = UA_JOBTYPE_METHODCALL,
                            .job.methodCall = {.method = (UA_ServerCallback)UA_Subscription_publishCallback,
                                               .data = sub} };

+ 1 - 1
src/ua_securechannel.c

@@ -219,7 +219,7 @@ UA_SecureChannel_sendBinaryMessage(UA_SecureChannel *channel, UA_UInt32 requestI
     /* Encode the message type */
     size_t messagePos = 0;
     UA_NodeId typeId = contentType->typeId; /* always numeric */
-    typeId.identifier.numeric += UA_ENCODINGOFFSET_BINARY;
+    typeId.identifier.numeric = contentType->binaryEncodingId;
     UA_NodeId_encodeBinary(&typeId, &message, &messagePos);
 
     /* Encode with the chunking callback */

+ 22 - 2
src/ua_session.c

@@ -70,7 +70,8 @@ void UA_Session_deleteMembersCleanup(UA_Session *session, UA_Server* server) {
 }
 
 void UA_Session_updateLifetime(UA_Session *session) {
-    session->validTill = UA_DateTime_nowMonotonic() + (UA_DateTime)(session->timeout * UA_MSEC_TO_DATETIME);
+    session->validTill = UA_DateTime_nowMonotonic() +
+        (UA_DateTime)(session->timeout * UA_MSEC_TO_DATETIME);
 }
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS
@@ -80,7 +81,8 @@ void UA_Session_addSubscription(UA_Session *session, UA_Subscription *newSubscri
 }
 
 UA_StatusCode
-UA_Session_deleteSubscription(UA_Server *server, UA_Session *session, UA_UInt32 subscriptionID) {
+UA_Session_deleteSubscription(UA_Server *server, UA_Session *session,
+                              UA_UInt32 subscriptionID) {
     UA_Subscription *sub = UA_Session_getSubscriptionByID(session, subscriptionID);
     if(!sub)
         return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
@@ -104,5 +106,23 @@ UA_UInt32 UA_Session_getUniqueSubscriptionID(UA_Session *session) {
     return ++(session->lastSubscriptionID);
 }
 
+void UA_Session_answerPublishRequestsWithoutSubscription(UA_Session *session) {
+    /* Do we have publish requests but no subscriptions? */
+    if(LIST_FIRST(&session->serverSubscriptions))
+        return;
+
+    /* Send a response for every queued request */
+    UA_PublishResponseEntry *pre;
+    while((pre = SIMPLEQ_FIRST(&session->responseQueue))) {
+        SIMPLEQ_REMOVE_HEAD(&session->responseQueue, listEntry);
+        UA_PublishResponse *response = &pre->response;
+        UA_UInt32 requestId = pre->requestId;
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOSUBSCRIPTION;
+        response->responseHeader.timestamp = UA_DateTime_now();
+        UA_SecureChannel_sendBinaryMessage(session->channel, requestId, response,
+                                           &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
+        UA_free(pre);
+    }
+}
 
 #endif

+ 8 - 6
src/ua_session.h

@@ -67,6 +67,8 @@ UA_Session_deleteSubscription(UA_Server *server, UA_Session *session,
 
 UA_UInt32
 UA_Session_getUniqueSubscriptionID(UA_Session *session);
+
+void UA_Session_answerPublishRequestsWithoutSubscription(UA_Session *session);
 #endif
 
 /**
@@ -77,42 +79,42 @@ UA_Session_getUniqueSubscriptionID(UA_Session *session);
     UA_LOG_TRACE(LOGGER, UA_LOGCATEGORY_SESSION, "Connection %i | SecureChannel %i | Session " UA_PRINTF_GUID_FORMAT " | " MSG, \
                  (SESSION->channel ? (SESSION->channel->connection ? SESSION->channel->connection->sockfd : 0) : 0), \
                  (SESSION->channel ? SESSION->channel->securityToken.channelId : 0), \
-                 UA_PRINTF_GUID_DATA(SESSION->sessionId), \
+                 UA_PRINTF_GUID_DATA(SESSION->sessionId.identifier.guid), \
                  ##__VA_ARGS__);
 
 #define UA_LOG_DEBUG_SESSION(LOGGER, SESSION, MSG, ...)                 \
     UA_LOG_DEBUG(LOGGER, UA_LOGCATEGORY_SESSION, "Connection %i | SecureChannel %i | Session " UA_PRINTF_GUID_FORMAT " | " MSG, \
                  (SESSION->channel ? (SESSION->channel->connection ? SESSION->channel->connection->sockfd : 0) : 0), \
                  (SESSION->channel ? SESSION->channel->securityToken.channelId : 0), \
-                 UA_PRINTF_GUID_DATA(SESSION->sessionId), \
+                 UA_PRINTF_GUID_DATA(SESSION->sessionId.identifier.guid), \
                  ##__VA_ARGS__);
 
 #define UA_LOG_INFO_SESSION(LOGGER, SESSION, MSG, ...)                  \
     UA_LOG_INFO(LOGGER, UA_LOGCATEGORY_SESSION, "Connection %i | SecureChannel %i | Session " UA_PRINTF_GUID_FORMAT " | " MSG, \
                  (SESSION->channel ? (SESSION->channel->connection ? SESSION->channel->connection->sockfd : 0) : 0), \
                  (SESSION->channel ? SESSION->channel->securityToken.channelId : 0), \
-                 UA_PRINTF_GUID_DATA(SESSION->sessionId), \
+                 UA_PRINTF_GUID_DATA(SESSION->sessionId.identifier.guid), \
                  ##__VA_ARGS__);
 
 #define UA_LOG_WARNING_SESSION(LOGGER, SESSION, MSG, ...)               \
     UA_LOG_WARNING(LOGGER, UA_LOGCATEGORY_SESSION, "Connection %i | SecureChannel %i | Session " UA_PRINTF_GUID_FORMAT " | " MSG, \
                    (SESSION->channel ? (SESSION->channel->connection ? SESSION->channel->connection->sockfd : 0) : 0), \
                    (SESSION->channel ? SESSION->channel->securityToken.channelId : 0), \
-                   UA_PRINTF_GUID_DATA(SESSION->sessionId), \
+                   UA_PRINTF_GUID_DATA(SESSION->sessionId.identifier.guid), \
                    ##__VA_ARGS__);
 
 #define UA_LOG_ERROR_SESSION(LOGGER, SESSION, MSG, ...)                 \
     UA_LOG_ERROR(LOGGER, UA_LOGCATEGORY_SESSION, "Connection %i | SecureChannel %i | Session " UA_PRINTF_GUID_FORMAT " | " MSG, \
                  (SESSION->channel ? (SESSION->channel->connection ? SESSION->channel->connection->sockfd : 0) : 0), \
                  (SESSION->channel ? SESSION->channel->securityToken.channelId : 0), \
-                 UA_PRINTF_GUID_DATA(SESSION->sessionId), \
+                 UA_PRINTF_GUID_DATA(SESSION->sessionId.identifier.guid), \
                  ##__VA_ARGS__);
 
 #define UA_LOG_FATAL_SESSION(LOGGER, SESSION, MSG, ...)                 \
     UA_LOG_FATAL(LOGGER, UA_LOGCATEGORY_SESSION, "Connection %i | SecureChannel %i | Session " UA_PRINTF_GUID_FORMAT " | " MSG, \
                  (SESSION->channel ? (SESSION->channel->connection ? SESSION->channel->connection->sockfd : 0) : 0), \
                  (SESSION->channel ? SESSION->channel->securityToken.channelId : 0), \
-                 UA_PRINTF_GUID_DATA(SESSION->sessionId), \
+                 UA_PRINTF_GUID_DATA(SESSION->sessionId.identifier.guid), \
                  ##__VA_ARGS__);
 
 #endif /* UA_SESSION_H_ */

+ 85 - 41
src/ua_types.c

@@ -6,7 +6,14 @@
 #include "pcg_basic.h"
 #include "libc_time.h"
 
-/* static variables */
+/* Datatype Handling
+ * -----------------
+ * This file contains handling functions for the builtin types and functions
+ * handling of structured types and arrays. These need type descriptions in a
+ * UA_DataType structure. The UA_DataType structures as well as all non-builtin
+ * datatypes are autogenerated. */
+
+/* Static definition of NULL type instances */
 UA_EXPORT const UA_String UA_STRING_NULL = {.length = 0, .data = NULL };
 UA_EXPORT const UA_ByteString UA_BYTESTRING_NULL = {.length = 0, .data = NULL };
 UA_EXPORT const UA_Guid UA_GUID_NULL = {.data1 = 0, .data2 = 0, .data3 = 0, .data4 = {0,0,0,0,0,0,0,0}};
@@ -20,46 +27,56 @@ static void UA_deleteMembers_noInit(void *p, const UA_DataType *type);
 /***************************/
 /* Random Number Generator */
 /***************************/
+
 static UA_THREAD_LOCAL pcg32_random_t UA_rng = PCG32_INITIALIZER;
 
-UA_EXPORT void UA_random_seed(UA_UInt64 seed) {
+void
+UA_random_seed(UA_UInt64 seed) {
     pcg32_srandom_r(&UA_rng, seed, (uint64_t)UA_DateTime_now());
 }
 
+UA_UInt32
+UA_UInt32_random(void) {
+    return (UA_UInt32)pcg32_random_r(&UA_rng);
+}
+
 /*****************/
 /* Builtin Types */
 /*****************/
-UA_EXPORT UA_UInt32 UA_UInt32_random(void) {
-    return (UA_UInt32)pcg32_random_r(&UA_rng);
-}
 
-UA_String UA_String_fromChars(char const src[]) {
+UA_String
+UA_String_fromChars(char const src[]) {
     UA_String str = UA_STRING_NULL;
     size_t length = strlen(src);
     if(length > 0) {
         str.data = UA_malloc(length);
         if(!str.data)
             return str;
-    } else
+    } else {
         str.data = UA_EMPTY_ARRAY_SENTINEL;
+    }
     memcpy(str.data, src, length);
     str.length = length;
     return str;
 }
 
-UA_Boolean UA_String_equal(const UA_String *string1, const UA_String *string2) {
+UA_Boolean
+UA_String_equal(const UA_String *string1, const UA_String *string2) {
     if(string1->length != string2->length)
         return false;
-    UA_Int32 is = memcmp((char const*)string1->data, (char const*)string2->data, string1->length);
+    UA_Int32 is = memcmp((char const*)string1->data,
+                         (char const*)string2->data, string1->length);
     return (is == 0) ? true : false;
 }
 
-static void String_deleteMembers(UA_String *s, const UA_DataType *_) {
+static void
+String_deleteMembers(UA_String *s, const UA_DataType *_) {
     UA_free((void*)((uintptr_t)s->data & ~(uintptr_t)UA_EMPTY_ARRAY_SENTINEL));
 }
 
 /* DateTime */
-UA_DateTimeStruct UA_DateTime_toStruct(UA_DateTime t) {
+UA_DateTimeStruct
+UA_DateTime_toStruct(UA_DateTime t) {
     /* Calculating the the milli-, micro- and nanoseconds */
     UA_DateTimeStruct dateTimeStruct;
     dateTimeStruct.nanoSec  = (UA_UInt16)((t % 10) * 100);
@@ -80,14 +97,16 @@ UA_DateTimeStruct UA_DateTime_toStruct(UA_DateTime t) {
     return dateTimeStruct;
 }
 
-static void printNumber(UA_UInt16 n, UA_Byte *pos, size_t digits) {
+static void
+printNumber(UA_UInt16 n, UA_Byte *pos, size_t digits) {
     for(size_t i = digits; i > 0; i--) {
         pos[i-1] = (UA_Byte)((n % 10) + '0');
         n = n / 10;
     }
 }
 
-UA_String UA_DateTime_toString(UA_DateTime t) {
+UA_String
+UA_DateTime_toString(UA_DateTime t) {
     UA_String str = UA_STRING_NULL;
     // length of the string is 31 (plus \0 at the end)
     if(!(str.data = UA_malloc(32)))
@@ -115,13 +134,15 @@ UA_String UA_DateTime_toString(UA_DateTime t) {
 }
 
 /* Guid */
-UA_Boolean UA_Guid_equal(const UA_Guid *g1, const UA_Guid *g2) {
+UA_Boolean
+UA_Guid_equal(const UA_Guid *g1, const UA_Guid *g2) {
     if(memcmp(g1, g2, sizeof(UA_Guid)) == 0)
         return true;
     return false;
 }
 
-UA_Guid UA_Guid_random(void) {
+UA_Guid
+UA_Guid_random(void) {
     UA_Guid result;
     result.data1 = (UA_UInt32)pcg32_random_r(&UA_rng);
     UA_UInt32 r = (UA_UInt32)pcg32_random_r(&UA_rng);
@@ -141,7 +162,8 @@ UA_Guid UA_Guid_random(void) {
 }
 
 /* ByteString */
-UA_StatusCode UA_ByteString_allocBuffer(UA_ByteString *bs, size_t length) {
+UA_StatusCode
+UA_ByteString_allocBuffer(UA_ByteString *bs, size_t length) {
     if(length == 0) {
         UA_ByteString_init(bs);
         return UA_STATUSCODE_GOOD;
@@ -153,7 +175,8 @@ UA_StatusCode UA_ByteString_allocBuffer(UA_ByteString *bs, size_t length) {
 }
 
 /* NodeId */
-static void NodeId_deleteMembers(UA_NodeId *p, const UA_DataType *_) {
+static void
+NodeId_deleteMembers(UA_NodeId *p, const UA_DataType *_) {
     switch(p->identifierType) {
     case UA_NODEIDTYPE_STRING:
     case UA_NODEIDTYPE_BYTESTRING:
@@ -163,7 +186,8 @@ static void NodeId_deleteMembers(UA_NodeId *p, const UA_DataType *_) {
     }
 }
 
-static UA_StatusCode NodeId_copy(UA_NodeId const *src, UA_NodeId *dst, const UA_DataType *_) {
+static UA_StatusCode
+NodeId_copy(UA_NodeId const *src, UA_NodeId *dst, const UA_DataType *_) {
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     switch(src->identifierType) {
     case UA_NODEIDTYPE_NUMERIC:
@@ -186,7 +210,8 @@ static UA_StatusCode NodeId_copy(UA_NodeId const *src, UA_NodeId *dst, const UA_
     return retval;
 }
 
-UA_Boolean UA_NodeId_isNull(const UA_NodeId *p) {
+UA_Boolean
+UA_NodeId_isNull(const UA_NodeId *p) {
     if(p->namespaceIndex != 0)
         return false;
     switch(p->identifierType) {
@@ -210,7 +235,8 @@ UA_Boolean UA_NodeId_isNull(const UA_NodeId *p) {
     return (p->identifier.string.length == 0);
 }
 
-UA_Boolean UA_NodeId_equal(const UA_NodeId *n1, const UA_NodeId *n2) {
+UA_Boolean
+UA_NodeId_equal(const UA_NodeId *n1, const UA_NodeId *n2) {
     if(n1->namespaceIndex != n2->namespaceIndex || n1->identifierType!=n2->identifierType)
         return false;
     switch(n1->identifierType) {
@@ -230,13 +256,15 @@ UA_Boolean UA_NodeId_equal(const UA_NodeId *n1, const UA_NodeId *n2) {
 }
 
 /* ExpandedNodeId */
-static void ExpandedNodeId_deleteMembers(UA_ExpandedNodeId *p, const UA_DataType *_) {
+static void
+ExpandedNodeId_deleteMembers(UA_ExpandedNodeId *p, const UA_DataType *_) {
     NodeId_deleteMembers(&p->nodeId, _);
     String_deleteMembers(&p->namespaceUri, NULL);
 }
 
 static UA_StatusCode
-ExpandedNodeId_copy(UA_ExpandedNodeId const *src, UA_ExpandedNodeId *dst, const UA_DataType *_) {
+ExpandedNodeId_copy(UA_ExpandedNodeId const *src, UA_ExpandedNodeId *dst,
+                    const UA_DataType *_) {
     UA_StatusCode retval = NodeId_copy(&src->nodeId, &dst->nodeId, NULL);
     retval |= UA_String_copy(&src->namespaceUri, &dst->namespaceUri);
     dst->serverIndex = src->serverIndex;
@@ -244,7 +272,8 @@ ExpandedNodeId_copy(UA_ExpandedNodeId const *src, UA_ExpandedNodeId *dst, const
 }
 
 /* ExtensionObject */
-static void ExtensionObject_deleteMembers(UA_ExtensionObject *p, const UA_DataType *_) {
+static void
+ExtensionObject_deleteMembers(UA_ExtensionObject *p, const UA_DataType *_) {
     switch(p->encoding) {
     case UA_EXTENSIONOBJECT_ENCODED_NOBODY:
     case UA_EXTENSIONOBJECT_ENCODED_BYTESTRING:
@@ -262,7 +291,8 @@ static void ExtensionObject_deleteMembers(UA_ExtensionObject *p, const UA_DataTy
 }
 
 static UA_StatusCode
-ExtensionObject_copy(UA_ExtensionObject const *src, UA_ExtensionObject *dst, const UA_DataType *_) {
+ExtensionObject_copy(UA_ExtensionObject const *src, UA_ExtensionObject *dst,
+                     const UA_DataType *_) {
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     switch(src->encoding) {
     case UA_EXTENSIONOBJECT_ENCODED_NOBODY:
@@ -288,7 +318,8 @@ ExtensionObject_copy(UA_ExtensionObject const *src, UA_ExtensionObject *dst, con
 }
 
 /* Variant */
-static void Variant_deletemembers(UA_Variant *p, const UA_DataType *_) {
+static void
+Variant_deletemembers(UA_Variant *p, const UA_DataType *_) {
     if(p->storageType != UA_VARIANT_DATA)
         return;
     if(p->type && p->data > UA_EMPTY_ARRAY_SENTINEL) {
@@ -328,8 +359,9 @@ Variant_copy(UA_Variant const *src, UA_Variant *dst, const UA_DataType *_) {
  * - 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) {
+processRangeDefinition(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;
@@ -511,16 +543,18 @@ UA_Variant_setRangeCopy(UA_Variant *v, const void *array, size_t arraySize,
     return retval;
 }
 
-void UA_Variant_setScalar(UA_Variant *v, void * UA_RESTRICT p,
-                          const UA_DataType *type) {
+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) {
+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;
@@ -571,7 +605,8 @@ LocalizedText_copy(UA_LocalizedText const *src, UA_LocalizedText *dst,
 }
 
 /* DataValue */
-static void DataValue_deleteMembers(UA_DataValue *p, const UA_DataType *_) {
+static void
+DataValue_deleteMembers(UA_DataValue *p, const UA_DataType *_) {
     Variant_deletemembers(&p->value, NULL);
 }
 
@@ -622,7 +657,8 @@ DiagnosticInfo_copy(UA_DiagnosticInfo const *src, UA_DiagnosticInfo *dst,
 /* Structured Types */
 /********************/
 
-void * UA_new(const UA_DataType *type) {
+void *
+UA_new(const UA_DataType *type) {
     void *p = UA_calloc(1, type->memSize);
     return p;
 }
@@ -691,7 +727,8 @@ static const UA_copySignature copyJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
     (UA_copySignature)copyNoInit // all others
 };
 
-static UA_StatusCode copyNoInit(const void *src, void *dst, const UA_DataType *type) {
+static UA_StatusCode
+copyNoInit(const void *src, void *dst, const UA_DataType *type) {
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     uintptr_t ptrs = (uintptr_t)src;
     uintptr_t ptrd = (uintptr_t)dst;
@@ -726,7 +763,8 @@ static UA_StatusCode copyNoInit(const void *src, void *dst, const UA_DataType *t
     return retval;
 }
 
-UA_StatusCode UA_copy(const void *src, void *dst, const UA_DataType *type) {
+UA_StatusCode
+UA_copy(const void *src, void *dst, const UA_DataType *type) {
     memset(dst, 0, type->memSize); /* init */
     UA_StatusCode retval = copyNoInit(src, dst, type);
     if(retval != UA_STATUSCODE_GOOD)
@@ -766,7 +804,8 @@ static const UA_deleteMembersSignature deleteMembersJumpTable[UA_BUILTIN_TYPES_C
     (UA_deleteMembersSignature)UA_deleteMembers_noInit,
 };
 
-static void UA_deleteMembers_noInit(void *p, const UA_DataType *type) {
+static void
+UA_deleteMembers_noInit(void *p, const UA_DataType *type) {
     uintptr_t ptr = (uintptr_t)p;
     UA_Byte membersSize = type->membersSize;
     for(size_t i = 0; i < membersSize; i++) {
@@ -788,12 +827,14 @@ static void UA_deleteMembers_noInit(void *p, const UA_DataType *type) {
     }
 }
 
-void UA_deleteMembers(void *p, const UA_DataType *type) {
+void
+UA_deleteMembers(void *p, const UA_DataType *type) {
     UA_deleteMembers_noInit(p, type);
     memset(p, 0, type->memSize); /* init */
 }
 
-void UA_delete(void *p, const UA_DataType *type) {
+void
+UA_delete(void *p, const UA_DataType *type) {
     UA_deleteMembers_noInit(p, type);
     UA_free(p);
 }
@@ -802,14 +843,16 @@ void UA_delete(void *p, const UA_DataType *type) {
 /* Array Handling */
 /******************/
 
-void * UA_Array_new(size_t size, const UA_DataType *type) {
+void *
+UA_Array_new(size_t size, const UA_DataType *type) {
     if(size == 0)
         return UA_EMPTY_ARRAY_SENTINEL;
     return UA_calloc(size, type->memSize);
 }
 
 UA_StatusCode
-UA_Array_copy(const void *src, size_t src_size, void **dst, const UA_DataType *type) {
+UA_Array_copy(const void *src, size_t src_size,
+              void **dst, const UA_DataType *type) {
     if(src_size == 0) {
         if(src == NULL)
             *dst = NULL;
@@ -846,7 +889,8 @@ UA_Array_copy(const void *src, size_t src_size, void **dst, const UA_DataType *t
     return retval;
 }
 
-void UA_Array_delete(void *p, size_t size, const UA_DataType *type) {
+void
+UA_Array_delete(void *p, size_t size, const UA_DataType *type) {
     if(!type->fixedSize) {
         uintptr_t ptr = (uintptr_t)p;
         for(size_t i = 0; i < size; i++) {

+ 218 - 126
src/ua_types_encoding_binary.c

@@ -3,6 +3,22 @@
 #include "ua_types_generated.h"
 #include "ua_types_generated_handling.h"
 
+/* Type Encoding
+ * -------------
+
+ * This file contains encoding functions for the builtin data types and generic
+ * functions that operate on all types and arrays. This requires the type
+ * description from a UA_DataType structure. Note that some internal (static)
+ * deocidng functions may abort and leave the type in an inconsistent state. But
+ * this is always handled in UA_decodeBinary, where the error is caught and the
+ * type cleaned up.
+ *
+ * Breaking a message into chunks is integrated with the encoding. When the end
+ * of a buffer is reached, a callback is executed that sends the current buffer
+ * as a chunk and exchanges the encoding buffer "underneath" the ongoing
+ * encoding. This enables fast sending of large messages as spurious copying is
+ * avoided. */
+
 /* There is no robust way to detect float endianness in clang. This warning can
  * be removed if the target is known to be little endian with floats in the IEEE
  * 754 format. */
@@ -47,7 +63,8 @@ UA_THREAD_LOCAL UA_exchangeEncodeBuffer exchangeBufferCallback;
 UA_THREAD_LOCAL void *exchangeBufferCallbackHandle;
 
 /* Send the current chunk and replace the buffer */
-static UA_StatusCode exchangeBuffer(void) {
+static UA_StatusCode
+exchangeBuffer(void) {
     if(!exchangeBufferCallback)
         return UA_STATUSCODE_BADENCODINGERROR;
 
@@ -76,31 +93,55 @@ static UA_StatusCode exchangeBuffer(void) {
 
 /* The following en/decoding functions are used only when the architecture isn't
    little-endian. */
-static void UA_encode16(const UA_UInt16 v, UA_Byte buf[2]) {
-    buf[0] = (UA_Byte)v; buf[1] = (UA_Byte)(v >> 8);
+static void
+UA_encode16(const UA_UInt16 v, UA_Byte buf[2]) {
+    buf[0] = (UA_Byte)v;
+    buf[1] = (UA_Byte)(v >> 8);
 }
-static void UA_decode16(const UA_Byte buf[2], UA_UInt16 *v) {
+
+static void
+UA_decode16(const UA_Byte buf[2], UA_UInt16 *v) {
     *v = (UA_UInt16)((UA_UInt16)buf[0] + (((UA_UInt16)buf[1]) << 8));
 }
-static void UA_encode32(const UA_UInt32 v, UA_Byte buf[4]) {
-    buf[0] = (UA_Byte)v;         buf[1] = (UA_Byte)(v >> 8);
-    buf[2] = (UA_Byte)(v >> 16); buf[3] = (UA_Byte)(v >> 24);
+
+static void
+UA_encode32(const UA_UInt32 v, UA_Byte buf[4]) {
+    buf[0] = (UA_Byte)v;
+    buf[1] = (UA_Byte)(v >> 8);
+    buf[2] = (UA_Byte)(v >> 16);
+    buf[3] = (UA_Byte)(v >> 24);
 }
-static void UA_decode32(const UA_Byte buf[4], UA_UInt32 *v) {
-    *v = (UA_UInt32)((UA_UInt32)buf[0] + (((UA_UInt32)buf[1]) << 8) +
-                    (((UA_UInt32)buf[2]) << 16) + (((UA_UInt32)buf[3]) << 24));
+
+static void
+UA_decode32(const UA_Byte buf[4], UA_UInt32 *v) {
+    *v = (UA_UInt32)((UA_UInt32)buf[0] +
+                     (((UA_UInt32)buf[1]) << 8) +
+                     (((UA_UInt32)buf[2]) << 16) +
+                     (((UA_UInt32)buf[3]) << 24));
 }
-static void UA_encode64(const UA_UInt64 v, UA_Byte buf[8]) {
-    buf[0] = (UA_Byte)v;         buf[1] = (UA_Byte)(v >> 8);
-    buf[2] = (UA_Byte)(v >> 16); buf[3] = (UA_Byte)(v >> 24);
-    buf[4] = (UA_Byte)(v >> 32); buf[5] = (UA_Byte)(v >> 40);
-    buf[6] = (UA_Byte)(v >> 48); buf[7] = (UA_Byte)(v >> 56);
+
+static void
+UA_encode64(const UA_UInt64 v, UA_Byte buf[8]) {
+    buf[0] = (UA_Byte)v;
+    buf[1] = (UA_Byte)(v >> 8);
+    buf[2] = (UA_Byte)(v >> 16);
+    buf[3] = (UA_Byte)(v >> 24);
+    buf[4] = (UA_Byte)(v >> 32);
+    buf[5] = (UA_Byte)(v >> 40);
+    buf[6] = (UA_Byte)(v >> 48);
+    buf[7] = (UA_Byte)(v >> 56);
 }
-static void UA_decode64(const UA_Byte buf[8], UA_UInt64 *v) {
-    *v = (UA_UInt64)((UA_UInt64)buf[0] + (((UA_UInt64)buf[1]) << 8) +
-                    (((UA_UInt64)buf[2]) << 16) + (((UA_UInt64)buf[3]) << 24) +
-                    (((UA_UInt64)buf[4]) << 32) + (((UA_UInt64)buf[5]) << 40) +
-                    (((UA_UInt64)buf[6]) << 48) + (((UA_UInt64)buf[7]) << 56));
+
+static void
+UA_decode64(const UA_Byte buf[8], UA_UInt64 *v) {
+    *v = (UA_UInt64)((UA_UInt64)buf[0] +
+                     (((UA_UInt64)buf[1]) << 8) +
+                     (((UA_UInt64)buf[2]) << 16) +
+                     (((UA_UInt64)buf[3]) << 24) +
+                     (((UA_UInt64)buf[4]) << 32) +
+                     (((UA_UInt64)buf[5]) << 40) +
+                     (((UA_UInt64)buf[6]) << 48) +
+                     (((UA_UInt64)buf[7]) << 56));
 }
 
 /* Boolean */
@@ -174,7 +215,9 @@ UInt16_decodeBinary(UA_UInt16 *dst, const UA_DataType *_) {
 }
 
 static UA_INLINE UA_StatusCode
-Int16_decodeBinary(UA_Int16 *dst) { return UInt16_decodeBinary((UA_UInt16*)dst, NULL); }
+Int16_decodeBinary(UA_Int16 *dst) {
+    return UInt16_decodeBinary((UA_UInt16*)dst, NULL);
+}
 
 /* UInt32 */
 static UA_StatusCode
@@ -191,10 +234,14 @@ UInt32_encodeBinary(UA_UInt32 const *src, const UA_DataType *_) {
 }
 
 static UA_INLINE UA_StatusCode
-Int32_encodeBinary(UA_Int32 const *src) { return UInt32_encodeBinary((const UA_UInt32*)src, NULL); }
+Int32_encodeBinary(UA_Int32 const *src) {
+    return UInt32_encodeBinary((const UA_UInt32*)src, NULL);
+}
 
 static UA_INLINE UA_StatusCode
-StatusCode_encodeBinary(UA_StatusCode const *src) { return UInt32_encodeBinary((const UA_UInt32*)src, NULL); }
+StatusCode_encodeBinary(UA_StatusCode const *src) {
+    return UInt32_encodeBinary((const UA_UInt32*)src, NULL);
+}
 
 static UA_StatusCode
 UInt32_decodeBinary(UA_UInt32 *dst, const UA_DataType *_) {
@@ -210,10 +257,14 @@ UInt32_decodeBinary(UA_UInt32 *dst, const UA_DataType *_) {
 }
 
 static UA_INLINE UA_StatusCode
-Int32_decodeBinary(UA_Int32 *dst) { return UInt32_decodeBinary((UA_UInt32*)dst, NULL); }
+Int32_decodeBinary(UA_Int32 *dst) {
+    return UInt32_decodeBinary((UA_UInt32*)dst, NULL);
+}
 
 static UA_INLINE UA_StatusCode
-StatusCode_decodeBinary(UA_StatusCode *dst) { return UInt32_decodeBinary((UA_UInt32*)dst, NULL); }
+StatusCode_decodeBinary(UA_StatusCode *dst) {
+    return UInt32_decodeBinary((UA_UInt32*)dst, NULL);
+}
 
 /* UInt64 */
 static UA_StatusCode
@@ -230,10 +281,14 @@ UInt64_encodeBinary(UA_UInt64 const *src, const UA_DataType *_) {
 }
 
 static UA_INLINE UA_StatusCode
-Int64_encodeBinary(UA_Int64 const *src) { return UInt64_encodeBinary((const UA_UInt64*)src, NULL); }
+Int64_encodeBinary(UA_Int64 const *src) {
+    return UInt64_encodeBinary((const UA_UInt64*)src, NULL);
+}
 
 static UA_INLINE UA_StatusCode
-DateTime_encodeBinary(UA_DateTime const *src) { return UInt64_encodeBinary((const UA_UInt64*)src, NULL); }
+DateTime_encodeBinary(UA_DateTime const *src) {
+    return UInt64_encodeBinary((const UA_UInt64*)src, NULL);
+}
 
 static UA_StatusCode
 UInt64_decodeBinary(UA_UInt64 *dst, const UA_DataType *_) {
@@ -249,10 +304,14 @@ UInt64_decodeBinary(UA_UInt64 *dst, const UA_DataType *_) {
 }
 
 static UA_INLINE UA_StatusCode
-Int64_decodeBinary(UA_Int64 *dst) { return UInt64_decodeBinary((UA_UInt64*)dst, NULL); }
+Int64_decodeBinary(UA_Int64 *dst) {
+    return UInt64_decodeBinary((UA_UInt64*)dst, NULL);
+}
 
 static UA_INLINE UA_StatusCode
-DateTime_decodeBinary(UA_DateTime *dst) { return UInt64_decodeBinary((UA_UInt64*)dst, NULL); }
+DateTime_decodeBinary(UA_DateTime *dst) {
+    return UInt64_decodeBinary((UA_UInt64*)dst, NULL);
+}
 
 /************************/
 /* Floating Point Types */
@@ -268,9 +327,10 @@ DateTime_decodeBinary(UA_DateTime *dst) { return UInt64_decodeBinary((UA_UInt64*
 #include <math.h>
 
 /* Handling of IEEE754 floating point values was taken from Beej's Guide to
-   Network Programming (http://beej.us/guide/bgnet/) and enhanced to cover the
-   edge cases +/-0, +/-inf and nan. */
-static uint64_t pack754(long double f, unsigned bits, unsigned expbits) {
+ * Network Programming (http://beej.us/guide/bgnet/) and enhanced to cover the
+ * edge cases +/-0, +/-inf and nan. */
+static uint64_t
+pack754(long double f, unsigned bits, unsigned expbits) {
     unsigned significandbits = bits - expbits - 1;
     long double fnorm;
     long long sign;
@@ -285,7 +345,8 @@ static uint64_t pack754(long double f, unsigned bits, unsigned expbits) {
     return (uint64_t)((sign<<(bits-1)) | (exponent<<(bits-expbits-1)) | significand);
 }
 
-static long double unpack754(uint64_t i, unsigned bits, unsigned expbits) {
+static long double
+unpack754(uint64_t i, unsigned bits, unsigned expbits) {
     unsigned significandbits = bits - expbits - 1;
     long double result = (long double)(i&(uint64_t)((1LL<<significandbits)-1));
     result /= (1LL<<significandbits);
@@ -425,8 +486,8 @@ Array_encodeBinary(const void *src, size_t length, const UA_DataType *type) {
 }
 
 static UA_StatusCode
-Array_decodeBinary(void *UA_RESTRICT *UA_RESTRICT dst, size_t *out_length,
-                   const UA_DataType *type) {
+Array_decodeBinary(void *UA_RESTRICT *UA_RESTRICT dst,
+                   size_t *out_length, const UA_DataType *type) {
     /* decode the length */
     UA_Int32 signed_length;
     UA_StatusCode retval = Int32_decodeBinary(&signed_length);
@@ -494,10 +555,14 @@ String_decodeBinary(UA_String *dst, const UA_DataType *_) {
 }
 
 static UA_INLINE UA_StatusCode
-ByteString_encodeBinary(UA_ByteString const *src) { return String_encodeBinary((const UA_String*)src, NULL); }
+ByteString_encodeBinary(UA_ByteString const *src) {
+    return String_encodeBinary((const UA_String*)src, NULL);
+}
 
 static UA_INLINE UA_StatusCode
-ByteString_decodeBinary(UA_ByteString *dst) { return String_decodeBinary((UA_ByteString*)dst, NULL); }
+ByteString_decodeBinary(UA_ByteString *dst) {
+    return String_decodeBinary((UA_ByteString*)dst, NULL);
+}
 
 /* Guid */
 static UA_StatusCode
@@ -624,7 +689,7 @@ NodeId_decodeBinary(UA_NodeId *dst, const UA_DataType *_) {
         retval |= ByteString_decodeBinary(&dst->identifier.byteString);
         break;
     default:
-        retval |= UA_STATUSCODE_BADINTERNALERROR; // the client sends an encodingByte we do not recognize
+        retval |= UA_STATUSCODE_BADINTERNALERROR;
         break;
     }
     return retval;
@@ -697,6 +762,17 @@ LocalizedText_decodeBinary(UA_LocalizedText *dst, const UA_DataType *_) {
     return retval;
 }
 
+static UA_StatusCode
+findDataTypeByBinary(const UA_NodeId *typeId, const UA_DataType **findtype) {
+    for(size_t i = 0; i < UA_TYPES_COUNT; i++) {
+        if (UA_TYPES[i].binaryEncodingId == typeId->identifier.numeric) {
+            *findtype = &UA_TYPES[i];
+            return UA_STATUSCODE_GOOD;
+        }
+    }
+    return UA_STATUSCODE_BADNODEIDUNKNOWN;
+}
+
 /* ExtensionObject */
 static UA_StatusCode
 ExtensionObject_encodeBinary(UA_ExtensionObject const *src, const UA_DataType *_) {
@@ -708,7 +784,7 @@ ExtensionObject_encodeBinary(UA_ExtensionObject const *src, const UA_DataType *_
         UA_NodeId typeId = src->content.decoded.type->typeId;
         if(typeId.identifierType != UA_NODEIDTYPE_NUMERIC)
             return UA_STATUSCODE_BADENCODINGERROR;
-        typeId.identifier.numeric += UA_ENCODINGOFFSET_BINARY;
+        typeId.identifier.numeric= src->content.decoded.type->binaryEncodingId;
         encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING;
         retval = NodeId_encodeBinary(&typeId, NULL);
         retval |= Byte_encodeBinary(&encoding, NULL);
@@ -740,16 +816,6 @@ ExtensionObject_encodeBinary(UA_ExtensionObject const *src, const UA_DataType *_
     return retval;
 }
 
-static UA_StatusCode findDataType(const UA_NodeId *typeId, const UA_DataType **findtype) {
-    for(size_t i = 0; i < UA_TYPES_COUNT; i++) {
-        if(UA_NodeId_equal(typeId, &UA_TYPES[i].typeId)) {
-            *findtype = &UA_TYPES[i];
-            return UA_STATUSCODE_GOOD;
-        }
-    }
-    return UA_STATUSCODE_BADNODEIDUNKNOWN;
-}
-
 static UA_StatusCode
 ExtensionObject_decodeBinary(UA_ExtensionObject *dst, const UA_DataType *_) {
     UA_Byte encoding = 0;
@@ -778,8 +844,7 @@ ExtensionObject_decodeBinary(UA_ExtensionObject *dst, const UA_DataType *_) {
         /* helping clang analyzer, typeId is numeric */
         UA_assert(typeId.identifier.byteString.data == NULL);
         UA_assert(typeId.identifier.string.data == NULL);
-        typeId.identifier.numeric -= UA_ENCODINGOFFSET_BINARY;
-        findDataType(&typeId, &type);
+        findDataTypeByBinary(&typeId, &type);
         if(type) {
             pos += 4; /* jump over the length (todo: check if length matches) */
             dst->content.decoded.data = UA_new(type);
@@ -788,8 +853,9 @@ ExtensionObject_decodeBinary(UA_ExtensionObject *dst, const UA_DataType *_) {
                 dst->content.decoded.type = type;
                 dst->encoding = UA_EXTENSIONOBJECT_DECODED;
                 retval = decodeBinaryJumpTable[decode_index](dst->content.decoded.data, type);
-            } else
+            } else {
                 retval = UA_STATUSCODE_BADOUTOFMEMORY;
+            }
         } else {
             retval = ByteString_decodeBinary(&dst->content.encoded.body);
             dst->encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING;
@@ -823,10 +889,12 @@ Variant_encodeBinary(UA_Variant const *src, const UA_DataType *_) {
             encodingByte |= UA_VARIANT_ENCODINGMASKTYPE_DIMENSIONS;
     }
     if(isBuiltin) {
-        UA_Byte t = (UA_Byte) (UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK & (src->type->typeIndex + 1));
+        UA_Byte t = (UA_Byte)(UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK & (src->type->typeIndex + 1));
         encodingByte |= t;
-    } else
-        encodingByte |= UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK & (UA_Byte) 22; /* ExtensionObject */
+    } else {
+        /* Non-builtin types are wrapped in an ExtensionObject */
+        encodingByte |= UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK & (UA_Byte) 22;
+    }
     UA_StatusCode retval = Byte_encodeBinary(&encodingByte, NULL);
 
     /* Encode the content */
@@ -870,7 +938,8 @@ Variant_encodeBinary(UA_Variant const *src, const UA_DataType *_) {
 
     /* Encode the dimensions */
     if(hasDimensions)
-        retval |= Array_encodeBinary(src->arrayDimensions, src->arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]);
+        retval |= Array_encodeBinary(src->arrayDimensions, src->arrayDimensionsSize,
+                                     &UA_TYPES[UA_TYPES_INT32]);
 
     return retval;
 }
@@ -924,8 +993,7 @@ Variant_decodeBinary(UA_Variant *dst, const UA_DataType *_) {
         if(typeId.namespaceIndex == 0 && typeId.identifierType == UA_NODEIDTYPE_NUMERIC &&
            eo_encoding == UA_EXTENSIONOBJECT_ENCODED_BYTESTRING) {
             UA_assert(typeId.identifier.byteString.data == NULL); /* for clang analyzer <= 3.7 */
-            typeId.identifier.numeric -= UA_ENCODINGOFFSET_BINARY;
-            if(findDataType(&typeId, &dst->type) == UA_STATUSCODE_GOOD)
+            if(findDataTypeByBinary(&typeId, &dst->type) == UA_STATUSCODE_GOOD)
                 pos += 4; /* jump over the length (todo: check if length matches) */
             else
                 pos = old_pos; /* jump back and decode as extensionobject */
@@ -1019,8 +1087,9 @@ DataValue_decodeBinary(UA_DataValue *dst, const UA_DataType *_) {
 static UA_StatusCode
 DiagnosticInfo_encodeBinary(const UA_DiagnosticInfo *src, const UA_DataType *_) {
     UA_Byte encodingMask = (UA_Byte)
-        (src->hasSymbolicId | (src->hasNamespaceUri << 1) | (src->hasLocalizedText << 2) |
-         (src->hasLocale << 3) | (src->hasAdditionalInfo << 4) | (src->hasInnerDiagnosticInfo << 5));
+        (src->hasSymbolicId | (src->hasNamespaceUri << 1) |
+         (src->hasLocalizedText << 2) | (src->hasLocale << 3) |
+         (src->hasAdditionalInfo << 4) | (src->hasInnerDiagnosticInfo << 5));
     UA_StatusCode retval = Byte_encodeBinary(&encodingMask, NULL);
     if(src->hasSymbolicId)
         retval |= Int32_encodeBinary(&src->symbolicId);
@@ -1070,7 +1139,7 @@ DiagnosticInfo_decodeBinary(UA_DiagnosticInfo *dst, const UA_DataType *_) {
         retval |= StatusCode_decodeBinary(&dst->innerStatusCode);
     }
     if(encodingMask & 0x40) {
-        /* innerDiagnosticInfo is a pointer to struct, therefore allocate */
+        /* innerDiagnosticInfo is allocated on the heap */
         dst->innerDiagnosticInfo = UA_calloc(1, sizeof(UA_DiagnosticInfo));
         if(!dst->innerDiagnosticInfo)
             return UA_STATUSCODE_BADOUTOFMEMORY;
@@ -1085,39 +1154,10 @@ DiagnosticInfo_decodeBinary(UA_DiagnosticInfo *dst, const UA_DataType *_) {
 /********************/
 
 static UA_StatusCode
-UA_encodeBinaryInternal(const void *src, const UA_DataType *type) {
-    uintptr_t ptr = (uintptr_t)src;
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    UA_Byte membersSize = type->membersSize;
-    const UA_DataType *typelists[2] = { UA_TYPES, &type[-type->typeIndex] };
-    for(size_t i = 0; i < membersSize && retval == UA_STATUSCODE_GOOD; i++) {
-        const UA_DataTypeMember *member = &type->members[i];
-        const UA_DataType *membertype = &typelists[!member->namespaceZero][member->memberTypeIndex];
-        if(!member->isArray) {
-            ptr += member->padding;
-            size_t encode_index = membertype->builtin ? membertype->typeIndex : UA_BUILTIN_TYPES_COUNT;
-            size_t memSize = membertype->memSize;
-            UA_Byte *oldpos = pos;
-            retval |= encodeBinaryJumpTable[encode_index]((const void*)ptr, membertype);
-            ptr += memSize;
-            if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED) {
-                /* exchange/send the buffer and try to encode the same type once more */
-                pos = oldpos;
-                retval = exchangeBuffer();
-                /* re-encode the same member on the new buffer */
-                ptr -= member->padding + memSize;
-                i--;
-            }
-        } else {
-            ptr += member->padding;
-            const size_t length = *((const size_t*)ptr);
-            ptr += sizeof(size_t);
-            retval |= Array_encodeBinary(*(void *UA_RESTRICT const *)ptr, length, membertype);
-            ptr += sizeof(void*);
-        }
-    }
-    return retval;
-}
+UA_encodeBinaryInternal(const void *src, const UA_DataType *type);
+
+static UA_StatusCode
+UA_decodeBinaryInternal(void *dst, const UA_DataType *type);
 
 static const UA_encodeBinarySignature encodeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
     (UA_encodeBinarySignature)Boolean_encodeBinary,
@@ -1148,46 +1188,59 @@ static const UA_encodeBinarySignature encodeBinaryJumpTable[UA_BUILTIN_TYPES_COU
     (UA_encodeBinarySignature)UA_encodeBinaryInternal,
 };
 
-UA_StatusCode
-UA_encodeBinary(const void *src, const UA_DataType *type, UA_exchangeEncodeBuffer callback,
-                void *handle, UA_ByteString *dst, size_t *offset) {
-    pos = &dst->data[*offset];
-    end = &dst->data[dst->length];
-    encodeBuf = dst;
-    exchangeBufferCallback = callback;
-    exchangeBufferCallbackHandle = handle;
-    UA_StatusCode retval = UA_encodeBinaryInternal(src, type);
-    *offset = (size_t)(pos - dst->data) / sizeof(UA_Byte);
-    return retval;
-}
-
 static UA_StatusCode
-UA_decodeBinaryInternal(void *dst, const UA_DataType *type) {
-    uintptr_t ptr = (uintptr_t)dst;
+UA_encodeBinaryInternal(const void *src, const UA_DataType *type) {
+    uintptr_t ptr = (uintptr_t)src;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_Byte membersSize = type->membersSize;
     const UA_DataType *typelists[2] = { UA_TYPES, &type[-type->typeIndex] };
-    for(size_t i = 0; i < membersSize; i++) {
+    for(size_t i = 0; i < membersSize && retval == UA_STATUSCODE_GOOD; i++) {
         const UA_DataTypeMember *member = &type->members[i];
         const UA_DataType *membertype = &typelists[!member->namespaceZero][member->memberTypeIndex];
         if(!member->isArray) {
             ptr += member->padding;
-            size_t fi = membertype->builtin ? membertype->typeIndex : UA_BUILTIN_TYPES_COUNT;
+            size_t encode_index = membertype->builtin ? membertype->typeIndex : UA_BUILTIN_TYPES_COUNT;
             size_t memSize = membertype->memSize;
-            retval |= decodeBinaryJumpTable[fi]((void *UA_RESTRICT)ptr, membertype);
+            UA_Byte *oldpos = pos;
+            retval |= encodeBinaryJumpTable[encode_index]((const void*)ptr, membertype);
             ptr += memSize;
+            if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED) {
+                /* exchange/send the buffer and try to encode the same type once more */
+                pos = oldpos;
+                retval = exchangeBuffer();
+                /* re-encode the same member on the new buffer */
+                ptr -= member->padding + memSize;
+                i--;
+            }
         } else {
             ptr += member->padding;
-            size_t *length = (size_t*)ptr;
+            const size_t length = *((const size_t*)ptr);
             ptr += sizeof(size_t);
-            retval |= Array_decodeBinary((void *UA_RESTRICT *UA_RESTRICT)ptr, length, membertype);
+            retval |= Array_encodeBinary(*(void *UA_RESTRICT const *)ptr, length, membertype);
             ptr += sizeof(void*);
         }
     }
-    /* deleteMembers is executed only at the highest level in UA_decodeBinary to
-       avoid duplicate work */
-    /* if(retval != UA_STATUSCODE_GOOD) */
-    /*     UA_deleteMembers(dst, type); */
+    return retval;
+}
+
+UA_StatusCode
+UA_encodeBinary(const void *src, const UA_DataType *type,
+                UA_exchangeEncodeBuffer callback, void *handle,
+                UA_ByteString *dst, size_t *offset) {
+    /* Set the (thread-local) position and end pointers to save function
+       arguments */
+    pos = &dst->data[*offset];
+    end = &dst->data[dst->length];
+
+    /* Set the (thread-local) callbacks where the buffer is exchanged and the
+       current chunk sent out */
+    encodeBuf = dst;
+    exchangeBufferCallback = callback;
+    exchangeBufferCallbackHandle = handle;
+
+    /* Encode and clean up */
+    UA_StatusCode retval = UA_encodeBinaryInternal(src, type);
+    *offset = (size_t)(pos - dst->data) / sizeof(UA_Byte);
     return retval;
 }
 
@@ -1220,12 +1273,47 @@ static const UA_decodeBinarySignature decodeBinaryJumpTable[UA_BUILTIN_TYPES_COU
     (UA_decodeBinarySignature)UA_decodeBinaryInternal
 };
 
+static UA_StatusCode
+UA_decodeBinaryInternal(void *dst, const UA_DataType *type) {
+    uintptr_t ptr = (uintptr_t)dst;
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    UA_Byte membersSize = type->membersSize;
+    const UA_DataType *typelists[2] = { UA_TYPES, &type[-type->typeIndex] };
+    for(size_t i = 0; i < membersSize; i++) {
+        const UA_DataTypeMember *member = &type->members[i];
+        const UA_DataType *membertype = &typelists[!member->namespaceZero][member->memberTypeIndex];
+        if(!member->isArray) {
+            ptr += member->padding;
+            size_t fi = membertype->builtin ? membertype->typeIndex : UA_BUILTIN_TYPES_COUNT;
+            size_t memSize = membertype->memSize;
+            retval |= decodeBinaryJumpTable[fi]((void *UA_RESTRICT)ptr, membertype);
+            ptr += memSize;
+        } else {
+            ptr += member->padding;
+            size_t *length = (size_t*)ptr;
+            ptr += sizeof(size_t);
+            retval |= Array_decodeBinary((void *UA_RESTRICT *UA_RESTRICT)ptr, length, membertype);
+            ptr += sizeof(void*);
+        }
+    }
+    return retval;
+}
+
 UA_StatusCode
-UA_decodeBinary(const UA_ByteString *src, size_t *offset, void *dst, const UA_DataType *type) {
-    memset(dst, 0, type->memSize); // init
+UA_decodeBinary(const UA_ByteString *src, size_t *offset,
+                void *dst, const UA_DataType *type) {
+    /* Initialize the destination */
+    memset(dst, 0, type->memSize);
+
+    /* Set the (thread-local) position and end pointers to save function
+       arguments */
     pos = &src->data[*offset];
     end = &src->data[src->length];
+
+    /* Decode */
     UA_StatusCode retval = UA_decodeBinaryInternal(dst, type);
+
+    /* Clean up */
     if(retval == UA_STATUSCODE_GOOD)
         *offset = (size_t)(pos - src->data) / sizeof(UA_Byte);
     else
@@ -1253,15 +1341,18 @@ Array_calcSizeBinary(const void *src, size_t length, const UA_DataType *type) {
     return s;
 }
 
-static size_t calcSizeBinaryMemSize(const void *UA_RESTRICT p, const UA_DataType *type) {
+static size_t
+calcSizeBinaryMemSize(const void *UA_RESTRICT p, const UA_DataType *type) {
     return type->memSize;
 }
 
-static size_t String_calcSizeBinary(const UA_String *UA_RESTRICT p, const UA_DataType *_) {
+static size_t
+String_calcSizeBinary(const UA_String *UA_RESTRICT p, const UA_DataType *_) {
     return 4 + p->length;
 }
 
-static size_t Guid_calcSizeBinary(const UA_Guid *UA_RESTRICT p, const UA_DataType *_) {
+static size_t
+Guid_calcSizeBinary(const UA_Guid *UA_RESTRICT p, const UA_DataType *_) {
     return 16;
 }
 
@@ -1362,9 +1453,9 @@ Variant_calcSizeBinary(UA_Variant const *src, UA_DataType *_) {
     }
 
     size_t length = src->arrayLength;
-    if(isArray) {
+    if(isArray)
         s += 4;
-    } else
+    else
         length = 1;
 
     uintptr_t ptr = (uintptr_t)src->data;
@@ -1452,7 +1543,8 @@ static const UA_calcSizeBinarySignature calcSizeBinaryJumpTable[UA_BUILTIN_TYPES
     (UA_calcSizeBinarySignature)UA_calcSizeBinary
 };
 
-size_t UA_calcSizeBinary(void *p, const UA_DataType *type) {
+size_t
+UA_calcSizeBinary(void *p, const UA_DataType *type) {
     size_t s = 0;
     uintptr_t ptr = (uintptr_t)p;
     UA_Byte membersSize = type->membersSize;

+ 8 - 0
tests/CMakeLists.txt

@@ -66,6 +66,10 @@ add_executable(check_services_nodemanagement check_services_nodemanagement.c $<T
 target_link_libraries(check_services_nodemanagement ${LIBS})
 add_test_valgrind(services_nodemanagement ${CMAKE_CURRENT_BINARY_DIR}/check_services_nodemanagement)
 
+add_executable(check_services_subscriptions check_services_subscriptions.c $<TARGET_OBJECTS:open62541-object>)
+target_link_libraries(check_services_subscriptions ${LIBS})
+add_test(check_services_subscriptions ${CMAKE_CURRENT_BINARY_DIR}/check_services_subscriptions)
+
 add_executable(check_nodestore check_nodestore.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_nodestore ${LIBS})
 add_test_valgrind(nodestore ${CMAKE_CURRENT_BINARY_DIR}/check_nodestore)
@@ -74,6 +78,10 @@ add_executable(check_session check_session.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_session ${LIBS})
 add_test_valgrind(session ${CMAKE_CURRENT_BINARY_DIR}/check_session)
 
+add_executable(check_server_jobs check_server_jobs.c $<TARGET_OBJECTS:open62541-object>)
+target_link_libraries(check_server_jobs ${LIBS})
+add_test(check_server_jobs ${CMAKE_CURRENT_BINARY_DIR}/check_server_jobs)
+
 add_executable(check_server_userspace check_server_userspace.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_server_userspace ${LIBS})
 add_test_valgrind(check_server_userspace ${CMAKE_CURRENT_BINARY_DIR}/check_server_userspace)

+ 205 - 205
tests/check_nodestore.c

@@ -15,246 +15,246 @@
 int zeroCnt = 0;
 int visitCnt = 0;
 static void checkZeroVisitor(const UA_Node* node) {
-	visitCnt++;
-	if (node == NULL) zeroCnt++;
+    visitCnt++;
+    if (node == NULL) zeroCnt++;
 }
 
 static void printVisitor(const UA_Node* node) {
-	printf("%d\n", node->nodeId.identifier.numeric);
+    printf("%d\n", node->nodeId.identifier.numeric);
 }
 
 static UA_Node* createNode(UA_Int16 nsid, UA_Int32 id) {
-	UA_Node *p = (UA_Node *)UA_NodeStore_newVariableNode();
-	p->nodeId.identifierType = UA_NODEIDTYPE_NUMERIC;
-	p->nodeId.namespaceIndex = nsid;
-	p->nodeId.identifier.numeric = id;
-	p->nodeClass = UA_NODECLASS_VARIABLE;
-	return p;
+    UA_Node *p = (UA_Node *)UA_NodeStore_newVariableNode();
+    p->nodeId.identifierType = UA_NODEIDTYPE_NUMERIC;
+    p->nodeId.namespaceIndex = nsid;
+    p->nodeId.identifier.numeric = id;
+    p->nodeClass = UA_NODECLASS_VARIABLE;
+    return p;
 }
 
 START_TEST(replaceExistingNode) {
-	UA_NodeStore *ns = UA_NodeStore_new();
-	UA_Node* n1 = createNode(0,2253);
-	UA_NodeStore_insert(ns, n1);
+    UA_NodeStore *ns = UA_NodeStore_new();
+    UA_Node* n1 = createNode(0,2253);
+    UA_NodeStore_insert(ns, n1);
     UA_NodeId in1 = UA_NODEID_NUMERIC(0, 2253);
-	UA_Node* n2 = UA_NodeStore_getCopy(ns, &in1);
+    UA_Node* n2 = UA_NodeStore_getCopy(ns, &in1);
     UA_StatusCode retval = UA_NodeStore_replace(ns, n2);
     
-	ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     
-	UA_NodeStore_delete(ns);
+    UA_NodeStore_delete(ns);
 }
 END_TEST
 
 START_TEST(replaceOldNode) {
-	UA_NodeStore *ns = UA_NodeStore_new();
-	UA_Node* n1 = createNode(0,2253);
-	UA_NodeStore_insert(ns, n1);
+    UA_NodeStore *ns = UA_NodeStore_new();
+    UA_Node* n1 = createNode(0,2253);
+    UA_NodeStore_insert(ns, n1);
     UA_NodeId in1 = UA_NODEID_NUMERIC(0,2253);
-	UA_Node* n2 = UA_NodeStore_getCopy(ns, &in1);
-	UA_Node* n3 = UA_NodeStore_getCopy(ns, &in1);
+    UA_Node* n2 = UA_NodeStore_getCopy(ns, &in1);
+    UA_Node* n3 = UA_NodeStore_getCopy(ns, &in1);
 
     /* shall succeed */
     UA_StatusCode retval = UA_NodeStore_replace(ns, n2);
-	ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
     /* shall fail */
     retval = UA_NodeStore_replace(ns, n3);
-	ck_assert_int_ne(retval, UA_STATUSCODE_GOOD);
+    ck_assert_int_ne(retval, UA_STATUSCODE_GOOD);
     
-	UA_NodeStore_delete(ns);
+    UA_NodeStore_delete(ns);
 }
 END_TEST
 
 START_TEST(findNodeInUA_NodeStoreWithSingleEntry) {
 #ifdef UA_ENABLE_MULTITHREADING
-   	rcu_register_thread();
+    rcu_register_thread();
 #endif
-	// given
-	UA_NodeStore *ns = UA_NodeStore_new();
-	UA_Node* n1 = createNode(0,2253);
-	UA_NodeStore_insert(ns, n1);
+    // given
+    UA_NodeStore *ns = UA_NodeStore_new();
+    UA_Node* n1 = createNode(0,2253);
+    UA_NodeStore_insert(ns, n1);
     UA_NodeId in1 = UA_NODEID_NUMERIC(0,2253);
-	const UA_Node* nr = UA_NodeStore_get(ns, &in1);
-	// then
-	ck_assert_int_eq((uintptr_t)n1, (uintptr_t)nr);
-	// finally
-	UA_NodeStore_delete(ns);
+    const UA_Node* nr = UA_NodeStore_get(ns, &in1);
+    // then
+    ck_assert_int_eq((uintptr_t)n1, (uintptr_t)nr);
+    // finally
+    UA_NodeStore_delete(ns);
 #ifdef UA_ENABLE_MULTITHREADING
-	rcu_unregister_thread();
+    rcu_unregister_thread();
 #endif
 }
 END_TEST
 
 START_TEST(failToFindNodeInOtherUA_NodeStore) {
 #ifdef UA_ENABLE_MULTITHREADING
-   	rcu_register_thread();
+    rcu_register_thread();
 #endif
-	// given
-	UA_NodeStore *ns = UA_NodeStore_new();
+    // given
+    UA_NodeStore *ns = UA_NodeStore_new();
 
-	UA_Node* n1 = createNode(0,2255);
+    UA_Node* n1 = createNode(0,2255);
     UA_NodeStore_insert(ns, n1);
 
-	// when
-	UA_NodeId in1 = UA_NODEID_NUMERIC(1, 2255);
-	const UA_Node* nr = UA_NodeStore_get(ns, &in1);
-	// then
-	ck_assert_int_eq((uintptr_t)nr, 0);
-	// finally
-	UA_NodeStore_delete(ns);
+    // when
+    UA_NodeId in1 = UA_NODEID_NUMERIC(1, 2255);
+    const UA_Node* nr = UA_NodeStore_get(ns, &in1);
+    // then
+    ck_assert_int_eq((uintptr_t)nr, 0);
+    // finally
+    UA_NodeStore_delete(ns);
 #ifdef UA_ENABLE_MULTITHREADING
-	rcu_unregister_thread();
+    rcu_unregister_thread();
 #endif
 }
 END_TEST
 
 START_TEST(findNodeInUA_NodeStoreWithSeveralEntries) {
 #ifdef UA_ENABLE_MULTITHREADING
-   	rcu_register_thread();
+    rcu_register_thread();
 #endif
-	// given
-	UA_NodeStore *ns = UA_NodeStore_new();
-	UA_Node* n1 = createNode(0,2253);
+    // given
+    UA_NodeStore *ns = UA_NodeStore_new();
+    UA_Node* n1 = createNode(0,2253);
     UA_NodeStore_insert(ns, n1);
-	UA_Node* n2 = createNode(0,2255);
+    UA_Node* n2 = createNode(0,2255);
     UA_NodeStore_insert(ns, n2);
-	UA_Node* n3 = createNode(0,2257);
+    UA_Node* n3 = createNode(0,2257);
     UA_NodeStore_insert(ns, n3);
-	UA_Node* n4 = createNode(0,2200);
+    UA_Node* n4 = createNode(0,2200);
     UA_NodeStore_insert(ns, n4);
-	UA_Node* n5 = createNode(0,1);
+    UA_Node* n5 = createNode(0,1);
     UA_NodeStore_insert(ns, n5);
-	UA_Node* n6 = createNode(0,12);
+    UA_Node* n6 = createNode(0,12);
     UA_NodeStore_insert(ns, n6);
 
-	// when
+    // when
     UA_NodeId in3 = UA_NODEID_NUMERIC(0, 2257);
-	const UA_Node* nr = UA_NodeStore_get(ns, &in3);
-	// then
-	ck_assert_int_eq((uintptr_t)nr, (uintptr_t)n3);
-	// finally
-	UA_NodeStore_delete(ns);
+    const UA_Node* nr = UA_NodeStore_get(ns, &in3);
+    // then
+    ck_assert_int_eq((uintptr_t)nr, (uintptr_t)n3);
+    // finally
+    UA_NodeStore_delete(ns);
 #ifdef UA_ENABLE_MULTITHREADING
-	rcu_unregister_thread();
+    rcu_unregister_thread();
 #endif
 }
 END_TEST
 
 START_TEST(iterateOverUA_NodeStoreShallNotVisitEmptyNodes) {
 #ifdef UA_ENABLE_MULTITHREADING
-   	rcu_register_thread();
+    rcu_register_thread();
 #endif
-	// given
-	UA_NodeStore *ns = UA_NodeStore_new();
-	UA_Node* n1 = createNode(0,2253);
+    // given
+    UA_NodeStore *ns = UA_NodeStore_new();
+    UA_Node* n1 = createNode(0,2253);
     UA_NodeStore_insert(ns, n1);
-	UA_Node* n2 = createNode(0,2255);
+    UA_Node* n2 = createNode(0,2255);
     UA_NodeStore_insert(ns, n2);
-	UA_Node* n3 = createNode(0,2257);
+    UA_Node* n3 = createNode(0,2257);
     UA_NodeStore_insert(ns, n3);
-	UA_Node* n4 = createNode(0,2200);
+    UA_Node* n4 = createNode(0,2200);
     UA_NodeStore_insert(ns, n4);
-	UA_Node* n5 = createNode(0,1);
+    UA_Node* n5 = createNode(0,1);
     UA_NodeStore_insert(ns, n5);
-	UA_Node* n6 = createNode(0,12);
+    UA_Node* n6 = createNode(0,12);
     UA_NodeStore_insert(ns, n6);
 
-	// when
-	zeroCnt = 0;
-	visitCnt = 0;
-	UA_NodeStore_iterate(ns,checkZeroVisitor);
-	// then
-	ck_assert_int_eq(zeroCnt, 0);
-	ck_assert_int_eq(visitCnt, 6);
-	// finally
-	UA_NodeStore_delete(ns);
+    // when
+    zeroCnt = 0;
+    visitCnt = 0;
+    UA_NodeStore_iterate(ns,checkZeroVisitor);
+    // then
+    ck_assert_int_eq(zeroCnt, 0);
+    ck_assert_int_eq(visitCnt, 6);
+    // finally
+    UA_NodeStore_delete(ns);
 #ifdef UA_ENABLE_MULTITHREADING
-	rcu_unregister_thread();
+    rcu_unregister_thread();
 #endif
 }
 END_TEST
 
 START_TEST(findNodeInExpandedNamespace) {
 #ifdef UA_ENABLE_MULTITHREADING
-   	rcu_register_thread();
+    rcu_register_thread();
 #endif
-	// given
-	UA_NodeStore *ns = UA_NodeStore_new();
-	UA_Node* n;
-	UA_Int32 i=0;
-	for (; i<200; i++) {
-		n = createNode(0,i);
+    // given
+    UA_NodeStore *ns = UA_NodeStore_new();
+    UA_Node* n;
+    UA_Int32 i=0;
+    for (; i<200; i++) {
+        n = createNode(0,i);
         UA_NodeStore_insert(ns, n);
-	}
-	// when
-	UA_Node *n2 = createNode(0,25);
-	const UA_Node* nr = UA_NodeStore_get(ns,&n2->nodeId);
-	// then
-	ck_assert_int_eq(nr->nodeId.identifier.numeric,n2->nodeId.identifier.numeric);
-	// finally
+    }
+    // when
+    UA_Node *n2 = createNode(0,25);
+    const UA_Node* nr = UA_NodeStore_get(ns,&n2->nodeId);
+    // then
+    ck_assert_int_eq(nr->nodeId.identifier.numeric,n2->nodeId.identifier.numeric);
+    // finally
     UA_NodeStore_deleteNode(n2);
-	UA_NodeStore_delete(ns);
+    UA_NodeStore_delete(ns);
 #ifdef UA_ENABLE_MULTITHREADING
-	rcu_unregister_thread();
+    rcu_unregister_thread();
 #endif
 }
 END_TEST
 
 START_TEST(iterateOverExpandedNamespaceShallNotVisitEmptyNodes) {
 #ifdef UA_ENABLE_MULTITHREADING
-   	rcu_register_thread();
+    rcu_register_thread();
 #endif
-	// given
-	UA_NodeStore *ns = UA_NodeStore_new();
+    // given
+    UA_NodeStore *ns = UA_NodeStore_new();
     UA_Node* n;
-	UA_Int32 i=0;
-	for (; i<200; i++) {
-		n = createNode(0,i);
+    UA_Int32 i=0;
+    for (; i<200; i++) {
+        n = createNode(0,i);
         UA_NodeStore_insert(ns, n);
-	}
-	// when
-	zeroCnt = 0;
-	visitCnt = 0;
-	UA_NodeStore_iterate(ns,checkZeroVisitor);
-	// then
-	ck_assert_int_eq(zeroCnt, 0);
-	ck_assert_int_eq(visitCnt, 200);
-	// finally
-	UA_NodeStore_delete(ns);
+    }
+    // when
+    zeroCnt = 0;
+    visitCnt = 0;
+    UA_NodeStore_iterate(ns,checkZeroVisitor);
+    // then
+    ck_assert_int_eq(zeroCnt, 0);
+    ck_assert_int_eq(visitCnt, 200);
+    // finally
+    UA_NodeStore_delete(ns);
 #ifdef UA_ENABLE_MULTITHREADING
-	rcu_unregister_thread();
+    rcu_unregister_thread();
 #endif
 }
 END_TEST
 
 START_TEST(failToFindNonExistantNodeInUA_NodeStoreWithSeveralEntries) {
 #ifdef UA_ENABLE_MULTITHREADING
-   	rcu_register_thread();
+    rcu_register_thread();
 #endif
-	// given
-	UA_NodeStore *ns = UA_NodeStore_new();
-	UA_Node* n1 = createNode(0,2253);
+    // given
+    UA_NodeStore *ns = UA_NodeStore_new();
+    UA_Node* n1 = createNode(0,2253);
     UA_NodeStore_insert(ns, n1);
-	UA_Node* n2 = createNode(0,2255);
+    UA_Node* n2 = createNode(0,2255);
     UA_NodeStore_insert(ns, n2);
-	UA_Node* n3 = createNode(0,2257);
+    UA_Node* n3 = createNode(0,2257);
     UA_NodeStore_insert(ns, n3);
-	UA_Node* n4 = createNode(0,2200);
+    UA_Node* n4 = createNode(0,2200);
     UA_NodeStore_insert(ns, n4);
-	UA_Node* n5 = createNode(0,1);
+    UA_Node* n5 = createNode(0,1);
     UA_NodeStore_insert(ns, n5);
 
     UA_NodeId id = UA_NODEID_NUMERIC(0, 12);
 
-	// when
-	const UA_Node* nr = UA_NodeStore_get(ns, &id);
-	// then
-	ck_assert_int_eq((uintptr_t)nr, 0);
-	// finally
-	UA_NodeStore_delete(ns);
+    // when
+    const UA_Node* nr = UA_NodeStore_get(ns, &id);
+    // then
+    ck_assert_int_eq((uintptr_t)nr, 0);
+    // finally
+    UA_NodeStore_delete(ns);
 #ifdef UA_ENABLE_MULTITHREADING
-	rcu_unregister_thread();
+    rcu_unregister_thread();
 #endif
 }
 END_TEST
@@ -265,114 +265,114 @@ END_TEST
 
 #ifdef UA_ENABLE_MULTITHREADING
 struct UA_NodeStoreProfileTest {
-	UA_NodeStore *ns;
-	UA_Int32 min_val;
-	UA_Int32 max_val;
-	UA_Int32 rounds;
+    UA_NodeStore *ns;
+    UA_Int32 min_val;
+    UA_Int32 max_val;
+    UA_Int32 rounds;
 };
 
 static void *profileGetThread(void *arg) {
-   	rcu_register_thread();
-	struct UA_NodeStoreProfileTest *test = (struct UA_NodeStoreProfileTest*) arg;
-	UA_NodeId id;
+    rcu_register_thread();
+    struct UA_NodeStoreProfileTest *test = (struct UA_NodeStoreProfileTest*) arg;
+    UA_NodeId id;
     UA_NodeId_init(&id);
-	UA_Int32 max_val = test->max_val;
-	UA_NodeStore *ns = test->ns;
-	for(UA_Int32 x = 0; x<test->rounds; x++) {
-		for(UA_Int32 i=test->min_val; i<max_val; i++) {
-			id.identifier.numeric = i;
-			UA_NodeStore_get(ns,&id);
-		}
-	}
-	rcu_unregister_thread();
-	
-	return NULL;
+    UA_Int32 max_val = test->max_val;
+    UA_NodeStore *ns = test->ns;
+    for(UA_Int32 x = 0; x<test->rounds; x++) {
+        for(UA_Int32 i=test->min_val; i<max_val; i++) {
+            id.identifier.numeric = i;
+            UA_NodeStore_get(ns,&id);
+        }
+    }
+    rcu_unregister_thread();
+    
+    return NULL;
 }
 #endif
 
 START_TEST(profileGetDelete) {
 #ifdef UA_ENABLE_MULTITHREADING
-   	rcu_register_thread();
+    rcu_register_thread();
 #endif
 
 #define N 1000000
-	UA_NodeStore *ns = UA_NodeStore_new();
-	UA_Node *n;
-	for (int i=0; i<N; i++) {
-		n = createNode(0,i);
+    UA_NodeStore *ns = UA_NodeStore_new();
+    UA_Node *n;
+    for (int i=0; i<N; i++) {
+        n = createNode(0,i);
         UA_NodeStore_insert(ns, n);
-	}
-	clock_t begin, end;
-	begin = clock();
+    }
+    clock_t begin, end;
+    begin = clock();
 #ifdef UA_ENABLE_MULTITHREADING
 #define THREADS 4
     pthread_t t[THREADS];
-	struct UA_NodeStoreProfileTest p[THREADS];
-	for (int i = 0; i < THREADS; i++) {
-		p[i] = (struct UA_NodeStoreProfileTest){ns, i*(N/THREADS), (i+1)*(N/THREADS), 50};
-		pthread_create(&t[i], NULL, profileGetThread, &p[i]);
-	}
-	for (int i = 0; i < THREADS; i++)
-		pthread_join(t[i], NULL);
-	end = clock();
-	printf("Time for %d create/get/delete on %d threads in a namespace: %fs.\n", N, THREADS, (double)(end - begin) / CLOCKS_PER_SEC);
+    struct UA_NodeStoreProfileTest p[THREADS];
+    for (int i = 0; i < THREADS; i++) {
+        p[i] = (struct UA_NodeStoreProfileTest){ns, i*(N/THREADS), (i+1)*(N/THREADS), 50};
+        pthread_create(&t[i], NULL, profileGetThread, &p[i]);
+    }
+    for (int i = 0; i < THREADS; i++)
+        pthread_join(t[i], NULL);
+    end = clock();
+    printf("Time for %d create/get/delete on %d threads in a namespace: %fs.\n", N, THREADS, (double)(end - begin) / CLOCKS_PER_SEC);
 #else
-	UA_NodeId id;
+    UA_NodeId id;
     UA_NodeId_init(&id);
-	for(UA_Int32 x = 0; x<50; x++) {
-	    for(int i=0; i<N; i++) {
-	        id.identifier.numeric = i;
+    for(UA_Int32 x = 0; x<50; x++) {
+        for(int i=0; i<N; i++) {
+            id.identifier.numeric = i;
             UA_NodeStore_get(ns,&id);
         }
     }
-	end = clock();
-	printf("Time for single-threaded %d create/get/delete in a namespace: %fs.\n", N, (double)(end - begin) / CLOCKS_PER_SEC);
+    end = clock();
+    printf("Time for single-threaded %d create/get/delete in a namespace: %fs.\n", N, (double)(end - begin) / CLOCKS_PER_SEC);
 #endif
 
-	UA_NodeStore_delete(ns);
+    UA_NodeStore_delete(ns);
 
 #ifdef UA_ENABLE_MULTITHREADING
-	rcu_unregister_thread();
+    rcu_unregister_thread();
 #endif
 }
 END_TEST
 
 static Suite * namespace_suite (void) {
-	Suite *s = suite_create ("UA_NodeStore");
-
-	TCase* tc_find = tcase_create ("Find");
-	tcase_add_test (tc_find, findNodeInUA_NodeStoreWithSingleEntry);
-	tcase_add_test (tc_find, findNodeInUA_NodeStoreWithSeveralEntries);
-	tcase_add_test (tc_find, findNodeInExpandedNamespace);
-	tcase_add_test (tc_find, failToFindNonExistantNodeInUA_NodeStoreWithSeveralEntries);
-	tcase_add_test (tc_find, failToFindNodeInOtherUA_NodeStore);
-	suite_add_tcase (s, tc_find);
-
-	TCase *tc_replace = tcase_create("Replace");
-	tcase_add_test (tc_replace, replaceExistingNode);
-	tcase_add_test (tc_replace, replaceOldNode);
-	suite_add_tcase (s, tc_replace);
-
-	TCase* tc_iterate = tcase_create ("Iterate");
-	tcase_add_test (tc_iterate, iterateOverUA_NodeStoreShallNotVisitEmptyNodes);
-	tcase_add_test (tc_iterate, iterateOverExpandedNamespaceShallNotVisitEmptyNodes);
-	suite_add_tcase (s, tc_iterate);
-	
-	/* TCase* tc_profile = tcase_create ("Profile"); */
-	/* tcase_add_test (tc_profile, profileGetDelete); */
-	/* suite_add_tcase (s, tc_profile); */
-
-	return s;
+    Suite *s = suite_create ("UA_NodeStore");
+
+    TCase* tc_find = tcase_create ("Find");
+    tcase_add_test (tc_find, findNodeInUA_NodeStoreWithSingleEntry);
+    tcase_add_test (tc_find, findNodeInUA_NodeStoreWithSeveralEntries);
+    tcase_add_test (tc_find, findNodeInExpandedNamespace);
+    tcase_add_test (tc_find, failToFindNonExistantNodeInUA_NodeStoreWithSeveralEntries);
+    tcase_add_test (tc_find, failToFindNodeInOtherUA_NodeStore);
+    suite_add_tcase (s, tc_find);
+
+    TCase *tc_replace = tcase_create("Replace");
+    tcase_add_test (tc_replace, replaceExistingNode);
+    tcase_add_test (tc_replace, replaceOldNode);
+    suite_add_tcase (s, tc_replace);
+
+    TCase* tc_iterate = tcase_create ("Iterate");
+    tcase_add_test (tc_iterate, iterateOverUA_NodeStoreShallNotVisitEmptyNodes);
+    tcase_add_test (tc_iterate, iterateOverExpandedNamespaceShallNotVisitEmptyNodes);
+    suite_add_tcase (s, tc_iterate);
+    
+    /* TCase* tc_profile = tcase_create ("Profile"); */
+    /* tcase_add_test (tc_profile, profileGetDelete); */
+    /* suite_add_tcase (s, tc_profile); */
+
+    return s;
 }
 
 
 int main (void) {
-	int number_failed = 0;
-	Suite *s = namespace_suite();
-	SRunner *sr = srunner_create(s);
-	srunner_set_fork_status(sr,CK_NOFORK);
-	srunner_run_all(sr, CK_NORMAL);
-	number_failed += srunner_ntests_failed (sr);
-	srunner_free(sr);
-	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+    int number_failed = 0;
+    Suite *s = namespace_suite();
+    SRunner *sr = srunner_create(s);
+    srunner_set_fork_status(sr,CK_NOFORK);
+    srunner_run_all(sr, CK_NORMAL);
+    number_failed += srunner_ntests_failed (sr);
+    srunner_free(sr);
+    return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
 }

+ 86 - 0
tests/check_server_jobs.c

@@ -0,0 +1,86 @@
+#include "ua_server.h"
+#include "server/ua_server_internal.h"
+#include "ua_config_standard.h"
+
+#include "check.h"
+#include <unistd.h>
+
+UA_Server *server = NULL;
+
+static void setup(void) {
+    server = UA_Server_new(UA_ServerConfig_standard);
+    UA_Server_run_startup(server);
+}
+
+static void teardown(void) {
+    UA_Server_run_shutdown(server);
+    UA_Server_delete(server);
+}
+
+UA_Boolean *executed;
+
+static void
+dummyJob(UA_Server *server, void *data) {
+    *executed = true;
+}
+
+START_TEST(Server_addRemoveRepeatedJob) {
+    executed = UA_Boolean_new();
+    UA_Guid id;
+    UA_Job rj = (UA_Job){
+        .type = UA_JOBTYPE_METHODCALL,
+        .job.methodCall = {.data = NULL, .method = dummyJob}
+    };
+    UA_Server_addRepeatedJob(server, rj, 10, &id);
+
+    usleep(15*1000);
+    UA_Server_run_iterate(server, false);
+
+    ck_assert_uint_eq(*executed, true);
+
+    UA_Server_removeRepeatedJob(server, id);
+    UA_Boolean_delete(executed);
+}
+END_TEST
+
+UA_Guid *jobId;
+
+static void
+removeItselfJob(UA_Server *server, void *data) {
+    UA_Server_removeRepeatedJob(server, *jobId);
+}
+
+START_TEST(Server_repeatedJobRemoveItself) {
+    jobId = UA_Guid_new();
+    UA_Job rj = (UA_Job){
+        .type = UA_JOBTYPE_METHODCALL,
+        .job.methodCall = {.data = NULL, .method = removeItselfJob}
+    };
+    UA_Server_addRepeatedJob(server, rj, 10, jobId);
+
+    usleep(15*1000);
+    UA_Server_run_iterate(server, false);
+
+    UA_Guid_delete(jobId);
+}
+END_TEST
+
+static Suite* testSuite_Client(void) {
+    Suite *s = suite_create("Server Jobs");
+    TCase *tc_server = tcase_create("Server Repeated Jobs");
+    tcase_add_checked_fixture(tc_server, setup, teardown);
+    tcase_add_test(tc_server, Server_addRemoveRepeatedJob);
+    tcase_add_test(tc_server, Server_repeatedJobRemoveItself);
+    suite_add_tcase(s, tc_server);
+    return s;
+}
+
+int main(void) {
+    Suite *s = testSuite_Client();
+    SRunner *sr = srunner_create(s);
+    srunner_set_fork_status(sr, CK_NOFORK);
+    srunner_run_all(sr,CK_NORMAL);
+    int number_failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+    return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}

+ 14 - 14
tests/check_server_userspace.c

@@ -23,27 +23,27 @@ START_TEST(Server_addNamespace_ShallWork)
 END_TEST
 
 static Suite* testSuite_ServerUserspace(void) {
-	Suite *s = suite_create("ServerUserspace");
-	TCase *tc_core = tcase_create("Core");
-	tcase_add_test(tc_core, Server_addNamespace_ShallWork);
+    Suite *s = suite_create("ServerUserspace");
+    TCase *tc_core = tcase_create("Core");
+    tcase_add_test(tc_core, Server_addNamespace_ShallWork);
 
-	suite_add_tcase(s,tc_core);
-	return s;
+    suite_add_tcase(s,tc_core);
+    return s;
 }
 
 int main(void) {
-	int number_failed = 0;
+    int number_failed = 0;
 
-	Suite *s;
-	SRunner *sr;
+    Suite *s;
+    SRunner *sr;
 
-	s = testSuite_ServerUserspace();
-	sr = srunner_create(s);
-	srunner_run_all(sr,CK_NORMAL);
-	number_failed += srunner_ntests_failed(sr);
-	srunner_free(sr);
+    s = testSuite_ServerUserspace();
+    sr = srunner_create(s);
+    srunner_run_all(sr,CK_NORMAL);
+    number_failed += srunner_ntests_failed(sr);
+    srunner_free(sr);
 
-	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+    return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
 }
 
 

+ 72 - 72
tests/check_services_attributes.c

@@ -1039,83 +1039,83 @@ START_TEST(numericRange) {
 } END_TEST
 
 static Suite * testSuite_services_attributes(void) {
-	Suite *s = suite_create("services_attributes_read");
-
-	TCase *tc_readSingleAttributes = tcase_create("readSingleAttributes");
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeValueWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeValueRangeWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeNodeIdWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeNodeClassWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeBrowseNameWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeDisplayNameWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeDescriptionWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeWriteMaskWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeUserWriteMaskWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeIsAbstractWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeSymmetricWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeInverseNameWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeContainsNoLoopsWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeEventNotifierWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeDataTypeWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeValueRankWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeArrayDimensionsWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeAccessLevelWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeUserAccessLevelWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeMinimumSamplingIntervalWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeHistorizingWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeExecutableWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeUserExecutableWithoutTimestamp);
+    Suite *s = suite_create("services_attributes_read");
+
+    TCase *tc_readSingleAttributes = tcase_create("readSingleAttributes");
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeValueWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeValueRangeWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeNodeIdWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeNodeClassWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeBrowseNameWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeDisplayNameWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeDescriptionWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeWriteMaskWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeUserWriteMaskWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeIsAbstractWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeSymmetricWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeInverseNameWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeContainsNoLoopsWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeEventNotifierWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeDataTypeWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeValueRankWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeArrayDimensionsWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeAccessLevelWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeUserAccessLevelWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeMinimumSamplingIntervalWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeHistorizingWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeExecutableWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeUserExecutableWithoutTimestamp);
         tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeDataTypeWithoutTimestampFromBrokenSource);
         tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeValueWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeDataTypeWithoutTimestamp);
-	tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeArrayDimensionsWithoutTimestamp);
-
-	suite_add_tcase(s, tc_readSingleAttributes);
-
-	TCase *tc_writeSingleAttributes = tcase_create("writeSingleAttributes");
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeNodeId);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeNodeclass);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeBrowseName);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeDisplayName);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeDescription);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeWriteMask);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeUserWriteMask);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeIsAbstract);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeSymmetric);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeInverseName);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeContainsNoLoops);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeEventNotifier);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeValue);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeDataType);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeValueRank);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeArrayDimensions);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeAccessLevel);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeUserAccessLevel);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeMinimumSamplingInterval);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeHistorizing);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeExecutable);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeUserExecutable);
-	tcase_add_test(tc_writeSingleAttributes, WriteSingleDataSourceAttributeValue);
-
-	suite_add_tcase(s, tc_writeSingleAttributes);
-
-	TCase *tc_parseNumericRange = tcase_create("parseNumericRange");
-	tcase_add_test(tc_parseNumericRange, numericRange);
-	suite_add_tcase(s, tc_parseNumericRange);
-
-	return s;
+    tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeDataTypeWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeArrayDimensionsWithoutTimestamp);
+
+    suite_add_tcase(s, tc_readSingleAttributes);
+
+    TCase *tc_writeSingleAttributes = tcase_create("writeSingleAttributes");
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeNodeId);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeNodeclass);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeBrowseName);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeDisplayName);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeDescription);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeWriteMask);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeUserWriteMask);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeIsAbstract);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeSymmetric);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeInverseName);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeContainsNoLoops);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeEventNotifier);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeValue);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeDataType);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeValueRank);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeArrayDimensions);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeAccessLevel);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeUserAccessLevel);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeMinimumSamplingInterval);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeHistorizing);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeExecutable);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeUserExecutable);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleDataSourceAttributeValue);
+
+    suite_add_tcase(s, tc_writeSingleAttributes);
+
+    TCase *tc_parseNumericRange = tcase_create("parseNumericRange");
+    tcase_add_test(tc_parseNumericRange, numericRange);
+    suite_add_tcase(s, tc_parseNumericRange);
+
+    return s;
 }
 
 int main(void) {
 
-	int number_failed = 0;
-	Suite *s;
-	s = testSuite_services_attributes();
-	SRunner *sr = srunner_create(s);
-	srunner_set_fork_status(sr, CK_NOFORK);
-	srunner_run_all(sr, CK_NORMAL);
+    int number_failed = 0;
+    Suite *s;
+    s = testSuite_services_attributes();
+    SRunner *sr = srunner_create(s);
+    srunner_set_fork_status(sr, CK_NOFORK);
+    srunner_run_all(sr, CK_NORMAL);
 
-	number_failed += srunner_ntests_failed(sr);
-	srunner_free(sr);
-	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+    number_failed += srunner_ntests_failed(sr);
+    srunner_free(sr);
+    return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
 }

+ 15 - 15
tests/check_services_nodemanagement.c

@@ -88,25 +88,25 @@ START_TEST(AddNodeTwiceGivesError) {
 } END_TEST
 
 static Suite * testSuite_services_nodemanagement(void) {
-	Suite *s = suite_create("services_nodemanagement");
+    Suite *s = suite_create("services_nodemanagement");
 
-	TCase *tc_addnodes = tcase_create("addnodes");
-	tcase_add_test(tc_addnodes, AddVariableNode);
+    TCase *tc_addnodes = tcase_create("addnodes");
+    tcase_add_test(tc_addnodes, AddVariableNode);
         tcase_add_test(tc_addnodes, AddComplexTypeWithInheritance);
-	tcase_add_test(tc_addnodes, AddNodeTwiceGivesError);
+    tcase_add_test(tc_addnodes, AddNodeTwiceGivesError);
 
-	suite_add_tcase(s, tc_addnodes);
-	return s;
+    suite_add_tcase(s, tc_addnodes);
+    return s;
 }
 
 int main(void) {
-	int number_failed = 0;
-	Suite *s;
-	s = testSuite_services_nodemanagement();
-	SRunner *sr = srunner_create(s);
-	srunner_set_fork_status(sr, CK_NOFORK);
-	srunner_run_all(sr, CK_NORMAL);
-	number_failed += srunner_ntests_failed(sr);
-	srunner_free(sr);
-	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+    int number_failed = 0;
+    Suite *s;
+    s = testSuite_services_nodemanagement();
+    SRunner *sr = srunner_create(s);
+    srunner_set_fork_status(sr, CK_NOFORK);
+    srunner_run_all(sr, CK_NORMAL);
+    number_failed += srunner_ntests_failed(sr);
+    srunner_free(sr);
+    return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
 }

+ 128 - 0
tests/check_services_subscriptions.c

@@ -0,0 +1,128 @@
+#include "ua_server.h"
+#include "server/ua_services.h"
+#include "server/ua_server_internal.h"
+#include "server/ua_subscription.h"
+#include "ua_config_standard.h"
+
+#include "check.h"
+#include <unistd.h>
+
+UA_Server *server = NULL;
+
+static void setup(void) {
+    server = UA_Server_new(UA_ServerConfig_standard);
+    UA_Server_run_startup(server);
+}
+
+static void teardown(void) {
+    UA_Server_run_shutdown(server);
+    UA_Server_delete(server);
+}
+
+START_TEST(Server_createSubscription) {
+    /* Create a subscription */
+    UA_CreateSubscriptionRequest request;
+    UA_CreateSubscriptionRequest_init(&request);
+    request.publishingEnabled = true;
+
+    UA_CreateSubscriptionResponse response;
+    UA_CreateSubscriptionResponse_init(&response);
+
+    Service_CreateSubscription(server, &adminSession, &request, &response);
+    ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
+    UA_UInt32 subscriptionId = response.subscriptionId;
+
+    UA_CreateSubscriptionResponse_deleteMembers(&response);
+
+    /* Remove the subscription */
+    UA_DeleteSubscriptionsRequest del_request;
+    UA_DeleteSubscriptionsRequest_init(&del_request);
+    del_request.subscriptionIdsSize = 1;
+    del_request.subscriptionIds = &subscriptionId;
+
+    UA_DeleteSubscriptionsResponse del_response;
+    UA_DeleteSubscriptionsResponse_init(&del_response);
+
+    Service_DeleteSubscriptions(server, &adminSession, &del_request, &del_response);
+    ck_assert_uint_eq(del_response.resultsSize, 1);
+    ck_assert_uint_eq(del_response.results[0], UA_STATUSCODE_GOOD);
+
+    UA_DeleteSubscriptionsResponse_deleteMembers(&del_response);
+}
+END_TEST
+
+START_TEST(Server_publishCallback) {
+    /* Create a subscription */
+    UA_CreateSubscriptionRequest request;
+    UA_CreateSubscriptionResponse response;
+
+    UA_CreateSubscriptionRequest_init(&request);
+    request.publishingEnabled = true;
+    UA_CreateSubscriptionResponse_init(&response);
+    Service_CreateSubscription(server, &adminSession, &request, &response);
+    ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
+    UA_UInt32 subscriptionId1 = response.subscriptionId;
+    UA_CreateSubscriptionResponse_deleteMembers(&response);
+
+    /* Create a second subscription */
+    UA_CreateSubscriptionRequest_init(&request);
+    request.publishingEnabled = true;
+    UA_CreateSubscriptionResponse_init(&response);
+    Service_CreateSubscription(server, &adminSession, &request, &response);
+    ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
+    UA_UInt32 subscriptionId2 = response.subscriptionId;
+    UA_Double publishingInterval = response.revisedPublishingInterval;
+    ck_assert(publishingInterval > 0.0f);
+    UA_CreateSubscriptionResponse_deleteMembers(&response);
+
+    /* Sleep until the publishing interval times out */
+    usleep((useconds_t)(publishingInterval * 1000) + 1000);
+
+
+    UA_Subscription *sub;
+    LIST_FOREACH(sub, &adminSession.serverSubscriptions, listEntry)
+        ck_assert_uint_eq(sub->currentKeepAliveCount, 0);
+
+    UA_Server_run_iterate(server, false);
+
+    LIST_FOREACH(sub, &adminSession.serverSubscriptions, listEntry)
+        ck_assert_uint_eq(sub->currentKeepAliveCount, 1);
+
+    /* Remove the subscriptions */
+    UA_DeleteSubscriptionsRequest del_request;
+    UA_DeleteSubscriptionsRequest_init(&del_request);
+    UA_UInt32 removeIds[2] = {subscriptionId1, subscriptionId2};
+    del_request.subscriptionIdsSize = 2;
+    del_request.subscriptionIds = removeIds;
+
+    UA_DeleteSubscriptionsResponse del_response;
+    UA_DeleteSubscriptionsResponse_init(&del_response);
+
+    Service_DeleteSubscriptions(server, &adminSession, &del_request, &del_response);
+    ck_assert_uint_eq(del_response.resultsSize, 2);
+    ck_assert_uint_eq(del_response.results[0], UA_STATUSCODE_GOOD);
+    ck_assert_uint_eq(del_response.results[1], UA_STATUSCODE_GOOD);
+
+    UA_DeleteSubscriptionsResponse_deleteMembers(&del_response);
+}
+END_TEST
+
+static Suite* testSuite_Client(void) {
+    Suite *s = suite_create("Server Subscription");
+    TCase *tc_server = tcase_create("Server Subscription Basic");
+    tcase_add_checked_fixture(tc_server, setup, teardown);
+    tcase_add_test(tc_server, Server_createSubscription);
+    tcase_add_test(tc_server, Server_publishCallback);
+    suite_add_tcase(s, tc_server);
+    return s;
+}
+
+int main(void) {
+    Suite *s = testSuite_Client();
+    SRunner *sr = srunner_create(s);
+    srunner_set_fork_status(sr, CK_NOFORK);
+    srunner_run_all(sr,CK_NORMAL);
+    int number_failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+    return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}

+ 26 - 26
tests/check_services_view.c

@@ -7,47 +7,47 @@
 
 /* START_TEST(Service_TranslateBrowsePathsToNodeIds_SmokeTest)
 {
-	UA_TranslateBrowsePathsToNodeIdsRequest request;
-	UA_TranslateBrowsePathsToNodeIdsRequest_init(&request);
+    UA_TranslateBrowsePathsToNodeIdsRequest request;
+    UA_TranslateBrowsePathsToNodeIdsRequest_init(&request);
 
-	UA_TranslateBrowsePathsToNodeIdsResponse response;
-	UA_TranslateBrowsePathsToNodeIdsResponse_init(&response);
+    UA_TranslateBrowsePathsToNodeIdsResponse response;
+    UA_TranslateBrowsePathsToNodeIdsResponse_init(&response);
 
-	request.browsePathsSize = 1;
-	UA_Array_new((void**)&request.browsePaths,request.browsePathsSize, &UA_.types[UA_BROWSEPATH]);
+    request.browsePathsSize = 1;
+    UA_Array_new((void**)&request.browsePaths,request.browsePathsSize, &UA_.types[UA_BROWSEPATH]);
 
-	Service_TranslateBrowsePathsToNodeIds(NULL,&request,&response);
+    Service_TranslateBrowsePathsToNodeIds(NULL,&request,&response);
 
-	ck_assert_int_eq(response.resultsSize,request.browsePathsSize);
-	ck_assert_int_eq(response.results[0].statusCode,UA_STATUSCODE_BADNOMATCH);
+    ck_assert_int_eq(response.resultsSize,request.browsePathsSize);
+    ck_assert_int_eq(response.results[0].statusCode,UA_STATUSCODE_BADNOMATCH);
 
-	//finally
-	UA_TranslateBrowsePathsToNodeIdsRequest_deleteMembers(&request);
-	UA_TranslateBrowsePathsToNodeIdsResponse_deleteMembers(&response);
+    //finally
+    UA_TranslateBrowsePathsToNodeIdsRequest_deleteMembers(&request);
+    UA_TranslateBrowsePathsToNodeIdsResponse_deleteMembers(&response);
 }
 END_TEST */
 
 static Suite* testSuite_Service_TranslateBrowsePathsToNodeIds(void) {
-	Suite *s = suite_create("Service_TranslateBrowsePathsToNodeIds");
-	TCase *tc_core = tcase_create("Core");
-	//tcase_add_test(tc_core, Service_TranslateBrowsePathsToNodeIds_SmokeTest);
-	suite_add_tcase(s,tc_core);
-	return s;
+    Suite *s = suite_create("Service_TranslateBrowsePathsToNodeIds");
+    TCase *tc_core = tcase_create("Core");
+    //tcase_add_test(tc_core, Service_TranslateBrowsePathsToNodeIds_SmokeTest);
+    suite_add_tcase(s,tc_core);
+    return s;
 }
 
 int main(void) {
-	int number_failed = 0;
+    int number_failed = 0;
 
-	Suite *s;
-	SRunner *sr;
+    Suite *s;
+    SRunner *sr;
 
-	s = testSuite_Service_TranslateBrowsePathsToNodeIds();
-	sr = srunner_create(s);
-	srunner_run_all(sr,CK_NORMAL);
-	number_failed += srunner_ntests_failed(sr);
-	srunner_free(sr);
+    s = testSuite_Service_TranslateBrowsePathsToNodeIds();
+    sr = srunner_create(s);
+    srunner_run_all(sr,CK_NORMAL);
+    number_failed += srunner_ntests_failed(sr);
+    srunner_free(sr);
 
-	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+    return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
 }
 
 

+ 25 - 25
tests/check_session.c

@@ -7,8 +7,8 @@
 
 START_TEST(Session_init_ShallWork)
 {
-	UA_Session session;
-	UA_Session_init(&session);
+    UA_Session session;
+    UA_Session_init(&session);
 
     UA_NodeId tmpNodeId;
     UA_NodeId_init(&tmpNodeId);
@@ -16,9 +16,9 @@ START_TEST(Session_init_ShallWork)
     UA_ApplicationDescription_init(&tmpAppDescription);
     UA_DateTime tmpDateTime;
     UA_DateTime_init(&tmpDateTime);
-	ck_assert_int_eq(session.activated,false);
-	ck_assert_int_eq(session.authenticationToken.identifier.numeric,tmpNodeId.identifier.numeric);
-	ck_assert_int_eq(session.availableContinuationPoints,MAXCONTINUATIONPOINTS);
+    ck_assert_int_eq(session.activated,false);
+    ck_assert_int_eq(session.authenticationToken.identifier.numeric,tmpNodeId.identifier.numeric);
+    ck_assert_int_eq(session.availableContinuationPoints,MAXCONTINUATIONPOINTS);
     ck_assert_ptr_eq(session.channel,NULL);
     ck_assert_ptr_eq(session.clientDescription.applicationName.locale.data,NULL);
     ck_assert_ptr_eq(session.continuationPoints.lh_first, NULL);
@@ -33,40 +33,40 @@ END_TEST
 
 START_TEST(Session_updateLifetime_ShallWork)
 {
-	UA_Session session;
-	UA_Session_init(&session);
+    UA_Session session;
+    UA_Session_init(&session);
     UA_DateTime tmpDateTime;
     tmpDateTime = session.validTill;
-	UA_Session_updateLifetime(&session);
+    UA_Session_updateLifetime(&session);
 
-	UA_Int32 result = (session.validTill > tmpDateTime);
-	ck_assert_int_gt(result,0);
+    UA_Int32 result = (session.validTill > tmpDateTime);
+    ck_assert_int_gt(result,0);
 }
 END_TEST
 
 static Suite* testSuite_Session(void) {
-	Suite *s = suite_create("Session");
-	TCase *tc_core = tcase_create("Core");
-	tcase_add_test(tc_core, Session_init_ShallWork);
-	tcase_add_test(tc_core, Session_updateLifetime_ShallWork);
+    Suite *s = suite_create("Session");
+    TCase *tc_core = tcase_create("Core");
+    tcase_add_test(tc_core, Session_init_ShallWork);
+    tcase_add_test(tc_core, Session_updateLifetime_ShallWork);
 
-	suite_add_tcase(s,tc_core);
-	return s;
+    suite_add_tcase(s,tc_core);
+    return s;
 }
 
 int main(void) {
-	int number_failed = 0;
+    int number_failed = 0;
 
-	Suite *s;
-	SRunner *sr;
+    Suite *s;
+    SRunner *sr;
 
-	s = testSuite_Session();
-	sr = srunner_create(s);
-	srunner_run_all(sr,CK_NORMAL);
-	number_failed += srunner_ntests_failed(sr);
-	srunner_free(sr);
+    s = testSuite_Session();
+    sr = srunner_create(s);
+    srunner_run_all(sr,CK_NORMAL);
+    number_failed += srunner_ntests_failed(sr);
+    srunner_free(sr);
 
-	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+    return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
 }
 
 

+ 20 - 0
tools/generate_datatypes.py

@@ -75,9 +75,13 @@ class Type(object):
                 break
 
     def datatype_c(self):
+        xmlEncodingId = "0"
+        binaryEncodingId = "0"
         if self.name in typedescriptions:
             description = typedescriptions[self.name]
             typeid = "{.namespaceIndex = %s, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = %s}" % (description.namespaceid, description.nodeid)
+            xmlEncodingId = description.xmlEncodingId
+            binaryEncodingId = description.binaryEncodingId
         else:
             typeid = "{.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 0}"
         return "{ .typeId = " + typeid + \
@@ -87,8 +91,10 @@ class Type(object):
             ",\n  .builtin = " + self.builtin + \
             ",\n  .fixedSize = " + self.fixed_size + \
             ",\n  .overlayable = " + self.overlayable + \
+            ",\n  .binaryEncodingId = " + binaryEncodingId + \
             ",\n  .membersSize = " + str(len(self.members)) + \
             ",\n  .members = %s_members" % self.name + " }"
+            #",\n  .xmlEncodingId = " + xmlEncodingId + \ Not used for now
 
     def members_c(self):
         members = "static UA_DataTypeMember %s_members[%s] = {" % (self.name, len(self.members))
@@ -291,6 +297,8 @@ class TypeDescription(object):
         self.name = name
         self.nodeid = nodeid
         self.namespaceid = namespaceid
+        self.xmlEncodingId = "0"
+        self.binaryEncodingId = "0"
 
 def parseTypeDescriptions(filename, namespaceid):
     definitions = {}
@@ -301,6 +309,18 @@ def parseTypeDescriptions(filename, namespaceid):
     for index, row in enumerate(rows):
         if len(row) < 3:
             continue
+        if row[2] == "Object":
+            # Check if node name ends with _Encoding_(DefaultXml|DefaultBinary) and store the node id in the corresponding DataType
+            m = re.match('(.*?)_Encoding_Default(Xml|Binary)$',row[0])
+            if (m):
+                baseType = m.group(1)
+                if baseType not in types:
+                    continue
+                if m.group(2) == "Xml":
+                    definitions[baseType].xmlEncodingId = row[1]
+                else:
+                    definitions[baseType].binaryEncodingId = row[1]
+            continue
         if row[2] != "DataType":
             continue
         if row[0] == "BaseDataType":

+ 0 - 2
tools/generate_nodeids.py

@@ -23,8 +23,6 @@ def useNodeId(row):
         return False
     if "Type_" in row[0]:
         return False
-    if "_Encoding_Default" in row[0]:
-        return False
     return True
 
 f = open(args.nodeids)