save
[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 bool ec_keyval_has_key(const struct ec_keyval *keyval, const char *key)
91 {
92         return !!ec_keyval_lookup(keyval, key);
93 }
94
95 void *ec_keyval_get(const struct ec_keyval *keyval, const char *key)
96 {
97         struct ec_keyval_elt *elt;
98
99         elt = ec_keyval_lookup(keyval, key);
100         if (elt == NULL)
101                 return NULL;
102
103         return elt->val;
104 }
105
106 int ec_keyval_del(struct ec_keyval *keyval, const char *key)
107 {
108         struct ec_keyval_elt *elt;
109         struct ec_keyval_elt *last = &keyval->vec[keyval->len - 1];
110
111         elt = ec_keyval_lookup(keyval, key);
112         if (elt == NULL)
113                 return -ENOENT;
114
115         ec_keyval_elt_free(elt);
116
117         if (elt != last)
118                 memcpy(elt, last, sizeof(*elt));
119
120         keyval->len--;
121
122         return 0;
123 }
124
125 int ec_keyval_set(struct ec_keyval *keyval, const char *key, void *val,
126         ec_keyval_elt_free_t free_cb)
127 {
128         struct ec_keyval_elt *elt, *new_vec;
129
130         assert(keyval != NULL);
131         assert(key != NULL);
132
133         ec_keyval_del(keyval, key);
134
135         new_vec = ec_realloc(keyval->vec,
136                 sizeof(*keyval->vec) * (keyval->len + 1));
137         if (new_vec == NULL)
138                 goto fail;
139
140         keyval->vec = new_vec;
141
142         elt = &new_vec[keyval->len];
143         elt->key = ec_strdup(key);
144         if (elt->key == NULL)
145                 goto fail;
146
147         elt->val = val;
148         elt->free = free_cb;
149         keyval->len++;
150
151         return 0;
152
153 fail:
154         if (free_cb)
155                 free_cb(val);
156         return -ENOMEM;
157 }
158
159 void ec_keyval_free(struct ec_keyval *keyval)
160 {
161         struct ec_keyval_elt *elt;
162         size_t i;
163
164         if (keyval == NULL)
165                 return;
166
167         for (i = 0; i < ec_keyval_len(keyval); i++) {
168                 elt = &keyval->vec[i];
169                 ec_keyval_elt_free(elt);
170         }
171         ec_free(keyval->vec);
172         ec_free(keyval);
173 }
174
175 size_t ec_keyval_len(const struct ec_keyval *keyval)
176 {
177         return keyval->len;
178 }
179
180 void ec_keyval_dump(const struct ec_keyval *keyval, FILE *out)
181 {
182         size_t i;
183
184         if (keyval == NULL) {
185                 fprintf(out, "empty keyval\n");
186                 return;
187         }
188
189         fprintf(out, "keyval:\n");
190         for (i = 0; i < ec_keyval_len(keyval); i++) {
191                 fprintf(out, "  %s: %p\n",
192                         keyval->vec[i].key,
193                         keyval->vec[i].val);
194         }
195 }
196
197 /* LCOV_EXCL_START */
198 static int ec_keyval_testcase(void)
199 {
200         struct ec_keyval *keyval;
201         char *val;
202
203         keyval = ec_keyval();
204         if (keyval == NULL) {
205                 EC_LOG(EC_LOG_ERR, "cannot create keyval\n");
206                 return -1;
207         }
208
209         EC_TEST_ASSERT(ec_keyval_len(keyval) == 0);
210         ec_keyval_set(keyval, "key1", "val1", NULL);
211         ec_keyval_set(keyval, "key2", ec_strdup("val2"), ec_free2);
212         EC_TEST_ASSERT(ec_keyval_len(keyval) == 2);
213
214         val = ec_keyval_get(keyval, "key1");
215         EC_TEST_ASSERT(val != NULL && !strcmp(val, "val1"));
216         val = ec_keyval_get(keyval, "key2");
217         EC_TEST_ASSERT(val != NULL && !strcmp(val, "val2"));
218         val = ec_keyval_get(keyval, "key3");
219         EC_TEST_ASSERT(val == NULL);
220
221         ec_keyval_set(keyval, "key1", "another_val1", NULL);
222         ec_keyval_set(keyval, "key2", ec_strdup("another_val2"), ec_free2);
223         EC_TEST_ASSERT(ec_keyval_len(keyval) == 2);
224
225         val = ec_keyval_get(keyval, "key1");
226         EC_TEST_ASSERT(val != NULL && !strcmp(val, "another_val1"));
227         val = ec_keyval_get(keyval, "key2");
228         EC_TEST_ASSERT(val != NULL && !strcmp(val, "another_val2"));
229
230         ec_keyval_del(keyval, "key1");
231         EC_TEST_ASSERT(ec_keyval_len(keyval) == 1);
232
233         ec_keyval_free(keyval);
234
235         return 0;
236 }
237 /* LCOV_EXCL_STOP */
238
239 static struct ec_test ec_keyval_test = {
240         .name = "keyval",
241         .test = ec_keyval_testcase,
242 };
243
244 EC_TEST_REGISTER(ec_keyval_test);