api documentation for ec_parse
[protos/libecoli.git] / src / ecoli_dict.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2016, Olivier MATZ <zer0@droids-corp.org>
3  */
4
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <sys/queue.h>
8 #include <stdlib.h>
9 #include <stdint.h>
10 #include <unistd.h>
11 #include <assert.h>
12 #include <errno.h>
13 #include <fcntl.h>
14
15 #include <ecoli_init.h>
16 #include <ecoli_malloc.h>
17 #include <ecoli_log.h>
18 #include <ecoli_test.h>
19 #include <ecoli_htable_private.h>
20 #include <ecoli_dict.h>
21
22 EC_LOG_TYPE_REGISTER(dict);
23
24 static size_t
25 _strlen(const char *s)
26 {
27         if (s == NULL)
28                 return 0;
29         return strlen(s);
30 }
31
32 struct ec_dict_elt {
33         struct ec_htable_elt htable;
34 };
35
36 struct ec_dict_elt_ref {
37         struct ec_htable_elt_ref htable;
38 };
39
40 struct ec_dict {
41         struct ec_htable htable;
42 };
43
44 struct ec_dict *ec_dict(void)
45 {
46         return (struct ec_dict *)ec_htable();
47 }
48
49 bool ec_dict_has_key(const struct ec_dict *dict, const char *key)
50 {
51         return ec_htable_has_key(
52                 &dict->htable, key, _strlen(key) + 1);
53 }
54
55 void *ec_dict_get(const struct ec_dict *dict, const char *key)
56 {
57         return ec_htable_get(&dict->htable, key, _strlen(key) + 1);
58 }
59
60 int ec_dict_del(struct ec_dict *dict, const char *key)
61 {
62         return ec_htable_del(&dict->htable, key, _strlen(key) + 1);
63 }
64
65 int ec_dict_set(struct ec_dict *dict, const char *key, void *val,
66         ec_dict_elt_free_t free_cb)
67 {
68         return ec_htable_set(&dict->htable, key, _strlen(key) + 1,
69                 val, free_cb);
70 }
71
72 void ec_dict_free(struct ec_dict *dict)
73 {
74         ec_htable_free(&dict->htable);
75 }
76
77 size_t ec_dict_len(const struct ec_dict *dict)
78 {
79         return ec_htable_len(&dict->htable);
80 }
81
82 struct ec_dict_elt_ref *
83 ec_dict_iter(const struct ec_dict *dict)
84 {
85         return (struct ec_dict_elt_ref *)ec_htable_iter(&dict->htable);
86 }
87
88 struct ec_dict_elt_ref *
89 ec_dict_iter_next(struct ec_dict_elt_ref *iter)
90 {
91         return (struct ec_dict_elt_ref *)ec_htable_iter_next(&iter->htable);
92 }
93
94 const char *
95 ec_dict_iter_get_key(const struct ec_dict_elt_ref *iter)
96 {
97         return (const char *)ec_htable_iter_get_key(&iter->htable);
98 }
99
100 void *
101 ec_dict_iter_get_val(const struct ec_dict_elt_ref *iter)
102 {
103         return (struct ec_dict_elt_ref *)ec_htable_iter_get_val(&iter->htable);
104 }
105
106 void ec_dict_dump(FILE *out, const struct ec_dict *dict)
107 {
108         struct ec_dict_elt_ref *iter;
109
110         if (dict == NULL) {
111                 fprintf(out, "empty dict\n");
112                 return;
113         }
114
115         fprintf(out, "dict:\n");
116         for (iter = ec_dict_iter(dict);
117              iter != NULL;
118              iter = ec_dict_iter_next(iter)) {
119                 fprintf(out, "  %s: %p\n",
120                         ec_dict_iter_get_key(iter),
121                         ec_dict_iter_get_val(iter));
122         }
123 }
124
125 struct ec_dict *ec_dict_dup(const struct ec_dict *dict)
126 {
127         return (struct ec_dict *)ec_htable_dup(&dict->htable);
128 }
129
130 /* LCOV_EXCL_START */
131 static int ec_dict_testcase(void)
132 {
133         struct ec_dict *dict, *dup;
134         struct ec_dict_elt_ref *iter;
135         char *val;
136         size_t i, count;
137         int ret, testres = 0;
138         FILE *f = NULL;
139         char *buf = NULL;
140         size_t buflen = 0;
141
142         dict = ec_dict();
143         if (dict == NULL) {
144                 EC_LOG(EC_LOG_ERR, "cannot create dict\n");
145                 return -1;
146         }
147
148         count = 0;
149         for (iter = ec_dict_iter(dict);
150              iter != NULL;
151              iter = ec_dict_iter_next(iter)) {
152                 count++;
153         }
154         testres |= EC_TEST_CHECK(count == 0, "invalid count in iterator");
155
156         testres |= EC_TEST_CHECK(ec_dict_len(dict) == 0, "bad dict len");
157         ret = ec_dict_set(dict, "key1", "val1", NULL);
158         testres |= EC_TEST_CHECK(ret == 0, "cannot set key");
159         ret = ec_dict_set(dict, "key2", ec_strdup("val2"), ec_free_func);
160         testres |= EC_TEST_CHECK(ret == 0, "cannot set key");
161         testres |= EC_TEST_CHECK(ec_dict_len(dict) == 2, "bad dict len");
162
163         val = ec_dict_get(dict, "key1");
164         testres |= EC_TEST_CHECK(val != NULL && !strcmp(val, "val1"),
165                                 "invalid dict value");
166         val = ec_dict_get(dict, "key2");
167         testres |= EC_TEST_CHECK(val != NULL && !strcmp(val, "val2"),
168                                 "invalid dict value");
169         val = ec_dict_get(dict, "key3");
170         testres |= EC_TEST_CHECK(val == NULL, "key3 should be NULL");
171
172         ret = ec_dict_set(dict, "key1", "another_val1", NULL);
173         testres |= EC_TEST_CHECK(ret == 0, "cannot set key");
174         ret = ec_dict_set(dict, "key2", ec_strdup("another_val2"),
175                         ec_free_func);
176         testres |= EC_TEST_CHECK(ret == 0, "cannot set key");
177         testres |= EC_TEST_CHECK(ec_dict_len(dict) == 2,
178                                 "bad dict len");
179
180         val = ec_dict_get(dict, "key1");
181         testres |= EC_TEST_CHECK(val != NULL && !strcmp(val, "another_val1"),
182                 "invalid dict value");
183         val = ec_dict_get(dict, "key2");
184         testres |= EC_TEST_CHECK(val != NULL && !strcmp(val, "another_val2"),
185                 "invalid dict value");
186         testres |= EC_TEST_CHECK(ec_dict_has_key(dict, "key1"),
187                 "key1 should be in dict");
188
189         f = open_memstream(&buf, &buflen);
190         if (f == NULL)
191                 goto fail;
192         ec_dict_dump(f, NULL);
193         fclose(f);
194         f = NULL;
195         free(buf);
196         buf = NULL;
197
198         f = open_memstream(&buf, &buflen);
199         if (f == NULL)
200                 goto fail;
201         ec_dict_dump(f, dict);
202         fclose(f);
203         f = NULL;
204         free(buf);
205         buf = NULL;
206
207         ret = ec_dict_del(dict, "key1");
208         testres |= EC_TEST_CHECK(ret == 0, "cannot del key1");
209         testres |= EC_TEST_CHECK(ec_dict_len(dict) == 1,
210                 "invalid dict len");
211         ret = ec_dict_del(dict, "key2");
212         testres |= EC_TEST_CHECK(ret == 0, "cannot del key2");
213         testres |= EC_TEST_CHECK(ec_dict_len(dict) == 0,
214                 "invalid dict len");
215
216         for (i = 0; i < 100; i++) {
217                 char key[8];
218                 snprintf(key, sizeof(key), "k%zd", i);
219                 ret = ec_dict_set(dict, key, "val", NULL);
220                 testres |= EC_TEST_CHECK(ret == 0, "cannot set key");
221         }
222         dup = ec_dict_dup(dict);
223         testres |= EC_TEST_CHECK(dup != NULL, "cannot duplicate dict");
224         if (dup != NULL) {
225                 for (i = 0; i < 100; i++) {
226                         char key[8];
227                         snprintf(key, sizeof(key), "k%zd", i);
228                         val = ec_dict_get(dup, key);
229                         testres |= EC_TEST_CHECK(
230                                 val != NULL && !strcmp(val, "val"),
231                                 "invalid dict value");
232                 }
233                 ec_dict_free(dup);
234                 dup = NULL;
235         }
236
237         count = 0;
238         for (iter = ec_dict_iter(dict);
239              iter != NULL;
240              iter = ec_dict_iter_next(iter)) {
241                 count++;
242         }
243         testres |= EC_TEST_CHECK(count == 100, "invalid count in iterator");
244
245         /* einval */
246         ret = ec_dict_set(dict, NULL, "val1", NULL);
247         testres |= EC_TEST_CHECK(ret == -1, "should not be able to set key");
248         val = ec_dict_get(dict, NULL);
249         testres |= EC_TEST_CHECK(val == NULL, "get(NULL) should no success");
250
251         ec_dict_free(dict);
252
253         return testres;
254
255 fail:
256         ec_dict_free(dict);
257         if (f)
258                 fclose(f);
259         free(buf);
260         return -1;
261 }
262 /* LCOV_EXCL_STOP */
263
264 static struct ec_test ec_dict_test = {
265         .name = "dict",
266         .test = ec_dict_testcase,
267 };
268
269 EC_TEST_REGISTER(ec_dict_test);