ua_nodestore_default.c 14 KB

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