ua_nodestore_hashmap.c 15 KB

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