|
@@ -2,26 +2,11 @@
|
|
|
#include <string.h>
|
|
|
#include <stdio.h>
|
|
|
|
|
|
-static hash_t hash_string(const UA_Byte * data, UA_Int32 len);
|
|
|
-static hash_t hash(const UA_NodeId * n);
|
|
|
-static unsigned int higher_prime_index(unsigned long n);
|
|
|
-static inline hash_t mod_1(hash_t x, hash_t y, hash_t inv, int shift);
|
|
|
-static inline hash_t mod(hash_t hash, const namespace * ns);
|
|
|
-static inline hash_t htab_mod_m2(hash_t hash, const namespace * ns);
|
|
|
-static inline void clear_slot(namespace * ns, ns_entry * slot);
|
|
|
-static void clear_ns(namespace * ns);
|
|
|
-static UA_Int32 find_slot(const namespace * ns, ns_entry ** slot, const UA_NodeId * nodeid);
|
|
|
-static ns_entry *find_empty_slot(const namespace * ns, hash_t hash);
|
|
|
-static UA_Int32 expand(namespace * ns);
|
|
|
-
|
|
|
-/* /\* We store UA_MethodNode_Callback instead of UA_MethodNode in the namespace. Pointer casting to */
|
|
|
-/* UA_MethodNode is possible since pointers point to the first element anyway. *\/ */
|
|
|
-/* typedef struct UA_MethodNode_Callback_T { */
|
|
|
-/* UA_MethodNode *method_node; */
|
|
|
-/* UA_Int32(*method_callback) (UA_list_List * input_args, UA_list_List * output_args); */
|
|
|
-/* } UA_MethodNode_Callback; */
|
|
|
-
|
|
|
-UA_Int32 init_tc(transaction_context * tc) {
|
|
|
+/*****************************************/
|
|
|
+/* Internal (not exported) functionality */
|
|
|
+/*****************************************/
|
|
|
+
|
|
|
+UA_Int32 Namespace_TransactionContext_init(Namespace_TransactionContext * tc) {
|
|
|
return UA_list_init((UA_list_List *) tc);
|
|
|
}
|
|
|
|
|
@@ -29,6 +14,7 @@ UA_Int32 init_tc(transaction_context * tc) {
|
|
|
Knuth's TAOCP (no linked lists here). Table of primes and mod-functions are from libiberty
|
|
|
(licensed under LGPL) */
|
|
|
|
|
|
+typedef UA_UInt32 hash_t;
|
|
|
struct prime_ent {
|
|
|
hash_t prime;
|
|
|
hash_t inv;
|
|
@@ -70,164 +56,6 @@ static struct prime_ent const prime_tab[] = {
|
|
|
{0xfffffffb, 0x00000006, 0x00000008, 31}
|
|
|
};
|
|
|
|
|
|
-UA_Int32 create_ns(namespace ** result, uint32_t size) {
|
|
|
- namespace *ns = UA_NULL;
|
|
|
- uint32_t sizePrimeIndex = higher_prime_index(size);
|
|
|
- size = prime_tab[sizePrimeIndex].prime;
|
|
|
-
|
|
|
- if(UA_alloc((void **)&ns, sizeof(namespace)) != UA_SUCCESS)
|
|
|
- return UA_ERR_NO_MEMORY;
|
|
|
-
|
|
|
- if(UA_alloc((void **)&ns->entries, sizeof(ns_entry) * size) != UA_SUCCESS) {
|
|
|
- UA_free(ns);
|
|
|
- return UA_ERR_NO_MEMORY;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- set entries to zero:
|
|
|
- ns->entries[i].lock = UA_NUll;
|
|
|
- ns->entries[i].node = UA_NULL;
|
|
|
- */
|
|
|
- memset(ns->entries, 0, size * sizeof(ns_entry));
|
|
|
-
|
|
|
- ns->size = size;
|
|
|
- ns->count = 0;
|
|
|
- ns->sizePrimeIndex = sizePrimeIndex;
|
|
|
- *result = ns;
|
|
|
- return UA_SUCCESS;
|
|
|
-}
|
|
|
-
|
|
|
-void empty_ns(namespace * ns) {
|
|
|
- clear_ns(ns);
|
|
|
-
|
|
|
- /* Downsize the table. */
|
|
|
- if(ns->size > 1024 * 1024 / sizeof(ns_entry)) {
|
|
|
- int nindex = higher_prime_index(1024 / sizeof(ns_entry));
|
|
|
- int nsize = prime_tab[nindex].prime;
|
|
|
- UA_free(ns->entries);
|
|
|
- UA_alloc((void **)&ns->entries, sizeof(ns_entry) * nsize); // FIXME: Check return value
|
|
|
- ns->size = nsize;
|
|
|
- ns->sizePrimeIndex = nindex;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/* This function frees all memory allocated for a namespace */
|
|
|
-void delete_ns(namespace * ns) {
|
|
|
- clear_ns(ns);
|
|
|
- UA_free(ns->entries);
|
|
|
- UA_free(ns);
|
|
|
-}
|
|
|
-
|
|
|
-UA_Int32 insert_node(namespace * ns, UA_Node * node) {
|
|
|
- if(ns->size * 3 <= ns->count * 4) {
|
|
|
- if(expand(ns) != UA_SUCCESS)
|
|
|
- return UA_ERROR;
|
|
|
- }
|
|
|
-
|
|
|
- hash_t h = hash(&node->nodeId);
|
|
|
- ns_entry *slot = find_empty_slot(ns, h);
|
|
|
-
|
|
|
-#ifdef MULTITHREADING
|
|
|
- if(UA_alloc((void **)&slot->lock, sizeof(pthread_rwlock_t)) != UA_SUCCESS)
|
|
|
- return UA_ERR_NO_MEMORY;
|
|
|
- pthread_rwlock_init((pthread_rwlock_t *) slot->lock, NULL);
|
|
|
-#endif
|
|
|
-
|
|
|
- slot->node = node;
|
|
|
- ns->count++;
|
|
|
- return UA_SUCCESS;
|
|
|
-}
|
|
|
-
|
|
|
-UA_Int32 get_node(namespace const *ns, const UA_NodeId * nodeid, UA_Node const **result,
|
|
|
- ns_lock ** lock) {
|
|
|
- ns_entry *slot;
|
|
|
- if(find_slot(ns, &slot, nodeid) != UA_SUCCESS)
|
|
|
- return UA_ERROR;
|
|
|
-
|
|
|
-#ifdef MULTITHREADING
|
|
|
- if(pthread_rwlock_rdlock((pthread_rwlock_t *) slot->lock) != 0)
|
|
|
- return UA_ERROR;
|
|
|
- *lock = slot->lock;
|
|
|
-#endif
|
|
|
-
|
|
|
- *result = slot->node;
|
|
|
- return UA_SUCCESS;
|
|
|
-}
|
|
|
-
|
|
|
-UA_Int32 get_writable_node(const namespace * ns, const UA_NodeId * nodeid, UA_Node ** result,
|
|
|
- ns_lock ** lock) {
|
|
|
- ns_entry *slot;
|
|
|
- if(find_slot(ns, &slot, nodeid) != UA_SUCCESS)
|
|
|
- return UA_ERROR;
|
|
|
-
|
|
|
-#ifdef MULTITHREADING
|
|
|
- if(pthread_rwlock_wrlock((pthread_rwlock_t *) slot->lock) != 0)
|
|
|
- return UA_ERROR;
|
|
|
- *lock = slot->lock;
|
|
|
-#endif
|
|
|
-
|
|
|
- *result = slot->node;
|
|
|
- return UA_SUCCESS;
|
|
|
-}
|
|
|
-
|
|
|
-#ifdef MULTITHREADING
|
|
|
-static inline void release_context_walker(void *lock) {
|
|
|
- pthread_rwlock_unlock(lock);
|
|
|
-}
|
|
|
-#endif
|
|
|
-
|
|
|
-UA_Int32 get_tc_node(namespace * ns, transaction_context * tc, const UA_NodeId * nodeid,
|
|
|
- UA_Node ** const result, ns_lock ** lock) {
|
|
|
- ns_entry *slot;
|
|
|
- if(find_slot(ns, &slot, nodeid) != UA_SUCCESS)
|
|
|
- return UA_ERROR;
|
|
|
-
|
|
|
-#ifdef MULTITHREADING
|
|
|
- if(pthread_rwlock_tryrdlock((pthread_rwlock_t *) slot->lock) != 0) {
|
|
|
- /* Transaction failed. Release all acquired locks and bail out. */
|
|
|
- UA_list_destroy((UA_list_List *) tc, release_context_walker);
|
|
|
- return UA_ERROR;
|
|
|
- }
|
|
|
- UA_list_addPayloadToBack((UA_list_List *) tc, slot->lock);
|
|
|
- *lock = slot->lock;
|
|
|
-#endif
|
|
|
-
|
|
|
- *result = slot->node;
|
|
|
- return UA_SUCCESS;
|
|
|
-}
|
|
|
-
|
|
|
-UA_Int32 get_tc_writable_node(namespace * ns, transaction_context * tc, const UA_NodeId * nodeid,
|
|
|
- UA_Node ** result, ns_lock ** lock) {
|
|
|
- ns_entry *slot;
|
|
|
- if(find_slot(ns, &slot, nodeid) != UA_SUCCESS)
|
|
|
- return UA_ERROR;
|
|
|
-
|
|
|
-#ifdef MULTITHREADING
|
|
|
- if(pthread_rwlock_trywrlock((pthread_rwlock_t *) slot->lock) != 0) {
|
|
|
- /* Transaction failed. Release all acquired locks and bail out. */
|
|
|
- UA_list_destroy((UA_list_List *) tc, release_context_walker);
|
|
|
- return UA_ERROR;
|
|
|
- }
|
|
|
- UA_list_addPayloadToBack((UA_list_List *) tc, slot->lock);
|
|
|
- *lock = slot->lock;
|
|
|
-#endif
|
|
|
-
|
|
|
- *result = slot->node;
|
|
|
- return UA_SUCCESS;
|
|
|
-}
|
|
|
-
|
|
|
-void delete_node(namespace * ns, UA_NodeId * nodeid) {
|
|
|
- ns_entry *slot;
|
|
|
- if(find_slot(ns, &slot, nodeid) != UA_SUCCESS)
|
|
|
- return;
|
|
|
- // TODO: Check if deleting the node makes the namespace inconsistent.
|
|
|
- clear_slot(ns, slot);
|
|
|
-
|
|
|
- /* Downsize the hashmap if it is very empty */
|
|
|
- if(ns->count * 8 < ns->size && ns->size > 32)
|
|
|
- expand(ns);
|
|
|
-}
|
|
|
-
|
|
|
/* Hashing inspired by code from from http://www.azillionmonkeys.com/qed/hash.html, licensed under
|
|
|
the LGPL 2.1 */
|
|
|
#undef get16bits
|
|
@@ -237,11 +65,11 @@ void delete_node(namespace * ns, UA_NodeId * nodeid) {
|
|
|
#endif
|
|
|
|
|
|
#if !defined (get16bits)
|
|
|
-#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) + \
|
|
|
- (uint32_t)(((const uint8_t *)(d))[0]) )
|
|
|
+#define get16bits(d) ((((UA_UInt32)(((const uint8_t *)(d))[1])) << 8) + \
|
|
|
+ (UA_UInt32)(((const uint8_t *)(d))[0]) )
|
|
|
#endif
|
|
|
|
|
|
-static hash_t hash_string(const UA_Byte * data, UA_Int32 len) {
|
|
|
+static inline hash_t hash_string(const UA_Byte * data, UA_Int32 len) {
|
|
|
hash_t hash = len;
|
|
|
hash_t tmp;
|
|
|
int rem;
|
|
@@ -292,7 +120,7 @@ static hash_t hash_string(const UA_Byte * data, UA_Int32 len) {
|
|
|
return hash;
|
|
|
}
|
|
|
|
|
|
-static hash_t hash(const UA_NodeId * n) {
|
|
|
+static inline hash_t hash(const UA_NodeId * n) {
|
|
|
switch (n->encodingByte) {
|
|
|
case UA_NODEIDTYPE_TWOBYTE:
|
|
|
case UA_NODEIDTYPE_FOURBYTE:
|
|
@@ -303,8 +131,7 @@ static hash_t hash(const UA_NodeId * n) {
|
|
|
case UA_NODEIDTYPE_GUID:
|
|
|
return hash_string((UA_Byte *) & (n->identifier.guid), sizeof(UA_Guid));
|
|
|
case UA_NODEIDTYPE_BYTESTRING:
|
|
|
- return hash_string((UA_Byte *) n->identifier.byteString.data,
|
|
|
- n->identifier.byteString.length);
|
|
|
+ return hash_string((UA_Byte *) n->identifier.byteString.data, n->identifier.byteString.length);
|
|
|
default:
|
|
|
return 0;
|
|
|
}
|
|
@@ -312,7 +139,7 @@ static hash_t hash(const UA_NodeId * n) {
|
|
|
|
|
|
/* The following function returns an index into the above table of the nearest prime number which
|
|
|
is greater than N, and near a power of two. */
|
|
|
-static unsigned int higher_prime_index(unsigned long n) {
|
|
|
+static inline unsigned int higher_prime_index(unsigned long n) {
|
|
|
unsigned int low = 0;
|
|
|
unsigned int high = sizeof(prime_tab) / sizeof(prime_tab[0]);
|
|
|
|
|
@@ -324,14 +151,10 @@ static unsigned int higher_prime_index(unsigned long n) {
|
|
|
high = mid;
|
|
|
}
|
|
|
|
|
|
- /* if (n > prime_tab[low].prime) */
|
|
|
- /* abort (); */
|
|
|
return low;
|
|
|
}
|
|
|
|
|
|
-/* Return X % Y. */
|
|
|
static inline hash_t mod_1(hash_t x, hash_t y, hash_t inv, int shift) {
|
|
|
-
|
|
|
/* The multiplicative inverses computed above are for 32-bit types, and requires that we are
|
|
|
able to compute a highpart multiply. */
|
|
|
#ifdef UNSIGNED_64BIT_TYPE
|
|
@@ -347,21 +170,20 @@ static inline hash_t mod_1(hash_t x, hash_t y, hash_t inv, int shift) {
|
|
|
return r;
|
|
|
}
|
|
|
#endif
|
|
|
-
|
|
|
- return x % y; /* Otherwise just use the native division routines. */
|
|
|
+ return x % y;
|
|
|
}
|
|
|
|
|
|
-static inline hash_t mod(hash_t h, const namespace * ns) {
|
|
|
+static inline hash_t mod(hash_t h, const Namespace * ns) {
|
|
|
const struct prime_ent *p = &prime_tab[ns->sizePrimeIndex];
|
|
|
return mod_1(h, p->prime, p->inv, p->shift);
|
|
|
}
|
|
|
|
|
|
-static inline hash_t mod_m2(hash_t h, const namespace * ns) {
|
|
|
+static inline hash_t mod_m2(hash_t h, const Namespace * ns) {
|
|
|
const struct prime_ent *p = &prime_tab[ns->sizePrimeIndex];
|
|
|
return 1 + mod_1(h, p->prime - 2, p->inv_m2, p->shift);
|
|
|
}
|
|
|
|
|
|
-static inline void clear_slot(namespace * ns, ns_entry * slot) {
|
|
|
+static inline void clear_slot(Namespace * ns, Namespace_Entry * slot) {
|
|
|
if(slot->node == UA_NULL)
|
|
|
return;
|
|
|
|
|
@@ -406,20 +228,11 @@ static inline void clear_slot(namespace * ns, ns_entry * slot) {
|
|
|
|
|
|
}
|
|
|
|
|
|
-/* Delete all entries */
|
|
|
-static void clear_ns(namespace * ns) {
|
|
|
- uint32_t size = ns->size;
|
|
|
- ns_entry *entries = ns->entries;
|
|
|
- for(uint32_t i = 0; i < size; i++)
|
|
|
- clear_slot(ns, &entries[i]);
|
|
|
- ns->count = 0;
|
|
|
-}
|
|
|
-
|
|
|
-static UA_Int32 find_slot(const namespace * ns, ns_entry ** slot, const UA_NodeId * nodeid) {
|
|
|
+static inline UA_Int32 find_slot(const Namespace * ns, Namespace_Entry ** slot, const UA_NodeId * nodeid) {
|
|
|
hash_t h = hash(nodeid);
|
|
|
hash_t index, hash2;
|
|
|
- uint32_t size;
|
|
|
- ns_entry *entry;
|
|
|
+ UA_UInt32 size;
|
|
|
+ Namespace_Entry *entry;
|
|
|
|
|
|
size = ns->size;
|
|
|
index = mod(h, ns);
|
|
@@ -449,21 +262,11 @@ static UA_Int32 find_slot(const namespace * ns, ns_entry ** slot, const UA_NodeI
|
|
|
return UA_SUCCESS;
|
|
|
}
|
|
|
|
|
|
-UA_Int32 iterate_ns(const namespace * ns, node_visitor visitor) {
|
|
|
- uint32_t i;
|
|
|
- for(i = 0; i < ns->size; i++) {
|
|
|
- ns_entry *entry = &ns->entries[i];
|
|
|
- if(entry != UA_NULL && visitor != UA_NULL)
|
|
|
- visitor(entry->node);
|
|
|
- }
|
|
|
- return UA_SUCCESS;
|
|
|
-}
|
|
|
-
|
|
|
/* Always returns an empty slot. This is inevitable if the entries are not completely full. */
|
|
|
-static ns_entry *find_empty_slot(const namespace * ns, hash_t h) {
|
|
|
+static Namespace_Entry *find_empty_slot(const Namespace * ns, hash_t h) {
|
|
|
hash_t index = mod(h, ns);
|
|
|
- uint32_t size = ns->size;
|
|
|
- ns_entry *slot = &ns->entries[index];
|
|
|
+ UA_UInt32 size = ns->size;
|
|
|
+ Namespace_Entry *slot = &ns->entries[index];
|
|
|
|
|
|
if(slot->node == UA_NULL)
|
|
|
return slot;
|
|
@@ -486,14 +289,14 @@ static ns_entry *find_empty_slot(const namespace * ns, hash_t h) {
|
|
|
hash table must already exist. Remember also that the place of the table entries is changed. If
|
|
|
memory allocation failures are allowed, this function will return zero, indicating that the
|
|
|
table could not be expanded. If all goes well, it will return a non-zero value. */
|
|
|
-static UA_Int32 expand(namespace * ns) {
|
|
|
- ns_entry *nentries;
|
|
|
+static UA_Int32 expand(Namespace * ns) {
|
|
|
+ Namespace_Entry *nentries;
|
|
|
int32_t nsize;
|
|
|
- uint32_t nindex;
|
|
|
+ UA_UInt32 nindex;
|
|
|
|
|
|
- ns_entry *oentries = ns->entries;
|
|
|
+ Namespace_Entry *oentries = ns->entries;
|
|
|
int32_t osize = ns->size;
|
|
|
- ns_entry *olimit = &oentries[osize];
|
|
|
+ Namespace_Entry *olimit = &oentries[osize];
|
|
|
int32_t count = ns->count;
|
|
|
|
|
|
/* Resize only when table after removal of unused elements is either too full or too empty. */
|
|
@@ -504,16 +307,16 @@ static UA_Int32 expand(namespace * ns) {
|
|
|
nindex = higher_prime_index(count * 2);
|
|
|
nsize = prime_tab[nindex].prime;
|
|
|
|
|
|
- if(UA_alloc((void **)&nentries, sizeof(ns_entry) * nsize) != UA_SUCCESS)
|
|
|
+ if(UA_alloc((void **)&nentries, sizeof(Namespace_Entry) * nsize) != UA_SUCCESS)
|
|
|
return UA_ERR_NO_MEMORY;
|
|
|
ns->entries = nentries;
|
|
|
ns->size = nsize;
|
|
|
ns->sizePrimeIndex = nindex;
|
|
|
|
|
|
- ns_entry *p = oentries;
|
|
|
+ Namespace_Entry *p = oentries;
|
|
|
do {
|
|
|
if(p->node != UA_NULL) {
|
|
|
- ns_entry *q = find_empty_slot(ns, hash(&p->node->nodeId));
|
|
|
+ Namespace_Entry *q = find_empty_slot(ns, hash(&p->node->nodeId));
|
|
|
*q = *p;
|
|
|
}
|
|
|
p++;
|
|
@@ -522,3 +325,179 @@ static UA_Int32 expand(namespace * ns) {
|
|
|
UA_free(oentries);
|
|
|
return UA_SUCCESS;
|
|
|
}
|
|
|
+
|
|
|
+/**********************/
|
|
|
+/* Exported functions */
|
|
|
+/**********************/
|
|
|
+
|
|
|
+UA_Int32 Namespace_create(Namespace ** result, UA_UInt32 size) {
|
|
|
+ Namespace *ns = UA_NULL;
|
|
|
+ UA_UInt32 sizePrimeIndex = higher_prime_index(size);
|
|
|
+ size = prime_tab[sizePrimeIndex].prime;
|
|
|
+
|
|
|
+ if(UA_alloc((void **)&ns, sizeof(Namespace)) != UA_SUCCESS)
|
|
|
+ return UA_ERR_NO_MEMORY;
|
|
|
+
|
|
|
+ if(UA_alloc((void **)&ns->entries, sizeof(Namespace_Entry) * size) != UA_SUCCESS) {
|
|
|
+ UA_free(ns);
|
|
|
+ return UA_ERR_NO_MEMORY;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ set entries to zero:
|
|
|
+ ns->entries[i].lock = UA_NUll;
|
|
|
+ ns->entries[i].node = UA_NULL;
|
|
|
+ */
|
|
|
+ memset(ns->entries, 0, size * sizeof(Namespace_Entry));
|
|
|
+
|
|
|
+ ns->size = size;
|
|
|
+ ns->count = 0;
|
|
|
+ ns->sizePrimeIndex = sizePrimeIndex;
|
|
|
+ *result = ns;
|
|
|
+ return UA_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+static void Namespace_clear(Namespace * ns) {
|
|
|
+ UA_UInt32 size = ns->size;
|
|
|
+ Namespace_Entry *entries = ns->entries;
|
|
|
+ for(UA_UInt32 i = 0; i < size; i++)
|
|
|
+ clear_slot(ns, &entries[i]);
|
|
|
+ ns->count = 0;
|
|
|
+}
|
|
|
+
|
|
|
+void Namespace_empty(Namespace * ns) {
|
|
|
+ Namespace_clear(ns);
|
|
|
+
|
|
|
+ /* Downsize the table. */
|
|
|
+ if(ns->size > 1024 * 1024 / sizeof(Namespace_Entry)) {
|
|
|
+ int nindex = higher_prime_index(1024 / sizeof(Namespace_Entry));
|
|
|
+ int nsize = prime_tab[nindex].prime;
|
|
|
+ UA_free(ns->entries);
|
|
|
+ UA_alloc((void **)&ns->entries, sizeof(Namespace_Entry) * nsize); // FIXME: Check return
|
|
|
+ // value
|
|
|
+ ns->size = nsize;
|
|
|
+ ns->sizePrimeIndex = nindex;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Namespace_delete(Namespace * ns) {
|
|
|
+ Namespace_clear(ns);
|
|
|
+ UA_free(ns->entries);
|
|
|
+ UA_free(ns);
|
|
|
+}
|
|
|
+
|
|
|
+UA_Int32 Namespace_insert(Namespace * ns, UA_Node * node) {
|
|
|
+ if(ns->size * 3 <= ns->count * 4) {
|
|
|
+ if(expand(ns) != UA_SUCCESS)
|
|
|
+ return UA_ERROR;
|
|
|
+ }
|
|
|
+
|
|
|
+ hash_t h = hash(&node->nodeId);
|
|
|
+ Namespace_Entry *slot = find_empty_slot(ns, h);
|
|
|
+
|
|
|
+#ifdef MULTITHREADING
|
|
|
+ if(UA_alloc((void **)&slot->lock, sizeof(pthread_rwlock_t)) != UA_SUCCESS)
|
|
|
+ return UA_ERR_NO_MEMORY;
|
|
|
+ pthread_rwlock_init((pthread_rwlock_t *) slot->lock, NULL);
|
|
|
+#endif
|
|
|
+
|
|
|
+ slot->node = node;
|
|
|
+ ns->count++;
|
|
|
+ return UA_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+UA_Int32 Namespace_get(Namespace const *ns, const UA_NodeId * nodeid, UA_Node const **result, Namespace_Lock ** lock) {
|
|
|
+ Namespace_Entry *slot;
|
|
|
+ if(find_slot(ns, &slot, nodeid) != UA_SUCCESS)
|
|
|
+ return UA_ERROR;
|
|
|
+
|
|
|
+#ifdef MULTITHREADING
|
|
|
+ if(pthread_rwlock_rdlock((pthread_rwlock_t *) slot->lock) != 0)
|
|
|
+ return UA_ERROR;
|
|
|
+ *lock = slot->lock;
|
|
|
+#endif
|
|
|
+
|
|
|
+ *result = slot->node;
|
|
|
+ return UA_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+UA_Int32 Namespace_getWritable(const Namespace * ns, const UA_NodeId * nodeid, UA_Node ** result, Namespace_Lock ** lock) {
|
|
|
+ Namespace_Entry *slot;
|
|
|
+ if(find_slot(ns, &slot, nodeid) != UA_SUCCESS)
|
|
|
+ return UA_ERROR;
|
|
|
+
|
|
|
+#ifdef MULTITHREADING
|
|
|
+ if(pthread_rwlock_wrlock((pthread_rwlock_t *) slot->lock) != 0)
|
|
|
+ return UA_ERROR;
|
|
|
+ *lock = slot->lock;
|
|
|
+#endif
|
|
|
+
|
|
|
+ *result = slot->node;
|
|
|
+ return UA_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef MULTITHREADING
|
|
|
+static inline void release_context_walker(void *lock) {
|
|
|
+ pthread_rwlock_unlock(lock);
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+UA_Int32 Namespace_transactionGet(Namespace * ns, Namespace_TransactionContext * tc, const UA_NodeId * nodeid, UA_Node ** const result, Namespace_Lock ** lock) {
|
|
|
+ Namespace_Entry *slot;
|
|
|
+ if(find_slot(ns, &slot, nodeid) != UA_SUCCESS)
|
|
|
+ return UA_ERROR;
|
|
|
+
|
|
|
+#ifdef MULTITHREADING
|
|
|
+ if(pthread_rwlock_tryrdlock((pthread_rwlock_t *) slot->lock) != 0) {
|
|
|
+ /* Transaction failed. Release all acquired locks and bail out. */
|
|
|
+ UA_list_destroy((UA_list_List *) tc, release_context_walker);
|
|
|
+ return UA_ERROR;
|
|
|
+ }
|
|
|
+ UA_list_addPayloadToBack((UA_list_List *) tc, slot->lock);
|
|
|
+ *lock = slot->lock;
|
|
|
+#endif
|
|
|
+
|
|
|
+ *result = slot->node;
|
|
|
+ return UA_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+UA_Int32 Namespace_transactionGetWritable(Namespace * ns, Namespace_TransactionContext * tc, const UA_NodeId * nodeid, UA_Node ** result, Namespace_Lock ** lock) {
|
|
|
+ Namespace_Entry *slot;
|
|
|
+ if(find_slot(ns, &slot, nodeid) != UA_SUCCESS)
|
|
|
+ return UA_ERROR;
|
|
|
+
|
|
|
+#ifdef MULTITHREADING
|
|
|
+ if(pthread_rwlock_trywrlock((pthread_rwlock_t *) slot->lock) != 0) {
|
|
|
+ /* Transaction failed. Release all acquired locks and bail out. */
|
|
|
+ UA_list_destroy((UA_list_List *) tc, release_context_walker);
|
|
|
+ return UA_ERROR;
|
|
|
+ }
|
|
|
+ UA_list_addPayloadToBack((UA_list_List *) tc, slot->lock);
|
|
|
+ *lock = slot->lock;
|
|
|
+#endif
|
|
|
+
|
|
|
+ *result = slot->node;
|
|
|
+ return UA_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+void Namespace_remove(Namespace * ns, UA_NodeId * nodeid) {
|
|
|
+ Namespace_Entry *slot;
|
|
|
+ if(find_slot(ns, &slot, nodeid) != UA_SUCCESS)
|
|
|
+ return;
|
|
|
+ // TODO: Check if deleting the node makes the Namespace inconsistent.
|
|
|
+ clear_slot(ns, slot);
|
|
|
+
|
|
|
+ /* Downsize the hashmap if it is very empty */
|
|
|
+ if(ns->count * 8 < ns->size && ns->size > 32)
|
|
|
+ expand(ns);
|
|
|
+}
|
|
|
+
|
|
|
+UA_Int32 Namespace_iterate(const Namespace * ns, Namespace_nodeVisitor visitor) {
|
|
|
+ UA_UInt32 i;
|
|
|
+ for(i = 0; i < ns->size; i++) {
|
|
|
+ Namespace_Entry *entry = &ns->entries[i];
|
|
|
+ if(entry != UA_NULL && visitor != UA_NULL)
|
|
|
+ visitor(entry->node);
|
|
|
+ }
|
|
|
+ return UA_SUCCESS;
|
|
|
+}
|