13 #include "dictionary.h"
15 // ----------------------------------------------------------------------------
16 // dictionary statistics
18 static inline void NETDATA_DICTIONARY_STATS_INSERTS_PLUS1(DICTIONARY *dict) {
19 if(likely(dict->stats))
20 dict->stats->inserts++;
22 static inline void NETDATA_DICTIONARY_STATS_DELETES_PLUS1(DICTIONARY *dict) {
23 if(likely(dict->stats))
24 dict->stats->deletes++;
26 static inline void NETDATA_DICTIONARY_STATS_SEARCHES_PLUS1(DICTIONARY *dict) {
27 if(likely(dict->stats))
28 dict->stats->searches++;
30 static inline void NETDATA_DICTIONARY_STATS_ENTRIES_PLUS1(DICTIONARY *dict) {
31 if(likely(dict->stats))
32 dict->stats->entries++;
34 static inline void NETDATA_DICTIONARY_STATS_ENTRIES_MINUS1(DICTIONARY *dict) {
35 if(likely(dict->stats))
36 dict->stats->entries--;
40 // ----------------------------------------------------------------------------
43 static inline void dictionary_read_lock(DICTIONARY *dict) {
44 if(likely(dict->rwlock)) {
45 // debug(D_DICTIONARY, "Dictionary READ lock");
46 pthread_rwlock_rdlock(dict->rwlock);
50 static inline void dictionary_write_lock(DICTIONARY *dict) {
51 if(likely(dict->rwlock)) {
52 // debug(D_DICTIONARY, "Dictionary WRITE lock");
53 pthread_rwlock_wrlock(dict->rwlock);
57 static inline void dictionary_unlock(DICTIONARY *dict) {
58 if(likely(dict->rwlock)) {
59 // debug(D_DICTIONARY, "Dictionary UNLOCK lock");
60 pthread_rwlock_unlock(dict->rwlock);
65 // ----------------------------------------------------------------------------
68 static int name_value_compare(void* a, void* b) {
69 if(((NAME_VALUE *)a)->hash < ((NAME_VALUE *)b)->hash) return -1;
70 else if(((NAME_VALUE *)a)->hash > ((NAME_VALUE *)b)->hash) return 1;
71 else return strcmp(((NAME_VALUE *)a)->name, ((NAME_VALUE *)b)->name);
74 #define dictionary_name_value_index_add_nolock(dict, nv) do { NETDATA_DICTIONARY_STATS_INSERTS_PLUS1(dict); avl_insert(&((dict)->values_index), (avl *)(nv)); } while(0)
75 #define dictionary_name_value_index_del_nolock(dict, nv) do { NETDATA_DICTIONARY_STATS_DELETES_PLUS1(dict); avl_remove(&(dict->values_index), (avl *)(nv)); } while(0)
77 static inline NAME_VALUE *dictionary_name_value_index_find_nolock(DICTIONARY *dict, const char *name, uint32_t hash) {
79 tmp.hash = (hash)?hash:simple_hash(name);
80 tmp.name = (char *)name;
82 NETDATA_DICTIONARY_STATS_SEARCHES_PLUS1(dict);
83 return (NAME_VALUE *)avl_search(&(dict->values_index), (avl *) &tmp);
86 // ----------------------------------------------------------------------------
89 static NAME_VALUE *dictionary_name_value_create_nolock(DICTIONARY *dict, const char *name, void *value, size_t value_len, uint32_t hash) {
90 debug(D_DICTIONARY, "Creating name value entry for name '%s'.", name);
92 NAME_VALUE *nv = calloc(1, sizeof(NAME_VALUE));
93 if(unlikely(!nv)) fatal("Cannot allocate name_value of size %zu", sizeof(NAME_VALUE));
95 if(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)
96 nv->name = (char *)name;
98 nv->name = strdup(name);
99 if (unlikely(!nv->name))
100 fatal("Cannot allocate name_value.name of size %zu", strlen(name));
103 nv->hash = (hash)?hash:simple_hash(nv->name);
105 if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)
108 nv->value = malloc(value_len);
109 if (unlikely(!nv->value))
110 fatal("Cannot allocate name_value.value of size %zu", value_len);
112 memcpy(nv->value, value, value_len);
116 dictionary_name_value_index_add_nolock(dict, nv);
117 NETDATA_DICTIONARY_STATS_ENTRIES_PLUS1(dict);
122 static void dictionary_name_value_destroy_nolock(DICTIONARY *dict, NAME_VALUE *nv) {
123 debug(D_DICTIONARY, "Destroying name value entry for name '%s'.", nv->name);
125 dictionary_name_value_index_del_nolock(dict, nv);
127 NETDATA_DICTIONARY_STATS_ENTRIES_MINUS1(dict);
129 if(!(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)) {
130 debug(D_REGISTRY, "Dictionary freeing value of '%s'", nv->name);
134 if(!(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)) {
135 debug(D_REGISTRY, "Dictionary freeing name '%s'", nv->name);
142 // ----------------------------------------------------------------------------
143 // API - basic methods
145 DICTIONARY *dictionary_create(uint32_t flags) {
146 debug(D_DICTIONARY, "Creating dictionary.");
148 DICTIONARY *dict = calloc(1, sizeof(DICTIONARY));
149 if(unlikely(!dict)) fatal("Cannot allocate DICTIONARY");
151 if(flags & DICTIONARY_FLAG_WITH_STATISTICS) {
152 dict->stats = calloc(1, sizeof(struct dictionary_stats));
153 if(!dict->stats) fatal("Cannot allocate statistics for DICTIONARY");
156 if(!(flags & DICTIONARY_FLAG_SINGLE_THREADED)) {
157 dict->rwlock = calloc(1, sizeof(pthread_rwlock_t));
158 if(!dict->rwlock) fatal("Cannot allocate pthread_rwlock_t for DICTIONARY");
159 pthread_rwlock_init(dict->rwlock, NULL);
162 avl_init(&dict->values_index, name_value_compare);
168 void dictionary_destroy(DICTIONARY *dict) {
169 debug(D_DICTIONARY, "Destroying dictionary.");
171 dictionary_write_lock(dict);
173 while(dict->values_index.root)
174 dictionary_name_value_destroy_nolock(dict, (NAME_VALUE *)dict->values_index.root);
176 dictionary_unlock(dict);
187 // ----------------------------------------------------------------------------
189 void *dictionary_set(DICTIONARY *dict, const char *name, void *value, size_t value_len) {
190 debug(D_DICTIONARY, "SET dictionary entry with name '%s'.", name);
192 uint32_t hash = simple_hash(name);
194 dictionary_write_lock(dict);
196 NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, hash);
198 debug(D_DICTIONARY, "Dictionary entry with name '%s' not found. Creating a new one.", name);
200 nv = dictionary_name_value_create_nolock(dict, name, value, value_len, hash);
202 fatal("Cannot create name_value.");
205 debug(D_DICTIONARY, "Dictionary entry with name '%s' found. Changing its value.", name);
207 if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE) {
208 debug(D_REGISTRY, "Dictionary: linking value to '%s'", name);
212 debug(D_REGISTRY, "Dictionary: cloning value to '%s'", name);
214 // copy the new value without breaking
215 // any other thread accessing the same entry
216 void *new = malloc(value_len),
220 fatal("Cannot allocate value of size %zu", value_len);
222 memcpy(new, value, value_len);
225 debug(D_REGISTRY, "Dictionary: freeing old value of '%s'", name);
230 dictionary_unlock(dict);
235 void *dictionary_get(DICTIONARY *dict, const char *name) {
236 debug(D_DICTIONARY, "GET dictionary entry with name '%s'.", name);
238 dictionary_read_lock(dict);
239 NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0);
240 dictionary_unlock(dict);
243 debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
247 debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
251 int dictionary_del(DICTIONARY *dict, const char *name) {
254 debug(D_DICTIONARY, "DEL dictionary entry with name '%s'.", name);
256 dictionary_write_lock(dict);
258 NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0);
260 debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
264 debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
265 dictionary_name_value_destroy_nolock(dict, nv);
269 dictionary_unlock(dict);
275 // ----------------------------------------------------------------------------
276 // API - walk through the dictionary
277 // the dictionary is locked for reading while this happens
278 // do not user other dictionary calls while walking the dictionary - deadlock!
280 static int dictionary_walker(avl *a, int (*callback)(void *entry, void *data), void *data) {
281 int total = 0, ret = 0;
284 ret = dictionary_walker(a->avl_link[0], callback, data);
285 if(ret < 0) return ret;
289 ret = callback(((NAME_VALUE *)a)->value, data);
290 if(ret < 0) return ret;
294 ret = dictionary_walker(a->avl_link[1], callback, data);
295 if (ret < 0) return ret;
302 int dictionary_get_all(DICTIONARY *dict, int (*callback)(void *entry, void *data), void *data) {
305 dictionary_read_lock(dict);
307 if(likely(dict->values_index.root))
308 ret = dictionary_walker(dict->values_index.root, callback, data);
310 dictionary_unlock(dict);