Browse Source

Merge branch 'master' into external_datasource

his merge is necessary,
Julius Pfrommer 10 years ago
parent
commit
dfbf009d3a

+ 31 - 29
examples/networklayer_tcp.c

@@ -62,6 +62,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 +121,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);
@@ -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;

+ 18 - 16
examples/networklayer_tcp_concurrent.c

@@ -8,8 +8,9 @@
 #include <uv.h>
 #include <assert.h>
 #include <malloc.h>
-#include "networklayer_tcp.h"
 #include "ua_transport.h"
+#include "ua_statuscodes.h"
+#include "networklayer_tcp.h"
 
 struct NetworklayerTCP {
     UA_Server *server;
@@ -21,13 +22,13 @@ struct NetworklayerTCP {
 	UA_UInt32 connectionsSize;
 };
 
-UA_Int32 NetworklayerTCP_new(NetworklayerTCP **newlayer, UA_ConnectionConfig localConf, UA_UInt32 port) {
-    *newlayer = malloc(sizeof(NetworklayerTCP));
-    if(newlayer == UA_NULL)
-        return UA_ERROR;
-	(*newlayer)->localConf = localConf;
-	(*newlayer)->port = port;
-	return UA_SUCCESS;
+NetworklayerTCP * NetworklayerTCP_new(UA_ConnectionConfig localConf, UA_UInt32 port) {
+    NetworklayerTCP *newlayer = malloc(sizeof(NetworklayerTCP));
+    if(newlayer) {
+        newlayer->localConf = localConf;
+        newlayer->port = port;
+    }
+	return newlayer;
 }
 
 void NetworklayerTCP_delete(NetworklayerTCP *layer) {
@@ -49,7 +50,7 @@ static void on_close(uv_handle_t * handle) {
     free(handle);
 }
 
-void close(void *handle) {
+static void closeHandle(void *handle) {
     uv_close((uv_handle_t *)handle, on_close);
 }
 
@@ -73,7 +74,7 @@ static void after_write(uv_write_t * req, int status) {
     free(wr);
 }
 
-static void write(void *handle, const UA_ByteStringArray buf) {
+static void writeHandle(void *handle, const UA_ByteStringArray buf) {
     uv_buf_t *uv_bufs = malloc(buf.stringsSize * sizeof(uv_buf_t));
     for(UA_UInt32 i=0; i<buf.stringsSize; i++) {
         uv_bufs[i].len = buf.strings[i].length;
@@ -134,7 +135,7 @@ static void on_connection(uv_stream_t *server, int status) {
         return;
 
     UA_Connection *connection = malloc(sizeof(UA_Connection));
-    UA_Connection_init(connection, layer->localConf, stream, close, write);
+    UA_Connection_init(connection, layer->localConf, stream, closeHandle, writeHandle);
     stream->data = connection;
 
     assert(uv_accept(server, (uv_stream_t*)stream) == 0);
@@ -147,24 +148,25 @@ void check_running(uv_timer_t* handle, int status) {
         uv_stop(layer->uvloop);
 }
 
-UA_Int32 NetworkLayerTCP_run(NetworklayerTCP *layer, UA_Server *server, struct timeval tv, void(*worker)(UA_Server*), UA_Boolean *running) {
+UA_StatusCode NetworkLayerTCP_run(NetworklayerTCP *layer, UA_Server *server, struct timeval tv,
+                                  void(*worker)(UA_Server*), UA_Boolean *running) {
     layer->uvloop = uv_default_loop();
     layer->server = server;
     struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", layer->port);
     if(uv_tcp_init(layer->uvloop, &layer->uvserver)) {
 		printf("Socket creation error\n");
-        return UA_ERROR;
+        return UA_STATUSCODE_BADINTERNALERROR;
     }
     
     if(uv_tcp_bind(&layer->uvserver, addr)) {
         printf("Bind error\n");
-        return UA_ERROR;
+        return UA_STATUSCODE_BADINTERNALERROR;
     }
 
 #define MAXBACKLOG 10
     if(uv_listen((uv_stream_t*)&layer->uvserver, MAXBACKLOG, on_connection)) {
         printf("Listen error");
-        return UA_ERROR;
+        return UA_STATUSCODE_BADINTERNALERROR;
     }
     layer->uvloop->data = (void*)layer; // so we can get the pointer to the server
     layer->running = running;
@@ -174,5 +176,5 @@ UA_Int32 NetworkLayerTCP_run(NetworklayerTCP *layer, UA_Server *server, struct t
     uv_timer_start(&timer_check_running, check_running, 0, 500);
     
     uv_run(layer->uvloop, UV_RUN_DEFAULT);
-    return UA_SUCCESS;
+    return UA_STATUSCODE_GOOD;
 }

+ 4 - 0
include/ua_server.h

@@ -35,6 +35,10 @@ 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, const UA_Node **node,
+                                              const UA_ExpandedNodeId *parentNodeId,
+                                              const UA_NodeId *referenceTypeId);
+UA_StatusCode UA_EXPORT UA_Server_addReference(UA_Server *server, const UA_AddReferencesItem *item);
 void UA_EXPORT UA_Server_addScalarVariableNode(UA_Server *server, UA_QualifiedName *browseName, void *value,
                                                const UA_VTable_Entry *vt, const UA_ExpandedNodeId *parentNodeId,
                                                const UA_NodeId *referenceTypeId );

+ 5 - 89
include/ua_types.h

@@ -59,8 +59,6 @@ extern "C" {
  *   the stack, whereas the dynamically sized members is heap-allocated. To
  *   reuse the variable, the remaining members (not dynamically allocated) need
  *   to be cleaned up with an _init.
- *
- * @{
  */
 
 /** @brief A two-state logical value (true or false). */
@@ -344,6 +342,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 +392,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;                   \
@@ -429,7 +421,7 @@ UA_StatusCode UA_EXPORT UA_Array_new(void **p, UA_Int32 noElements, const UA_VTa
 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);
 
-/* @brief The destination array is allocated according to noElements. */
+/* @brief The destination array is allocated with size noElements. */
 UA_StatusCode UA_EXPORT UA_Array_copy(const void *src, UA_Int32 noElements, const UA_VTable_Entry *vt, void **dst);
 #ifdef DEBUG
 void UA_EXPORT UA_Array_print(const void *p, UA_Int32 noElements, const UA_VTable_Entry *vt, FILE *stream);
@@ -468,82 +460,6 @@ struct UA_VTable_Entry {
     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"
 #endif

+ 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);    
 }

+ 26 - 18
src/server/ua_nodestore.h

@@ -9,34 +9,43 @@
 
    @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.
-
-   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.
+   @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.
+
+   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. */
@@ -45,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. */

+ 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);

+ 1 - 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();

+ 173 - 167
src/server/ua_server.c

@@ -38,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);
@@ -46,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)
@@ -85,15 +87,16 @@ 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(NODE, REFTYPE_NODEID, INVERSE, TARGET_EXPNODEID) do { \
-        struct UA_ReferenceNode refnode;                                \
-        UA_ReferenceNode_init(&refnode);                                \
-        refnode.referenceTypeId = REFTYPE_NODEID;                       \
-        refnode.isInverse       = INVERSE;                              \
-        refnode.targetId = TARGET_EXPNODEID;                            \
-        AddReference(server->nodestore, (UA_Node *)NODE, &refnode);     \
+#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 {                                \
@@ -106,229 +109,236 @@ UA_Server * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertifica
     /* References */
     /**************/
     
+    /* bootstrap by manually inserting "references" and "hassubtype" */
     UA_ReferenceTypeNode *references = UA_ReferenceTypeNode_new();
-    references->nodeId    = UA_NODEIDS[UA_REFERENCES];
-    references->nodeClass = UA_NODECLASS_REFERENCETYPE;
     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    = UA_NODEIDS[UA_HIERARCHICALREFERENCES];
-    hierarchicalreferences->nodeClass = UA_NODECLASS_REFERENCETYPE;
     COPYNAMES(hierarchicalreferences, "Hierarchicalreferences");
+    hierarchicalreferences->nodeId     = UA_NODEIDS[UA_HIERARCHICALREFERENCES];
+    hierarchicalreferences->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     hierarchicalreferences->isAbstract = UA_TRUE;
     hierarchicalreferences->symmetric  = UA_FALSE;
-    ADDREFERENCE(hierarchicalreferences, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_NONHIERARCHICALREFERENCES];
-    nonhierarchicalreferences->nodeClass = UA_NODECLASS_REFERENCETYPE;
     COPYNAMES(nonhierarchicalreferences, "NonHierarchicalReferences");
+    nonhierarchicalreferences->nodeId     = UA_NODEIDS[UA_NONHIERARCHICALREFERENCES];
+    nonhierarchicalreferences->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     nonhierarchicalreferences->isAbstract = UA_TRUE;
     nonhierarchicalreferences->symmetric  = UA_FALSE;
-    ADDREFERENCE(nonhierarchicalreferences, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_HASCHILD];
-    haschild->nodeClass = UA_NODECLASS_REFERENCETYPE;
     COPYNAMES(haschild, "HasChild");
+    haschild->nodeId     = UA_NODEIDS[UA_HASCHILD];
+    haschild->nodeClass  = UA_NODECLASS_REFERENCETYPE;
     haschild->isAbstract = UA_TRUE;
     haschild->symmetric  = UA_FALSE;
-    ADDREFERENCE(haschild, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_ORGANIZES];
-    organizes->nodeClass = UA_NODECLASS_REFERENCETYPE;
     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;
-    ADDREFERENCE(organizes, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_HASEVENTSOURCE];
-    haseventsource->nodeClass = UA_NODECLASS_REFERENCETYPE;
     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;
-    ADDREFERENCE(haseventsource, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_HASMODELLINGRULE];
-    hasmodellingrule->nodeClass = UA_NODECLASS_REFERENCETYPE;
     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;
-    ADDREFERENCE(hasmodellingrule, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_HASENCODING];
-    hasencoding->nodeClass = UA_NODECLASS_REFERENCETYPE;
     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;
-    ADDREFERENCE(hasencoding, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_HASDESCRIPTION];
-    hasdescription->nodeClass = UA_NODECLASS_REFERENCETYPE;
     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;
-    ADDREFERENCE(hasdescription, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_HASTYPEDEFINITION];
-    hastypedefinition->nodeClass = UA_NODECLASS_REFERENCETYPE;
     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;
-    ADDREFERENCE(hastypedefinition, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_GENERATESEVENT];
-    generatesevent->nodeClass = UA_NODECLASS_REFERENCETYPE;
     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;
-    ADDREFERENCE(generatesevent, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_AGGREGATES];
-    aggregates->nodeClass = UA_NODECLASS_REFERENCETYPE;
     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, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_HASSUBTYPE];
-    hassubtype->nodeClass = UA_NODECLASS_REFERENCETYPE;
-    COPYNAMES(hassubtype, "HasSubtype");
-    UA_LocalizedText_copycstring("SubtypeOf", &hassubtype->inverseName);
-    hassubtype->isAbstract = UA_FALSE;
-    hassubtype->symmetric  = UA_FALSE;
-    ADDREFERENCE(hassubtype, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_HASPROPERTY];
-    hasproperty->nodeClass = UA_NODECLASS_REFERENCETYPE;
     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;
-    ADDREFERENCE(hasproperty, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_HASCOMPONENT];
-    hascomponent->nodeClass = UA_NODECLASS_REFERENCETYPE;
     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;
-    ADDREFERENCE(hascomponent, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_HASNOTIFIER];
-    hasnotifier->nodeClass = UA_NODECLASS_REFERENCETYPE;
     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;
-    ADDREFERENCE(hasnotifier, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_HASORDEREDCOMPONENT];
-    hasorderedcomponent->nodeClass = UA_NODECLASS_REFERENCETYPE;
     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;
-    ADDREFERENCE(hasorderedcomponent, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_HASMODELPARENT];
-    hasmodelparent->nodeClass = UA_NODECLASS_REFERENCETYPE;
     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;
-    ADDREFERENCE(hasmodelparent, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_FROMSTATE];
-    fromstate->nodeClass = UA_NODECLASS_REFERENCETYPE;
     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;
-    ADDREFERENCE(fromstate, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_TOSTATE];
-    tostate->nodeClass = UA_NODECLASS_REFERENCETYPE;
     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;
-    ADDREFERENCE(tostate, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_HASCAUSE];
-    hascause->nodeClass = UA_NODECLASS_REFERENCETYPE;
     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;
-    ADDREFERENCE(hascause, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_HASEFFECT];
-    haseffect->nodeClass = UA_NODECLASS_REFERENCETYPE;
     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;
-    ADDREFERENCE(haseffect, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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    = UA_NODEIDS[UA_HASHISTORICALCONFIGURATION];
-    hashistoricalconfiguration->nodeClass = UA_NODECLASS_REFERENCETYPE;
     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;
-    ADDREFERENCE(hashistoricalconfiguration, UA_NODEIDS[UA_HASSUBTYPE], UA_TRUE, UA_EXPANDEDNODEIDS[UA_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]);
 
     /***********/
     /* Objects */
@@ -338,77 +348,81 @@ UA_Server * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertifica
     folderType->nodeId    = UA_NODEIDS[UA_FOLDERTYPE];
     folderType->nodeClass = UA_NODECLASS_OBJECTTYPE;
     COPYNAMES(folderType, "FolderType");
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&folderType, UA_NODESTORE_INSERT_UNIQUE);
+    UA_NodeStore_insert(server->nodestore, (const UA_Node**)&folderType, UA_FALSE);
 
     UA_ObjectNode *root = UA_ObjectNode_new();
+    COPYNAMES(root, "Root");
     root->nodeId    = UA_NODEIDS[UA_ROOTFOLDER];
     root->nodeClass = UA_NODECLASS_OBJECT;
-    COPYNAMES(root, "Root");
-    ADDREFERENCE(root, UA_NODEIDS[UA_HASTYPEDEFINITION], UA_FALSE, UA_EXPANDEDNODEIDS[UA_FOLDERTYPE]);
-    ADDREFERENCE(root, UA_NODEIDS[UA_ORGANIZES], UA_FALSE, UA_EXPANDEDNODEIDS[UA_OBJECTSFOLDER]);
-    ADDREFERENCE(root, UA_NODEIDS[UA_ORGANIZES], UA_FALSE, UA_EXPANDEDNODEIDS[UA_TYPESFOLDER]);
-    ADDREFERENCE(root, UA_NODEIDS[UA_ORGANIZES], UA_FALSE, UA_EXPANDEDNODEIDS[UA_VIEWSFOLDER]);
-    /* Root is replaced with a managed node that we need to release at the end.*/
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&root, UA_NODESTORE_INSERT_UNIQUE | UA_NODESTORE_INSERT_GETMANAGED);
+    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();
+    COPYNAMES(objects, "Objects");
     objects->nodeId    = UA_NODEIDS[UA_OBJECTSFOLDER];
     objects->nodeClass = UA_NODECLASS_OBJECT;
-    COPYNAMES(objects, "Objects");
-    ADDREFERENCE(objects, UA_NODEIDS[UA_HASTYPEDEFINITION], UA_FALSE, UA_EXPANDEDNODEIDS[UA_FOLDERTYPE]);
-    ADDREFERENCE(objects, UA_NODEIDS[UA_ORGANIZES], UA_FALSE, UA_EXPANDEDNODEIDS[UA_SERVER]);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&objects, UA_NODESTORE_INSERT_UNIQUE);
+    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();
+    COPYNAMES(types, "Types");
     types->nodeId    = UA_NODEIDS[UA_TYPESFOLDER];
     types->nodeClass = UA_NODECLASS_OBJECT;
-    COPYNAMES(types, "Types");
-    ADDREFERENCE(types, UA_NODEIDS[UA_HASTYPEDEFINITION], UA_FALSE, UA_EXPANDEDNODEIDS[UA_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]);
 
     UA_ObjectNode *views = UA_ObjectNode_new();
+    COPYNAMES(views, "Views");
     views->nodeId    = UA_NODEIDS[UA_VIEWSFOLDER];
     views->nodeClass = UA_NODECLASS_OBJECT;
-    COPYNAMES(views, "Views");
-    ADDREFERENCE(views, UA_NODEIDS[UA_HASTYPEDEFINITION], UA_FALSE, UA_EXPANDEDNODEIDS[UA_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]);
 
     UA_ObjectNode *servernode = UA_ObjectNode_new();
+    COPYNAMES(servernode, "Server");
     servernode->nodeId    = UA_NODEIDS[UA_SERVER];
     servernode->nodeClass = UA_NODECLASS_OBJECT;
-    COPYNAMES(servernode, "Server");
-    ADDREFERENCE(servernode, UA_NODEIDS[UA_HASCOMPONENT], UA_FALSE, UA_EXPANDEDNODEIDS[UA_SERVER_SERVERCAPABILITIES]);
-    ADDREFERENCE(servernode, UA_NODEIDS[UA_HASCOMPONENT], UA_FALSE, UA_EXPANDEDNODEIDS[UA_SERVER_NAMESPACEARRAY]);
-    ADDREFERENCE(servernode, UA_NODEIDS[UA_HASPROPERTY], UA_FALSE, UA_EXPANDEDNODEIDS[UA_SERVER_SERVERSTATUS]);
-    ADDREFERENCE(servernode, UA_NODEIDS[UA_HASPROPERTY], UA_FALSE, UA_EXPANDEDNODEIDS[UA_SERVER_SERVERARRAY]);
-    UA_NodeStore_insert(server->nodestore, (UA_Node**)&servernode, UA_NODESTORE_INSERT_UNIQUE);
+    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();
+    COPYNAMES(namespaceArray, "NamespaceArray");
     namespaceArray->nodeId    = UA_NODEIDS[UA_SERVER_NAMESPACEARRAY];
     namespaceArray->nodeClass = UA_NODECLASS_VARIABLE;
-    COPYNAMES(namespaceArray, "NamespaceArray");
     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;
     // Fixme: Insert the external namespaces
-    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]);
+    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]);
 
-    UA_VariableNode *serverstatus = UA_VariableNode_new();
-    serverstatus->nodeId    = UA_NODEIDS[UA_SERVER_SERVERSTATUS];
-    serverstatus->nodeClass = UA_NODECLASS_VARIABLE;
-    COPYNAMES(serverstatus, "ServerStatus");
     UA_ServerStatusDataType *status = UA_ServerStatusDataType_new();
     status->startTime   = UA_DateTime_now();
     status->currentTime = UA_DateTime_now();
@@ -418,47 +432,39 @@ 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]);
 
+    // todo: make this variable point to a member of the serverstatus
     UA_VariableNode *state = UA_VariableNode_new();
+    UA_ServerState stateEnum = UA_SERVERSTATE_RUNNING;
+    COPYNAMES(state, "State");
     state->nodeId    = UA_NODEIDS[UA_SERVER_SERVERSTATUS_STATE];
     state->nodeClass = UA_NODECLASS_VARIABLE;
-    COPYNAMES(state, "State");
     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);
-}
-
 void UA_EXPORT
-UA_Server_addScalarVariableNode(UA_Server *server, UA_QualifiedName *browseName, void *value, const UA_VTable_Entry *vt,
-                                const UA_ExpandedNodeId *parentNodeId, const UA_NodeId *referenceTypeId) {
+UA_Server_addScalarVariableNode(UA_Server *server, UA_QualifiedName *browseName, void *value,
+                                const UA_VTable_Entry *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);
@@ -469,5 +475,5 @@ UA_Server_addScalarVariableNode(UA_Server *server, UA_QualifiedName *browseName,
     tmpNode->value.storage.data.dataPtr = value;
     tmpNode->value.storageType = UA_VARIANT_DATA_NODELETE;
     tmpNode->value.storage.data.arrayLength = 1;
-    AddNode(server, &adminSession, (UA_Node**)&tmpNode, parentNodeId, referenceTypeId);
+    UA_Server_addNode(server, (const UA_Node**)&tmpNode, parentNodeId, referenceTypeId);
 }

+ 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);

+ 34 - 12
src/server/ua_services_attribute.c

@@ -14,9 +14,8 @@
 
 /** 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_NULL;
-    UA_Int32       result = UA_NodeStore_get(server->nodestore, &(id->nodeId), &node);
-    if(result != UA_STATUSCODE_GOOD || node == UA_NULL) {
+    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;
@@ -194,7 +193,6 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
         response->responseHeader.serviceResult = retval;
         return;
     }
-    response->resultsSize = request->nodesToReadSize;
 
     /* ### Begin External Namespaces */
     UA_Boolean isExternal[request->nodesToReadSize];
@@ -202,7 +200,7 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
     UA_UInt32 indices[request->nodesToReadSize];
     for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
         UA_UInt32 indexSize = 0;
-        for(UA_Int32 i = 0;i < response->resultsSize;i++) {
+        for(UA_Int32 i = 0;i < request->nodesToReadSize;i++) {
             if(request->nodesToRead[i].nodeId.namespaceIndex !=
                server->externalNamespaces[j].index)
                 continue;
@@ -218,6 +216,7 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
     }
     /* ### End External Namespaces */
 
+    response->resultsSize = request->nodesToReadSize;
     for(UA_Int32 i = 0;i < response->resultsSize;i++) {
         if(!isExternal[i])
             __readValue(server, &request->nodesToRead[i], &response->results[i]);
@@ -225,12 +224,11 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
 }
 
 static UA_StatusCode __writeValue(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;
+    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){ } */
@@ -361,8 +359,32 @@ void Service_Write(UA_Server *server, UA_Session *session,
         response->responseHeader.serviceResult = retval;
         return;
     }
+
+    /* ### Begin External Namespaces */
+    UA_Boolean isExternal[request->nodesToWriteSize];
+    memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToWriteSize);
+    UA_UInt32 indices[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] = __writeValue(server, &request->nodesToWrite[i]);
+    for(UA_Int32 i = 0;i < request->nodesToWriteSize;i++) {
+        if(!isExternal[i])
+            response->results[i] = __writeValue(server, &request->nodesToWrite[i]);
+    }
 }

+ 3 - 3
src/server/ua_services_internal.h

@@ -9,15 +9,15 @@
 #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,
+UA_AddNodesResult AddNode(UA_Server *server, UA_Session *session, const UA_Node **node,
                           const UA_ExpandedNodeId *parentNodeId, const UA_NodeId *referenceTypeId);
+UA_StatusCode AddReference(UA_Server *server, const UA_AddReferencesItem *item);
 
 #endif /* UA_SERVICES_INTERNAL_H_ */

+ 186 - 82
src/server/ua_services_nodemanagement.c

@@ -7,6 +7,29 @@
 #include "ua_session.h"
 #include "ua_util.h"
 
+const UA_VTable_Entry * UA_Node_getVT(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];
+}
+
 #define COPY_STANDARDATTRIBUTES do {                                    \
     if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_DISPLAYNAME) {  \
         vnode->displayName = attr.displayName;                          \
@@ -82,68 +105,65 @@ 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 void __addNode(UA_Server *server, UA_Session *session, const UA_Node **node,
+                      const UA_ExpandedNodeId *parentNodeId, const UA_NodeId *referenceTypeId,
+                      UA_AddNodesResult *result) {
+    const UA_Node *parent = UA_NodeStore_get(server->nodestore, &parentNodeId->nodeId);
+    if(!parent) {
+        result->statusCode = UA_STATUSCODE_BADPARENTNODEIDINVALID;
+        return;
     }
 
-    const UA_ReferenceTypeNode *referenceType;
-    if(UA_NodeStore_get(server->nodestore, referenceTypeId, (const UA_Node**)&referenceType) != UA_STATUSCODE_GOOD) {
-        result.statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
+    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;
+        result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
         goto ret2;
     }
 
     if(referenceType->isAbstract == UA_TRUE) {
-        result.statusCode = UA_STATUSCODE_BADREFERENCENOTALLOWED;
+        result->statusCode = UA_STATUSCODE_BADREFERENCENOTALLOWED;
         goto ret2;
     }
 
-    // todo: test if the referenetype is hierarchical
-
+    // todo: test if the referencetype 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;
+        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
+        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;
+        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);
+        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;
         }
     }
     
-    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);
+    // 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
 
@@ -155,97 +175,181 @@ UA_AddNodesResult AddNode(UA_Server *server, UA_Session *session, UA_Node **node
  ret:
     UA_NodeStore_release(parent);
 
+    return;
+}
+
+/* Exposed to userland */
+UA_AddNodesResult UA_Server_addNode(UA_Server *server, const UA_Node **node,
+                                    const UA_ExpandedNodeId *parentNodeId, const UA_NodeId *referenceTypeId) {
+    UA_AddNodesResult result;
+    UA_AddNodesResult_init(&result);
+    __addNode(server, &adminSession, node, parentNodeId, referenceTypeId, &result);
     return result;
 }
 
-static void addNodeFromAttributes(UA_Server *server, UA_Session *session, UA_AddNodesItem *item,
-                                  UA_AddNodesResult *result) {
+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;
+    // parse the node
+    UA_Node *node;
+    const UA_VTable_Entry *nodeVT = UA_NULL;
     if(item->nodeClass == UA_NODECLASS_VARIABLE)
-        result->statusCode = parseVariableNode(&item->nodeAttributes, &newNode, &newNodeVT);
+        result->statusCode = parseVariableNode(&item->nodeAttributes, &node, &nodeVT);
     else // add more node types here..
         result->statusCode = UA_STATUSCODE_BADNOTIMPLEMENTED;
 
     if(result->statusCode != UA_STATUSCODE_GOOD)
         return;
 
-    *result = AddNode(server, session, &newNode, &item->parentNodeId, &item->referenceTypeId);
+    // add the node
+    __addNode(server, session, (const UA_Node **)&node, &item->parentNodeId, &item->referenceTypeId, result);
     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[request->nodesToAddSize];
+    memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToAddSize);
+    UA_UInt32 indices[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;
+static UA_StatusCode __addSingleReference(UA_Server *server, 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 *ens = UA_NULL;
+    for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
+        if(item->sourceNodeId.namespaceIndex == server->externalNamespaces[j].index) {
+            ens = &server->externalNamespaces[j].externalNodeStore;
+            break;
+        }
+    }
 
-    if(count < 0) count = 0;
+    if(ens) {
+        // todo: use external nodestore
 
-    if(!(new_refs = UA_alloc(sizeof(UA_ReferenceNode)*(count+1))))
-        return UA_STATUSCODE_BADOUTOFMEMORY;
+    } else {
+        // 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_VTable_Entry *nodeVT = UA_Node_getVT(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;
+        }
 
-    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;
+        // 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;
+        UA_NodeStore_replace(server->nodestore, (const UA_Node **)&newNode, UA_FALSE);
+        UA_NodeStore_release(node);
     }
-
-    node->references     = new_refs;
-    node->referencesSize = count+1;
-    UA_free(old_refs);
     return UA_STATUSCODE_GOOD;
-}
+} 
 
-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)
+UA_StatusCode UA_Server_addReference(UA_Server *server, const UA_AddReferencesItem *item) {
+    // the first direction
+    UA_StatusCode retval = __addSingleReference(server, item);
+    if(retval != UA_STATUSCODE_GOOD)
         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);
+    // detect when the inverse reference shall be added as well
+    UA_AddReferencesItem item2;
+    UA_AddReferencesItem_init(&item2);
+    item2.sourceNodeId = item->targetNodeId.nodeId;
+    item2.referenceTypeId = item->referenceTypeId;
+    item2.isForward = !item->isForward;
+    item2.targetNodeId.nodeId = item->sourceNodeId;
+    retval = __addSingleReference(server, &item2);
+    // todo: if this fails, remove the first reference
 
     return retval;
 }
 
-void Service_AddReferences(UA_Server *server, UA_Session *session,
-                           const UA_AddReferencesRequest *request,
+void Service_AddReferences(UA_Server *server, UA_Session *session, const UA_AddReferencesRequest *request,
                            UA_AddReferencesResponse *response) {
-    response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTIMPLEMENTED;
+    if(request->referencesToAddSize <= 0) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
+        return;
+    }
+
+    response->results = UA_alloc(sizeof(UA_StatusCode)*request->referencesToAddSize);
+    if(!response->results) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
+        return;
+    }
+
+    response->resultsSize = request->referencesToAddSize;
+    for(UA_Int32 i = 0;i < response->resultsSize;i++)
+            response->results[i] = UA_Server_addReference(server, &request->referencesToAdd[i]);
 }

+ 29 - 16
src/server/ua_services_view.c

@@ -64,8 +64,8 @@ getRelevantTargetNode(UA_NodeStore *ns, const UA_BrowseDescription *browseDescri
             return UA_NULL;
     }
 
-    const UA_Node *node;
-    if(UA_NodeStore_get(ns, &reference->targetId.nodeId, &node) != UA_STATUSCODE_GOOD)
+    const UA_Node *node = UA_NodeStore_get(ns, &reference->targetId.nodeId);
+    if(!node)
         return UA_NULL;
 
     if(browseDescription->nodeClassMask != 0 && (node->nodeClass & browseDescription->nodeClassMask) == 0) {
@@ -95,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
@@ -152,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 {
@@ -161,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
@@ -183,13 +191,16 @@ static void getBrowseResult(UA_NodeStore *ns, const UA_BrowseDescription *browse
         UA_UInt32 currentRefs = 0;
         for(UA_Int32 i = 0;i < parentNode->referencesSize && currentRefs < maxReferences;i++) {
             // 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);
+            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;
@@ -212,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;

+ 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)

+ 1 - 0
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"

+ 89 - 89
tests/check_nodestore.c

@@ -3,7 +3,7 @@
 #include <time.h>
 
 #include "ua_types.h"
-#include "server/nodestore/open62541_nodestore.h"
+#include "server/ua_nodestore.h"
 #include "ua_util.h"
 #include "check.h"
 
@@ -23,10 +23,10 @@ void printVisitor(const UA_Node* node) {
 	printf("%d\n", node->nodeId.identifier.numeric);
 }
 
-START_TEST(test_open62541NodeStore) {
-	open62541NodeStore *ns = UA_NULL;
-	open62541NodeStore_new(&ns);
-	open62541NodeStore_delete(ns);
+START_TEST(test_UA_NodeStore) {
+	UA_NodeStore *ns = UA_NULL;
+	UA_NodeStore_new(&ns);
+	UA_NodeStore_delete(ns);
 }
 END_TEST
 
@@ -39,113 +39,113 @@ UA_StatusCode createNode(UA_Node** p, UA_Int16 nsid, UA_Int32 id) {
 	return UA_STATUSCODE_GOOD;
 }
 
-START_TEST(findNodeInopen62541NodeStoreWithSingleEntry) {
+START_TEST(findNodeInUA_NodeStoreWithSingleEntry) {
 #ifdef MULTITHREADING
    	rcu_register_thread();
 #endif
 	// given
-	open62541NodeStore *ns;
-	open62541NodeStore_new(&ns);
+	UA_NodeStore *ns;
+	UA_NodeStore_new(&ns);
 	UA_Node* n1; createNode(&n1,0,2253);
-	open62541NodeStore_insert(ns, &n1, UA_NODESTORE_INSERT_UNIQUE | UA_NODESTORE_INSERT_GETMANAGED);
+	UA_NodeStore_insert(ns, &n1, UA_NODESTORE_INSERT_UNIQUE | UA_NODESTORE_INSERT_GETMANAGED);
 	const UA_Node* nr = UA_NULL;
 	UA_Int32 retval;
 	// when
-	retval = open62541NodeStore_get(ns,&n1->nodeId,&nr);
+	retval = UA_NodeStore_get(ns,&n1->nodeId,&nr);
 	// then
 	ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 	ck_assert_ptr_eq((void*)nr, (void*)n1);
 	// finally
-	open62541NodeStore_release(n1);
-	open62541NodeStore_release(nr);
-	open62541NodeStore_delete(ns);
+	UA_NodeStore_release(n1);
+	UA_NodeStore_release(nr);
+	UA_NodeStore_delete(ns);
 #ifdef MULTITHREADING
 	rcu_unregister_thread();
 #endif
 }
 END_TEST
 
-START_TEST(failToFindNodeInOtheropen62541NodeStore) {
+START_TEST(failToFindNodeInOtherUA_NodeStore) {
 #ifdef MULTITHREADING
    	rcu_register_thread();
 #endif
 	// given
-	open62541NodeStore *ns = UA_NULL;
-	open62541NodeStore_new(&ns);
+	UA_NodeStore *ns = UA_NULL;
+	UA_NodeStore_new(&ns);
 
-	UA_Node* n1; createNode(&n1,0,2253); open62541NodeStore_insert(ns, &n1, 0);
-	UA_Node* n2; createNode(&n2,0,2253); open62541NodeStore_insert(ns, &n2, 0);
+	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);
 
 	const UA_Node* nr = UA_NULL;
 	// when
 	UA_Node* n; createNode(&n,1,2255);
-	UA_Int32 retval = open62541NodeStore_get(ns,&n->nodeId, &nr);
+	UA_Int32 retval = UA_NodeStore_get(ns,&n->nodeId, &nr);
 	// then
 	ck_assert_int_ne(retval, UA_STATUSCODE_GOOD);
 	// finally
 	UA_Node_delete(n);
-	open62541NodeStore_release(nr);
-	open62541NodeStore_delete(ns);
+	UA_NodeStore_release(nr);
+	UA_NodeStore_delete(ns);
 #ifdef MULTITHREADING
 	rcu_unregister_thread();
 #endif
 }
 END_TEST
 
-START_TEST(findNodeInopen62541NodeStoreWithSeveralEntries) {
+START_TEST(findNodeInUA_NodeStoreWithSeveralEntries) {
 #ifdef MULTITHREADING
    	rcu_register_thread();
 #endif
 	// given
-	open62541NodeStore *ns;
-	open62541NodeStore_new(&ns);
-	UA_Node* n1; createNode(&n1,0,2253); open62541NodeStore_insert(ns, &n1, 0);
-	UA_Node* n2; createNode(&n2,0,2255); open62541NodeStore_insert(ns, &n2, 0);
-	UA_Node* n3; createNode(&n3,0,2257); open62541NodeStore_insert(ns, &n3, UA_NODESTORE_INSERT_GETMANAGED);
-	UA_Node* n4; createNode(&n4,0,2200); open62541NodeStore_insert(ns, &n4, 0);
-	UA_Node* n5; createNode(&n5,0,1); open62541NodeStore_insert(ns, &n5, 0);
-	UA_Node* n6; createNode(&n6,0,12); open62541NodeStore_insert(ns, &n6, 0);
+	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;
 	// when
-	retval = open62541NodeStore_get(ns,&(n3->nodeId),&nr);
+	retval = UA_NodeStore_get(ns,&(n3->nodeId),&nr);
 	// then
 	ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 	ck_assert_ptr_eq((void*)nr, (void*)n3);
 	// finally
-	open62541NodeStore_release(n3);
-	open62541NodeStore_release(nr);
-	open62541NodeStore_delete(ns);
+	UA_NodeStore_release(n3);
+	UA_NodeStore_release(nr);
+	UA_NodeStore_delete(ns);
 #ifdef MULTITHREADING
 	rcu_unregister_thread();
 #endif
 }
 END_TEST
 
-START_TEST(iterateOveropen62541NodeStoreShallNotVisitEmptyNodes) {
+START_TEST(iterateOverUA_NodeStoreShallNotVisitEmptyNodes) {
 #ifdef MULTITHREADING
    	rcu_register_thread();
 #endif
 	// given
-	open62541NodeStore *ns;
-	open62541NodeStore_new(&ns);
-	UA_Node* n1; createNode(&n1,0,2253); open62541NodeStore_insert(ns, &n1, 0);
-	UA_Node* n2; createNode(&n2,0,2255); open62541NodeStore_insert(ns, &n2, 0);
-	UA_Node* n3; createNode(&n3,0,2257); open62541NodeStore_insert(ns, &n3, 0);
-	UA_Node* n4; createNode(&n4,0,2200); open62541NodeStore_insert(ns, &n4, 0);
-	UA_Node* n5; createNode(&n5,0,1); open62541NodeStore_insert(ns, &n5, 0);
-	UA_Node* n6; createNode(&n6,0,12); open62541NodeStore_insert(ns, &n6, 0);
+	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);
 
 	// when
 	zeroCnt = 0;
 	visitCnt = 0;
-	open62541NodeStore_iterate(ns,checkZeroVisitor);
+	UA_NodeStore_iterate(ns,checkZeroVisitor);
 	// then
 	ck_assert_int_eq(zeroCnt, 0);
 	ck_assert_int_eq(visitCnt, 6);
 	// finally
-	open62541NodeStore_delete(ns);
+	UA_NodeStore_delete(ns);
 #ifdef MULTITHREADING
 	rcu_unregister_thread();
 #endif
@@ -157,25 +157,25 @@ START_TEST(findNodeInExpandedNamespace) {
    	rcu_register_thread();
 #endif
 	// given
-	open62541NodeStore *ns;
-	open62541NodeStore_new(&ns);
+	UA_NodeStore *ns;
+	UA_NodeStore_new(&ns);
 	UA_Node* n;
 	UA_Int32 i=0;
 	for (; i<200; i++) {
-		createNode(&n,0,i); open62541NodeStore_insert(ns, &n, 0);
+		createNode(&n,0,i); UA_NodeStore_insert(ns, &n, 0);
 	}
 	const UA_Node* nr = UA_NULL;
 	UA_Int32 retval;
 	// when
 	createNode(&n,0,25);
-	retval = open62541NodeStore_get(ns,&(n->nodeId),&nr);
+	retval = UA_NodeStore_get(ns,&(n->nodeId),&nr);
 	// 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);
-	open62541NodeStore_release(nr);
-	open62541NodeStore_delete(ns);
+	UA_NodeStore_release(nr);
+	UA_NodeStore_delete(ns);
 #ifdef MULTITHREADING
 	rcu_unregister_thread();
 #endif
@@ -187,51 +187,51 @@ START_TEST(iterateOverExpandedNamespaceShallNotVisitEmptyNodes) {
    	rcu_register_thread();
 #endif
 	// given
-	open62541NodeStore *ns;
-	open62541NodeStore_new(&ns);
+	UA_NodeStore *ns;
+	UA_NodeStore_new(&ns);
 	UA_Node* n;
 	UA_Int32 i=0;
 	for (; i<200; i++) {
-		createNode(&n,0,i); open62541NodeStore_insert(ns, &n, 0);
+		createNode(&n,0,i); UA_NodeStore_insert(ns, &n, 0);
 	}
 	// when
 	zeroCnt = 0;
 	visitCnt = 0;
-	open62541NodeStore_iterate(ns,checkZeroVisitor);
+	UA_NodeStore_iterate(ns,checkZeroVisitor);
 	// then
 	ck_assert_int_eq(zeroCnt, 0);
 	ck_assert_int_eq(visitCnt, 200);
 	// finally
-	open62541NodeStore_delete(ns);
+	UA_NodeStore_delete(ns);
 #ifdef MULTITHREADING
 	rcu_unregister_thread();
 #endif
 }
 END_TEST
 
-START_TEST(failToFindNonExistantNodeInopen62541NodeStoreWithSeveralEntries) {
+START_TEST(failToFindNonExistantNodeInUA_NodeStoreWithSeveralEntries) {
 #ifdef MULTITHREADING
    	rcu_register_thread();
 #endif
 	// given
-	open62541NodeStore *ns;
-	open62541NodeStore_new(&ns);
-	UA_Node* n1; createNode(&n1,0,2253); open62541NodeStore_insert(ns, &n1, 0);
-	UA_Node* n2; createNode(&n2,0,2255); open62541NodeStore_insert(ns, &n2, 0);
-	UA_Node* n3; createNode(&n3,0,2257); open62541NodeStore_insert(ns, &n3, 0);
-	UA_Node* n4; createNode(&n4,0,2200); open62541NodeStore_insert(ns, &n4, 0);
-	UA_Node* n5; createNode(&n5,0,1); open62541NodeStore_insert(ns, &n5, 0);
+	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); 
 
 	const UA_Node* nr = UA_NULL;
 	UA_Int32 retval;
 	// when
-	retval = open62541NodeStore_get(ns, &(n6->nodeId), &nr);
+	retval = UA_NodeStore_get(ns, &(n6->nodeId), &nr);
 	// then
 	ck_assert_int_ne(retval, UA_STATUSCODE_GOOD);
 	// finally
 	UA_free((void *)n6);
-	open62541NodeStore_delete(ns);
+	UA_NodeStore_delete(ns);
 #ifdef MULTITHREADING
 	rcu_unregister_thread();
 #endif
@@ -243,8 +243,8 @@ END_TEST
 /************************************/
 
 #ifdef MULTITHREADING
-struct open62541NodeStoreProfileTest {
-	open62541NodeStore *ns;
+struct UA_NodeStoreProfileTest {
+	UA_NodeStore *ns;
 	UA_Int32 min_val;
 	UA_Int32 max_val;
 	UA_Int32 rounds;
@@ -252,16 +252,16 @@ struct open62541NodeStoreProfileTest {
 
 void *profileGetThread(void *arg) {
    	rcu_register_thread();
-	struct open62541NodeStoreProfileTest *test = (struct open62541NodeStoreProfileTest*) arg;
+	struct UA_NodeStoreProfileTest *test = (struct UA_NodeStoreProfileTest*) arg;
 	UA_NodeId id = NS0NODEID(0);
 	const UA_Node *cn;
 	UA_Int32 max_val = test->max_val;
-	open62541NodeStore *ns = test->ns;
+	UA_NodeStore *ns = test->ns;
 	for(UA_Int32 x = 0; x<test->rounds; x++) {
 		for (UA_Int32 i=test->min_val; i<max_val; i++) {
 			id.identifier.numeric = i;
-			open62541NodeStore_get(ns,&id, &cn);
-			open62541NodeStore_release(cn);
+			UA_NodeStore_get(ns,&id, &cn);
+			UA_NodeStore_release(cn);
 		}
 	}
 	rcu_unregister_thread();
@@ -276,25 +276,25 @@ START_TEST(profileGetDelete) {
 #endif
 
 #define N 1000000
-	open62541NodeStore *ns;
-	open62541NodeStore_new(&ns);
+	UA_NodeStore *ns;
+	UA_NodeStore_new(&ns);
 	UA_Int32 i=0;
 	UA_Node *n;
 	for (; i<N; i++) {
-		createNode(&n,0,i); open62541NodeStore_insert(ns, &n, 0);
+		createNode(&n,0,i); UA_NodeStore_insert(ns, &n, 0);
 	}
 	clock_t begin, end;
 	begin = clock();
 #ifdef MULTITHREADING
 #define THREADS 4
     pthread_t t[THREADS];
-	struct open62541NodeStoreProfileTest p[THREADS];
+	struct UA_NodeStoreProfileTest p[THREADS];
 	for (int i = 0; i < THREADS; i++) {
-		p[i] = (struct open62541NodeStoreProfileTest){ns, i*(N/THREADS), (i+1)*(N/THREADS), 50};
-		pthread_create(&t[i], UA_NULL, profileGetThread, &p[i]);
+		p[i] = (struct UA_NodeStoreProfileTest){ns, i*(N/THREADS), (i+1)*(N/THREADS), 50};
+		pthread_create(&t[i], NULL, profileGetThread, &p[i]);
 	}
 	for (int i = 0; i < THREADS; i++)
-		pthread_join(t[i], UA_NULL);
+		pthread_join(t[i], NULL);
 	end = clock();
 	printf("Time for %d create/get/delete on %d threads in a namespace: %fs.\n", N, THREADS, (double)(end - begin) / CLOCKS_PER_SEC);
 #else
@@ -303,15 +303,15 @@ START_TEST(profileGetDelete) {
 	for(UA_Int32 x = 0; x<50; x++) {
 	    for(i=0; i<N; i++) {
 	        id.identifier.numeric = i;
-			open62541NodeStore_get(ns,&id, &cn);
-			open62541NodeStore_release(cn);
+			UA_NodeStore_get(ns,&id, &cn);
+			UA_NodeStore_release(cn);
         }
     }
 	end = clock();
 	printf("Time for single-threaded %d create/get/delete in a namespace: %fs.\n", N, (double)(end - begin) / CLOCKS_PER_SEC);
 #endif
 
-	open62541NodeStore_delete(ns);
+	UA_NodeStore_delete(ns);
 
 #ifdef MULTITHREADING
 	rcu_unregister_thread();
@@ -320,22 +320,22 @@ START_TEST(profileGetDelete) {
 END_TEST
 
 Suite * namespace_suite (void) {
-	Suite *s = suite_create ("open62541NodeStore");
+	Suite *s = suite_create ("UA_NodeStore");
 
 	TCase *tc_cd = tcase_create ("Create/Delete");
-	tcase_add_test (tc_cd, test_open62541NodeStore);
+	tcase_add_test (tc_cd, test_UA_NodeStore);
 	suite_add_tcase (s, tc_cd);
 
 	TCase* tc_find = tcase_create ("Find");
-	tcase_add_test (tc_find, findNodeInopen62541NodeStoreWithSingleEntry);
-	tcase_add_test (tc_find, findNodeInopen62541NodeStoreWithSeveralEntries);
+	tcase_add_test (tc_find, findNodeInUA_NodeStoreWithSingleEntry);
+	tcase_add_test (tc_find, findNodeInUA_NodeStoreWithSeveralEntries);
 	tcase_add_test (tc_find, findNodeInExpandedNamespace);
-	tcase_add_test (tc_find, failToFindNonExistantNodeInopen62541NodeStoreWithSeveralEntries);
-	tcase_add_test (tc_find, failToFindNodeInOtheropen62541NodeStore);
+	tcase_add_test (tc_find, failToFindNonExistantNodeInUA_NodeStoreWithSeveralEntries);
+	tcase_add_test (tc_find, failToFindNodeInOtherUA_NodeStore);
 	suite_add_tcase (s, tc_find);
 
 	TCase* tc_iterate = tcase_create ("Iterate");
-	tcase_add_test (tc_iterate, iterateOveropen62541NodeStoreShallNotVisitEmptyNodes);
+	tcase_add_test (tc_iterate, iterateOverUA_NodeStoreShallNotVisitEmptyNodes);
 	tcase_add_test (tc_iterate, iterateOverExpandedNamespaceShallNotVisitEmptyNodes);
 	suite_add_tcase (s, tc_iterate);
 	

+ 1 - 0
tools/generate_builtin.py

@@ -325,6 +325,7 @@ printc('''/**
  */
  
 #include "''' + args.outfile.split("/")[-1] + '''.h"
+#include "ua_types_internal.h"
 #include "ua_namespace_0.h"
 #include "ua_util.h"\n''')