dynamic log types
[protos/libecoli.git] / lib / ecoli_keyval.c
1 /*
2  * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of the University of California, Berkeley nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include <sys/types.h>
29 #include <stdlib.h>
30 #include <assert.h>
31 #include <errno.h>
32
33 #include <ecoli_malloc.h>
34 #include <ecoli_log.h>
35 #include <ecoli_test.h>
36 #include <ecoli_keyval.h>
37
38 EC_LOG_TYPE_REGISTER(keyval);
39
40 struct ec_keyval_elt {
41         char *key;
42         void *val;
43         ec_keyval_elt_free_t free;
44 };
45
46 struct ec_keyval {
47         size_t len;
48         struct ec_keyval_elt *vec;
49 };
50
51 struct ec_keyval *ec_keyval(void)
52 {
53         struct ec_keyval *keyval;
54
55         keyval = ec_calloc(1, sizeof(*keyval));
56         if (keyval == NULL)
57                 return NULL;
58
59         return keyval;
60 }
61
62 static struct ec_keyval_elt *ec_keyval_lookup(const struct ec_keyval *keyval,
63         const char *key)
64 {
65         struct ec_keyval_elt *elt;
66         size_t i;
67
68         if (keyval == NULL || keyval->vec == NULL)
69                 return NULL;
70
71         for (i = 0; i < ec_keyval_len(keyval); i++) {
72                 elt = &keyval->vec[i];
73                 if (strcmp(elt->key, key) == 0)
74                         return elt;
75         }
76
77         return NULL;
78 }
79
80 static void ec_keyval_elt_free(struct ec_keyval_elt *elt)
81 {
82         if (elt == NULL)
83                 return;
84
85         ec_free(elt->key);
86         if (elt->free != NULL)
87                 elt->free(elt->val);
88 }
89
90 void *ec_keyval_get(const struct ec_keyval *keyval, const char *key)
91 {
92         struct ec_keyval_elt *elt;
93
94         elt = ec_keyval_lookup(keyval, key);
95         if (elt == NULL)
96                 return NULL;
97
98         return elt->val;
99 }
100
101 int ec_keyval_del(struct ec_keyval *keyval, const char *key)
102 {
103         struct ec_keyval_elt *elt;
104         struct ec_keyval_elt *last = &keyval->vec[keyval->len - 1];
105
106         elt = ec_keyval_lookup(keyval, key);
107         if (elt == NULL)
108                 return -ENOENT;
109
110         ec_keyval_elt_free(elt);
111
112         if (elt != last)
113                 memcpy(elt, last, sizeof(*elt));
114
115         keyval->len--;
116
117         return 0;
118 }
119
120 int ec_keyval_set(struct ec_keyval *keyval, const char *key, void *val,
121         ec_keyval_elt_free_t free_cb)
122 {
123         struct ec_keyval_elt *elt, *new_vec;
124
125         assert(keyval != NULL);
126         assert(key != NULL);
127
128         ec_keyval_del(keyval, key);
129
130         new_vec = ec_realloc(keyval->vec,
131                 sizeof(*keyval->vec) * (keyval->len + 1));
132         if (new_vec == NULL)
133                 goto fail;
134
135         keyval->vec = new_vec;
136
137         elt = &new_vec[keyval->len];
138         elt->key = ec_strdup(key);
139         if (elt->key == NULL)
140                 goto fail;
141
142         elt->val = val;
143         elt->free = free_cb;
144         keyval->len++;
145
146         return 0;
147
148 fail:
149         if (free_cb)
150                 free_cb(val);
151         return -ENOMEM;
152 }
153
154 void ec_keyval_free(struct ec_keyval *keyval)
155 {
156         struct ec_keyval_elt *elt;
157         size_t i;
158
159         if (keyval == NULL)
160                 return;
161
162         for (i = 0; i < ec_keyval_len(keyval); i++) {
163                 elt = &keyval->vec[i];
164                 ec_keyval_elt_free(elt);
165         }
166         ec_free(keyval->vec);
167         ec_free(keyval);
168 }
169
170 size_t ec_keyval_len(const struct ec_keyval *keyval)
171 {
172         return keyval->len;
173 }
174
175 void ec_keyval_dump(const struct ec_keyval *keyval, FILE *out)
176 {
177         size_t i;
178
179         if (keyval == NULL) {
180                 fprintf(out, "empty keyval\n");
181                 return;
182         }
183
184         fprintf(out, "keyval:\n");
185         for (i = 0; i < ec_keyval_len(keyval); i++) {
186                 fprintf(out, "  %s: %p\n",
187                         keyval->vec[i].key,
188                         keyval->vec[i].val);
189         }
190 }
191
192 /* LCOV_EXCL_START */
193 static int ec_keyval_testcase(void)
194 {
195         struct ec_keyval *keyval;
196         char *val;
197
198         keyval = ec_keyval();
199         if (keyval == NULL) {
200                 EC_LOG(EC_LOG_ERR, "cannot create keyval\n");
201                 return -1;
202         }
203
204         EC_TEST_ASSERT(ec_keyval_len(keyval) == 0);
205         ec_keyval_set(keyval, "key1", "val1", NULL);
206         ec_keyval_set(keyval, "key2", ec_strdup("val2"), ec_free2);
207         EC_TEST_ASSERT(ec_keyval_len(keyval) == 2);
208
209         val = ec_keyval_get(keyval, "key1");
210         EC_TEST_ASSERT(val != NULL && !strcmp(val, "val1"));
211         val = ec_keyval_get(keyval, "key2");
212         EC_TEST_ASSERT(val != NULL && !strcmp(val, "val2"));
213         val = ec_keyval_get(keyval, "key3");
214         EC_TEST_ASSERT(val == NULL);
215
216         ec_keyval_set(keyval, "key1", "another_val1", NULL);
217         ec_keyval_set(keyval, "key2", ec_strdup("another_val2"), ec_free2);
218         EC_TEST_ASSERT(ec_keyval_len(keyval) == 2);
219
220         val = ec_keyval_get(keyval, "key1");
221         EC_TEST_ASSERT(val != NULL && !strcmp(val, "another_val1"));
222         val = ec_keyval_get(keyval, "key2");
223         EC_TEST_ASSERT(val != NULL && !strcmp(val, "another_val2"));
224
225         ec_keyval_del(keyval, "key1");
226         EC_TEST_ASSERT(ec_keyval_len(keyval) == 1);
227
228         ec_keyval_free(keyval);
229
230         return 0;
231 }
232 /* LCOV_EXCL_STOP */
233
234 static struct ec_test ec_keyval_test = {
235         .name = "keyval",
236         .test = ec_keyval_testcase,
237 };
238
239 EC_TEST_REGISTER(ec_keyval_test);