b35c56926c545c0337d87f1729c916812908de14
[protos/libecoli.git] / lib / ecoli_strvec.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 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
219         strvec = ec_strvec();
220         if (strvec == NULL) {
221                 EC_TEST_ERR("cannot create strvec\n");
222                 goto fail;
223         }
224         if (ec_strvec_len(strvec) != 0) {
225                 EC_TEST_ERR("bad strvec len (0)\n");
226                 goto fail;
227         }
228         if (ec_strvec_add(strvec, "0") < 0) {
229                 EC_TEST_ERR("cannot add (0) in strvec\n");
230                 goto fail;
231         }
232         if (ec_strvec_len(strvec) != 1) {
233                 EC_TEST_ERR("bad strvec len (1)\n");
234                 goto fail;
235         }
236         if (ec_strvec_add(strvec, "1") < 0) {
237                 EC_TEST_ERR("cannot add (1) in strvec\n");
238                 goto fail;
239         }
240         if (ec_strvec_len(strvec) != 2) {
241                 EC_TEST_ERR("bad strvec len (2)\n");
242                 goto fail;
243         }
244         if (strcmp(ec_strvec_val(strvec, 0), "0")) {
245                 EC_TEST_ERR("invalid element in strvec (0)\n");
246                 goto fail;
247         }
248         if (strcmp(ec_strvec_val(strvec, 1), "1")) {
249                 EC_TEST_ERR("invalid element in strvec (1)\n");
250                 goto fail;
251         }
252         if (ec_strvec_val(strvec, 2) != NULL) {
253                 EC_TEST_ERR("strvec val should be NULL\n");
254                 goto fail;
255         }
256
257         strvec2 = ec_strvec_dup(strvec);
258         if (strvec2 == NULL) {
259                 EC_TEST_ERR("cannot create strvec2\n");
260                 goto fail;
261         }
262         if (ec_strvec_len(strvec2) != 2) {
263                 EC_TEST_ERR("bad strvec2 len (2)\n");
264                 goto fail;
265         }
266         if (strcmp(ec_strvec_val(strvec2, 0), "0")) {
267                 EC_TEST_ERR("invalid element in strvec2 (0)\n");
268                 goto fail;
269         }
270         if (strcmp(ec_strvec_val(strvec2, 1), "1")) {
271                 EC_TEST_ERR("invalid element in strvec2 (1)\n");
272                 goto fail;
273         }
274         if (ec_strvec_val(strvec2, 2) != NULL) {
275                 EC_TEST_ERR("strvec2 val should be NULL\n");
276                 goto fail;
277         }
278         ec_strvec_free(strvec2);
279
280         strvec2 = ec_strvec_ndup(strvec, 0, 0);
281         if (strvec2 == NULL) {
282                 EC_TEST_ERR("cannot create strvec2\n");
283                 goto fail;
284         }
285         if (ec_strvec_len(strvec2) != 0) {
286                 EC_TEST_ERR("bad strvec2 len (0)\n");
287                 goto fail;
288         }
289         if (ec_strvec_val(strvec2, 0) != NULL) {
290                 EC_TEST_ERR("strvec2 val should be NULL\n");
291                 goto fail;
292         }
293         ec_strvec_free(strvec2);
294
295         strvec2 = ec_strvec_ndup(strvec, 1, 1);
296         if (strvec2 == NULL) {
297                 EC_TEST_ERR("cannot create strvec2\n");
298                 goto fail;
299         }
300         if (ec_strvec_len(strvec2) != 1) {
301                 EC_TEST_ERR("bad strvec2 len (1)\n");
302                 goto fail;
303         }
304         if (strcmp(ec_strvec_val(strvec2, 0), "1")) {
305                 EC_TEST_ERR("invalid element in strvec2 (1)\n");
306                 goto fail;
307         }
308         if (ec_strvec_val(strvec2, 1) != NULL) {
309                 EC_TEST_ERR("strvec2 val should be NULL\n");
310                 goto fail;
311         }
312         ec_strvec_free(strvec2);
313
314         strvec2 = ec_strvec_ndup(strvec, 3, 1);
315         if (strvec2 != NULL) {
316                 EC_TEST_ERR("strvec2 should be NULL\n");
317                 goto fail;
318         }
319         ec_strvec_free(strvec2);
320
321         ec_strvec_dump(stdout, strvec);
322         ec_strvec_dump(stdout, NULL);
323
324         ec_strvec_free(strvec);
325
326         return 0;
327
328 fail:
329         ec_strvec_free(strvec);
330         ec_strvec_free(strvec2);
331         return -1;
332 }
333 /* LCOV_EXCL_STOP */
334
335 static struct ec_test ec_node_str_test = {
336         .name = "strvec",
337         .test = ec_strvec_testcase,
338 };
339
340 EC_TEST_REGISTER(ec_node_str_test);