ua_nodestore_hashmap.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
  2. * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
  3. *
  4. * Copyright 2014-2019 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
  5. * Copyright 2017 (c) Julian Grothoff
  6. * Copyright 2017 (c) Stefan Profanter, fortiss GmbH
  7. */
  8. #include <open62541/plugin/nodestore_default.h>
  9. /* container_of */
  10. #define container_of(ptr, type, member) \
  11. (type *)((uintptr_t)ptr - offsetof(type,member))
  12. /* The default Nodestore is simply a hash-map from NodeIds to Nodes. To find an
  13. * entry, iterate over candidate positions according to the NodeId hash.
  14. *
  15. * - Tombstone or non-matching NodeId: continue searching
  16. * - Matching NodeId: Return the entry
  17. * - NULL: Abort the search */
  18. typedef struct UA_NodeMapEntry {
  19. struct UA_NodeMapEntry *orig; /* the version this is a copy from (or NULL) */
  20. UA_UInt16 refCount; /* How many consumers have a reference to the node? */
  21. UA_Boolean deleted; /* Node was marked as deleted and can be deleted when refCount == 0 */
  22. UA_Node node;
  23. } UA_NodeMapEntry;
  24. #define UA_NODEMAP_MINSIZE 64
  25. #define UA_NODEMAP_TOMBSTONE ((UA_NodeMapEntry*)0x01)
  26. typedef struct {
  27. UA_NodeMapEntry *entry;
  28. UA_UInt32 nodeIdHash;
  29. } UA_NodeMapSlot;
  30. typedef struct {
  31. UA_NodeMapSlot *slots;
  32. UA_UInt32 size;
  33. UA_UInt32 count;
  34. UA_UInt32 sizePrimeIndex;
  35. } UA_NodeMap;
  36. /*********************/
  37. /* HashMap Utilities */
  38. /*********************/
  39. /* The size of the hash-map is always a prime number. They are chosen to be
  40. * close to the next power of 2. So the size ca. doubles with each prime. */
  41. static UA_UInt32 const primes[] = {
  42. 7, 13, 31, 61, 127, 251,
  43. 509, 1021, 2039, 4093, 8191, 16381,
  44. 32749, 65521, 131071, 262139, 524287, 1048573,
  45. 2097143, 4194301, 8388593, 16777213, 33554393, 67108859,
  46. 134217689, 268435399, 536870909, 1073741789, 2147483647, 4294967291
  47. };
  48. static UA_UInt32 mod(UA_UInt32 h, UA_UInt32 size) { return h % size; }
  49. static UA_UInt32 mod2(UA_UInt32 h, UA_UInt32 size) { return 1 + (h % (size - 2)); }
  50. static UA_UInt16
  51. higher_prime_index(UA_UInt32 n) {
  52. UA_UInt16 low = 0;
  53. UA_UInt16 high = (UA_UInt16)(sizeof(primes) / sizeof(UA_UInt32));
  54. while(low != high) {
  55. UA_UInt16 mid = (UA_UInt16)(low + ((high - low) / 2));
  56. if(n > primes[mid])
  57. low = (UA_UInt16)(mid + 1);
  58. else
  59. high = mid;
  60. }
  61. return low;
  62. }
  63. /* Returns an empty slot or null if the nodeid exists or if no empty slot is found. */
  64. static UA_NodeMapSlot *
  65. findFreeSlot(const UA_NodeMap *ns, const UA_NodeId *nodeid) {
  66. UA_UInt32 h = UA_NodeId_hash(nodeid);
  67. UA_UInt32 size = ns->size;
  68. UA_UInt64 idx = mod(h, size); /* Use 64bit container to avoid overflow */
  69. UA_UInt32 startIdx = (UA_UInt32)idx;
  70. UA_UInt32 hash2 = mod2(h, size);
  71. UA_NodeMapSlot *candidate = NULL;
  72. do {
  73. UA_NodeMapSlot *slot = &ns->slots[(UA_UInt32)idx];
  74. if(slot->entry > UA_NODEMAP_TOMBSTONE) {
  75. /* A Node with the NodeId does already exist */
  76. if(slot->nodeIdHash == h &&
  77. UA_NodeId_equal(&slot->entry->node.nodeId, nodeid))
  78. return NULL;
  79. } else {
  80. /* Found a candidate node */
  81. if(!candidate)
  82. candidate = slot;
  83. /* No matching node can come afterwards */
  84. if(slot->entry == NULL)
  85. return candidate;
  86. }
  87. idx += hash2;
  88. if(idx >= size)
  89. idx -= size;
  90. } while((UA_UInt32)idx != startIdx);
  91. return candidate;
  92. }
  93. /* The occupancy of the table after the call will be about 50% */
  94. static UA_StatusCode
  95. expand(UA_NodeMap *ns) {
  96. UA_UInt32 osize = ns->size;
  97. UA_UInt32 count = ns->count;
  98. /* Resize only when table after removal of unused elements is either too
  99. full or too empty */
  100. if(count * 2 < osize && (count * 8 > osize || osize <= UA_NODEMAP_MINSIZE))
  101. return UA_STATUSCODE_GOOD;
  102. UA_NodeMapSlot *oslots = ns->slots;
  103. UA_UInt32 nindex = higher_prime_index(count * 2);
  104. UA_UInt32 nsize = primes[nindex];
  105. UA_NodeMapSlot *nslots= (UA_NodeMapSlot*)UA_calloc(nsize, sizeof(UA_NodeMapSlot));
  106. if(!nslots)
  107. return UA_STATUSCODE_BADOUTOFMEMORY;
  108. ns->slots = nslots;
  109. ns->size = nsize;
  110. ns->sizePrimeIndex = nindex;
  111. /* recompute the position of every entry and insert the pointer */
  112. for(size_t i = 0, j = 0; i < osize && j < count; ++i) {
  113. if(oslots[i].entry <= UA_NODEMAP_TOMBSTONE)
  114. continue;
  115. UA_NodeMapSlot *s = findFreeSlot(ns, &oslots[i].entry->node.nodeId);
  116. UA_assert(s);
  117. *s = oslots[i];
  118. ++j;
  119. }
  120. UA_free(oslots);
  121. return UA_STATUSCODE_GOOD;
  122. }
  123. static UA_NodeMapEntry *
  124. createEntry(UA_NodeClass nodeClass) {
  125. size_t size = sizeof(UA_NodeMapEntry) - sizeof(UA_Node);
  126. switch(nodeClass) {
  127. case UA_NODECLASS_OBJECT:
  128. size += sizeof(UA_ObjectNode);
  129. break;
  130. case UA_NODECLASS_VARIABLE:
  131. size += sizeof(UA_VariableNode);
  132. break;
  133. case UA_NODECLASS_METHOD:
  134. size += sizeof(UA_MethodNode);
  135. break;
  136. case UA_NODECLASS_OBJECTTYPE:
  137. size += sizeof(UA_ObjectTypeNode);
  138. break;
  139. case UA_NODECLASS_VARIABLETYPE:
  140. size += sizeof(UA_VariableTypeNode);
  141. break;
  142. case UA_NODECLASS_REFERENCETYPE:
  143. size += sizeof(UA_ReferenceTypeNode);
  144. break;
  145. case UA_NODECLASS_DATATYPE:
  146. size += sizeof(UA_DataTypeNode);
  147. break;
  148. case UA_NODECLASS_VIEW:
  149. size += sizeof(UA_ViewNode);
  150. break;
  151. default:
  152. return NULL;
  153. }
  154. UA_NodeMapEntry *entry = (UA_NodeMapEntry*)UA_calloc(1, size);
  155. if(!entry)
  156. return NULL;
  157. entry->node.nodeClass = nodeClass;
  158. return entry;
  159. }
  160. static void
  161. deleteNodeMapEntry(UA_NodeMapEntry *entry) {
  162. UA_Node_clear(&entry->node);
  163. UA_free(entry);
  164. }
  165. static void
  166. cleanupNodeMapEntry(UA_NodeMapEntry *entry) {
  167. if(entry->deleted && entry->refCount == 0)
  168. deleteNodeMapEntry(entry);
  169. }
  170. static UA_NodeMapSlot *
  171. findOccupiedSlot(const UA_NodeMap *ns, const UA_NodeId *nodeid) {
  172. UA_UInt32 h = UA_NodeId_hash(nodeid);
  173. UA_UInt32 size = ns->size;
  174. UA_UInt64 idx = mod(h, size); /* Use 64bit container to avoid overflow */
  175. UA_UInt32 hash2 = mod2(h, size);
  176. UA_UInt32 startIdx = (UA_UInt32)idx;
  177. do {
  178. UA_NodeMapSlot *slot= &ns->slots[(UA_UInt32)idx];
  179. if(slot->entry > UA_NODEMAP_TOMBSTONE) {
  180. if(slot->nodeIdHash == h &&
  181. UA_NodeId_equal(&slot->entry->node.nodeId, nodeid))
  182. return slot;
  183. } else {
  184. if(slot->entry == NULL)
  185. return NULL; /* No further entry possible */
  186. }
  187. idx += hash2;
  188. if(idx >= size)
  189. idx -= size;
  190. } while((UA_UInt32)idx != startIdx);
  191. return NULL;
  192. }
  193. /***********************/
  194. /* Interface functions */
  195. /***********************/
  196. static UA_Node *
  197. UA_NodeMap_newNode(void *context, UA_NodeClass nodeClass) {
  198. UA_NodeMapEntry *entry = createEntry(nodeClass);
  199. if(!entry)
  200. return NULL;
  201. return &entry->node;
  202. }
  203. static void
  204. UA_NodeMap_deleteNode(void *context, UA_Node *node) {
  205. UA_NodeMapEntry *entry = container_of(node, UA_NodeMapEntry, node);
  206. UA_assert(&entry->node == node);
  207. deleteNodeMapEntry(entry);
  208. }
  209. static const UA_Node *
  210. UA_NodeMap_getNode(void *context, const UA_NodeId *nodeid) {
  211. UA_NodeMap *ns = (UA_NodeMap*)context;
  212. UA_NodeMapSlot *slot = findOccupiedSlot(ns, nodeid);
  213. if(!slot)
  214. return NULL;
  215. ++slot->entry->refCount;
  216. return &slot->entry->node;
  217. }
  218. static void
  219. UA_NodeMap_releaseNode(void *context, const UA_Node *node) {
  220. if (!node)
  221. return;
  222. UA_NodeMapEntry *entry = container_of(node, UA_NodeMapEntry, node);
  223. UA_assert(&entry->node == node);
  224. UA_assert(entry->refCount > 0);
  225. --entry->refCount;
  226. cleanupNodeMapEntry(entry);
  227. }
  228. static UA_StatusCode
  229. UA_NodeMap_getNodeCopy(void *context, const UA_NodeId *nodeid,
  230. UA_Node **outNode) {
  231. UA_NodeMap *ns = (UA_NodeMap*)context;
  232. UA_NodeMapSlot *slot = findOccupiedSlot(ns, nodeid);
  233. if(!slot)
  234. return UA_STATUSCODE_BADNODEIDUNKNOWN;
  235. UA_NodeMapEntry *entry = slot->entry;
  236. UA_NodeMapEntry *newItem = createEntry(entry->node.nodeClass);
  237. if(!newItem)
  238. return UA_STATUSCODE_BADOUTOFMEMORY;
  239. UA_StatusCode retval = UA_Node_copy(&entry->node, &newItem->node);
  240. if(retval == UA_STATUSCODE_GOOD) {
  241. newItem->orig = entry; /* Store the pointer to the original */
  242. *outNode = &newItem->node;
  243. } else {
  244. deleteNodeMapEntry(newItem);
  245. }
  246. return retval;
  247. }
  248. static UA_StatusCode
  249. UA_NodeMap_removeNode(void *context, const UA_NodeId *nodeid) {
  250. UA_NodeMap *ns = (UA_NodeMap*)context;
  251. UA_NodeMapSlot *slot = findOccupiedSlot(ns, nodeid);
  252. if(!slot)
  253. return UA_STATUSCODE_BADNODEIDUNKNOWN;
  254. UA_NodeMapEntry *entry = slot->entry;
  255. slot->entry = UA_NODEMAP_TOMBSTONE;
  256. UA_atomic_sync(); /* Set the tombstone before cleaning up. E.g. if the
  257. * nodestore is accessed from an interrupt. */
  258. entry->deleted = true;
  259. cleanupNodeMapEntry(entry);
  260. --ns->count;
  261. /* Downsize the hashmap if it is very empty */
  262. if(ns->count * 8 < ns->size && ns->size > UA_NODEMAP_MINSIZE)
  263. expand(ns); /* Can fail. Just continue with the bigger hashmap. */
  264. return UA_STATUSCODE_GOOD;
  265. }
  266. static UA_StatusCode
  267. UA_NodeMap_insertNode(void *context, UA_Node *node,
  268. UA_NodeId *addedNodeId) {
  269. UA_NodeMap *ns = (UA_NodeMap*)context;
  270. if(ns->size * 3 <= ns->count * 4) {
  271. if(expand(ns) != UA_STATUSCODE_GOOD)
  272. return UA_STATUSCODE_BADINTERNALERROR;
  273. }
  274. UA_NodeMapSlot *slot;
  275. if(node->nodeId.identifierType == UA_NODEIDTYPE_NUMERIC &&
  276. node->nodeId.identifier.numeric == 0) {
  277. /* Create a random nodeid: Start at least with 50,000 to make sure we
  278. * don not conflict with nodes from the spec. If we find a conflict, we
  279. * just try another identifier until we have tried all possible
  280. * identifiers. Since the size is prime and we don't change the increase
  281. * val, we will reach the starting id again. E.g. adding a nodeset will
  282. * create children while there are still other nodes which need to be
  283. * created. Thus the node ids may collide. */
  284. UA_UInt32 size = ns->size;
  285. UA_UInt64 identifier = mod(50000 + size+1, UA_UINT32_MAX); /* Use 64bit to
  286. * avoid overflow */
  287. UA_UInt32 increase = mod2(ns->count+1, size);
  288. UA_UInt32 startId = (UA_UInt32)identifier; /* mod ensures us that the id
  289. * is a valid 32 bit integer */
  290. do {
  291. node->nodeId.identifier.numeric = (UA_UInt32)identifier;
  292. slot = findFreeSlot(ns, &node->nodeId);
  293. if(slot)
  294. break;
  295. identifier += increase;
  296. if(identifier >= size)
  297. identifier -= size;
  298. } while((UA_UInt32)identifier != startId);
  299. } else {
  300. slot = findFreeSlot(ns, &node->nodeId);
  301. }
  302. if(!slot) {
  303. deleteNodeMapEntry(container_of(node, UA_NodeMapEntry, node));
  304. return UA_STATUSCODE_BADNODEIDEXISTS;
  305. }
  306. /* Copy the NodeId */
  307. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  308. if(addedNodeId) {
  309. retval = UA_NodeId_copy(&node->nodeId, addedNodeId);
  310. if(retval != UA_STATUSCODE_GOOD) {
  311. deleteNodeMapEntry(container_of(node, UA_NodeMapEntry, node));
  312. return retval;
  313. }
  314. }
  315. /* Insert the node */
  316. UA_NodeMapEntry *newEntry = container_of(node, UA_NodeMapEntry, node);
  317. slot->nodeIdHash = UA_NodeId_hash(&node->nodeId);
  318. UA_atomic_sync(); /* Set the hash first */
  319. slot->entry = newEntry;
  320. ++ns->count;
  321. return retval;
  322. }
  323. static UA_StatusCode
  324. UA_NodeMap_replaceNode(void *context, UA_Node *node) {
  325. UA_NodeMap *ns = (UA_NodeMap*)context;
  326. UA_NodeMapEntry *newEntry = container_of(node, UA_NodeMapEntry, node);
  327. /* Find the node */
  328. UA_NodeMapSlot *slot = findOccupiedSlot(ns, &node->nodeId);
  329. if(!slot) {
  330. deleteNodeMapEntry(newEntry);
  331. return UA_STATUSCODE_BADNODEIDUNKNOWN;
  332. }
  333. /* The node was already updated since the copy was made? */
  334. UA_NodeMapEntry *oldEntry = slot->entry;
  335. if(oldEntry != newEntry->orig) {
  336. deleteNodeMapEntry(newEntry);
  337. return UA_STATUSCODE_BADINTERNALERROR;
  338. }
  339. /* Replace the entry */
  340. slot->entry = newEntry;
  341. UA_atomic_sync();
  342. oldEntry->deleted = true;
  343. cleanupNodeMapEntry(oldEntry);
  344. return UA_STATUSCODE_GOOD;
  345. }
  346. static void
  347. UA_NodeMap_iterate(void *context, UA_NodestoreVisitor visitor,
  348. void *visitorContext) {
  349. UA_NodeMap *ns = (UA_NodeMap*)context;
  350. for(UA_UInt32 i = 0; i < ns->size; ++i) {
  351. UA_NodeMapSlot *slot = &ns->slots[i];
  352. if(slot->entry > UA_NODEMAP_TOMBSTONE) {
  353. /* The visitor can delete the node. So refcount here. */
  354. slot->entry->refCount++;
  355. visitor(visitorContext, &slot->entry->node);
  356. slot->entry->refCount--;
  357. cleanupNodeMapEntry(slot->entry);
  358. }
  359. }
  360. }
  361. static void
  362. UA_NodeMap_delete(void *context) {
  363. UA_NodeMap *ns = (UA_NodeMap*)context;
  364. UA_UInt32 size = ns->size;
  365. UA_NodeMapSlot *slots = ns->slots;
  366. for(UA_UInt32 i = 0; i < size; ++i) {
  367. if(slots[i].entry > UA_NODEMAP_TOMBSTONE) {
  368. /* On debugging builds, check that all nodes were release */
  369. UA_assert(slots[i].entry->refCount == 0);
  370. /* Delete the node */
  371. deleteNodeMapEntry(slots[i].entry);
  372. }
  373. }
  374. UA_free(ns->slots);
  375. UA_free(ns);
  376. }
  377. UA_StatusCode
  378. UA_Nodestore_HashMap(UA_Nodestore *ns) {
  379. /* Allocate and initialize the nodemap */
  380. UA_NodeMap *nodemap = (UA_NodeMap*)UA_malloc(sizeof(UA_NodeMap));
  381. if(!nodemap)
  382. return UA_STATUSCODE_BADOUTOFMEMORY;
  383. nodemap->sizePrimeIndex = higher_prime_index(UA_NODEMAP_MINSIZE);
  384. nodemap->size = primes[nodemap->sizePrimeIndex];
  385. nodemap->count = 0;
  386. nodemap->slots = (UA_NodeMapSlot*)
  387. UA_calloc(nodemap->size, sizeof(UA_NodeMapSlot));
  388. if(!nodemap->slots) {
  389. UA_free(nodemap);
  390. return UA_STATUSCODE_BADOUTOFMEMORY;
  391. }
  392. /* Populate the nodestore */
  393. ns->context = nodemap;
  394. ns->clear = UA_NodeMap_delete;
  395. ns->newNode = UA_NodeMap_newNode;
  396. ns->deleteNode = UA_NodeMap_deleteNode;
  397. ns->getNode = UA_NodeMap_getNode;
  398. ns->releaseNode = UA_NodeMap_releaseNode;
  399. ns->getNodeCopy = UA_NodeMap_getNodeCopy;
  400. ns->insertNode = UA_NodeMap_insertNode;
  401. ns->replaceNode = UA_NodeMap_replaceNode;
  402. ns->removeNode = UA_NodeMap_removeNode;
  403. ns->iterate = UA_NodeMap_iterate;
  404. return UA_STATUSCODE_GOOD;
  405. }