rollback strvec to previous version
[protos/libecoli.git] / lib / ecoli_strvec.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 <stdio.h>
30 #include <stdlib.h>
31 #include <errno.h>
32
33 #include <ecoli_malloc.h>
34 #include <ecoli_test.h>
35 #include <ecoli_log.h>
36 #include <ecoli_strvec.h>
37
38 EC_LOG_TYPE_REGISTER(strvec);
39
40 struct ec_strvec_elt {
41         unsigned int refcnt;
42         char *str;
43 };
44
45 struct ec_strvec {
46         size_t len;
47         struct ec_strvec_elt **vec;
48 };
49
50 struct ec_strvec *ec_strvec(void)
51 {
52         struct ec_strvec *strvec;
53
54         strvec = ec_calloc(1, sizeof(*strvec));
55         if (strvec == NULL)
56                 return NULL;
57
58         return strvec;
59 }
60
61 int ec_strvec_add(struct ec_strvec *strvec, const char *s)
62 {
63         struct ec_strvec_elt *elt, **new_vec;
64
65         new_vec = ec_realloc(strvec->vec,
66                 sizeof(*strvec->vec) * (strvec->len + 1));
67         if (new_vec == NULL)
68                 return -ENOMEM;
69
70         strvec->vec = new_vec;
71
72         elt = ec_malloc(sizeof(*elt));
73         if (elt == NULL)
74                 return -ENOMEM;
75
76         elt->str = ec_strdup(s);
77         if (elt->str == NULL) {
78                 ec_free(elt);
79                 return -ENOMEM;
80         }
81         elt->refcnt = 1;
82
83         new_vec[strvec->len] = elt;
84         strvec->len++;
85         return 0;
86 }
87
88 struct ec_strvec *ec_strvec_from_array(const char * const *strarr,
89         size_t n)
90 {
91         struct ec_strvec *strvec = NULL;
92         size_t i;
93
94         strvec = ec_strvec();
95         for (i = 0; i < n; i++) {
96                 if (ec_strvec_add(strvec, strarr[i]) < 0)
97                         goto fail;
98         }
99
100         return strvec;
101
102 fail:
103         ec_strvec_free(strvec);
104         return NULL;
105 }
106
107 int ec_strvec_del_last(struct ec_strvec *strvec)
108 {
109         struct ec_strvec_elt *elt;
110
111         if (strvec->len == 0) {
112                 errno = EINVAL;
113                 return -1;
114         }
115
116         elt = strvec->vec[strvec->len - 1];
117         elt->refcnt--;
118         if (elt->refcnt == 0) {
119                 ec_free(elt->str);
120                 ec_free(elt);
121         }
122         strvec->len--;
123         return 0;
124 }
125
126 struct ec_strvec *ec_strvec_ndup(const struct ec_strvec *strvec, size_t off,
127         size_t len)
128 {
129         struct ec_strvec *copy = NULL;
130         size_t i, veclen;
131
132         veclen = ec_strvec_len(strvec);
133         if (off + len > veclen)
134                 return NULL;
135
136         copy = ec_strvec();
137         if (copy == NULL)
138                 goto fail;
139
140         if (len == 0)
141                 return copy;
142
143         copy->vec = ec_calloc(len, sizeof(*copy->vec));
144         if (copy->vec == NULL)
145                 goto fail;
146
147         for (i = 0; i < len; i++) {
148                 copy->vec[i] = strvec->vec[i + off];
149                 copy->vec[i]->refcnt++;
150         }
151         copy->len = len;
152
153         return copy;
154
155 fail:
156         ec_strvec_free(copy);
157         return NULL;
158 }
159
160 struct ec_strvec *ec_strvec_dup(const struct ec_strvec *strvec)
161 {
162         return ec_strvec_ndup(strvec, 0, ec_strvec_len(strvec));
163 }
164
165 void ec_strvec_free(struct ec_strvec *strvec)
166 {
167         struct ec_strvec_elt *elt;
168         size_t i;
169
170         if (strvec == NULL)
171                 return;
172
173         for (i = 0; i < ec_strvec_len(strvec); i++) {
174                 elt = strvec->vec[i];
175                 elt->refcnt--;
176                 if (elt->refcnt == 0) {
177                         ec_free(elt->str);
178                         ec_free(elt);
179                 }
180         }
181
182         ec_free(strvec->vec);
183         ec_free(strvec);
184 }
185
186 size_t ec_strvec_len(const struct ec_strvec *strvec)
187 {
188         return strvec->len;
189 }
190
191 const char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx)
192 {
193         if (strvec == NULL || idx >= strvec->len)
194                 return NULL;
195
196         return strvec->vec[idx]->str;
197 }
198
199 int ec_strvec_cmp(const struct ec_strvec *strvec1,
200                 const struct ec_strvec *strvec2)
201 {
202         size_t i;
203
204         if (ec_strvec_len(strvec1) != ec_strvec_len(strvec2))
205                 return -1;
206
207         for (i = 0; i < ec_strvec_len(strvec1); i++) {
208                 if (strcmp(ec_strvec_val(strvec1, i),
209                                 ec_strvec_val(strvec2, i)))
210                         return -1;
211         }
212
213         return 0;
214 }
215
216 void ec_strvec_dump(FILE *out, const struct ec_strvec *strvec)
217 {
218         size_t i;
219
220         if (strvec == NULL) {
221                 fprintf(out, "none\n");
222                 return;
223         }
224
225         fprintf(out, "strvec (len=%zu) [", strvec->len);
226         for (i = 0; i < ec_strvec_len(strvec); i++) {
227                 if (i == 0)
228                         fprintf(out, "%s", strvec->vec[i]->str);
229                 else
230                         fprintf(out, ", %s", strvec->vec[i]->str);
231         }
232         fprintf(out, "]\n");
233
234 }
235
236 /* LCOV_EXCL_START */
237 static int ec_strvec_testcase(void)
238 {
239         struct ec_strvec *strvec = NULL;
240         struct ec_strvec *strvec2 = NULL;
241
242         strvec = ec_strvec();
243         if (strvec == NULL) {
244                 EC_TEST_ERR("cannot create strvec\n");
245                 goto fail;
246         }
247         if (ec_strvec_len(strvec) != 0) {
248                 EC_TEST_ERR("bad strvec len (0)\n");
249                 goto fail;
250         }
251         if (ec_strvec_add(strvec, "0") < 0) {
252                 EC_TEST_ERR("cannot add (0) in strvec\n");
253                 goto fail;
254         }
255         if (ec_strvec_len(strvec) != 1) {
256                 EC_TEST_ERR("bad strvec len (1)\n");
257                 goto fail;
258         }
259         if (ec_strvec_add(strvec, "1") < 0) {
260                 EC_TEST_ERR("cannot add (1) in strvec\n");
261                 goto fail;
262         }
263         if (ec_strvec_len(strvec) != 2) {
264                 EC_TEST_ERR("bad strvec len (2)\n");
265                 goto fail;
266         }
267         if (strcmp(ec_strvec_val(strvec, 0), "0")) {
268                 EC_TEST_ERR("invalid element in strvec (0)\n");
269                 goto fail;
270         }
271         if (strcmp(ec_strvec_val(strvec, 1), "1")) {
272                 EC_TEST_ERR("invalid element in strvec (1)\n");
273                 goto fail;
274         }
275         if (ec_strvec_val(strvec, 2) != NULL) {
276                 EC_TEST_ERR("strvec val should be NULL\n");
277                 goto fail;
278         }
279
280         strvec2 = ec_strvec_dup(strvec);
281         if (strvec2 == NULL) {
282                 EC_TEST_ERR("cannot create strvec2\n");
283                 goto fail;
284         }
285         if (ec_strvec_len(strvec2) != 2) {
286                 EC_TEST_ERR("bad strvec2 len (2)\n");
287                 goto fail;
288         }
289         if (strcmp(ec_strvec_val(strvec2, 0), "0")) {
290                 EC_TEST_ERR("invalid element in strvec2 (0)\n");
291                 goto fail;
292         }
293         if (strcmp(ec_strvec_val(strvec2, 1), "1")) {
294                 EC_TEST_ERR("invalid element in strvec2 (1)\n");
295                 goto fail;
296         }
297         if (ec_strvec_val(strvec2, 2) != NULL) {
298                 EC_TEST_ERR("strvec2 val should be NULL\n");
299                 goto fail;
300         }
301         ec_strvec_free(strvec2);
302
303         strvec2 = ec_strvec_ndup(strvec, 0, 0);
304         if (strvec2 == NULL) {
305                 EC_TEST_ERR("cannot create strvec2\n");
306                 goto fail;
307         }
308         if (ec_strvec_len(strvec2) != 0) {
309                 EC_TEST_ERR("bad strvec2 len (0)\n");
310                 goto fail;
311         }
312         if (ec_strvec_val(strvec2, 0) != NULL) {
313                 EC_TEST_ERR("strvec2 val should be NULL\n");
314                 goto fail;
315         }
316         ec_strvec_free(strvec2);
317
318         strvec2 = ec_strvec_ndup(strvec, 1, 1);
319         if (strvec2 == NULL) {
320                 EC_TEST_ERR("cannot create strvec2\n");
321                 goto fail;
322         }
323         if (ec_strvec_len(strvec2) != 1) {
324                 EC_TEST_ERR("bad strvec2 len (1)\n");
325                 goto fail;
326         }
327         if (strcmp(ec_strvec_val(strvec2, 0), "1")) {
328                 EC_TEST_ERR("invalid element in strvec2 (1)\n");
329                 goto fail;
330         }
331         if (ec_strvec_val(strvec2, 1) != NULL) {
332                 EC_TEST_ERR("strvec2 val should be NULL\n");
333                 goto fail;
334         }
335         ec_strvec_free(strvec2);
336
337         strvec2 = ec_strvec_ndup(strvec, 3, 1);
338         if (strvec2 != NULL) {
339                 EC_TEST_ERR("strvec2 should be NULL\n");
340                 goto fail;
341         }
342         ec_strvec_free(strvec2);
343
344         ec_strvec_dump(stdout, strvec);
345         ec_strvec_dump(stdout, NULL);
346
347         ec_strvec_free(strvec);
348
349         return 0;
350
351 fail:
352         ec_strvec_free(strvec);
353         ec_strvec_free(strvec2);
354         return -1;
355 }
356 /* LCOV_EXCL_STOP */
357
358 static struct ec_test ec_node_str_test = {
359         .name = "strvec",
360         .test = ec_strvec_testcase,
361 };
362
363 EC_TEST_REGISTER(ec_node_str_test);