vec
[protos/libecoli.git] / lib / ecoli_vec.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 <stdint.h>
32 #include <errno.h>
33 #include <assert.h>
34
35 #include <ecoli_malloc.h>
36 #include <ecoli_log.h>
37 #include <ecoli_test.h>
38 #include <ecoli_vec.h>
39
40 struct ec_vec {
41         size_t len;
42         size_t size;
43         size_t elt_size;
44         ec_vec_elt_copy_t copy;
45         ec_vec_elt_free_t free;
46         void *vec;
47 };
48
49 static void *get_obj(const struct ec_vec *vec, size_t idx)
50 {
51         assert(vec->elt_size != 0);
52         return (char *)vec->vec + (idx * vec->elt_size);
53 }
54
55 struct ec_vec *
56 ec_vec(size_t elt_size, size_t size, ec_vec_elt_copy_t copy,
57         ec_vec_elt_free_t free)
58 {
59         struct ec_vec *vec;
60
61         if (elt_size == 0) {
62                 errno = -EINVAL;
63                 return NULL;
64         }
65
66         vec = ec_calloc(1, sizeof(*vec));
67         if (vec == NULL)
68                 return NULL;
69
70         vec->elt_size = elt_size;
71         vec->copy = copy;
72         vec->free = free;
73
74         if (size == 0)
75                 return vec;
76
77         vec->vec = ec_calloc(size, vec->elt_size);
78         if (vec->vec == NULL) {
79                 ec_free(vec);
80                 return NULL;
81         }
82
83         return vec;
84 }
85
86 int ec_vec_add_by_ref(struct ec_vec *vec, void *ptr)
87 {
88         void *new_vec;
89
90         if (vec->len + 1 > vec->size) {
91                 new_vec = ec_realloc(vec->vec, vec->elt_size * (vec->len + 1));
92                 if (new_vec == NULL)
93                         return -ENOMEM;
94         }
95
96         vec->vec = new_vec;
97         if (vec->copy)
98                 vec->copy(get_obj(vec, vec->len), ptr);
99         else
100                 memcpy(get_obj(vec, vec->len), ptr, vec->elt_size);
101         vec->len++;
102
103         return 0;
104 }
105
106 int ec_vec_add_ptr(struct ec_vec *vec, void *elt)
107 {
108         if (vec->elt_size != sizeof(elt))
109                 return -EINVAL;
110
111         return ec_vec_add_by_ref(vec, &elt);
112 }
113
114 int ec_vec_add_u8(struct ec_vec *vec, uint8_t elt)
115 {
116         if (vec->elt_size != sizeof(elt))
117                 return -EINVAL;
118
119         return ec_vec_add_by_ref(vec, &elt);
120 }
121
122 int ec_vec_add_u16(struct ec_vec *vec, uint16_t elt)
123 {
124         if (vec->elt_size != sizeof(elt))
125                 return -EINVAL;
126
127         return ec_vec_add_by_ref(vec, &elt);
128 }
129
130 int ec_vec_add_u32(struct ec_vec *vec, uint32_t elt)
131 {
132         if (vec->elt_size != sizeof(elt))
133                 return -EINVAL;
134
135         return ec_vec_add_by_ref(vec, &elt);
136 }
137
138 int ec_vec_add_u64(struct ec_vec *vec, uint64_t elt)
139 {
140         if (vec->elt_size != sizeof(elt))
141                 return -EINVAL;
142
143         return ec_vec_add_by_ref(vec, &elt);
144 }
145
146 struct ec_vec *ec_vec_ndup(const struct ec_vec *vec, size_t off,
147         size_t len)
148 {
149         struct ec_vec *copy = NULL;
150         size_t i, veclen;
151
152         veclen = ec_vec_len(vec);
153         if (off + len > veclen)
154                 return NULL;
155
156         copy = ec_vec(vec->elt_size, len, vec->copy, vec->free);
157         if (copy == NULL)
158                 goto fail;
159
160         if (len == 0)
161                 return copy;
162
163         for (i = 0; i < len; i++) {
164                 if (vec->copy)
165                         vec->copy(get_obj(copy, i), get_obj(vec, i + off));
166                 else
167                         memcpy(get_obj(copy, i), get_obj(vec, i + off),
168                                 vec->elt_size);
169         }
170         copy->len = len;
171
172         return copy;
173
174 fail:
175         ec_vec_free(copy);
176         return NULL;
177 }
178
179 size_t ec_vec_len(const struct ec_vec *vec)
180 {
181         return vec->len;
182 }
183
184 struct ec_vec *ec_vec_dup(const struct ec_vec *vec)
185 {
186         return ec_vec_ndup(vec, 0, ec_vec_len(vec));
187 }
188
189 void ec_vec_free(struct ec_vec *vec)
190 {
191         size_t i;
192
193         if (vec == NULL)
194                 return;
195
196         for (i = 0; i < ec_vec_len(vec); i++) {
197                 if (vec->free)
198                         vec->free(get_obj(vec, i));
199         }
200
201         ec_free(vec->vec);
202         ec_free(vec);
203 }
204
205 int ec_vec_get(void *ptr, const struct ec_vec *vec, size_t idx)
206 {
207         if (vec == NULL || idx >= vec->len)
208                 return -EINVAL;
209
210         if (vec->copy)
211                 vec->copy(ptr, get_obj(vec, idx));
212         else
213                 memcpy(ptr, get_obj(vec, idx), vec->elt_size);
214
215         return 0;
216 }
217
218 static void str_free(void *elt)
219 {
220         char **s = elt;
221
222         ec_free(*s);
223 }
224
225 #define GOTO_FAIL do {                                       \
226                 ec_log(EC_LOG_ERR, "%s:%d: test failed\n",   \
227                         __FILE__, __LINE__);                 \
228                 goto fail;                                   \
229         } while(0)
230
231 /* LCOV_EXCL_START */
232 static int ec_vec_testcase(void)
233 {
234         struct ec_vec *vec = NULL;
235         struct ec_vec *vec2 = NULL;
236         uint8_t val8;
237         uint16_t val16;
238         uint32_t val32;
239         uint64_t val64;
240         void *valp;
241         char *vals;
242
243         /* uint8_t vector */
244         vec = ec_vec(sizeof(val8), 0, NULL, NULL);
245         if (vec == NULL)
246                 GOTO_FAIL;
247
248         if (ec_vec_add_u8(vec, 0) < 0)
249                 GOTO_FAIL;
250         if (ec_vec_add_u8(vec, 1) < 0)
251                 GOTO_FAIL;
252         if (ec_vec_add_u8(vec, 2) < 0)
253                 GOTO_FAIL;
254         /* should fail */
255         if (ec_vec_add_u16(vec, 3) == 0)
256                 GOTO_FAIL;
257         if (ec_vec_add_u32(vec, 3) == 0)
258                 GOTO_FAIL;
259         if (ec_vec_add_u64(vec, 3) == 0)
260                 GOTO_FAIL;
261         if (ec_vec_add_ptr(vec, (void *)3) == 0)
262                 GOTO_FAIL;
263
264         if (ec_vec_get(&val8, vec, 0) < 0)
265                 GOTO_FAIL;
266         if (val8 != 0)
267                 GOTO_FAIL;
268         if (ec_vec_get(&val8, vec, 1) < 0)
269                 GOTO_FAIL;
270         if (val8 != 1)
271                 GOTO_FAIL;
272         if (ec_vec_get(&val8, vec, 2) < 0)
273                 GOTO_FAIL;
274         if (val8 != 2)
275                 GOTO_FAIL;
276
277         /* duplicate the vector */
278         vec2 = ec_vec_dup(vec);
279         if (vec2 == NULL)
280                 GOTO_FAIL;
281         if (ec_vec_get(&val8, vec2, 0) < 0)
282                 GOTO_FAIL;
283         if (val8 != 0)
284                 GOTO_FAIL;
285         if (ec_vec_get(&val8, vec2, 1) < 0)
286                 GOTO_FAIL;
287         if (val8 != 1)
288                 GOTO_FAIL;
289         if (ec_vec_get(&val8, vec2, 2) < 0)
290                 GOTO_FAIL;
291         if (val8 != 2)
292                 GOTO_FAIL;
293
294         ec_vec_free(vec2);
295         vec2 = NULL;
296
297         /* dup at offset 1 */
298         vec2 = ec_vec_ndup(vec, 1, 2);
299         if (vec2 == NULL)
300                 GOTO_FAIL;
301         if (ec_vec_get(&val8, vec2, 0) < 0)
302                 GOTO_FAIL;
303         if (val8 != 1)
304                 GOTO_FAIL;
305         if (ec_vec_get(&val8, vec2, 1) < 0)
306                 GOTO_FAIL;
307         if (val8 != 2)
308                 GOTO_FAIL;
309
310         ec_vec_free(vec2);
311         vec2 = NULL;
312
313         /* len = 0, duplicate is empty */
314         vec2 = ec_vec_ndup(vec, 2, 0);
315         if (vec2 == NULL)
316                 GOTO_FAIL;
317         if (ec_vec_get(&val8, vec2, 0) == 0)
318                 GOTO_FAIL;
319
320         ec_vec_free(vec2);
321         vec2 = NULL;
322
323         /* bad dup args */
324         vec2 = ec_vec_ndup(vec, 10, 1);
325         if (vec2 != NULL)
326                 GOTO_FAIL;
327
328         ec_vec_free(vec);
329         vec = NULL;
330
331         /* uint16_t vector */
332         vec = ec_vec(sizeof(val16), 0, NULL, NULL);
333         if (vec == NULL)
334                 GOTO_FAIL;
335
336         if (ec_vec_add_u16(vec, 0) < 0)
337                 GOTO_FAIL;
338         if (ec_vec_add_u16(vec, 1) < 0)
339                 GOTO_FAIL;
340         if (ec_vec_add_u16(vec, 2) < 0)
341                 GOTO_FAIL;
342         /* should fail */
343         if (ec_vec_add_u8(vec, 3) == 0)
344                 GOTO_FAIL;
345
346         if (ec_vec_get(&val16, vec, 0) < 0)
347                 GOTO_FAIL;
348         if (val16 != 0)
349                 GOTO_FAIL;
350         if (ec_vec_get(&val16, vec, 1) < 0)
351                 GOTO_FAIL;
352         if (val16 != 1)
353                 GOTO_FAIL;
354         if (ec_vec_get(&val16, vec, 2) < 0)
355                 GOTO_FAIL;
356         if (val16 != 2)
357                 GOTO_FAIL;
358
359         ec_vec_free(vec);
360         vec = NULL;
361
362         /* uint32_t vector */
363         vec = ec_vec(sizeof(val32), 0, NULL, NULL);
364         if (vec == NULL)
365                 GOTO_FAIL;
366
367         if (ec_vec_add_u32(vec, 0) < 0)
368                 GOTO_FAIL;
369         if (ec_vec_add_u32(vec, 1) < 0)
370                 GOTO_FAIL;
371         if (ec_vec_add_u32(vec, 2) < 0)
372                 GOTO_FAIL;
373
374         if (ec_vec_get(&val32, vec, 0) < 0)
375                 GOTO_FAIL;
376         if (val32 != 0)
377                 GOTO_FAIL;
378         if (ec_vec_get(&val32, vec, 1) < 0)
379                 GOTO_FAIL;
380         if (val32 != 1)
381                 GOTO_FAIL;
382         if (ec_vec_get(&val32, vec, 2) < 0)
383                 GOTO_FAIL;
384         if (val32 != 2)
385                 GOTO_FAIL;
386
387         ec_vec_free(vec);
388         vec = NULL;
389
390         /* uint64_t vector */
391         vec = ec_vec(sizeof(val64), 0, NULL, NULL);
392         if (vec == NULL)
393                 GOTO_FAIL;
394
395         if (ec_vec_add_u64(vec, 0) < 0)
396                 GOTO_FAIL;
397         if (ec_vec_add_u64(vec, 1) < 0)
398                 GOTO_FAIL;
399         if (ec_vec_add_u64(vec, 2) < 0)
400                 GOTO_FAIL;
401
402         if (ec_vec_get(&val64, vec, 0) < 0)
403                 GOTO_FAIL;
404         if (val64 != 0)
405                 GOTO_FAIL;
406         if (ec_vec_get(&val64, vec, 1) < 0)
407                 GOTO_FAIL;
408         if (val64 != 1)
409                 GOTO_FAIL;
410         if (ec_vec_get(&val64, vec, 2) < 0)
411                 GOTO_FAIL;
412         if (val64 != 2)
413                 GOTO_FAIL;
414
415         ec_vec_free(vec);
416         vec = NULL;
417
418         /* ptr vector */
419         vec = ec_vec(sizeof(valp), 0, NULL, NULL);
420         if (vec == NULL)
421                 GOTO_FAIL;
422
423         if (ec_vec_add_ptr(vec, (void *)0) < 0)
424                 GOTO_FAIL;
425         if (ec_vec_add_ptr(vec, (void *)1) < 0)
426                 GOTO_FAIL;
427         if (ec_vec_add_ptr(vec, (void *)2) < 0)
428                 GOTO_FAIL;
429
430         if (ec_vec_get(&valp, vec, 0) < 0)
431                 GOTO_FAIL;
432         if (valp != (void *)0)
433                 GOTO_FAIL;
434         if (ec_vec_get(&valp, vec, 1) < 0)
435                 GOTO_FAIL;
436         if (valp != (void *)1)
437                 GOTO_FAIL;
438         if (ec_vec_get(&valp, vec, 2) < 0)
439                 GOTO_FAIL;
440         if (valp != (void *)2)
441                 GOTO_FAIL;
442
443         ec_vec_free(vec);
444         vec = NULL;
445
446         /* string vector */
447         vec = ec_vec(sizeof(valp), 0, NULL, str_free);
448         if (vec == NULL)
449                 GOTO_FAIL;
450
451         if (ec_vec_add_ptr(vec, ec_strdup("0")) < 0)
452                 GOTO_FAIL;
453         if (ec_vec_add_ptr(vec, ec_strdup("1")) < 0)
454                 GOTO_FAIL;
455         if (ec_vec_add_ptr(vec, ec_strdup("2")) < 0)
456                 GOTO_FAIL;
457
458         if (ec_vec_get(&vals, vec, 0) < 0)
459                 GOTO_FAIL;
460         if (strcmp(vals, "0"))
461                 GOTO_FAIL;
462         if (ec_vec_get(&vals, vec, 1) < 0)
463                 GOTO_FAIL;
464         if (strcmp(vals, "1"))
465                 GOTO_FAIL;
466         if (ec_vec_get(&vals, vec, 2) < 0)
467                 GOTO_FAIL;
468         if (strcmp(vals, "2"))
469                 GOTO_FAIL;
470
471         ec_vec_free(vec);
472         vec = NULL;
473
474         /* invalid args */
475         vec = ec_vec(0, 0, NULL, NULL);
476         if (vec != NULL)
477                 GOTO_FAIL;
478
479         return 0;
480
481 fail:
482         ec_vec_free(vec);
483         ec_vec_free(vec2);
484         return -1;
485 }
486 /* LCOV_EXCL_STOP */
487
488 static struct ec_test ec_vec_test = {
489         .name = "vec",
490         .test = ec_vec_testcase,
491 };
492
493 EC_TEST_REGISTER(ec_vec_test);