32208adf76b89e0dc871303f988760e5e58c7ca4
[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_strvec.h>
35 #include <ecoli_test.h>
36 #include <ecoli_log.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 -1;
69
70         strvec->vec = new_vec;
71
72         elt = ec_malloc(sizeof(*elt));
73         if (elt == NULL)
74                 return -1;
75
76         elt->str = ec_strdup(s);
77         if (elt->str == NULL) {
78                 ec_free(elt);
79                 return -1;
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_ndup(const struct ec_strvec *strvec, size_t off,
89         size_t len)
90 {
91         struct ec_strvec *copy = NULL;
92         size_t i;
93
94         if (off + len > ec_strvec_len(strvec)) {
95                 errno = EINVAL;
96                 return NULL;
97         }
98
99         copy = ec_strvec();
100         if (copy == NULL)
101                 goto fail;
102
103         if (len == 0)
104                 return copy;
105
106         copy->vec = ec_calloc(len, sizeof(*copy->vec));
107         if (copy->vec == NULL)
108                 goto fail;
109
110         for (i = 0; i < len; i++) {
111                 copy->vec[i] = strvec->vec[i + off];
112                 copy->vec[i]->refcnt++;
113                 copy->len++;
114         }
115
116         return copy;
117
118 fail:
119         ec_strvec_free(copy);
120         return NULL;
121 }
122
123 struct ec_strvec *ec_strvec_dup(const struct ec_strvec *strvec)
124 {
125         return ec_strvec_ndup(strvec, 0, ec_strvec_len(strvec));
126 }
127
128 void ec_strvec_free(struct ec_strvec *strvec)
129 {
130         struct ec_strvec_elt *elt;
131         size_t i;
132
133         if (strvec == NULL)
134                 return;
135
136         for (i = 0; i < ec_strvec_len(strvec); i++) {
137                 elt = strvec->vec[i];
138                 elt->refcnt--;
139                 if (elt->refcnt == 0) {
140                         ec_free(elt->str);
141                         ec_free(elt);
142                 }
143         }
144
145         ec_free(strvec->vec);
146         ec_free(strvec);
147 }
148
149 size_t ec_strvec_len(const struct ec_strvec *strvec)
150 {
151         if (strvec == NULL)
152                 return 0;
153
154         return strvec->len;
155 }
156
157 const char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx)
158 {
159         if (strvec == NULL || idx >= strvec->len)
160                 return NULL;
161
162         return strvec->vec[idx]->str;
163 }
164
165 void ec_strvec_dump(FILE *out, const struct ec_strvec *strvec)
166 {
167         size_t i;
168
169         if (strvec == NULL) {
170                 fprintf(out, "none\n");
171                 return;
172         }
173
174         fprintf(out, "strvec (len=%zu) [", strvec->len);
175         for (i = 0; i < ec_strvec_len(strvec); i++) {
176                 if (i == 0)
177                         fprintf(out, "%s", strvec->vec[i]->str);
178                 else
179                         fprintf(out, ", %s", strvec->vec[i]->str);
180         }
181         fprintf(out, "]\n");
182
183 }
184
185 /* LCOV_EXCL_START */
186 static int ec_strvec_testcase(void)
187 {
188         struct ec_strvec *strvec = NULL;
189         struct ec_strvec *strvec2 = NULL;
190
191         strvec = ec_strvec();
192         if (strvec == NULL) {
193                 EC_TEST_ERR("cannot create strvec\n");
194                 goto fail;
195         }
196         if (ec_strvec_len(strvec) != 0) {
197                 EC_TEST_ERR("bad strvec len (0)\n");
198                 goto fail;
199         }
200         if (ec_strvec_add(strvec, "0") < 0) {
201                 EC_TEST_ERR("cannot add (0) in strvec\n");
202                 goto fail;
203         }
204         if (ec_strvec_len(strvec) != 1) {
205                 EC_TEST_ERR("bad strvec len (1)\n");
206                 goto fail;
207         }
208         if (ec_strvec_add(strvec, "1") < 0) {
209                 EC_TEST_ERR("cannot add (1) in strvec\n");
210                 goto fail;
211         }
212         if (ec_strvec_len(strvec) != 2) {
213                 EC_TEST_ERR("bad strvec len (2)\n");
214                 goto fail;
215         }
216         if (strcmp(ec_strvec_val(strvec, 0), "0")) {
217                 EC_TEST_ERR("invalid element in strvec (0)\n");
218                 goto fail;
219         }
220         if (strcmp(ec_strvec_val(strvec, 1), "1")) {
221                 EC_TEST_ERR("invalid element in strvec (1)\n");
222                 goto fail;
223         }
224         if (ec_strvec_val(strvec, 2) != NULL) {
225                 EC_TEST_ERR("strvec val should be NULL\n");
226                 goto fail;
227         }
228
229         strvec2 = ec_strvec_dup(strvec);
230         if (strvec2 == NULL) {
231                 EC_TEST_ERR("cannot create strvec2\n");
232                 goto fail;
233         }
234         if (ec_strvec_len(strvec2) != 2) {
235                 EC_TEST_ERR("bad strvec2 len (2)\n");
236                 goto fail;
237         }
238         if (strcmp(ec_strvec_val(strvec2, 0), "0")) {
239                 EC_TEST_ERR("invalid element in strvec2 (0)\n");
240                 goto fail;
241         }
242         if (strcmp(ec_strvec_val(strvec2, 1), "1")) {
243                 EC_TEST_ERR("invalid element in strvec2 (1)\n");
244                 goto fail;
245         }
246         if (ec_strvec_val(strvec2, 2) != NULL) {
247                 EC_TEST_ERR("strvec2 val should be NULL\n");
248                 goto fail;
249         }
250         ec_strvec_free(strvec2);
251
252         strvec2 = ec_strvec_ndup(strvec, 0, 0);
253         if (strvec2 == NULL) {
254                 EC_TEST_ERR("cannot create strvec2\n");
255                 goto fail;
256         }
257         if (ec_strvec_len(strvec2) != 0) {
258                 EC_TEST_ERR("bad strvec2 len (0)\n");
259                 goto fail;
260         }
261         if (ec_strvec_val(strvec2, 0) != NULL) {
262                 EC_TEST_ERR("strvec2 val should be NULL\n");
263                 goto fail;
264         }
265         ec_strvec_free(strvec2);
266
267         strvec2 = ec_strvec_ndup(strvec, 1, 1);
268         if (strvec2 == NULL) {
269                 EC_TEST_ERR("cannot create strvec2\n");
270                 goto fail;
271         }
272         if (ec_strvec_len(strvec2) != 1) {
273                 EC_TEST_ERR("bad strvec2 len (1)\n");
274                 goto fail;
275         }
276         if (strcmp(ec_strvec_val(strvec2, 0), "1")) {
277                 EC_TEST_ERR("invalid element in strvec2 (1)\n");
278                 goto fail;
279         }
280         if (ec_strvec_val(strvec2, 1) != NULL) {
281                 EC_TEST_ERR("strvec2 val should be NULL\n");
282                 goto fail;
283         }
284         ec_strvec_free(strvec2);
285
286         strvec2 = ec_strvec_ndup(strvec, 3, 1);
287         if (strvec2 != NULL) {
288                 EC_TEST_ERR("strvec2 should be NULL\n");
289                 goto fail;
290         }
291         ec_strvec_free(strvec2);
292
293         ec_strvec_dump(stdout, strvec);
294         ec_strvec_dump(stdout, NULL);
295
296         ec_strvec_free(strvec);
297
298         return 0;
299
300 fail:
301         ec_strvec_free(strvec);
302         ec_strvec_free(strvec2);
303         return -1;
304 }
305 /* LCOV_EXCL_STOP */
306
307 static struct ec_test ec_node_str_test = {
308         .name = "strvec",
309         .test = ec_strvec_testcase,
310 };
311
312 EC_TEST_REGISTER(ec_node_str_test);