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 struct ec_keyval_elt {
39         char *key;
40         void *val;
41         ec_keyval_elt_free_t free;
42 };
43
44 struct ec_keyval {
45         size_t len;
46         struct ec_keyval_elt *vec;
47 };
48
49 struct ec_keyval *ec_keyval(void)
50 {
51         struct ec_keyval *keyval;
52
53         keyval = ec_calloc(1, sizeof(*keyval));
54         if (keyval == NULL)
55                 return NULL;
56
57         return keyval;
58 }
59
60 static struct ec_keyval_elt *ec_keyval_lookup(const struct ec_keyval *keyval,
61         const char *key)
62 {
63         struct ec_keyval_elt *elt;
64         size_t i;
65
66         if (keyval == NULL || keyval->vec == NULL)
67                 return NULL;
68
69         for (i = 0; i < ec_keyval_len(keyval); i++) {
70                 elt = &keyval->vec[i];
71                 if (strcmp(elt->key, key) == 0)
72                         return elt;
73         }
74
75         return NULL;
76 }
77
78 static void ec_keyval_elt_free(struct ec_keyval_elt *elt)
79 {
80         if (elt == NULL)
81                 return;
82
83         ec_free(elt->key);
84         if (elt->free != NULL)
85                 elt->free(elt->val);
86 }
87
88 void *ec_keyval_get(const struct ec_keyval *keyval, const char *key)
89 {
90         struct ec_keyval_elt *elt;
91
92         elt = ec_keyval_lookup(keyval, key);
93         if (elt == NULL)
94                 return NULL;
95
96         return elt->val;
97 }
98
99 int ec_keyval_del(struct ec_keyval *keyval, const char *key)
100 {
101         struct ec_keyval_elt *elt;
102         struct ec_keyval_elt *last = &keyval->vec[keyval->len - 1];
103
104         elt = ec_keyval_lookup(keyval, key);
105         if (elt == NULL)
106                 return -ENOENT;
107
108         ec_keyval_elt_free(elt);
109
110         if (elt != last)
111                 memcpy(elt, last, sizeof(*elt));
112
113         keyval->len--;
114
115         return 0;
116 }
117
118 int ec_keyval_set(struct ec_keyval *keyval, const char *key, void *val,
119         ec_keyval_elt_free_t free_cb)
120 {
121         struct ec_keyval_elt *elt, *new_vec;
122
123         assert(keyval != NULL);
124         assert(key != NULL);
125
126         ec_keyval_del(keyval, key);
127
128         new_vec = ec_realloc(keyval->vec,
129                 sizeof(*keyval->vec) * (keyval->len + 1));
130         if (new_vec == NULL)
131                 goto fail;
132
133         keyval->vec = new_vec;
134
135         elt = &new_vec[keyval->len];
136         elt->key = ec_strdup(key);
137         if (elt->key == NULL)
138                 goto fail;
139
140         elt->val = val;
141         elt->free = free_cb;
142         keyval->len++;
143
144         return 0;
145
146 fail:
147         if (free_cb)
148                 free_cb(val);
149         return -ENOMEM;
150 }
151
152 void ec_keyval_free(struct ec_keyval *keyval)
153 {
154         struct ec_keyval_elt *elt;
155         size_t i;
156
157         if (keyval == NULL)
158                 return;
159
160         for (i = 0; i < ec_keyval_len(keyval); i++) {
161                 elt = &keyval->vec[i];
162                 ec_keyval_elt_free(elt);
163         }
164         ec_free(keyval->vec);
165         ec_free(keyval);
166 }
167
168 size_t ec_keyval_len(const struct ec_keyval *keyval)
169 {
170         return keyval->len;
171 }
172
173 void ec_keyval_dump(const struct ec_keyval *keyval, FILE *out)
174 {
175         size_t i;
176
177         if (keyval == NULL) {
178                 fprintf(out, "empty keyval\n");
179                 return;
180         }
181
182         fprintf(out, "keyval:\n");
183         for (i = 0; i < ec_keyval_len(keyval); i++) {
184                 fprintf(out, "  %s: %p\n",
185                         keyval->vec[i].key,
186                         keyval->vec[i].val);
187         }
188 }
189
190 static int ec_keyval_testcase(void)
191 {
192         struct ec_keyval *keyval;
193         char *val;
194
195         keyval = ec_keyval();
196         if (keyval == NULL) {
197                 ec_log(EC_LOG_ERR, "cannot create keyval\n");
198                 return -1;
199         }
200
201         EC_TEST_ASSERT(ec_keyval_len(keyval) == 0);
202         ec_keyval_set(keyval, "key1", "val1", NULL);
203         ec_keyval_set(keyval, "key2", ec_strdup("val2"), ec_free2);
204         EC_TEST_ASSERT(ec_keyval_len(keyval) == 2);
205
206         val = ec_keyval_get(keyval, "key1");
207         EC_TEST_ASSERT(val != NULL && !strcmp(val, "val1"));
208         val = ec_keyval_get(keyval, "key2");
209         EC_TEST_ASSERT(val != NULL && !strcmp(val, "val2"));
210         val = ec_keyval_get(keyval, "key3");
211         EC_TEST_ASSERT(val == NULL);
212
213         ec_keyval_set(keyval, "key1", "another_val1", NULL);
214         ec_keyval_set(keyval, "key2", ec_strdup("another_val2"), ec_free2);
215         EC_TEST_ASSERT(ec_keyval_len(keyval) == 2);
216
217         val = ec_keyval_get(keyval, "key1");
218         EC_TEST_ASSERT(val != NULL && !strcmp(val, "another_val1"));
219         val = ec_keyval_get(keyval, "key2");
220         EC_TEST_ASSERT(val != NULL && !strcmp(val, "another_val2"));
221
222         ec_keyval_del(keyval, "key1");
223         EC_TEST_ASSERT(ec_keyval_len(keyval) == 1);
224
225         ec_keyval_free(keyval);
226
227         return 0;
228 }
229
230 static struct ec_test ec_keyval_test = {
231         .name = "keyval",
232         .test = ec_keyval_testcase,
233 };
234
235 EC_TEST_REGISTER(ec_keyval_test);