Explorar el Código

Nodestore plugin interface

Julian Grothoff hace 6 años
padre
commit
5533c48c6e

+ 115 - 9
src/server/ua_nodes.h

@@ -2,8 +2,15 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef UA_NODES_H_
-#define UA_NODES_H_
+#ifndef UA_SERVER_NODES_H_
+#define UA_SERVER_NODES_H_
+
+/* !!! Warning !!!
+ *
+ * If you are not developing a nodestore plugin, then you should not work with
+ * the definitions from this file directly. The underlying node structures are
+ * not meant to be used directly by end users. Please use the public server API
+ * / OPC UA services to interact with the information model. */
 
 #ifdef __cplusplus
 extern "C" {
@@ -32,10 +39,11 @@ extern "C" {
  * section on :ref:`ReferenceTypes <referencetypenode>` for more details on
  * possible references and their semantics.
  *
- * The structures defined in this section are *not user-facing*. The interaction
- * with the information model is possible only via the OPC UA :ref:`services`.
- * Still, we reproduce how nodes are represented internally so that users may
- * have a clear mental model.
+ * **Warning!!** The structures defined in this section are only relevant for
+ * the developers of custom Nodestores. The interaction with the information
+ * model is possible only via the OPC UA :ref:`services`. So the following
+ * sections are purely informational so that users may have a clear mental
+ * model of the underlying representation.
  *
  * Base Node Attributes
  * --------------------
@@ -75,6 +83,33 @@ typedef struct {
     UA_NODE_BASEATTRIBUTES
 } UA_Node;
 
+/* The following methods specialize internally for the different node classes
+ * (distinguished by the nodeClass member) */
+
+/* Attributes must be of a matching type (VariableAttributes, ObjectAttributes,
+ * and so on). The attributes are copied. Note that the attributes structs do
+ * not contain NodeId, NodeClass and BrowseName. The NodeClass of the node needs
+ * to be correctly set before calling this method. UA_Node_deleteMembers is
+ * called on the node when an error occurs internally. */
+UA_StatusCode UA_EXPORT
+UA_Node_setAttributes(UA_Node *node, const void *attributes,
+                      const UA_DataType *attributeType);
+
+UA_StatusCode UA_EXPORT
+UA_Node_copy(const UA_Node *src, UA_Node *dst);
+
+UA_StatusCode UA_EXPORT
+UA_Node_addReference(UA_Node *node, const UA_AddReferencesItem *item);
+
+UA_StatusCode UA_EXPORT
+UA_Node_deleteReference(UA_Node *node, const UA_DeleteReferencesItem *item);
+
+void UA_EXPORT
+UA_Node_deleteReferences(UA_Node *node);
+
+void UA_EXPORT
+UA_Node_deleteMembers(UA_Node *node);
+
 /**
  * VariableNode
  * ------------
@@ -140,6 +175,7 @@ typedef struct {
  *
  * Consistency between the array dimensions attribute in the variable and its
  * :ref:`variabletypenode` is ensured. */
+
 /* Indicates whether a variable contains data inline or whether it points to an
  * external data source */
 typedef enum {
@@ -184,6 +220,7 @@ typedef struct {
  * variable type may provide semantic information. For example, an instance from
  * ``MotorTemperatureVariableType`` is more meaningful than a float variable
  * instantiated from ``BaseDataVariable``. */
+
 typedef struct {
     UA_NODE_BASEATTRIBUTES
     UA_NODE_VARIABLEATTRIBUTES
@@ -209,8 +246,8 @@ typedef struct {
  *
  * Note that the same MethodNode may be referenced from several objects (and
  * object types). For this, the NodeId of the method *and of the object
- * providing context* is part of a Call request message.
- */
+ * providing context* is part of a Call request message. */
+
 typedef struct {
     UA_NODE_BASEATTRIBUTES
     UA_Boolean executable;
@@ -227,6 +264,7 @@ typedef struct {
  * and software objects. Objects are instances of an :ref:`object
  * type<objecttypenode>` and may contain variables, methods and further
  * objects. */
+
 typedef struct {
     UA_NODE_BASEATTRIBUTES
     UA_Byte eventNotifier;
@@ -241,6 +279,7 @@ typedef struct {
  * ObjectTypes provide definitions for Objects. Abstract objects cannot be
  * instantiated. See :ref:`object-lifecycle` for the use of constructor and
  * destructor callbacks. */
+
 typedef struct {
     UA_NODE_BASEATTRIBUTES
     UA_Boolean isAbstract;
@@ -351,6 +390,7 @@ typedef struct {
  * electrical and information related connections. A client can then learn the
  * layout of a (physical) system represented in an OPC UA information model
  * based on a common understanding of just two custom reference types. */
+
 typedef struct {
     UA_NODE_BASEATTRIBUTES
     UA_Boolean isAbstract;
@@ -372,6 +412,7 @@ typedef struct {
  * Abstract DataTypes (e.g. ``Number``) cannot be the type of actual values.
  * They are used to constrain values to possible child DataTypes (e.g.
  * ``UInt32``). */
+
 typedef struct {
     UA_NODE_BASEATTRIBUTES
     UA_Boolean isAbstract;
@@ -386,14 +427,79 @@ typedef struct {
  * references only. ViewNodes can be created and be interacted with. But their
  * use in the :ref:`Browse<view-services>` service is currently unsupported in
  * open62541. */
+
 typedef struct {
     UA_NODE_BASEATTRIBUTES
     UA_Byte eventNotifier;
     UA_Boolean containsNoLoops;
 } UA_ViewNode;
 
+/**
+ * Nodestore
+ * =========
+ * The following definitions are used for implementing node storage plugins.
+ * Most users will want to use one of the predefined Nodestores.
+ *
+ * Warning! Endusers should not manually edit nodes. Please use the server API
+ * for that. Otherwise, the consistency checks of the server are omitted. This
+ * can crash the application eventually. */
+
+typedef void
+(*UA_NodestoreVisitor)(void *visitorContext, const UA_Node *node);
+
+typedef struct {
+    /* Nodestore context and lifecycle */
+    void *context;
+    void (*deleteNodestore)(void *nodestoreContext);
+
+    /* For non-multithreaded access, some nodestores allow that nodes are edited
+     * without a copy/replace. This is not possible when the node is only an
+     * intermediate representation and stored e.g. in a database backend. */
+    UA_Boolean inPlaceEditAllowed;
+
+    /* The following definitions are used to create empty nodes of the different
+     * node types. The memory is managed by the nodestore. Therefore, the node
+     * has to be removed via a special deleteNode function. (If the new node is
+     * not added to the nodestore.) */
+    UA_Node * (*newNode)(void *nodestoreContext, UA_NodeClass nodeClass);
+
+    void (*deleteNode)(void *nodestoreContext, UA_Node *node);
+
+    /* ``Get`` returns a pointer to an immutable node. ``Release`` indicates
+     * that the pointer is no longer accessed afterwards. */
+
+    const UA_Node * (*getNode)(void *nodestoreContext, const UA_NodeId *nodeId);
+
+    void (*releaseNode)(void *nodestoreContext, const UA_Node *node);
+
+    /* Returns an editable copy of a node (needs to be deleted with the
+     * deleteNode function or inserted / replaced into the nodestore). */
+    UA_StatusCode (*getNodeCopy)(void *nodestoreContext, const UA_NodeId *nodeId,
+                                 UA_Node **outNode);
+
+    /* Inserts a new node into the nodestore. If the NodeId is zero, then a
+     * fresh numeric NodeId is assigned. If insertion fails, the node is
+     * deleted. */
+    UA_StatusCode (*insertNode)(void *nodestoreContext, UA_Node *node,
+                                UA_NodeId *addedNodeId);
+
+    /* To replace a node, get an editable copy of the node, edit and replace
+     * with this function. If the node was already replaced since the copy was
+     * made, UA_STATUSCODE_BADINTERNALERROR is returned. If the NodeId is not
+     * found, UA_STATUSCODE_BADNODEIDUNKNOWN is returned. In both error cases,
+     * the editable node is deleted. */
+    UA_StatusCode (*replaceNode)(void *nodestoreContext, UA_Node *node);
+
+    /* Removes a node from the nodestore. */
+    UA_StatusCode (*removeNode)(void *nodestoreContext, const UA_NodeId *nodeId);
+
+    /* Execute a callback for every node in the nodestore. */
+    void (*iterate)(void *nodestoreContext, void* visitorContext,
+                    UA_NodestoreVisitor visitor);
+} UA_Nodestore;
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
 
-#endif /* UA_NODES_H_ */
+#endif /* UA_SERVER_NODES_H_ */

+ 463 - 0
plugins/ua_nodestore_default.c

@@ -0,0 +1,463 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+#include "ua_nodestore_default.h"
+
+#ifdef UA_ENABLE_MULTITHREADING
+#include <pthread.h>
+#define BEGIN_CRITSECT(NODEMAP) pthread_mutex_lock(&(NODEMAP)->mutex)
+#define END_CRITSECT(NODEMAP) pthread_mutex_unlock(&(NODEMAP)->mutex)
+#else
+#define BEGIN_CRITSECT(NODEMAP)
+#define END_CRITSECT(NODEMAP)
+#endif
+
+/* The default Nodestore is simply a hash-map from NodeIds to Nodes. To find an
+ * entry, iterate over candidate positions according to the NodeId hash.
+ *
+ * - Tombstone or non-matching NodeId: continue searching
+ * - Matching NodeId: Return the entry
+ * - NULL: Abort the search */
+
+typedef struct UA_NodeMapEntry {
+    struct UA_NodeMapEntry *orig; /* the version this is a copy from (or NULL) */
+    UA_UInt16 refCount; /* How many consumers have a reference to the node? */
+    UA_Boolean deleted; /* Node was marked as deleted and can be deleted when refCount == 0 */
+    UA_Node node;
+} UA_NodeMapEntry;
+
+#define UA_NODEMAP_MINSIZE 64
+#define UA_NODEMAP_TOMBSTONE ((UA_NodeMapEntry*)0x01)
+
+typedef struct {
+    UA_NodeMapEntry **entries;
+    UA_UInt32 size;
+    UA_UInt32 count;
+    UA_UInt32 sizePrimeIndex;
+#ifdef UA_ENABLE_MULTITHREADING
+    pthread_mutex_t mutex; /* Protect access */
+#endif
+} UA_NodeMap;
+
+/*********************/
+/* HashMap Utilities */
+/*********************/
+
+/* 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. */
+static UA_UInt32 const primes[] = {
+    7,         13,         31,         61,         127,         251,
+    509,       1021,       2039,       4093,       8191,        16381,
+    32749,     65521,      131071,     262139,     524287,      1048573,
+    2097143,   4194301,    8388593,    16777213,   33554393,    67108859,
+    134217689, 268435399,  536870909,  1073741789, 2147483647,  4294967291
+};
+
+static UA_UInt32 mod(UA_UInt32 h, UA_UInt32 size) { return h % size; }
+static UA_UInt32 mod2(UA_UInt32 h, UA_UInt32 size) { return 1 + (h % (size - 2)); }
+
+static UA_UInt16
+higher_prime_index(UA_UInt32 n) {
+    UA_UInt16 low  = 0;
+    UA_UInt16 high = (UA_UInt16)(sizeof(primes) / sizeof(UA_UInt32));
+    while(low != high) {
+        UA_UInt16 mid = (UA_UInt16)(low + ((high - low) / 2));
+        if(n > primes[mid])
+            low = (UA_UInt16)(mid + 1);
+        else
+            high = mid;
+    }
+    return low;
+}
+
+/* returns an empty slot or null if the nodeid exists */
+static UA_NodeMapEntry **
+findFreeSlot(const UA_NodeMap *ns, const UA_NodeId *nodeid) {
+    UA_UInt32 h = UA_NodeId_hash(nodeid);
+    UA_UInt32 size = ns->size;
+    UA_UInt32 idx = mod(h, size);
+    UA_UInt32 hash2 = mod2(h, size);
+
+    while(true) {
+        UA_NodeMapEntry *e = ns->entries[idx];
+        if(e > UA_NODEMAP_TOMBSTONE &&
+           UA_NodeId_equal(&e->node.nodeId, nodeid))
+            return NULL;
+        if(ns->entries[idx] <= UA_NODEMAP_TOMBSTONE)
+            return &ns->entries[idx];
+        idx += hash2;
+        if(idx >= size)
+            idx -= size;
+    }
+
+    /* NOTREACHED */
+    return NULL;
+}
+
+/* The occupancy of the table after the call will be about 50% */
+static UA_StatusCode
+expand(UA_NodeMap *ns) {
+    UA_UInt32 osize = ns->size;
+    UA_UInt32 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 <= UA_NODEMAP_MINSIZE))
+        return UA_STATUSCODE_GOOD;
+
+    UA_NodeMapEntry **oentries = ns->entries;
+    UA_UInt32 nindex = higher_prime_index(count * 2);
+    UA_UInt32 nsize = primes[nindex];
+    UA_NodeMapEntry **nentries = (UA_NodeMapEntry **)UA_calloc(nsize, sizeof(UA_NodeMapEntry*));
+    if(!nentries)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    ns->entries = nentries;
+    ns->size = nsize;
+    ns->sizePrimeIndex = nindex;
+
+    /* recompute the position of every entry and insert the pointer */
+    for(size_t i = 0, j = 0; i < osize && j < count; ++i) {
+        if(oentries[i] <= UA_NODEMAP_TOMBSTONE)
+            continue;
+        UA_NodeMapEntry **e = findFreeSlot(ns, &oentries[i]->node.nodeId);
+        UA_assert(e);
+        *e = oentries[i];
+        ++j;
+    }
+
+    UA_free(oentries);
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_NodeMapEntry *
+newEntry(UA_NodeClass nodeClass) {
+    size_t size = sizeof(UA_NodeMapEntry) - sizeof(UA_Node);
+    switch(nodeClass) {
+    case UA_NODECLASS_OBJECT:
+        size += sizeof(UA_ObjectNode);
+        break;
+    case UA_NODECLASS_VARIABLE:
+        size += sizeof(UA_VariableNode);
+        break;
+    case UA_NODECLASS_METHOD:
+        size += sizeof(UA_MethodNode);
+        break;
+    case UA_NODECLASS_OBJECTTYPE:
+        size += sizeof(UA_ObjectTypeNode);
+        break;
+    case UA_NODECLASS_VARIABLETYPE:
+        size += sizeof(UA_VariableTypeNode);
+        break;
+    case UA_NODECLASS_REFERENCETYPE:
+        size += sizeof(UA_ReferenceTypeNode);
+        break;
+    case UA_NODECLASS_DATATYPE:
+        size += sizeof(UA_DataTypeNode);
+        break;
+    case UA_NODECLASS_VIEW:
+        size += sizeof(UA_ViewNode);
+        break;
+    default:
+        return NULL;
+    }
+    UA_NodeMapEntry *entry = (UA_NodeMapEntry*)UA_calloc(1, size);
+    if(!entry)
+        return NULL;
+    entry->node.nodeClass = nodeClass;
+    return entry;
+}
+
+static void
+deleteEntry(UA_NodeMapEntry *entry) {
+    UA_Node_deleteMembers(&entry->node);
+    UA_free(entry);
+}
+
+static void
+cleanupEntry(UA_NodeMapEntry *entry) {
+    if(entry->deleted && entry->refCount == 0)
+        deleteEntry(entry);
+}
+
+static UA_StatusCode
+clearSlot(UA_NodeMap *ns, UA_NodeMapEntry **slot) {
+    (*slot)->deleted = true;
+    cleanupEntry(*slot);
+    *slot = UA_NODEMAP_TOMBSTONE;
+    --ns->count;
+    /* Downsize the hashmap if it is very empty */
+    if(ns->count * 8 < ns->size && ns->size > 32)
+        expand(ns); /* Can fail. Just continue with the bigger hashmap. */
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_NodeMapEntry **
+findOccupiedSlot(const UA_NodeMap *ns, const UA_NodeId *nodeid) {
+    UA_UInt32 h = UA_NodeId_hash(nodeid);
+    UA_UInt32 size = ns->size;
+    UA_UInt32 idx = mod(h, size);
+    UA_UInt32 hash2 = mod2(h, size);
+
+    while(true) {
+        UA_NodeMapEntry *e = ns->entries[idx];
+        if(!e)
+            return NULL;
+        if(e > UA_NODEMAP_TOMBSTONE &&
+           UA_NodeId_equal(&e->node.nodeId, nodeid))
+            return &ns->entries[idx];
+        idx += hash2;
+        if(idx >= size)
+            idx -= size;
+    }
+
+    /* NOTREACHED */
+    return NULL;
+}
+
+/***********************/
+/* Interface functions */
+/***********************/
+
+static UA_Node *
+UA_NodeMap_newNode(void *context, UA_NodeClass nodeClass) {
+    UA_NodeMapEntry *entry = newEntry(nodeClass);
+    if(!entry)
+        return NULL;
+    return &entry->node;
+}
+
+static void
+UA_NodeMap_deleteNode(void *context, UA_Node *node) {
+#ifdef UA_ENABLE_MULTITHREADING
+    UA_NodeMap *ns = (UA_NodeMap*)context;
+#endif
+    BEGIN_CRITSECT(ns);
+    UA_NodeMapEntry *entry = container_of(node, UA_NodeMapEntry, node);
+    UA_assert(&entry->node == node);
+    deleteEntry(entry);
+    END_CRITSECT(ns);
+}
+
+static const UA_Node *
+UA_NodeMap_getNode(void *context, const UA_NodeId *nodeid) {
+    UA_NodeMap *ns = (UA_NodeMap*)context;
+    BEGIN_CRITSECT(ns);
+    UA_NodeMapEntry **entry = findOccupiedSlot(ns, nodeid);
+    if(!entry) {
+        END_CRITSECT(ns);
+        return NULL;
+    }
+    ++(*entry)->refCount;
+    END_CRITSECT(ns);
+    return (const UA_Node*)&(*entry)->node;
+}
+
+static void
+UA_NodeMap_releaseNode(void *context, const UA_Node *node) {
+#ifdef UA_ENABLE_MULTITHREADING
+    UA_NodeMap *ns = (UA_NodeMap*)context;
+#endif
+    BEGIN_CRITSECT(ns);
+    UA_NodeMapEntry *entry = container_of(node, UA_NodeMapEntry, node);
+    UA_assert(&entry->node == node);
+    UA_assert(entry->refCount > 0);
+    --entry->refCount;
+    cleanupEntry(entry);
+    END_CRITSECT(ns);
+}
+
+static UA_StatusCode
+UA_NodeMap_getNodeCopy(void *context, const UA_NodeId *nodeid,
+                       UA_Node **outNode) {
+    UA_NodeMap *ns = (UA_NodeMap*)context;
+    BEGIN_CRITSECT(ns);
+    UA_NodeMapEntry **slot = findOccupiedSlot(ns, nodeid);
+    if(!slot) {
+        END_CRITSECT(ns);
+        return UA_STATUSCODE_BADNODEIDUNKNOWN;
+    }
+    UA_NodeMapEntry *entry = *slot;
+    UA_NodeMapEntry *newItem = newEntry(entry->node.nodeClass);
+    if(!newItem) {
+        END_CRITSECT(ns);
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    }
+    UA_StatusCode retval = UA_Node_copy(&entry->node, &newItem->node);
+    if(retval == UA_STATUSCODE_GOOD) {
+        newItem->orig = entry; // store the pointer to the original
+        *outNode = &newItem->node;
+    } else {
+        deleteEntry(newItem);
+    }
+    END_CRITSECT(ns);
+    return retval;
+}
+
+static UA_StatusCode
+UA_NodeMap_removeNode(void *context, const UA_NodeId *nodeid) {
+    UA_NodeMap *ns = (UA_NodeMap*)context;
+    BEGIN_CRITSECT(ns);
+    UA_NodeMapEntry **slot = findOccupiedSlot(ns, nodeid);
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    if(slot)
+        retval = clearSlot(ns, slot);
+    else
+        retval = UA_STATUSCODE_BADNODEIDUNKNOWN;
+    END_CRITSECT(ns);
+    return retval;
+}
+
+static UA_StatusCode
+UA_NodeMap_insertNode(void *context, UA_Node *node,
+                      UA_NodeId *addedNodeId) {
+    UA_NodeMap *ns = (UA_NodeMap*)context;
+    BEGIN_CRITSECT(ns);
+    if(ns->size * 3 <= ns->count * 4) {
+        if(expand(ns) != UA_STATUSCODE_GOOD) {
+            END_CRITSECT(ns);
+            return UA_STATUSCODE_BADINTERNALERROR;
+        }
+    }
+
+    UA_NodeId tempNodeid;
+    tempNodeid = node->nodeId;
+    tempNodeid.namespaceIndex = 0;
+    UA_NodeMapEntry **slot;
+    if(tempNodeid.identifierType == UA_NODEIDTYPE_NUMERIC &&
+       tempNodeid.identifier.numeric == 0) {
+        /* create a random nodeid */
+        if(node->nodeId.namespaceIndex == 0)
+            node->nodeId.namespaceIndex = 1;
+        UA_UInt32 identifier = ns->count+1; // start value
+        UA_UInt32 size = ns->size;
+        UA_UInt32 increase = mod2(identifier, size);
+        while(true) {
+            node->nodeId.identifier.numeric = identifier;
+            slot = findFreeSlot(ns, &node->nodeId);
+            if(slot)
+                break;
+            identifier += increase;
+            if(identifier >= size)
+                identifier -= size;
+        }
+    } else {
+        slot = findFreeSlot(ns, &node->nodeId);
+        if(!slot) {
+            deleteEntry(container_of(node, UA_NodeMapEntry, node));
+            END_CRITSECT(ns);
+            return UA_STATUSCODE_BADNODEIDEXISTS;
+        }
+    }
+
+    *slot = container_of(node, UA_NodeMapEntry, node);
+    ++ns->count;
+    UA_assert(&(*slot)->node == node);
+
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    if(addedNodeId) {
+        retval = UA_NodeId_copy(&node->nodeId, addedNodeId);
+        if(retval != UA_STATUSCODE_GOOD)
+            clearSlot(ns, slot);
+    }
+
+    END_CRITSECT(ns);
+    return retval;
+}
+
+static UA_StatusCode
+UA_NodeMap_replaceNode(void *context, UA_Node *node) {
+    UA_NodeMap *ns = (UA_NodeMap*)context;
+    BEGIN_CRITSECT(ns);
+    UA_NodeMapEntry **slot = findOccupiedSlot(ns, &node->nodeId);
+    if(!slot) {
+        END_CRITSECT(ns);
+        return UA_STATUSCODE_BADNODEIDUNKNOWN;
+    }
+    UA_NodeMapEntry *newEntry = container_of(node, UA_NodeMapEntry, node);
+    if(*slot != newEntry->orig) {
+        /* The node was updated since the copy was made */
+        deleteEntry(newEntry);
+        END_CRITSECT(ns);
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+    (*slot)->deleted = true;
+    cleanupEntry(*slot);
+    *slot = newEntry;
+    END_CRITSECT(ns);
+    return UA_STATUSCODE_GOOD;
+}
+
+static void
+UA_NodeMap_iterate(void *context, void *visitorContext,
+                   UA_NodestoreVisitor visitor) {
+    UA_NodeMap *ns = (UA_NodeMap*)context;
+    BEGIN_CRITSECT(ns);
+    for(UA_UInt32 i = 0; i < ns->size; ++i) {
+        if(ns->entries[i] > UA_NODEMAP_TOMBSTONE) {
+            END_CRITSECT(ns);
+            UA_NodeMapEntry *entry = ns->entries[i];
+            entry->refCount++;
+            visitor(visitorContext, &entry->node);
+            entry->refCount--;
+            cleanupEntry(entry);
+            BEGIN_CRITSECT(ns);
+        }
+    }
+    END_CRITSECT(ns);
+}
+
+static void
+UA_NodeMap_delete(void *context) {
+    UA_NodeMap *ns = (UA_NodeMap*)context;
+#ifdef UA_ENABLE_MULTITHREADING
+    pthread_mutex_destroy(&ns->mutex);
+#endif
+    UA_UInt32 size = ns->size;
+    UA_NodeMapEntry **entries = ns->entries;
+    for(UA_UInt32 i = 0; i < size; ++i) {
+        if(entries[i] > UA_NODEMAP_TOMBSTONE) {
+            /* On debugging builds, check that all nodes were release */
+            UA_assert(entries[i]->refCount == 0);
+            /* Delete the node */
+            deleteEntry(entries[i]);
+        }
+    }
+    UA_free(ns->entries);
+    UA_free(ns);
+}
+
+UA_StatusCode
+UA_Nodestore_default_new(UA_Nodestore *ns) {
+    /* Allocate and initialize the nodemap */
+    UA_NodeMap *nodemap = (UA_NodeMap*)UA_malloc(sizeof(UA_NodeMap));
+    if(!nodemap)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    nodemap->sizePrimeIndex = higher_prime_index(UA_NODEMAP_MINSIZE);
+    nodemap->size = primes[nodemap->sizePrimeIndex];
+    nodemap->count = 0;
+    nodemap->entries = (UA_NodeMapEntry**)
+        UA_calloc(nodemap->size, sizeof(UA_NodeMapEntry*));
+    if(!nodemap->entries) {
+        UA_free(nodemap);
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    }
+#ifdef UA_ENABLE_MULTITHREADING
+    pthread_mutex_init(&nodemap->mutex, NULL);
+#endif
+
+    /* Populate the nodestore */
+    ns->context = nodemap;
+    ns->deleteNodestore = UA_NodeMap_delete;
+    ns->inPlaceEditAllowed = true;
+    ns->newNode = UA_NodeMap_newNode;
+    ns->deleteNode = UA_NodeMap_deleteNode;
+    ns->getNode = UA_NodeMap_getNode;
+    ns->releaseNode = UA_NodeMap_releaseNode;
+    ns->getNodeCopy = UA_NodeMap_getNodeCopy;
+    ns->insertNode = UA_NodeMap_insertNode;
+    ns->replaceNode = UA_NodeMap_replaceNode;
+    ns->removeNode = UA_NodeMap_removeNode;
+    ns->iterate = UA_NodeMap_iterate;
+
+    return UA_STATUSCODE_GOOD;
+}

+ 21 - 0
plugins/ua_nodestore_default.h

@@ -0,0 +1,21 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+#ifndef UA_NODESTORE_DEFAULT_H_
+#define UA_NODESTORE_DEFAULT_H_
+
+#include "ua_plugin_nodestore.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Initializes the nodestore, sets the context and function pointers */
+UA_StatusCode UA_EXPORT
+UA_Nodestore_default_new(UA_Nodestore *ns);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* UA_NODESTORE_DEFAULT_H_ */

+ 0 - 332
src/server/ua_nodestore.c

@@ -1,332 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "ua_nodestore.h"
-#include "ua_server_internal.h"
-#include "ua_util.h"
-
-#ifndef UA_ENABLE_MULTITHREADING /* conditional compilation */
-
-#define UA_NODESTORE_MINSIZE 64
-
-typedef struct UA_NodeStoreEntry {
-    struct UA_NodeStoreEntry *orig; // the version this is a copy from (or NULL)
-    UA_Node node;
-} UA_NodeStoreEntry;
-
-#define UA_NODESTORE_TOMBSTONE ((UA_NodeStoreEntry*)0x01)
-
-struct UA_NodeStore {
-    UA_NodeStoreEntry **entries;
-    UA_UInt32 size;
-    UA_UInt32 count;
-    UA_UInt32 sizePrimeIndex;
-};
-
-/* 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. */
-static UA_UInt32 const primes[] = {
-    7,         13,         31,         61,         127,         251,
-    509,       1021,       2039,       4093,       8191,        16381,
-    32749,     65521,      131071,     262139,     524287,      1048573,
-    2097143,   4194301,    8388593,    16777213,   33554393,    67108859,
-    134217689, 268435399,  536870909,  1073741789, 2147483647,  4294967291
-};
-
-static UA_UInt32 mod(UA_UInt32 h, UA_UInt32 size) { return h % size; }
-static UA_UInt32 mod2(UA_UInt32 h, UA_UInt32 size) { return 1 + (h % (size - 2)); }
-
-static UA_UInt16
-higher_prime_index(UA_UInt32 n) {
-    UA_UInt16 low  = 0;
-    UA_UInt16 high = (UA_UInt16)(sizeof(primes) / sizeof(UA_UInt32));
-    while(low != high) {
-        UA_UInt16 mid = (UA_UInt16)(low + ((high - low) / 2));
-        if(n > primes[mid])
-            low = (UA_UInt16)(mid + 1);
-        else
-            high = mid;
-    }
-    return low;
-}
-
-static UA_NodeStoreEntry *
-instantiateEntry(UA_NodeClass nodeClass) {
-    size_t size = sizeof(UA_NodeStoreEntry) - sizeof(UA_Node);
-    switch(nodeClass) {
-    case UA_NODECLASS_OBJECT:
-        size += sizeof(UA_ObjectNode);
-        break;
-    case UA_NODECLASS_VARIABLE:
-        size += sizeof(UA_VariableNode);
-        break;
-    case UA_NODECLASS_METHOD:
-        size += sizeof(UA_MethodNode);
-        break;
-    case UA_NODECLASS_OBJECTTYPE:
-        size += sizeof(UA_ObjectTypeNode);
-        break;
-    case UA_NODECLASS_VARIABLETYPE:
-        size += sizeof(UA_VariableTypeNode);
-        break;
-    case UA_NODECLASS_REFERENCETYPE:
-        size += sizeof(UA_ReferenceTypeNode);
-        break;
-    case UA_NODECLASS_DATATYPE:
-        size += sizeof(UA_DataTypeNode);
-        break;
-    case UA_NODECLASS_VIEW:
-        size += sizeof(UA_ViewNode);
-        break;
-    default:
-        return NULL;
-    }
-    UA_NodeStoreEntry *entry = (UA_NodeStoreEntry *)UA_calloc(1, size);
-    if(!entry)
-        return NULL;
-    entry->node.nodeClass = nodeClass;
-    return entry;
-}
-
-static void
-deleteEntry(UA_NodeStoreEntry *entry) {
-    UA_Node_deleteMembersAnyNodeClass(&entry->node);
-    UA_free(entry);
-}
-
-/* returns slot of a valid node or null */
-static UA_NodeStoreEntry **
-findNode(const UA_NodeStore *ns, const UA_NodeId *nodeid) {
-    UA_UInt32 h = UA_NodeId_hash(nodeid);
-    UA_UInt32 size = ns->size;
-    UA_UInt32 idx = mod(h, size);
-    UA_UInt32 hash2 = mod2(h, size);
-
-    while(true) {
-        UA_NodeStoreEntry *e = ns->entries[idx];
-        if(!e)
-            return NULL;
-        if(e > UA_NODESTORE_TOMBSTONE &&
-           UA_NodeId_equal(&e->node.nodeId, nodeid))
-            return &ns->entries[idx];
-        idx += hash2;
-        if(idx >= size)
-            idx -= size;
-    }
-
-    /* NOTREACHED */
-    return NULL;
-}
-
-/* returns an empty slot or null if the nodeid exists */
-static UA_NodeStoreEntry **
-findSlot(const UA_NodeStore *ns, const UA_NodeId *nodeid) {
-    UA_UInt32 h = UA_NodeId_hash(nodeid);
-    UA_UInt32 size = ns->size;
-    UA_UInt32 idx = mod(h, size);
-    UA_UInt32 hash2 = mod2(h, size);
-
-    while(true) {
-        UA_NodeStoreEntry *e = ns->entries[idx];
-        if(e > UA_NODESTORE_TOMBSTONE &&
-           UA_NodeId_equal(&e->node.nodeId, nodeid))
-            return NULL;
-        if(ns->entries[idx] <= UA_NODESTORE_TOMBSTONE)
-            return &ns->entries[idx];
-        idx += hash2;
-        if(idx >= size)
-            idx -= size;
-    }
-
-    /* NOTREACHED */
-    return NULL;
-}
-
-/* The occupancy of the table after the call will be about 50% */
-static UA_StatusCode
-expand(UA_NodeStore *ns) {
-    UA_UInt32 osize = ns->size;
-    UA_UInt32 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 <= UA_NODESTORE_MINSIZE))
-        return UA_STATUSCODE_GOOD;
-
-    UA_NodeStoreEntry **oentries = ns->entries;
-    UA_UInt32 nindex = higher_prime_index(count * 2);
-    UA_UInt32 nsize = primes[nindex];
-    UA_NodeStoreEntry **nentries = (UA_NodeStoreEntry **)UA_calloc(nsize, sizeof(UA_NodeStoreEntry*));
-    if(!nentries)
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-
-    ns->entries = nentries;
-    ns->size = nsize;
-    ns->sizePrimeIndex = nindex;
-
-    /* recompute the position of every entry and insert the pointer */
-    for(size_t i = 0, j = 0; i < osize && j < count; ++i) {
-        if(oentries[i] <= UA_NODESTORE_TOMBSTONE)
-            continue;
-        UA_NodeStoreEntry **e = findSlot(ns, &oentries[i]->node.nodeId);
-        UA_assert(e);
-        *e = oentries[i];
-        ++j;
-    }
-
-    UA_free(oentries);
-    return UA_STATUSCODE_GOOD;
-}
-
-/**********************/
-/* Exported functions */
-/**********************/
-
-UA_NodeStore *
-UA_NodeStore_new(void) {
-    UA_NodeStore *ns = (UA_NodeStore *)UA_malloc(sizeof(UA_NodeStore));
-    if(!ns)
-        return NULL;
-    ns->sizePrimeIndex = higher_prime_index(UA_NODESTORE_MINSIZE);
-    ns->size = primes[ns->sizePrimeIndex];
-    ns->count = 0;
-    ns->entries = (UA_NodeStoreEntry **)UA_calloc(ns->size, sizeof(UA_NodeStoreEntry*));
-    if(!ns->entries) {
-        UA_free(ns);
-        return NULL;
-    }
-    return ns;
-}
-
-void
-UA_NodeStore_delete(UA_NodeStore *ns) {
-    UA_UInt32 size = ns->size;
-    UA_NodeStoreEntry **entries = ns->entries;
-    for(UA_UInt32 i = 0; i < size; ++i) {
-        if(entries[i] > UA_NODESTORE_TOMBSTONE)
-            deleteEntry(entries[i]);
-    }
-    UA_free(ns->entries);
-    UA_free(ns);
-}
-
-UA_Node *
-UA_NodeStore_newNode(UA_NodeClass nodeClass) {
-    UA_NodeStoreEntry *entry = instantiateEntry(nodeClass);
-    if(!entry)
-        return NULL;
-    return &entry->node;
-}
-
-void
-UA_NodeStore_deleteNode(UA_Node *node) {
-    UA_NodeStoreEntry *entry = container_of(node, UA_NodeStoreEntry, node);
-    UA_assert(&entry->node == node);
-    deleteEntry(entry);
-}
-
-UA_StatusCode
-UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node) {
-    if(ns->size * 3 <= ns->count * 4) {
-        if(expand(ns) != UA_STATUSCODE_GOOD)
-            return UA_STATUSCODE_BADINTERNALERROR;
-    }
-
-    UA_NodeId tempNodeid;
-    tempNodeid = node->nodeId;
-    tempNodeid.namespaceIndex = 0;
-    UA_NodeStoreEntry **entry;
-    if(UA_NodeId_isNull(&tempNodeid)) {
-        /* create a random nodeid */
-        if(node->nodeId.namespaceIndex == 0)
-            node->nodeId.namespaceIndex = 1;
-        UA_UInt32 identifier = ns->count+1; // start value
-        UA_UInt32 size = ns->size;
-        UA_UInt32 increase = mod2(identifier, size);
-        while(true) {
-            node->nodeId.identifier.numeric = identifier;
-            entry = findSlot(ns, &node->nodeId);
-            if(entry)
-                break;
-            identifier += increase;
-            if(identifier >= size)
-                identifier -= size;
-        }
-    } else {
-        entry = findSlot(ns, &node->nodeId);
-        if(!entry) {
-            UA_NodeStore_deleteNode(node);
-            return UA_STATUSCODE_BADNODEIDEXISTS;
-        }
-    }
-
-    *entry = container_of(node, UA_NodeStoreEntry, node);
-    ++ns->count;
-    UA_assert(&(*entry)->node == node);
-    return UA_STATUSCODE_GOOD;
-}
-
-UA_StatusCode
-UA_NodeStore_replace(UA_NodeStore *ns, UA_Node *node) {
-    UA_NodeStoreEntry **entry = findNode(ns, &node->nodeId);
-    if(!entry)
-        return UA_STATUSCODE_BADNODEIDUNKNOWN;
-    UA_NodeStoreEntry *newEntry = container_of(node, UA_NodeStoreEntry, node);
-    if(*entry != newEntry->orig) {
-        // the node was replaced since the copy was made
-        deleteEntry(newEntry);
-        return UA_STATUSCODE_BADINTERNALERROR;
-    }
-    deleteEntry(*entry);
-    *entry = newEntry;
-    return UA_STATUSCODE_GOOD;
-}
-
-const UA_Node *
-UA_NodeStore_get(UA_NodeStore *ns, const UA_NodeId *nodeid) {
-    UA_NodeStoreEntry **entry = findNode(ns, nodeid);
-    if(!entry)
-        return NULL;
-    return (const UA_Node*)&(*entry)->node;
-}
-
-UA_Node *
-UA_NodeStore_getCopy(UA_NodeStore *ns, const UA_NodeId *nodeid) {
-    UA_NodeStoreEntry **slot = findNode(ns, nodeid);
-    if(!slot)
-        return NULL;
-    UA_NodeStoreEntry *entry = *slot;
-    UA_NodeStoreEntry *newItem = instantiateEntry(entry->node.nodeClass);
-    if(!newItem)
-        return NULL;
-    if(UA_Node_copyAnyNodeClass(&entry->node, &newItem->node) != UA_STATUSCODE_GOOD) {
-        deleteEntry(newItem);
-        return NULL;
-    }
-    newItem->orig = entry; // store the pointer to the original
-    return &newItem->node;
-}
-
-UA_StatusCode
-UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
-    UA_NodeStoreEntry **slot = findNode(ns, nodeid);
-    if(!slot)
-        return UA_STATUSCODE_BADNODEIDUNKNOWN;
-    deleteEntry(*slot);
-    *slot = UA_NODESTORE_TOMBSTONE;
-    --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.
-    return UA_STATUSCODE_GOOD;
-}
-
-void
-UA_NodeStore_iterate(UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor) {
-    for(UA_UInt32 i = 0; i < ns->size; ++i) {
-        if(ns->entries[i] > UA_NODESTORE_TOMBSTONE)
-            visitor((UA_Node*)&ns->entries[i]->node);
-    }
-}
-
-#endif /* UA_ENABLE_MULTITHREADING */

+ 0 - 99
src/server/ua_nodestore.h

@@ -1,99 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef UA_NODESTORE_H_
-#define UA_NODESTORE_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ua_nodes.h"
-
-/**
- * Nodestore
- * ---------
- * Stores nodes that can be indexed by their NodeId. Internally, it is based on
- * a hash-map implementation. */
-struct UA_NodeStore;
-typedef struct UA_NodeStore UA_NodeStore;
-
-/**
- * Nodestore Lifecycle
- * ^^^^^^^^^^^^^^^^^^^ */
-/* Create a new nodestore */
-UA_NodeStore * UA_NodeStore_new(void);
-
-/* Delete the nodestore and all nodes in it. Do not call from a read-side
-   critical section (multithreading). */
-void UA_NodeStore_delete(UA_NodeStore *ns);
-
-/**
- * Node Lifecycle
- * ^^^^^^^^^^^^^^
- *
- * The following definitions are used to create empty nodes of the different
- * node types. The memory is managed by the nodestore. Therefore, the node has
- * to be removed via a special deleteNode function. (If the new node is not
- * added to the nodestore.) */
-/* Create an editable node of the given NodeClass. */
-UA_Node * UA_NodeStore_newNode(UA_NodeClass nodeClass);
-#define UA_NodeStore_newObjectNode() \
-    (UA_ObjectNode*)UA_NodeStore_newNode(UA_NODECLASS_OBJECT)
-#define UA_NodeStore_newVariableNode() \
-    (UA_VariableNode*)UA_NodeStore_newNode(UA_NODECLASS_VARIABLE)
-#define UA_NodeStore_newMethodNode() \
-    (UA_MethodNode*)UA_NodeStore_newNode(UA_NODECLASS_METHOD)
-#define UA_NodeStore_newObjectTypeNode() \
-    (UA_ObjectTypeNode*)UA_NodeStore_newNode(UA_NODECLASS_OBJECTTYPE)
-#define UA_NodeStore_newVariableTypeNode() \
-    (UA_VariableTypeNode*)UA_NodeStore_newNode(UA_NODECLASS_VARIABLETYPE)
-#define UA_NodeStore_newReferenceTypeNode() \
-    (UA_ReferenceTypeNode*)UA_NodeStore_newNode(UA_NODECLASS_REFERENCETYPE)
-#define UA_NodeStore_newDataTypeNode() \
-    (UA_DataTypeNode*)UA_NodeStore_newNode(UA_NODECLASS_DATATYPE)
-#define UA_NodeStore_newViewNode() \
-    (UA_ViewNode*)UA_NodeStore_newNode(UA_NODECLASS_VIEW)
-
-/* Delete an editable node. */
-void UA_NodeStore_deleteNode(UA_Node *node);
-
-/**
- * Insert / Get / Replace / Remove
- * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
-/* Inserts a new node into the nodestore. If the nodeid is zero, then a fresh
- * numeric nodeid from namespace 1 is assigned. If insertion fails, the node is
- * deleted. */
-UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node);
-
-/* The returned node is immutable. */
-const UA_Node * UA_NodeStore_get(UA_NodeStore *ns, const UA_NodeId *nodeid);
-
-/* Returns an editable copy of a node (needs to be deleted with the deleteNode
-   function or inserted / replaced into the nodestore). */
-UA_Node * UA_NodeStore_getCopy(UA_NodeStore *ns, const UA_NodeId *nodeid);
-
-/* To replace a node, get an editable copy of the node, edit and replace with
- * this function. If the node was already replaced since the copy was made,
- * UA_STATUSCODE_BADINTERNALERROR is returned. If the nodeid is not found,
- * UA_STATUSCODE_BADNODEIDUNKNOWN is returned. In both error cases, the editable
- * node is deleted. */
-UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, UA_Node *node);
-
-/* Remove a node in the nodestore. */
-UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid);
-
-/**
- * Iteration
- * ^^^^^^^^^
- * The following definitions are used to call a callback for every node in the
- * nodestore. */
-typedef void (*UA_NodeStore_nodeVisitor)(const UA_Node *node);
-void UA_NodeStore_iterate(UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif /* UA_NODESTORE_H_ */

+ 0 - 235
src/server/ua_nodestore_concurrent.c

@@ -1,235 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "ua_util.h"
-#include "ua_nodestore.h"
-#include "ua_server_internal.h"
-
-#ifdef UA_ENABLE_MULTITHREADING /* conditional compilation */
-#include <urcu/rculfhash.h>
-
-struct nodeEntry {
-    struct cds_lfht_node htn; ///< Contains the next-ptr for urcu-hashmap
-    struct rcu_head rcu_head; ///< For call-rcu
-    struct nodeEntry *orig; //< the version this is a copy from (or NULL)
-    UA_Node node; ///< Might be cast from any _bigger_ UA_Node* type. Allocate enough memory!
-};
-
-static struct nodeEntry * instantiateEntry(UA_NodeClass nc) {
-    size_t size = sizeof(struct nodeEntry) - sizeof(UA_Node);
-    switch(nc) {
-    case UA_NODECLASS_OBJECT:
-        size += sizeof(UA_ObjectNode);
-        break;
-    case UA_NODECLASS_VARIABLE:
-        size += sizeof(UA_VariableNode);
-        break;
-    case UA_NODECLASS_METHOD:
-        size += sizeof(UA_MethodNode);
-        break;
-    case UA_NODECLASS_OBJECTTYPE:
-        size += sizeof(UA_ObjectTypeNode);
-        break;
-    case UA_NODECLASS_VARIABLETYPE:
-        size += sizeof(UA_VariableTypeNode);
-        break;
-    case UA_NODECLASS_REFERENCETYPE:
-        size += sizeof(UA_ReferenceTypeNode);
-        break;
-    case UA_NODECLASS_DATATYPE:
-        size += sizeof(UA_DataTypeNode);
-        break;
-    case UA_NODECLASS_VIEW:
-        size += sizeof(UA_ViewNode);
-        break;
-    default:
-        return NULL;
-    }
-    struct nodeEntry *entry = (struct nodeEntry*)UA_calloc(1, size);
-    if(!entry)
-        return NULL;
-    entry->node.nodeClass = nc;
-    return entry;
-}
-
-static void deleteEntry(struct rcu_head *head) {
-    struct nodeEntry *entry = container_of(head, struct nodeEntry, rcu_head);
-    UA_Node_deleteMembersAnyNodeClass(&entry->node);
-    UA_free(entry);
-}
-
-/* We are in a rcu_read lock. So the node will not be freed under our feet. */
-static int compare(struct cds_lfht_node *htn, const void *orig) {
-    const UA_NodeId *origid = (const UA_NodeId *)orig;
-    /* The htn is first in the entry structure. */
-    const UA_NodeId *newid  = &((struct nodeEntry *)htn)->node.nodeId;
-    return UA_NodeId_equal(newid, origid);
-}
-
-UA_NodeStore * UA_NodeStore_new() {
-    /* 64 is the minimum size for the hashtable. */
-    return (UA_NodeStore*)cds_lfht_new(64, 64, 0, CDS_LFHT_AUTO_RESIZE, NULL);
-}
-
-/* do not call with read-side critical section held!! */
-void UA_NodeStore_delete(UA_NodeStore *ns) {
-    UA_ASSERT_RCU_LOCKED();
-    struct cds_lfht *ht = (struct cds_lfht*)ns;
-    struct cds_lfht_iter iter;
-    cds_lfht_first(ht, &iter);
-    while(iter.node) {
-        if(!cds_lfht_del(ht, iter.node)) {
-            /* points to the htn entry, which is first */
-            struct nodeEntry *entry = (struct nodeEntry*) iter.node;
-            call_rcu(&entry->rcu_head, deleteEntry);
-        }
-        cds_lfht_next(ht, &iter);
-    }
-    UA_RCU_UNLOCK();
-    cds_lfht_destroy(ht, NULL);
-    UA_RCU_LOCK();
-}
-
-UA_Node * UA_NodeStore_newNode(UA_NodeClass nc) {
-    struct nodeEntry *entry = instantiateEntry(nc);
-    if(!entry)
-        return NULL;
-    return (UA_Node*)&entry->node;
-}
-
-void UA_NodeStore_deleteNode(UA_Node *node) {
-    struct nodeEntry *entry = container_of(node, struct nodeEntry, node);
-    deleteEntry(&entry->rcu_head);
-}
-
-UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node) {
-    UA_ASSERT_RCU_LOCKED();
-    struct nodeEntry *entry = container_of(node, struct nodeEntry, node);
-    struct cds_lfht *ht = (struct cds_lfht*)ns;
-    cds_lfht_node_init(&entry->htn);
-    struct cds_lfht_node *result;
-    //namespace index is assumed to be valid
-    UA_NodeId tempNodeid;
-    tempNodeid = node->nodeId;
-    tempNodeid.namespaceIndex = 0;
-    if(!UA_NodeId_isNull(&tempNodeid)) {
-        UA_UInt32 h = UA_NodeId_hash(&node->nodeId);
-        result = cds_lfht_add_unique(ht, h, compare, &node->nodeId, &entry->htn);
-        /* If the nodeid exists already */
-        if(result != &entry->htn) {
-            deleteEntry(&entry->rcu_head);
-            return UA_STATUSCODE_BADNODEIDEXISTS;
-        }
-    } else {
-        /* create a unique nodeid */
-        node->nodeId.identifierType = UA_NODEIDTYPE_NUMERIC;
-        if(node->nodeId.namespaceIndex == 0) // original request for ns=0 should yield ns=1
-            node->nodeId.namespaceIndex = 1;
-
-        unsigned long identifier;
-        long before, after;
-        cds_lfht_count_nodes(ht, &before, &identifier, &after); // current number of nodes stored
-        ++identifier;
-
-        node->nodeId.identifier.numeric = (UA_UInt32)identifier;
-        while(true) {
-            UA_UInt32 h = UA_NodeId_hash(&node->nodeId);
-            result = cds_lfht_add_unique(ht, h, compare, &node->nodeId, &entry->htn);
-            if(result == &entry->htn)
-                break;
-            node->nodeId.identifier.numeric += (UA_UInt32)(identifier * 2654435761);
-        }
-    }
-    return UA_STATUSCODE_GOOD;
-}
-
-UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, UA_Node *node) {
-    UA_ASSERT_RCU_LOCKED();
-    struct nodeEntry *entry = container_of(node, struct nodeEntry, node);
-    struct cds_lfht *ht = (struct cds_lfht*)ns;
-
-    /* Get the current version */
-    UA_UInt32 h = UA_NodeId_hash(&node->nodeId);
-    struct cds_lfht_iter iter;
-    cds_lfht_lookup(ht, h, compare, &node->nodeId, &iter);
-    if(!iter.node)
-        return UA_STATUSCODE_BADNODEIDUNKNOWN;
-
-    /* We try to replace an obsolete version of the node */
-    struct nodeEntry *oldEntry = (struct nodeEntry*)iter.node;
-    if(oldEntry != entry->orig) {
-        deleteEntry(&entry->rcu_head);
-        return UA_STATUSCODE_BADINTERNALERROR;
-    }
-    
-    cds_lfht_node_init(&entry->htn);
-    if(cds_lfht_replace(ht, &iter, h, compare, &node->nodeId, &entry->htn) != 0) {
-        /* Replacing failed. Maybe the node got replaced just before this thread tried to.*/
-        deleteEntry(&entry->rcu_head);
-        return UA_STATUSCODE_BADINTERNALERROR;
-    }
-        
-    /* If an entry got replaced, mark it as dead. */
-    call_rcu(&oldEntry->rcu_head, deleteEntry);
-    return UA_STATUSCODE_GOOD;
-}
-
-UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
-    UA_ASSERT_RCU_LOCKED();
-    struct cds_lfht *ht = (struct cds_lfht*)ns;
-    UA_UInt32 h = UA_NodeId_hash(nodeid);
-    struct cds_lfht_iter iter;
-    cds_lfht_lookup(ht, h, compare, nodeid, &iter);
-    if(!iter.node || cds_lfht_del(ht, iter.node) != 0)
-        return UA_STATUSCODE_BADNODEIDUNKNOWN;
-    struct nodeEntry *entry = (struct nodeEntry*)iter.node;
-    call_rcu(&entry->rcu_head, deleteEntry);
-    return UA_STATUSCODE_GOOD;
-}
-
-const UA_Node * UA_NodeStore_get(UA_NodeStore *ns, const UA_NodeId *nodeid) {
-    UA_ASSERT_RCU_LOCKED();
-    struct cds_lfht *ht = (struct cds_lfht*)ns;
-    UA_UInt32 h = UA_NodeId_hash(nodeid);
-    struct cds_lfht_iter iter;
-    cds_lfht_lookup(ht, h, compare, nodeid, &iter);
-    struct nodeEntry *found_entry = (struct nodeEntry*)iter.node;
-    if(!found_entry)
-        return NULL;
-    return &found_entry->node;
-}
-
-UA_Node * UA_NodeStore_getCopy(UA_NodeStore *ns, const UA_NodeId *nodeid) {
-    UA_ASSERT_RCU_LOCKED();
-    struct cds_lfht *ht = (struct cds_lfht*)ns;
-    UA_UInt32 h = UA_NodeId_hash(nodeid);
-    struct cds_lfht_iter iter;
-    cds_lfht_lookup(ht, h, compare, nodeid, &iter);
-    struct nodeEntry *entry = (struct nodeEntry*)iter.node;
-    if(!entry)
-        return NULL;
-    struct nodeEntry *n = instantiateEntry(entry->node.nodeClass);
-    if(!n)
-        return NULL;
-    if(UA_Node_copyAnyNodeClass(&entry->node, &n->node) != UA_STATUSCODE_GOOD) {
-        deleteEntry(&n->rcu_head);
-        return NULL;
-    }
-    n->orig = entry;
-    return &n->node;
-}
-
-void UA_NodeStore_iterate(UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor) {
-    UA_ASSERT_RCU_LOCKED();
-    struct cds_lfht *ht = (struct cds_lfht*)ns;
-    struct cds_lfht_iter iter;
-    cds_lfht_first(ht, &iter);
-    while(iter.node != NULL) {
-        struct nodeEntry *found_entry = (struct nodeEntry*)iter.node;
-        visitor(&found_entry->node);
-        cds_lfht_next(ht, &iter);
-    }
-}
-
-#endif /* UA_ENABLE_MULTITHREADING */