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