continue !
[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 {
39         size_t len;
40         struct ec_keyval_elt *vec;
41 };
42
43 struct ec_keyval_elt {
44         char *key;
45         void *val;
46         ec_keyval_elt_free_t free;
47 };
48
49 struct ec_keyval *ec_keyval_new(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 *new_vec;
122         struct ec_keyval_elt *elt;
123
124         assert(keyval != NULL);
125         assert(key != NULL);
126
127         ec_keyval_del(keyval, key);
128
129         new_vec = ec_realloc(keyval->vec,
130                 sizeof(*keyval->vec) * (keyval->len + 1));
131         if (new_vec == NULL)
132                 return -ENOMEM;
133
134         keyval->vec = new_vec;
135
136         elt = &new_vec[keyval->len];
137         elt->key = ec_strdup(key);
138         if (elt->key == NULL)
139                 return -ENOMEM;
140
141         elt->val = val;
142         elt->free = free_cb;
143         keyval->len++;
144
145         return 0;
146 }
147
148 void ec_keyval_free(struct ec_keyval *keyval)
149 {
150         struct ec_keyval_elt *elt;
151         size_t i;
152
153         if (keyval == NULL)
154                 return;
155
156         for (i = 0; i < ec_keyval_len(keyval); i++) {
157                 elt = &keyval->vec[i];
158                 ec_keyval_elt_free(elt);
159         }
160         ec_free(keyval->vec);
161         ec_free(keyval);
162 }
163
164 size_t ec_keyval_len(const struct ec_keyval *keyval)
165 {
166         return keyval->len;
167 }
168
169 void ec_keyval_dump(const struct ec_keyval *keyval, FILE *out)
170 {
171         size_t i;
172
173         if (keyval == NULL) {
174                 fprintf(out, "empty keyval\n");
175                 return;
176         }
177
178         fprintf(out, "keyval:\n");
179         for (i = 0; i < ec_keyval_len(keyval); i++) {
180                 fprintf(out, "  %s: %p\n",
181                         keyval->vec[i].key,
182                         keyval->vec[i].val);
183         }
184 }
185
186 static int ec_keyval_testcase(void)
187 {
188         struct ec_keyval *keyval;
189         char *val;
190
191         keyval = ec_keyval_new();
192         if (keyval == NULL) {
193                 ec_log(EC_LOG_ERR, "cannot create keyval\n");
194                 return -1;
195         }
196
197         EC_TEST_ASSERT(ec_keyval_len(keyval) == 0);
198         ec_keyval_set(keyval, "key1", "val1", NULL);
199         ec_keyval_set(keyval, "key2", ec_strdup("val2"), ec_free2);
200         EC_TEST_ASSERT(ec_keyval_len(keyval) == 2);
201
202         val = ec_keyval_get(keyval, "key1");
203         EC_TEST_ASSERT(val != NULL && !strcmp(val, "val1"));
204         val = ec_keyval_get(keyval, "key2");
205         EC_TEST_ASSERT(val != NULL && !strcmp(val, "val2"));
206         val = ec_keyval_get(keyval, "key3");
207         EC_TEST_ASSERT(val == NULL);
208
209         ec_keyval_set(keyval, "key1", "another_val1", NULL);
210         ec_keyval_set(keyval, "key2", ec_strdup("another_val2"), ec_free2);
211         EC_TEST_ASSERT(ec_keyval_len(keyval) == 2);
212
213         val = ec_keyval_get(keyval, "key1");
214         EC_TEST_ASSERT(val != NULL && !strcmp(val, "another_val1"));
215         val = ec_keyval_get(keyval, "key2");
216         EC_TEST_ASSERT(val != NULL && !strcmp(val, "another_val2"));
217
218         ec_keyval_del(keyval, "key1");
219         EC_TEST_ASSERT(ec_keyval_len(keyval) == 1);
220
221         ec_keyval_free(keyval);
222
223         return 0;
224 }
225
226 static struct ec_test ec_keyval_test = {
227         .name = "keyval",
228         .test = ec_keyval_testcase,
229 };
230
231 EC_REGISTER_TEST(ec_keyval_test);