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