dictionary.c 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. // NOT TO BE USED BY USERS YET
  3. #define DICTIONARY_FLAG_REFERENCE_COUNTERS (1 << 6) // maintain reference counter in walkthrough and foreach
  4. typedef struct dictionary DICTIONARY;
  5. #define DICTIONARY_INTERNALS
  6. #include "../libnetdata.h"
  7. #ifndef ENABLE_DBENGINE
  8. #define DICTIONARY_WITH_AVL
  9. #warning Compiling DICTIONARY with an AVL index
  10. #else
  11. #define DICTIONARY_WITH_JUDYHS
  12. #endif
  13. #ifdef DICTIONARY_WITH_JUDYHS
  14. #include <Judy.h>
  15. #endif
  16. /*
  17. * This version uses JudyHS arrays to index the dictionary
  18. *
  19. * The following output is from the unit test, at the end of this file:
  20. *
  21. * This is the JudyHS version:
  22. *
  23. * 1000000 x dictionary_set() (dictionary size 0 entries, 0 KB)...
  24. * 1000000 x dictionary_get(existing) (dictionary size 1000000 entries, 74001 KB)...
  25. * 1000000 x dictionary_get(non-existing) (dictionary size 1000000 entries, 74001 KB)...
  26. * Walking through the dictionary (dictionary size 1000000 entries, 74001 KB)...
  27. * 1000000 x dictionary_del(existing) (dictionary size 1000000 entries, 74001 KB)...
  28. * 1000000 x dictionary_set() (dictionary size 0 entries, 0 KB)...
  29. * Destroying dictionary (dictionary size 1000000 entries, 74001 KB)...
  30. *
  31. * TIMINGS:
  32. * adding 316027 usec, positive search 156740 usec, negative search 84524, walk through 15036 usec, deleting 361444, destroy 107394 usec
  33. *
  34. * This is from the JudySL version:
  35. *
  36. * Creating dictionary of 1000000 entries...
  37. * Checking index of 1000000 entries...
  38. * Walking 1000000 entries and checking name-value pairs...
  39. * Created and checked 1000000 entries, found 0 errors - used 58376 KB of memory
  40. * Destroying dictionary of 1000000 entries...
  41. * Deleted 1000000 entries
  42. * create 338975 usec, check 156080 usec, walk 80764 usec, destroy 444569 usec
  43. *
  44. * This is the AVL version:
  45. *
  46. * Creating dictionary of 1000000 entries...
  47. * Checking index of 1000000 entries...
  48. * Walking 1000000 entries and checking name-value pairs...
  49. * Created and checked 1000000 entries, found 0 errors - used 89626 KB of memory
  50. * Destroying dictionary of 1000000 entries...
  51. * create 413892 usec, check 220006 usec, walk 34247 usec, destroy 98062 usec
  52. *
  53. * So, the JudySL is a lot slower to WALK and DESTROY (DESTROY does a WALK)
  54. * It is slower, because for every item, JudySL copies the KEY/NAME to a
  55. * caller supplied buffer (Index). So, by just walking over 1 million items,
  56. * JudySL does 1 million strcpy() !!!
  57. *
  58. * It also seems that somehow JudySLDel() is unbelievably slow too!
  59. *
  60. */
  61. /*
  62. * Every item in the dictionary has the following structure.
  63. */
  64. typedef struct name_value {
  65. #ifdef DICTIONARY_WITH_AVL
  66. avl_t avl_node;
  67. #endif
  68. struct name_value *next; // a double linked list to allow fast insertions and deletions
  69. struct name_value *prev;
  70. char *name; // the name of the dictionary item
  71. void *value; // the value of the dictionary item
  72. } NAME_VALUE;
  73. /*
  74. * When DICTIONARY_FLAG_WITH_STATISTICS is set, we need to keep track of all the memory
  75. * we allocate and free. So, we need to keep track of the sizes of all names and values.
  76. * We do this by overloading NAME_VALUE with the following additional fields.
  77. */
  78. typedef enum name_value_flags {
  79. NAME_VALUE_FLAG_NONE = 0,
  80. NAME_VALUE_FLAG_DELETED = (1 << 0), // this item is deleted
  81. } NAME_VALUE_FLAGS;
  82. typedef struct name_value_with_stats {
  83. NAME_VALUE name_value_data_here; // never used - just to put the lengths at the right position
  84. size_t name_len; // the size of the name, including the terminating zero
  85. size_t value_len; // the size of the value (assumed binary)
  86. size_t refcount; // the reference counter
  87. NAME_VALUE_FLAGS flags; // the flags for this item
  88. } NAME_VALUE_WITH_STATS;
  89. struct dictionary_stats {
  90. size_t inserts;
  91. size_t deletes;
  92. size_t searches;
  93. size_t resets;
  94. size_t entries;
  95. size_t memory;
  96. };
  97. struct dictionary {
  98. DICTIONARY_FLAGS flags; // the flags of the dictionary
  99. NAME_VALUE *first_item; // the double linked list base pointers
  100. NAME_VALUE *last_item;
  101. #ifdef DICTIONARY_WITH_AVL
  102. avl_tree_type values_index;
  103. NAME_VALUE *hash_base;
  104. #endif
  105. #ifdef DICTIONARY_WITH_JUDYHS
  106. Pvoid_t JudyHSArray; // the hash table
  107. #endif
  108. netdata_rwlock_t *rwlock; // the r/w lock when DICTIONARY_FLAG_SINGLE_THREADED is not set
  109. void (*ins_callback)(const char *name, void *value, void *data);
  110. void *ins_callback_data;
  111. void (*del_callback)(const char *name, void *value, void *data);
  112. void *del_callback_data;
  113. void (*conflict_callback)(const char *name, void *old_value, void *new_value, void *data);
  114. void *conflict_callback_data;
  115. struct dictionary_stats *stats; // the statistics when DICTIONARY_FLAG_WITH_STATISTICS is set
  116. };
  117. void dictionary_register_insert_callback(DICTIONARY *dict, void (*ins_callback)(const char *name, void *value, void *data), void *data) {
  118. dict->ins_callback = ins_callback;
  119. dict->ins_callback_data = data;
  120. }
  121. void dictionary_register_delete_callback(DICTIONARY *dict, void (*del_callback)(const char *name, void *value, void *data), void *data) {
  122. dict->del_callback = del_callback;
  123. dict->del_callback_data = data;
  124. }
  125. void dictionary_register_conflict_callback(DICTIONARY *dict, void (*conflict_callback)(const char *name, void *old_value, void *new_value, void *data), void *data) {
  126. dict->conflict_callback = conflict_callback;
  127. dict->conflict_callback_data = data;
  128. }
  129. // ----------------------------------------------------------------------------
  130. // dictionary statistics maintenance
  131. size_t dictionary_stats_allocated_memory(DICTIONARY *dict) {
  132. if(unlikely(dict->flags & DICTIONARY_FLAG_WITH_STATISTICS))
  133. return dict->stats->memory;
  134. return 0;
  135. }
  136. size_t dictionary_stats_entries(DICTIONARY *dict) {
  137. if(unlikely(dict->flags & DICTIONARY_FLAG_WITH_STATISTICS))
  138. return dict->stats->entries;
  139. return 0;
  140. }
  141. size_t dictionary_stats_searches(DICTIONARY *dict) {
  142. if(unlikely(dict->flags & DICTIONARY_FLAG_WITH_STATISTICS))
  143. return dict->stats->searches;
  144. return 0;
  145. }
  146. size_t dictionary_stats_inserts(DICTIONARY *dict) {
  147. if(unlikely(dict->flags & DICTIONARY_FLAG_WITH_STATISTICS))
  148. return dict->stats->inserts;
  149. return 0;
  150. }
  151. size_t dictionary_stats_deletes(DICTIONARY *dict) {
  152. if(unlikely(dict->flags & DICTIONARY_FLAG_WITH_STATISTICS))
  153. return dict->stats->deletes;
  154. return 0;
  155. }
  156. size_t dictionary_stats_resets(DICTIONARY *dict) {
  157. if(unlikely(dict->flags & DICTIONARY_FLAG_WITH_STATISTICS))
  158. return dict->stats->resets;
  159. return 0;
  160. }
  161. static inline void DICTIONARY_STATS_SEARCHES_PLUS1(DICTIONARY *dict) {
  162. if(unlikely(dict->flags & DICTIONARY_FLAG_WITH_STATISTICS))
  163. dict->stats->searches++;
  164. }
  165. static inline void DICTIONARY_STATS_ENTRIES_PLUS1(DICTIONARY *dict, size_t size) {
  166. if(unlikely(dict->flags & DICTIONARY_FLAG_WITH_STATISTICS)) {
  167. dict->stats->inserts++;
  168. dict->stats->entries++;
  169. dict->stats->memory += size;
  170. }
  171. }
  172. static inline void DICTIONARY_STATS_ENTRIES_MINUS1(DICTIONARY *dict, size_t size) {
  173. if(unlikely(dict->flags & DICTIONARY_FLAG_WITH_STATISTICS)) {
  174. dict->stats->deletes++;
  175. dict->stats->entries--;
  176. dict->stats->memory -= size;
  177. }
  178. }
  179. static inline void DICTIONARY_STATS_VALUE_RESETS_PLUS1(DICTIONARY *dict, size_t oldsize, size_t newsize) {
  180. if(unlikely(dict->flags & DICTIONARY_FLAG_WITH_STATISTICS)) {
  181. dict->stats->resets++;
  182. dict->stats->memory += newsize;
  183. dict->stats->memory -= oldsize;
  184. }
  185. }
  186. // ----------------------------------------------------------------------------
  187. // dictionary locks
  188. static inline size_t dictionary_lock_init(DICTIONARY *dict) {
  189. if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
  190. dict->rwlock = mallocz(sizeof(netdata_rwlock_t));
  191. netdata_rwlock_init(dict->rwlock);
  192. return sizeof(netdata_rwlock_t);
  193. }
  194. dict->rwlock = NULL;
  195. return 0;
  196. }
  197. static inline size_t dictionary_lock_free(DICTIONARY *dict) {
  198. if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
  199. netdata_rwlock_destroy(dict->rwlock);
  200. freez(dict->rwlock);
  201. return sizeof(netdata_rwlock_t);
  202. }
  203. return 0;
  204. }
  205. static inline void dictionary_lock_rlock(DICTIONARY *dict) {
  206. if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
  207. // debug(D_DICTIONARY, "Dictionary READ lock");
  208. netdata_rwlock_rdlock(dict->rwlock);
  209. }
  210. }
  211. static inline void dictionary_lock_wrlock(DICTIONARY *dict) {
  212. if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
  213. // debug(D_DICTIONARY, "Dictionary WRITE lock");
  214. netdata_rwlock_wrlock(dict->rwlock);
  215. }
  216. }
  217. static inline void dictionary_unlock(DICTIONARY *dict) {
  218. if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
  219. // debug(D_DICTIONARY, "Dictionary UNLOCK lock");
  220. netdata_rwlock_unlock(dict->rwlock);
  221. }
  222. }
  223. // ----------------------------------------------------------------------------
  224. // reference counters
  225. static inline size_t reference_counter_init(DICTIONARY *dict) {
  226. (void)dict;
  227. // allocate memory required for reference counters
  228. // return number of bytes
  229. return 0;
  230. }
  231. static inline size_t reference_counter_free(DICTIONARY *dict) {
  232. (void)dict;
  233. // free memory required for reference counters
  234. // return number of bytes
  235. return 0;
  236. }
  237. static void reference_counter_acquire(DICTIONARY *dict, NAME_VALUE *nv) {
  238. if(unlikely(dict->flags & DICTIONARY_FLAG_REFERENCE_COUNTERS)) {
  239. NAME_VALUE_WITH_STATS *nvs = (NAME_VALUE_WITH_STATS *)nv;
  240. __atomic_fetch_add(&nvs->refcount, 1, __ATOMIC_SEQ_CST);
  241. }
  242. }
  243. static void reference_counter_release(DICTIONARY *dict, NAME_VALUE *nv) {
  244. if(unlikely(dict->flags & DICTIONARY_FLAG_REFERENCE_COUNTERS)) {
  245. NAME_VALUE_WITH_STATS *nvs = (NAME_VALUE_WITH_STATS *)nv;
  246. __atomic_fetch_sub(&nvs->refcount, 1, __ATOMIC_SEQ_CST);
  247. }
  248. }
  249. static int reference_counter_mark_deleted(DICTIONARY *dict, NAME_VALUE *nv) {
  250. if(unlikely(dict->flags & DICTIONARY_FLAG_REFERENCE_COUNTERS)) {
  251. NAME_VALUE_WITH_STATS *nvs = (NAME_VALUE_WITH_STATS *)nv;
  252. nvs->flags |= NAME_VALUE_FLAG_DELETED;
  253. return 1;
  254. }
  255. return 0;
  256. }
  257. // ----------------------------------------------------------------------------
  258. // hash table
  259. #ifdef DICTIONARY_WITH_AVL
  260. static int name_value_compare(void* a, void* b) {
  261. return strcmp(((NAME_VALUE *)a)->name, ((NAME_VALUE *)b)->name);
  262. }
  263. static void hashtable_init_unsafe(DICTIONARY *dict) {
  264. avl_init(&dict->values_index, name_value_compare);
  265. }
  266. static size_t hashtable_destroy_unsafe(DICTIONARY *dict) {
  267. (void)dict;
  268. return 0;
  269. }
  270. static inline int hashtable_delete_unsafe(DICTIONARY *dict, const char *name, size_t name_len, NAME_VALUE *nv) {
  271. (void)name;
  272. (void)name_len;
  273. if(unlikely(avl_remove(&(dict->values_index), (avl_t *)(nv)) != (avl_t *)nv))
  274. return 0;
  275. return 1;
  276. }
  277. static inline NAME_VALUE *hashtable_get_unsafe(DICTIONARY *dict, const char *name, size_t name_len) {
  278. (void)name_len;
  279. NAME_VALUE tmp;
  280. tmp.name = (char *)name;
  281. return (NAME_VALUE *)avl_search(&(dict->values_index), (avl_t *) &tmp);
  282. }
  283. static inline NAME_VALUE **hashtable_insert_unsafe(DICTIONARY *dict, const char *name, size_t name_len) {
  284. // AVL needs a NAME_VALUE to insert into the dictionary but we don't have it yet.
  285. // So, the only thing we can do, is return an existing one if it is already there.
  286. // Returning NULL will make the caller thing we added it, will allocate one
  287. // and will call hashtable_inserted_name_value_unsafe(), at which we will do
  288. // the actual indexing.
  289. dict->hash_base = hashtable_get_unsafe(dict, name, name_len);
  290. return &dict->hash_base;
  291. }
  292. static inline void hashtable_inserted_name_value_unsafe(DICTIONARY *dict, const char *name, size_t name_len, NAME_VALUE *nv) {
  293. // we have our new NAME_VALUE object.
  294. // Let's index it.
  295. (void)name;
  296. (void)name_len;
  297. if(unlikely(avl_insert(&((dict)->values_index), (avl_t *)(nv)) != (avl_t *)nv))
  298. error("dictionary: INTERNAL ERROR: duplicate insertion to dictionary.");
  299. }
  300. #endif
  301. #ifdef DICTIONARY_WITH_JUDYHS
  302. static void hashtable_init_unsafe(DICTIONARY *dict) {
  303. dict->JudyHSArray = NULL;
  304. }
  305. static size_t hashtable_destroy_unsafe(DICTIONARY *dict) {
  306. if(unlikely(!dict->JudyHSArray)) return 0;
  307. JError_t J_Error;
  308. Word_t ret = JudyHSFreeArray(&dict->JudyHSArray, &J_Error);
  309. if(unlikely(ret == (Word_t) JERR)) {
  310. error("DICTIONARY: Cannot destroy JudyHS, JU_ERRNO_* == %u, ID == %d",
  311. JU_ERRNO(&J_Error), JU_ERRID(&J_Error));
  312. }
  313. debug(D_DICTIONARY, "Dictionary: hash table freed %lu bytes", ret);
  314. dict->JudyHSArray = NULL;
  315. return (size_t)ret;
  316. }
  317. static inline NAME_VALUE **hashtable_insert_unsafe(DICTIONARY *dict, const char *name, size_t name_len) {
  318. JError_t J_Error;
  319. Pvoid_t *Rc = JudyHSIns(&dict->JudyHSArray, (void *)name, name_len, &J_Error);
  320. if (unlikely(Rc == PJERR)) {
  321. fatal("DICTIONARY: Cannot insert entry with name '%s' to JudyHS, JU_ERRNO_* == %u, ID == %d",
  322. name, JU_ERRNO(&J_Error), JU_ERRID(&J_Error));
  323. }
  324. // if *Rc == 0, new item added to the array
  325. // otherwise the existing item value is returned in *Rc
  326. // we return a pointer to a pointer, so that the caller can
  327. // put anything needed at the value of the index.
  328. // The pointer to pointer we return has to be used before
  329. // any other operation that may change the index (insert/delete).
  330. return (NAME_VALUE **)Rc;
  331. }
  332. static inline int hashtable_delete_unsafe(DICTIONARY *dict, const char *name, size_t name_len, NAME_VALUE *nv) {
  333. (void)nv;
  334. if(unlikely(!dict->JudyHSArray)) return 0;
  335. JError_t J_Error;
  336. int ret = JudyHSDel(&dict->JudyHSArray, (void *)name, name_len, &J_Error);
  337. if(unlikely(ret == JERR)) {
  338. error("DICTIONARY: Cannot delete entry with name '%s' from JudyHS, JU_ERRNO_* == %u, ID == %d", name,
  339. JU_ERRNO(&J_Error), JU_ERRID(&J_Error));
  340. return 0;
  341. }
  342. // Hey, this is problematic! We need the value back, not just an int with a status!
  343. // https://sourceforge.net/p/judy/feature-requests/23/
  344. if(unlikely(ret == 0)) {
  345. // not found in the dictionary
  346. return 0;
  347. }
  348. else {
  349. // found and deleted from the dictionary
  350. return 1;
  351. }
  352. }
  353. static inline NAME_VALUE *hashtable_get_unsafe(DICTIONARY *dict, const char *name, size_t name_len) {
  354. if(unlikely(!dict->JudyHSArray)) return NULL;
  355. DICTIONARY_STATS_SEARCHES_PLUS1(dict);
  356. Pvoid_t *Rc;
  357. Rc = JudyHSGet(dict->JudyHSArray, (void *)name, name_len);
  358. if(likely(Rc)) {
  359. // found in the hash table
  360. return (NAME_VALUE *)*Rc;
  361. }
  362. else {
  363. // not found in the hash table
  364. return NULL;
  365. }
  366. }
  367. static inline void hashtable_inserted_name_value_unsafe(DICTIONARY *dict, const char *name, size_t name_len, NAME_VALUE *nv) {
  368. (void)dict;
  369. (void)name;
  370. (void)name_len;
  371. (void)nv;
  372. ;
  373. }
  374. #endif // DICTIONARY_WITH_JUDYHS
  375. // ----------------------------------------------------------------------------
  376. // linked list management
  377. static inline void linkedlist_namevalue_link_unsafe(DICTIONARY *dict, NAME_VALUE *nv) {
  378. if (unlikely(!dict->first_item)) {
  379. // we are the only ones here
  380. nv->next = NULL;
  381. nv->prev = NULL;
  382. dict->first_item = dict->last_item = nv;
  383. return;
  384. }
  385. if(dict->flags & DICTIONARY_FLAG_ADD_IN_FRONT) {
  386. // add it at the beginning
  387. nv->prev = NULL;
  388. nv->next = dict->first_item;
  389. if (likely(nv->next)) nv->next->prev = nv;
  390. dict->first_item = nv;
  391. }
  392. else {
  393. // add it at the end
  394. nv->next = NULL;
  395. nv->prev = dict->last_item;
  396. if (likely(nv->prev)) nv->prev->next = nv;
  397. dict->last_item = nv;
  398. }
  399. }
  400. static inline void linkedlist_namevalue_unlink_unsafe(DICTIONARY *dict, NAME_VALUE *nv) {
  401. if(nv->next) nv->next->prev = nv->prev;
  402. if(nv->prev) nv->prev->next = nv->next;
  403. if(dict->first_item == nv) dict->first_item = nv->next;
  404. if(dict->last_item == nv) dict->last_item = nv->prev;
  405. }
  406. // ----------------------------------------------------------------------------
  407. // NAME_VALUE methods
  408. static inline size_t namevalue_alloc_size(DICTIONARY *dict) {
  409. return (dict->flags & DICTIONARY_FLAG_WITH_STATISTICS) ? sizeof(NAME_VALUE_WITH_STATS) : sizeof(NAME_VALUE);
  410. }
  411. static inline size_t namevalue_get_namelen(DICTIONARY *dict, NAME_VALUE *nv) {
  412. if(unlikely(dict->flags & DICTIONARY_FLAG_WITH_STATISTICS)) {
  413. NAME_VALUE_WITH_STATS *nvs = (NAME_VALUE_WITH_STATS *)nv;
  414. return nvs->name_len;
  415. }
  416. return 0;
  417. }
  418. static inline size_t namevalue_get_valuelen(DICTIONARY *dict, NAME_VALUE *nv) {
  419. if(unlikely(dict->flags & DICTIONARY_FLAG_WITH_STATISTICS)) {
  420. NAME_VALUE_WITH_STATS *nvs = (NAME_VALUE_WITH_STATS *)nv;
  421. return nvs->value_len;
  422. }
  423. return 0;
  424. }
  425. static inline void namevalue_set_valuelen(DICTIONARY *dict, NAME_VALUE *nv, size_t value_len) {
  426. if(unlikely(dict->flags & DICTIONARY_FLAG_WITH_STATISTICS)) {
  427. NAME_VALUE_WITH_STATS *nvs = (NAME_VALUE_WITH_STATS *)nv;
  428. nvs->value_len = value_len;
  429. }
  430. }
  431. static inline void namevalue_set_namevaluelen(DICTIONARY *dict, NAME_VALUE *nv, size_t name_len, size_t value_len) {
  432. if(unlikely(dict->flags & DICTIONARY_FLAG_WITH_STATISTICS)) {
  433. NAME_VALUE_WITH_STATS *nvs = (NAME_VALUE_WITH_STATS *)nv;
  434. nvs->name_len = name_len;
  435. nvs->value_len = value_len;
  436. }
  437. }
  438. static NAME_VALUE *namevalue_create_unsafe(DICTIONARY *dict, const char *name, size_t name_len, void *value, size_t value_len) {
  439. debug(D_DICTIONARY, "Creating name value entry for name '%s'.", name);
  440. size_t size = namevalue_alloc_size(dict);
  441. NAME_VALUE *nv = mallocz(size);
  442. size_t allocated = size;
  443. namevalue_set_namevaluelen(dict, nv, name_len, value_len);
  444. if(likely(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE))
  445. nv->name = (char *)name;
  446. else {
  447. nv->name = mallocz(name_len);
  448. memcpy(nv->name, name, name_len);
  449. allocated += name_len;
  450. }
  451. if(likely(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE))
  452. nv->value = value;
  453. else {
  454. if(likely(value_len)) {
  455. if (value) {
  456. // a value has been supplied
  457. // copy it
  458. nv->value = mallocz(value_len);
  459. memcpy(nv->value, value, value_len);
  460. }
  461. else {
  462. // no value has been supplied
  463. // allocate a clear memory block
  464. nv->value = callocz(1, value_len);
  465. }
  466. }
  467. else {
  468. // the caller want an item without any value
  469. nv->value = NULL;
  470. }
  471. allocated += value_len;
  472. }
  473. DICTIONARY_STATS_ENTRIES_PLUS1(dict, allocated);
  474. return nv;
  475. }
  476. static void namevalue_reset_unsafe(DICTIONARY *dict, NAME_VALUE *nv, void *value, size_t value_len) {
  477. debug(D_DICTIONARY, "Dictionary entry with name '%s' found. Changing its value.", nv->name);
  478. if(likely(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)) {
  479. debug(D_DICTIONARY, "Dictionary: linking value to '%s'", nv->name);
  480. nv->value = value;
  481. namevalue_set_valuelen(dict, nv, value_len);
  482. }
  483. else {
  484. debug(D_DICTIONARY, "Dictionary: cloning value to '%s'", nv->name);
  485. DICTIONARY_STATS_VALUE_RESETS_PLUS1(dict, namevalue_get_valuelen(dict, nv), value_len);
  486. void *old = nv->value;
  487. void *new = mallocz(value_len);
  488. memcpy(new, value, value_len);
  489. nv->value = new;
  490. namevalue_set_valuelen(dict, nv, value_len);
  491. debug(D_DICTIONARY, "Dictionary: freeing old value of '%s'", nv->name);
  492. freez(old);
  493. }
  494. }
  495. static size_t namevalue_destroy_unsafe(DICTIONARY *dict, NAME_VALUE *nv) {
  496. debug(D_DICTIONARY, "Destroying name value entry for name '%s'.", nv->name);
  497. size_t freed = 0;
  498. if(unlikely(!(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE))) {
  499. debug(D_DICTIONARY, "Dictionary freeing value of '%s'", nv->name);
  500. freez(nv->value);
  501. freed += namevalue_get_valuelen(dict, nv);
  502. }
  503. if(unlikely(!(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE))) {
  504. debug(D_DICTIONARY, "Dictionary freeing name '%s'", nv->name);
  505. freez(nv->name);
  506. freed += namevalue_get_namelen(dict, nv);
  507. }
  508. freez(nv);
  509. freed += namevalue_alloc_size(dict);
  510. DICTIONARY_STATS_ENTRIES_MINUS1(dict, freed);
  511. return freed;
  512. }
  513. // ----------------------------------------------------------------------------
  514. // API - dictionary management
  515. DICTIONARY *dictionary_create(DICTIONARY_FLAGS flags) {
  516. debug(D_DICTIONARY, "Creating dictionary.");
  517. if((flags & DICTIONARY_FLAG_REFERENCE_COUNTERS) && (flags & DICTIONARY_FLAG_SINGLE_THREADED)) {
  518. error("DICTIONARY: requested reference counters on single threaded dictionary. Not adding reference counters.");
  519. flags &= ~DICTIONARY_FLAG_REFERENCE_COUNTERS;
  520. }
  521. if(flags & DICTIONARY_FLAG_REFERENCE_COUNTERS) {
  522. // we need statistics to allocate the extra NAME_VALUE attributes
  523. flags |= DICTIONARY_FLAG_WITH_STATISTICS;
  524. }
  525. DICTIONARY *dict = callocz(1, sizeof(DICTIONARY));
  526. size_t allocated = sizeof(DICTIONARY);
  527. dict->flags = flags;
  528. dict->first_item = dict->last_item = NULL;
  529. allocated += dictionary_lock_init(dict);
  530. allocated += reference_counter_init(dict);
  531. if(flags & DICTIONARY_FLAG_WITH_STATISTICS) {
  532. dict->stats = callocz(1, sizeof(struct dictionary_stats));
  533. allocated += sizeof(struct dictionary_stats);
  534. dict->stats->memory = allocated;
  535. }
  536. else
  537. dict->stats = NULL;
  538. hashtable_init_unsafe(dict);
  539. return (DICTIONARY *)dict;
  540. }
  541. size_t dictionary_destroy(DICTIONARY *dict) {
  542. debug(D_DICTIONARY, "Destroying dictionary.");
  543. dictionary_lock_wrlock(dict);
  544. size_t freed = 0;
  545. NAME_VALUE *nv = dict->first_item;
  546. while (nv) {
  547. // cache nv->next
  548. // because we are going to free nv
  549. NAME_VALUE *nvnext = nv->next;
  550. freed += namevalue_destroy_unsafe(dict, nv);
  551. nv = nvnext;
  552. // to speed up destruction, we don't
  553. // unlink nv from the linked-list here
  554. }
  555. dict->first_item = NULL;
  556. dict->last_item = NULL;
  557. // destroy the dictionary
  558. freed += hashtable_destroy_unsafe(dict);
  559. dictionary_unlock(dict);
  560. freed += dictionary_lock_free(dict);
  561. freed += reference_counter_free(dict);
  562. if(unlikely(dict->flags & DICTIONARY_FLAG_WITH_STATISTICS)) {
  563. freez(dict->stats);
  564. dict->stats = NULL;
  565. freed += sizeof(struct dictionary_stats);
  566. }
  567. freez(dict);
  568. freed += sizeof(DICTIONARY);
  569. return freed;
  570. }
  571. // ----------------------------------------------------------------------------
  572. // API - items management
  573. void *dictionary_set_unsafe(DICTIONARY *dict, const char *name, void *value, size_t value_len) {
  574. if(unlikely(!name || !*name)) {
  575. error("Attempted to dictionary_set() a dictionary item without a name");
  576. return NULL;
  577. }
  578. size_t name_len = strlen(name) + 1; // we need the terminating null too
  579. debug(D_DICTIONARY, "SET dictionary entry with name '%s'.", name);
  580. // DISCUSSION:
  581. // Is it better to gain a read-lock and do a hashtable_get_unsafe()
  582. // before we write lock to do hashtable_insert_unsafe()?
  583. //
  584. // Probably this depends on the use case.
  585. // For statsd for example that does dictionary_set() to update received values,
  586. // it could be beneficial to do a get() before we insert().
  587. //
  588. // But the caller has the option to do this on his/her own.
  589. // So, let's do the fastest here and let the caller decide the flow of calls.
  590. NAME_VALUE *nv, **pnv = hashtable_insert_unsafe(dict, name, name_len);
  591. if(likely(*pnv == 0)) {
  592. // a new item added to the index
  593. nv = *pnv = namevalue_create_unsafe(dict, name, name_len, value, value_len);
  594. hashtable_inserted_name_value_unsafe(dict, name, name_len, nv);
  595. linkedlist_namevalue_link_unsafe(dict, nv);
  596. if(dict->ins_callback)
  597. dict->ins_callback(nv->name, nv->value, dict->ins_callback_data);
  598. }
  599. else {
  600. // the item is already in the index
  601. // so, either we will return the old one
  602. // or overwrite the value, depending on dictionary flags
  603. nv = *pnv;
  604. if(!(dict->flags & DICTIONARY_FLAG_DONT_OVERWRITE_VALUE)) {
  605. if(dict->del_callback)
  606. dict->del_callback(nv->name, nv->value, dict->del_callback_data);
  607. namevalue_reset_unsafe(dict, nv, value, value_len);
  608. if(dict->ins_callback)
  609. dict->ins_callback(nv->name, nv->value, dict->ins_callback_data);
  610. }
  611. else if(dict->conflict_callback)
  612. dict->conflict_callback(nv->name, nv->value, value, dict->conflict_callback_data);
  613. }
  614. return nv->value;
  615. }
  616. void *dictionary_set(DICTIONARY *dict, const char *name, void *value, size_t value_len) {
  617. dictionary_lock_wrlock(dict);
  618. void *ret = dictionary_set_unsafe(dict, name, value, value_len);
  619. dictionary_unlock(dict);
  620. return ret;
  621. }
  622. void *dictionary_get_unsafe(DICTIONARY *dict, const char *name) {
  623. if(unlikely(!name || !*name)) {
  624. error("Attempted to dictionary_get() without a name");
  625. return NULL;
  626. }
  627. size_t name_len = strlen(name) + 1; // we need the terminating null too
  628. debug(D_DICTIONARY, "GET dictionary entry with name '%s'.", name);
  629. NAME_VALUE *nv = hashtable_get_unsafe(dict, name, name_len);
  630. if(unlikely(!nv)) {
  631. debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
  632. return NULL;
  633. }
  634. debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
  635. return nv->value;
  636. }
  637. void *dictionary_get(DICTIONARY *dict, const char *name) {
  638. dictionary_lock_rlock(dict);
  639. void *ret = dictionary_get_unsafe(dict, name);
  640. dictionary_unlock(dict);
  641. return ret;
  642. }
  643. int dictionary_del_unsafe(DICTIONARY *dict, const char *name) {
  644. if(unlikely(!name || !*name)) {
  645. error("Attempted to dictionary_det() without a name");
  646. return -1;
  647. }
  648. size_t name_len = strlen(name) + 1; // we need the terminating null too
  649. debug(D_DICTIONARY, "DEL dictionary entry with name '%s'.", name);
  650. // Unfortunately, the JudyHSDel() does not return the value of the
  651. // item that was deleted, so we have to find it before we delete it,
  652. // since we need to release our structures too.
  653. int ret;
  654. NAME_VALUE *nv = hashtable_get_unsafe(dict, name, name_len);
  655. if(unlikely(!nv)) {
  656. debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
  657. ret = -1;
  658. }
  659. else {
  660. debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
  661. if(hashtable_delete_unsafe(dict, name, name_len, nv) == 0)
  662. error("DICTIONARY: INTERNAL ERROR: tried to delete item with name '%s' that is not in the index", name);
  663. if(!reference_counter_mark_deleted(dict, nv)) {
  664. linkedlist_namevalue_unlink_unsafe(dict, nv);
  665. if(dict->del_callback)
  666. dict->del_callback(nv->name, nv->value, dict->del_callback_data);
  667. namevalue_destroy_unsafe(dict, nv);
  668. }
  669. ret = 0;
  670. }
  671. return ret;
  672. }
  673. int dictionary_del(DICTIONARY *dict, const char *name) {
  674. dictionary_lock_wrlock(dict);
  675. int ret = dictionary_del_unsafe(dict, name);
  676. dictionary_unlock(dict);
  677. return ret;
  678. }
  679. // ----------------------------------------------------------------------------
  680. // traversal with loop
  681. void *dictionary_foreach_start_rw(DICTFE *dfe, DICTIONARY *dict, char rw) {
  682. if(unlikely(!dfe || !dict)) return NULL;
  683. dfe->dict = dict;
  684. dfe->started_ut = now_realtime_usec();
  685. if(rw == 'r' || rw == 'R')
  686. dictionary_lock_rlock(dict);
  687. else
  688. dictionary_lock_wrlock(dict);
  689. NAME_VALUE *nv = dict->first_item;
  690. dfe->last_position_index = (void *)nv;
  691. if(likely(nv)) {
  692. dfe->next_position_index = (void *)nv->next;
  693. dfe->name = nv->name;
  694. dfe->value = (void *)nv->value;
  695. reference_counter_acquire(dict, nv);
  696. }
  697. else {
  698. dfe->next_position_index = NULL;
  699. dfe->name = NULL;
  700. dfe->value = NULL;
  701. }
  702. return dfe->value;
  703. }
  704. void *dictionary_foreach_next(DICTFE *dfe) {
  705. if(unlikely(!dfe || !dfe->dict)) return NULL;
  706. NAME_VALUE *nv = (NAME_VALUE *)dfe->last_position_index;
  707. if(likely(nv))
  708. reference_counter_release(dfe->dict, nv);
  709. nv = dfe->last_position_index = dfe->next_position_index;
  710. if(likely(nv)) {
  711. dfe->next_position_index = (void *)nv->next;
  712. dfe->name = nv->name;
  713. dfe->value = (void *)nv->value;
  714. reference_counter_acquire(dfe->dict, nv);
  715. }
  716. else {
  717. dfe->next_position_index = NULL;
  718. dfe->name = NULL;
  719. dfe->value = NULL;
  720. }
  721. return dfe->value;
  722. }
  723. usec_t dictionary_foreach_done(DICTFE *dfe) {
  724. if(unlikely(!dfe || !dfe->dict)) return 0;
  725. NAME_VALUE *nv = (NAME_VALUE *)dfe->last_position_index;
  726. if(nv)
  727. reference_counter_release(dfe->dict, nv);
  728. dictionary_unlock((DICTIONARY *)dfe->dict);
  729. dfe->dict = NULL;
  730. dfe->last_position_index = NULL;
  731. dfe->next_position_index = NULL;
  732. dfe->name = NULL;
  733. dfe->value = NULL;
  734. usec_t usec = now_realtime_usec() - dfe->started_ut;
  735. dfe->started_ut = 0;
  736. return usec;
  737. }
  738. // ----------------------------------------------------------------------------
  739. // API - walk through the dictionary
  740. // the dictionary is locked for reading while this happens
  741. // do not use other dictionary calls while walking the dictionary - deadlock!
  742. int dictionary_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(const char *name, void *entry, void *data), void *data) {
  743. if(rw == 'r' || rw == 'R')
  744. dictionary_lock_rlock(dict);
  745. else
  746. dictionary_lock_wrlock(dict);
  747. // written in such a way, that the callback can delete the active element
  748. int ret = 0;
  749. NAME_VALUE *nv = dict->first_item, *nv_next;
  750. while(nv) {
  751. nv_next = nv->next;
  752. reference_counter_acquire(dict, nv);
  753. int r = callback(nv->name, nv->value, data);
  754. reference_counter_release(dict, nv);
  755. if(unlikely(r < 0)) {
  756. ret = r;
  757. break;
  758. }
  759. ret += r;
  760. nv = nv_next;
  761. }
  762. dictionary_unlock(dict);
  763. return ret;
  764. }
  765. // ----------------------------------------------------------------------------
  766. // unit test
  767. static void dictionary_unittest_free_char_pp(char **pp, size_t entries) {
  768. for(size_t i = 0; i < entries ;i++)
  769. freez(pp[i]);
  770. freez(pp);
  771. }
  772. static char **dictionary_unittest_generate_names(size_t entries) {
  773. char **names = mallocz(sizeof(char *) * entries);
  774. for(size_t i = 0; i < entries ;i++) {
  775. char buf[25 + 1] = "";
  776. snprintfz(buf, 25, "name.%zu.0123456789.%zu \t !@#$%%^&*(),./[]{}\\|~`", i, entries / 2 + i);
  777. names[i] = strdupz(buf);
  778. }
  779. return names;
  780. }
  781. static char **dictionary_unittest_generate_values(size_t entries) {
  782. char **values = mallocz(sizeof(char *) * entries);
  783. for(size_t i = 0; i < entries ;i++) {
  784. char buf[25 + 1] = "";
  785. snprintfz(buf, 25, "value-%zu-0987654321.%zu%%^&*(),. \t !@#$/[]{}\\|~`", i, entries / 2 + i);
  786. values[i] = strdupz(buf);
  787. }
  788. return values;
  789. }
  790. static size_t dictionary_unittest_set_clone(DICTIONARY *dict, char **names, char **values, size_t entries) {
  791. size_t errors = 0;
  792. for(size_t i = 0; i < entries ;i++) {
  793. size_t vallen = strlen(values[i]) + 1;
  794. char *val = (char *)dictionary_set(dict, names[i], values[i], vallen);
  795. if(val == values[i]) { fprintf(stderr, ">>> %s() returns reference to value\n", __FUNCTION__); errors++; }
  796. if(!val || memcmp(val, values[i], vallen) != 0) { fprintf(stderr, ">>> %s() returns invalid value\n", __FUNCTION__); errors++; }
  797. }
  798. return errors;
  799. }
  800. static size_t dictionary_unittest_set_nonclone(DICTIONARY *dict, char **names, char **values, size_t entries) {
  801. size_t errors = 0;
  802. for(size_t i = 0; i < entries ;i++) {
  803. size_t vallen = strlen(values[i]) + 1;
  804. char *val = (char *)dictionary_set(dict, names[i], values[i], vallen);
  805. if(val != values[i]) { fprintf(stderr, ">>> %s() returns invalid pointer to value\n", __FUNCTION__); errors++; }
  806. }
  807. return errors;
  808. }
  809. static size_t dictionary_unittest_get_clone(DICTIONARY *dict, char **names, char **values, size_t entries) {
  810. size_t errors = 0;
  811. for(size_t i = 0; i < entries ;i++) {
  812. size_t vallen = strlen(values[i]) + 1;
  813. char *val = (char *)dictionary_get(dict, names[i]);
  814. if(val == values[i]) { fprintf(stderr, ">>> %s() returns reference to value\n", __FUNCTION__); errors++; }
  815. if(!val || memcmp(val, values[i], vallen) != 0) { fprintf(stderr, ">>> %s() returns invalid value\n", __FUNCTION__); errors++; }
  816. }
  817. return errors;
  818. }
  819. static size_t dictionary_unittest_get_nonclone(DICTIONARY *dict, char **names, char **values, size_t entries) {
  820. size_t errors = 0;
  821. for(size_t i = 0; i < entries ;i++) {
  822. char *val = (char *)dictionary_get(dict, names[i]);
  823. if(val != values[i]) { fprintf(stderr, ">>> %s() returns invalid pointer to value\n", __FUNCTION__); errors++; }
  824. }
  825. return errors;
  826. }
  827. static size_t dictionary_unittest_get_nonexisting(DICTIONARY *dict, char **names, char **values, size_t entries) {
  828. (void)names;
  829. size_t errors = 0;
  830. for(size_t i = 0; i < entries ;i++) {
  831. char *val = (char *)dictionary_get(dict, values[i]);
  832. if(val) { fprintf(stderr, ">>> %s() returns non-existing item\n", __FUNCTION__); errors++; }
  833. }
  834. return errors;
  835. }
  836. static size_t dictionary_unittest_del_nonexisting(DICTIONARY *dict, char **names, char **values, size_t entries) {
  837. (void)names;
  838. size_t errors = 0;
  839. for(size_t i = 0; i < entries ;i++) {
  840. int ret = dictionary_del(dict, values[i]);
  841. if(ret != -1) { fprintf(stderr, ">>> %s() deleted non-existing item\n", __FUNCTION__); errors++; }
  842. }
  843. return errors;
  844. }
  845. static size_t dictionary_unittest_del_existing(DICTIONARY *dict, char **names, char **values, size_t entries) {
  846. (void)values;
  847. size_t errors = 0;
  848. size_t forward_from = 0, forward_to = entries / 3;
  849. size_t middle_from = forward_to, middle_to = entries * 2 / 3;
  850. size_t backward_from = middle_to, backward_to = entries;
  851. for(size_t i = forward_from; i < forward_to ;i++) {
  852. int ret = dictionary_del(dict, names[i]);
  853. if(ret == -1) { fprintf(stderr, ">>> %s() didn't delete (forward) existing item\n", __FUNCTION__); errors++; }
  854. }
  855. for(size_t i = middle_to - 1; i >= middle_from ;i--) {
  856. int ret = dictionary_del(dict, names[i]);
  857. if(ret == -1) { fprintf(stderr, ">>> %s() didn't delete (middle) existing item\n", __FUNCTION__); errors++; }
  858. }
  859. for(size_t i = backward_to - 1; i >= backward_from ;i--) {
  860. int ret = dictionary_del(dict, names[i]);
  861. if(ret == -1) { fprintf(stderr, ">>> %s() didn't delete (backward) existing item\n", __FUNCTION__); errors++; }
  862. }
  863. return errors;
  864. }
  865. static size_t dictionary_unittest_reset_clone(DICTIONARY *dict, char **names, char **values, size_t entries) {
  866. (void)values;
  867. // set the name as value too
  868. size_t errors = 0;
  869. for(size_t i = 0; i < entries ;i++) {
  870. size_t vallen = strlen(names[i]) + 1;
  871. char *val = (char *)dictionary_set(dict, names[i], names[i], vallen);
  872. if(val == names[i]) { fprintf(stderr, ">>> %s() returns reference to value\n", __FUNCTION__); errors++; }
  873. if(!val || memcmp(val, names[i], vallen) != 0) { fprintf(stderr, ">>> %s() returns invalid value\n", __FUNCTION__); errors++; }
  874. }
  875. return errors;
  876. }
  877. static size_t dictionary_unittest_reset_nonclone(DICTIONARY *dict, char **names, char **values, size_t entries) {
  878. (void)values;
  879. // set the name as value too
  880. size_t errors = 0;
  881. for(size_t i = 0; i < entries ;i++) {
  882. size_t vallen = strlen(names[i]) + 1;
  883. char *val = (char *)dictionary_set(dict, names[i], names[i], vallen);
  884. if(val != names[i]) { fprintf(stderr, ">>> %s() returns invalid pointer to value\n", __FUNCTION__); errors++; }
  885. if(!val) { fprintf(stderr, ">>> %s() returns invalid value\n", __FUNCTION__); errors++; }
  886. }
  887. return errors;
  888. }
  889. static size_t dictionary_unittest_reset_dont_overwrite_nonclone(DICTIONARY *dict, char **names, char **values, size_t entries) {
  890. // set the name as value too
  891. size_t errors = 0;
  892. for(size_t i = 0; i < entries ;i++) {
  893. size_t vallen = strlen(names[i]) + 1;
  894. char *val = (char *)dictionary_set(dict, names[i], names[i], vallen);
  895. if(val != values[i]) { fprintf(stderr, ">>> %s() returns invalid pointer to value\n", __FUNCTION__); errors++; }
  896. }
  897. return errors;
  898. }
  899. static int dictionary_unittest_walkthrough_callback(const char *name, void *value, void *data) {
  900. (void)name;
  901. (void)value;
  902. (void)data;
  903. return 1;
  904. }
  905. static size_t dictionary_unittest_walkthrough(DICTIONARY *dict, char **names, char **values, size_t entries) {
  906. (void)names;
  907. (void)values;
  908. int sum = dictionary_walkthrough_read(dict, dictionary_unittest_walkthrough_callback, NULL);
  909. if(sum < (int)entries) return entries - sum;
  910. else return sum - entries;
  911. }
  912. static int dictionary_unittest_walkthrough_delete_this_callback(const char *name, void *value, void *data) {
  913. (void)value;
  914. if(dictionary_del_having_write_lock((DICTIONARY *)data, name) == -1)
  915. return 0;
  916. return 1;
  917. }
  918. static size_t dictionary_unittest_walkthrough_delete_this(DICTIONARY *dict, char **names, char **values, size_t entries) {
  919. (void)names;
  920. (void)values;
  921. int sum = dictionary_walkthrough_write(dict, dictionary_unittest_walkthrough_delete_this_callback, dict);
  922. if(sum < (int)entries) return entries - sum;
  923. else return sum - entries;
  924. }
  925. static int dictionary_unittest_walkthrough_stop_callback(const char *name, void *value, void *data) {
  926. (void)name;
  927. (void)value;
  928. (void)data;
  929. return -1;
  930. }
  931. static size_t dictionary_unittest_walkthrough_stop(DICTIONARY *dict, char **names, char **values, size_t entries) {
  932. (void)names;
  933. (void)values;
  934. (void)entries;
  935. int sum = dictionary_walkthrough_read(dict, dictionary_unittest_walkthrough_stop_callback, NULL);
  936. if(sum != -1) return 1;
  937. return 0;
  938. }
  939. static size_t dictionary_unittest_foreach(DICTIONARY *dict, char **names, char **values, size_t entries) {
  940. (void)names;
  941. (void)values;
  942. (void)entries;
  943. size_t count = 0;
  944. char *item;
  945. dfe_start_read(dict, item)
  946. count++;
  947. dfe_done(item);
  948. if(count > entries) return count - entries;
  949. return entries - count;
  950. }
  951. static size_t dictionary_unittest_foreach_delete_this(DICTIONARY *dict, char **names, char **values, size_t entries) {
  952. (void)names;
  953. (void)values;
  954. (void)entries;
  955. size_t count = 0;
  956. char *item;
  957. dfe_start_write(dict, item)
  958. if(dictionary_del_having_write_lock(dict, item_name) != -1) count++;
  959. dfe_done(item);
  960. if(count > entries) return count - entries;
  961. return entries - count;
  962. }
  963. static size_t dictionary_unittest_destroy(DICTIONARY *dict, char **names, char **values, size_t entries) {
  964. (void)names;
  965. (void)values;
  966. (void)entries;
  967. size_t bytes = dictionary_destroy(dict);
  968. fprintf(stderr, " %s() freed %zu bytes,", __FUNCTION__, bytes);
  969. return 0;
  970. }
  971. static usec_t dictionary_unittest_run_and_measure_time(DICTIONARY *dict, char *message, char **names, char **values, size_t entries, size_t *errors, size_t (*callback)(DICTIONARY *dict, char **names, char **values, size_t entries)) {
  972. fprintf(stderr, "%-40s... ", message);
  973. usec_t started = now_realtime_usec();
  974. size_t errs = callback(dict, names, values, entries);
  975. usec_t ended = now_realtime_usec();
  976. usec_t dt = ended - started;
  977. if(callback == dictionary_unittest_destroy) dict = NULL;
  978. fprintf(stderr, " %zu errors, %zu items in dictionary, %llu usec \n", errs, dict? dictionary_stats_entries(dict):0, dt);
  979. *errors += errs;
  980. return dt;
  981. }
  982. void dictionary_unittest_clone(DICTIONARY *dict, char **names, char **values, size_t entries, size_t *errors) {
  983. dictionary_unittest_run_and_measure_time(dict, "adding entries", names, values, entries, errors, dictionary_unittest_set_clone);
  984. dictionary_unittest_run_and_measure_time(dict, "getting entries", names, values, entries, errors, dictionary_unittest_get_clone);
  985. dictionary_unittest_run_and_measure_time(dict, "getting non-existing entries", names, values, entries, errors, dictionary_unittest_get_nonexisting);
  986. dictionary_unittest_run_and_measure_time(dict, "resetting entries", names, values, entries, errors, dictionary_unittest_reset_clone);
  987. dictionary_unittest_run_and_measure_time(dict, "deleting non-existing entries", names, values, entries, errors, dictionary_unittest_del_nonexisting);
  988. dictionary_unittest_run_and_measure_time(dict, "traverse foreach read loop", names, values, entries, errors, dictionary_unittest_foreach);
  989. dictionary_unittest_run_and_measure_time(dict, "walkthrough read callback", names, values, entries, errors, dictionary_unittest_walkthrough);
  990. dictionary_unittest_run_and_measure_time(dict, "walkthrough read callback stop", names, values, entries, errors, dictionary_unittest_walkthrough_stop);
  991. dictionary_unittest_run_and_measure_time(dict, "deleting existing entries", names, values, entries, errors, dictionary_unittest_del_existing);
  992. dictionary_unittest_run_and_measure_time(dict, "walking through empty", names, values, 0, errors, dictionary_unittest_walkthrough);
  993. dictionary_unittest_run_and_measure_time(dict, "traverse foreach empty", names, values, 0, errors, dictionary_unittest_foreach);
  994. dictionary_unittest_run_and_measure_time(dict, "destroying empty dictionary", names, values, entries, errors, dictionary_unittest_destroy);
  995. }
  996. void dictionary_unittest_nonclone(DICTIONARY *dict, char **names, char **values, size_t entries, size_t *errors) {
  997. dictionary_unittest_run_and_measure_time(dict, "adding entries", names, values, entries, errors, dictionary_unittest_set_nonclone);
  998. dictionary_unittest_run_and_measure_time(dict, "getting entries", names, values, entries, errors, dictionary_unittest_get_nonclone);
  999. dictionary_unittest_run_and_measure_time(dict, "getting non-existing entries", names, values, entries, errors, dictionary_unittest_get_nonexisting);
  1000. dictionary_unittest_run_and_measure_time(dict, "resetting entries", names, values, entries, errors, dictionary_unittest_reset_nonclone);
  1001. dictionary_unittest_run_and_measure_time(dict, "deleting non-existing entries", names, values, entries, errors, dictionary_unittest_del_nonexisting);
  1002. dictionary_unittest_run_and_measure_time(dict, "traverse foreach read loop", names, values, entries, errors, dictionary_unittest_foreach);
  1003. dictionary_unittest_run_and_measure_time(dict, "walkthrough read callback", names, values, entries, errors, dictionary_unittest_walkthrough);
  1004. dictionary_unittest_run_and_measure_time(dict, "walkthrough read callback stop", names, values, entries, errors, dictionary_unittest_walkthrough_stop);
  1005. dictionary_unittest_run_and_measure_time(dict, "deleting existing entries", names, values, entries, errors, dictionary_unittest_del_existing);
  1006. dictionary_unittest_run_and_measure_time(dict, "walking through empty", names, values, 0, errors, dictionary_unittest_walkthrough);
  1007. dictionary_unittest_run_and_measure_time(dict, "traverse foreach empty", names, values, 0, errors, dictionary_unittest_foreach);
  1008. dictionary_unittest_run_and_measure_time(dict, "destroying empty dictionary", names, values, entries, errors, dictionary_unittest_destroy);
  1009. }
  1010. int dictionary_unittest(size_t entries) {
  1011. if(entries < 10) entries = 10;
  1012. DICTIONARY *dict;
  1013. size_t errors = 0;
  1014. fprintf(stderr, "Generating %zu names and values...\n", entries);
  1015. char **names = dictionary_unittest_generate_names(entries);
  1016. char **values = dictionary_unittest_generate_values(entries);
  1017. fprintf(stderr, "\nCreating dictionary single threaded, clone, %zu items\n", entries);
  1018. dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED|DICTIONARY_FLAG_WITH_STATISTICS);
  1019. dictionary_unittest_clone(dict, names, values, entries, &errors);
  1020. fprintf(stderr, "\nCreating dictionary multi threaded, clone, %zu items\n", entries);
  1021. dict = dictionary_create(DICTIONARY_FLAG_WITH_STATISTICS);
  1022. dictionary_unittest_clone(dict, names, values, entries, &errors);
  1023. fprintf(stderr, "\nCreating dictionary single threaded, non-clone, add-in-front options, %zu items\n", entries);
  1024. dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED|DICTIONARY_FLAG_WITH_STATISTICS|DICTIONARY_FLAG_NAME_LINK_DONT_CLONE|DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE|DICTIONARY_FLAG_ADD_IN_FRONT);
  1025. dictionary_unittest_nonclone(dict, names, values, entries, &errors);
  1026. fprintf(stderr, "\nCreating dictionary multi threaded, non-clone, add-in-front options, %zu items\n", entries);
  1027. dict = dictionary_create(DICTIONARY_FLAG_WITH_STATISTICS|DICTIONARY_FLAG_NAME_LINK_DONT_CLONE|DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE|DICTIONARY_FLAG_ADD_IN_FRONT);
  1028. dictionary_unittest_nonclone(dict, names, values, entries, &errors);
  1029. fprintf(stderr, "\nCreating dictionary single-threaded, non-clone, don't overwrite options, %zu items\n", entries);
  1030. dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED|DICTIONARY_FLAG_WITH_STATISTICS|DICTIONARY_FLAG_NAME_LINK_DONT_CLONE|DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE|DICTIONARY_FLAG_DONT_OVERWRITE_VALUE);
  1031. dictionary_unittest_run_and_measure_time(dict, "adding entries", names, values, entries, &errors, dictionary_unittest_set_nonclone);
  1032. dictionary_unittest_run_and_measure_time(dict, "resetting non-overwrite entries", names, values, entries, &errors, dictionary_unittest_reset_dont_overwrite_nonclone);
  1033. dictionary_unittest_run_and_measure_time(dict, "traverse foreach read loop", names, values, entries, &errors, dictionary_unittest_foreach);
  1034. dictionary_unittest_run_and_measure_time(dict, "walkthrough read callback", names, values, entries, &errors, dictionary_unittest_walkthrough);
  1035. dictionary_unittest_run_and_measure_time(dict, "walkthrough read callback stop", names, values, entries, &errors, dictionary_unittest_walkthrough_stop);
  1036. dictionary_unittest_run_and_measure_time(dict, "destroying full dictionary", names, values, entries, &errors, dictionary_unittest_destroy);
  1037. fprintf(stderr, "\nCreating dictionary multi-threaded, non-clone, don't overwrite options, %zu items\n", entries);
  1038. dict = dictionary_create(DICTIONARY_FLAG_WITH_STATISTICS|DICTIONARY_FLAG_NAME_LINK_DONT_CLONE|DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE|DICTIONARY_FLAG_DONT_OVERWRITE_VALUE);
  1039. dictionary_unittest_run_and_measure_time(dict, "adding entries", names, values, entries, &errors, dictionary_unittest_set_nonclone);
  1040. dictionary_unittest_run_and_measure_time(dict, "walkthrough write delete this", names, values, entries, &errors, dictionary_unittest_walkthrough_delete_this);
  1041. dictionary_unittest_run_and_measure_time(dict, "destroying empty dictionary", names, values, entries, &errors, dictionary_unittest_destroy);
  1042. fprintf(stderr, "\nCreating dictionary multi-threaded, non-clone, don't overwrite options, %zu items\n", entries);
  1043. dict = dictionary_create(DICTIONARY_FLAG_WITH_STATISTICS|DICTIONARY_FLAG_NAME_LINK_DONT_CLONE|DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE|DICTIONARY_FLAG_DONT_OVERWRITE_VALUE);
  1044. dictionary_unittest_run_and_measure_time(dict, "adding entries", names, values, entries, &errors, dictionary_unittest_set_nonclone);
  1045. dictionary_unittest_run_and_measure_time(dict, "foreach write delete this", names, values, entries, &errors, dictionary_unittest_foreach_delete_this);
  1046. dictionary_unittest_run_and_measure_time(dict, "traverse foreach read loop empty", names, values, 0, &errors, dictionary_unittest_foreach);
  1047. dictionary_unittest_run_and_measure_time(dict, "walkthrough read callback empty", names, values, 0, &errors, dictionary_unittest_walkthrough);
  1048. dictionary_unittest_run_and_measure_time(dict, "destroying empty dictionary", names, values, entries, &errors, dictionary_unittest_destroy);
  1049. dictionary_unittest_free_char_pp(names, entries);
  1050. dictionary_unittest_free_char_pp(values, entries);
  1051. fprintf(stderr, "\n%zu errors found\n", errors);
  1052. return (int)errors;
  1053. }