소스 검색

Merge pull request #139 from acplt/external_datasource

Merging in .. keeping the history of the branch intact
Julius Pfrommer 10 년 전
부모
커밋
b87f71f764

+ 1 - 0
.gitignore

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

+ 5 - 1
CMakeLists.txt

@@ -25,9 +25,10 @@ set(lib_sources src/ua_types.c
                 src/ua_session.c
                 src/ua_util.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_session_manager.c
-                src/server/ua_server_binary.c
                 src/server/ua_services_attribute.c
                 src/server/ua_services_session.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") 
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.c
                           ${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
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_builtin.py
                            ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/Opc.Ua.Types.bsd)
 
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_namespace_0.c
                           ${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
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_namespace.py
                            ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/NodeIds.csv)
 
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.c
                           ${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
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_namespace.py
                            ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/Custom.Opc.Ua.Transport.bsd)

+ 33 - 31
examples/networklayer_tcp.c

@@ -4,6 +4,7 @@
  */
 
 #ifdef WIN32
+#include <malloc.h>
 #include <winsock2.h>
 #include <sys/types.h>
 #include <Windows.h>
@@ -62,6 +63,37 @@ NetworklayerTCP *NetworklayerTCP_new(UA_ConnectionConfig localConf, UA_UInt32 po
 	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
 // actual socket.
 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;
 }
 
-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 */
 void closeCallback(TCPConnectionHandle *handle) {
 	shutdown(handle->sockfd,2);
@@ -114,7 +134,7 @@ void writeCallback(TCPConnectionHandle *handle, UA_ByteStringArray gather_buf) {
 	UA_UInt32 total_len = 0;
 	UA_UInt32 nWritten = 0;
 #ifdef WIN32
-	LPWSABUF buf = malloc(gather_buf.stringsSize * sizeof(WSABUF));
+	LPWSABUF buf = _alloca(gather_buf.stringsSize * sizeof(WSABUF));
 	int result = 0;
 	for(UA_UInt32 i = 0; i<gather_buf.stringsSize; i++) {
 		buf[i].buf = gather_buf.strings[i].data;
@@ -130,7 +150,6 @@ void writeCallback(TCPConnectionHandle *handle, UA_ByteStringArray gather_buf) {
 		} while (errno == EINTR);
 		nWritten += n;
 	}
-	free(buf);
 #else
 	struct iovec iov[gather_buf.stringsSize];
 	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);
 }
 
-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) {
 #ifdef WIN32
 	u_long iMode = 1;

+ 14 - 12
examples/opcuaServer.c

@@ -48,22 +48,23 @@ UA_ByteString loadCertificate() {
 
     return certificate;
 }
-
 int main(int argc, char** argv) {
 	signal(SIGINT, stopHandler); /* catches ctrl-c */
 
 	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);
-	//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
     UA_UInt32 nodeCount = 500;
     UA_Int32 data = 42;
@@ -81,7 +82,8 @@ int main(int argc, char** argv) {
         tmpNode->value.storage.data.dataPtr = &data;
         tmpNode->value.storageType = UA_VARIANT_DATA_NODELETE;
         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
 	
@@ -93,5 +95,5 @@ int main(int argc, char** argv) {
 	UA_Server_delete(server);
 	NetworklayerTCP_delete(nl);
     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_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;
 typedef struct UA_Server UA_Server;
 
 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_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
 } // 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_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 {
     UA_Int32  arrayLength;        // total number of elements in the data-pointer
     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. */
 } 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 {
-    const UA_VTable_Entry *vt; // internal entry into vTable
+    const UA_TypeVTable *vt; /// The VTable of the datatype in question
     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;
     union {
         UA_VariantData       data;
@@ -241,8 +229,6 @@ typedef struct UA_Variant {
     } storage;
 } UA_Variant;
 
-/** @} */
-
 /** @brief A data value with an associated status code and timestamps. */
 typedef struct UA_DataValue {
     UA_Byte       encodingMask;
@@ -344,6 +330,10 @@ UA_TYPE_PROTOTYPES(UA_Variant)
 UA_TYPE_PROTOTYPES(UA_DiagnosticInfo)
 UA_TYPE_PROTOTYPES(UA_InvalidType)
 
+/**********************************************/
+/* Custom functions for the builtin datatypes */
+/**********************************************/
+
 /* String */
 #define UA_STRING_NULL (UA_String) {-1, (UA_Byte*)0 }
 #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 */
 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_isBasicType(UA_NodeId const *id);
-
-#define NS0NODEID(NUMERIC_ID)                                                                        \
-    (UA_NodeId) {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = \
-                     NUMERIC_ID }
 
 /* ExpandedNodeId */
 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 */
 #define UA_QUALIFIEDNAME_STATIC(VARIABLE, STRING) do { \
         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);
 
 /* 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 */
-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
-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
 
 /**********/
@@ -450,7 +430,7 @@ typedef struct UA_Encoding {
 
 #define UA_ENCODING_BINARY 0 // Binary encoding is always available
 
-struct UA_VTable_Entry {
+struct UA_TypeVTable {
     UA_NodeId     typeId;
     UA_Byte       *name;
     void *        (*new)();
@@ -461,88 +441,12 @@ struct UA_VTable_Entry {
 #ifdef DEBUG
     void          (*print)(const void *p, FILE *stream);
 #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
 };
 
-/***********************************/
-/* 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
 } // 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;
 
 	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_SetElementHandler(parser, XML_Stack_startElement, XML_Stack_endElement);
 	XML_SetCharacterDataHandler(parser, XML_Stack_handleText);

+ 228 - 158
src/server/ua_nodestore.c

@@ -2,16 +2,30 @@
 #include "ua_util.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 {
-    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
    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[] = {
     7,         13,         31,         61,         127,         251,
     509,       1021,       2039,       4093,       8191,        16381,
@@ -20,13 +34,8 @@ static hash_t const primes[] = {
     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) {
     UA_UInt16 low  = 0;
     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) */
-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 uint32_t *blocks;
-
     static const uint32_t c1 = 0xcc9e2d51;
     static const uint32_t c2 = 0x1b873593;
     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 n  = 0xe6546b64;
     hash_t hash = seed;
-
-    if(data == UA_NULL)
-        return 0;
-
     blocks = (const uint32_t *)data;
     for(int32_t i = 0;i < nblocks;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);
     uint32_t       k1   = 0;
-
     switch(len & 3) {
     case 3:
         k1 ^= tail[2] << 16;
-
     case 2:
         k1 ^= tail[1] << 8;
-
     case 1:
         k1   ^= tail[0];
         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 *= 0xc2b2ae35;
     hash ^= (hash >> 16);
-
     return hash;
 }
 
-static INLINE hash_t hash(const UA_NodeId *n) {
+static hash_t hash(const UA_NodeId *n) {
     switch(n->identifierType) {
     case UA_NODEIDTYPE_NUMERIC:
         /*  Knuth's multiplicative hashing */
         return (n->identifier.numeric + n->namespaceIndex) * 2654435761;   // mod(2^32) is implicit
-
     case UA_NODEIDTYPE_STRING:
         return hash_array(n->identifier.string.data, n->identifier.string.length, n->namespaceIndex);
-
     case UA_NODEIDTYPE_GUID:
         return hash_array((UA_Byte *)&(n->identifier.guid), sizeof(UA_Guid), n->namespaceIndex);
-
     case UA_NODEIDTYPE_BYTESTRING:
         return hash_array((UA_Byte *)n->identifier.byteString.data, n->identifier.byteString.length, n->namespaceIndex);
-
     default:
         UA_assert(UA_FALSE);
         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) {
         *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;
-        return UA_STATUSCODE_GOOD;
+        return UA_TRUE;
     }
 
     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) {
             *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;
-            return UA_STATUSCODE_GOOD;
+            return UA_TRUE;
         }
     }
 
     /* NOTREACHED */
-    return UA_STATUSCODE_GOOD;
+    return UA_TRUE;
 }
 
 /* The following function changes size of memory allocated for the entries and
    repeatedly inserts the table elements. The occupancy of the table after the
    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.  */
     if(count * 2 < osize && (count * 8 > osize || osize <= 32))
         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;
 
-    memset(nentries, 0, nsize * sizeof(UA_Node *));
+    memset(nentries, 0, nsize * sizeof(struct nodeEntry *));
+    struct nodeEntry **oentries = ns->entries;
     ns->entries = nentries;
     ns->size    = nsize;
     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);
     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 */
 /**********************/
 
-UA_StatusCode UA_NodeStore_new(UA_NodeStore **result) {
+UA_NodeStore * UA_NodeStore_new() {
     UA_NodeStore *ns;
-    UA_UInt32     sizePrimeIndex, size;
     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);
-        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) {
-    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);
 }
 
-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(expand(ns) != UA_STATUSCODE_GOOD)
+        if(__expand(ns) != UA_STATUSCODE_GOOD)
             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 {
-        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;
+    }
 
-    ns->count++;
     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;
 
-    *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;
 }
 
+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) {
-    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;
 
     // 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 */
     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;
 }
 
 void UA_NodeStore_iterate(const UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor) {
     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) {
-    ;
+    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_
 #define UA_NODESTORE_H_
 
-#include "ua_server.h"
+#include "ua_types_generated.h"
+#include "ua_util.h"
 
 /**
    @ingroup server
 
    @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 */
-UA_StatusCode UA_NodeStore_new(UA_NodeStore **result);
+UA_NodeStore * UA_NodeStore_new();
 
 /** @brief Delete the namespace and all nodes in it */
 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
 
-    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
     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.
     They can only be replaced. After the Node is no longer used, the locked
     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
     namespace. */
@@ -57,6 +67,4 @@ typedef void (*UA_NodeStore_nodeVisitor)(const UA_Node *node);
 /** @brief Iterate over all nodes in a namespace. */
 void UA_NodeStore_iterate(const UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor);
 
-/// @} /* end of group */
-
 #endif /* UA_NODESTORE_H_ */

+ 12 - 12
src/server/ua_nodestore_concurrent.c

@@ -6,13 +6,13 @@
 #include <urcu/uatomic.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 {
     struct cds_lfht_node htn;      /* contains next-ptr for urcu-hashmap */
     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;
 
 struct UA_NodeStore {
@@ -26,7 +26,7 @@ struct UA_NodeStore {
 typedef UA_UInt32 hash_t;
 
 /* 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 c2 = 0x1b873593;
     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;
 }
 
-static INLINE hash_t hash(const UA_NodeId *n) {
+static hash_t hash(const UA_NodeId *n) {
     switch(n->identifierType) {
     case UA_NODEIDTYPE_NUMERIC:
         /*  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
    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. */
 static void markDead(struct rcu_head *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;
 
     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 */
 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
-    if(uatomic_sub_return(&entry->readcount, 1) > 0)
+    if(uatomic_sub_return(&entry->refcount, 1) > 0)
         return;
 
     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);
 
     cds_lfht_node_init(&entry->htn);
-    entry->readcount = ALIVE_BIT;
+    entry->refcount = ALIVE_BIT;
     if(flags & UA_NODESTORE_INSERT_GETMANAGED)
-        entry->readcount++;
+        entry->refcount++;
 
     hash_t nhash = hash(&(*node)->nodeId);
     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. */
-    uatomic_inc(&found_entry->readcount);
+    uatomic_inc(&found_entry->refcount);
     rcu_read_unlock();
 
     *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);
     while(iter.node != UA_NULL) {
         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;
         rcu_read_unlock();
         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);
 
     entry->channel.connection = conn;
+    conn->channel = &entry->channel;
     entry->channel.securityToken.channelId       = cm->lastChannelId++;
     entry->channel.securityToken.tokenId         = cm->lastTokenId++;
     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 ?
                                              cm->maxChannelLifetime : request->requestedLifetime;
 
+    if(channel->serverNonce.data != UA_NULL)
+        UA_ByteString_deleteMembers(&channel->serverNonce);
     UA_SecureChannel_generateNonce(&channel->serverNonce);
     UA_ByteString_copy(&channel->serverNonce, &response->serverNonce);
     UA_ChannelSecurityToken_copy(&channel->securityToken, &response->securityToken);

+ 257 - 353
src/server/ua_server.c

@@ -6,6 +6,25 @@
 #include "ua_util.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) {
     UA_ApplicationDescription_deleteMembers(&server->description);
     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 *server = UA_alloc(sizeof(UA_Server));
     if(!server)
-        return server;
+        return UA_NULL;
     
     // mockup application 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_LocalizedText_copycstring("The open62541 application", &server->description.applicationName);
     server->description.applicationType = UA_APPLICATIONTYPE_SERVER;
+    server->externalNamespacesSize = 0;
+    server->externalNamespaces = UA_NULL;
 
     UA_ByteString_init(&server->serverCertificate);
     if(serverCertificate)
@@ -34,7 +55,7 @@ UA_Server * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertifica
 
     // mockup endpoint description
     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;
     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
     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 */
     /**************/
-
-    // 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();
-    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->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();
-    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->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();
-    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->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();
-    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->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();
-    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->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();
-    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->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();
-    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->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();
-    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->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();
-    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->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();
-    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->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();
-    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->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();
-    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->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();
-    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->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();
-    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->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();
-    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->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();
-    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->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();
-    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->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();
-    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->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();
-    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->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();
-    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->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();
-    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->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();
-    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->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();
-    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();
-    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();
-    objects->nodeId    = ObjId_ObjectsFolder.nodeId;
+    COPYNAMES(objects, "Objects");
+    objects->nodeId    = UA_NODEIDS[UA_OBJECTSFOLDER];
     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();
-    types->nodeId    = ObjId_TypesFolder.nodeId;
+    COPYNAMES(types, "Types");
+    types->nodeId    = UA_NODEIDS[UA_TYPESFOLDER];
     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();
-    views->nodeId    = ObjId_ViewsFolder.nodeId;
+    COPYNAMES(views, "Views");
+    views->nodeId    = UA_NODEIDS[UA_VIEWSFOLDER];
     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();
-    servernode->nodeId    = ObjId_Server.nodeId;
+    COPYNAMES(servernode, "Server");
+    servernode->nodeId    = UA_NODEIDS[UA_SERVER];
     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();
-    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]);
     namespaceArray->value.vt = &UA_TYPES[UA_STRING];
     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));
     if(dimensions) {
         *dimensions = 2;
         namespaceArray->arrayDimensions = dimensions;
         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->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();
     status->startTime   = 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("0.0", &status->buildInfo.softwareVersion);
     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);
-    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.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();
-    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;
-    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.storage.data.arrayDimensionsLength = 1; // added to ensure encoding in readreponse
     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;
-    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;
 }
-
-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.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_SequenceHeader_calcSizeBinary(&seqHeader);

+ 24 - 2
src/server/ua_server_internal.h

@@ -2,19 +2,41 @@
 #define UA_SERVER_INTERNAL_H_
 
 #include "ua_server.h"
+#include "ua_nodestore.h"
 #include "ua_session_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 {
     UA_ApplicationDescription description;
     UA_Int32 endpointDescriptionsSize;
     UA_EndpointDescription *endpointDescriptions;
+
     UA_ByteString serverCertificate;
-    
     UA_SecureChannelManager secureChannelManager;
     UA_SessionManager sessionManager;
-    UA_NodeStore *nodestore;
     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_ */

+ 17 - 33
src/server/ua_services.h

@@ -11,8 +11,6 @@
  * @defgroup services Services
  *
  * @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
  * 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
 /** @} */
 
@@ -85,9 +82,7 @@ void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
 void Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
                              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);
 // 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,
                     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,
                                            const UA_TranslateBrowsePathsToNodeIdsRequest *request,
                                            UA_TranslateBrowsePathsToNodeIdsResponse *response);
 // Service_BrowseNext
-// Service_TranslateBrowsePathsToNodeIds
 // Service_RegisterNodes
 // Service_UnregisterNodes
 /** @} */
 
-
-/* Part 4: 5.9 Query Service Set */
 /**
  * @name Query Service Set
  *
@@ -166,7 +154,6 @@ void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *sessio
 // Service_QueryNext
 /** @} */
 
-/* Part 4: 5.10 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
  * 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
+
 /**
  * @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
@@ -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
  *  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
 /** @} */
 
@@ -261,6 +247,4 @@ void Service_Write(UA_Server *server, UA_Session *session,
 // 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_services.h"
+#include "ua_services_internal.h"
 #include "ua_statuscodes.h"
 #include "ua_nodestore.h"
 #include "ua_namespace_0.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;
 
     switch(id->attributeId) {
     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;
 
     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;
 
     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;
 
     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;
 
     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;
 
     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;
 
     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;
 
     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;
 
     case UA_ATTRIBUTEID_SYMMETRIC:
         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);
         break;
 
     case UA_ATTRIBUTEID_INVERSENAME:
         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);
         break;
 
     case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
         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);
         break;
 
     case UA_ATTRIBUTEID_EVENTNOTIFIER:
         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);
         break;
 
     case UA_ATTRIBUTEID_VALUE:
         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;
 
     case UA_ATTRIBUTEID_DATATYPE:
         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);
         break;
 
     case UA_ATTRIBUTEID_VALUERANK:
         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);
         break;
 
     case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
         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)->arrayDimensions);
         break;
 
     case UA_ATTRIBUTEID_ACCESSLEVEL:
         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);
         break;
 
     case UA_ATTRIBUTEID_USERACCESSLEVEL:
         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);
         break;
 
     case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
         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);
         break;
 
     case UA_ATTRIBUTEID_HISTORIZING:
         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);
         break;
 
     case UA_ATTRIBUTEID_EXECUTABLE:
         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);
         break;
 
     case UA_ATTRIBUTEID_USEREXECUTABLE:
         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);
         break;
 
     default:
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
-        v.status       = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
+        v->status       = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
         break;
     }
 
     UA_NodeStore_release(node);
 
     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,
@@ -216,24 +186,49 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
         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) {
         response->responseHeader.serviceResult = retval;
         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;
-    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) {
     case UA_ATTRIBUTEID_NODEID:
         /* 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);
     return retval;
-
 }
 
 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;
         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;
-    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).
  */
 
+#ifndef UA_SERVICES_INTERNAL_H_
+#define UA_SERVICES_INTERNAL_H_
+
 #include "ua_session.h"
 #include "ua_nodestore.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)
 
 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;
 
     UA_VariableAttributes attr;
@@ -82,170 +83,219 @@ static UA_StatusCode parseVariableNode(UA_ExtensionObject *attributes, UA_Node *
     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,
                                   UA_AddNodesResult *result) {
+    // adding nodes to ns0 is not allowed over the wire
     if(item->requestedNewNodeId.nodeId.namespaceIndex == 0) {
-        // adding nodes to ns0 is not allowed over the wire
         result->statusCode = UA_STATUSCODE_BADNODEIDREJECTED;
         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;
+    }
 
     if(result->statusCode != UA_STATUSCODE_GOOD)
         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)
-        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) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         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) {
         response->responseHeader.serviceResult = retval;
         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;
-    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. */
 static UA_StatusCode fillReferenceDescription(UA_NodeStore *ns, const UA_Node *currentNode, UA_ReferenceNode *reference,
                                               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_ReferenceDescription_init(referenceDescription);
     retval |= UA_NodeId_copy(&currentNode->nodeId, &referenceDescription->nodeId.nodeId);
     //TODO: ExpandedNodeId is mocked up
     referenceDescription->nodeId.serverIndex = 0;
@@ -48,20 +43,17 @@ static UA_StatusCode fillReferenceDescription(UA_NodeStore *ns, const UA_Node *c
     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)
-        return UA_FALSE;
+        return UA_NULL;
 
     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;
     if(!isRelevant) {
         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;
         }
         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
@@ -107,12 +95,13 @@ static UA_StatusCode findRelevantReferenceTypes(UA_NodeStore *ns, const UA_NodeI
         return UA_STATUSCODE_BADOUTOFMEMORY;
     }
         
-    const UA_ReferenceTypeNode *node;
     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;
-        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;
 
         // Find subtypes of the current referencetype
@@ -164,7 +153,8 @@ static void getBrowseResult(UA_NodeStore *ns, const UA_BrowseDescription *browse
     if(!returnAll) {
         if(browseDescription->includeSubtypes) {
             browseResult->statusCode = findRelevantReferenceTypes(ns, &browseDescription->referenceTypeId,
-                                                                  &relevantReferenceTypes, &relevantReferenceTypesSize);
+                                                                  &relevantReferenceTypes,
+                                                                  &relevantReferenceTypesSize);
             if(browseResult->statusCode != UA_STATUSCODE_GOOD)
                 return;
         } 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;
         if(!returnAll && browseDescription->includeSubtypes)
             UA_Array_delete(relevantReferenceTypes, relevantReferenceTypesSize, &UA_TYPES[UA_NODEID]);
         return;
     }
 
+    maxReferences = parentNode->referencesSize;
     // 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
        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 {
         UA_UInt32 currentRefs = 0;
         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;
 
             // 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) {
                 UA_Array_delete(browseResult->references, currentRefs, &UA_TYPES[UA_REFERENCEDESCRIPTION]);
                 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]);
 }
 
-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) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         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) {
         response->responseHeader.serviceResult = retval;
         return;

+ 7 - 3
src/ua_config.h.in

@@ -21,10 +21,14 @@
       #define UA_EXPORT __declspec(dllexport)
     #endif
   #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
-      #define UA_EXPORT __declspec(dllimport)
+    	#define UA_EXPORT
     #endif
   #endif
 #else

+ 1 - 1
src/ua_session.c

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

+ 1 - 0
src/ua_transport.c

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

+ 12 - 8
src/ua_types.c

@@ -13,6 +13,7 @@
 
 #include "ua_util.h"
 #include "ua_types.h"
+#include "ua_types_internal.h"
 #include "ua_types_encoding_binary.h"
 #include "ua_namespace_0.h"
 #include "ua_statuscodes.h"
@@ -165,7 +166,10 @@ UA_Int32 UA_String_copycstring(char const *src, UA_String *dst) {
     if(length == 0) {
         dst->length = 0;
         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);
         dst->length = length;
     } 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 */
-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);
     v->vt = vt;
     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;
 }
 
-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);
     v->vt = vt;
     v->storage.data.arrayLength = arrayLength;
@@ -1008,7 +1012,7 @@ void UA_InvalidType_print(const UA_InvalidType *p, FILE *stream) {
 /* 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) {
         *p = UA_NULL;
         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;
 }
 
-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_UInt32 memSize = vt->memSize;
     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_UInt32 memSize = vt->memSize;
     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_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);
     if(retval)
         return retval;
@@ -1073,7 +1077,7 @@ UA_StatusCode UA_Array_copy(const void *src, UA_Int32 noElements, const UA_VTabl
 }
 
 #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);
     char     *cp      = (char *)p; // so compilers allow pointer arithmetic
     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. */
-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
         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. */
-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);
     if(!is_builtin(&vt->typeId))
         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. */
-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) {
     //Null Arrays are encoded with length = -1 // part 6 - §5.24
     if(length < -1)
@@ -52,7 +52,7 @@ UA_StatusCode UA_Array_encodeBinary(const void *src, UA_Int32 length, const UA_V
     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) {
     //Null Arrays are encoded with length = -1 // part 6 - §5.24
     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,
-                                    const UA_VTable_Entry *vt, void **dst) {
+                                    const UA_TypeVTable *vt, void **dst) {
     if(length <= 0) {
         *dst = UA_NULL;
         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,
                          .identifier.numeric = encodingByte & UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK };
     UA_Int32 typeNs0Id = UA_ns0ToVTableIndex(&typeid );
-    const UA_VTable_Entry *vt = &UA_TYPES[typeNs0Id];
+    const UA_TypeVTable *vt = &UA_TYPES[typeNs0Id];
 
     if(!isArray) {
         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. */
-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. */
-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);
 
 /* @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,
-                                    const UA_VTable_Entry *vt, void **dst);
+                                    const UA_TypeVTable *vt, void **dst);
 
 /// @} /* 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
 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_alloca(UA_Int32 size, char *file, UA_Int32 line);
+
 #else
 extern INLINE void _UA_free(void *ptr);
 extern INLINE void * _UA_alloc(UA_Int32 size);
+extern INLINE void * _UA_alloca(UA_Int32 size);
 #endif

+ 27 - 3
src/ua_util.h

@@ -9,10 +9,13 @@
 #include "ua_config.h"
 
 #include <stddef.h> /* Needed for sys/queue.h */
-#if !defined(MSVC) && !defined(__MINGW32__)
+
+#ifndef WIN32
 #include <sys/queue.h>
+#include <alloca.h>
 #else
 #include "queue.h"
+#include <malloc.h>
 #endif
 
 #include "ua_types.h"
@@ -51,12 +54,12 @@
 #define UA_free(ptr) _UA_free(ptr, # ptr, __FILE__, __LINE__)
 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));
-    free(ptr); // checks if ptr != NULL in the background
+    free(ptr); // checks if ptr != UA_NULL in the background
 }
 #else
 #define UA_free(ptr) _UA_free(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
 
@@ -78,4 +81,25 @@ INLINE void UA_memcpy(void *dst, void const *src, UA_Int32 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_ */

+ 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);
 
 	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);
 	number_failed += srunner_ntests_failed(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) {
-	UA_NodeStore *ns = UA_NULL;
-	UA_NodeStore_new(&ns);
+	UA_NodeStore *ns = UA_NodeStore_new();
 	UA_NodeStore_delete(ns);
 }
 END_TEST
@@ -44,16 +43,11 @@ START_TEST(findNodeInUA_NodeStoreWithSingleEntry) {
    	rcu_register_thread();
 #endif
 	// given
-	UA_NodeStore *ns;
-	UA_NodeStore_new(&ns);
+	UA_NodeStore *ns = UA_NodeStore_new();
 	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
-	ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 	ck_assert_ptr_eq((void*)nr, (void*)n1);
 	// finally
 	UA_NodeStore_release(n1);
@@ -70,21 +64,19 @@ START_TEST(failToFindNodeInOtherUA_NodeStore) {
    	rcu_register_thread();
 #endif
 	// 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
 	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
-	ck_assert_int_ne(retval, UA_STATUSCODE_GOOD);
+	ck_assert_ptr_eq(nr, UA_NULL);
 	// finally
 	UA_Node_delete(n);
-	UA_NodeStore_release(nr);
 	UA_NodeStore_delete(ns);
 #ifdef MULTITHREADING
 	rcu_unregister_thread();
@@ -97,21 +89,23 @@ START_TEST(findNodeInUA_NodeStoreWithSeveralEntries) {
    	rcu_register_thread();
 #endif
 	// 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
-	retval = UA_NodeStore_get(ns,&(n3->nodeId),&nr);
+	const UA_Node* nr = UA_NodeStore_get(ns,&(n3->nodeId));
 	// then
-	ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 	ck_assert_ptr_eq((void*)nr, (void*)n3);
 	// finally
 	UA_NodeStore_release(n3);
@@ -128,14 +122,19 @@ START_TEST(iterateOverUA_NodeStoreShallNotVisitEmptyNodes) {
    	rcu_register_thread();
 #endif
 	// 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
 	zeroCnt = 0;
@@ -157,20 +156,17 @@ START_TEST(findNodeInExpandedNamespace) {
    	rcu_register_thread();
 #endif
 	// given
-	UA_NodeStore *ns;
-	UA_NodeStore_new(&ns);
+	UA_NodeStore *ns = UA_NodeStore_new();
 	UA_Node* n;
 	UA_Int32 i=0;
 	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
 	createNode(&n,0,25);
-	retval = UA_NodeStore_get(ns,&(n->nodeId),&nr);
+	const UA_Node* nr = UA_NodeStore_get(ns,&(n->nodeId));
 	// then
-	ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 	ck_assert_int_eq(nr->nodeId.identifier.numeric,n->nodeId.identifier.numeric);
 	// finally
 	UA_free((void*)n);
@@ -187,12 +183,12 @@ START_TEST(iterateOverExpandedNamespaceShallNotVisitEmptyNodes) {
    	rcu_register_thread();
 #endif
 	// given
-	UA_NodeStore *ns;
-	UA_NodeStore_new(&ns);
+	UA_NodeStore *ns = UA_NodeStore_new();
 	UA_Node* n;
 	UA_Int32 i=0;
 	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
 	zeroCnt = 0;
@@ -214,21 +210,23 @@ START_TEST(failToFindNonExistantNodeInUA_NodeStoreWithSeveralEntries) {
    	rcu_register_thread();
 #endif
 	// 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); 
 
-	const UA_Node* nr = UA_NULL;
-	UA_Int32 retval;
 	// when
-	retval = UA_NodeStore_get(ns, &(n6->nodeId), &nr);
+	const UA_Node* nr = UA_NodeStore_get(ns, &n6->nodeId);
 	// then
-	ck_assert_int_ne(retval, UA_STATUSCODE_GOOD);
+	ck_assert_ptr_eq(nr, UA_NULL);
 	// finally
 	UA_free((void *)n6);
 	UA_NodeStore_delete(ns);
@@ -260,7 +258,7 @@ void *profileGetThread(void *arg) {
 	for(UA_Int32 x = 0; x<test->rounds; x++) {
 		for (UA_Int32 i=test->min_val; i<max_val; i++) {
 			id.identifier.numeric = i;
-			UA_NodeStore_get(ns,&id, &cn);
+			cn = UA_NodeStore_get(ns,&id);
 			UA_NodeStore_release(cn);
 		}
 	}
@@ -276,12 +274,12 @@ START_TEST(profileGetDelete) {
 #endif
 
 #define N 1000000
-	UA_NodeStore *ns;
-	UA_NodeStore_new(&ns);
+	UA_NodeStore *ns = UA_NodeStore_new();
 	UA_Int32 i=0;
 	UA_Node *n;
 	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;
 	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);
 #else
 	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(i=0; i<N; i++) {
 	        id.identifier.numeric = i;
-			UA_NodeStore_get(ns,&id, &cn);
+			cn = UA_NodeStore_get(ns,&id);
 			UA_NodeStore_release(cn);
         }
     }
@@ -348,12 +347,12 @@ Suite * namespace_suite (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);
-	srunner_free (sr);
+	srunner_free(sr);
 	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)
 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
 def skipType(name):
@@ -81,7 +81,7 @@ def printableStructuredType(element):
 def createEnumerated(element):	
     valuemap = OrderedDict()
     name = "UA_" + element.get("Name")
-    fixed_size.add(name)
+    fixed_size[name] = 4
     printh("") # newline
     for child in element:
         if child.tag == "{http://opcfoundation.org/BinarySchema/}Documentation":
@@ -148,6 +148,18 @@ def createStructured(element):
             else:
                 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
     if len(membermap) > 0:
         printh("typedef struct %(name)s {")
@@ -168,20 +180,18 @@ def createStructured(element):
     # 4) CalcSizeBinary
     printc('''UA_UInt32 %(name)s_calcSizeBinary(%(name)s const * ptr) {
     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")
-    if has_fixed_size:
-        fixed_size.add(name)
 
     # 5) EncodeBinary
     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 "ua_types_internal.h"
 #include "ua_namespace_0.h"
 #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
 
 # types that are to be excluded
-exclude_kinds = set(["Object","ObjectType","Variable","Method","ReferenceType"])
 exclude_types = set(["Number", "Integer", "UInteger", "Enumeration", "Image", "ImageBMP",
                      "ImageGIF", "ImageJPG", "ImagePNG", "References", "BaseVariableType",
                      "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_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]:
-        return True
+        return False
     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)
-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','')
 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')
 fc = open(args.outfile + ".c",'w')
@@ -104,8 +132,9 @@ printh('''/**********************************************************
  */
 
 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_ExpandedNodeId UA_EXPORT *UA_EXPANDEDNODEIDS;
 
 /** 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) {''')
 
 i = 0
-for row in rows:
-    if skipType(row):
+for index, row in enumerate(rows):
+    if not useDataType(row):
         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()+'_NS0 '+row[1])
     printc('\tcase '+row[1]+': retval='+name.upper()+'; break; //'+row[2])
     i = i+1
+printc('''\t}\n\treturn retval;\n}\n''');
 
 printh('\n#define SIZE_UA_VTABLE '+str(i));
 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:
-    if row[0] == "" or (row[2] != "ReferenceType" and (row[2] != "Object" or "_Encoding_Default" in row[0])):
+    if not useOtherType(row):
         continue
     name = "UA_" + row[0]
     printh("#define "+name.upper()+" "+str(i))
+    printh('#define '+name.upper()+'_NS0 '+row[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:
-    if skipType(row):
+    if row[0] not in type_names:
         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] + "}" + 
            ",\n.name=(UA_Byte*)&\"%(name)s\"" +
            ",\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 "") +
            "}},")
 
-printc('};')
+printc('};\n')
 
 # make the nodeids available as well
 printc('''const UA_NodeId *UA_NODEIDS = (UA_NodeId[]){''')
 for row in rows:
-    if skipType(row):
+    if not row[0] in type_names:
         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] + "},")
 
 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
     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('};')
 
 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",
 							"BrowseResponse", "BrowseResult", "ReferenceDescription", "BrowseRequest", "ViewDescription", "BrowseDescription",
 							"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)