]> arthur.barton.de Git - netdata.git/blob - src/dictionary.c
registry: working prototype with load/save
[netdata.git] / src / dictionary.c
1 #ifdef HAVE_CONFIG_H
2 #include <config.h>
3 #endif
4
5 #include <pthread.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 #include "avl.h"
10 #include "common.h"
11 #include "log.h"
12
13 #include "dictionary.h"
14
15 // ----------------------------------------------------------------------------
16 // name_value index
17
18 static int name_value_iterator(avl *a) { if(a) {}; return 0; }
19
20 static int name_value_compare(void* a, void* b) {
21         if(((NAME_VALUE *)a)->hash < ((NAME_VALUE *)b)->hash) return -1;
22         else if(((NAME_VALUE *)a)->hash > ((NAME_VALUE *)b)->hash) return 1;
23         else return strcmp(((NAME_VALUE *)a)->name, ((NAME_VALUE *)b)->name);
24 }
25
26 #define dictionary_name_value_index_add_nolock(dict, nv) do { (dict)->inserts++; avl_insert(&((dict)->values_index), (avl *)(nv)); } while(0)
27 #define dictionary_name_value_index_del_nolock(dict, nv) do { (dict)->deletes++; avl_remove(&(dict->values_index), (avl *)(nv)); } while(0)
28
29 static inline NAME_VALUE *dictionary_name_value_index_find_nolock(DICTIONARY *dict, const char *name, uint32_t hash) {
30         NAME_VALUE *result = NULL, tmp;
31         tmp.hash = (hash)?hash:simple_hash(name);
32         tmp.name = (char *)name;
33
34         dict->searches++;
35         avl_search(&(dict->values_index), (avl *)&tmp, name_value_iterator, (avl **)&result);
36
37         return result;
38 }
39
40 static void dictionary_read_lock(DICTIONARY *dict) {
41         if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
42                 // debug(D_DICTIONARY, "Dictionary READ lock");
43                 pthread_rwlock_rdlock(&dict->rwlock);
44         }
45 }
46
47 static void dictionary_write_lock(DICTIONARY *dict) {
48         if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
49                 // debug(D_DICTIONARY, "Dictionary WRITE lock");
50                 pthread_rwlock_wrlock(&dict->rwlock);
51         }
52 }
53
54 static void dictionary_unlock(DICTIONARY *dict) {
55         if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
56                 // debug(D_DICTIONARY, "Dictionary UNLOCK lock");
57                 pthread_rwlock_unlock(&dict->rwlock);
58         }
59 }
60
61 // ----------------------------------------------------------------------------
62
63 static NAME_VALUE *dictionary_name_value_create_nolock(DICTIONARY *dict, const char *name, void *value, size_t value_len, uint32_t hash) {
64         debug(D_DICTIONARY, "Creating name value entry for name '%s'.", name);
65
66         NAME_VALUE *nv = calloc(1, sizeof(NAME_VALUE));
67         if(unlikely(!nv)) fatal("Cannot allocate name_value of size %z", sizeof(NAME_VALUE));
68
69         if(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)
70                 nv->name = (char *)name;
71         else {
72                 nv->name = strdup(name);
73                 if (unlikely(!nv->name))
74                         fatal("Cannot allocate name_value.name of size %z", strlen(name));
75         }
76
77         nv->hash = (hash)?hash:simple_hash(nv->name);
78
79         if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)
80                 nv->value = value;
81         else {
82                 nv->value = malloc(value_len);
83                 if (unlikely(!nv->value))
84                         fatal("Cannot allocate name_value.value of size %z", value_len);
85
86                 memcpy(nv->value, value, value_len);
87         }
88
89         // index it
90         dictionary_name_value_index_add_nolock(dict, nv);
91         dict->entries++;
92
93         return nv;
94 }
95
96 static void dictionary_name_value_destroy_nolock(DICTIONARY *dict, NAME_VALUE *nv) {
97         debug(D_DICTIONARY, "Destroying name value entry for name '%s'.", nv->name);
98
99         dictionary_name_value_index_del_nolock(dict, nv);
100
101         dict->entries--;
102
103         if(!(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)) {
104                 debug(D_REGISTRY, "Dictionary freeing value of '%s'", nv->name);
105                 free(nv->value);
106         }
107
108         if(!(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)) {
109                 debug(D_REGISTRY, "Dictionary freeing name '%s'", nv->name);
110                 free(nv->name);
111         }
112
113         free(nv);
114 }
115
116 // ----------------------------------------------------------------------------
117
118 DICTIONARY *dictionary_create(uint32_t flags) {
119         debug(D_DICTIONARY, "Creating dictionary.");
120
121         DICTIONARY *dict = calloc(1, sizeof(DICTIONARY));
122         if(unlikely(!dict)) fatal("Cannot allocate DICTIONARY");
123
124         avl_init(&dict->values_index, name_value_compare);
125         pthread_rwlock_init(&dict->rwlock, NULL);
126
127         dict->flags = flags;
128
129         return dict;
130 }
131
132 void dictionary_destroy(DICTIONARY *dict) {
133         debug(D_DICTIONARY, "Destroying dictionary.");
134
135         dictionary_write_lock(dict);
136
137         while(dict->values_index.root)
138                 dictionary_name_value_destroy_nolock(dict, (NAME_VALUE *)dict->values_index.root);
139
140         dictionary_unlock(dict);
141
142         free(dict);
143 }
144
145 // ----------------------------------------------------------------------------
146
147 void *dictionary_set(DICTIONARY *dict, const char *name, void *value, size_t value_len) {
148         debug(D_DICTIONARY, "SET dictionary entry with name '%s'.", name);
149
150         uint32_t hash = simple_hash(name);
151
152         dictionary_read_lock(dict);
153         NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, hash);
154         dictionary_unlock(dict);
155
156         if(unlikely(!nv)) {
157                 debug(D_DICTIONARY, "Dictionary entry with name '%s' not found. Creating a new one.", name);
158
159                 dictionary_write_lock(dict);
160                 nv = dictionary_name_value_create_nolock(dict, name, value, value_len, hash);
161                 dictionary_unlock(dict);
162
163                 if(unlikely(!nv))
164                         fatal("Cannot create name_value.");
165         }
166         else {
167                 debug(D_DICTIONARY, "Dictionary entry with name '%s' found. Changing its value.", name);
168
169                 if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE) {
170                         debug(D_REGISTRY, "Dictionary: linking value to '%s'", name);
171                         nv->value = value;
172                 }
173                 else {
174                         debug(D_REGISTRY, "Dictionary: cloning value to '%s'", name);
175
176                         void *value = malloc(value_len),
177                                         *old = nv->value;
178
179                         if(unlikely(!nv->value))
180                                 fatal("Cannot allocate value of size %z", value_len);
181
182                         memcpy(value, value, value_len);
183                         nv->value = value;
184
185                         debug(D_REGISTRY, "Dictionary: freeing old value of '%s'", name);
186                         free(old);
187                 }
188         }
189
190         return nv->value;
191 }
192
193 void *dictionary_get(DICTIONARY *dict, const char *name) {
194         debug(D_DICTIONARY, "GET dictionary entry with name '%s'.", name);
195
196         dictionary_read_lock(dict);
197         NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0);
198         dictionary_unlock(dict);
199
200         if(unlikely(!nv)) {
201                 debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
202                 return NULL;
203         }
204
205         debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
206         return nv->value;
207 }
208
209 int dictionary_del(DICTIONARY *dict, const char *name) {
210         debug(D_DICTIONARY, "DEL dictionary entry with name '%s'.", name);
211
212         dictionary_read_lock(dict);
213         NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0);
214         dictionary_unlock(dict);
215
216         if(unlikely(!nv)) {
217                 debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
218                 return -1;
219         }
220
221         debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
222         dictionary_write_lock(dict);
223         dictionary_name_value_destroy_nolock(dict, nv);
224         dictionary_unlock(dict);
225
226         return 0;
227 }
228
229
230 static int dictionary_walker(avl *a, int (*callback)(void *entry, void *data), void *data) {
231         int total = 0, ret = 0;
232
233         if(a->right) {
234                 ret = dictionary_walker(a->right, callback, data);
235                 if(ret < 0) return ret;
236                 total += ret;
237         }
238
239         ret = callback(((NAME_VALUE *)a)->value, data);
240         if(ret < 0) return ret;
241         total += ret;
242
243         if(a->left) {
244                 dictionary_walker(a->left, callback, data);
245                 if (ret < 0) return ret;
246                 total += ret;
247         }
248
249         return total;
250 }
251
252 int dictionary_get_all(DICTIONARY *dict, int (*callback)(void *entry, void *data), void *data) {
253         int ret = 0;
254
255         dictionary_read_lock(dict);
256
257         if(likely(dict->values_index.root))
258                 ret = dictionary_walker(dict->values_index.root, callback, data);
259
260         dictionary_unlock(dict);
261
262         return ret;
263 }