ua_nodestore_default.c 15 KB

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