ua_nodestore_default.c 14 KB

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