Browse Source

Merge pull request #139 from acplt/external_datasource

Merging in .. keeping the history of the branch intact
Julius Pfrommer 10 years ago
parent
commit
b87f71f764

+ 1 - 0
.gitignore

@@ -69,3 +69,4 @@ tests/check_encode
 tests/check_memory
 tests/check_memory
 tests/check_namespace
 tests/check_namespace
 tests/coverage
 tests/coverage
+Makefile

+ 5 - 1
CMakeLists.txt

@@ -25,9 +25,10 @@ set(lib_sources src/ua_types.c
                 src/ua_session.c
                 src/ua_session.c
                 src/ua_util.c
                 src/ua_util.c
                 src/server/ua_server.c
                 src/server/ua_server.c
+				src/server/ua_server_addressspace.c
+				src/server/ua_server_binary.c
                 src/server/ua_securechannel_manager.c
                 src/server/ua_securechannel_manager.c
                 src/server/ua_session_manager.c
                 src/server/ua_session_manager.c
-                src/server/ua_server_binary.c
                 src/server/ua_services_attribute.c
                 src/server/ua_services_attribute.c
                 src/server/ua_services_session.c
                 src/server/ua_services_session.c
                 src/server/ua_services_discovery.c
                 src/server/ua_services_discovery.c
@@ -139,18 +140,21 @@ file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/src_generated")
 include_directories("${PROJECT_BINARY_DIR}/src_generated") 
 include_directories("${PROJECT_BINARY_DIR}/src_generated") 
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.c
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.c
                           ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.h
                           ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.h
+                   PRE_BUILD
                    COMMAND python ${PROJECT_SOURCE_DIR}/tools/generate_builtin.py --export-prototypes ${generate_src_options} ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated
                    COMMAND python ${PROJECT_SOURCE_DIR}/tools/generate_builtin.py --export-prototypes ${generate_src_options} ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_builtin.py
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_builtin.py
                            ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd)
                            ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd)
 
 
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_0.c
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_0.c
                           ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_0.h
                           ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_0.h
+                   PRE_BUILD
                    COMMAND python ${PROJECT_SOURCE_DIR}/tools/generate_namespace.py ${generate_src_options} ${PROJECT_SOURCE_DIR}/tools/schema/NodeIds.csv ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_0
                    COMMAND python ${PROJECT_SOURCE_DIR}/tools/generate_namespace.py ${generate_src_options} ${PROJECT_SOURCE_DIR}/tools/schema/NodeIds.csv ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_0
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_namespace.py
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_namespace.py
                            ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/NodeIds.csv)
                            ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/NodeIds.csv)
 
 
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.c
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.c
                           ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.h
                           ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.h
+                   PRE_BUILD
                    COMMAND python ${PROJECT_SOURCE_DIR}/tools/generate_builtin.py --additional-includes=ua_transport.h ${PROJECT_SOURCE_DIR}/tools/schema/Custom.Opc.Ua.Transport.bsd ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated
                    COMMAND python ${PROJECT_SOURCE_DIR}/tools/generate_builtin.py --additional-includes=ua_transport.h ${PROJECT_SOURCE_DIR}/tools/schema/Custom.Opc.Ua.Transport.bsd ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_namespace.py
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_namespace.py
                            ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/Custom.Opc.Ua.Transport.bsd)
                            ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/Custom.Opc.Ua.Transport.bsd)

+ 33 - 31
examples/networklayer_tcp.c

@@ -4,6 +4,7 @@
  */
  */
 
 
 #ifdef WIN32
 #ifdef WIN32
+#include <malloc.h>
 #include <winsock2.h>
 #include <winsock2.h>
 #include <sys/types.h>
 #include <sys/types.h>
 #include <Windows.h>
 #include <Windows.h>
@@ -62,6 +63,37 @@ NetworklayerTCP *NetworklayerTCP_new(UA_ConnectionConfig localConf, UA_UInt32 po
 	return newlayer;
 	return newlayer;
 }
 }
 
 
+void NetworklayerTCP_delete(NetworklayerTCP *layer) {
+	for(UA_UInt32 index = 0;index < layer->connectionsSize;index++) {
+		shutdown(layer->connections[index].sockfd, 2);
+        if(layer->connections[index].connection.channel)
+            layer->connections[index].connection.channel->connection = NULL;
+        UA_Connection_deleteMembers(&layer->connections[index].connection);
+		CLOSESOCKET(layer->connections[index].sockfd);
+	}
+	free(layer->connections);
+	free(layer);
+}
+
+void closeCallback(TCPConnectionHandle *handle);
+void writeCallback(TCPConnectionHandle *handle, UA_ByteStringArray gather_buf);
+
+static UA_StatusCode NetworklayerTCP_add(NetworklayerTCP *layer, UA_Int32 newsockfd) {
+    layer->connectionsSize++;
+	layer->connections = realloc(layer->connections, sizeof(TCPConnection) * layer->connectionsSize);
+	TCPConnection *newconnection = &layer->connections[layer->connectionsSize-1];
+	newconnection->sockfd = newsockfd;
+
+	struct TCPConnectionHandle *callbackhandle;
+    if(!(callbackhandle = malloc(sizeof(struct TCPConnectionHandle))))
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    callbackhandle->layer = layer;
+	callbackhandle->sockfd = newsockfd;
+	UA_Connection_init(&newconnection->connection, layer->localConf, callbackhandle,
+					   (UA_Connection_closeCallback)closeCallback, (UA_Connection_writeCallback)writeCallback);
+	return UA_STATUSCODE_GOOD;
+}
+
 // copy the array of connections, but _loose_ one. This does not close the
 // copy the array of connections, but _loose_ one. This does not close the
 // actual socket.
 // actual socket.
 static UA_StatusCode NetworklayerTCP_remove(NetworklayerTCP *layer, UA_Int32 sockfd) {
 static UA_StatusCode NetworklayerTCP_remove(NetworklayerTCP *layer, UA_Int32 sockfd) {
@@ -90,18 +122,6 @@ static UA_StatusCode NetworklayerTCP_remove(NetworklayerTCP *layer, UA_Int32 soc
 	return UA_STATUSCODE_GOOD;
 	return UA_STATUSCODE_GOOD;
 }
 }
 
 
-void NetworklayerTCP_delete(NetworklayerTCP *layer) {
-	for(UA_UInt32 index = 0;index < layer->connectionsSize;index++) {
-		shutdown(layer->connections[index].sockfd, 2);
-        if(layer->connections[index].connection.channel)
-            layer->connections[index].connection.channel->connection = NULL;
-        UA_Connection_deleteMembers(&layer->connections[index].connection);
-		CLOSESOCKET(layer->connections[index].sockfd);
-	}
-	free(layer->connections);
-	free(layer);
-}
-
 /** Callback function */
 /** Callback function */
 void closeCallback(TCPConnectionHandle *handle) {
 void closeCallback(TCPConnectionHandle *handle) {
 	shutdown(handle->sockfd,2);
 	shutdown(handle->sockfd,2);
@@ -114,7 +134,7 @@ void writeCallback(TCPConnectionHandle *handle, UA_ByteStringArray gather_buf) {
 	UA_UInt32 total_len = 0;
 	UA_UInt32 total_len = 0;
 	UA_UInt32 nWritten = 0;
 	UA_UInt32 nWritten = 0;
 #ifdef WIN32
 #ifdef WIN32
-	LPWSABUF buf = malloc(gather_buf.stringsSize * sizeof(WSABUF));
+	LPWSABUF buf = _alloca(gather_buf.stringsSize * sizeof(WSABUF));
 	int result = 0;
 	int result = 0;
 	for(UA_UInt32 i = 0; i<gather_buf.stringsSize; i++) {
 	for(UA_UInt32 i = 0; i<gather_buf.stringsSize; i++) {
 		buf[i].buf = gather_buf.strings[i].data;
 		buf[i].buf = gather_buf.strings[i].data;
@@ -130,7 +150,6 @@ void writeCallback(TCPConnectionHandle *handle, UA_ByteStringArray gather_buf) {
 		} while (errno == EINTR);
 		} while (errno == EINTR);
 		nWritten += n;
 		nWritten += n;
 	}
 	}
-	free(buf);
 #else
 #else
 	struct iovec iov[gather_buf.stringsSize];
 	struct iovec iov[gather_buf.stringsSize];
 	for(UA_UInt32 i=0;i<gather_buf.stringsSize;i++) {
 	for(UA_UInt32 i=0;i<gather_buf.stringsSize;i++) {
@@ -161,23 +180,6 @@ void writeCallback(TCPConnectionHandle *handle, UA_ByteStringArray gather_buf) {
         free(gather_buf.strings[i].data);
         free(gather_buf.strings[i].data);
 }
 }
 
 
-static UA_StatusCode NetworklayerTCP_add(NetworklayerTCP *layer, UA_Int32 newsockfd) {
-    layer->connectionsSize++;
-	layer->connections = realloc(layer->connections, sizeof(TCPConnection) * layer->connectionsSize);
-	TCPConnection *newconnection = &layer->connections[layer->connectionsSize-1];
-	newconnection->sockfd = newsockfd;
-
-	struct TCPConnectionHandle *callbackhandle;
-    callbackhandle = malloc(sizeof(struct TCPConnectionHandle));
-    if(!callbackhandle)
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-    callbackhandle->layer = layer;
-	callbackhandle->sockfd = newsockfd;
-	UA_Connection_init(&newconnection->connection, layer->localConf, callbackhandle,
-					   (UA_Connection_closeCallback)closeCallback, (UA_Connection_writeCallback)writeCallback);
-	return UA_STATUSCODE_GOOD;
-}
-
 static UA_StatusCode setNonBlocking(int sockid) {
 static UA_StatusCode setNonBlocking(int sockid) {
 #ifdef WIN32
 #ifdef WIN32
 	u_long iMode = 1;
 	u_long iMode = 1;

+ 14 - 12
examples/opcuaServer.c

@@ -48,22 +48,23 @@ UA_ByteString loadCertificate() {
 
 
     return certificate;
     return certificate;
 }
 }
-
 int main(int argc, char** argv) {
 int main(int argc, char** argv) {
 	signal(SIGINT, stopHandler); /* catches ctrl-c */
 	signal(SIGINT, stopHandler); /* catches ctrl-c */
 
 
 	UA_String endpointUrl;
 	UA_String endpointUrl;
-	UA_String_copycstring("opc.tcp://192.168.56.101:16664",&endpointUrl);
-    UA_ByteString certificate = loadCertificate();
+    UA_String_copycstring("opc.tcp://localhost:16664",&endpointUrl);
+	UA_ByteString certificate = loadCertificate();
 	UA_Server *server = UA_Server_new(&endpointUrl, &certificate);
 	UA_Server *server = UA_Server_new(&endpointUrl, &certificate);
-	//Logger_Stdout_init(&server->logger);
-
-    UA_Int32 myInteger = 42;
-    UA_String myIntegerName;
-    UA_STRING_STATIC(myIntegerName, "The Answer");
-    UA_Server_addScalarVariableNode(server, &myIntegerName, (void*)&myInteger, &UA_TYPES[UA_INT32],
-                                    &UA_NODEIDS[UA_OBJECTSFOLDER], &UA_NODEIDS[UA_HASCOMPONENT]);
 
 
+	//add a node to the adresspace
+    UA_Int32 *myInteger = malloc(sizeof(UA_Int32));
+    *myInteger = 42;
+    UA_QualifiedName myIntegerName;
+    UA_QualifiedName_copycstring("the answer is",&myIntegerName);
+    UA_Server_addScalarVariableNode(server, &myIntegerName, myInteger, &UA_TYPES[UA_INT32],
+                                    &UA_EXPANDEDNODEIDS[UA_OBJECTSFOLDER], &UA_NODEIDS[UA_ORGANIZES]);
+    UA_QualifiedName_deleteMembers(&myIntegerName);
+    
 #ifdef BENCHMARK
 #ifdef BENCHMARK
     UA_UInt32 nodeCount = 500;
     UA_UInt32 nodeCount = 500;
     UA_Int32 data = 42;
     UA_Int32 data = 42;
@@ -81,7 +82,8 @@ int main(int argc, char** argv) {
         tmpNode->value.storage.data.dataPtr = &data;
         tmpNode->value.storage.data.dataPtr = &data;
         tmpNode->value.storageType = UA_VARIANT_DATA_NODELETE;
         tmpNode->value.storageType = UA_VARIANT_DATA_NODELETE;
         tmpNode->value.storage.data.arrayLength = 1;
         tmpNode->value.storage.data.arrayLength = 1;
-        UA_Server_addNode(server, (UA_Node**)&tmpNode, &UA_NODEIDS[UA_OBJECTSFOLDER], &UA_NODEIDS[UA_HASCOMPONENT]);
+        UA_Server_addNode(server, (const UA_Node**)&tmpNode, &UA_EXPANDEDNODEIDS[UA_OBJECTSFOLDER],
+                          &UA_NODEIDS[UA_HASCOMPONENT]);
     }
     }
 #endif
 #endif
 	
 	
@@ -93,5 +95,5 @@ int main(int argc, char** argv) {
 	UA_Server_delete(server);
 	UA_Server_delete(server);
 	NetworklayerTCP_delete(nl);
 	NetworklayerTCP_delete(nl);
     UA_String_deleteMembers(&endpointUrl);
     UA_String_deleteMembers(&endpointUrl);
-	return retval == UA_STATUSCODE_GOOD ? 0 : retval;
+	return retval;
 }
 }

+ 104 - 12
include/ua_server.h

@@ -25,24 +25,116 @@ extern "C" {
 #include "ua_connection.h"
 #include "ua_connection.h"
 #include "ua_log.h"
 #include "ua_log.h"
 
 
-/** @defgroup server Server */
-
-struct UA_NodeStore;
-typedef struct UA_NodeStore UA_NodeStore;
+/**
+ * @defgroup server Server
+ *
+ * @brief This module describes the server object and functions to interact with * it.
+ *
+ * @{
+ */
 
 
 struct UA_Server;
 struct UA_Server;
 typedef struct UA_Server UA_Server;
 typedef struct UA_Server UA_Server;
 
 
 UA_Server UA_EXPORT * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertificate);
 UA_Server UA_EXPORT * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertificate);
 void UA_EXPORT UA_Server_delete(UA_Server *server);
 void UA_EXPORT UA_Server_delete(UA_Server *server);
-void UA_EXPORT UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, const UA_ByteString *msg);
-
-/* Services for local use */
-UA_AddNodesResult UA_EXPORT UA_Server_addNode(UA_Server *server, UA_Node **node, const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId);
-void UA_EXPORT UA_Server_addReference(UA_Server *server, const UA_AddReferencesRequest *request, UA_AddReferencesResponse *response);
-UA_AddNodesResult UA_EXPORT UA_Server_addScalarVariableNode(UA_Server *server, UA_String *browseName, void *value,
-                                                            const UA_VTable_Entry *vt, const UA_NodeId *parentNodeId,
-                                                            const UA_NodeId *referenceTypeId );
+void UA_EXPORT UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection,
+                                              const UA_ByteString *msg);
+
+/**
+ * @brief Adds a node to the server's address space
+ *
+ * If adding the node succeeds, the pointer to the node is set to null. If the
+ * original nodeid is null (ns=0,i=0), a unique new nodeid is created for the
+ * node and returned in the AddNodesResult struct. */
+UA_AddNodesResult UA_EXPORT
+UA_Server_addNode(UA_Server *server, const UA_Node **node, const UA_ExpandedNodeId *parentNodeId,
+                  const UA_NodeId *referenceTypeId);
+
+/** @brief Adds a reference to the server's address space */
+UA_StatusCode UA_EXPORT
+UA_Server_addReference(UA_Server *server, const UA_AddReferencesItem *item);
+
+/**
+ * @brief Adds a VariableNode to the server's address space that points to a
+ * scalar value. The value must lie on the heap and cannot be reused afterwards
+ * as it becomes attached to the lifecycle of the VariableNode */
+void UA_EXPORT
+UA_Server_addScalarVariableNode(UA_Server *server, UA_QualifiedName *browseName, void *value,
+                                const UA_TypeVTable *vt, const UA_ExpandedNodeId *parentNodeId,
+                                const UA_NodeId *referenceTypeId );
+/** @} */
+
+/**
+ * @ingroup server
+ *
+ * @defgroup external_nodestore External Nodestore
+ *
+ * @brief This modules describes the VTable and function signatures to add an
+ * external nodestore to the server.
+ *
+ * To plug in outside data sources, one can use
+ *
+ * - VariableNodes with a data source (functions that are called for read and write access)
+ * - An external nodestore that is mapped to specific namespaces
+ *
+ * If no external nodestore is defined for a nodeid, it is always looked up in
+ * the "local" nodestore of open62541. Namespace Zero is always in the local
+ * nodestore.
+ *
+ *  @{
+ */
+
+typedef UA_Int32 (*UA_ExternalNodeStore_addNodes)
+(void *ensHandle, const UA_RequestHeader *requestHeader, UA_AddNodesItem *nodesToAdd, UA_UInt32 *indices,
+ UA_UInt32 indicesSize, UA_AddNodesResult* addNodesResults, UA_DiagnosticInfo *diagnosticInfos);
+
+typedef UA_Int32 (*UA_ExternalNodeStore_addReferences)
+(void *ensHandle, const UA_RequestHeader *requestHeader, UA_AddReferencesItem* referencesToAdd,
+ UA_UInt32 *indices,UA_UInt32 indicesSize, UA_StatusCode *addReferencesResults,
+ UA_DiagnosticInfo *diagnosticInfos);
+
+typedef UA_Int32 (*UA_ExternalNodeStore_deleteNodes)
+(void *ensHandle, const UA_RequestHeader *requestHeader, UA_DeleteNodesItem *nodesToDelete, UA_UInt32 *indices,
+ UA_UInt32 indicesSize, UA_StatusCode *deleteNodesResults, UA_DiagnosticInfo *diagnosticInfos);
+
+typedef UA_Int32 (*UA_ExternalNodeStore_deleteReferences)
+(void *ensHandle, const UA_RequestHeader *requestHeader, UA_DeleteReferencesItem *referenceToDelete,
+ UA_UInt32 *indices, UA_UInt32 indicesSize, UA_StatusCode deleteReferencesresults,
+ UA_DiagnosticInfo *diagnosticInfos);
+
+typedef UA_Int32 (*UA_ExternalNodeStore_readNodes)
+(void *ensHandle, const UA_RequestHeader *requestHeader, UA_ReadValueId *readValueIds, UA_UInt32 *indices,
+ UA_UInt32 indicesSize,UA_DataValue *readNodesResults, UA_Boolean timeStampToReturn,
+ UA_DiagnosticInfo *diagnosticInfos);
+
+typedef UA_Int32 (*UA_ExternalNodeStore_writeNodes)
+(void *ensHandle, const UA_RequestHeader *requestHeader, UA_WriteValue *writeValues, UA_UInt32 *indices,
+ UA_UInt32 indicesSize, UA_StatusCode *writeNodesResults, UA_DiagnosticInfo *diagnosticInfo);
+
+typedef UA_Int32 (*UA_ExternalNodeStore_browseNodes)
+(void *ensHandle, const UA_RequestHeader *requestHeader, UA_BrowseDescription *browseDescriptions,
+ UA_UInt32 *indices, UA_UInt32 indicesSize, UA_UInt32 requestedMaxReferencesPerNode,
+ UA_BrowseResult *browseResults, UA_DiagnosticInfo *diagnosticInfos);
+
+typedef UA_Int32 (*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_addReferences addReferences;
+	UA_ExternalNodeStore_deleteReferences deleteReferences;
+	UA_ExternalNodeStore_delete delete;
+} UA_ExternalNodeStore;
+
+UA_StatusCode UA_EXPORT
+UA_Server_addExternalNamespace(UA_Server *server, UA_UInt16 namespaceIndex, UA_ExternalNodeStore *nodeStore);
+
+/** @} */
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 } // extern "C"
 } // extern "C"

+ 27 - 123
include/ua_types.h

@@ -188,22 +188,11 @@ typedef struct UA_ExtensionObject {
     UA_ByteString body; // contains either the bytestring or a pointer to the memory-object
     UA_ByteString body; // contains either the bytestring or a pointer to the memory-object
 } UA_ExtensionObject;
 } UA_ExtensionObject;
 
 
-struct UA_VTable_Entry; // forwards declaration
-typedef struct UA_VTable_Entry UA_VTable_Entry;
-
-/* Variant */
-/** @defgroup variant Variant
- *
- * @brief Variants may contain (arrays of) any other datatype.
- *
- * For that, we provide a reference to the utility functions of the type as well
- * as possible encoding by pointing an entry in the vtable of types. References
- * may either contain a structure with the variants data, or a datasource where
- * this structure may be obtained (using reference counting for async access).
- *
- * @{
- */
+struct UA_TypeVTable; // forward declaration
+typedef struct UA_TypeVTable UA_TypeVTable;
 
 
+/** @brief Pointers to data that is stored in memory. The "type" of the data is
+    stored in the variant itself. */
 typedef struct UA_VariantData {
 typedef struct UA_VariantData {
     UA_Int32  arrayLength;        // total number of elements in the data-pointer
     UA_Int32  arrayLength;        // total number of elements in the data-pointer
     void     *dataPtr;
     void     *dataPtr;
@@ -224,16 +213,15 @@ typedef struct UA_VariantDataSource {
     void (*delete)(const void *identifier); /**< Indicates that the node with the datasource was removed from the namespace. */
     void (*delete)(const void *identifier); /**< Indicates that the node with the datasource was removed from the namespace. */
 } UA_VariantDataSource;
 } UA_VariantDataSource;
 
 
-/** @brief A union of all of the types specified above.
- *
- * Variants store (arrays of) built-in types. If you want to store a more
- * complex (or self-defined) type, you have to use an UA_ExtensionObject.*/
+/** @brief Variants store (arrays of) any data type. Either they provide a
+    pointer to the data in memory, or functions from which the data can be
+    accessed. */
 typedef struct UA_Variant {
 typedef struct UA_Variant {
-    const UA_VTable_Entry *vt; // internal entry into vTable
+    const UA_TypeVTable *vt; /// The VTable of the datatype in question
     enum {
     enum {
-        UA_VARIANT_DATA,
-        UA_VARIANT_DATA_NODELETE, // do not free the data (e.g. because it is "borrowed" and points into a larger structure)
-        UA_VARIANT_DATASOURCE
+        UA_VARIANT_DATA, ///< The data is stored in memory and "owned" by this variant
+        UA_VARIANT_DATA_NODELETE, ///< The data is stored in memory, but only "borrowed" and shall not be deleted at the end of this variant's lifecycle
+        UA_VARIANT_DATASOURCE ///< The data is provided externally. Call the functions in the datasource to get a current version
     } storageType;
     } storageType;
     union {
     union {
         UA_VariantData       data;
         UA_VariantData       data;
@@ -241,8 +229,6 @@ typedef struct UA_Variant {
     } storage;
     } storage;
 } UA_Variant;
 } UA_Variant;
 
 
-/** @} */
-
 /** @brief A data value with an associated status code and timestamps. */
 /** @brief A data value with an associated status code and timestamps. */
 typedef struct UA_DataValue {
 typedef struct UA_DataValue {
     UA_Byte       encodingMask;
     UA_Byte       encodingMask;
@@ -344,6 +330,10 @@ UA_TYPE_PROTOTYPES(UA_Variant)
 UA_TYPE_PROTOTYPES(UA_DiagnosticInfo)
 UA_TYPE_PROTOTYPES(UA_DiagnosticInfo)
 UA_TYPE_PROTOTYPES(UA_InvalidType)
 UA_TYPE_PROTOTYPES(UA_InvalidType)
 
 
+/**********************************************/
+/* Custom functions for the builtin datatypes */
+/**********************************************/
+
 /* String */
 /* String */
 #define UA_STRING_NULL (UA_String) {-1, (UA_Byte*)0 }
 #define UA_STRING_NULL (UA_String) {-1, (UA_Byte*)0 }
 #define UA_STRING_STATIC(VARIABLE, STRING) do { \
 #define UA_STRING_STATIC(VARIABLE, STRING) do { \
@@ -390,20 +380,10 @@ void UA_EXPORT UA_ByteString_printx_hex(char *label, const UA_ByteString *string
 /* NodeId */
 /* NodeId */
 UA_Boolean UA_EXPORT UA_NodeId_equal(const UA_NodeId *n1, const UA_NodeId *n2);
 UA_Boolean UA_EXPORT UA_NodeId_equal(const UA_NodeId *n1, const UA_NodeId *n2);
 UA_Boolean UA_EXPORT UA_NodeId_isNull(const UA_NodeId *p);
 UA_Boolean UA_EXPORT UA_NodeId_isNull(const UA_NodeId *p);
-UA_Boolean UA_EXPORT UA_NodeId_isBasicType(UA_NodeId const *id);
-
-#define NS0NODEID(NUMERIC_ID)                                                                        \
-    (UA_NodeId) {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = \
-                     NUMERIC_ID }
 
 
 /* ExpandedNodeId */
 /* ExpandedNodeId */
 UA_Boolean UA_EXPORT UA_ExpandedNodeId_isNull(const UA_ExpandedNodeId *p);
 UA_Boolean UA_EXPORT UA_ExpandedNodeId_isNull(const UA_ExpandedNodeId *p);
 
 
-#define NS0EXPANDEDNODEID(VARIABLE, NUMERIC_ID) do {   \
-        VARIABLE.nodeId       = NS0NODEID(NUMERIC_ID); \
-        VARIABLE.namespaceUri = UA_STRING_NULL;        \
-        VARIABLE.serverIndex  = 0; } while(0)
-
 /* QualifiedName */
 /* QualifiedName */
 #define UA_QUALIFIEDNAME_STATIC(VARIABLE, STRING) do { \
 #define UA_QUALIFIEDNAME_STATIC(VARIABLE, STRING) do { \
         VARIABLE.namespaceIndex = 0;                   \
         VARIABLE.namespaceIndex = 0;                   \
@@ -421,18 +401,18 @@ void UA_EXPORT UA_QualifiedName_printf(char const *label, const UA_QualifiedName
 UA_StatusCode UA_EXPORT UA_LocalizedText_copycstring(char const *src, UA_LocalizedText *dst);
 UA_StatusCode UA_EXPORT UA_LocalizedText_copycstring(char const *src, UA_LocalizedText *dst);
 
 
 /* Variant */
 /* Variant */
-UA_StatusCode UA_EXPORT UA_Variant_copySetValue(UA_Variant *v, const UA_VTable_Entry *vt, const void *value);
-UA_StatusCode UA_EXPORT UA_Variant_copySetArray(UA_Variant *v, const UA_VTable_Entry *vt, UA_Int32 arrayLength, const void *array);
+UA_StatusCode UA_EXPORT UA_Variant_copySetValue(UA_Variant *v, const UA_TypeVTable *vt, const void *value);
+UA_StatusCode UA_EXPORT UA_Variant_copySetArray(UA_Variant *v, const UA_TypeVTable *vt, UA_Int32 arrayLength, const void *array);
 
 
 /* Array operations */
 /* Array operations */
-UA_StatusCode UA_EXPORT UA_Array_new(void **p, UA_Int32 noElements, const UA_VTable_Entry *vt);
-void UA_EXPORT UA_Array_init(void *p, UA_Int32 noElements, const UA_VTable_Entry *vt);
-void UA_EXPORT UA_Array_delete(void *p, UA_Int32 noElements, const UA_VTable_Entry *vt);
+UA_StatusCode UA_EXPORT UA_Array_new(void **p, UA_Int32 noElements, const UA_TypeVTable *vt);
+void UA_EXPORT UA_Array_init(void *p, UA_Int32 noElements, const UA_TypeVTable *vt);
+void UA_EXPORT UA_Array_delete(void *p, UA_Int32 noElements, const UA_TypeVTable *vt);
 
 
-/* @brief The destination array is allocated according to noElements. */
-UA_StatusCode UA_EXPORT UA_Array_copy(const void *src, UA_Int32 noElements, const UA_VTable_Entry *vt, void **dst);
+/* @brief The destination array is allocated with size noElements. */
+UA_StatusCode UA_EXPORT UA_Array_copy(const void *src, UA_Int32 noElements, const UA_TypeVTable *vt, void **dst);
 #ifdef DEBUG
 #ifdef DEBUG
-void UA_EXPORT UA_Array_print(const void *p, UA_Int32 noElements, const UA_VTable_Entry *vt, FILE *stream);
+void UA_EXPORT UA_Array_print(const void *p, UA_Int32 noElements, const UA_TypeVTable *vt, FILE *stream);
 #endif
 #endif
 
 
 /**********/
 /**********/
@@ -450,7 +430,7 @@ typedef struct UA_Encoding {
 
 
 #define UA_ENCODING_BINARY 0 // Binary encoding is always available
 #define UA_ENCODING_BINARY 0 // Binary encoding is always available
 
 
-struct UA_VTable_Entry {
+struct UA_TypeVTable {
     UA_NodeId     typeId;
     UA_NodeId     typeId;
     UA_Byte       *name;
     UA_Byte       *name;
     void *        (*new)();
     void *        (*new)();
@@ -461,88 +441,12 @@ struct UA_VTable_Entry {
 #ifdef DEBUG
 #ifdef DEBUG
     void          (*print)(const void *p, FILE *stream);
     void          (*print)(const void *p, FILE *stream);
 #endif
 #endif
-    UA_UInt32  memSize;                        // size of the struct only in memory (no dynamic components)
-    UA_Boolean dynMembers;                     // does the type contain members that are dynamically
-                                               // allocated (strings, ..)? Otherwise, the size on
-                                               // the wire == size in memory.
+    UA_UInt32  memSize;                        // size of the struct
+    UA_Boolean dynMembers;                     // does the type contain members that are dynamically on the heap?
     UA_Encoding encodings[UA_ENCODING_AMOUNT]; // binary, xml, ... UA_ENCODING_AMOUNT is set by the build script
     UA_Encoding encodings[UA_ENCODING_AMOUNT]; // binary, xml, ... UA_ENCODING_AMOUNT is set by the build script
 };
 };
 
 
-/***********************************/
-/* Macros for type implementations */
-/***********************************/
-
-#define UA_TYPE_DEFAULT(TYPE)            \
-    UA_TYPE_DELETE_DEFAULT(TYPE)         \
-    UA_TYPE_DELETEMEMBERS_NOACTION(TYPE) \
-    UA_TYPE_INIT_DEFAULT(TYPE)           \
-    UA_TYPE_NEW_DEFAULT(TYPE)            \
-    UA_TYPE_COPY_DEFAULT(TYPE)           \
-    
-#define UA_TYPE_NEW_DEFAULT(TYPE)                             \
-    TYPE * TYPE##_new() {                                     \
-        TYPE *p = UA_alloc(sizeof(TYPE));                     \
-        if(p) TYPE##_init(p);                                 \
-        return p;                                             \
-    }
-
-#define UA_TYPE_INIT_DEFAULT(TYPE) \
-    void TYPE##_init(TYPE * p) {   \
-        *p = (TYPE)0;              \
-    }
-
-#define UA_TYPE_INIT_AS(TYPE, TYPE_AS) \
-    void TYPE##_init(TYPE * p) {       \
-        TYPE_AS##_init((TYPE_AS *)p);  \
-    }
-
-#define UA_TYPE_DELETE_DEFAULT(TYPE) \
-    void TYPE##_delete(TYPE *p) {    \
-        TYPE##_deleteMembers(p);     \
-        UA_free(p);                  \
-    }
-
-#define UA_TYPE_DELETE_AS(TYPE, TYPE_AS) \
-    void TYPE##_delete(TYPE * p) {       \
-        TYPE_AS##_delete((TYPE_AS *)p);  \
-    }
-
-#define UA_TYPE_DELETEMEMBERS_NOACTION(TYPE) \
-    void TYPE##_deleteMembers(TYPE * p) { return; }
-
-#define UA_TYPE_DELETEMEMBERS_AS(TYPE, TYPE_AS) \
-    void TYPE##_deleteMembers(TYPE * p) { TYPE_AS##_deleteMembers((TYPE_AS *)p); }
-
-/* Use only when the type has no arrays. Otherwise, implement deep copy */
-#define UA_TYPE_COPY_DEFAULT(TYPE)                             \
-    UA_StatusCode TYPE##_copy(TYPE const *src, TYPE *dst) {    \
-        *dst = *src;                                           \
-        return UA_STATUSCODE_GOOD;                             \
-    }
-
-#define UA_TYPE_COPY_AS(TYPE, TYPE_AS)                         \
-    UA_StatusCode TYPE##_copy(TYPE const *src, TYPE *dst) {    \
-        return TYPE_AS##_copy((TYPE_AS *)src, (TYPE_AS *)dst); \
-    }
-
-#ifdef DEBUG //print functions only in debug mode
-#define UA_TYPE_PRINT_AS(TYPE, TYPE_AS)              \
-    void TYPE##_print(TYPE const *p, FILE *stream) { \
-        TYPE_AS##_print((TYPE_AS *)p, stream);       \
-    }
-#else
-#define UA_TYPE_PRINT_AS(TYPE, TYPE_AS)
-#endif
-
-#define UA_TYPE_AS(TYPE, TYPE_AS)           \
-    UA_TYPE_NEW_DEFAULT(TYPE)               \
-    UA_TYPE_INIT_AS(TYPE, TYPE_AS)          \
-    UA_TYPE_DELETE_AS(TYPE, TYPE_AS)        \
-    UA_TYPE_DELETEMEMBERS_AS(TYPE, TYPE_AS) \
-    UA_TYPE_COPY_AS(TYPE, TYPE_AS)          \
-    UA_TYPE_PRINT_AS(TYPE, TYPE_AS)
-
-/// @} /* end of group */
+/** @} */
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 } // extern "C"
 } // extern "C"

+ 1 - 1
src/ongoing/ua_namespace_xml.c

@@ -17,7 +17,7 @@ UA_Int32 Namespace_loadXml(Namespace **ns,UA_UInt32 nsid,const char* rootName, X
 	*ns = n.ns;
 	*ns = n.ns;
 
 
 	XML_Stack_addChildHandler(&s, "UANodeSet", strlen("UANodeSet"), (XML_decoder) UA_NodeSet_decodeXmlFromStack, UA_INVALIDTYPE, &n);
 	XML_Stack_addChildHandler(&s, "UANodeSet", strlen("UANodeSet"), (XML_decoder) UA_NodeSet_decodeXmlFromStack, UA_INVALIDTYPE, &n);
-	XML_Parser parser = XML_ParserCreate(NULL);
+	XML_Parser parser = XML_ParserCreate(UA_NULL);
 	XML_SetUserData(parser, &s);
 	XML_SetUserData(parser, &s);
 	XML_SetElementHandler(parser, XML_Stack_startElement, XML_Stack_endElement);
 	XML_SetElementHandler(parser, XML_Stack_startElement, XML_Stack_endElement);
 	XML_SetCharacterDataHandler(parser, XML_Stack_handleText);
 	XML_SetCharacterDataHandler(parser, XML_Stack_handleText);

+ 228 - 158
src/server/ua_nodestore.c

@@ -2,16 +2,30 @@
 #include "ua_util.h"
 #include "ua_util.h"
 #include "ua_statuscodes.h"
 #include "ua_statuscodes.h"
 
 
+/* It could happen that we want to delete a node even though a function higher
+   in the call-chain still has a reference. So we count references and delete
+   once the count falls to zero. That means we copy every node to a new place
+   where it is right behind the refcount integer.
+
+   Since we copy nodes on the heap, we make the alloc for the nodeEntry bigger
+   to accommodate for the different nodeclasses (the nodeEntry may have an
+   overlength "tail"). */
+#define ALIVE_BIT (1 << 15) /* Alive bit in the refcount */
+struct nodeEntry {
+    UA_UInt16 refcount;
+    const UA_Node node;
+};
+
 struct UA_NodeStore {
 struct UA_NodeStore {
-    const UA_Node **entries;
-    UA_UInt32       size;
-    UA_UInt32       count;
-    UA_UInt32       sizePrimeIndex;
+    struct nodeEntry **entries;
+    UA_UInt32          size;
+    UA_UInt32          count;
+    UA_UInt32          sizePrimeIndex;
 };
 };
 
 
-typedef UA_UInt32 hash_t;
 /* The size of the hash-map is always a prime number. They are chosen to be
 /* The size of the hash-map is always a prime number. They are chosen to be
    close to the next power of 2. So the size ca. doubles with each prime. */
    close to the next power of 2. So the size ca. doubles with each prime. */
+typedef UA_UInt32 hash_t;
 static hash_t const primes[] = {
 static hash_t const primes[] = {
     7,         13,         31,         61,         127,         251,
     7,         13,         31,         61,         127,         251,
     509,       1021,       2039,       4093,       8191,        16381,
     509,       1021,       2039,       4093,       8191,        16381,
@@ -20,13 +34,8 @@ static hash_t const primes[] = {
     134217689, 268435399,  536870909,  1073741789, 2147483647,  4294967291
     134217689, 268435399,  536870909,  1073741789, 2147483647,  4294967291
 };
 };
 
 
-static INLINE hash_t mod(hash_t h, hash_t size) {
-    return h % size;
-}
-static INLINE hash_t mod2(hash_t h, hash_t size) {
-    return 1 + (h % (size - 2));
-}
-
+static INLINE hash_t mod(hash_t h, hash_t size) { return h % size; }
+static INLINE hash_t mod2(hash_t h, hash_t size) { return 1 + (h % (size - 2)); }
 static INLINE UA_Int16 higher_prime_index(hash_t n) {
 static INLINE UA_Int16 higher_prime_index(hash_t n) {
     UA_UInt16 low  = 0;
     UA_UInt16 low  = 0;
     UA_UInt16 high = sizeof(primes) / sizeof(hash_t);
     UA_UInt16 high = sizeof(primes) / sizeof(hash_t);
@@ -41,10 +50,12 @@ static INLINE UA_Int16 higher_prime_index(hash_t n) {
 }
 }
 
 
 /* Based on Murmur-Hash 3 by Austin Appleby (public domain, freely usable) */
 /* Based on Murmur-Hash 3 by Austin Appleby (public domain, freely usable) */
-static INLINE hash_t hash_array(const UA_Byte *data, UA_UInt32 len, UA_UInt32 seed) {
+static hash_t hash_array(const UA_Byte *data, UA_UInt32 len, UA_UInt32 seed) {
+    if(data == UA_NULL)
+        return 0;
+
     const int32_t   nblocks = len / 4;
     const int32_t   nblocks = len / 4;
     const uint32_t *blocks;
     const uint32_t *blocks;
-
     static const uint32_t c1 = 0xcc9e2d51;
     static const uint32_t c1 = 0xcc9e2d51;
     static const uint32_t c2 = 0x1b873593;
     static const uint32_t c2 = 0x1b873593;
     static const uint32_t r1 = 15;
     static const uint32_t r1 = 15;
@@ -52,10 +63,6 @@ static INLINE hash_t hash_array(const UA_Byte *data, UA_UInt32 len, UA_UInt32 se
     static const uint32_t m  = 5;
     static const uint32_t m  = 5;
     static const uint32_t n  = 0xe6546b64;
     static const uint32_t n  = 0xe6546b64;
     hash_t hash = seed;
     hash_t hash = seed;
-
-    if(data == UA_NULL)
-        return 0;
-
     blocks = (const uint32_t *)data;
     blocks = (const uint32_t *)data;
     for(int32_t i = 0;i < nblocks;i++) {
     for(int32_t i = 0;i < nblocks;i++) {
         uint32_t k = blocks[i];
         uint32_t k = blocks[i];
@@ -68,14 +75,11 @@ static INLINE hash_t hash_array(const UA_Byte *data, UA_UInt32 len, UA_UInt32 se
 
 
     const uint8_t *tail = (const uint8_t *)(data + nblocks * 4);
     const uint8_t *tail = (const uint8_t *)(data + nblocks * 4);
     uint32_t       k1   = 0;
     uint32_t       k1   = 0;
-
     switch(len & 3) {
     switch(len & 3) {
     case 3:
     case 3:
         k1 ^= tail[2] << 16;
         k1 ^= tail[2] << 16;
-
     case 2:
     case 2:
         k1 ^= tail[1] << 8;
         k1 ^= tail[1] << 8;
-
     case 1:
     case 1:
         k1   ^= tail[0];
         k1   ^= tail[0];
         k1   *= c1;
         k1   *= c1;
@@ -90,95 +94,42 @@ static INLINE hash_t hash_array(const UA_Byte *data, UA_UInt32 len, UA_UInt32 se
     hash ^= (hash >> 13);
     hash ^= (hash >> 13);
     hash *= 0xc2b2ae35;
     hash *= 0xc2b2ae35;
     hash ^= (hash >> 16);
     hash ^= (hash >> 16);
-
     return hash;
     return hash;
 }
 }
 
 
-static INLINE hash_t hash(const UA_NodeId *n) {
+static hash_t hash(const UA_NodeId *n) {
     switch(n->identifierType) {
     switch(n->identifierType) {
     case UA_NODEIDTYPE_NUMERIC:
     case UA_NODEIDTYPE_NUMERIC:
         /*  Knuth's multiplicative hashing */
         /*  Knuth's multiplicative hashing */
         return (n->identifier.numeric + n->namespaceIndex) * 2654435761;   // mod(2^32) is implicit
         return (n->identifier.numeric + n->namespaceIndex) * 2654435761;   // mod(2^32) is implicit
-
     case UA_NODEIDTYPE_STRING:
     case UA_NODEIDTYPE_STRING:
         return hash_array(n->identifier.string.data, n->identifier.string.length, n->namespaceIndex);
         return hash_array(n->identifier.string.data, n->identifier.string.length, n->namespaceIndex);
-
     case UA_NODEIDTYPE_GUID:
     case UA_NODEIDTYPE_GUID:
         return hash_array((UA_Byte *)&(n->identifier.guid), sizeof(UA_Guid), n->namespaceIndex);
         return hash_array((UA_Byte *)&(n->identifier.guid), sizeof(UA_Guid), n->namespaceIndex);
-
     case UA_NODEIDTYPE_BYTESTRING:
     case UA_NODEIDTYPE_BYTESTRING:
         return hash_array((UA_Byte *)n->identifier.byteString.data, n->identifier.byteString.length, n->namespaceIndex);
         return hash_array((UA_Byte *)n->identifier.byteString.data, n->identifier.byteString.length, n->namespaceIndex);
-
     default:
     default:
         UA_assert(UA_FALSE);
         UA_assert(UA_FALSE);
         return 0;
         return 0;
     }
     }
 }
 }
 
 
-static INLINE void clear_entry(UA_NodeStore *ns, const UA_Node **entry) {
-    const UA_Node *node;
-    if(entry == UA_NULL || *entry == UA_NULL)
-        return;
-
-    node = *entry;
-    switch(node->nodeClass) {
-    case UA_NODECLASS_OBJECT:
-        UA_ObjectNode_delete((UA_ObjectNode *)node);
-        break;
-
-    case UA_NODECLASS_VARIABLE:
-        UA_VariableNode_delete((UA_VariableNode *)node);
-        break;
-
-    case UA_NODECLASS_METHOD:
-        UA_MethodNode_delete((UA_MethodNode *)node);
-        break;
-
-    case UA_NODECLASS_OBJECTTYPE:
-        UA_ObjectTypeNode_delete((UA_ObjectTypeNode *)node);
-        break;
-
-    case UA_NODECLASS_VARIABLETYPE:
-        UA_VariableTypeNode_delete((UA_VariableTypeNode *)node);
-        break;
-
-    case UA_NODECLASS_REFERENCETYPE:
-        UA_ReferenceTypeNode_delete((UA_ReferenceTypeNode *)node);
-        break;
-
-    case UA_NODECLASS_DATATYPE:
-        UA_DataTypeNode_delete((UA_DataTypeNode *)node);
-        break;
-
-    case UA_NODECLASS_VIEW:
-        UA_ViewNode_delete((UA_ViewNode *)node);
-        break;
-
-    default:
-        UA_assert(UA_FALSE);
-        break;
-    }
-    entry = UA_NULL;
-    ns->count--;
-}
-
-/* Returns UA_STATUSCODE_GOOD if an entry was found. Otherwise, An error code is
-   returned and the "entry" argument points to the first free entry under the
-   NodeId. */
-static INLINE UA_StatusCode find_entry(const UA_NodeStore *ns, const UA_NodeId *nodeid, const UA_Node ***entry) {
-    hash_t          h     = hash(nodeid);
-    UA_UInt32       size  = ns->size;
-    hash_t          index = mod(h, size);
-    const UA_Node **e     = &ns->entries[index];
+/* Returns UA_TRUE if an entry was found under the nodeid. Otherwise, returns
+   false and sets slot to a pointer to the next free slot. */
+static UA_Boolean __containsNodeId(const UA_NodeStore *ns, const UA_NodeId *nodeid, struct nodeEntry ***entry) {
+    hash_t         h     = hash(nodeid);
+    UA_UInt32      size  = ns->size;
+    hash_t         index = mod(h, size);
+    struct nodeEntry **e = &ns->entries[index];
 
 
     if(*e == UA_NULL) {
     if(*e == UA_NULL) {
         *entry = e;
         *entry = e;
-        return UA_STATUSCODE_BADINTERNALERROR;
+        return UA_FALSE;
     }
     }
 
 
-    if(UA_NodeId_equal(&(*e)->nodeId, nodeid)) {
+    if(UA_NodeId_equal(&(*e)->node.nodeId, nodeid)) {
         *entry = e;
         *entry = e;
-        return UA_STATUSCODE_GOOD;
+        return UA_TRUE;
     }
     }
 
 
     hash_t hash2 = mod2(h, size);
     hash_t hash2 = mod2(h, size);
@@ -191,156 +142,275 @@ static INLINE UA_StatusCode find_entry(const UA_NodeStore *ns, const UA_NodeId *
 
 
         if(*e == UA_NULL) {
         if(*e == UA_NULL) {
             *entry = e;
             *entry = e;
-            return UA_STATUSCODE_BADINTERNALERROR;
+            return UA_FALSE;
         }
         }
 
 
-        if(UA_NodeId_equal(&(*e)->nodeId, nodeid)) {
+        if(UA_NodeId_equal(&(*e)->node.nodeId, nodeid)) {
             *entry = e;
             *entry = e;
-            return UA_STATUSCODE_GOOD;
+            return UA_TRUE;
         }
         }
     }
     }
 
 
     /* NOTREACHED */
     /* NOTREACHED */
-    return UA_STATUSCODE_GOOD;
+    return UA_TRUE;
 }
 }
 
 
 /* The following function changes size of memory allocated for the entries and
 /* The following function changes size of memory allocated for the entries and
    repeatedly inserts the table elements. The occupancy of the table after the
    repeatedly inserts the table elements. The occupancy of the table after the
    call will be about 50%. */
    call will be about 50%. */
-static UA_StatusCode expand(UA_NodeStore *ns) {
-    const UA_Node **nentries;
-    int32_t nsize;
-    UA_UInt32 nindex;
-
-    const UA_Node **oentries = ns->entries;
-    int32_t osize = ns->size;
-    const UA_Node **olimit   = &oentries[osize];
-    int32_t count = ns->count;
-
+static UA_StatusCode __expand(UA_NodeStore *ns) {
+    UA_Int32 osize = ns->size;
+    UA_Int32 count = ns->count;
     /* Resize only when table after removal of unused elements is either too full or too empty.  */
     /* Resize only when table after removal of unused elements is either too full or too empty.  */
     if(count * 2 < osize && (count * 8 > osize || osize <= 32))
     if(count * 2 < osize && (count * 8 > osize || osize <= 32))
         return UA_STATUSCODE_GOOD;
         return UA_STATUSCODE_GOOD;
 
 
-    nindex = higher_prime_index(count * 2);
-    nsize  = primes[nindex];
 
 
-    if(!(nentries = UA_alloc(sizeof(UA_Node *) * nsize)))
+    UA_UInt32 nindex = higher_prime_index(count * 2);
+    UA_Int32 nsize = primes[nindex];
+    struct nodeEntry **nentries;
+    if(!(nentries = UA_alloc(sizeof(struct nodeEntry *) * nsize)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
 
-    memset(nentries, 0, nsize * sizeof(UA_Node *));
+    memset(nentries, 0, nsize * sizeof(struct nodeEntry *));
+    struct nodeEntry **oentries = ns->entries;
     ns->entries = nentries;
     ns->entries = nentries;
     ns->size    = nsize;
     ns->size    = nsize;
     ns->sizePrimeIndex = nindex;
     ns->sizePrimeIndex = nindex;
 
 
-    const UA_Node **p = oentries;
-    do {
-        if(*p != UA_NULL) {
-            const UA_Node **e;
-            find_entry(ns, &(*p)->nodeId, &e);  /* We know this returns an empty entry here */
-            *e = *p;
-        }
-        p++;
-    } while(p < olimit);
+    // recompute the position of every entry and insert the pointer
+    for(UA_Int32 i=0, j=0;i<osize && j<count;i++) {
+        if(!oentries[i])
+            continue;
+        struct nodeEntry **e;
+        __containsNodeId(ns, &(*oentries[i]).node.nodeId, &e);  /* We know this returns an empty entry here */
+        *e = oentries[i];
+        j++;
+    }
 
 
     UA_free(oentries);
     UA_free(oentries);
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
+/* Marks the entry dead and deletes if necessary. */
+static void __deleteEntry(struct nodeEntry *entry) {
+    if(entry->refcount > 0)
+        return;
+    const UA_Node *node = &entry->node;
+    switch(node->nodeClass) {
+    case UA_NODECLASS_OBJECT:
+        UA_ObjectNode_deleteMembers((UA_ObjectNode *)node);
+        break;
+    case UA_NODECLASS_VARIABLE:
+        UA_VariableNode_deleteMembers((UA_VariableNode *)node);
+        break;
+    case UA_NODECLASS_METHOD:
+        UA_MethodNode_deleteMembers((UA_MethodNode *)node);
+        break;
+    case UA_NODECLASS_OBJECTTYPE:
+        UA_ObjectTypeNode_deleteMembers((UA_ObjectTypeNode *)node);
+        break;
+    case UA_NODECLASS_VARIABLETYPE:
+        UA_VariableTypeNode_deleteMembers((UA_VariableTypeNode *)node);
+        break;
+    case UA_NODECLASS_REFERENCETYPE:
+        UA_ReferenceTypeNode_deleteMembers((UA_ReferenceTypeNode *)node);
+        break;
+    case UA_NODECLASS_DATATYPE:
+        UA_DataTypeNode_deleteMembers((UA_DataTypeNode *)node);
+        break;
+    case UA_NODECLASS_VIEW:
+        UA_ViewNode_deleteMembers((UA_ViewNode *)node);
+        break;
+    default:
+        UA_assert(UA_FALSE);
+        break;
+    }
+    UA_free(entry);
+}
+
+static INLINE struct nodeEntry * __nodeEntryFromNode(const UA_Node *node) {
+    UA_UInt32 nodesize = 0;
+    /* Copy the node into the entry. Then reset the original node. It shall no longer be used. */
+    switch(node->nodeClass) {
+    case UA_NODECLASS_OBJECT:
+        nodesize = sizeof(UA_ObjectNode);
+        break;
+    case UA_NODECLASS_VARIABLE:
+        nodesize = sizeof(UA_VariableNode);
+        break;
+    case UA_NODECLASS_METHOD:
+        nodesize = sizeof(UA_MethodNode);
+        break;
+    case UA_NODECLASS_OBJECTTYPE:
+        nodesize = sizeof(UA_ObjectTypeNode);
+        break;
+    case UA_NODECLASS_VARIABLETYPE:
+        nodesize = sizeof(UA_VariableTypeNode);
+        break;
+    case UA_NODECLASS_REFERENCETYPE:
+        nodesize = sizeof(UA_ReferenceTypeNode);
+        break;
+    case UA_NODECLASS_DATATYPE:
+        nodesize = sizeof(UA_DataTypeNode);
+        break;
+    case UA_NODECLASS_VIEW:
+        nodesize = sizeof(UA_ViewNode);
+        break;
+    default:
+        UA_assert(UA_FALSE);
+    }
+
+    struct nodeEntry *entry;
+    if(!(entry = UA_alloc(sizeof(struct nodeEntry) - sizeof(UA_Node) + nodesize)))
+        return UA_NULL;
+    memcpy((void *)&entry->node, node, nodesize);
+    UA_free((void*)node);
+    return entry;
+}
+
 /**********************/
 /**********************/
 /* Exported functions */
 /* Exported functions */
 /**********************/
 /**********************/
 
 
-UA_StatusCode UA_NodeStore_new(UA_NodeStore **result) {
+UA_NodeStore * UA_NodeStore_new() {
     UA_NodeStore *ns;
     UA_NodeStore *ns;
-    UA_UInt32     sizePrimeIndex, size;
     if(!(ns = UA_alloc(sizeof(UA_NodeStore))))
     if(!(ns = UA_alloc(sizeof(UA_NodeStore))))
-        return UA_STATUSCODE_BADOUTOFMEMORY;
+        return UA_NULL;
 
 
-    sizePrimeIndex = higher_prime_index(32);
-    size = primes[sizePrimeIndex];
-    if(!(ns->entries = UA_alloc(sizeof(UA_Node *) * size))) {
+    ns->sizePrimeIndex = higher_prime_index(32);
+    ns->size = primes[ns->sizePrimeIndex];
+    ns->count = 0;
+    if(!(ns->entries = UA_alloc(sizeof(struct nodeEntry *) * ns->size))) {
         UA_free(ns);
         UA_free(ns);
-        return UA_STATUSCODE_BADOUTOFMEMORY;
+        return UA_NULL;
     }
     }
-
-    /* set entries to zero */
-    memset(ns->entries, 0, size * sizeof(UA_Node *));
-
-    *ns     = (UA_NodeStore) {ns->entries, size, 0, sizePrimeIndex };
-    *result = ns;
-    return UA_STATUSCODE_GOOD;
+    memset(ns->entries, 0, ns->size * sizeof(struct nodeEntry *));
+    return ns;
 }
 }
 
 
 void UA_NodeStore_delete(UA_NodeStore *ns) {
 void UA_NodeStore_delete(UA_NodeStore *ns) {
-    UA_UInt32       size    = ns->size;
-    const UA_Node **entries = ns->entries;
-
-    for(UA_UInt32 i = 0;i < size;i++)
-        clear_entry(ns, &entries[i]);
+    UA_UInt32 size = ns->size;
+    struct nodeEntry **entries = ns->entries;
+    for(UA_UInt32 i = 0;i < size;i++) {
+        if(entries[i] != UA_NULL) {
+            entries[i]->refcount &= ~ALIVE_BIT; // mark dead
+            __deleteEntry(entries[i]);
+            entries[i] = UA_NULL;
+            ns->count--;
+        }
+    }
 
 
     UA_free(ns->entries);
     UA_free(ns->entries);
     UA_free(ns);
     UA_free(ns);
 }
 }
 
 
-UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node **node, UA_Byte flags) {
+UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, const UA_Node **node, UA_Boolean getManaged) {
     if(ns->size * 3 <= ns->count * 4) {
     if(ns->size * 3 <= ns->count * 4) {
-        if(expand(ns) != UA_STATUSCODE_GOOD)
+        if(__expand(ns) != UA_STATUSCODE_GOOD)
             return UA_STATUSCODE_BADINTERNALERROR;
             return UA_STATUSCODE_BADINTERNALERROR;
     }
     }
 
 
-    const UA_Node **entry;
-    UA_Int32 found = find_entry(ns, &(*node)->nodeId, &entry);
-
-    if(flags & UA_NODESTORE_INSERT_UNIQUE) {
-        if(found == UA_STATUSCODE_GOOD)
-            return UA_STATUSCODE_BADNODEIDEXISTS;
-        else
-            *entry = *node;
+    // get a free slot
+    struct nodeEntry **slot;
+    UA_NodeId *nodeId = (UA_NodeId *)&(*node)->nodeId;
+    if(UA_NodeId_isNull(nodeId)) {
+        // find a unique nodeid that is not taken
+        nodeId->identifierType = UA_NODEIDTYPE_NUMERIC;
+        nodeId->namespaceIndex = 1; // namespace 1 is always in the local nodestore
+        UA_Int32 identifier = ns->count+1; // start value
+        UA_Int32 size = ns->size;
+        hash_t increase = mod2(identifier, size);
+        while(UA_TRUE) {
+            nodeId->identifier.numeric = identifier;
+            if(!__containsNodeId(ns, nodeId, &slot))
+                break;
+            identifier += increase;
+            if(identifier >= size)
+                identifier -= size;
+        }
     } else {
     } else {
-        if(found == UA_STATUSCODE_GOOD)
-            clear_entry(ns, entry);
-        *entry = *node;
+        if(__containsNodeId(ns, nodeId, &slot))
+            return UA_STATUSCODE_BADNODEIDEXISTS;
     }
     }
+    
+    struct nodeEntry *entry = __nodeEntryFromNode(*node);
+    if(!entry)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    *slot = entry;
+    ns->count++;
 
 
-    if(!(flags & UA_NODESTORE_INSERT_GETMANAGED))
+    if(getManaged) {
+        entry->refcount = ALIVE_BIT + 1;
+        *node = &entry->node;
+    } else {
+        entry->refcount = ALIVE_BIT;
         *node = UA_NULL;
         *node = UA_NULL;
+    }
 
 
-    ns->count++;
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
-UA_StatusCode UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid, const UA_Node **managedNode) {
-    const UA_Node **entry;
-    if(find_entry(ns, nodeid, &entry) != UA_STATUSCODE_GOOD)
+UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, const UA_Node **node, UA_Boolean getManaged) {
+    struct nodeEntry **slot;
+    const UA_NodeId *nodeId = &(*node)->nodeId;
+    if(!__containsNodeId(ns, nodeId, &slot))
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
 
 
-    *managedNode = *entry;
+    struct nodeEntry *entry = __nodeEntryFromNode(*node);
+    if(!entry)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    (*slot)->refcount &= ~ALIVE_BIT; // mark dead
+    __deleteEntry(*slot);
+    *slot = entry;
+
+    if(getManaged) {
+        entry->refcount = ALIVE_BIT + 1;
+        *node = &entry->node;
+    } else {
+        entry->refcount = ALIVE_BIT;
+        *node = UA_NULL;
+    }
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
+const UA_Node * UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid) {
+    struct nodeEntry **slot;
+    if(!__containsNodeId(ns, nodeid, &slot))
+        return UA_NULL;
+    (*slot)->refcount++;
+    return &(*slot)->node;
+}
+
 UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
 UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
-    const UA_Node **entry;
-    if(find_entry(ns, nodeid, &entry) != UA_STATUSCODE_GOOD)
+    struct nodeEntry **slot;
+    if(!__containsNodeId(ns, nodeid, &slot))
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
 
 
     // Check before if deleting the node makes the UA_NodeStore inconsistent.
     // Check before if deleting the node makes the UA_NodeStore inconsistent.
-    clear_entry(ns, entry);
+    (*slot)->refcount &= ~ALIVE_BIT; // mark dead
+    __deleteEntry(*slot);
+    *slot = UA_NULL;
+    ns->count--;
 
 
     /* Downsize the hashmap if it is very empty */
     /* Downsize the hashmap if it is very empty */
     if(ns->count * 8 < ns->size && ns->size > 32)
     if(ns->count * 8 < ns->size && ns->size > 32)
-        expand(ns); // this can fail. we just continue with the bigger hashmap.
+        __expand(ns); // this can fail. we just continue with the bigger hashmap.
 
 
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
 void UA_NodeStore_iterate(const UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor) {
 void UA_NodeStore_iterate(const UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor) {
     for(UA_UInt32 i = 0;i < ns->size;i++) {
     for(UA_UInt32 i = 0;i < ns->size;i++) {
-        const UA_Node *node = ns->entries[i];
-        if(node != UA_NULL)
-            visitor(node);
+        if(ns->entries[i] != UA_NULL)
+            visitor(&ns->entries[i]->node);
     }
     }
 }
 }
 
 
 void UA_NodeStore_release(const UA_Node *managed) {
 void UA_NodeStore_release(const UA_Node *managed) {
-    ;
+    struct nodeEntry *entry = (struct nodeEntry *) ((char*)managed - offsetof(struct nodeEntry, node));
+    entry->refcount--;
+    __deleteEntry(entry);    
 }
 }

+ 29 - 21
src/server/ua_nodestore.h

@@ -1,40 +1,51 @@
 #ifndef UA_NODESTORE_H_
 #ifndef UA_NODESTORE_H_
 #define UA_NODESTORE_H_
 #define UA_NODESTORE_H_
 
 
-#include "ua_server.h"
+#include "ua_types_generated.h"
+#include "ua_util.h"
 
 
 /**
 /**
    @ingroup server
    @ingroup server
 
 
    @defgroup nodestore NodeStore
    @defgroup nodestore NodeStore
 
 
-   @brief The nodestore is the central storage for nodes in the UA address
-   space. Internally, the nodestore is realised as hash-map where nodes are
-   stored and retrieved with their nodeid.
+   @brief The nodestore contains the nodes in the UA address space. Internally,
+   it is based on a hash-map that maps nodes to their nodeid.
 
 
-   The nodes in the nodestore are immutable. To change the content of a node, it
-   needs to be replaced as a whole. When a node is inserted into the namespace,
-   it gets replaced with a pointer to a managed node. Managed nodes shall never
-   be freed by the user. This is done by the namespace when the node is removed
-   and no readers (in other threads) access the node.
+   ATTENTION! You need to allocate single nodes on the heap (with _new) before
+   adding them to the nodestore with _insert or _replace. The node is then
+   copied to a new (managed) location in the nodestore and the original memory
+   is freed. The nodes in the nodestore are immutable. To change the content of
+   a node, it needs to be replaced as a whole.
 
 
-   @{
+   ATTENTION! Every node you _get from the nodestore needs to be _released when
+   it is no longer needed. Otherwise, we can't know if somebody still uses it
+   (especially in multi-threaded environments).
  */
  */
 
 
+struct UA_NodeStore;
+typedef struct UA_NodeStore UA_NodeStore;
+
 /** @brief Create a new namespace */
 /** @brief Create a new namespace */
-UA_StatusCode UA_NodeStore_new(UA_NodeStore **result);
+UA_NodeStore * UA_NodeStore_new();
 
 
 /** @brief Delete the namespace and all nodes in it */
 /** @brief Delete the namespace and all nodes in it */
 void UA_NodeStore_delete(UA_NodeStore *ns);
 void UA_NodeStore_delete(UA_NodeStore *ns);
 
 
-#define UA_NODESTORE_INSERT_UNIQUE 1
-#define UA_NODESTORE_INSERT_GETMANAGED 2
 /** @brief Insert a new node into the namespace
 /** @brief Insert a new node into the namespace
 
 
-    With the UNIQUE flag, the node is only inserted if the nodeid does not
-    already exist. With the GETMANAGED flag, the node pointer is replaced with
-    the managed pointer. Otherwise, it is set to UA_NULL. */
-UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node **node, UA_Byte flags);
+    With the getManaged flag, the node pointer is replaced with the managed
+    pointer. Otherwise, it is set to UA_NULL.
+
+    If the nodeid is zero, then a fresh numeric nodeid from namespace 1 is
+    assigned. */
+UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, const UA_Node **node, UA_Boolean getManaged);
+
+/** @brief Replace an existing node in the nodestore
+
+    With the getManaged flag, the node pointer is replaced with the managed
+    pointer. Otherwise, it is set to UA_NULL. */
+UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, const UA_Node **node, UA_Boolean getManaged);
 
 
 /** @brief Remove a node from the namespace. Always succeeds, even if the node
 /** @brief Remove a node from the namespace. Always succeeds, even if the node
     was not found. */
     was not found. */
@@ -43,8 +54,7 @@ UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid);
 /** @brief Retrieve a node (read-only) from the namespace. Nodes are immutable.
 /** @brief Retrieve a node (read-only) from the namespace. Nodes are immutable.
     They can only be replaced. After the Node is no longer used, the locked
     They can only be replaced. After the Node is no longer used, the locked
     entry needs to be released. */
     entry needs to be released. */
-UA_StatusCode UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid,
-                               const UA_Node **managedNode);
+const UA_Node * UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid);
 
 
 /** @brief Release a managed node. Do never insert a node that isn't stored in a
 /** @brief Release a managed node. Do never insert a node that isn't stored in a
     namespace. */
     namespace. */
@@ -57,6 +67,4 @@ typedef void (*UA_NodeStore_nodeVisitor)(const UA_Node *node);
 /** @brief Iterate over all nodes in a namespace. */
 /** @brief Iterate over all nodes in a namespace. */
 void UA_NodeStore_iterate(const UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor);
 void UA_NodeStore_iterate(const UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor);
 
 
-/// @} /* end of group */
-
 #endif /* UA_NODESTORE_H_ */
 #endif /* UA_NODESTORE_H_ */

+ 12 - 12
src/server/ua_nodestore_concurrent.c

@@ -6,13 +6,13 @@
 #include <urcu/uatomic.h>
 #include <urcu/uatomic.h>
 #include <urcu/rculfhash.h>
 #include <urcu/rculfhash.h>
 
 
-#define ALIVE_BIT (1 << 15) /* Alive bit in the readcount */
+#define ALIVE_BIT (1 << 15) /* Alive bit in the refcount */
 
 
 typedef struct UA_NodeStore_Entry {
 typedef struct UA_NodeStore_Entry {
     struct cds_lfht_node htn;      /* contains next-ptr for urcu-hashmap */
     struct cds_lfht_node htn;      /* contains next-ptr for urcu-hashmap */
     struct rcu_head      rcu_head; /* For call-rcu */
     struct rcu_head      rcu_head; /* For call-rcu */
-    UA_UInt16 readcount;           /* Counts the amount of readers on it [alive-bit, 15 counter-bits] */
-    UA_Node   node;                /* Might be cast from any _bigger_ UA_Node* type. Allocate enough memory! */
+    UA_UInt16 refcount;            /* Counts the amount of readers on it [alive-bit, 15 counter-bits] */
+    const UA_Node node;            /* Might be cast from any _bigger_ UA_Node* type. Allocate enough memory! */
 } UA_NodeStore_Entry;
 } UA_NodeStore_Entry;
 
 
 struct UA_NodeStore {
 struct UA_NodeStore {
@@ -26,7 +26,7 @@ struct UA_NodeStore {
 typedef UA_UInt32 hash_t;
 typedef UA_UInt32 hash_t;
 
 
 /* Based on Murmur-Hash 3 by Austin Appleby (public domain, freely usable) */
 /* Based on Murmur-Hash 3 by Austin Appleby (public domain, freely usable) */
-static INLINE hash_t hash_array(const UA_Byte *data, UA_UInt32 len, UA_UInt32 seed) {
+static hash_t hash_array(const UA_Byte *data, UA_UInt32 len, UA_UInt32 seed) {
     static const uint32_t c1 = 0xcc9e2d51;
     static const uint32_t c1 = 0xcc9e2d51;
     static const uint32_t c2 = 0x1b873593;
     static const uint32_t c2 = 0x1b873593;
     static const uint32_t r1 = 15;
     static const uint32_t r1 = 15;
@@ -76,7 +76,7 @@ static INLINE hash_t hash_array(const UA_Byte *data, UA_UInt32 len, UA_UInt32 se
     return hash;
     return hash;
 }
 }
 
 
-static INLINE hash_t hash(const UA_NodeId *n) {
+static hash_t hash(const UA_NodeId *n) {
     switch(n->identifierType) {
     switch(n->identifierType) {
     case UA_NODEIDTYPE_NUMERIC:
     case UA_NODEIDTYPE_NUMERIC:
         /*  Knuth's multiplicative hashing */
         /*  Knuth's multiplicative hashing */
@@ -151,11 +151,11 @@ static int compare(struct cds_lfht_node *htn, const void *orig) {
 
 
 /* The entry was removed from the hashtable. No more readers can get it. Since
 /* The entry was removed from the hashtable. No more readers can get it. Since
    all readers using the node for a longer time (outside the rcu critical
    all readers using the node for a longer time (outside the rcu critical
-   section) increased the readcount, we only need to wait for the readcount
+   section) increased the refcount, we only need to wait for the refcount
    to reach zero. */
    to reach zero. */
 static void markDead(struct rcu_head *head) {
 static void markDead(struct rcu_head *head) {
     UA_NodeStore_Entry *entry = caa_container_of(head, UA_NodeStore_Entry, rcu_head);
     UA_NodeStore_Entry *entry = caa_container_of(head, UA_NodeStore_Entry, rcu_head);
-    if(uatomic_sub_return(&entry->readcount, ALIVE_BIT) > 0)
+    if(uatomic_and(&entry->refcount, ~ALIVE_BIT) > 0)
         return;
         return;
 
 
     node_deleteMembers(&entry->node);
     node_deleteMembers(&entry->node);
@@ -166,7 +166,7 @@ static void markDead(struct rcu_head *head) {
 /* Free the entry if it is dead and nobody uses it anymore */
 /* Free the entry if it is dead and nobody uses it anymore */
 void UA_NodeStore_release(const UA_Node *managed) {
 void UA_NodeStore_release(const UA_Node *managed) {
     UA_NodeStore_Entry *entry = caa_container_of(managed, UA_NodeStore_Entry, node); // pointer to the first entry
     UA_NodeStore_Entry *entry = caa_container_of(managed, UA_NodeStore_Entry, node); // pointer to the first entry
-    if(uatomic_sub_return(&entry->readcount, 1) > 0)
+    if(uatomic_sub_return(&entry->refcount, 1) > 0)
         return;
         return;
 
 
     node_deleteMembers(managed);
     node_deleteMembers(managed);
@@ -257,9 +257,9 @@ UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node **node, UA_Byte flag
     memcpy(&entry->node, *node, nodesize);
     memcpy(&entry->node, *node, nodesize);
 
 
     cds_lfht_node_init(&entry->htn);
     cds_lfht_node_init(&entry->htn);
-    entry->readcount = ALIVE_BIT;
+    entry->refcount = ALIVE_BIT;
     if(flags & UA_NODESTORE_INSERT_GETMANAGED)
     if(flags & UA_NODESTORE_INSERT_GETMANAGED)
-        entry->readcount++;
+        entry->refcount++;
 
 
     hash_t nhash = hash(&(*node)->nodeId);
     hash_t nhash = hash(&(*node)->nodeId);
     struct cds_lfht_node *result;
     struct cds_lfht_node *result;
@@ -328,7 +328,7 @@ UA_StatusCode UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid,
     }
     }
 
 
     /* This is done within a read-lock. The node will not be marked dead within a read-lock. */
     /* This is done within a read-lock. The node will not be marked dead within a read-lock. */
-    uatomic_inc(&found_entry->readcount);
+    uatomic_inc(&found_entry->refcount);
     rcu_read_unlock();
     rcu_read_unlock();
 
 
     *managedNode = &found_entry->node;
     *managedNode = &found_entry->node;
@@ -343,7 +343,7 @@ void UA_NodeStore_iterate(const UA_NodeStore *ns, UA_NodeStore_nodeVisitor visit
     cds_lfht_first(ht, &iter);
     cds_lfht_first(ht, &iter);
     while(iter.node != UA_NULL) {
     while(iter.node != UA_NULL) {
         UA_NodeStore_Entry *found_entry = (UA_NodeStore_Entry *)cds_lfht_iter_get_node(&iter);
         UA_NodeStore_Entry *found_entry = (UA_NodeStore_Entry *)cds_lfht_iter_get_node(&iter);
-        uatomic_inc(&found_entry->readcount);
+        uatomic_inc(&found_entry->refcount);
         const UA_Node      *node = &found_entry->node;
         const UA_Node      *node = &found_entry->node;
         rcu_read_unlock();
         rcu_read_unlock();
         visitor(node);
         visitor(node);

+ 3 - 0
src/server/ua_securechannel_manager.c

@@ -45,6 +45,7 @@ UA_StatusCode UA_SecureChannelManager_open(UA_SecureChannelManager           *cm
     UA_SecureChannel_init(&entry->channel);
     UA_SecureChannel_init(&entry->channel);
 
 
     entry->channel.connection = conn;
     entry->channel.connection = conn;
+    conn->channel = &entry->channel;
     entry->channel.securityToken.channelId       = cm->lastChannelId++;
     entry->channel.securityToken.channelId       = cm->lastChannelId++;
     entry->channel.securityToken.tokenId         = cm->lastTokenId++;
     entry->channel.securityToken.tokenId         = cm->lastTokenId++;
     entry->channel.securityToken.createdAt       = UA_DateTime_now();
     entry->channel.securityToken.createdAt       = UA_DateTime_now();
@@ -96,6 +97,8 @@ UA_StatusCode UA_SecureChannelManager_renew(UA_SecureChannelManager           *c
     channel->securityToken.revisedLifetime = request->requestedLifetime > cm->maxChannelLifetime ?
     channel->securityToken.revisedLifetime = request->requestedLifetime > cm->maxChannelLifetime ?
                                              cm->maxChannelLifetime : request->requestedLifetime;
                                              cm->maxChannelLifetime : request->requestedLifetime;
 
 
+    if(channel->serverNonce.data != UA_NULL)
+        UA_ByteString_deleteMembers(&channel->serverNonce);
     UA_SecureChannel_generateNonce(&channel->serverNonce);
     UA_SecureChannel_generateNonce(&channel->serverNonce);
     UA_ByteString_copy(&channel->serverNonce, &response->serverNonce);
     UA_ByteString_copy(&channel->serverNonce, &response->serverNonce);
     UA_ChannelSecurityToken_copy(&channel->securityToken, &response->securityToken);
     UA_ChannelSecurityToken_copy(&channel->securityToken, &response->securityToken);

+ 257 - 353
src/server/ua_server.c

@@ -6,6 +6,25 @@
 #include "ua_util.h"
 #include "ua_util.h"
 #include "ua_services.h"
 #include "ua_services.h"
 
 
+/**********************/
+/* Namespace Handling */
+/**********************/
+
+void UA_ExternalNamespace_init(UA_ExternalNamespace *ens) {
+	ens->index = 0;
+    memset(&ens->externalNodeStore, 0, sizeof(UA_ExternalNamespace));
+	UA_String_init(&ens->url);
+}
+
+void UA_ExternalNamespace_deleteMembers(UA_ExternalNamespace *ens) {
+	UA_String_deleteMembers(&ens->url);
+    ens->externalNodeStore.delete(ens->externalNodeStore.ensHandle);
+}
+
+/**********/
+/* Server */
+/**********/
+
 void UA_Server_delete(UA_Server *server) {
 void UA_Server_delete(UA_Server *server) {
     UA_ApplicationDescription_deleteMembers(&server->description);
     UA_ApplicationDescription_deleteMembers(&server->description);
     UA_SecureChannelManager_deleteMembers(&server->secureChannelManager);
     UA_SecureChannelManager_deleteMembers(&server->secureChannelManager);
@@ -19,7 +38,7 @@ void UA_Server_delete(UA_Server *server) {
 UA_Server * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertificate) {
 UA_Server * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertificate) {
     UA_Server *server = UA_alloc(sizeof(UA_Server));
     UA_Server *server = UA_alloc(sizeof(UA_Server));
     if(!server)
     if(!server)
-        return server;
+        return UA_NULL;
     
     
     // mockup application description
     // mockup application description
     UA_ApplicationDescription_init(&server->description);
     UA_ApplicationDescription_init(&server->description);
@@ -27,6 +46,8 @@ UA_Server * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertifica
     UA_String_copycstring("http://open62541.info/applications/4711", &server->description.applicationUri);
     UA_String_copycstring("http://open62541.info/applications/4711", &server->description.applicationUri);
     UA_LocalizedText_copycstring("The open62541 application", &server->description.applicationName);
     UA_LocalizedText_copycstring("The open62541 application", &server->description.applicationName);
     server->description.applicationType = UA_APPLICATIONTYPE_SERVER;
     server->description.applicationType = UA_APPLICATIONTYPE_SERVER;
+    server->externalNamespacesSize = 0;
+    server->externalNamespaces = UA_NULL;
 
 
     UA_ByteString_init(&server->serverCertificate);
     UA_ByteString_init(&server->serverCertificate);
     if(serverCertificate)
     if(serverCertificate)
@@ -34,7 +55,7 @@ UA_Server * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertifica
 
 
     // mockup endpoint description
     // mockup endpoint description
     server->endpointDescriptionsSize = 1;
     server->endpointDescriptionsSize = 1;
-    UA_EndpointDescription *endpoint = UA_alloc(sizeof(UA_EndpointDescription)); // todo: check return code
+    UA_EndpointDescription *endpoint = UA_EndpointDescription_new(); // todo: check return code
 
 
     endpoint->securityMode = UA_MESSAGESECURITYMODE_NONE;
     endpoint->securityMode = UA_MESSAGESECURITYMODE_NONE;
     UA_String_copycstring("http://opcfoundation.org/UA/SecurityPolicy#None", &endpoint->securityPolicyUri);
     UA_String_copycstring("http://opcfoundation.org/UA/SecurityPolicy#None", &endpoint->securityPolicyUri);
@@ -66,429 +87,342 @@ UA_Server * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertifica
 #define STARTSESSIONID 1
 #define STARTSESSIONID 1
     UA_SessionManager_init(&server->sessionManager, MAXSESSIONCOUNT, SESSIONLIFETIME, STARTSESSIONID);
     UA_SessionManager_init(&server->sessionManager, MAXSESSIONCOUNT, SESSIONLIFETIME, STARTSESSIONID);
 
 
-    UA_NodeStore_new(&server->nodestore);
+    server->nodestore = UA_NodeStore_new();
+
+#define ADDREFERENCE(NODEID, REFTYPE_NODEID, TARGET_EXPNODEID) do { \
+        UA_AddReferencesItem item;                                      \
+        UA_AddReferencesItem_init(&item);                               \
+        item.sourceNodeId = NODEID;                                     \
+        item.referenceTypeId = REFTYPE_NODEID;                          \
+        item.isForward = UA_TRUE;                                       \
+        item.targetNodeId = TARGET_EXPNODEID;                           \
+        UA_Server_addReference(server, &item);                          \
+    } while(0)
+
+#define COPYNAMES(TARGET, NAME) do {                                \
+        UA_QualifiedName_copycstring(NAME, &TARGET->browseName);    \
+        UA_LocalizedText_copycstring(NAME, &TARGET->displayName);   \
+        UA_LocalizedText_copycstring(NAME, &TARGET->description);   \
+    } while(0)
 
 
     /**************/
     /**************/
     /* References */
     /* References */
     /**************/
     /**************/
-
-    // ReferenceType Ids
-    UA_ExpandedNodeId RefTypeId_References; NS0EXPANDEDNODEID(RefTypeId_References, 31);
-    UA_ExpandedNodeId RefTypeId_NonHierarchicalReferences; NS0EXPANDEDNODEID(RefTypeId_NonHierarchicalReferences, 32);
-    UA_ExpandedNodeId RefTypeId_HierarchicalReferences; NS0EXPANDEDNODEID(RefTypeId_HierarchicalReferences, 33);
-    UA_ExpandedNodeId RefTypeId_HasChild; NS0EXPANDEDNODEID(RefTypeId_HasChild, 34);
-    UA_ExpandedNodeId RefTypeId_Organizes; NS0EXPANDEDNODEID(RefTypeId_Organizes, 35);
-    UA_ExpandedNodeId RefTypeId_HasEventSource; NS0EXPANDEDNODEID(RefTypeId_HasEventSource, 36);
-    UA_ExpandedNodeId RefTypeId_HasModellingRule; NS0EXPANDEDNODEID(RefTypeId_HasModellingRule, 37);
-    UA_ExpandedNodeId RefTypeId_HasEncoding; NS0EXPANDEDNODEID(RefTypeId_HasEncoding, 38);
-    UA_ExpandedNodeId RefTypeId_HasDescription; NS0EXPANDEDNODEID(RefTypeId_HasDescription, 39);
-    UA_ExpandedNodeId RefTypeId_HasTypeDefinition; NS0EXPANDEDNODEID(RefTypeId_HasTypeDefinition, 40);
-    UA_ExpandedNodeId RefTypeId_GeneratesEvent; NS0EXPANDEDNODEID(RefTypeId_GeneratesEvent, 41);
-    UA_ExpandedNodeId RefTypeId_Aggregates; NS0EXPANDEDNODEID(RefTypeId_Aggregates, 44);
-    UA_ExpandedNodeId RefTypeId_HasSubtype; NS0EXPANDEDNODEID(RefTypeId_HasSubtype, 45);
-    UA_ExpandedNodeId RefTypeId_HasProperty; NS0EXPANDEDNODEID(RefTypeId_HasProperty, 46);
-    UA_ExpandedNodeId RefTypeId_HasComponent; NS0EXPANDEDNODEID(RefTypeId_HasComponent, 47);
-    UA_ExpandedNodeId RefTypeId_HasNotifier; NS0EXPANDEDNODEID(RefTypeId_HasNotifier, 48);
-    UA_ExpandedNodeId RefTypeId_HasOrderedComponent; NS0EXPANDEDNODEID(RefTypeId_HasOrderedComponent, 49);
-    UA_ExpandedNodeId RefTypeId_HasModelParent; NS0EXPANDEDNODEID(RefTypeId_HasModelParent, 50);
-    UA_ExpandedNodeId RefTypeId_FromState; NS0EXPANDEDNODEID(RefTypeId_FromState, 51);
-    UA_ExpandedNodeId RefTypeId_ToState; NS0EXPANDEDNODEID(RefTypeId_ToState, 52);
-    UA_ExpandedNodeId RefTypeId_HasCause; NS0EXPANDEDNODEID(RefTypeId_HasCause, 53);
-    UA_ExpandedNodeId RefTypeId_HasEffect; NS0EXPANDEDNODEID(RefTypeId_HasEffect, 54);
-    UA_ExpandedNodeId RefTypeId_HasHistoricalConfiguration; NS0EXPANDEDNODEID(RefTypeId_HasHistoricalConfiguration, 56);
-
-#define ADDREFERENCE(NODE, REFTYPE, INVERSE, TARGET_NODEID) do { \
-    static struct UA_ReferenceNode NODE##REFTYPE##TARGET_NODEID;    \
-    UA_ReferenceNode_init(&NODE##REFTYPE##TARGET_NODEID);       \
-    NODE##REFTYPE##TARGET_NODEID.referenceTypeId = REFTYPE.nodeId;     \
-    NODE##REFTYPE##TARGET_NODEID.isInverse       = INVERSE; \
-    NODE##REFTYPE##TARGET_NODEID.targetId = TARGET_NODEID; \
-    AddReference(server->nodestore, (UA_Node *)NODE, &NODE##REFTYPE##TARGET_NODEID); \
-    } while(0)
     
     
+    /* bootstrap by manually inserting "references" and "hassubtype" */
     UA_ReferenceTypeNode *references = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *references = UA_ReferenceTypeNode_new();
-    references->nodeId    = RefTypeId_References.nodeId;
-    references->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("References", &references->browseName);
-    UA_LocalizedText_copycstring("References", &references->displayName);
-    UA_LocalizedText_copycstring("References", &references->description);
+    COPYNAMES(references, "References");
+    references->nodeId     = UA_NODEIDS[UA_REFERENCES];
+    references->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     references->isAbstract = UA_TRUE;
     references->isAbstract = UA_TRUE;
     references->symmetric  = UA_TRUE;
     references->symmetric  = UA_TRUE;
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&references, UA_NODESTORE_INSERT_UNIQUE);
+    // this node has no parent??
+    UA_NodeStore_insert(server->nodestore, (const UA_Node**)&references, UA_FALSE);
 
 
+    UA_ReferenceTypeNode *hassubtype = UA_ReferenceTypeNode_new();
+    COPYNAMES(hassubtype, "HasSubtype");
+    UA_LocalizedText_copycstring("SubtypeOf", &hassubtype->inverseName);
+    hassubtype->nodeId     = UA_NODEIDS[UA_HASSUBTYPE];
+    hassubtype->nodeClass  = UA_NODECLASS_REFERENCETYPE;
+    hassubtype->isAbstract = UA_FALSE;
+    hassubtype->symmetric  = UA_FALSE;
+    UA_NodeStore_insert(server->nodestore, (const UA_Node**)&hassubtype, UA_FALSE);
+
+    /* continue adding reference types with normal "addnode" */
     UA_ReferenceTypeNode *hierarchicalreferences = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *hierarchicalreferences = UA_ReferenceTypeNode_new();
-    hierarchicalreferences->nodeId    = RefTypeId_HierarchicalReferences.nodeId;
-    hierarchicalreferences->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("HierarchicalReferences", &hierarchicalreferences->browseName);
-    UA_LocalizedText_copycstring("HierarchicalReferences", &hierarchicalreferences->displayName);
-    UA_LocalizedText_copycstring("HierarchicalReferences", &hierarchicalreferences->description);
+    COPYNAMES(hierarchicalreferences, "Hierarchicalreferences");
+    hierarchicalreferences->nodeId     = UA_NODEIDS[UA_HIERARCHICALREFERENCES];
+    hierarchicalreferences->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     hierarchicalreferences->isAbstract = UA_TRUE;
     hierarchicalreferences->isAbstract = UA_TRUE;
     hierarchicalreferences->symmetric  = UA_FALSE;
     hierarchicalreferences->symmetric  = UA_FALSE;
-    ADDREFERENCE(hierarchicalreferences, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_References);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&hierarchicalreferences, UA_NODESTORE_INSERT_UNIQUE);
+    UA_Server_addNode(server, (const UA_Node**)&hierarchicalreferences,
+                      &UA_EXPANDEDNODEIDS[UA_REFERENCES], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
     UA_ReferenceTypeNode *nonhierarchicalreferences = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *nonhierarchicalreferences = UA_ReferenceTypeNode_new();
-    nonhierarchicalreferences->nodeId    = RefTypeId_NonHierarchicalReferences.nodeId;
-    nonhierarchicalreferences->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("NonHierarchicalReferences", &nonhierarchicalreferences->browseName);
-    UA_LocalizedText_copycstring("NonHierarchicalReferences", &nonhierarchicalreferences->displayName);
-    UA_LocalizedText_copycstring("NonHierarchicalReferences", &nonhierarchicalreferences->description);
+    COPYNAMES(nonhierarchicalreferences, "NonHierarchicalReferences");
+    nonhierarchicalreferences->nodeId     = UA_NODEIDS[UA_NONHIERARCHICALREFERENCES];
+    nonhierarchicalreferences->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     nonhierarchicalreferences->isAbstract = UA_TRUE;
     nonhierarchicalreferences->isAbstract = UA_TRUE;
     nonhierarchicalreferences->symmetric  = UA_FALSE;
     nonhierarchicalreferences->symmetric  = UA_FALSE;
-    ADDREFERENCE(nonhierarchicalreferences, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_References);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&nonhierarchicalreferences, UA_NODESTORE_INSERT_UNIQUE);
+    UA_Server_addNode(server, (const UA_Node **)&nonhierarchicalreferences,
+                      &UA_EXPANDEDNODEIDS[UA_REFERENCES], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
     UA_ReferenceTypeNode *haschild = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *haschild = UA_ReferenceTypeNode_new();
-    haschild->nodeId    = RefTypeId_HasChild.nodeId;
-    haschild->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("HasChild", &haschild->browseName);
-    UA_LocalizedText_copycstring("HasChild", &haschild->displayName);
-    UA_LocalizedText_copycstring("HasChild", &haschild->description);
+    COPYNAMES(haschild, "HasChild");
+    haschild->nodeId     = UA_NODEIDS[UA_HASCHILD];
+    haschild->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     haschild->isAbstract = UA_TRUE;
     haschild->isAbstract = UA_TRUE;
     haschild->symmetric  = UA_FALSE;
     haschild->symmetric  = UA_FALSE;
-    ADDREFERENCE(haschild, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_HierarchicalReferences);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&haschild, UA_NODESTORE_INSERT_UNIQUE);
+    UA_Server_addNode(server, (const UA_Node **)&haschild,
+                      &UA_EXPANDEDNODEIDS[UA_HIERARCHICALREFERENCES], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
     UA_ReferenceTypeNode *organizes = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *organizes = UA_ReferenceTypeNode_new();
-    organizes->nodeId    = RefTypeId_Organizes.nodeId;
-    organizes->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("Organizes", &organizes->browseName);
-    UA_LocalizedText_copycstring("Organizes", &organizes->displayName);
-    UA_LocalizedText_copycstring("Organizes", &organizes->description);
+    COPYNAMES(organizes, "Organizes");
+    UA_LocalizedText_copycstring("OrganizedBy", &organizes->inverseName);
+    organizes->nodeId     = UA_NODEIDS[UA_ORGANIZES];
+    organizes->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     organizes->isAbstract = UA_FALSE;
     organizes->isAbstract = UA_FALSE;
     organizes->symmetric  = UA_FALSE;
     organizes->symmetric  = UA_FALSE;
-    UA_LocalizedText_copycstring("OrganizedBy", &organizes->inverseName);
-    ADDREFERENCE(organizes, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_HierarchicalReferences);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&organizes, UA_NODESTORE_INSERT_UNIQUE);
+    UA_Server_addNode(server, (const UA_Node **)&organizes,
+                      &UA_EXPANDEDNODEIDS[UA_HIERARCHICALREFERENCES], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
     UA_ReferenceTypeNode *haseventsource = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *haseventsource = UA_ReferenceTypeNode_new();
-    haseventsource->nodeId    = RefTypeId_HasEventSource.nodeId;
-    haseventsource->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("HasEventSource", &haseventsource->browseName);
-    UA_LocalizedText_copycstring("HasEventSource", &haseventsource->displayName);
-    UA_LocalizedText_copycstring("HasEventSource", &haseventsource->description);
+    COPYNAMES(haseventsource, "HasEventSource");
+    UA_LocalizedText_copycstring("EventSourceOf", &haseventsource->inverseName);
+    haseventsource->nodeId     = UA_NODEIDS[UA_HASEVENTSOURCE];
+    haseventsource->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     haseventsource->isAbstract = UA_FALSE;
     haseventsource->isAbstract = UA_FALSE;
     haseventsource->symmetric  = UA_FALSE;
     haseventsource->symmetric  = UA_FALSE;
-    UA_LocalizedText_copycstring("EventSourceOf", &haseventsource->inverseName);
-    ADDREFERENCE(haseventsource, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_HierarchicalReferences);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&haseventsource, UA_NODESTORE_INSERT_UNIQUE);
+    UA_Server_addNode(server, (const UA_Node **)&haseventsource,
+                      &UA_EXPANDEDNODEIDS[UA_HIERARCHICALREFERENCES], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
     UA_ReferenceTypeNode *hasmodellingrule = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *hasmodellingrule = UA_ReferenceTypeNode_new();
-    hasmodellingrule->nodeId    = RefTypeId_HasModellingRule.nodeId;
-    hasmodellingrule->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("HasModellingRule", &hasmodellingrule->browseName);
-    UA_LocalizedText_copycstring("HasModellingRule", &hasmodellingrule->displayName);
-    UA_LocalizedText_copycstring("HasModellingRule", &hasmodellingrule->description);
+    COPYNAMES(hasmodellingrule, "HasModellingRule");
+    UA_LocalizedText_copycstring("ModellingRuleOf", &hasmodellingrule->inverseName);
+    hasmodellingrule->nodeId     = UA_NODEIDS[UA_HASMODELLINGRULE];
+    hasmodellingrule->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     hasmodellingrule->isAbstract = UA_FALSE;
     hasmodellingrule->isAbstract = UA_FALSE;
     hasmodellingrule->symmetric  = UA_FALSE;
     hasmodellingrule->symmetric  = UA_FALSE;
-    UA_LocalizedText_copycstring("ModellingRuleOf", &hasmodellingrule->inverseName);
-    ADDREFERENCE(hasmodellingrule, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_NonHierarchicalReferences);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&hasmodellingrule, UA_NODESTORE_INSERT_UNIQUE);
+    UA_Server_addNode(server, (const UA_Node **)&hasmodellingrule,
+                      &UA_EXPANDEDNODEIDS[UA_NONHIERARCHICALREFERENCES], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
     UA_ReferenceTypeNode *hasencoding = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *hasencoding = UA_ReferenceTypeNode_new();
-    hasencoding->nodeId    = RefTypeId_HasEncoding.nodeId;
-    hasencoding->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("HasEncoding", &hasencoding->browseName);
-    UA_LocalizedText_copycstring("HasEncoding", &hasencoding->displayName);
-    UA_LocalizedText_copycstring("HasEncoding", &hasencoding->description);
+    COPYNAMES(hasencoding, "HasEncoding");
+    UA_LocalizedText_copycstring("EncodingOf", &hasencoding->inverseName);
+    hasencoding->nodeId     = UA_NODEIDS[UA_HASENCODING];
+    hasencoding->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     hasencoding->isAbstract = UA_FALSE;
     hasencoding->isAbstract = UA_FALSE;
     hasencoding->symmetric  = UA_FALSE;
     hasencoding->symmetric  = UA_FALSE;
-    UA_LocalizedText_copycstring("EncodingOf", &hasencoding->inverseName);
-    ADDREFERENCE(hasencoding, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_NonHierarchicalReferences);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&hasencoding, UA_NODESTORE_INSERT_UNIQUE);
+    UA_Server_addNode(server, (const UA_Node **)&hasencoding,
+                      &UA_EXPANDEDNODEIDS[UA_NONHIERARCHICALREFERENCES], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
     UA_ReferenceTypeNode *hasdescription = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *hasdescription = UA_ReferenceTypeNode_new();
-    hasdescription->nodeId    = RefTypeId_HasDescription.nodeId;
-    hasdescription->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("HasDescription", &hasdescription->browseName);
-    UA_LocalizedText_copycstring("HasDescription", &hasdescription->displayName);
-    UA_LocalizedText_copycstring("HasDescription", &hasdescription->description);
+    COPYNAMES(hasdescription, "HasDescription");
+    UA_LocalizedText_copycstring("DescriptionOf", &hasdescription->inverseName);
+    hasdescription->nodeId     = UA_NODEIDS[UA_HASDESCRIPTION];
+    hasdescription->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     hasdescription->isAbstract = UA_FALSE;
     hasdescription->isAbstract = UA_FALSE;
     hasdescription->symmetric  = UA_FALSE;
     hasdescription->symmetric  = UA_FALSE;
-    UA_LocalizedText_copycstring("DescriptionOf", &hasdescription->inverseName);
-    ADDREFERENCE(hasdescription, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_NonHierarchicalReferences);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&hasdescription, UA_NODESTORE_INSERT_UNIQUE);
+    UA_Server_addNode(server, (const UA_Node **)&hasdescription,
+                      &UA_EXPANDEDNODEIDS[UA_NONHIERARCHICALREFERENCES], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
     UA_ReferenceTypeNode *hastypedefinition = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *hastypedefinition = UA_ReferenceTypeNode_new();
-    hastypedefinition->nodeId    = RefTypeId_HasTypeDefinition.nodeId;
-    hastypedefinition->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("HasTypeDefinition", &hastypedefinition->browseName);
-    UA_LocalizedText_copycstring("HasTypeDefinition", &hastypedefinition->displayName);
-    UA_LocalizedText_copycstring("HasTypeDefinition", &hastypedefinition->description);
+    COPYNAMES(hastypedefinition, "HasTypeDefinition");
+    UA_LocalizedText_copycstring("TypeDefinitionOf", &hastypedefinition->inverseName);
+    hastypedefinition->nodeId     = UA_NODEIDS[UA_HASTYPEDEFINITION];
+    hastypedefinition->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     hastypedefinition->isAbstract = UA_FALSE;
     hastypedefinition->isAbstract = UA_FALSE;
     hastypedefinition->symmetric  = UA_FALSE;
     hastypedefinition->symmetric  = UA_FALSE;
-    UA_LocalizedText_copycstring("TypeDefinitionOf", &hastypedefinition->inverseName);
-    ADDREFERENCE(hastypedefinition, RefTypeId_HasSubtype, UA_TRUE,  RefTypeId_NonHierarchicalReferences);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&hastypedefinition, UA_NODESTORE_INSERT_UNIQUE);
+    UA_Server_addNode(server, (const UA_Node **)&hastypedefinition,
+                      &UA_EXPANDEDNODEIDS[UA_NONHIERARCHICALREFERENCES], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
     UA_ReferenceTypeNode *generatesevent = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *generatesevent = UA_ReferenceTypeNode_new();
-    generatesevent->nodeId    = RefTypeId_GeneratesEvent.nodeId;
-    generatesevent->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("GeneratesEvent", &generatesevent->browseName);
-    UA_LocalizedText_copycstring("GeneratesEvent", &generatesevent->displayName);
-    UA_LocalizedText_copycstring("GeneratesEvent", &generatesevent->description);
+    COPYNAMES(generatesevent, "GeneratesEvent");
+    UA_LocalizedText_copycstring("GeneratedBy", &generatesevent->inverseName);
+    generatesevent->nodeId     = UA_NODEIDS[UA_GENERATESEVENT];
+    generatesevent->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     generatesevent->isAbstract = UA_FALSE;
     generatesevent->isAbstract = UA_FALSE;
     generatesevent->symmetric  = UA_FALSE;
     generatesevent->symmetric  = UA_FALSE;
-    UA_LocalizedText_copycstring("GeneratedBy", &generatesevent->inverseName);
-    ADDREFERENCE(generatesevent, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_NonHierarchicalReferences);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&generatesevent, UA_NODESTORE_INSERT_UNIQUE);
+    UA_Server_addNode(server, (const UA_Node **)&generatesevent,
+                      &UA_EXPANDEDNODEIDS[UA_NONHIERARCHICALREFERENCES], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
     UA_ReferenceTypeNode *aggregates = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *aggregates = UA_ReferenceTypeNode_new();
-    aggregates->nodeId    = RefTypeId_Aggregates.nodeId;
-    aggregates->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("Aggregates", &aggregates->browseName);
-    UA_LocalizedText_copycstring("Aggregates", &aggregates->displayName);
-    UA_LocalizedText_copycstring("Aggregates", &aggregates->description);
+    COPYNAMES(aggregates, "Aggregates");
+    // Todo: Is there an inverse name?
+    aggregates->nodeId     = UA_NODEIDS[UA_AGGREGATES];
+    aggregates->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     aggregates->isAbstract = UA_TRUE;
     aggregates->isAbstract = UA_TRUE;
     aggregates->symmetric  = UA_FALSE;
     aggregates->symmetric  = UA_FALSE;
-    ADDREFERENCE(aggregates, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_HasChild);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&aggregates, UA_NODESTORE_INSERT_UNIQUE);
+    UA_Server_addNode(server, (const UA_Node **)&aggregates,
+                      &UA_EXPANDEDNODEIDS[UA_HASCHILD], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
-    UA_ReferenceTypeNode *hassubtype = UA_ReferenceTypeNode_new();
-    hassubtype->nodeId    = RefTypeId_HasSubtype.nodeId;
-    hassubtype->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("HasSubtype", &hassubtype->browseName);
-    UA_LocalizedText_copycstring("HasSubtype", &hassubtype->displayName);
-    UA_LocalizedText_copycstring("HasSubtype", &hassubtype->description);
-    hassubtype->isAbstract = UA_FALSE;
-    hassubtype->symmetric  = UA_FALSE;
-    UA_LocalizedText_copycstring("SubtypeOf", &hassubtype->inverseName);
-    ADDREFERENCE(hassubtype, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_HasChild);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&hassubtype, UA_NODESTORE_INSERT_UNIQUE);
+    // complete bootstrap of hassubtype
+    ADDREFERENCE(UA_NODEIDS[UA_HASCHILD], UA_NODEIDS[UA_HASSUBTYPE],
+                 UA_EXPANDEDNODEIDS[UA_HASSUBTYPE]);
 
 
     UA_ReferenceTypeNode *hasproperty = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *hasproperty = UA_ReferenceTypeNode_new();
-    hasproperty->nodeId    = RefTypeId_HasProperty.nodeId;
-    hasproperty->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("HasProperty", &hasproperty->browseName);
-    UA_LocalizedText_copycstring("HasProperty", &hasproperty->displayName);
-    UA_LocalizedText_copycstring("HasProperty", &hasproperty->description);
+    COPYNAMES(hasproperty, "HasProperty");
+    UA_LocalizedText_copycstring("PropertyOf", &hasproperty->inverseName);
+    hasproperty->nodeId     = UA_NODEIDS[UA_HASPROPERTY];
+    hasproperty->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     hasproperty->isAbstract = UA_FALSE;
     hasproperty->isAbstract = UA_FALSE;
     hasproperty->symmetric  = UA_FALSE;
     hasproperty->symmetric  = UA_FALSE;
-    UA_LocalizedText_copycstring("PropertyOf", &hasproperty->inverseName);
-    ADDREFERENCE(hasproperty, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_Aggregates);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&hasproperty, UA_NODESTORE_INSERT_UNIQUE);
+    UA_Server_addNode(server, (const UA_Node **)&hasproperty,
+                      &UA_EXPANDEDNODEIDS[UA_AGGREGATES], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
     UA_ReferenceTypeNode *hascomponent = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *hascomponent = UA_ReferenceTypeNode_new();
-    hascomponent->nodeId    = RefTypeId_HasComponent.nodeId;
-    hascomponent->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("HasComponent", &hascomponent->browseName);
-    UA_LocalizedText_copycstring("HasComponent", &hascomponent->displayName);
-    UA_LocalizedText_copycstring("HasComponent", &hascomponent->description);
+    COPYNAMES(hascomponent, "HasComponent");
+    UA_LocalizedText_copycstring("ComponentOf", &hascomponent->inverseName);
+    hascomponent->nodeId     = UA_NODEIDS[UA_HASCOMPONENT];
+    hascomponent->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     hascomponent->isAbstract = UA_FALSE;
     hascomponent->isAbstract = UA_FALSE;
     hascomponent->symmetric  = UA_FALSE;
     hascomponent->symmetric  = UA_FALSE;
-    UA_LocalizedText_copycstring("ComponentOf", &hascomponent->inverseName);
-    ADDREFERENCE(hascomponent, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_Aggregates);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&hascomponent, UA_NODESTORE_INSERT_UNIQUE);
+    UA_Server_addNode(server, (const UA_Node **)&hascomponent,
+                      &UA_EXPANDEDNODEIDS[UA_AGGREGATES], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
     UA_ReferenceTypeNode *hasnotifier = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *hasnotifier = UA_ReferenceTypeNode_new();
-    hasnotifier->nodeId    = RefTypeId_HasNotifier.nodeId;
-    hasnotifier->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("HasNotifier", &hasnotifier->browseName);
-    UA_LocalizedText_copycstring("HasNotifier", &hasnotifier->displayName);
-    UA_LocalizedText_copycstring("HasNotifier", &hasnotifier->description);
+    COPYNAMES(hasnotifier, "HasNotifier");
+    UA_LocalizedText_copycstring("NotifierOf", &hasnotifier->inverseName);
+    hasnotifier->nodeId     = UA_NODEIDS[UA_HASNOTIFIER];
+    hasnotifier->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     hasnotifier->isAbstract = UA_FALSE;
     hasnotifier->isAbstract = UA_FALSE;
     hasnotifier->symmetric  = UA_FALSE;
     hasnotifier->symmetric  = UA_FALSE;
-    UA_LocalizedText_copycstring("NotifierOf", &hasnotifier->inverseName);
-    ADDREFERENCE(hasnotifier, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_HasEventSource);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&hasnotifier, UA_NODESTORE_INSERT_UNIQUE);
+    UA_Server_addNode(server, (const UA_Node **)&hasnotifier,
+                      &UA_EXPANDEDNODEIDS[UA_HASEVENTSOURCE], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
     UA_ReferenceTypeNode *hasorderedcomponent = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *hasorderedcomponent = UA_ReferenceTypeNode_new();
-    hasorderedcomponent->nodeId    = RefTypeId_HasOrderedComponent.nodeId;
-    hasorderedcomponent->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("HasOrderedComponent", &hasorderedcomponent->browseName);
-    UA_LocalizedText_copycstring("HasOrderedComponent", &hasorderedcomponent->displayName);
-    UA_LocalizedText_copycstring("HasOrderedComponent", &hasorderedcomponent->description);
+    COPYNAMES(hasorderedcomponent, "HasOrderedComponent");
+    UA_LocalizedText_copycstring("OrderedComponentOf", &hasorderedcomponent->inverseName);
+    hasorderedcomponent->nodeId     = UA_NODEIDS[UA_HASORDEREDCOMPONENT];
+    hasorderedcomponent->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     hasorderedcomponent->isAbstract = UA_FALSE;
     hasorderedcomponent->isAbstract = UA_FALSE;
     hasorderedcomponent->symmetric  = UA_FALSE;
     hasorderedcomponent->symmetric  = UA_FALSE;
-    UA_LocalizedText_copycstring("OrderedComponentOf", &hasorderedcomponent->inverseName);
-    ADDREFERENCE(hasorderedcomponent, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_HasComponent);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&hasorderedcomponent, UA_NODESTORE_INSERT_UNIQUE);
+    UA_Server_addNode(server, (const UA_Node **)&hasorderedcomponent,
+                      &UA_EXPANDEDNODEIDS[UA_HASCOMPONENT], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
     UA_ReferenceTypeNode *hasmodelparent = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *hasmodelparent = UA_ReferenceTypeNode_new();
-    hasmodelparent->nodeId    = RefTypeId_HasModelParent.nodeId;
-    hasmodelparent->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("HasModelParent", &hasmodelparent->browseName);
-    UA_LocalizedText_copycstring("HasModelParent", &hasmodelparent->displayName);
-    UA_LocalizedText_copycstring("HasModelParent", &hasmodelparent->description);
+    COPYNAMES(hasmodelparent, "HasModelParent");
+    UA_LocalizedText_copycstring("ModelParentOf", &hasmodelparent->inverseName);
+    hasmodelparent->nodeId     = UA_NODEIDS[UA_HASMODELPARENT];
+    hasmodelparent->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     hasmodelparent->isAbstract = UA_FALSE;
     hasmodelparent->isAbstract = UA_FALSE;
     hasmodelparent->symmetric  = UA_FALSE;
     hasmodelparent->symmetric  = UA_FALSE;
-    UA_LocalizedText_copycstring("ModelParentOf", &hasmodelparent->inverseName);
-    ADDREFERENCE(hasmodelparent, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_NonHierarchicalReferences);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&hasmodelparent, UA_NODESTORE_INSERT_UNIQUE);
+    UA_Server_addNode(server, (const UA_Node **)&hasmodelparent,
+                      &UA_EXPANDEDNODEIDS[UA_NONHIERARCHICALREFERENCES], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
     UA_ReferenceTypeNode *fromstate = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *fromstate = UA_ReferenceTypeNode_new();
-    fromstate->nodeId    = RefTypeId_FromState.nodeId;
-    fromstate->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("FromState", &fromstate->browseName);
-    UA_LocalizedText_copycstring("FromState", &fromstate->displayName);
-    UA_LocalizedText_copycstring("FromState", &fromstate->description);
+    COPYNAMES(fromstate, "FromState");
+    UA_LocalizedText_copycstring("ToTransition", &fromstate->inverseName);
+    fromstate->nodeId     = UA_NODEIDS[UA_FROMSTATE];
+    fromstate->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     fromstate->isAbstract = UA_FALSE;
     fromstate->isAbstract = UA_FALSE;
     fromstate->symmetric  = UA_FALSE;
     fromstate->symmetric  = UA_FALSE;
-    UA_LocalizedText_copycstring("ToTransition", &fromstate->inverseName);
-    ADDREFERENCE(fromstate, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_NonHierarchicalReferences);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&fromstate, UA_NODESTORE_INSERT_UNIQUE);
+    UA_Server_addNode(server, (const UA_Node **)&fromstate,
+                      &UA_EXPANDEDNODEIDS[UA_NONHIERARCHICALREFERENCES], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
     UA_ReferenceTypeNode *tostate = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *tostate = UA_ReferenceTypeNode_new();
-    tostate->nodeId    = RefTypeId_ToState.nodeId;
-    tostate->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("ToState", &tostate->browseName);
-    UA_LocalizedText_copycstring("ToState", &tostate->displayName);
-    UA_LocalizedText_copycstring("ToState", &tostate->description);
+    COPYNAMES(tostate, "ToState");
+    UA_LocalizedText_copycstring("FromTransition", &tostate->inverseName);
+    tostate->nodeId     = UA_NODEIDS[UA_TOSTATE];
+    tostate->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     tostate->isAbstract = UA_FALSE;
     tostate->isAbstract = UA_FALSE;
     tostate->symmetric  = UA_FALSE;
     tostate->symmetric  = UA_FALSE;
-    UA_LocalizedText_copycstring("FromTransition", &tostate->inverseName);
-    ADDREFERENCE(tostate, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_NonHierarchicalReferences);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&tostate, UA_NODESTORE_INSERT_UNIQUE);
+    UA_Server_addNode(server, (const UA_Node **)&tostate,
+                      &UA_EXPANDEDNODEIDS[UA_NONHIERARCHICALREFERENCES], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
     UA_ReferenceTypeNode *hascause = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *hascause = UA_ReferenceTypeNode_new();
-    hascause->nodeId    = RefTypeId_HasCause.nodeId;
-    hascause->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("HasCause", &hascause->browseName);
-    UA_LocalizedText_copycstring("HasCause", &hascause->displayName);
-    UA_LocalizedText_copycstring("HasCause", &hascause->description);
+    COPYNAMES(hascause, "HasCause");
+    UA_LocalizedText_copycstring("MayBeCausedBy", &hascause->inverseName);
+    hascause->nodeId     = UA_NODEIDS[UA_HASCAUSE];
+    hascause->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     hascause->isAbstract = UA_FALSE;
     hascause->isAbstract = UA_FALSE;
     hascause->symmetric  = UA_FALSE;
     hascause->symmetric  = UA_FALSE;
-    UA_LocalizedText_copycstring("MayBeCausedBy", &hascause->inverseName);
-    ADDREFERENCE(hascause, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_NonHierarchicalReferences);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&hascause, UA_NODESTORE_INSERT_UNIQUE);
+    UA_Server_addNode(server, (const UA_Node **)&hascause,
+                      &UA_EXPANDEDNODEIDS[UA_NONHIERARCHICALREFERENCES], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
     UA_ReferenceTypeNode *haseffect = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *haseffect = UA_ReferenceTypeNode_new();
-    haseffect->nodeId    = RefTypeId_HasEffect.nodeId;
-    haseffect->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("HasEffect", &haseffect->browseName);
-    UA_LocalizedText_copycstring("HasEffect", &haseffect->displayName);
-    UA_LocalizedText_copycstring("HasEffect", &haseffect->description);
+    COPYNAMES(haseffect, "HasEffect");
+    UA_LocalizedText_copycstring("MayBeEffectedBy", &haseffect->inverseName);
+    haseffect->nodeId     = UA_NODEIDS[UA_HASEFFECT];
+    haseffect->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     haseffect->isAbstract = UA_FALSE;
     haseffect->isAbstract = UA_FALSE;
     haseffect->symmetric  = UA_FALSE;
     haseffect->symmetric  = UA_FALSE;
-    UA_LocalizedText_copycstring("MayBeEffectedBy", &haseffect->inverseName);
-    ADDREFERENCE(haseffect, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_NonHierarchicalReferences);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&haseffect, UA_NODESTORE_INSERT_UNIQUE);
+    UA_Server_addNode(server, (const UA_Node **)&haseffect,
+                      &UA_EXPANDEDNODEIDS[UA_NONHIERARCHICALREFERENCES], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
     UA_ReferenceTypeNode *hashistoricalconfiguration = UA_ReferenceTypeNode_new();
     UA_ReferenceTypeNode *hashistoricalconfiguration = UA_ReferenceTypeNode_new();
-    hashistoricalconfiguration->nodeId    = RefTypeId_HasHistoricalConfiguration.nodeId;
-    hashistoricalconfiguration->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    UA_QualifiedName_copycstring("HasHistoricalConfiguration", &hashistoricalconfiguration->browseName);
-    UA_LocalizedText_copycstring("HasHistoricalConfiguration", &hashistoricalconfiguration->displayName);
-    UA_LocalizedText_copycstring("HasHistoricalConfiguration", &hashistoricalconfiguration->description);
+    COPYNAMES(hashistoricalconfiguration, "HasHistoricalConfiguration");
+    UA_LocalizedText_copycstring("HistoricalConfigurationOf", &hashistoricalconfiguration->inverseName);
+    hashistoricalconfiguration->nodeId     = UA_NODEIDS[UA_HASHISTORICALCONFIGURATION];
+    hashistoricalconfiguration->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     hashistoricalconfiguration->isAbstract = UA_FALSE;
     hashistoricalconfiguration->isAbstract = UA_FALSE;
     hashistoricalconfiguration->symmetric  = UA_FALSE;
     hashistoricalconfiguration->symmetric  = UA_FALSE;
-    UA_LocalizedText_copycstring("HistoricalConfigurationOf", &hashistoricalconfiguration->inverseName);
-    ADDREFERENCE(hashistoricalconfiguration, RefTypeId_HasSubtype, UA_TRUE, RefTypeId_Aggregates);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&hashistoricalconfiguration, UA_NODESTORE_INSERT_UNIQUE);
-
+    UA_Server_addNode(server, (const UA_Node **)&hashistoricalconfiguration,
+                      &UA_EXPANDEDNODEIDS[UA_AGGREGATES], &UA_NODEIDS[UA_HASSUBTYPE]);
 
 
-    // ObjectTypes (Ids only)
-    UA_ExpandedNodeId ObjTypeId_FolderType; NS0EXPANDEDNODEID(ObjTypeId_FolderType, 61);
-
-    // Objects (Ids only)
-    UA_ExpandedNodeId ObjId_ObjectsFolder; NS0EXPANDEDNODEID(ObjId_ObjectsFolder, 85);
-    UA_ExpandedNodeId ObjId_TypesFolder; NS0EXPANDEDNODEID(ObjId_TypesFolder, 86);
-    UA_ExpandedNodeId ObjId_ViewsFolder; NS0EXPANDEDNODEID(ObjId_ViewsFolder, 87);
-    UA_ExpandedNodeId ObjId_Server; NS0EXPANDEDNODEID(ObjId_Server, 2253);
-    UA_ExpandedNodeId ObjId_ServerArray; NS0EXPANDEDNODEID(ObjId_ServerArray, 2254);
-    UA_ExpandedNodeId ObjId_NamespaceArray; NS0EXPANDEDNODEID(ObjId_NamespaceArray, 2255);
-    UA_ExpandedNodeId ObjId_ServerStatus; NS0EXPANDEDNODEID(ObjId_ServerStatus, 2256);
-    UA_ExpandedNodeId ObjId_ServerCapabilities; NS0EXPANDEDNODEID(ObjId_ServerCapabilities, 2268);
-    UA_ExpandedNodeId ObjId_State; NS0EXPANDEDNODEID(ObjId_State, 2259);
-
-    // FolderType
+    /***********/
+    /* Objects */
+    /***********/
+    
     UA_ObjectNode *folderType = UA_ObjectNode_new();
     UA_ObjectNode *folderType = UA_ObjectNode_new();
-    folderType->nodeId    = NS0NODEID(61);
-    folderType->nodeClass = UA_NODECLASS_OBJECTTYPE; // I should not have to set this manually
-    UA_QualifiedName_copycstring("FolderType", &folderType->browseName);
-    UA_LocalizedText_copycstring("FolderType", &folderType->displayName);
-    UA_LocalizedText_copycstring("FolderType", &folderType->description);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&folderType, UA_NODESTORE_INSERT_UNIQUE);
-
-    // Root
+    folderType->nodeId    = UA_NODEIDS[UA_FOLDERTYPE];
+    folderType->nodeClass = UA_NODECLASS_OBJECTTYPE;
+    COPYNAMES(folderType, "FolderType");
+    UA_NodeStore_insert(server->nodestore, (const UA_Node**)&folderType, UA_FALSE);
+
     UA_ObjectNode *root = UA_ObjectNode_new();
     UA_ObjectNode *root = UA_ObjectNode_new();
-    root->nodeId    = NS0NODEID(84);
-    root->nodeClass = UA_NODECLASS_OBJECT; // I should not have to set this manually
-    UA_QualifiedName_copycstring("Root", &root->browseName);
-    UA_LocalizedText_copycstring("Root", &root->displayName);
-    UA_LocalizedText_copycstring("Root", &root->description);
-    ADDREFERENCE(root, RefTypeId_HasTypeDefinition, UA_FALSE, ObjTypeId_FolderType);
-    ADDREFERENCE(root, RefTypeId_Organizes, UA_FALSE, ObjId_ObjectsFolder);
-    ADDREFERENCE(root, RefTypeId_Organizes, UA_FALSE, ObjId_TypesFolder);
-    ADDREFERENCE(root, RefTypeId_Organizes, UA_FALSE, ObjId_ViewsFolder);
-    /* root becomes a managed node. we need to release it at the end.*/
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&root, UA_NODESTORE_INSERT_UNIQUE | UA_NODESTORE_INSERT_GETMANAGED);
-
-    // Objects
+    COPYNAMES(root, "Root");
+    root->nodeId    = UA_NODEIDS[UA_ROOTFOLDER];
+    root->nodeClass = UA_NODECLASS_OBJECT;
+    UA_NodeStore_insert(server->nodestore, (const UA_Node**)&root, UA_FALSE);
+    ADDREFERENCE(UA_NODEIDS[UA_ROOTFOLDER], UA_NODEIDS[UA_HASTYPEDEFINITION],
+                 UA_EXPANDEDNODEIDS[UA_FOLDERTYPE]);
+    ADDREFERENCE(UA_NODEIDS[UA_ROOTFOLDER], UA_NODEIDS[UA_ORGANIZES],
+                 UA_EXPANDEDNODEIDS[UA_OBJECTSFOLDER]);
+    ADDREFERENCE(UA_NODEIDS[UA_ROOTFOLDER], UA_NODEIDS[UA_ORGANIZES],
+                 UA_EXPANDEDNODEIDS[UA_TYPESFOLDER]);
+    ADDREFERENCE(UA_NODEIDS[UA_ROOTFOLDER], UA_NODEIDS[UA_ORGANIZES],
+                 UA_EXPANDEDNODEIDS[UA_VIEWSFOLDER]);
+
     UA_ObjectNode *objects = UA_ObjectNode_new();
     UA_ObjectNode *objects = UA_ObjectNode_new();
-    objects->nodeId    = ObjId_ObjectsFolder.nodeId;
+    COPYNAMES(objects, "Objects");
+    objects->nodeId    = UA_NODEIDS[UA_OBJECTSFOLDER];
     objects->nodeClass = UA_NODECLASS_OBJECT;
     objects->nodeClass = UA_NODECLASS_OBJECT;
-    UA_QualifiedName_copycstring("Objects", &objects->browseName);
-    UA_LocalizedText_copycstring("Objects", &objects->displayName);
-    UA_LocalizedText_copycstring("Objects", &objects->description);
-    ADDREFERENCE(objects, RefTypeId_HasTypeDefinition, UA_FALSE, ObjTypeId_FolderType);
-    ADDREFERENCE(objects, RefTypeId_Organizes, UA_FALSE, ObjId_Server);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&objects, UA_NODESTORE_INSERT_UNIQUE);
-
-    // Types
+    UA_NodeStore_insert(server->nodestore, (const UA_Node**)&objects, UA_FALSE);
+    ADDREFERENCE(UA_NODEIDS[UA_OBJECTSFOLDER], UA_NODEIDS[UA_HASTYPEDEFINITION],
+                 UA_EXPANDEDNODEIDS[UA_FOLDERTYPE]);
+    ADDREFERENCE(UA_NODEIDS[UA_OBJECTSFOLDER], UA_NODEIDS[UA_ORGANIZES], UA_EXPANDEDNODEIDS[UA_SERVER]);
+
     UA_ObjectNode *types = UA_ObjectNode_new();
     UA_ObjectNode *types = UA_ObjectNode_new();
-    types->nodeId    = ObjId_TypesFolder.nodeId;
+    COPYNAMES(types, "Types");
+    types->nodeId    = UA_NODEIDS[UA_TYPESFOLDER];
     types->nodeClass = UA_NODECLASS_OBJECT;
     types->nodeClass = UA_NODECLASS_OBJECT;
-    UA_QualifiedName_copycstring("Types", &types->browseName);
-    UA_LocalizedText_copycstring("Types", &types->displayName);
-    UA_LocalizedText_copycstring("Types", &types->description);
-    ADDREFERENCE(types, RefTypeId_HasTypeDefinition, UA_FALSE, ObjTypeId_FolderType);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&types, UA_NODESTORE_INSERT_UNIQUE);
+    UA_NodeStore_insert(server->nodestore, (const UA_Node**)&types, UA_FALSE);
+    ADDREFERENCE(UA_NODEIDS[UA_TYPESFOLDER], UA_NODEIDS[UA_HASTYPEDEFINITION],
+                 UA_EXPANDEDNODEIDS[UA_FOLDERTYPE]);
 
 
-    // Views
     UA_ObjectNode *views = UA_ObjectNode_new();
     UA_ObjectNode *views = UA_ObjectNode_new();
-    views->nodeId    = ObjId_ViewsFolder.nodeId;
+    COPYNAMES(views, "Views");
+    views->nodeId    = UA_NODEIDS[UA_VIEWSFOLDER];
     views->nodeClass = UA_NODECLASS_OBJECT;
     views->nodeClass = UA_NODECLASS_OBJECT;
-    UA_QualifiedName_copycstring("Views", &views->browseName);
-    UA_LocalizedText_copycstring("Views", &views->displayName);
-    UA_LocalizedText_copycstring("Views", &views->description);
-    ADDREFERENCE(views, RefTypeId_HasTypeDefinition, UA_FALSE, ObjTypeId_FolderType);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&views, UA_NODESTORE_INSERT_UNIQUE);
+    UA_NodeStore_insert(server->nodestore, (const UA_Node**)&views, UA_FALSE);
+    ADDREFERENCE(UA_NODEIDS[UA_VIEWSFOLDER], UA_NODEIDS[UA_HASTYPEDEFINITION],
+                 UA_EXPANDEDNODEIDS[UA_FOLDERTYPE]);
 
 
-    // Server
     UA_ObjectNode *servernode = UA_ObjectNode_new();
     UA_ObjectNode *servernode = UA_ObjectNode_new();
-    servernode->nodeId    = ObjId_Server.nodeId;
+    COPYNAMES(servernode, "Server");
+    servernode->nodeId    = UA_NODEIDS[UA_SERVER];
     servernode->nodeClass = UA_NODECLASS_OBJECT;
     servernode->nodeClass = UA_NODECLASS_OBJECT;
-    UA_QualifiedName_copycstring("Server", &servernode->browseName);
-    UA_LocalizedText_copycstring("Server", &servernode->displayName);
-    UA_LocalizedText_copycstring("Server", &servernode->description);
-    ADDREFERENCE(servernode, RefTypeId_HasComponent, UA_FALSE, ObjId_ServerCapabilities);
-    ADDREFERENCE(servernode, RefTypeId_HasComponent, UA_FALSE, ObjId_NamespaceArray);
-    ADDREFERENCE(servernode, RefTypeId_HasProperty, UA_FALSE, ObjId_ServerStatus);
-    ADDREFERENCE(servernode, RefTypeId_HasProperty, UA_FALSE, ObjId_ServerArray);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&servernode, UA_NODESTORE_INSERT_UNIQUE);
-
-    // NamespaceArray
+    UA_NodeStore_insert(server->nodestore, (const UA_Node**)&servernode, UA_FALSE);
+    ADDREFERENCE(UA_NODEIDS[UA_SERVER], UA_NODEIDS[UA_HASCOMPONENT],
+                 UA_EXPANDEDNODEIDS[UA_SERVER_SERVERCAPABILITIES]);
+    ADDREFERENCE(UA_NODEIDS[UA_SERVER], UA_NODEIDS[UA_HASPROPERTY], UA_EXPANDEDNODEIDS[UA_SERVER_SERVERARRAY]);
+
     UA_VariableNode *namespaceArray = UA_VariableNode_new();
     UA_VariableNode *namespaceArray = UA_VariableNode_new();
-    namespaceArray->nodeId    = ObjId_NamespaceArray.nodeId;
-    namespaceArray->nodeClass = UA_NODECLASS_VARIABLE; //FIXME: this should go into _new?
-    UA_QualifiedName_copycstring("NodeStoreArray", &namespaceArray->browseName);
-    UA_LocalizedText_copycstring("NodeStoreArray", &namespaceArray->displayName);
-    UA_LocalizedText_copycstring("NodeStoreArray", &namespaceArray->description);
+    COPYNAMES(namespaceArray, "NamespaceArray");
+    namespaceArray->nodeId    = UA_NODEIDS[UA_SERVER_NAMESPACEARRAY];
+    namespaceArray->nodeClass = UA_NODECLASS_VARIABLE;
     UA_Array_new(&namespaceArray->value.storage.data.dataPtr, 2, &UA_TYPES[UA_STRING]);
     UA_Array_new(&namespaceArray->value.storage.data.dataPtr, 2, &UA_TYPES[UA_STRING]);
     namespaceArray->value.vt = &UA_TYPES[UA_STRING];
     namespaceArray->value.vt = &UA_TYPES[UA_STRING];
     namespaceArray->value.storage.data.arrayLength = 2;
     namespaceArray->value.storage.data.arrayLength = 2;
-    UA_String_copycstring("http://opcfoundation.org/UA/", &((UA_String *)(namespaceArray->value.storage.data.dataPtr))[0]);
-    UA_String_copycstring("http://localhost:16664/open62541/", &((UA_String *)(namespaceArray->value.storage.data.dataPtr))[1]);
+    // Fixme: Insert the external namespaces
+    UA_String_copycstring("http://opcfoundation.org/UA/",
+                          &((UA_String *)(namespaceArray->value.storage.data.dataPtr))[0]);
+    UA_String_copycstring("urn:myServer:myApplication",
+                          &((UA_String *)(namespaceArray->value.storage.data.dataPtr))[1]);
     UA_UInt32 *dimensions = UA_alloc(sizeof(UA_UInt32));
     UA_UInt32 *dimensions = UA_alloc(sizeof(UA_UInt32));
     if(dimensions) {
     if(dimensions) {
         *dimensions = 2;
         *dimensions = 2;
         namespaceArray->arrayDimensions = dimensions;
         namespaceArray->arrayDimensions = dimensions;
         namespaceArray->arrayDimensionsSize = 1;
         namespaceArray->arrayDimensionsSize = 1;
     }
     }
-    namespaceArray->dataType = NS0NODEID(UA_STRING_NS0);
-    namespaceArray->valueRank       = 1;
+    namespaceArray->dataType = UA_NODEIDS[UA_STRING];
+    namespaceArray->valueRank = 1;
     namespaceArray->minimumSamplingInterval = 1.0;
     namespaceArray->minimumSamplingInterval = 1.0;
-    namespaceArray->historizing     = UA_FALSE;
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&namespaceArray, UA_NODESTORE_INSERT_UNIQUE);
+    namespaceArray->historizing = UA_FALSE;
+    UA_Server_addNode(server, (const UA_Node **)&namespaceArray,
+                      &UA_EXPANDEDNODEIDS[UA_SERVER], &UA_NODEIDS[UA_HASCOMPONENT]);
 
 
-    // ServerStatus
-    UA_VariableNode *serverstatus = UA_VariableNode_new();
-    serverstatus->nodeId    = ObjId_ServerStatus.nodeId;
-    serverstatus->nodeClass = UA_NODECLASS_VARIABLE;
-    UA_QualifiedName_copycstring("ServerStatus", &serverstatus->browseName);
-    UA_LocalizedText_copycstring("ServerStatus", &serverstatus->displayName);
-    UA_LocalizedText_copycstring("ServerStatus", &serverstatus->description);
     UA_ServerStatusDataType *status = UA_ServerStatusDataType_new();
     UA_ServerStatusDataType *status = UA_ServerStatusDataType_new();
     status->startTime   = UA_DateTime_now();
     status->startTime   = UA_DateTime_now();
     status->currentTime = UA_DateTime_now();
     status->currentTime = UA_DateTime_now();
@@ -498,61 +432,31 @@ UA_Server * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertifica
     UA_String_copycstring("open62541", &status->buildInfo.productName);
     UA_String_copycstring("open62541", &status->buildInfo.productName);
     UA_String_copycstring("0.0", &status->buildInfo.softwareVersion);
     UA_String_copycstring("0.0", &status->buildInfo.softwareVersion);
     UA_String_copycstring("0.0", &status->buildInfo.buildNumber);
     UA_String_copycstring("0.0", &status->buildInfo.buildNumber);
-    status->buildInfo.buildDate     = UA_DateTime_now();
-    status->secondsTillShutdown     = 99999999;
+    status->buildInfo.buildDate = UA_DateTime_now();
+    status->secondsTillShutdown = 99999999;
     UA_LocalizedText_copycstring("because", &status->shutdownReason);
     UA_LocalizedText_copycstring("because", &status->shutdownReason);
-    serverstatus->value.vt          = &UA_TYPES[UA_SERVERSTATUSDATATYPE]; // gets encoded as an extensionobject
+    UA_VariableNode *serverstatus = UA_VariableNode_new();
+    COPYNAMES(serverstatus, "ServerStatus");
+    serverstatus->nodeId    = UA_NODEIDS[UA_SERVER_SERVERSTATUS];
+    serverstatus->nodeClass = UA_NODECLASS_VARIABLE;
+    serverstatus->value.vt = &UA_TYPES[UA_SERVERSTATUSDATATYPE]; // gets encoded as an extensionobject
     serverstatus->value.storage.data.arrayLength = 1;
     serverstatus->value.storage.data.arrayLength = 1;
-    serverstatus->value.storage.data.dataPtr        = status;
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&serverstatus, UA_NODESTORE_INSERT_UNIQUE);
+    serverstatus->value.storage.data.dataPtr = status;
+    UA_Server_addNode(server, (const UA_Node **)&serverstatus,
+                      &UA_EXPANDEDNODEIDS[UA_SERVER], &UA_NODEIDS[UA_HASPROPERTY]);
 
 
-    // State (Component of ServerStatus)
+    // todo: make this variable point to a member of the serverstatus
     UA_VariableNode *state = UA_VariableNode_new();
     UA_VariableNode *state = UA_VariableNode_new();
-    state->nodeId    = ObjId_State.nodeId;
+    UA_ServerState stateEnum = UA_SERVERSTATE_RUNNING;
+    COPYNAMES(state, "State");
+    state->nodeId    = UA_NODEIDS[UA_SERVER_SERVERSTATUS_STATE];
     state->nodeClass = UA_NODECLASS_VARIABLE;
     state->nodeClass = UA_NODECLASS_VARIABLE;
-    UA_QualifiedName_copycstring("State", &state->browseName);
-    UA_LocalizedText_copycstring("State", &state->displayName);
-    UA_LocalizedText_copycstring("State", &state->description);
     state->value.vt = &UA_TYPES[UA_SERVERSTATE];
     state->value.vt = &UA_TYPES[UA_SERVERSTATE];
     state->value.storage.data.arrayDimensionsLength = 1; // added to ensure encoding in readreponse
     state->value.storage.data.arrayDimensionsLength = 1; // added to ensure encoding in readreponse
     state->value.storage.data.arrayLength = 1;
     state->value.storage.data.arrayLength = 1;
-    state->value.storage.data.dataPtr = &status->state; // points into the other object.
+    state->value.storage.data.dataPtr = &stateEnum; // points into the other object.
     state->value.storageType = UA_VARIANT_DATA_NODELETE;
     state->value.storageType = UA_VARIANT_DATA_NODELETE;
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&state, UA_NODESTORE_INSERT_UNIQUE);
-
-    UA_NodeStore_release((const UA_Node *)root);
+    UA_NodeStore_insert(server->nodestore, (const UA_Node**)&state, UA_FALSE);
 
 
     return server;
     return server;
 }
 }
-
-UA_AddNodesResult UA_Server_addNode(UA_Server *server, UA_Node **node, const UA_NodeId *parentNodeId,
-                                    const UA_NodeId *referenceTypeId) {
-    UA_ExpandedNodeId expParentNodeId;
-    UA_ExpandedNodeId_init(&expParentNodeId);
-    UA_NodeId_copy(parentNodeId, &expParentNodeId.nodeId);
-    return AddNode(server, &adminSession, node, &expParentNodeId, referenceTypeId);
-}
-
-void UA_Server_addReference(UA_Server *server, const UA_AddReferencesRequest *request,
-                            UA_AddReferencesResponse *response) {
-    Service_AddReferences(server, &adminSession, request, response);
-}
-
-UA_AddNodesResult UA_Server_addScalarVariableNode(UA_Server *server, UA_String *browseName, void *value,
-                                                  const UA_VTable_Entry *vt, const UA_NodeId *parentNodeId,
-                                                  const UA_NodeId *referenceTypeId ) {
-    UA_ExpandedNodeId expParentNodeId;
-    UA_ExpandedNodeId_init(&expParentNodeId);
-    UA_NodeId_copy(parentNodeId, &expParentNodeId.nodeId);
-    UA_VariableNode *tmpNode = UA_VariableNode_new();
-    UA_String_copy(browseName, &tmpNode->browseName.name);
-    UA_String_copy(browseName, &tmpNode->displayName.text);
-    /* UA_LocalizedText_copycstring("integer value", &tmpNode->description); */
-    tmpNode->nodeClass = UA_NODECLASS_VARIABLE;
-    tmpNode->valueRank = -1;
-    tmpNode->value.vt = vt;
-    tmpNode->value.storage.data.dataPtr = value;
-    tmpNode->value.storageType = UA_VARIANT_DATA_NODELETE;
-    tmpNode->value.storage.data.arrayLength = 1;
-    return AddNode(server, &adminSession, (UA_Node**)&tmpNode, &expParentNodeId, referenceTypeId);
-}

+ 210 - 0
src/server/ua_server_addressspace.c

@@ -0,0 +1,210 @@
+#include "ua_server.h"
+#include "ua_server_internal.h"
+#include "ua_namespace_0.h"
+
+const UA_TypeVTable * UA_Node_getTypeVT(const UA_Node *node) {
+    switch(node->nodeClass) {
+    case UA_NODECLASS_OBJECT:
+        return &UA_TYPES[UA_OBJECTNODE];
+    case UA_NODECLASS_VARIABLE:
+        return &UA_TYPES[UA_VARIABLENODE];
+    case UA_NODECLASS_METHOD:
+        return &UA_TYPES[UA_METHODNODE];
+    case UA_NODECLASS_OBJECTTYPE:
+        return &UA_TYPES[UA_OBJECTTYPENODE];
+    case UA_NODECLASS_VARIABLETYPE:
+        return &UA_TYPES[UA_VARIABLETYPENODE];
+    case UA_NODECLASS_REFERENCETYPE:
+        return &UA_TYPES[UA_REFERENCETYPENODE];
+    case UA_NODECLASS_DATATYPE:
+        return &UA_TYPES[UA_DATATYPENODE];
+    case UA_NODECLASS_VIEW:
+        return &UA_TYPES[UA_VIEWNODE];
+    default: break;
+    }
+
+    return &UA_TYPES[UA_INVALIDTYPE];
+}
+
+void UA_Server_addScalarVariableNode(UA_Server *server, UA_QualifiedName *browseName, void *value,
+                                     const UA_TypeVTable *vt, const UA_ExpandedNodeId *parentNodeId,
+                                     const UA_NodeId *referenceTypeId) {
+    UA_VariableNode *tmpNode = UA_VariableNode_new();
+    UA_QualifiedName_copy(browseName, &tmpNode->browseName);
+    UA_String_copy(&browseName->name, &tmpNode->displayName.text);
+    /* UA_LocalizedText_copycstring("integer value", &tmpNode->description); */
+    tmpNode->nodeClass = UA_NODECLASS_VARIABLE;
+    tmpNode->valueRank = -1;
+    tmpNode->value.vt = vt;
+    tmpNode->value.storage.data.dataPtr = value;
+    tmpNode->value.storageType = UA_VARIANT_DATA;
+    tmpNode->value.storage.data.arrayLength = 1;
+    UA_Server_addNode(server, (const UA_Node**)&tmpNode, parentNodeId, referenceTypeId);
+}
+
+/* Adds a one-way reference to the local nodestore */
+UA_StatusCode addOneWayReferenceWithSession (UA_Server *server, UA_Session *session,
+                                             const UA_AddReferencesItem *item) {
+    // use the servers nodestore
+    const UA_Node *node = UA_NodeStore_get(server->nodestore, &item->sourceNodeId);
+    // todo differentiate between error codes
+    if(!node)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    const UA_TypeVTable *nodeVT = UA_Node_getTypeVT(node);
+    UA_Node *newNode = nodeVT->new();
+    nodeVT->copy(node, newNode);
+
+    UA_Int32 count = node->referencesSize;
+    if(count < 0)
+        count = 0;
+    UA_ReferenceNode *old_refs = newNode->references;
+    UA_ReferenceNode *new_refs = UA_alloc(sizeof(UA_ReferenceNode)*(count+1));
+    if(!new_refs) {
+        nodeVT->delete(newNode);
+        UA_NodeStore_release(node);
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    }
+
+    // insert the new reference
+    UA_memcpy(new_refs, old_refs, sizeof(UA_ReferenceNode)*count);
+    UA_ReferenceNode_init(&new_refs[count]);
+    UA_StatusCode retval = UA_NodeId_copy(&item->referenceTypeId, &new_refs[count].referenceTypeId);
+    new_refs[count].isInverse = !item->isForward;
+    retval |= UA_ExpandedNodeId_copy(&item->targetNodeId, &new_refs[count].targetId);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_Array_delete(new_refs, ++count, &UA_TYPES[UA_REFERENCENODE]);
+        newNode->references = UA_NULL;
+        newNode->referencesSize = 0;
+        nodeVT->delete(newNode);
+        UA_NodeStore_release(node);
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    }
+
+    UA_free(old_refs);
+    newNode->references = new_refs;
+    newNode->referencesSize = ++count;
+    retval = UA_NodeStore_replace(server->nodestore, (const UA_Node **)&newNode, UA_FALSE);
+    if(retval)
+        nodeVT->delete(newNode);
+    UA_NodeStore_release(node);
+
+    return retval;
+}
+
+UA_StatusCode UA_Server_addReference(UA_Server *server, const UA_AddReferencesItem *item) {
+    return UA_Server_addReferenceWithSession(server, &adminSession, item);
+}
+
+UA_StatusCode UA_Server_addReferenceWithSession(UA_Server *server, UA_Session *session,
+                                                const UA_AddReferencesItem *item) {
+    // todo: we don't support references to other servers (expandednodeid) for now
+    if(item->targetServerUri.length > 0)
+        return UA_STATUSCODE_BADNOTIMPLEMENTED;
+    
+    // Is this for an external nodestore?
+    UA_ExternalNodeStore *ensFirst = UA_NULL;
+    UA_ExternalNodeStore *ensSecond = UA_NULL;
+    for(UA_Int32 j = 0;j<server->externalNamespacesSize && (!ensFirst || !ensSecond);j++) {
+        if(item->sourceNodeId.namespaceIndex == server->externalNamespaces[j].index)
+            ensFirst = &server->externalNamespaces[j].externalNodeStore;
+        if(item->targetNodeId.nodeId.namespaceIndex == server->externalNamespaces[j].index)
+            ensSecond = &server->externalNamespaces[j].externalNodeStore;
+    }
+
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    if(ensFirst) {
+        // todo: use external nodestore
+    } else
+        retval = addOneWayReferenceWithSession (server, session, item);
+
+    if(retval) return retval;
+
+    UA_AddReferencesItem secondItem;
+    secondItem = *item;
+    secondItem.targetNodeId.nodeId = item->sourceNodeId;
+    secondItem.sourceNodeId = item->targetNodeId.nodeId;
+    secondItem.isForward = !item->isForward;
+    if(ensSecond) {
+        // todo: use external nodestore
+    } else
+        retval = addOneWayReferenceWithSession (server, session, &secondItem);
+    // todo: remove reference if the second direction failed
+
+    return retval;
+} 
+
+UA_AddNodesResult UA_Server_addNode(UA_Server *server, const UA_Node **node,
+                                    const UA_ExpandedNodeId *parentNodeId, const UA_NodeId *referenceTypeId) {
+    return UA_Server_addNodeWithSession(server, &adminSession, node, parentNodeId, referenceTypeId);
+}
+
+UA_AddNodesResult UA_Server_addNodeWithSession(UA_Server *server, UA_Session *session, const UA_Node **node,
+                                               const UA_ExpandedNodeId *parentNodeId, const UA_NodeId *referenceTypeId) {
+    UA_AddNodesResult result;
+    UA_AddNodesResult_init(&result);
+
+    const UA_Node *parent = UA_NodeStore_get(server->nodestore, &parentNodeId->nodeId);
+    if(!parent) {
+        result.statusCode = UA_STATUSCODE_BADPARENTNODEIDINVALID;
+        return result;
+    }
+
+    const UA_ReferenceTypeNode *referenceType =
+        (const UA_ReferenceTypeNode *)UA_NodeStore_get(server->nodestore, referenceTypeId);
+    if(!referenceType) {
+        result.statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
+        goto ret;
+    }
+
+    if(referenceType->nodeClass != UA_NODECLASS_REFERENCETYPE) {
+        result.statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
+        goto ret2;
+    }
+
+    if(referenceType->isAbstract == UA_TRUE) {
+        result.statusCode = UA_STATUSCODE_BADREFERENCENOTALLOWED;
+        goto ret2;
+    }
+
+    // todo: test if the referencetype is hierarchical
+    if(UA_NodeId_isNull(&(*node)->nodeId)) {
+        if(UA_NodeStore_insert(server->nodestore, node, UA_TRUE) != UA_STATUSCODE_GOOD) {
+            result.statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+            goto ret2;
+        }
+        result.addedNodeId = (*node)->nodeId; // cannot fail as unique nodeids are numeric
+    } else {
+        if(UA_NodeId_copy(&(*node)->nodeId, &result.addedNodeId) != UA_STATUSCODE_GOOD) {
+            result.statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+            goto ret2;
+        }
+
+        if(UA_NodeStore_insert(server->nodestore, node, UA_TRUE) != UA_STATUSCODE_GOOD) {
+            result.statusCode = UA_STATUSCODE_BADNODEIDEXISTS;  // todo: differentiate out of memory
+            UA_NodeId_deleteMembers(&result.addedNodeId);
+            goto ret2;
+        }
+    }
+    
+    // reference back to the parent
+    UA_AddReferencesItem item;
+    UA_AddReferencesItem_init(&item);
+    item.sourceNodeId = (*node)->nodeId;
+    item.referenceTypeId = referenceType->nodeId;
+    item.isForward = UA_FALSE;
+    item.targetNodeId.nodeId = parent->nodeId;
+    UA_Server_addReference(server, &item);
+
+    // todo: error handling. remove new node from nodestore
+
+    UA_NodeStore_release(*node);
+    *node = UA_NULL;
+    
+ ret2:
+    UA_NodeStore_release((UA_Node*)referenceType);
+ ret:
+    UA_NodeStore_release(parent);
+
+    return result;
+}

+ 2 - 2
src/server/ua_server_binary.c

@@ -91,8 +91,8 @@ static void processOpen(UA_Connection *connection, UA_Server *server, const UA_B
     respHeader.isFinal     = 'F';
     respHeader.isFinal     = 'F';
     respHeader.messageSize = 8+4; //header + securechannelid
     respHeader.messageSize = 8+4; //header + securechannelid
 
 
-    UA_ExpandedNodeId responseType;
-    NS0EXPANDEDNODEID(responseType, 449);
+    UA_ExpandedNodeId responseType = UA_EXPANDEDNODEIDS[UA_OPENSECURECHANNELRESPONSE];
+    responseType.nodeId.identifier.numeric += UA_ENCODINGOFFSET_BINARY;
 
 
     respHeader.messageSize += UA_AsymmetricAlgorithmSecurityHeader_calcSizeBinary(&asymHeader);
     respHeader.messageSize += UA_AsymmetricAlgorithmSecurityHeader_calcSizeBinary(&asymHeader);
     respHeader.messageSize += UA_SequenceHeader_calcSizeBinary(&seqHeader);
     respHeader.messageSize += UA_SequenceHeader_calcSizeBinary(&seqHeader);

+ 24 - 2
src/server/ua_server_internal.h

@@ -2,19 +2,41 @@
 #define UA_SERVER_INTERNAL_H_
 #define UA_SERVER_INTERNAL_H_
 
 
 #include "ua_server.h"
 #include "ua_server.h"
+#include "ua_nodestore.h"
 #include "ua_session_manager.h"
 #include "ua_session_manager.h"
 #include "ua_securechannel_manager.h"
 #include "ua_securechannel_manager.h"
 
 
+/** Mapping of namespace-id and url to an external nodestore. For namespaces
+    that have no mapping defined, the internal nodestore is used by default. */
+typedef struct UA_ExternalNamespace {
+	UA_UInt16 index;
+	UA_String url;
+	UA_ExternalNodeStore externalNodeStore;
+} UA_ExternalNamespace;
+
+void UA_ExternalNamespace_init(UA_ExternalNamespace *ens);
+void UA_ExternalNamespace_deleteMembers(UA_ExternalNamespace *ens);
+
 struct UA_Server {
 struct UA_Server {
     UA_ApplicationDescription description;
     UA_ApplicationDescription description;
     UA_Int32 endpointDescriptionsSize;
     UA_Int32 endpointDescriptionsSize;
     UA_EndpointDescription *endpointDescriptions;
     UA_EndpointDescription *endpointDescriptions;
+
     UA_ByteString serverCertificate;
     UA_ByteString serverCertificate;
-    
     UA_SecureChannelManager secureChannelManager;
     UA_SecureChannelManager secureChannelManager;
     UA_SessionManager sessionManager;
     UA_SessionManager sessionManager;
-    UA_NodeStore *nodestore;
     UA_Logger logger;
     UA_Logger logger;
+
+    UA_NodeStore *nodestore;
+    UA_Int32 externalNamespacesSize;
+    UA_ExternalNamespace *externalNamespaces;
 };
 };
 
 
+UA_AddNodesResult
+UA_Server_addNodeWithSession(UA_Server *server, UA_Session *session, const UA_Node **node,
+                             const UA_ExpandedNodeId *parentNodeId, const UA_NodeId *referenceTypeId);
+
+UA_StatusCode
+UA_Server_addReferenceWithSession(UA_Server *server, UA_Session *session, const UA_AddReferencesItem *item);
+
 #endif /* UA_SERVER_INTERNAL_H_ */
 #endif /* UA_SERVER_INTERNAL_H_ */

+ 17 - 33
src/server/ua_services.h

@@ -11,8 +11,6 @@
  * @defgroup services Services
  * @defgroup services Services
  *
  *
  * @brief This module describes all the services used to communicate in in OPC UA.
  * @brief This module describes all the services used to communicate in in OPC UA.
- *
- * @{
  */
  */
 
 
 /**
 /**
@@ -30,8 +28,7 @@
  * the configuration information required to establish a SecureChannel and a
  * the configuration information required to establish a SecureChannel and a
  * Session.
  * Session.
  */
  */
-void Service_GetEndpoints(UA_Server                    *server,
-                          const UA_GetEndpointsRequest *request, UA_GetEndpointsResponse *response);
+void Service_GetEndpoints(UA_Server *server, const UA_GetEndpointsRequest *request, UA_GetEndpointsResponse *response);
 // Service_RegisterServer
 // Service_RegisterServer
 /** @} */
 /** @} */
 
 
@@ -85,9 +82,7 @@ void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
 void Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
 void Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
                              const UA_ActivateSessionRequest *request, UA_ActivateSessionResponse *response);
                              const UA_ActivateSessionRequest *request, UA_ActivateSessionResponse *response);
 
 
-/**
- * @brief This Service is used to terminate a Session.
- */
+/** @brief This Service is used to terminate a Session. */
 void Service_CloseSession(UA_Server *server, const UA_CloseSessionRequest *request, UA_CloseSessionResponse *response);
 void Service_CloseSession(UA_Server *server, const UA_CloseSessionRequest *request, UA_CloseSessionResponse *response);
 // Service_Cancel
 // Service_Cancel
 /** @} */
 /** @} */
@@ -102,20 +97,18 @@ void Service_CloseSession(UA_Server *server, const UA_CloseSessionRequest *reque
  * @{
  * @{
  */
  */
 
 
-/**
- * @brief This Service is used to add one or more Nodes into the AddressSpace hierarchy.
- */
-void Service_AddNodes(UA_Server *server, UA_Session *session,
-                      const UA_AddNodesRequest *request, UA_AddNodesResponse *response);
+/** @brief This Service is used to add one or more Nodes into the AddressSpace hierarchy. */
+void Service_AddNodes(UA_Server *server, UA_Session *session, const UA_AddNodesRequest *request, UA_AddNodesResponse *response);
 
 
-/**
- * @brief This Service is used to add one or more References to one or more Nodes
- */
-void Service_AddReferences(UA_Server *server, UA_Session *session,
-                           const UA_AddReferencesRequest *request, UA_AddReferencesResponse *response);
+/** @brief This Service is used to add one or more References to one or more Nodes. */
+void Service_AddReferences(UA_Server *server, UA_Session *session, const UA_AddReferencesRequest *request, UA_AddReferencesResponse *response);
+
+/** @brief This Service is used to delete one or more Nodes from the AddressSpace. */
+void Service_DeleteNodes(UA_Server *server, UA_Session *session, const UA_DeleteNodesRequest *request, UA_DeleteNodesResponse *response);
+
+/** @brief This Service is used to delete one or more References of a Node. */
+void Service_DeleteReferences(UA_Server *server, UA_Session *session, const UA_DeleteReferencesRequest *request, UA_DeleteReferencesResponse *response);
 
 
-// Service_DeleteNodes
-// Service_DeleteReferences
 /** @} */
 /** @} */
 
 
 /**
 /**
@@ -135,20 +128,15 @@ void Service_AddReferences(UA_Server *server, UA_Session *session,
 void Service_Browse(UA_Server *server, UA_Session *session,
 void Service_Browse(UA_Server *server, UA_Session *session,
                     const UA_BrowseRequest *request, UA_BrowseResponse *response);
                     const UA_BrowseRequest *request, UA_BrowseResponse *response);
 
 
-/**
- * @brief This Service is used to translate textual node paths to their respective ids.
- */
+/** @brief This Service is used to translate textual node paths to their respective ids. */
 void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
 void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
                                            const UA_TranslateBrowsePathsToNodeIdsRequest *request,
                                            const UA_TranslateBrowsePathsToNodeIdsRequest *request,
                                            UA_TranslateBrowsePathsToNodeIdsResponse *response);
                                            UA_TranslateBrowsePathsToNodeIdsResponse *response);
 // Service_BrowseNext
 // Service_BrowseNext
-// Service_TranslateBrowsePathsToNodeIds
 // Service_RegisterNodes
 // Service_RegisterNodes
 // Service_UnregisterNodes
 // Service_UnregisterNodes
 /** @} */
 /** @} */
 
 
-
-/* Part 4: 5.9 Query Service Set */
 /**
 /**
  * @name Query Service Set
  * @name Query Service Set
  *
  *
@@ -166,7 +154,6 @@ void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *sessio
 // Service_QueryNext
 // Service_QueryNext
 /** @} */
 /** @} */
 
 
-/* Part 4: 5.10 Attribute Service Set */
 /**
 /**
  * @name Attribute Service Set
  * @name Attribute Service Set
  *
  *
@@ -183,9 +170,9 @@ void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *sessio
  * values as a composite, to read individual elements or to read ranges of
  * values as a composite, to read individual elements or to read ranges of
  * elements of the composite.
  * elements of the composite.
  */
  */
-void Service_Read(UA_Server *server, UA_Session *session,
-                  const UA_ReadRequest *request, UA_ReadResponse *response);
+void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *request, UA_ReadResponse *response);
 // Service_HistoryRead
 // Service_HistoryRead
+
 /**
 /**
  * @brief This Service is used to write one or more Attributes of one or more
  * @brief This Service is used to write one or more Attributes of one or more
  *  Nodes. For constructed Attribute values whose elements are indexed, such as
  *  Nodes. For constructed Attribute values whose elements are indexed, such as
@@ -193,8 +180,7 @@ void Service_Read(UA_Server *server, UA_Session *session,
  *  values as a composite, to write individual elements or to write ranges of
  *  values as a composite, to write individual elements or to write ranges of
  *  elements of the composite.
  *  elements of the composite.
  */
  */
-void Service_Write(UA_Server *server, UA_Session *session,
-                   const UA_WriteRequest *request, UA_WriteResponse *response);
+void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request, UA_WriteResponse *response);
 // Service_HistoryUpdate
 // Service_HistoryUpdate
 /** @} */
 /** @} */
 
 
@@ -261,6 +247,4 @@ void Service_Write(UA_Server *server, UA_Session *session,
 // Service_DeleteSubscription
 // Service_DeleteSubscription
 /** @} */
 /** @} */
 
 
-/** @} */ // end of group
-
-#endif
+#endif /* UA_SERVICES_H_ */

+ 126 - 108
src/server/ua_services_attribute.c

@@ -1,212 +1,182 @@
 #include "ua_server_internal.h"
 #include "ua_server_internal.h"
 #include "ua_services.h"
 #include "ua_services.h"
+#include "ua_services_internal.h"
 #include "ua_statuscodes.h"
 #include "ua_statuscodes.h"
 #include "ua_nodestore.h"
 #include "ua_nodestore.h"
 #include "ua_namespace_0.h"
 #include "ua_namespace_0.h"
 #include "ua_util.h"
 #include "ua_util.h"
 
 
-enum UA_AttributeId {
-    UA_ATTRIBUTEID_NODEID                  = 1,
-    UA_ATTRIBUTEID_NODECLASS               = 2,
-    UA_ATTRIBUTEID_BROWSENAME              = 3,
-    UA_ATTRIBUTEID_DISPLAYNAME             = 4,
-    UA_ATTRIBUTEID_DESCRIPTION             = 5,
-    UA_ATTRIBUTEID_WRITEMASK               = 6,
-    UA_ATTRIBUTEID_USERWRITEMASK           = 7,
-    UA_ATTRIBUTEID_ISABSTRACT              = 8,
-    UA_ATTRIBUTEID_SYMMETRIC               = 9,
-    UA_ATTRIBUTEID_INVERSENAME             = 10,
-    UA_ATTRIBUTEID_CONTAINSNOLOOPS         = 11,
-    UA_ATTRIBUTEID_EVENTNOTIFIER           = 12,
-    UA_ATTRIBUTEID_VALUE                   = 13,
-    UA_ATTRIBUTEID_DATATYPE                = 14,
-    UA_ATTRIBUTEID_VALUERANK               = 15,
-    UA_ATTRIBUTEID_ARRAYDIMENSIONS         = 16,
-    UA_ATTRIBUTEID_ACCESSLEVEL             = 17,
-    UA_ATTRIBUTEID_USERACCESSLEVEL         = 18,
-    UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL = 19,
-    UA_ATTRIBUTEID_HISTORIZING             = 20,
-    UA_ATTRIBUTEID_EXECUTABLE              = 21,
-    UA_ATTRIBUTEID_USEREXECUTABLE          = 22
-};
-
-#define CHECK_NODECLASS(CLASS)                                 \
-    if(!(node->nodeClass & (CLASS))) {                         \
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE; \
-        v.status       = UA_STATUSCODE_BADNOTREADABLE;         \
-        break;                                                 \
-    }                                                          \
-
-static UA_DataValue service_read_node(UA_Server *server, const UA_ReadValueId *id) {
-    UA_DataValue v;
-    UA_DataValue_init(&v);
-
-    UA_Node const *node   = UA_NULL;
-    UA_Int32       result = UA_NodeStore_get(server->nodestore, &(id->nodeId), &node);
-    if(result != UA_STATUSCODE_GOOD || node == UA_NULL) {
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
-        v.status       = UA_STATUSCODE_BADNODEIDUNKNOWN;
-        return v;
+#define CHECK_NODECLASS(CLASS)                                  \
+    if(!(node->nodeClass & (CLASS))) {                          \
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE; \
+        v->status       = UA_STATUSCODE_BADNOTREADABLE;         \
+        break;                                                  \
     }
     }
+
+/** Reads a single attribute from a node in the nodestore. */
+static void readValue(UA_Server *server, const UA_ReadValueId *id, UA_DataValue *v) {
+    UA_Node const *node = UA_NodeStore_get(server->nodestore, &(id->nodeId));
+    if(!node) {
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
+        v->status       = UA_STATUSCODE_BADNODEIDUNKNOWN;
+        return;
+    }
+
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
 
 
     switch(id->attributeId) {
     switch(id->attributeId) {
     case UA_ATTRIBUTEID_NODEID:
     case UA_ATTRIBUTEID_NODEID:
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_NODEID], &node->nodeId);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_NODEID], &node->nodeId);
         break;
         break;
 
 
     case UA_ATTRIBUTEID_NODECLASS:
     case UA_ATTRIBUTEID_NODECLASS:
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_INT32], &node->nodeClass);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_INT32], &node->nodeClass);
         break;
         break;
 
 
     case UA_ATTRIBUTEID_BROWSENAME:
     case UA_ATTRIBUTEID_BROWSENAME:
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_QUALIFIEDNAME], &node->browseName);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_QUALIFIEDNAME], &node->browseName);
         break;
         break;
 
 
     case UA_ATTRIBUTEID_DISPLAYNAME:
     case UA_ATTRIBUTEID_DISPLAYNAME:
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_LOCALIZEDTEXT], &node->displayName);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_LOCALIZEDTEXT], &node->displayName);
         break;
         break;
 
 
     case UA_ATTRIBUTEID_DESCRIPTION:
     case UA_ATTRIBUTEID_DESCRIPTION:
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_LOCALIZEDTEXT], &node->description);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_LOCALIZEDTEXT], &node->description);
         break;
         break;
 
 
     case UA_ATTRIBUTEID_WRITEMASK:
     case UA_ATTRIBUTEID_WRITEMASK:
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_UINT32], &node->writeMask);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_UINT32], &node->writeMask);
         break;
         break;
 
 
     case UA_ATTRIBUTEID_USERWRITEMASK:
     case UA_ATTRIBUTEID_USERWRITEMASK:
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_UINT32], &node->userWriteMask);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_UINT32], &node->userWriteMask);
         break;
         break;
 
 
     case UA_ATTRIBUTEID_ISABSTRACT:
     case UA_ATTRIBUTEID_ISABSTRACT:
-        CHECK_NODECLASS(
-            UA_NODECLASS_REFERENCETYPE | UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE |
-            UA_NODECLASS_DATATYPE);
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |=
-            UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN], &((UA_ReferenceTypeNode *)node)->isAbstract);
+        CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE | UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE |
+                        UA_NODECLASS_DATATYPE);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_BOOLEAN], &((UA_ReferenceTypeNode *)node)->isAbstract);
         break;
         break;
 
 
     case UA_ATTRIBUTEID_SYMMETRIC:
     case UA_ATTRIBUTEID_SYMMETRIC:
         CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
         CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN],
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_BOOLEAN],
                                           &((UA_ReferenceTypeNode *)node)->symmetric);
                                           &((UA_ReferenceTypeNode *)node)->symmetric);
         break;
         break;
 
 
     case UA_ATTRIBUTEID_INVERSENAME:
     case UA_ATTRIBUTEID_INVERSENAME:
         CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
         CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_LOCALIZEDTEXT],
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_LOCALIZEDTEXT],
                                           &((UA_ReferenceTypeNode *)node)->inverseName);
                                           &((UA_ReferenceTypeNode *)node)->inverseName);
         break;
         break;
 
 
     case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
     case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
         CHECK_NODECLASS(UA_NODECLASS_VIEW);
         CHECK_NODECLASS(UA_NODECLASS_VIEW);
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN],
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_BOOLEAN],
                                           &((UA_ViewNode *)node)->containsNoLoops);
                                           &((UA_ViewNode *)node)->containsNoLoops);
         break;
         break;
 
 
     case UA_ATTRIBUTEID_EVENTNOTIFIER:
     case UA_ATTRIBUTEID_EVENTNOTIFIER:
         CHECK_NODECLASS(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT);
         CHECK_NODECLASS(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT);
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BYTE],
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_BYTE],
                                           &((UA_ViewNode *)node)->eventNotifier);
                                           &((UA_ViewNode *)node)->eventNotifier);
         break;
         break;
 
 
     case UA_ATTRIBUTEID_VALUE:
     case UA_ATTRIBUTEID_VALUE:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |= UA_Variant_copy(&((UA_VariableNode *)node)->value, &v.value); // todo: zero-copy
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copy(&((UA_VariableNode *)node)->value, &v->value); // todo: zero-copy
         break;
         break;
 
 
     case UA_ATTRIBUTEID_DATATYPE:
     case UA_ATTRIBUTEID_DATATYPE:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_NODEID],
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_NODEID],
                                           &((UA_VariableTypeNode *)node)->dataType);
                                           &((UA_VariableTypeNode *)node)->dataType);
         break;
         break;
 
 
     case UA_ATTRIBUTEID_VALUERANK:
     case UA_ATTRIBUTEID_VALUERANK:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_INT32],
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_INT32],
                                           &((UA_VariableTypeNode *)node)->valueRank);
                                           &((UA_VariableTypeNode *)node)->valueRank);
         break;
         break;
 
 
     case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
     case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        UA_Variant_copySetArray(&v.value, &UA_TYPES[UA_UINT32],
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        UA_Variant_copySetArray(&v->value, &UA_TYPES[UA_UINT32],
                                 ((UA_VariableTypeNode *)node)->arrayDimensionsSize,
                                 ((UA_VariableTypeNode *)node)->arrayDimensionsSize,
                                 &((UA_VariableTypeNode *)node)->arrayDimensions);
                                 &((UA_VariableTypeNode *)node)->arrayDimensions);
         break;
         break;
 
 
     case UA_ATTRIBUTEID_ACCESSLEVEL:
     case UA_ATTRIBUTEID_ACCESSLEVEL:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BYTE],
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_BYTE],
                                           &((UA_VariableNode *)node)->accessLevel);
                                           &((UA_VariableNode *)node)->accessLevel);
         break;
         break;
 
 
     case UA_ATTRIBUTEID_USERACCESSLEVEL:
     case UA_ATTRIBUTEID_USERACCESSLEVEL:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BYTE],
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_BYTE],
                                           &((UA_VariableNode *)node)->userAccessLevel);
                                           &((UA_VariableNode *)node)->userAccessLevel);
         break;
         break;
 
 
     case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
     case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_DOUBLE],
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_DOUBLE],
                                           &((UA_VariableNode *)node)->minimumSamplingInterval);
                                           &((UA_VariableNode *)node)->minimumSamplingInterval);
         break;
         break;
 
 
     case UA_ATTRIBUTEID_HISTORIZING:
     case UA_ATTRIBUTEID_HISTORIZING:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN],
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_BOOLEAN],
                                           &((UA_VariableNode *)node)->historizing);
                                           &((UA_VariableNode *)node)->historizing);
         break;
         break;
 
 
     case UA_ATTRIBUTEID_EXECUTABLE:
     case UA_ATTRIBUTEID_EXECUTABLE:
         CHECK_NODECLASS(UA_NODECLASS_METHOD);
         CHECK_NODECLASS(UA_NODECLASS_METHOD);
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN],
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_BOOLEAN],
                                           &((UA_MethodNode *)node)->executable);
                                           &((UA_MethodNode *)node)->executable);
         break;
         break;
 
 
     case UA_ATTRIBUTEID_USEREXECUTABLE:
     case UA_ATTRIBUTEID_USEREXECUTABLE:
         CHECK_NODECLASS(UA_NODECLASS_METHOD);
         CHECK_NODECLASS(UA_NODECLASS_METHOD);
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-        retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN],
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_BOOLEAN],
                                           &((UA_MethodNode *)node)->userExecutable);
                                           &((UA_MethodNode *)node)->userExecutable);
         break;
         break;
 
 
     default:
     default:
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
-        v.status       = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
+        v->status       = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
         break;
         break;
     }
     }
 
 
     UA_NodeStore_release(node);
     UA_NodeStore_release(node);
 
 
     if(retval != UA_STATUSCODE_GOOD) {
     if(retval != UA_STATUSCODE_GOOD) {
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
-        v.status       = UA_STATUSCODE_BADNOTREADABLE;
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
+        v->status       = UA_STATUSCODE_BADNOTREADABLE;
     }
     }
-
-    return v;
 }
 }
 
 
 void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *request,
 void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *request,
@@ -216,24 +186,49 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
         return;
         return;
     }
     }
 
 
-    UA_StatusCode retval = UA_Array_new((void**)&response->results, request->nodesToReadSize, &UA_TYPES[UA_DATAVALUE]);
+    UA_StatusCode retval = UA_Array_new((void**)&response->results,
+                                        request->nodesToReadSize,
+                                        &UA_TYPES[UA_DATAVALUE]);
     if(retval) {
     if(retval) {
         response->responseHeader.serviceResult = retval;
         response->responseHeader.serviceResult = retval;
         return;
         return;
     }
     }
 
 
+    /* ### Begin External Namespaces */
+    UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * request->nodesToReadSize);
+    memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToReadSize);
+    UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * request->nodesToReadSize);
+    for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
+        UA_UInt32 indexSize = 0;
+        for(UA_Int32 i = 0;i < request->nodesToReadSize;i++) {
+            if(request->nodesToRead[i].nodeId.namespaceIndex !=
+               server->externalNamespaces[j].index)
+                continue;
+            isExternal[i] = UA_TRUE;
+            indices[indexSize] = i;
+            indexSize++;
+        }
+        if(indexSize == 0)
+            continue;
+        UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
+        ens->readNodes(ens->ensHandle, &request->requestHeader, request->nodesToRead,
+                       indices, indexSize, response->results, UA_FALSE, response->diagnosticInfos);
+    }
+    /* ### End External Namespaces */
+
     response->resultsSize = request->nodesToReadSize;
     response->resultsSize = request->nodesToReadSize;
-    for(UA_Int32 i = 0;i < response->resultsSize;i++)
-        response->results[i] = service_read_node(server, &request->nodesToRead[i]);
+    for(UA_Int32 i = 0;i < response->resultsSize;i++) {
+        if(!isExternal[i])
+            readValue(server, &request->nodesToRead[i], &response->results[i]);
+    }
 }
 }
 
 
-static UA_StatusCode Service_Write_writeNode(UA_Server *server, UA_WriteValue *writeValue) {
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    const UA_Node *node;
-    retval = UA_NodeStore_get(server->nodestore, &writeValue->nodeId, &node);
-    if(retval)
-        return retval;
+static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *writeValue) {
+    const UA_Node *node = UA_NodeStore_get(server->nodestore, &writeValue->nodeId);
+    if(!node)
+        return UA_STATUSCODE_BADNODEIDUNKNOWN;
 
 
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
     switch(writeValue->attributeId) {
     switch(writeValue->attributeId) {
     case UA_ATTRIBUTEID_NODEID:
     case UA_ATTRIBUTEID_NODEID:
         /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){ } */
         /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){ } */
@@ -353,7 +348,6 @@ static UA_StatusCode Service_Write_writeNode(UA_Server *server, UA_WriteValue *w
 
 
     UA_NodeStore_release(node);
     UA_NodeStore_release(node);
     return retval;
     return retval;
-
 }
 }
 
 
 void Service_Write(UA_Server *server, UA_Session *session,
 void Service_Write(UA_Server *server, UA_Session *session,
@@ -365,8 +359,32 @@ void Service_Write(UA_Server *server, UA_Session *session,
         response->responseHeader.serviceResult = retval;
         response->responseHeader.serviceResult = retval;
         return;
         return;
     }
     }
+
+    /* ### Begin External Namespaces */
+    UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * request->nodesToWriteSize);
+    memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToWriteSize);
+    UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * request->nodesToWriteSize);
+    for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
+        UA_UInt32 indexSize = 0;
+        for(UA_Int32 i = 0;i < request->nodesToWriteSize;i++) {
+            if(request->nodesToWrite[i].nodeId.namespaceIndex !=
+               server->externalNamespaces[j].index)
+                continue;
+            isExternal[i] = UA_TRUE;
+            indices[indexSize] = i;
+            indexSize++;
+        }
+        if(indexSize == 0)
+            continue;
+        UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
+        ens->writeNodes(ens->ensHandle, &request->requestHeader, request->nodesToWrite,
+                        indices, indexSize, response->results, response->diagnosticInfos);
+    }
+    /* ### End External Namespaces */
     
     
     response->resultsSize = request->nodesToWriteSize;
     response->resultsSize = request->nodesToWriteSize;
-    for(UA_Int32 i = 0;i < request->nodesToWriteSize;i++)
-        response->results[i] = Service_Write_writeNode(server, &request->nodesToWrite[i]);
+    for(UA_Int32 i = 0;i < request->nodesToWriteSize;i++) {
+        if(!isExternal[i])
+            response->results[i] = writeValue(server, &request->nodesToWrite[i]);
+    }
 }
 }

+ 31 - 9
src/server/ua_services_internal.h

@@ -3,16 +3,38 @@
  * internally as well (with a simplified API as no access rights are checked).
  * internally as well (with a simplified API as no access rights are checked).
  */
  */
 
 
+#ifndef UA_SERVICES_INTERNAL_H_
+#define UA_SERVICES_INTERNAL_H_
+
 #include "ua_session.h"
 #include "ua_session.h"
 #include "ua_nodestore.h"
 #include "ua_nodestore.h"
 #include "ua_types_generated.h"
 #include "ua_types_generated.h"
+#include "ua_namespace_0.h"
 
 
-/* @brief Add a reference (and the inverse reference to the target node).
- *
- * @param The node to which the reference shall be added
- * @param The reference itself
- * @param The namespace where the target node is looked up for the reverse reference (this is omitted if targetns is UA_NULL)
- */
-UA_Int32 AddReference(UA_NodeStore *nodestore, UA_Node *node, UA_ReferenceNode *reference);
-UA_AddNodesResult AddNode(UA_Server *server, UA_Session *session, UA_Node **node,
-                          const UA_ExpandedNodeId *parentNodeId, const UA_NodeId *referenceTypeId);
+/** The (nodes) AttributeIds are defined in part 6, table A1 of the standard */
+typedef enum UA_AttributeId {
+    UA_ATTRIBUTEID_NODEID                  = 1,
+    UA_ATTRIBUTEID_NODECLASS               = 2,
+    UA_ATTRIBUTEID_BROWSENAME              = 3,
+    UA_ATTRIBUTEID_DISPLAYNAME             = 4,
+    UA_ATTRIBUTEID_DESCRIPTION             = 5,
+    UA_ATTRIBUTEID_WRITEMASK               = 6,
+    UA_ATTRIBUTEID_USERWRITEMASK           = 7,
+    UA_ATTRIBUTEID_ISABSTRACT              = 8,
+    UA_ATTRIBUTEID_SYMMETRIC               = 9,
+    UA_ATTRIBUTEID_INVERSENAME             = 10,
+    UA_ATTRIBUTEID_CONTAINSNOLOOPS         = 11,
+    UA_ATTRIBUTEID_EVENTNOTIFIER           = 12,
+    UA_ATTRIBUTEID_VALUE                   = 13,
+    UA_ATTRIBUTEID_DATATYPE                = 14,
+    UA_ATTRIBUTEID_VALUERANK               = 15,
+    UA_ATTRIBUTEID_ARRAYDIMENSIONS         = 16,
+    UA_ATTRIBUTEID_ACCESSLEVEL             = 17,
+    UA_ATTRIBUTEID_USERACCESSLEVEL         = 18,
+    UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL = 19,
+    UA_ATTRIBUTEID_HISTORIZING             = 20,
+    UA_ATTRIBUTEID_EXECUTABLE              = 21,
+    UA_ATTRIBUTEID_USEREXECUTABLE          = 22
+} UA_AttributeId;
+
+#endif /* UA_SERVICES_INTERNAL_H_ */

+ 171 - 121
src/server/ua_services_nodemanagement.c

@@ -23,8 +23,9 @@
     } while(0)
     } while(0)
 
 
 static UA_StatusCode parseVariableNode(UA_ExtensionObject *attributes, UA_Node **new_node,
 static UA_StatusCode parseVariableNode(UA_ExtensionObject *attributes, UA_Node **new_node,
-                                       const UA_VTable_Entry **vt) {
-    if(attributes->typeId.identifier.numeric != 357) // VariableAttributes_Encoding_DefaultBinary,357,Object
+                                       const UA_TypeVTable **vt) {
+    if(attributes->typeId.identifier.numeric !=
+       UA_NODEIDS[UA_VARIABLEATTRIBUTES].identifier.numeric + UA_ENCODINGOFFSET_BINARY)
         return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
         return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
 
 
     UA_VariableAttributes attr;
     UA_VariableAttributes attr;
@@ -82,170 +83,219 @@ static UA_StatusCode parseVariableNode(UA_ExtensionObject *attributes, UA_Node *
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
-UA_Int32 AddReference(UA_NodeStore *nodestore, UA_Node *node, UA_ReferenceNode *reference);
-
-/**
-   If adding the node succeeds, the pointer will be set to zero. If the nodeid
-   of the node is null (ns=0,i=0), a unique new nodeid will be assigned and
-   returned in the AddNodesResult.
- */
-UA_AddNodesResult AddNode(UA_Server *server, UA_Session *session, UA_Node **node,
-                          const UA_ExpandedNodeId *parentNodeId, const UA_NodeId *referenceTypeId) {
-    UA_AddNodesResult result;
-    UA_AddNodesResult_init(&result);
-    
-    const UA_Node *parent;
-    if(UA_NodeStore_get(server->nodestore, &parentNodeId->nodeId, &parent) != UA_STATUSCODE_GOOD) {
-        result.statusCode = UA_STATUSCODE_BADPARENTNODEIDINVALID;
-        return result;
+static UA_StatusCode parseObjectNode(UA_ExtensionObject *attributes,
+                                     UA_Node **new_node, const UA_TypeVTable **vt) {
+    if(attributes->typeId.identifier.numeric !=
+       UA_NODEIDS[UA_OBJECTATTRIBUTES].identifier.numeric + UA_ENCODINGOFFSET_BINARY)  // VariableAttributes_Encoding_DefaultBinary
+        return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
+    UA_ObjectAttributes attr;
+    UA_UInt32 pos = 0;
+    // todo return more informative error codes from decodeBinary
+    if (UA_ObjectAttributes_decodeBinary(&attributes->body, &pos, &attr) != UA_STATUSCODE_GOOD)
+        return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
+    UA_ObjectNode *vnode = UA_ObjectNode_new();
+    if(!vnode) {
+        UA_ObjectAttributes_deleteMembers(&attr);
+        return UA_STATUSCODE_BADOUTOFMEMORY;
     }
     }
 
 
-    const UA_ReferenceTypeNode *referenceType;
-    if(UA_NodeStore_get(server->nodestore, referenceTypeId, (const UA_Node**)&referenceType) != UA_STATUSCODE_GOOD) {
-        result.statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
-        goto ret;
-    }
+    // now copy all the attributes. This potentially removes them from the decoded attributes.
+    COPY_STANDARDATTRIBUTES;
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_EVENTNOTIFIER)
+        vnode->eventNotifier = attr.eventNotifier;
+    UA_ObjectAttributes_deleteMembers(&attr);
+    *new_node = (UA_Node*) vnode;
+    *vt = &UA_TYPES[UA_OBJECTNODE];
+    return UA_STATUSCODE_GOOD;
+}
 
 
-    if(referenceType->nodeClass != UA_NODECLASS_REFERENCETYPE) {
-        result.statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
-        goto ret2;
+static UA_StatusCode parseReferenceTypeNode(UA_ExtensionObject *attributes,
+                                            UA_Node **new_node, const UA_TypeVTable **vt) {
+    UA_ReferenceTypeAttributes attr;
+    UA_UInt32 pos = 0;
+    // todo return more informative error codes from decodeBinary
+    if(UA_ReferenceTypeAttributes_decodeBinary(&attributes->body, &pos, &attr) != UA_STATUSCODE_GOOD)
+        return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
+    UA_ReferenceTypeNode *vnode = UA_ReferenceTypeNode_new();
+    if(!vnode) {
+        UA_ReferenceTypeAttributes_deleteMembers(&attr);
+        return UA_STATUSCODE_BADOUTOFMEMORY;
     }
     }
 
 
-    if(referenceType->isAbstract == UA_TRUE) {
-        result.statusCode = UA_STATUSCODE_BADREFERENCENOTALLOWED;
-        goto ret2;
+    // now copy all the attributes. This potentially removes them from the decoded attributes.
+    COPY_STANDARDATTRIBUTES;
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_ISABSTRACT)
+        vnode->isAbstract = attr.isAbstract;
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_SYMMETRIC)
+        vnode->symmetric = attr.symmetric;
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_INVERSENAME) {
+        vnode->inverseName = attr.inverseName;
+        attr.inverseName.text.length = -1;
+        attr.inverseName.text.data = UA_NULL;
+        attr.inverseName.locale.length = -1;
+        attr.inverseName.locale.data = UA_NULL;
     }
     }
+    UA_ReferenceTypeAttributes_deleteMembers(&attr);
+    *new_node = (UA_Node*) vnode;
+    *vt = &UA_TYPES[UA_REFERENCETYPENODE];
+    return UA_STATUSCODE_GOOD;
+}
 
 
-    // todo: test if the referenetype is hierarchical
-
-    if(UA_NodeId_isNull(&(*node)->nodeId)) {
-        if(UA_NodeStore_insert(server->nodestore, node,
-                               UA_NODESTORE_INSERT_UNIQUE | UA_NODESTORE_INSERT_GETMANAGED) != UA_STATUSCODE_GOOD) {
-            result.statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
-            goto ret2;
-        }
-        result.addedNodeId = (*node)->nodeId; // cannot fail as unique nodeids are numeric
-    } else {
-        if(UA_NodeId_copy(&(*node)->nodeId, &result.addedNodeId) != UA_STATUSCODE_GOOD) {
-            result.statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
-            goto ret2;
-        }
-
-        if(UA_NodeStore_insert(server->nodestore, node, UA_NODESTORE_INSERT_GETMANAGED) != UA_STATUSCODE_GOOD) {
-            result.statusCode = UA_STATUSCODE_BADNODEIDEXISTS;  // todo: differentiate out of memory
-            UA_NodeId_deleteMembers(&result.addedNodeId);
-            goto ret2;
-        }
+static UA_StatusCode parseObjectTypeNode(UA_ExtensionObject *attributes,
+                                         UA_Node **new_node, const UA_TypeVTable **vt) {
+    UA_ObjectTypeAttributes attr;
+    UA_UInt32 pos = 0;
+    // todo return more informative error codes from decodeBinary
+    if(UA_ObjectTypeAttributes_decodeBinary(&attributes->body, &pos, &attr) != UA_STATUSCODE_GOOD)
+        return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
+    UA_ObjectTypeNode *vnode = UA_ObjectTypeNode_new();
+    if(!vnode) {
+        UA_ObjectTypeAttributes_deleteMembers(&attr);
+        return UA_STATUSCODE_BADOUTOFMEMORY;
     }
     }
     
     
-    UA_ReferenceNode ref;
-    UA_ReferenceNode_init(&ref);
-    ref.referenceTypeId = referenceType->nodeId; // is numeric
-    ref.isInverse = UA_TRUE; // todo: check if they are all not inverse..
-    ref.targetId.nodeId = parent->nodeId;
-    AddReference(server->nodestore, *node, &ref);
-
-    // todo: error handling. remove new node from nodestore
-
-    UA_NodeStore_release(*node);
-    *node = UA_NULL;
-    
- ret2:
-    UA_NodeStore_release((UA_Node*)referenceType);
- ret:
-    UA_NodeStore_release(parent);
+    // now copy all the attributes. This potentially removes them from the decoded attributes.
+    COPY_STANDARDATTRIBUTES;
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_ISABSTRACT) {
+        vnode->isAbstract = attr.isAbstract;
+    }
+    UA_ObjectTypeAttributes_deleteMembers(&attr);
+    *new_node = (UA_Node*) vnode;
+    *vt = &UA_TYPES[UA_OBJECTTYPENODE];
+    return UA_STATUSCODE_GOOD;
+}
 
 
-    return result;
+static UA_StatusCode parseViewNode(UA_ExtensionObject *attributes, UA_Node **new_node,
+                                   const UA_TypeVTable **vt) {
+    UA_ViewAttributes attr;
+    UA_UInt32 pos = 0;
+    // todo return more informative error codes from decodeBinary
+    if(UA_ViewAttributes_decodeBinary(&attributes->body, &pos, &attr) != UA_STATUSCODE_GOOD)
+        return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
+    UA_ViewNode *vnode = UA_ViewNode_new();
+    if(!vnode) {
+        UA_ViewAttributes_deleteMembers(&attr);
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    }
+    // now copy all the attributes. This potentially removes them from the decoded attributes.
+    COPY_STANDARDATTRIBUTES;
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_CONTAINSNOLOOPS)
+        vnode->containsNoLoops = attr.containsNoLoops;
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_EVENTNOTIFIER)
+        vnode->eventNotifier = attr.eventNotifier;
+    UA_ViewAttributes_deleteMembers(&attr);
+    *new_node = (UA_Node*) vnode;
+    *vt = &UA_TYPES[UA_VIEWNODE];
+    return UA_STATUSCODE_GOOD;
 }
 }
 
 
 static void addNodeFromAttributes(UA_Server *server, UA_Session *session, UA_AddNodesItem *item,
 static void addNodeFromAttributes(UA_Server *server, UA_Session *session, UA_AddNodesItem *item,
                                   UA_AddNodesResult *result) {
                                   UA_AddNodesResult *result) {
+    // adding nodes to ns0 is not allowed over the wire
     if(item->requestedNewNodeId.nodeId.namespaceIndex == 0) {
     if(item->requestedNewNodeId.nodeId.namespaceIndex == 0) {
-        // adding nodes to ns0 is not allowed over the wire
         result->statusCode = UA_STATUSCODE_BADNODEIDREJECTED;
         result->statusCode = UA_STATUSCODE_BADNODEIDREJECTED;
         return;
         return;
     }
     }
 
 
-    UA_Node *newNode;
-    const UA_VTable_Entry *newNodeVT = UA_NULL;
-    if(item->nodeClass == UA_NODECLASS_VARIABLE)
-        result->statusCode = parseVariableNode(&item->nodeAttributes, &newNode, &newNodeVT);
-    else // add more node types here..
+    // parse the node
+    UA_Node *node;
+    const UA_TypeVTable *nodeVT = UA_NULL;
+
+    switch (item->nodeClass) {
+    case UA_NODECLASS_OBJECT:
+        result->statusCode = parseObjectNode(&item->nodeAttributes, &node, &nodeVT);
+        break;
+    case UA_NODECLASS_OBJECTTYPE:
+        result->statusCode = parseObjectTypeNode(&item->nodeAttributes, &node, &nodeVT);
+        break;
+    case UA_NODECLASS_REFERENCETYPE:
+        result->statusCode = parseReferenceTypeNode(&item->nodeAttributes, &node, &nodeVT);
+        break;
+    case UA_NODECLASS_VARIABLE:
+        result->statusCode = parseVariableNode(&item->nodeAttributes, &node, &nodeVT);
+        break;
+    default:
         result->statusCode = UA_STATUSCODE_BADNOTIMPLEMENTED;
         result->statusCode = UA_STATUSCODE_BADNOTIMPLEMENTED;
+    }
 
 
     if(result->statusCode != UA_STATUSCODE_GOOD)
     if(result->statusCode != UA_STATUSCODE_GOOD)
         return;
         return;
 
 
-    *result = AddNode(server, session, &newNode, &item->parentNodeId, &item->referenceTypeId);
+    // add the node
+    *result = UA_Server_addNodeWithSession(server, session, (const UA_Node **)&node,
+                                           &item->parentNodeId, &item->referenceTypeId);
     if(result->statusCode != UA_STATUSCODE_GOOD)
     if(result->statusCode != UA_STATUSCODE_GOOD)
-        newNodeVT->delete(newNode);
+        nodeVT->delete(node);
 }
 }
 
 
-void Service_AddNodes(UA_Server *server, UA_Session *session,
-                      const UA_AddNodesRequest *request, UA_AddNodesResponse *response) {
+void Service_AddNodes(UA_Server *server, UA_Session *session, const UA_AddNodesRequest *request,
+                      UA_AddNodesResponse *response) {
     if(request->nodesToAddSize <= 0) {
     if(request->nodesToAddSize <= 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         return;
         return;
     }
     }
 
 
-    UA_StatusCode retval = UA_Array_new((void**)&response->results, request->nodesToAddSize, &UA_TYPES[UA_ADDNODESRESULT]);
+    UA_StatusCode retval = UA_Array_new((void**)&response->results, request->nodesToAddSize,
+                                        &UA_TYPES[UA_ADDNODESRESULT]);
     if(retval) {
     if(retval) {
         response->responseHeader.serviceResult = retval;
         response->responseHeader.serviceResult = retval;
         return;
         return;
     }
     }
+
+    /* ### Begin External Namespaces */
+    UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * request->nodesToAddSize);
+    memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToAddSize);
+    UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * request->nodesToAddSize);
+    for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
+        UA_UInt32 indexSize = 0;
+        for(UA_Int32 i = 0;i < request->nodesToAddSize;i++) {
+            if(request->nodesToAdd[i].requestedNewNodeId.nodeId.namespaceIndex != server->externalNamespaces[j].index)
+                continue;
+            isExternal[i] = UA_TRUE;
+            indices[indexSize] = i;
+            indexSize++;
+        }
+        if(indexSize == 0)
+            continue;
+        UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
+        ens->addNodes(ens->ensHandle, &request->requestHeader, request->nodesToAdd,
+                      indices, indexSize, response->results, response->diagnosticInfos);
+    }
+    /* ### End External Namespaces */
     
     
     response->resultsSize = request->nodesToAddSize;
     response->resultsSize = request->nodesToAddSize;
-    for(int i = 0;i < request->nodesToAddSize;i++)
-        addNodeFromAttributes(server, session, &request->nodesToAdd[i], &response->results[i]);
+    for(int i = 0;i < request->nodesToAddSize;i++) {
+        if(!isExternal[i])
+            addNodeFromAttributes(server, session, &request->nodesToAdd[i], &response->results[i]);
+    }
 }
 }
 
 
-static UA_Int32 AddSingleReference(UA_Node *node, UA_ReferenceNode *reference) {
-    // TODO: Check if reference already exists
-    UA_Int32 count = node->referencesSize;
-    UA_ReferenceNode *old_refs = node->references;
-    UA_ReferenceNode *new_refs;
-
-    if(count < 0) count = 0;
-
-    if(!(new_refs = UA_alloc(sizeof(UA_ReferenceNode)*(count+1))))
-        return UA_STATUSCODE_BADOUTOFMEMORY;
+void Service_AddReferences(UA_Server *server, UA_Session *session, const UA_AddReferencesRequest *request,
+                           UA_AddReferencesResponse *response) {
+    if(request->referencesToAddSize <= 0) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
+        return;
+    }
 
 
-    UA_memcpy(new_refs, old_refs, sizeof(UA_ReferenceNode)*count);
-    if(UA_ReferenceNode_copy(reference, &new_refs[count]) != UA_STATUSCODE_GOOD) {
-        UA_free(new_refs);
-        return UA_STATUSCODE_BADOUTOFMEMORY;
+    response->results = UA_alloc(sizeof(UA_StatusCode)*request->referencesToAddSize);
+    if(!response->results) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
+        return;
     }
     }
 
 
-    node->references     = new_refs;
-    node->referencesSize = count+1;
-    UA_free(old_refs);
-    return UA_STATUSCODE_GOOD;
+    response->resultsSize = request->referencesToAddSize;
+    for(UA_Int32 i = 0;i < response->resultsSize;i++)
+        response->results[i] = UA_Server_addReferenceWithSession(server, session,
+                                                                 &request->referencesToAdd[i]);
 }
 }
 
 
-UA_Int32 AddReference(UA_NodeStore *nodestore, UA_Node *node, UA_ReferenceNode *reference) {
-    UA_Int32 retval = AddSingleReference(node, reference);
-    UA_Node *targetnode;
-    UA_ReferenceNode inversereference;
-    if(retval != UA_STATUSCODE_GOOD || nodestore == UA_NULL)
-        return retval;
-
-    // Do a copy every time?
-    if(UA_NodeStore_get(nodestore, &reference->targetId.nodeId, (const UA_Node **)&targetnode) != UA_STATUSCODE_GOOD)
-        return UA_STATUSCODE_BADINTERNALERROR;
-
-    inversereference.referenceTypeId       = reference->referenceTypeId;
-    inversereference.isInverse             = !reference->isInverse;
-    inversereference.targetId.nodeId       = node->nodeId;
-    inversereference.targetId.namespaceUri = UA_STRING_NULL;
-    inversereference.targetId.serverIndex  = 0;
-    retval = AddSingleReference(targetnode, &inversereference);
-    UA_NodeStore_release(targetnode);
-
-    return retval;
+void Service_DeleteNodes(UA_Server *server, UA_Session *session, const UA_DeleteNodesRequest *request,
+                         UA_DeleteNodesResponse *response) {
+
 }
 }
 
 
-void Service_AddReferences(UA_Server *server, UA_Session *session,
-                           const UA_AddReferencesRequest *request,
-                           UA_AddReferencesResponse *response) {
-    response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTIMPLEMENTED;
+void Service_DeleteReferences(UA_Server *server, UA_Session *session,
+                              const UA_DeleteReferencesRequest *request,
+                              UA_DeleteReferencesResponse *response) {
+
 }
 }

+ 45 - 44
src/server/ua_services_view.c

@@ -8,13 +8,8 @@
 /* Releases the current node, even if it was supplied as an argument. */
 /* Releases the current node, even if it was supplied as an argument. */
 static UA_StatusCode fillReferenceDescription(UA_NodeStore *ns, const UA_Node *currentNode, UA_ReferenceNode *reference,
 static UA_StatusCode fillReferenceDescription(UA_NodeStore *ns, const UA_Node *currentNode, UA_ReferenceNode *reference,
                                               UA_UInt32 resultMask, UA_ReferenceDescription *referenceDescription) {
                                               UA_UInt32 resultMask, UA_ReferenceDescription *referenceDescription) {
-    UA_ReferenceDescription_init(referenceDescription);
-    if(!currentNode && resultMask != 0) {
-        if(UA_NodeStore_get(ns, &reference->targetId.nodeId, &currentNode) != UA_STATUSCODE_GOOD)
-            return UA_STATUSCODE_BADINTERNALERROR;
-    }
-
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    UA_ReferenceDescription_init(referenceDescription);
     retval |= UA_NodeId_copy(&currentNode->nodeId, &referenceDescription->nodeId.nodeId);
     retval |= UA_NodeId_copy(&currentNode->nodeId, &referenceDescription->nodeId.nodeId);
     //TODO: ExpandedNodeId is mocked up
     //TODO: ExpandedNodeId is mocked up
     referenceDescription->nodeId.serverIndex = 0;
     referenceDescription->nodeId.serverIndex = 0;
@@ -48,20 +43,17 @@ static UA_StatusCode fillReferenceDescription(UA_NodeStore *ns, const UA_Node *c
     return retval;
     return retval;
 }
 }
 
 
-/* Tests if the node is relevant an shall be returned. If the targetNode needs
-   to be retrieved from the nodestore to determine this, the targetNode is
-   returned if the node is relevant. */
-static UA_Boolean isRelevantTargetNode(UA_NodeStore *ns, const UA_BrowseDescription *browseDescription, UA_Boolean returnAll,
-                                       UA_ReferenceNode *reference, const UA_Node **currentNode,
-                                       UA_NodeId *relevantRefTypes, UA_UInt32 relevantRefTypesCount) {
-    // 1) Test Browse direction
+/* Tests if the node is relevant to the browse request and shall be returned. If
+   so, it is retrieved from the Nodestore. If not, null is returned. */
+static const UA_Node *
+getRelevantTargetNode(UA_NodeStore *ns, const UA_BrowseDescription *browseDescription, UA_Boolean returnAll,
+                      UA_ReferenceNode *reference, UA_NodeId *relevantRefTypes, UA_UInt32 relevantRefTypesCount) {
     if(reference->isInverse == UA_TRUE && browseDescription->browseDirection == UA_BROWSEDIRECTION_FORWARD)
     if(reference->isInverse == UA_TRUE && browseDescription->browseDirection == UA_BROWSEDIRECTION_FORWARD)
-        return UA_FALSE;
+        return UA_NULL;
 
 
     else if(reference->isInverse == UA_FALSE && browseDescription->browseDirection == UA_BROWSEDIRECTION_INVERSE)
     else if(reference->isInverse == UA_FALSE && browseDescription->browseDirection == UA_BROWSEDIRECTION_INVERSE)
-        return UA_FALSE;
+        return UA_NULL;
 
 
-    // 2) Test if the reference type is relevant
     UA_Boolean isRelevant = returnAll;
     UA_Boolean isRelevant = returnAll;
     if(!isRelevant) {
     if(!isRelevant) {
         for(UA_UInt32 i = 0;i < relevantRefTypesCount;i++) {
         for(UA_UInt32 i = 0;i < relevantRefTypesCount;i++) {
@@ -69,23 +61,19 @@ static UA_Boolean isRelevantTargetNode(UA_NodeStore *ns, const UA_BrowseDescript
                 isRelevant = UA_TRUE;
                 isRelevant = UA_TRUE;
         }
         }
         if(!isRelevant)
         if(!isRelevant)
-            return UA_FALSE;
+            return UA_NULL;
     }
     }
 
 
-    // 3) Test if the target nodeClass is relevant
-    if(browseDescription->nodeClassMask == 0)
-        return UA_TRUE; // the node is relevant, but we didn't need to get it from the nodestore yet.
-
-    if(UA_NodeStore_get(ns, &reference->targetId.nodeId, currentNode) != UA_STATUSCODE_GOOD)
-        return UA_FALSE;
+    const UA_Node *node = UA_NodeStore_get(ns, &reference->targetId.nodeId);
+    if(!node)
+        return UA_NULL;
 
 
-    if(((*currentNode)->nodeClass & browseDescription->nodeClassMask) == 0) {
-        UA_NodeStore_release(*currentNode);
-        return UA_FALSE;
+    if(browseDescription->nodeClassMask != 0 && (node->nodeClass & browseDescription->nodeClassMask) == 0) {
+        UA_NodeStore_release(node);
+        return UA_NULL;
     }
     }
 
 
-    // the node is relevant and was retrieved from the nodestore, do not release it.
-    return UA_TRUE;
+    return node;
 }
 }
 
 
 /* We do not search across namespaces so far. The id of the root-referencetype
 /* We do not search across namespaces so far. The id of the root-referencetype
@@ -107,12 +95,13 @@ static UA_StatusCode findRelevantReferenceTypes(UA_NodeStore *ns, const UA_NodeI
         return UA_STATUSCODE_BADOUTOFMEMORY;
         return UA_STATUSCODE_BADOUTOFMEMORY;
     }
     }
         
         
-    const UA_ReferenceTypeNode *node;
     do {
     do {
-        retval |= UA_NodeStore_get(ns, &typeArray[currentIndex], (const UA_Node **)&node);
-        if(retval)
+        const UA_ReferenceTypeNode *node =
+            (const UA_ReferenceTypeNode *)UA_NodeStore_get(ns, &typeArray[currentIndex]);
+        if(!node)
             break;
             break;
-        if(node->nodeClass != UA_NODECLASS_REFERENCETYPE) // subtypes of referencestypes are always referencestypes?
+        // subtypes of referencestypes are always referencestypes?
+        if(node->nodeClass != UA_NODECLASS_REFERENCETYPE) 
             continue;
             continue;
 
 
         // Find subtypes of the current referencetype
         // Find subtypes of the current referencetype
@@ -164,7 +153,8 @@ static void getBrowseResult(UA_NodeStore *ns, const UA_BrowseDescription *browse
     if(!returnAll) {
     if(!returnAll) {
         if(browseDescription->includeSubtypes) {
         if(browseDescription->includeSubtypes) {
             browseResult->statusCode = findRelevantReferenceTypes(ns, &browseDescription->referenceTypeId,
             browseResult->statusCode = findRelevantReferenceTypes(ns, &browseDescription->referenceTypeId,
-                                                                  &relevantReferenceTypes, &relevantReferenceTypesSize);
+                                                                  &relevantReferenceTypes,
+                                                                  &relevantReferenceTypesSize);
             if(browseResult->statusCode != UA_STATUSCODE_GOOD)
             if(browseResult->statusCode != UA_STATUSCODE_GOOD)
                 return;
                 return;
         } else {
         } else {
@@ -173,17 +163,23 @@ static void getBrowseResult(UA_NodeStore *ns, const UA_BrowseDescription *browse
         }
         }
     }
     }
 
 
-    const UA_Node *parentNode;
-    if(UA_NodeStore_get(ns, &browseDescription->nodeId, &parentNode) != UA_STATUSCODE_GOOD) {
+    const UA_Node *parentNode = UA_NodeStore_get(ns, &browseDescription->nodeId);
+    if(!parentNode) {
         browseResult->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
         browseResult->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
         if(!returnAll && browseDescription->includeSubtypes)
         if(!returnAll && browseDescription->includeSubtypes)
             UA_Array_delete(relevantReferenceTypes, relevantReferenceTypesSize, &UA_TYPES[UA_NODEID]);
             UA_Array_delete(relevantReferenceTypes, relevantReferenceTypesSize, &UA_TYPES[UA_NODEID]);
         return;
         return;
     }
     }
 
 
+    maxReferences = parentNode->referencesSize;
     // 0 => unlimited references
     // 0 => unlimited references
-    if(maxReferences == 0 || maxReferences > UA_INT32_MAX || (UA_Int32)maxReferences > parentNode->referencesSize)
-        maxReferences = parentNode->referencesSize;
+    if(maxReferences <= 0 || maxReferences > UA_INT32_MAX ||
+       (UA_Int32)maxReferences > parentNode->referencesSize) {
+        if(parentNode->referencesSize < 0)
+            maxReferences = 0;
+        else
+            maxReferences = parentNode->referencesSize;
+    }
 
 
     /* We allocate an array that is probably too big. But since most systems
     /* We allocate an array that is probably too big. But since most systems
        have more than enough memory, this has zero impact on speed and
        have more than enough memory, this has zero impact on speed and
@@ -194,14 +190,17 @@ static void getBrowseResult(UA_NodeStore *ns, const UA_BrowseDescription *browse
     } else {
     } else {
         UA_UInt32 currentRefs = 0;
         UA_UInt32 currentRefs = 0;
         for(UA_Int32 i = 0;i < parentNode->referencesSize && currentRefs < maxReferences;i++) {
         for(UA_Int32 i = 0;i < parentNode->referencesSize && currentRefs < maxReferences;i++) {
-            // 1) Is the node relevant? This might retrieve the node from the nodestore
-            const UA_Node *currentNode = UA_NULL;
-            if(!isRelevantTargetNode(ns, browseDescription, returnAll, &parentNode->references[i], &currentNode,
-                                     relevantReferenceTypes, relevantReferenceTypesSize))
+            // 1) Is the node relevant? If yes, the node is retrieved from the nodestore.
+            const UA_Node *currentNode = getRelevantTargetNode(ns, browseDescription, returnAll,
+                                                               &parentNode->references[i],
+                                                               relevantReferenceTypes,
+                                                               relevantReferenceTypesSize);
+            if(!currentNode)
                 continue;
                 continue;
 
 
             // 2) Fill the reference description. This also releases the current node.
             // 2) Fill the reference description. This also releases the current node.
-            if(fillReferenceDescription(ns, currentNode, &parentNode->references[i], browseDescription->resultMask,
+            if(fillReferenceDescription(ns, currentNode, &parentNode->references[i],
+                                        browseDescription->resultMask,
                                         &browseResult->references[currentRefs]) != UA_STATUSCODE_GOOD) {
                                         &browseResult->references[currentRefs]) != UA_STATUSCODE_GOOD) {
                 UA_Array_delete(browseResult->references, currentRefs, &UA_TYPES[UA_REFERENCEDESCRIPTION]);
                 UA_Array_delete(browseResult->references, currentRefs, &UA_TYPES[UA_REFERENCEDESCRIPTION]);
                 currentRefs = 0;
                 currentRefs = 0;
@@ -224,13 +223,15 @@ static void getBrowseResult(UA_NodeStore *ns, const UA_BrowseDescription *browse
         UA_Array_delete(relevantReferenceTypes, relevantReferenceTypesSize, &UA_TYPES[UA_NODEID]);
         UA_Array_delete(relevantReferenceTypes, relevantReferenceTypesSize, &UA_TYPES[UA_NODEID]);
 }
 }
 
 
-void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseRequest *request, UA_BrowseResponse *response) {
+void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseRequest *request,
+                    UA_BrowseResponse *response) {
     if(request->nodesToBrowseSize <= 0) {
     if(request->nodesToBrowseSize <= 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         return;
         return;
     }
     }
 
 
-    UA_StatusCode retval = UA_Array_new((void**)&response->results, request->nodesToBrowseSize, &UA_TYPES[UA_BROWSERESULT]);
+    UA_StatusCode retval = UA_Array_new((void**)&response->results, request->nodesToBrowseSize,
+                                        &UA_TYPES[UA_BROWSERESULT]);
     if(retval) {
     if(retval) {
         response->responseHeader.serviceResult = retval;
         response->responseHeader.serviceResult = retval;
         return;
         return;

+ 7 - 3
src/ua_config.h.in

@@ -21,10 +21,14 @@
       #define UA_EXPORT __declspec(dllexport)
       #define UA_EXPORT __declspec(dllexport)
     #endif
     #endif
   #else
   #else
-    #ifdef __GNUC__
-      #define UA_EXPORT __attribute__ ((dllimport))
+    #ifndef STATIC_LINKING
+    	#ifdef __GNUC__
+      		#define UA_EXPORT __attribute__ ((dllimport))
+    	#else
+      		#define UA_EXPORT __declspec(dllimport)
+    	#endif
     #else
     #else
-      #define UA_EXPORT __declspec(dllimport)
+    	#define UA_EXPORT
     #endif
     #endif
   #endif
   #endif
 #else
 #else

+ 1 - 1
src/ua_session.c

@@ -51,7 +51,7 @@ UA_Session * UA_Session_new() {
 UA_StatusCode UA_Session_generateToken(UA_NodeId *newToken) {
 UA_StatusCode UA_Session_generateToken(UA_NodeId *newToken) {
     //Random token generation
     //Random token generation
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    srand(time(NULL));
+    srand(time(UA_NULL));
 
 
     UA_Int32 i, r = 0;
     UA_Int32 i, r = 0;
     newToken->namespaceIndex = 0; // where else?
     newToken->namespaceIndex = 0; // where else?

+ 1 - 0
src/ua_transport.c

@@ -2,6 +2,7 @@
 #include <stdio.h>
 #include <stdio.h>
 #endif
 #endif
 #include "ua_transport.h"
 #include "ua_transport.h"
+#include "ua_types_internal.h"
 #include "ua_util.h"
 #include "ua_util.h"
 
 
 UA_TYPE_DEFAULT(UA_MessageType)
 UA_TYPE_DEFAULT(UA_MessageType)

+ 12 - 8
src/ua_types.c

@@ -13,6 +13,7 @@
 
 
 #include "ua_util.h"
 #include "ua_util.h"
 #include "ua_types.h"
 #include "ua_types.h"
+#include "ua_types_internal.h"
 #include "ua_types_encoding_binary.h"
 #include "ua_types_encoding_binary.h"
 #include "ua_namespace_0.h"
 #include "ua_namespace_0.h"
 #include "ua_statuscodes.h"
 #include "ua_statuscodes.h"
@@ -165,7 +166,10 @@ UA_Int32 UA_String_copycstring(char const *src, UA_String *dst) {
     if(length == 0) {
     if(length == 0) {
         dst->length = 0;
         dst->length = 0;
         dst->data = UA_NULL;
         dst->data = UA_NULL;
-    } else if((dst->data = UA_alloc(length)) != UA_NULL) {
+        return UA_STATUSCODE_GOOD;
+    }
+    dst->data = UA_alloc(length);
+    if(dst->data != UA_NULL) {
         memcpy(dst->data, src, length);
         memcpy(dst->data, src, length);
         dst->length = length;
         dst->length = length;
     } else {
     } else {
@@ -855,7 +859,7 @@ UA_StatusCode UA_Variant_copy(UA_Variant const *src, UA_Variant *dst) {
 }
 }
 
 
 /** Copies data into a variant. The target variant has always a storagetype UA_VARIANT_DATA */
 /** Copies data into a variant. The target variant has always a storagetype UA_VARIANT_DATA */
-UA_StatusCode UA_Variant_copySetValue(UA_Variant *v, const UA_VTable_Entry *vt, const void *value) {
+UA_StatusCode UA_Variant_copySetValue(UA_Variant *v, const UA_TypeVTable *vt, const void *value) {
     UA_Variant_init(v);
     UA_Variant_init(v);
     v->vt = vt;
     v->vt = vt;
     v->storage.data.arrayLength = 1; // no array but a single entry
     v->storage.data.arrayLength = 1; // no array but a single entry
@@ -871,7 +875,7 @@ UA_StatusCode UA_Variant_copySetValue(UA_Variant *v, const UA_VTable_Entry *vt,
     return retval;
     return retval;
 }
 }
 
 
-UA_StatusCode UA_Variant_copySetArray(UA_Variant *v, const UA_VTable_Entry *vt, UA_Int32 arrayLength, const void *array) {
+UA_StatusCode UA_Variant_copySetArray(UA_Variant *v, const UA_TypeVTable *vt, UA_Int32 arrayLength, const void *array) {
     UA_Variant_init(v);
     UA_Variant_init(v);
     v->vt = vt;
     v->vt = vt;
     v->storage.data.arrayLength = arrayLength;
     v->storage.data.arrayLength = arrayLength;
@@ -1008,7 +1012,7 @@ void UA_InvalidType_print(const UA_InvalidType *p, FILE *stream) {
 /* Array */
 /* Array */
 /*********/
 /*********/
 
 
-UA_StatusCode UA_Array_new(void **p, UA_Int32 noElements, const UA_VTable_Entry *vt) {
+UA_StatusCode UA_Array_new(void **p, UA_Int32 noElements, const UA_TypeVTable *vt) {
     if(noElements <= 0) {
     if(noElements <= 0) {
         *p = UA_NULL;
         *p = UA_NULL;
         return UA_STATUSCODE_GOOD;
         return UA_STATUSCODE_GOOD;
@@ -1028,7 +1032,7 @@ UA_StatusCode UA_Array_new(void **p, UA_Int32 noElements, const UA_VTable_Entry
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
-void UA_Array_init(void *p, UA_Int32 noElements, const UA_VTable_Entry *vt) {
+void UA_Array_init(void *p, UA_Int32 noElements, const UA_TypeVTable *vt) {
     UA_Byte *cp = (UA_Byte *)p; // so compilers allow pointer arithmetic
     UA_Byte *cp = (UA_Byte *)p; // so compilers allow pointer arithmetic
     UA_UInt32 memSize = vt->memSize;
     UA_UInt32 memSize = vt->memSize;
     for(UA_Int32 i = 0;i<noElements;i++) {
     for(UA_Int32 i = 0;i<noElements;i++) {
@@ -1037,7 +1041,7 @@ void UA_Array_init(void *p, UA_Int32 noElements, const UA_VTable_Entry *vt) {
     }
     }
 }
 }
 
 
-void UA_Array_delete(void *p, UA_Int32 noElements, const UA_VTable_Entry *vt) {
+void UA_Array_delete(void *p, UA_Int32 noElements, const UA_TypeVTable *vt) {
     UA_Byte *cp = (UA_Byte *)p; // so compilers allow pointer arithmetic
     UA_Byte *cp = (UA_Byte *)p; // so compilers allow pointer arithmetic
     UA_UInt32 memSize = vt->memSize;
     UA_UInt32 memSize = vt->memSize;
     for(UA_Int32 i = 0;i<noElements;i++) {
     for(UA_Int32 i = 0;i<noElements;i++) {
@@ -1048,7 +1052,7 @@ void UA_Array_delete(void *p, UA_Int32 noElements, const UA_VTable_Entry *vt) {
         UA_free(p);
         UA_free(p);
 }
 }
 
 
-UA_StatusCode UA_Array_copy(const void *src, UA_Int32 noElements, const UA_VTable_Entry *vt, void **dst) {
+UA_StatusCode UA_Array_copy(const void *src, UA_Int32 noElements, const UA_TypeVTable *vt, void **dst) {
     UA_StatusCode retval = UA_Array_new(dst, noElements, vt);
     UA_StatusCode retval = UA_Array_new(dst, noElements, vt);
     if(retval)
     if(retval)
         return retval;
         return retval;
@@ -1073,7 +1077,7 @@ UA_StatusCode UA_Array_copy(const void *src, UA_Int32 noElements, const UA_VTabl
 }
 }
 
 
 #ifdef DEBUG
 #ifdef DEBUG
-void UA_Array_print(const void *p, UA_Int32 noElements, const UA_VTable_Entry *vt, FILE *stream) {
+void UA_Array_print(const void *p, UA_Int32 noElements, const UA_TypeVTable *vt, FILE *stream) {
     fprintf(stream, "(%s){", vt->name);
     fprintf(stream, "(%s){", vt->name);
     char     *cp      = (char *)p; // so compilers allow pointer arithmetic
     char     *cp      = (char *)p; // so compilers allow pointer arithmetic
     UA_UInt32 memSize = vt->memSize;
     UA_UInt32 memSize = vt->memSize;

+ 6 - 6
src/ua_types_encoding_binary.c

@@ -13,7 +13,7 @@ static INLINE UA_Boolean is_builtin(const UA_NodeId *typeid ) {
 /*********/
 /*********/
 
 
 /** The data-pointer may be null. Then the array is assumed to be empy. */
 /** The data-pointer may be null. Then the array is assumed to be empy. */
-UA_UInt32 UA_Array_calcSizeBinary(UA_Int32 length, const UA_VTable_Entry *vt, const void *data) {
+UA_UInt32 UA_Array_calcSizeBinary(UA_Int32 length, const UA_TypeVTable *vt, const void *data) {
     if(!data) //empty arrays are encoded with the length member either 0 or -1
     if(!data) //empty arrays are encoded with the length member either 0 or -1
         return sizeof(UA_Int32);
         return sizeof(UA_Int32);
 
 
@@ -28,7 +28,7 @@ UA_UInt32 UA_Array_calcSizeBinary(UA_Int32 length, const UA_VTable_Entry *vt, co
 }
 }
 
 
 /** The data-pointer may be null. Then the array is assumed to be empy. */
 /** The data-pointer may be null. Then the array is assumed to be empy. */
-static UA_UInt32 UA_Array_calcSizeBinary_asExtensionObject(UA_Int32 length, const UA_VTable_Entry *vt, const void *data) {
+static UA_UInt32 UA_Array_calcSizeBinary_asExtensionObject(UA_Int32 length, const UA_TypeVTable *vt, const void *data) {
     UA_UInt32 l = UA_Array_calcSizeBinary(length, vt, data);
     UA_UInt32 l = UA_Array_calcSizeBinary(length, vt, data);
     if(!is_builtin(&vt->typeId))
     if(!is_builtin(&vt->typeId))
         l += 9*length;  // extensionobject header for each element
         l += 9*length;  // extensionobject header for each element
@@ -36,7 +36,7 @@ static UA_UInt32 UA_Array_calcSizeBinary_asExtensionObject(UA_Int32 length, cons
 }
 }
 
 
 /* The src-pointer may be null if the array length is <= 0. */
 /* The src-pointer may be null if the array length is <= 0. */
-UA_StatusCode UA_Array_encodeBinary(const void *src, UA_Int32 length, const UA_VTable_Entry *vt,
+UA_StatusCode UA_Array_encodeBinary(const void *src, UA_Int32 length, const UA_TypeVTable *vt,
                                     UA_ByteString *dst, UA_UInt32 *offset) {
                                     UA_ByteString *dst, UA_UInt32 *offset) {
     //Null Arrays are encoded with length = -1 // part 6 - §5.24
     //Null Arrays are encoded with length = -1 // part 6 - §5.24
     if(length < -1)
     if(length < -1)
@@ -52,7 +52,7 @@ UA_StatusCode UA_Array_encodeBinary(const void *src, UA_Int32 length, const UA_V
     return retval;
     return retval;
 }
 }
 
 
-static UA_StatusCode UA_Array_encodeBinary_asExtensionObject(const void *src, UA_Int32 length, const UA_VTable_Entry *vt,
+static UA_StatusCode UA_Array_encodeBinary_asExtensionObject(const void *src, UA_Int32 length, const UA_TypeVTable *vt,
                                                              UA_ByteString *dst, UA_UInt32 *offset) {
                                                              UA_ByteString *dst, UA_UInt32 *offset) {
     //Null Arrays are encoded with length = -1 // part 6 - §5.24
     //Null Arrays are encoded with length = -1 // part 6 - §5.24
     if(length < -1)
     if(length < -1)
@@ -78,7 +78,7 @@ static UA_StatusCode UA_Array_encodeBinary_asExtensionObject(const void *src, UA
 }
 }
 
 
 UA_StatusCode UA_Array_decodeBinary(const UA_ByteString *src, UA_UInt32 *offset, UA_Int32 length,
 UA_StatusCode UA_Array_decodeBinary(const UA_ByteString *src, UA_UInt32 *offset, UA_Int32 length,
-                                    const UA_VTable_Entry *vt, void **dst) {
+                                    const UA_TypeVTable *vt, void **dst) {
     if(length <= 0) {
     if(length <= 0) {
         *dst = UA_NULL;
         *dst = UA_NULL;
         return UA_STATUSCODE_GOOD;
         return UA_STATUSCODE_GOOD;
@@ -902,7 +902,7 @@ UA_StatusCode UA_Variant_decodeBinary(UA_ByteString const *src, UA_UInt32 *offse
     UA_NodeId typeid = { .namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
     UA_NodeId typeid = { .namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
                          .identifier.numeric = encodingByte & UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK };
                          .identifier.numeric = encodingByte & UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK };
     UA_Int32 typeNs0Id = UA_ns0ToVTableIndex(&typeid );
     UA_Int32 typeNs0Id = UA_ns0ToVTableIndex(&typeid );
-    const UA_VTable_Entry *vt = &UA_TYPES[typeNs0Id];
+    const UA_TypeVTable *vt = &UA_TYPES[typeNs0Id];
 
 
     if(!isArray) {
     if(!isArray) {
         if(!(data->dataPtr = UA_alloc(vt->memSize)))
         if(!(data->dataPtr = UA_alloc(vt->memSize)))

+ 3 - 3
src/ua_types_encoding_binary.h

@@ -94,15 +94,15 @@ UA_TYPE_BINARY_ENCODING(UA_InvalidType)
 /*********/
 /*********/
 
 
 /* Computes the size of an array (incl. length field) in a binary blob. */
 /* Computes the size of an array (incl. length field) in a binary blob. */
-UA_UInt32 UA_Array_calcSizeBinary(UA_Int32 length, const UA_VTable_Entry *vt, const void *data);
+UA_UInt32 UA_Array_calcSizeBinary(UA_Int32 length, const UA_TypeVTable *vt, const void *data);
 
 
 /* @brief Encodes an array into a binary blob. The array size is printed as well. */
 /* @brief Encodes an array into a binary blob. The array size is printed as well. */
-UA_StatusCode UA_Array_encodeBinary(const void *src, UA_Int32 length, const UA_VTable_Entry *vt,
+UA_StatusCode UA_Array_encodeBinary(const void *src, UA_Int32 length, const UA_TypeVTable *vt,
                                     UA_ByteString *dst, UA_UInt32 *offset);
                                     UA_ByteString *dst, UA_UInt32 *offset);
 
 
 /* @brief Decodes an array from a binary blob. The array is allocated automatically before decoding. */
 /* @brief Decodes an array from a binary blob. The array is allocated automatically before decoding. */
 UA_StatusCode UA_Array_decodeBinary(const UA_ByteString *src, UA_UInt32 *offset, UA_Int32 length,
 UA_StatusCode UA_Array_decodeBinary(const UA_ByteString *src, UA_UInt32 *offset, UA_Int32 length,
-                                    const UA_VTable_Entry *vt, void **dst);
+                                    const UA_TypeVTable *vt, void **dst);
 
 
 /// @} /* end of group */
 /// @} /* end of group */
 
 

+ 76 - 0
src/ua_types_internal.h

@@ -0,0 +1,76 @@
+#ifndef UA_TYPES_INTERNAL_H_
+#define UA_TYPES_INTERNAL_H_
+
+/* Macros for type implementations */
+
+#define UA_TYPE_DEFAULT(TYPE)            \
+    UA_TYPE_DELETE_DEFAULT(TYPE)         \
+    UA_TYPE_DELETEMEMBERS_NOACTION(TYPE) \
+    UA_TYPE_INIT_DEFAULT(TYPE)           \
+    UA_TYPE_NEW_DEFAULT(TYPE)            \
+    UA_TYPE_COPY_DEFAULT(TYPE)           \
+    
+#define UA_TYPE_NEW_DEFAULT(TYPE)                             \
+    TYPE * TYPE##_new() {                                     \
+        TYPE *p = UA_alloc(sizeof(TYPE));                     \
+        if(p) TYPE##_init(p);                                 \
+        return p;                                             \
+    }
+
+#define UA_TYPE_INIT_DEFAULT(TYPE) \
+    void TYPE##_init(TYPE * p) {   \
+        *p = (TYPE)0;              \
+    }
+
+#define UA_TYPE_INIT_AS(TYPE, TYPE_AS) \
+    void TYPE##_init(TYPE * p) {       \
+        TYPE_AS##_init((TYPE_AS *)p);  \
+    }
+
+#define UA_TYPE_DELETE_DEFAULT(TYPE) \
+    void TYPE##_delete(TYPE *p) {    \
+        TYPE##_deleteMembers(p);     \
+        UA_free(p);                  \
+    }
+
+#define UA_TYPE_DELETE_AS(TYPE, TYPE_AS) \
+    void TYPE##_delete(TYPE * p) {       \
+        TYPE_AS##_delete((TYPE_AS *)p);  \
+    }
+
+#define UA_TYPE_DELETEMEMBERS_NOACTION(TYPE) \
+    void TYPE##_deleteMembers(TYPE * p) { return; }
+
+#define UA_TYPE_DELETEMEMBERS_AS(TYPE, TYPE_AS) \
+    void TYPE##_deleteMembers(TYPE * p) { TYPE_AS##_deleteMembers((TYPE_AS *)p); }
+
+/* Use only when the type has no arrays. Otherwise, implement deep copy */
+#define UA_TYPE_COPY_DEFAULT(TYPE)                             \
+    UA_StatusCode TYPE##_copy(TYPE const *src, TYPE *dst) {    \
+        *dst = *src;                                           \
+        return UA_STATUSCODE_GOOD;                             \
+    }
+
+#define UA_TYPE_COPY_AS(TYPE, TYPE_AS)                         \
+    UA_StatusCode TYPE##_copy(TYPE const *src, TYPE *dst) {    \
+        return TYPE_AS##_copy((TYPE_AS *)src, (TYPE_AS *)dst); \
+    }
+
+#ifdef DEBUG //print functions only in debug mode
+#define UA_TYPE_PRINT_AS(TYPE, TYPE_AS)              \
+    void TYPE##_print(TYPE const *p, FILE *stream) { \
+        TYPE_AS##_print((TYPE_AS *)p, stream);       \
+    }
+#else
+#define UA_TYPE_PRINT_AS(TYPE, TYPE_AS)
+#endif
+
+#define UA_TYPE_AS(TYPE, TYPE_AS)           \
+    UA_TYPE_NEW_DEFAULT(TYPE)               \
+    UA_TYPE_INIT_AS(TYPE, TYPE_AS)          \
+    UA_TYPE_DELETE_AS(TYPE, TYPE_AS)        \
+    UA_TYPE_DELETEMEMBERS_AS(TYPE, TYPE_AS) \
+    UA_TYPE_COPY_AS(TYPE, TYPE_AS)          \
+    UA_TYPE_PRINT_AS(TYPE, TYPE_AS)
+
+#endif /* UA_TYPES_INTERNAL_H_ */

+ 3 - 0
src/ua_util.c

@@ -7,7 +7,10 @@ extern INLINE void UA_memcpy(void *dst, void const *src, UA_Int32 size);
 #ifdef DEBUG
 #ifdef DEBUG
 extern INLINE void _UA_free(void *ptr, char *pname, char *f, UA_Int32 l);
 extern INLINE void _UA_free(void *ptr, char *pname, char *f, UA_Int32 l);
 extern INLINE void * _UA_alloc(UA_Int32 size, char *file, UA_Int32 line);
 extern INLINE void * _UA_alloc(UA_Int32 size, char *file, UA_Int32 line);
+extern INLINE void * _UA_alloca(UA_Int32 size, char *file, UA_Int32 line);
+
 #else
 #else
 extern INLINE void _UA_free(void *ptr);
 extern INLINE void _UA_free(void *ptr);
 extern INLINE void * _UA_alloc(UA_Int32 size);
 extern INLINE void * _UA_alloc(UA_Int32 size);
+extern INLINE void * _UA_alloca(UA_Int32 size);
 #endif
 #endif

+ 27 - 3
src/ua_util.h

@@ -9,10 +9,13 @@
 #include "ua_config.h"
 #include "ua_config.h"
 
 
 #include <stddef.h> /* Needed for sys/queue.h */
 #include <stddef.h> /* Needed for sys/queue.h */
-#if !defined(MSVC) && !defined(__MINGW32__)
+
+#ifndef WIN32
 #include <sys/queue.h>
 #include <sys/queue.h>
+#include <alloca.h>
 #else
 #else
 #include "queue.h"
 #include "queue.h"
+#include <malloc.h>
 #endif
 #endif
 
 
 #include "ua_types.h"
 #include "ua_types.h"
@@ -51,12 +54,12 @@
 #define UA_free(ptr) _UA_free(ptr, # ptr, __FILE__, __LINE__)
 #define UA_free(ptr) _UA_free(ptr, # ptr, __FILE__, __LINE__)
 INLINE void _UA_free(void *ptr, char *pname, char *f, UA_Int32 l) {
 INLINE void _UA_free(void *ptr, char *pname, char *f, UA_Int32 l) {
     DBG_VERBOSE(printf("UA_free;%p;;%s;;%s;%d\n", ptr, pname, f, l); fflush(stdout));
     DBG_VERBOSE(printf("UA_free;%p;;%s;;%s;%d\n", ptr, pname, f, l); fflush(stdout));
-    free(ptr); // checks if ptr != NULL in the background
+    free(ptr); // checks if ptr != UA_NULL in the background
 }
 }
 #else
 #else
 #define UA_free(ptr) _UA_free(ptr)
 #define UA_free(ptr) _UA_free(ptr)
 INLINE void _UA_free(void *ptr) {
 INLINE void _UA_free(void *ptr) {
-    free(ptr); // checks if ptr != NULL in the background
+    free(ptr); // checks if ptr != UA_NULL in the background
 }
 }
 #endif
 #endif
 
 
@@ -78,4 +81,25 @@ INLINE void UA_memcpy(void *dst, void const *src, UA_Int32 size) {
     memcpy(dst, src, size);
     memcpy(dst, src, size);
 }
 }
 
 
+#ifdef DEBUG
+#define UA_alloca(size) _UA_alloca(size, __FILE__, __LINE__) 
+INLINE void * _UA_alloca(UA_Int32 size, char *file, UA_Int32 line) {
+	DBG_VERBOSE(printf("UA_alloc - %d;%s;%d\n", size, file, line); fflush(stdout));
+#ifdef WIN32
+	return _alloca(size);
+#else
+	return alloca(size);
+#endif
+}
+#else
+#define UA_alloca(size) _UA_alloca(size) 
+INLINE void * _UA_alloca(UA_Int32 size) {
+#ifdef WIN32
+	return _alloca(size);
+#else
+	return alloca(size);
+#endif
+}
+#endif
+
 #endif /* UA_UTIL_H_ */
 #endif /* UA_UTIL_H_ */

+ 0 - 137
tests/check_indexedList.c

@@ -1,137 +0,0 @@
-#include <stdlib.h> // EXIT_SUCCESS
-#include "util/ua_indexedList.h"
-#include "util/ua_util.h"
-#include "check.h"
-
-/* global test counters */
-UA_Int32 visit_count = 0;
-UA_Int32 free_count = 0;
-
-void visitor(void* payload){
-	visit_count++;
-}
-
-void freer(void* payload){
-	if(payload){
-		free_count++;
-		UA_free(payload);
-	}
-}
-
-_Bool matcher(void* payload){
-	if(payload == NULL){
-		return FALSE;
-	}
-	if(*((UA_Int32*)payload) == 42){
-		return TRUE;
-	}
-	return FALSE;
-}
-
-START_TEST(linkedList_test_destroyemptylist)
-{
-	UA_indexedList_List list;
-	UA_indexedList_init(&list);
-	ck_assert_int_eq(list.size, 0);
-	UA_indexedList_destroy(&list, UA_indexedList_defaultFreer);
-	ck_assert_int_eq(list.size, 0);
-}
-END_TEST
-
-START_TEST(linkedList_test_destroysingle)
-{
-	UA_indexedList_List list;
-	UA_indexedList_init(&list);
-	ck_assert_int_eq(list.size, 0);
-	UA_Int32* payload = (UA_Int32*)malloc(sizeof(*payload));
-	*payload = 10;
-	UA_indexedList_addValue(&list, 1, payload);
-	UA_indexedList_destroy(&list, UA_indexedList_defaultFreer);
-	ck_assert_int_eq(list.size, 0);
-}
-END_TEST
-	
-START_TEST(linkedList_test_basic)
-{
-
-	ck_assert_int_eq(UA_indexedList_addValue(UA_NULL, 0, UA_NULL), UA_ERROR);
-	ck_assert_int_eq(UA_indexedList_addValueToFront(UA_NULL, 0, UA_NULL), UA_ERROR);
-	ck_assert_int_eq(UA_indexedList_destroy(UA_NULL, UA_NULL), UA_ERROR);
-	ck_assert_int_eq(UA_indexedList_find(UA_NULL, 0), UA_NULL);
-	ck_assert_int_eq(UA_indexedList_iterateValues(UA_NULL, UA_NULL), UA_ERROR);
-	ck_assert_int_eq(UA_indexedList_removeElement(UA_NULL, 0, UA_NULL), UA_ERROR);
-
-	UA_indexedList_List list;
-	UA_indexedList_init(&list);
-
-	ck_assert_int_eq(list.size, 0);
-
-	UA_Int32* payload = (UA_Int32*)malloc(sizeof(*payload));
-	*payload = 10;
-	UA_indexedList_addValue(&list, 1, payload);
-	payload = (UA_Int32*)malloc(sizeof(*payload));
-	*payload = 20;
-	UA_indexedList_addValueToFront(&list, 2, payload);
-	payload = (UA_Int32*)malloc(sizeof(*payload));
-	*payload = 30;
-	UA_indexedList_addValue(&list, 3, payload);
-
-	ck_assert_int_eq(list.size, 3);
-
-	UA_indexedList_iterateValues(&list, visitor);
-	ck_assert_int_eq(visit_count, 3);
-	visit_count = 0;
-
-	payload = (UA_Int32*)UA_indexedList_findValue(&list, 2);
-	if(payload){
-		ck_assert_int_eq(*payload, 20);
-	}else{
-		fail("Element 20 not found");
-	}
-
-	UA_indexedList_removeElement(&list, UA_indexedList_find(&list, 2), freer);
-	ck_assert_int_eq(free_count, 1);
-	free_count=0;
-	ck_assert_int_eq(list.size, 2);
-
-	payload = (UA_Int32*)UA_indexedList_findValue(&list, 2);
-	if(payload){
-		fail("Previously removed element 20 found");
-	}
-
-	UA_indexedList_iterateValues(&list, visitor);
-	ck_assert_int_eq(visit_count, 2);
-
-	UA_indexedList_destroy(&list, freer);
-
-	ck_assert_int_eq(free_count, 2);
-	ck_assert_int_eq(list.size, 0);
-	
-}
-END_TEST
-
-Suite*linkedList_testSuite(void)
-{
-	Suite *s = suite_create("linkedList_test");
-	TCase *tc_core = tcase_create("Core");
-	tcase_add_test(tc_core, linkedList_test_basic);
-	tcase_add_test(tc_core, linkedList_test_destroyemptylist);
-	tcase_add_test(tc_core, linkedList_test_destroysingle);
-	suite_add_tcase(s,tc_core);
-	return s;
-}
-
-int main (void)
-{
-	int number_failed = 0;
-
-	Suite* s = linkedList_testSuite();
-	SRunner* 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;
-
-}
-

+ 1 - 2
tests/check_memory.c

@@ -195,8 +195,7 @@ int main() {
 	suite_add_tcase(s, tc);
 	suite_add_tcase(s, tc);
 
 
 	sr = srunner_create(s);
 	sr = srunner_create(s);
-	//for debugging puposes only, will break make check
-	srunner_set_fork_status(sr, CK_NOFORK);
+	//srunner_set_fork_status(sr, CK_NOFORK);
 	srunner_run_all (sr, CK_NORMAL);
 	srunner_run_all (sr, CK_NORMAL);
 	number_failed += srunner_ntests_failed(sr);
 	number_failed += srunner_ntests_failed(sr);
 	srunner_free(sr);
 	srunner_free(sr);

+ 71 - 72
tests/check_nodestore.c

@@ -24,8 +24,7 @@ void printVisitor(const UA_Node* node) {
 }
 }
 
 
 START_TEST(test_UA_NodeStore) {
 START_TEST(test_UA_NodeStore) {
-	UA_NodeStore *ns = UA_NULL;
-	UA_NodeStore_new(&ns);
+	UA_NodeStore *ns = UA_NodeStore_new();
 	UA_NodeStore_delete(ns);
 	UA_NodeStore_delete(ns);
 }
 }
 END_TEST
 END_TEST
@@ -44,16 +43,11 @@ START_TEST(findNodeInUA_NodeStoreWithSingleEntry) {
    	rcu_register_thread();
    	rcu_register_thread();
 #endif
 #endif
 	// given
 	// given
-	UA_NodeStore *ns;
-	UA_NodeStore_new(&ns);
+	UA_NodeStore *ns = UA_NodeStore_new();
 	UA_Node* n1; createNode(&n1,0,2253);
 	UA_Node* n1; createNode(&n1,0,2253);
-	UA_NodeStore_insert(ns, &n1, UA_NODESTORE_INSERT_UNIQUE | UA_NODESTORE_INSERT_GETMANAGED);
-	const UA_Node* nr = UA_NULL;
-	UA_Int32 retval;
-	// when
-	retval = UA_NodeStore_get(ns,&n1->nodeId,&nr);
+	UA_NodeStore_insert(ns, (const UA_Node **)&n1, UA_TRUE);
+	const UA_Node* nr = UA_NodeStore_get(ns,&n1->nodeId);
 	// then
 	// then
-	ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 	ck_assert_ptr_eq((void*)nr, (void*)n1);
 	ck_assert_ptr_eq((void*)nr, (void*)n1);
 	// finally
 	// finally
 	UA_NodeStore_release(n1);
 	UA_NodeStore_release(n1);
@@ -70,21 +64,19 @@ START_TEST(failToFindNodeInOtherUA_NodeStore) {
    	rcu_register_thread();
    	rcu_register_thread();
 #endif
 #endif
 	// given
 	// given
-	UA_NodeStore *ns = UA_NULL;
-	UA_NodeStore_new(&ns);
+	UA_NodeStore *ns = UA_NodeStore_new();
 
 
-	UA_Node* n1; createNode(&n1,0,2253); UA_NodeStore_insert(ns, &n1, 0);
-	UA_Node* n2; createNode(&n2,0,2253); UA_NodeStore_insert(ns, &n2, 0);
+	UA_Node* n1; createNode(&n1,0,2253);
+    UA_NodeStore_insert(ns, (const UA_Node **)&n1, UA_FALSE);
+	UA_Node* n2; createNode(&n2,0,2253); UA_NodeStore_insert(ns, (const UA_Node **)&n2, UA_FALSE);
 
 
-	const UA_Node* nr = UA_NULL;
 	// when
 	// when
 	UA_Node* n; createNode(&n,1,2255);
 	UA_Node* n; createNode(&n,1,2255);
-	UA_Int32 retval = UA_NodeStore_get(ns,&n->nodeId, &nr);
+	const UA_Node* nr = UA_NodeStore_get(ns,&n->nodeId);
 	// then
 	// then
-	ck_assert_int_ne(retval, UA_STATUSCODE_GOOD);
+	ck_assert_ptr_eq(nr, UA_NULL);
 	// finally
 	// finally
 	UA_Node_delete(n);
 	UA_Node_delete(n);
-	UA_NodeStore_release(nr);
 	UA_NodeStore_delete(ns);
 	UA_NodeStore_delete(ns);
 #ifdef MULTITHREADING
 #ifdef MULTITHREADING
 	rcu_unregister_thread();
 	rcu_unregister_thread();
@@ -97,21 +89,23 @@ START_TEST(findNodeInUA_NodeStoreWithSeveralEntries) {
    	rcu_register_thread();
    	rcu_register_thread();
 #endif
 #endif
 	// given
 	// given
-	UA_NodeStore *ns;
-	UA_NodeStore_new(&ns);
-	UA_Node* n1; createNode(&n1,0,2253); UA_NodeStore_insert(ns, &n1, 0);
-	UA_Node* n2; createNode(&n2,0,2255); UA_NodeStore_insert(ns, &n2, 0);
-	UA_Node* n3; createNode(&n3,0,2257); UA_NodeStore_insert(ns, &n3, UA_NODESTORE_INSERT_GETMANAGED);
-	UA_Node* n4; createNode(&n4,0,2200); UA_NodeStore_insert(ns, &n4, 0);
-	UA_Node* n5; createNode(&n5,0,1); UA_NodeStore_insert(ns, &n5, 0);
-	UA_Node* n6; createNode(&n6,0,12); UA_NodeStore_insert(ns, &n6, 0);
-
-	const UA_Node* nr = UA_NULL;
-	UA_Int32 retval;
+	UA_NodeStore *ns = UA_NodeStore_new();
+	UA_Node* n1; createNode(&n1,0,2253);
+    UA_NodeStore_insert(ns, (const UA_Node **)&n1, UA_FALSE);
+	UA_Node* n2; createNode(&n2,0,2255);
+    UA_NodeStore_insert(ns, (const UA_Node **)&n2, UA_FALSE);
+	UA_Node* n3; createNode(&n3,0,2257);
+    UA_NodeStore_insert(ns, (const UA_Node **)&n3, UA_TRUE);
+	UA_Node* n4; createNode(&n4,0,2200);
+    UA_NodeStore_insert(ns, (const UA_Node **)&n4, UA_FALSE);
+	UA_Node* n5; createNode(&n5,0,1);
+    UA_NodeStore_insert(ns, (const UA_Node **)&n5, UA_FALSE);
+	UA_Node* n6; createNode(&n6,0,12);
+    UA_NodeStore_insert(ns, (const UA_Node **)&n6, UA_FALSE);
+
 	// when
 	// when
-	retval = UA_NodeStore_get(ns,&(n3->nodeId),&nr);
+	const UA_Node* nr = UA_NodeStore_get(ns,&(n3->nodeId));
 	// then
 	// then
-	ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 	ck_assert_ptr_eq((void*)nr, (void*)n3);
 	ck_assert_ptr_eq((void*)nr, (void*)n3);
 	// finally
 	// finally
 	UA_NodeStore_release(n3);
 	UA_NodeStore_release(n3);
@@ -128,14 +122,19 @@ START_TEST(iterateOverUA_NodeStoreShallNotVisitEmptyNodes) {
    	rcu_register_thread();
    	rcu_register_thread();
 #endif
 #endif
 	// given
 	// given
-	UA_NodeStore *ns;
-	UA_NodeStore_new(&ns);
-	UA_Node* n1; createNode(&n1,0,2253); UA_NodeStore_insert(ns, &n1, 0);
-	UA_Node* n2; createNode(&n2,0,2255); UA_NodeStore_insert(ns, &n2, 0);
-	UA_Node* n3; createNode(&n3,0,2257); UA_NodeStore_insert(ns, &n3, 0);
-	UA_Node* n4; createNode(&n4,0,2200); UA_NodeStore_insert(ns, &n4, 0);
-	UA_Node* n5; createNode(&n5,0,1); UA_NodeStore_insert(ns, &n5, 0);
-	UA_Node* n6; createNode(&n6,0,12); UA_NodeStore_insert(ns, &n6, 0);
+	UA_NodeStore *ns = UA_NodeStore_new();
+	UA_Node* n1; createNode(&n1,0,2253);
+    UA_NodeStore_insert(ns, (const UA_Node **)&n1, 0);
+	UA_Node* n2; createNode(&n2,0,2255);
+    UA_NodeStore_insert(ns, (const UA_Node **)&n2, 0);
+	UA_Node* n3; createNode(&n3,0,2257);
+    UA_NodeStore_insert(ns, (const UA_Node **)&n3, 0);
+	UA_Node* n4; createNode(&n4,0,2200);
+    UA_NodeStore_insert(ns, (const UA_Node **)&n4, 0);
+	UA_Node* n5; createNode(&n5,0,1);
+    UA_NodeStore_insert(ns, (const UA_Node **)&n5, 0);
+	UA_Node* n6; createNode(&n6,0,12);
+    UA_NodeStore_insert(ns, (const UA_Node **)&n6, 0);
 
 
 	// when
 	// when
 	zeroCnt = 0;
 	zeroCnt = 0;
@@ -157,20 +156,17 @@ START_TEST(findNodeInExpandedNamespace) {
    	rcu_register_thread();
    	rcu_register_thread();
 #endif
 #endif
 	// given
 	// given
-	UA_NodeStore *ns;
-	UA_NodeStore_new(&ns);
+	UA_NodeStore *ns = UA_NodeStore_new();
 	UA_Node* n;
 	UA_Node* n;
 	UA_Int32 i=0;
 	UA_Int32 i=0;
 	for (; i<200; i++) {
 	for (; i<200; i++) {
-		createNode(&n,0,i); UA_NodeStore_insert(ns, &n, 0);
+		createNode(&n,0,i);
+        UA_NodeStore_insert(ns, (const UA_Node **)&n, UA_FALSE);
 	}
 	}
-	const UA_Node* nr = UA_NULL;
-	UA_Int32 retval;
 	// when
 	// when
 	createNode(&n,0,25);
 	createNode(&n,0,25);
-	retval = UA_NodeStore_get(ns,&(n->nodeId),&nr);
+	const UA_Node* nr = UA_NodeStore_get(ns,&(n->nodeId));
 	// then
 	// then
-	ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 	ck_assert_int_eq(nr->nodeId.identifier.numeric,n->nodeId.identifier.numeric);
 	ck_assert_int_eq(nr->nodeId.identifier.numeric,n->nodeId.identifier.numeric);
 	// finally
 	// finally
 	UA_free((void*)n);
 	UA_free((void*)n);
@@ -187,12 +183,12 @@ START_TEST(iterateOverExpandedNamespaceShallNotVisitEmptyNodes) {
    	rcu_register_thread();
    	rcu_register_thread();
 #endif
 #endif
 	// given
 	// given
-	UA_NodeStore *ns;
-	UA_NodeStore_new(&ns);
+	UA_NodeStore *ns = UA_NodeStore_new();
 	UA_Node* n;
 	UA_Node* n;
 	UA_Int32 i=0;
 	UA_Int32 i=0;
 	for (; i<200; i++) {
 	for (; i<200; i++) {
-		createNode(&n,0,i); UA_NodeStore_insert(ns, &n, 0);
+		createNode(&n,0,i);
+        UA_NodeStore_insert(ns, (const UA_Node **)&n, UA_FALSE);
 	}
 	}
 	// when
 	// when
 	zeroCnt = 0;
 	zeroCnt = 0;
@@ -214,21 +210,23 @@ START_TEST(failToFindNonExistantNodeInUA_NodeStoreWithSeveralEntries) {
    	rcu_register_thread();
    	rcu_register_thread();
 #endif
 #endif
 	// given
 	// given
-	UA_NodeStore *ns;
-	UA_NodeStore_new(&ns);
-	UA_Node* n1; createNode(&n1,0,2253); UA_NodeStore_insert(ns, &n1, 0);
-	UA_Node* n2; createNode(&n2,0,2255); UA_NodeStore_insert(ns, &n2, 0);
-	UA_Node* n3; createNode(&n3,0,2257); UA_NodeStore_insert(ns, &n3, 0);
-	UA_Node* n4; createNode(&n4,0,2200); UA_NodeStore_insert(ns, &n4, 0);
-	UA_Node* n5; createNode(&n5,0,1); UA_NodeStore_insert(ns, &n5, 0);
+	UA_NodeStore *ns = UA_NodeStore_new();
+	UA_Node* n1; createNode(&n1,0,2253);
+    UA_NodeStore_insert(ns, (const UA_Node **)&n1, UA_FALSE);
+	UA_Node* n2; createNode(&n2,0,2255);
+    UA_NodeStore_insert(ns, (const UA_Node **)&n2, UA_FALSE);
+	UA_Node* n3; createNode(&n3,0,2257);
+    UA_NodeStore_insert(ns, (const UA_Node **)&n3, UA_FALSE);
+	UA_Node* n4; createNode(&n4,0,2200);
+    UA_NodeStore_insert(ns, (const UA_Node **)&n4, UA_FALSE);
+	UA_Node* n5; createNode(&n5,0,1);
+    UA_NodeStore_insert(ns, (const UA_Node **)&n5, UA_FALSE);
 	UA_Node* n6; createNode(&n6,0,12); 
 	UA_Node* n6; createNode(&n6,0,12); 
 
 
-	const UA_Node* nr = UA_NULL;
-	UA_Int32 retval;
 	// when
 	// when
-	retval = UA_NodeStore_get(ns, &(n6->nodeId), &nr);
+	const UA_Node* nr = UA_NodeStore_get(ns, &n6->nodeId);
 	// then
 	// then
-	ck_assert_int_ne(retval, UA_STATUSCODE_GOOD);
+	ck_assert_ptr_eq(nr, UA_NULL);
 	// finally
 	// finally
 	UA_free((void *)n6);
 	UA_free((void *)n6);
 	UA_NodeStore_delete(ns);
 	UA_NodeStore_delete(ns);
@@ -260,7 +258,7 @@ void *profileGetThread(void *arg) {
 	for(UA_Int32 x = 0; x<test->rounds; x++) {
 	for(UA_Int32 x = 0; x<test->rounds; x++) {
 		for (UA_Int32 i=test->min_val; i<max_val; i++) {
 		for (UA_Int32 i=test->min_val; i<max_val; i++) {
 			id.identifier.numeric = i;
 			id.identifier.numeric = i;
-			UA_NodeStore_get(ns,&id, &cn);
+			cn = UA_NodeStore_get(ns,&id);
 			UA_NodeStore_release(cn);
 			UA_NodeStore_release(cn);
 		}
 		}
 	}
 	}
@@ -276,12 +274,12 @@ START_TEST(profileGetDelete) {
 #endif
 #endif
 
 
 #define N 1000000
 #define N 1000000
-	UA_NodeStore *ns;
-	UA_NodeStore_new(&ns);
+	UA_NodeStore *ns = UA_NodeStore_new();
 	UA_Int32 i=0;
 	UA_Int32 i=0;
 	UA_Node *n;
 	UA_Node *n;
 	for (; i<N; i++) {
 	for (; i<N; i++) {
-		createNode(&n,0,i); UA_NodeStore_insert(ns, &n, 0);
+		createNode(&n,0,i);
+        UA_NodeStore_insert(ns, (const UA_Node **)&n, UA_FALSE);
 	}
 	}
 	clock_t begin, end;
 	clock_t begin, end;
 	begin = clock();
 	begin = clock();
@@ -299,11 +297,12 @@ START_TEST(profileGetDelete) {
 	printf("Time for %d create/get/delete on %d threads in a namespace: %fs.\n", N, THREADS, (double)(end - begin) / CLOCKS_PER_SEC);
 	printf("Time for %d create/get/delete on %d threads in a namespace: %fs.\n", N, THREADS, (double)(end - begin) / CLOCKS_PER_SEC);
 #else
 #else
 	const UA_Node *cn;
 	const UA_Node *cn;
-	UA_NodeId id = NS0NODEID(0);
+	UA_NodeId id;
+    UA_NodeId_init(&id);
 	for(UA_Int32 x = 0; x<50; x++) {
 	for(UA_Int32 x = 0; x<50; x++) {
 	    for(i=0; i<N; i++) {
 	    for(i=0; i<N; i++) {
 	        id.identifier.numeric = i;
 	        id.identifier.numeric = i;
-			UA_NodeStore_get(ns,&id, &cn);
+			cn = UA_NodeStore_get(ns,&id);
 			UA_NodeStore_release(cn);
 			UA_NodeStore_release(cn);
         }
         }
     }
     }
@@ -348,12 +347,12 @@ Suite * namespace_suite (void) {
 
 
 
 
 int main (void) {
 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);
+	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);
 	number_failed += srunner_ntests_failed (sr);
-	srunner_free (sr);
+	srunner_free(sr);
 	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
 	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
 }
 }

+ 28 - 17
tools/generate_builtin.py

@@ -45,9 +45,9 @@ from type_lists import only_needed_types
 # some types are omitted (pretend they exist already)
 # some types are omitted (pretend they exist already)
 existing_types.add("NodeIdType")
 existing_types.add("NodeIdType")
 
 
-fixed_size = set(["UA_Boolean", "UA_SByte", "UA_Byte", "UA_Int16", "UA_UInt16",
-                  "UA_Int32", "UA_UInt32", "UA_Int64", "UA_UInt64", "UA_Float",
-                  "UA_Double", "UA_DateTime", "UA_Guid", "UA_StatusCode"])
+fixed_size = {"UA_Boolean": 1, "UA_SByte": 1, "UA_Byte": 1, "UA_Int16": 2, "UA_UInt16": 2,
+              "UA_Int32": 4, "UA_UInt32": 4, "UA_Int64": 8, "UA_UInt64": 8, "UA_Float": 4,
+              "UA_Double": 8, "UA_DateTime": 8, "UA_Guid": 16, "UA_StatusCode": 4}
 
 
 # types we do not want to autogenerate
 # types we do not want to autogenerate
 def skipType(name):
 def skipType(name):
@@ -81,7 +81,7 @@ def printableStructuredType(element):
 def createEnumerated(element):	
 def createEnumerated(element):	
     valuemap = OrderedDict()
     valuemap = OrderedDict()
     name = "UA_" + element.get("Name")
     name = "UA_" + element.get("Name")
-    fixed_size.add(name)
+    fixed_size[name] = 4
     printh("") # newline
     printh("") # newline
     for child in element:
     for child in element:
         if child.tag == "{http://opcfoundation.org/BinarySchema/}Documentation":
         if child.tag == "{http://opcfoundation.org/BinarySchema/}Documentation":
@@ -148,6 +148,18 @@ def createStructured(element):
             else:
             else:
                 membermap[childname] = "UA_" + typename
                 membermap[childname] = "UA_" + typename
 
 
+    # fixed size?
+    is_fixed_size = True
+    this_fixed_size = 0
+    for n,t in membermap.iteritems():
+        if t not in fixed_size.keys():
+            is_fixed_size = False
+        else:
+            this_fixed_size += fixed_size[t]
+
+    if is_fixed_size:
+        fixed_size[name] = this_fixed_size
+
     # 3) Print structure
     # 3) Print structure
     if len(membermap) > 0:
     if len(membermap) > 0:
         printh("typedef struct %(name)s {")
         printh("typedef struct %(name)s {")
@@ -168,20 +180,18 @@ def createStructured(element):
     # 4) CalcSizeBinary
     # 4) CalcSizeBinary
     printc('''UA_UInt32 %(name)s_calcSizeBinary(%(name)s const * ptr) {
     printc('''UA_UInt32 %(name)s_calcSizeBinary(%(name)s const * ptr) {
     return 0''')
     return 0''')
-    has_fixed_size = True
-    for n,t in membermap.iteritems():
-        if t in fixed_size:
-            printc('\t + sizeof(%(t)s) // %(n)s')
-        elif t.find("*") != -1:
-            printc('\t + UA_Array_calcSizeBinary(ptr->%(n)sSize,&UA_TYPES['+ t[0:t.find("*")].upper() +
-                   "],ptr->%(n)s)")
-            has_fixed_size = False
-        else:
-            printc('\t + %(t)s_calcSizeBinary(&ptr->%(n)s)')
-            has_fixed_size = False
+    if is_fixed_size:
+        printc('''+ %(this_fixed_size)s''')
+    else:
+        for n,t in membermap.iteritems():
+            if t in fixed_size:
+                printc('\t + sizeof(%(t)s) // %(n)s')
+            elif t.find("*") != -1:
+                printc('\t + UA_Array_calcSizeBinary(ptr->%(n)sSize,&UA_TYPES[' +
+                       t[0:t.find("*")].upper() + "],ptr->%(n)s)")
+            else:
+                printc('\t + %(t)s_calcSizeBinary(&ptr->%(n)s)')
     printc("\t;\n}\n")
     printc("\t;\n}\n")
-    if has_fixed_size:
-        fixed_size.add(name)
 
 
     # 5) EncodeBinary
     # 5) EncodeBinary
     printc('''UA_StatusCode %(name)s_encodeBinary(%(name)s const * src, UA_ByteString* dst, UA_UInt32 *offset) {
     printc('''UA_StatusCode %(name)s_encodeBinary(%(name)s const * src, UA_ByteString* dst, UA_UInt32 *offset) {
@@ -318,6 +328,7 @@ printc('''/**
  */
  */
  
  
 #include "''' + args.outfile.split("/")[-1] + '''.h"
 #include "''' + args.outfile.split("/")[-1] + '''.h"
+#include "ua_types_internal.h"
 #include "ua_namespace_0.h"
 #include "ua_namespace_0.h"
 #include "ua_util.h"\n''')
 #include "ua_util.h"\n''')
 
 

+ 69 - 46
tools/generate_namespace.py

@@ -19,7 +19,6 @@ args = parser.parse_args()
 from type_lists import only_needed_types
 from type_lists import only_needed_types
 
 
 # types that are to be excluded
 # types that are to be excluded
-exclude_kinds = set(["Object","ObjectType","Variable","Method","ReferenceType"])
 exclude_types = set(["Number", "Integer", "UInteger", "Enumeration", "Image", "ImageBMP",
 exclude_types = set(["Number", "Integer", "UInteger", "Enumeration", "Image", "ImageBMP",
                      "ImageGIF", "ImageJPG", "ImagePNG", "References", "BaseVariableType",
                      "ImageGIF", "ImageJPG", "ImagePNG", "References", "BaseVariableType",
                      "BaseDataVariableType", "PropertyType", "DataTypeDescriptionType",
                      "BaseDataVariableType", "PropertyType", "DataTypeDescriptionType",
@@ -60,20 +59,49 @@ fixed_size = ['UA_DeadbandType', 'UA_DataChangeTrigger', 'UA_Guid', 'UA_Applicat
               'UA_ExceptionDeviationFormat', 'UA_ComplianceLevel', 'UA_DateTime', 'UA_Int64',
               'UA_ExceptionDeviationFormat', 'UA_ComplianceLevel', 'UA_DateTime', 'UA_Int64',
               'UA_Double']
               'UA_Double']
 
 
-def skipType(row):
-    if row[0] == "" or row[0] in exclude_types or row[2] in exclude_kinds:
-        return True
+def useDataType(row):
+    if row[0] == "" or row[0] in exclude_types:
+        return False
+    if row[2] != "DataType":
+        return False
     if "Test" in row[0]:
     if "Test" in row[0]:
-        return True
+        return False
     if args.only_needed and not(row[0] in only_needed_types):
     if args.only_needed and not(row[0] in only_needed_types):
-        return True
-    return False
+        return False
+    return True
+
+type_names = [] # the names of the datattypes for which we create a vtable entry
+def useOtherType(row):
+    if row[0] in type_names or row[0] == "":
+        return False
+    if row[0].startswith("Audit"):
+        return False
+    if row[0].startswith("Program"):
+        return False
+    if row[0].startswith("Condition"):
+        return False
+    if row[0].startswith("Refresh"):
+        return False
+    if row[0].startswith("OpcUa_"):
+        return False
+    if row[0].startswith("SessionsDiagnosticsSummaryType_"):
+        return False
+    if "Type_" in row[0]:
+        return False
+    if "_Encoding_Default" in row[0]:
+        return False
+    return True
 
 
 f = open(args.nodeids)
 f = open(args.nodeids)
-input_str = f.read() + "\nInvalidType,0,DataType"
+input_str = f.read() + "\nInvalidType,0,DataType" + "\nHasModelParent,50,ReferenceType"
+f.close()
 input_str = input_str.replace('\r','')
 input_str = input_str.replace('\r','')
 rows = map(lambda x:tuple(x.split(',')), input_str.split('\n'))
 rows = map(lambda x:tuple(x.split(',')), input_str.split('\n'))
-f.close()
+for index, row in enumerate(rows):
+    if row[0] == "BaseDataType":
+    	rows[index]= ("Variant", row[1], row[2])
+    elif row[0] == "Structure":
+    	rows[index] = ("ExtensionObject", row[1], row[2])
 
 
 fh = open(args.outfile + ".h",'w')
 fh = open(args.outfile + ".h",'w')
 fc = open(args.outfile + ".c",'w')
 fc = open(args.outfile + ".c",'w')
@@ -104,8 +132,9 @@ printh('''/**********************************************************
  */
  */
 
 
 UA_Int32 UA_ns0ToVTableIndex(const UA_NodeId *id);\n
 UA_Int32 UA_ns0ToVTableIndex(const UA_NodeId *id);\n
-extern const UA_VTable_Entry UA_EXPORT *UA_TYPES;
+extern const UA_TypeVTable UA_EXPORT *UA_TYPES;
 extern const UA_NodeId UA_EXPORT *UA_NODEIDS;
 extern const UA_NodeId UA_EXPORT *UA_NODEIDS;
+extern const UA_ExpandedNodeId UA_EXPORT *UA_EXPANDEDNODEIDS;
 
 
 /** The entries of UA_TYPES can be accessed with the following indices */ ''')
 /** The entries of UA_TYPES can be accessed with the following indices */ ''')
 
 
@@ -124,48 +153,34 @@ UA_Int32 UA_ns0ToVTableIndex(const UA_NodeId *id) {
 	switch (id->identifier.numeric) {''')
 	switch (id->identifier.numeric) {''')
 
 
 i = 0
 i = 0
-for row in rows:
-    if skipType(row):
+for index, row in enumerate(rows):
+    if not useDataType(row):
         continue
         continue
-    if row[0] == "BaseDataType":
-    	name = "UA_Variant"
-    elif row[0] == "Structure":
-        name = "UA_ExtensionObject"
-    else:
-        name = "UA_" + row[0]
-	
+    type_names.append(row[0])
+    name = "UA_" + row[0]
     printh("#define "+name.upper()+" "+str(i))
     printh("#define "+name.upper()+" "+str(i))
+    printh('#define '+name.upper()+'_NS0 '+row[1])
     printc('\tcase '+row[1]+': retval='+name.upper()+'; break; //'+row[2])
     printc('\tcase '+row[1]+': retval='+name.upper()+'; break; //'+row[2])
     i = i+1
     i = i+1
+printc('''\t}\n\treturn retval;\n}\n''');
 
 
 printh('\n#define SIZE_UA_VTABLE '+str(i));
 printh('\n#define SIZE_UA_VTABLE '+str(i));
 printh("") # newline
 printh("") # newline
-printh("/** In UA_NODEIDS are the nodeids of the types, referencetypes and objects */")
-# assign indices to the reference types afterwards
+
+printh("/* Now all the non-datatype nodeids */")
 for row in rows:
 for row in rows:
-    if row[0] == "" or (row[2] != "ReferenceType" and (row[2] != "Object" or "_Encoding_Default" in row[0])):
+    if not useOtherType(row):
         continue
         continue
     name = "UA_" + row[0]
     name = "UA_" + row[0]
     printh("#define "+name.upper()+" "+str(i))
     printh("#define "+name.upper()+" "+str(i))
+    printh('#define '+name.upper()+'_NS0 '+row[1])
     i=i+1
     i=i+1
 
 
-printc('''\n}\nreturn retval;\n}\n''');
-
-printh("") # newline
-printh("/** These are the actual (numeric) nodeids of the types, not the indices to the vtable */")
-
-printc('''const UA_VTable_Entry *UA_TYPES = (UA_VTable_Entry[]){''')
+printc('''const UA_TypeVTable *UA_TYPES = (UA_TypeVTable[]){''')
 for row in rows:
 for row in rows:
-    if skipType(row):
+    if row[0] not in type_names:
         continue
         continue
-    if row[0] == "BaseDataType":
-        name = "UA_Variant"
-    elif row[0] == "Structure":
-        name = "UA_ExtensionObject"
-    else:
-	name = "UA_" + row[0]
-    printh('#define '+name.upper()+'_NS0 '+row[1])
-
+    name = "UA_" + row[0]
     printc("\t{.typeId={.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric=" + row[1] + "}" + 
     printc("\t{.typeId={.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric=" + row[1] + "}" + 
            ",\n.name=(UA_Byte*)&\"%(name)s\"" +
            ",\n.name=(UA_Byte*)&\"%(name)s\"" +
            ",\n.new=(void *(*)())%(name)s_new" +
            ",\n.new=(void *(*)())%(name)s_new" +
@@ -186,26 +201,34 @@ for row in rows:
             ",\n.decode=(UA_Int32(*)(const UA_ByteString*,UA_UInt32*,void*))%(name)s_decodeXml}" if (args.with_xml) else "") +
             ",\n.decode=(UA_Int32(*)(const UA_ByteString*,UA_UInt32*,void*))%(name)s_decodeXml}" if (args.with_xml) else "") +
            "}},")
            "}},")
 
 
-printc('};')
+printc('};\n')
 
 
 # make the nodeids available as well
 # make the nodeids available as well
 printc('''const UA_NodeId *UA_NODEIDS = (UA_NodeId[]){''')
 printc('''const UA_NodeId *UA_NODEIDS = (UA_NodeId[]){''')
 for row in rows:
 for row in rows:
-    if skipType(row):
+    if not row[0] in type_names:
         continue
         continue
-    if row[0] == "BaseDataType":
-        name = "UA_Variant"
-    elif row[0] == "Structure":
-        name = "UA_ExtensionObject"
-    else:
-	name = "UA_" + row[0]
     printc("\t{.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = " + row[1] + "},")
     printc("\t{.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = " + row[1] + "},")
 
 
 for row in rows:
 for row in rows:
-    if row[0] == "" or (row[2] != "ReferenceType" and (row[2] != "Object" or "_Encoding_Default" in row[0])):
+    if not useOtherType(row):
         continue
         continue
     printc("\t{.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = " + row[1] + "},")
     printc("\t{.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = " + row[1] + "},")
+printc('};\n')
+
+# and generate expanded nodeids
+printc('''const UA_ExpandedNodeId *UA_EXPANDEDNODEIDS = (UA_ExpandedNodeId[]){''')
+for row in rows:
+    if not row[0] in type_names:
+        continue
+    printc("\t{.nodeId = {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = " + row[1] +
+           "}, .namespaceUri = {.length = -1, .data = UA_NULL}, .serverIndex = 0},")
 
 
+for row in rows:
+    if not useOtherType(row):
+        continue
+    printc("\t{.nodeId = {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = " + row[1] +
+           "}, .namespaceUri = {.length = -1, .data = UA_NULL}, .serverIndex = 0},")
 printc('};')
 printc('};')
 
 
 printh('\n#endif /* OPCUA_NAMESPACE_0_H_ */')
 printh('\n#endif /* OPCUA_NAMESPACE_0_H_ */')

+ 4 - 3
tools/type_lists.py

@@ -19,7 +19,8 @@ only_needed_types = set([	"InvalidType", "Node", "NodeClass", "ReferenceNode", "
 							"RelativePath", "BrowsePathTarget", "RelativePathElement", "CreateSubscriptionRequest", "CreateSubscriptionResponse",
 							"RelativePath", "BrowsePathTarget", "RelativePathElement", "CreateSubscriptionRequest", "CreateSubscriptionResponse",
 							"BrowseResponse", "BrowseResult", "ReferenceDescription", "BrowseRequest", "ViewDescription", "BrowseDescription",
 							"BrowseResponse", "BrowseResult", "ReferenceDescription", "BrowseRequest", "ViewDescription", "BrowseDescription",
 							"BrowseDirection", "CloseSessionRequest", "AddNodesRequest", "AddNodesResponse", "AddNodesItem", "AddNodesResult",
 							"BrowseDirection", "CloseSessionRequest", "AddNodesRequest", "AddNodesResponse", "AddNodesItem", "AddNodesResult",
-							"AddReferencesRequest", "AddReferencesResponse", "AddReferencesItem", "VariableNode", "MethodNode", "VariableTypeNode",
-							"ViewNode", "ReferenceTypeNode", "BrowseResultMask", "ServerState", "ServerStatusDataType", "BuildInfo", "ObjectNode",
-							"DataTypeNode", "ObjectTypeNode", "IdType", "VariableAttributes", "NodeAttributesMask" ])
+							"DeleteNodesItem","AddReferencesRequest", "AddReferencesResponse", "AddReferencesItem","DeleteReferencesItem", "VariableNode",
+							"MethodNode", "VariableTypeNode", "ViewNode", "ReferenceTypeNode", "BrowseResultMask", "ServerState", "ServerStatusDataType",
+							"BuildInfo", "ObjectNode", "DataTypeNode", "ObjectTypeNode", "IdType", "VariableAttributes","ObjectAttributes","NodeAttributes","ReferenceTypeAttributes", "ViewAttributes", "ObjectTypeAttributes", "NodeAttributesMask","DeleteNodesItem",
+							"DeleteNodesRequest", "DeleteNodesResponse", "DeleteReferencesItem", "DeleteReferencesRequest", "DeleteReferencesResponse"])
 only_needed_types = only_needed_types.union(existing_types)
 only_needed_types = only_needed_types.union(existing_types)