1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright 2016, Olivier MATZ <zer0@droids-corp.org>
5 #define _GNU_SOURCE /* qsort_r */
11 #include <ecoli_malloc.h>
12 #include <ecoli_test.h>
13 #include <ecoli_log.h>
14 #include <ecoli_dict.h>
15 #include <ecoli_strvec.h>
17 EC_LOG_TYPE_REGISTER(strvec);
19 struct ec_strvec_elt {
22 struct ec_dict *attrs;
27 struct ec_strvec_elt **vec;
30 struct ec_strvec *ec_strvec(void)
32 struct ec_strvec *strvec;
34 strvec = ec_calloc(1, sizeof(*strvec));
41 static struct ec_strvec_elt *
42 __ec_strvec_elt(const char *s)
44 struct ec_strvec_elt *elt;
46 elt = ec_calloc(1, sizeof(*elt));
50 elt->str = ec_strdup(s);
51 if (elt->str == NULL) {
61 __ec_strvec_elt_free(struct ec_strvec_elt *elt)
64 if (elt->refcnt == 0) {
66 ec_dict_free(elt->attrs);
71 int ec_strvec_set(struct ec_strvec *strvec, size_t idx, const char *s)
73 struct ec_strvec_elt *elt;
75 if (strvec == NULL || s == NULL || idx >= strvec->len) {
80 elt = __ec_strvec_elt(s);
84 __ec_strvec_elt_free(strvec->vec[idx]);
85 strvec->vec[idx] = elt;
90 int ec_strvec_add(struct ec_strvec *strvec, const char *s)
92 struct ec_strvec_elt *elt, **new_vec;
94 if (strvec == NULL || s == NULL) {
99 new_vec = ec_realloc(strvec->vec,
100 sizeof(*strvec->vec) * (strvec->len + 1));
104 strvec->vec = new_vec;
106 elt = __ec_strvec_elt(s);
110 new_vec[strvec->len] = elt;
116 struct ec_strvec *ec_strvec_from_array(const char * const *strarr,
119 struct ec_strvec *strvec = NULL;
122 strvec = ec_strvec();
126 for (i = 0; i < n; i++) {
127 if (ec_strvec_add(strvec, strarr[i]) < 0)
134 ec_strvec_free(strvec);
138 int ec_strvec_del_last(struct ec_strvec *strvec)
140 if (strvec->len == 0) {
145 __ec_strvec_elt_free(strvec->vec[strvec->len - 1]);
151 struct ec_strvec *ec_strvec_ndup(const struct ec_strvec *strvec, size_t off,
154 struct ec_strvec *copy = NULL;
157 veclen = ec_strvec_len(strvec);
158 if (off + len > veclen)
168 copy->vec = ec_calloc(len, sizeof(*copy->vec));
169 if (copy->vec == NULL)
172 for (i = 0; i < len; i++) {
173 copy->vec[i] = strvec->vec[i + off];
174 copy->vec[i]->refcnt++;
181 ec_strvec_free(copy);
185 struct ec_strvec *ec_strvec_dup(const struct ec_strvec *strvec)
187 return ec_strvec_ndup(strvec, 0, ec_strvec_len(strvec));
190 void ec_strvec_free(struct ec_strvec *strvec)
192 struct ec_strvec_elt *elt;
198 for (i = 0; i < ec_strvec_len(strvec); i++) {
199 elt = strvec->vec[i];
200 __ec_strvec_elt_free(elt);
203 ec_free(strvec->vec);
207 size_t ec_strvec_len(const struct ec_strvec *strvec)
212 const char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx)
214 if (strvec == NULL || idx >= strvec->len)
217 return strvec->vec[idx]->str;
220 const struct ec_dict *ec_strvec_get_attrs(const struct ec_strvec *strvec,
223 if (strvec == NULL || idx >= strvec->len) {
228 return strvec->vec[idx]->attrs;
231 int ec_strvec_set_attrs(struct ec_strvec *strvec, size_t idx,
232 struct ec_dict *attrs)
234 struct ec_strvec_elt *elt;
236 if (strvec == NULL || idx >= strvec->len) {
241 elt = strvec->vec[idx];
242 if (elt->refcnt > 1) {
243 if (ec_strvec_set(strvec, idx, elt->str) < 0)
245 elt = strvec->vec[idx];
248 if (elt->attrs != NULL)
249 ec_dict_free(elt->attrs);
260 int ec_strvec_cmp(const struct ec_strvec *strvec1,
261 const struct ec_strvec *strvec2)
265 if (ec_strvec_len(strvec1) != ec_strvec_len(strvec2))
268 for (i = 0; i < ec_strvec_len(strvec1); i++) {
269 if (strcmp(ec_strvec_val(strvec1, i),
270 ec_strvec_val(strvec2, i)))
278 cmp_vec_elt(const void *p1, const void *p2, void *arg)
280 int (*str_cmp)(const char *s1, const char *s2) = arg;
281 const struct ec_strvec_elt * const *e1 = p1, * const *e2 = p2;
283 return str_cmp((*e1)->str, (*e2)->str);
286 void ec_strvec_sort(struct ec_strvec *strvec,
287 int (*str_cmp)(const char *s1, const char *s2))
291 qsort_r(strvec->vec, ec_strvec_len(strvec),
292 sizeof(*strvec->vec), cmp_vec_elt, str_cmp);
295 void ec_strvec_dump(FILE *out, const struct ec_strvec *strvec)
299 if (strvec == NULL) {
300 fprintf(out, "none\n");
304 fprintf(out, "strvec (len=%zu) [", strvec->len);
305 for (i = 0; i < ec_strvec_len(strvec); i++) {
307 fprintf(out, "%s", strvec->vec[i]->str);
309 fprintf(out, ", %s", strvec->vec[i]->str);
315 /* LCOV_EXCL_START */
316 static int ec_strvec_testcase(void)
318 struct ec_strvec *strvec = NULL;
319 struct ec_strvec *strvec2 = NULL;
320 const struct ec_dict *const_attrs = NULL;
321 struct ec_dict *attrs = NULL;
327 strvec = ec_strvec();
328 if (strvec == NULL) {
329 EC_TEST_ERR("cannot create strvec\n");
332 if (ec_strvec_len(strvec) != 0) {
333 EC_TEST_ERR("bad strvec len (0)\n");
336 if (ec_strvec_add(strvec, "0") < 0) {
337 EC_TEST_ERR("cannot add (0) in strvec\n");
340 if (ec_strvec_len(strvec) != 1) {
341 EC_TEST_ERR("bad strvec len (1)\n");
344 if (ec_strvec_add(strvec, "1") < 0) {
345 EC_TEST_ERR("cannot add (1) in strvec\n");
348 if (ec_strvec_len(strvec) != 2) {
349 EC_TEST_ERR("bad strvec len (2)\n");
352 if (strcmp(ec_strvec_val(strvec, 0), "0")) {
353 EC_TEST_ERR("invalid element in strvec (0)\n");
356 if (strcmp(ec_strvec_val(strvec, 1), "1")) {
357 EC_TEST_ERR("invalid element in strvec (1)\n");
360 if (ec_strvec_val(strvec, 2) != NULL) {
361 EC_TEST_ERR("strvec val should be NULL\n");
365 strvec2 = ec_strvec_dup(strvec);
366 if (strvec2 == NULL) {
367 EC_TEST_ERR("cannot create strvec2\n");
370 if (ec_strvec_len(strvec2) != 2) {
371 EC_TEST_ERR("bad strvec2 len (2)\n");
374 if (strcmp(ec_strvec_val(strvec2, 0), "0")) {
375 EC_TEST_ERR("invalid element in strvec2 (0)\n");
378 if (strcmp(ec_strvec_val(strvec2, 1), "1")) {
379 EC_TEST_ERR("invalid element in strvec2 (1)\n");
382 if (ec_strvec_val(strvec2, 2) != NULL) {
383 EC_TEST_ERR("strvec2 val should be NULL\n");
386 ec_strvec_free(strvec2);
388 strvec2 = ec_strvec_ndup(strvec, 0, 0);
389 if (strvec2 == NULL) {
390 EC_TEST_ERR("cannot create strvec2\n");
393 if (ec_strvec_len(strvec2) != 0) {
394 EC_TEST_ERR("bad strvec2 len (0)\n");
397 if (ec_strvec_val(strvec2, 0) != NULL) {
398 EC_TEST_ERR("strvec2 val should be NULL\n");
401 ec_strvec_free(strvec2);
403 strvec2 = ec_strvec_ndup(strvec, 1, 1);
404 if (strvec2 == NULL) {
405 EC_TEST_ERR("cannot create strvec2\n");
408 if (ec_strvec_len(strvec2) != 1) {
409 EC_TEST_ERR("bad strvec2 len (1)\n");
412 if (strcmp(ec_strvec_val(strvec2, 0), "1")) {
413 EC_TEST_ERR("invalid element in strvec2 (1)\n");
416 if (ec_strvec_val(strvec2, 1) != NULL) {
417 EC_TEST_ERR("strvec2 val should be NULL\n");
420 ec_strvec_free(strvec2);
422 strvec2 = ec_strvec_ndup(strvec, 3, 1);
423 if (strvec2 != NULL) {
424 EC_TEST_ERR("strvec2 should be NULL\n");
427 ec_strvec_free(strvec2);
429 strvec2 = EC_STRVEC("0", "1");
430 if (strvec2 == NULL) {
431 EC_TEST_ERR("cannot create strvec from array\n");
434 testres |= EC_TEST_CHECK(ec_strvec_cmp(strvec, strvec2) == 0,
435 "strvec and strvec2 should be equal\n");
436 ec_strvec_free(strvec2);
438 f = open_memstream(&buf, &buflen);
441 ec_strvec_dump(f, strvec);
444 testres |= EC_TEST_CHECK(
445 strstr(buf, "strvec (len=2) [0, 1]"), "bad dump\n");
449 ec_strvec_del_last(strvec);
450 strvec2 = EC_STRVEC("0");
451 if (strvec2 == NULL) {
452 EC_TEST_ERR("cannot create strvec from array\n");
455 testres |= EC_TEST_CHECK(ec_strvec_cmp(strvec, strvec2) == 0,
456 "strvec and strvec2 should be equal\n");
457 ec_strvec_free(strvec2);
460 f = open_memstream(&buf, &buflen);
463 ec_strvec_dump(f, NULL);
466 testres |= EC_TEST_CHECK(
467 strstr(buf, "none"), "bad dump\n");
471 ec_strvec_free(strvec);
473 strvec = EC_STRVEC("e", "a", "f", "d", "b", "c");
474 if (strvec == NULL) {
475 EC_TEST_ERR("cannot create strvec from array\n");
480 EC_TEST_ERR("cannot create attrs\n");
483 if (ec_dict_set(attrs, "key", "value", NULL) < 0) {
484 EC_TEST_ERR("cannot set attr\n");
487 if (ec_strvec_set_attrs(strvec, 1, attrs) < 0) {
489 EC_TEST_ERR("cannot set attrs in strvec\n");
494 ec_strvec_sort(strvec, NULL);
496 /* attrs are now at index 0 after sorting */
497 const_attrs = ec_strvec_get_attrs(strvec, 0);
498 if (const_attrs == NULL) {
499 EC_TEST_ERR("cannot get attrs\n");
502 testres |= EC_TEST_CHECK(
503 ec_dict_has_key(const_attrs, "key"), "cannot get attrs key\n");
505 strvec2 = EC_STRVEC("a", "b", "c", "d", "e", "f");
506 if (strvec2 == NULL) {
507 EC_TEST_ERR("cannot create strvec from array\n");
510 testres |= EC_TEST_CHECK(ec_strvec_cmp(strvec, strvec2) == 0,
511 "strvec and strvec2 should be equal\n");
512 ec_strvec_free(strvec);
514 ec_strvec_free(strvec2);
523 ec_strvec_free(strvec);
524 ec_strvec_free(strvec2);
531 static struct ec_test ec_node_str_test = {
533 .test = ec_strvec_testcase,
536 EC_TEST_REGISTER(ec_node_str_test);