|
@@ -19,87 +19,7 @@ struct UA_NodeStore {
|
|
|
struct cds_lfht *ht; /* Hash table */
|
|
|
};
|
|
|
|
|
|
-/********/
|
|
|
-/* Hash */
|
|
|
-/********/
|
|
|
-
|
|
|
-typedef UA_UInt32 hash_t;
|
|
|
-
|
|
|
-/* Based on Murmur-Hash 3 by Austin Appleby (public domain, freely usable) */
|
|
|
-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;
|
|
|
- static const uint32_t r2 = 13;
|
|
|
- static const uint32_t m = 5;
|
|
|
- static const uint32_t n = 0xe6546b64;
|
|
|
- hash_t hash = seed;
|
|
|
-
|
|
|
- if(data == UA_NULL) return 0;
|
|
|
-
|
|
|
- const int32_t nblocks = len / 4;
|
|
|
- const uint32_t *blocks = (const uint32_t *)data;
|
|
|
- for(int32_t i = 0;i < nblocks;i++) {
|
|
|
- uint32_t k = blocks[i];
|
|
|
- k *= c1;
|
|
|
- k = (k << r1) | (k >> (32 - r1));
|
|
|
- k *= c2;
|
|
|
- hash ^= k;
|
|
|
- hash = ((hash << r2) | (hash >> (32 - r2))) * m + n;
|
|
|
- }
|
|
|
-
|
|
|
- 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;
|
|
|
- k1 = (k1 << r1) | (k1 >> (32 - r1));
|
|
|
- k1 *= c2;
|
|
|
- hash ^= k1;
|
|
|
- }
|
|
|
-
|
|
|
- hash ^= len;
|
|
|
- hash ^= (hash >> 16);
|
|
|
- hash *= 0x85ebca6b;
|
|
|
- hash ^= (hash >> 13);
|
|
|
- hash *= 0xc2b2ae35;
|
|
|
- hash ^= (hash >> 16);
|
|
|
-
|
|
|
- return hash;
|
|
|
-}
|
|
|
-
|
|
|
-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;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/****************/
|
|
|
-/* UA_NodeStore */
|
|
|
-/****************/
|
|
|
+#include "ua_nodestore_hash.inc"
|
|
|
|
|
|
static inline void node_deleteMembers(const UA_Node *node) {
|
|
|
switch(node->nodeClass) {
|
|
@@ -143,8 +63,8 @@ static inline void node_deleteMembers(const UA_Node *node) {
|
|
|
|
|
|
/* 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) {
|
|
|
- UA_NodeId *origid = (UA_NodeId *)orig;
|
|
|
- UA_NodeId *newid = &((UA_NodeStore_Entry *)htn)->node.nodeId; /* The htn is first in the entry structure. */
|
|
|
+ const UA_NodeId *origid = (const UA_NodeId *)orig;
|
|
|
+ const UA_NodeId *newid = &((UA_NodeStore_Entry *)htn)->node.nodeId; /* The htn is first in the entry structure. */
|
|
|
|
|
|
return UA_NodeId_equal(newid, origid);
|
|
|
}
|
|
@@ -155,7 +75,8 @@ static int compare(struct cds_lfht_node *htn, const void *orig) {
|
|
|
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_and(&entry->refcount, ~ALIVE_BIT) > 0)
|
|
|
+ uatomic_and(&entry->refcount, ~ALIVE_BIT); // set the alive bit to zero
|
|
|
+ if(uatomic_read(&entry->refcount) > 0)
|
|
|
return;
|
|
|
|
|
|
node_deleteMembers(&entry->node);
|
|
@@ -174,20 +95,18 @@ void UA_NodeStore_release(const UA_Node *managed) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
-UA_StatusCode UA_NodeStore_new(UA_NodeStore **result) {
|
|
|
+UA_NodeStore * UA_NodeStore_new() {
|
|
|
UA_NodeStore *ns;
|
|
|
if(!(ns = UA_alloc(sizeof(UA_NodeStore))))
|
|
|
- return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
+ return UA_NULL;
|
|
|
|
|
|
/* 32 is the minimum size for the hashtable. */
|
|
|
ns->ht = cds_lfht_new(32, 32, 0, CDS_LFHT_AUTO_RESIZE, NULL);
|
|
|
if(!ns->ht) {
|
|
|
UA_free(ns);
|
|
|
- return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
+ return UA_NULL;
|
|
|
}
|
|
|
-
|
|
|
- *result = ns;
|
|
|
- return UA_STATUSCODE_GOOD;
|
|
|
+ return ns;
|
|
|
}
|
|
|
|
|
|
void UA_NodeStore_delete(UA_NodeStore *ns) {
|
|
@@ -211,42 +130,34 @@ void UA_NodeStore_delete(UA_NodeStore *ns) {
|
|
|
UA_free(ns);
|
|
|
}
|
|
|
|
|
|
-UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node **node, UA_Byte flags) {
|
|
|
+UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, const UA_Node **node, UA_Boolean getManaged) {
|
|
|
UA_UInt32 nodesize;
|
|
|
/* 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:
|
|
|
return UA_STATUSCODE_BADINTERNALERROR;
|
|
|
}
|
|
@@ -254,18 +165,18 @@ UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node **node, UA_Byte flag
|
|
|
UA_NodeStore_Entry *entry;
|
|
|
if(!(entry = UA_alloc(sizeof(UA_NodeStore_Entry) - sizeof(UA_Node) + nodesize)))
|
|
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
- memcpy(&entry->node, *node, nodesize);
|
|
|
+ memcpy((void*)&entry->node, *node, nodesize);
|
|
|
|
|
|
cds_lfht_node_init(&entry->htn);
|
|
|
entry->refcount = ALIVE_BIT;
|
|
|
- if(flags & UA_NODESTORE_INSERT_GETMANAGED)
|
|
|
+ if(getManaged) // increase the counter before adding the node
|
|
|
entry->refcount++;
|
|
|
|
|
|
- hash_t nhash = hash(&(*node)->nodeId);
|
|
|
struct cds_lfht_node *result;
|
|
|
- if(flags & UA_NODESTORE_INSERT_UNIQUE) {
|
|
|
+ if(!UA_NodeId_isNull(&(*node)->nodeId)) {
|
|
|
+ hash_t h = hash(&(*node)->nodeId);
|
|
|
rcu_read_lock();
|
|
|
- result = cds_lfht_add_unique(ns->ht, nhash, compare, &entry->node.nodeId, &entry->htn);
|
|
|
+ result = cds_lfht_add_unique(ns->ht, h, compare, &entry->node.nodeId, &entry->htn);
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
/* If the nodeid exists already */
|
|
@@ -274,23 +185,106 @@ UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node **node, UA_Byte flag
|
|
|
return UA_STATUSCODE_BADNODEIDEXISTS;
|
|
|
}
|
|
|
} else {
|
|
|
+ /* create a unique nodeid */
|
|
|
+ ((UA_Node *)&entry->node)->nodeId.identifierType = UA_NODEIDTYPE_NUMERIC;
|
|
|
+ ((UA_Node *)&entry->node)->nodeId.namespaceIndex = 1; // namespace 1 is always in the local nodestore
|
|
|
+ unsigned long identifier;
|
|
|
+ long before, after;
|
|
|
rcu_read_lock();
|
|
|
- result = cds_lfht_add_replace(ns->ht, nhash, compare, &(*node)->nodeId, &entry->htn);
|
|
|
- /* If an entry got replaced, mark it as dead. */
|
|
|
- if(result) {
|
|
|
- UA_NodeStore_Entry *entry = caa_container_of(result, UA_NodeStore_Entry, htn);
|
|
|
- call_rcu(&entry->rcu_head, markDead); /* Queue this for the next time when no readers are on the entry.*/
|
|
|
- }
|
|
|
+ cds_lfht_count_nodes(ns->ht, &before, &identifier, &after); // current amount of nodes stored
|
|
|
rcu_read_unlock();
|
|
|
+ identifier++;
|
|
|
+
|
|
|
+ ((UA_Node *)&entry->node)->nodeId.identifier.numeric = identifier;
|
|
|
+ while(UA_TRUE) {
|
|
|
+ hash_t nhash = hash(&entry->node.nodeId);
|
|
|
+ rcu_read_lock();
|
|
|
+ result = cds_lfht_add_unique(ns->ht, nhash, compare, &entry->node.nodeId, &entry->htn);
|
|
|
+ rcu_read_unlock();
|
|
|
+ if(result == &entry->htn)
|
|
|
+ break;
|
|
|
+
|
|
|
+ ((UA_Node *)&entry->node)->nodeId.identifier.numeric += (identifier * 2654435761);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
UA_free((UA_Node *)*node); /* The old node is replaced by a managed node. */
|
|
|
- if(flags & UA_NODESTORE_INSERT_GETMANAGED)
|
|
|
+ if(getManaged)
|
|
|
+ *node = &entry->node;
|
|
|
+ else
|
|
|
+ *node = UA_NULL;
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
+UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, const UA_Node **node, UA_Boolean getManaged) {
|
|
|
+ UA_UInt32 nodesize;
|
|
|
+ /* 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:
|
|
|
+ return UA_STATUSCODE_BADINTERNALERROR;
|
|
|
+ }
|
|
|
+
|
|
|
+ UA_NodeStore_Entry *entry;
|
|
|
+ if(!(entry = UA_alloc(sizeof(UA_NodeStore_Entry) - sizeof(UA_Node) + nodesize)))
|
|
|
+ return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
+ memcpy((void*)&entry->node, *node, nodesize);
|
|
|
+
|
|
|
+ cds_lfht_node_init(&entry->htn);
|
|
|
+ entry->refcount = ALIVE_BIT;
|
|
|
+ if(getManaged) // increase the counter before adding the node
|
|
|
+ entry->refcount++;
|
|
|
+
|
|
|
+ hash_t h = hash(&(*node)->nodeId);
|
|
|
+
|
|
|
+ struct cds_lfht_iter iter;
|
|
|
+ rcu_read_lock();
|
|
|
+ cds_lfht_lookup(ns->ht, h, compare, &(*node)->nodeId, &iter);
|
|
|
+ struct cds_lfht_node *result = cds_lfht_iter_get_node(&iter);
|
|
|
+
|
|
|
+ if(result && cds_lfht_replace(ns->ht, &iter, h, compare, &(*node)->nodeId, &entry->htn) == 0) {
|
|
|
+ UA_NodeStore_Entry *entry = caa_container_of(result, UA_NodeStore_Entry, htn);
|
|
|
+ /* If an entry got replaced, mark it as dead. */
|
|
|
+ call_rcu(&entry->rcu_head, markDead);
|
|
|
+ rcu_read_unlock();
|
|
|
+ } else {
|
|
|
+ rcu_read_unlock();
|
|
|
+ UA_free(entry);
|
|
|
+ return UA_STATUSCODE_BADNODEIDUNKNOWN;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* The old node is replaced by a managed node. */
|
|
|
+ UA_free((UA_Node *)*node);
|
|
|
+ if(getManaged)
|
|
|
*node = &entry->node;
|
|
|
else
|
|
|
*node = UA_NULL;
|
|
|
|
|
|
return UA_STATUSCODE_GOOD;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
|
|
@@ -314,7 +308,7 @@ UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
|
|
|
return UA_STATUSCODE_GOOD;
|
|
|
}
|
|
|
|
|
|
-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) {
|
|
|
hash_t nhash = hash(nodeid);
|
|
|
struct cds_lfht_iter iter;
|
|
|
|
|
@@ -324,15 +318,13 @@ UA_StatusCode UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid,
|
|
|
|
|
|
if(!found_entry) {
|
|
|
rcu_read_unlock();
|
|
|
- return UA_STATUSCODE_BADNODEIDUNKNOWN;
|
|
|
+ return UA_NULL;
|
|
|
}
|
|
|
|
|
|
/* This is done within a read-lock. The node will not be marked dead within a read-lock. */
|
|
|
uatomic_inc(&found_entry->refcount);
|
|
|
rcu_read_unlock();
|
|
|
-
|
|
|
- *managedNode = &found_entry->node;
|
|
|
- return UA_STATUSCODE_GOOD;
|
|
|
+ return &found_entry->node;
|
|
|
}
|
|
|
|
|
|
void UA_NodeStore_iterate(const UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor) {
|