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_strvec.h>
16 EC_LOG_TYPE_REGISTER(strvec);
18 struct ec_strvec_elt {
25 struct ec_strvec_elt **vec;
28 struct ec_strvec *ec_strvec(void)
30 struct ec_strvec *strvec;
32 strvec = ec_calloc(1, sizeof(*strvec));
39 int ec_strvec_add(struct ec_strvec *strvec, const char *s)
41 struct ec_strvec_elt *elt, **new_vec;
43 new_vec = ec_realloc(strvec->vec,
44 sizeof(*strvec->vec) * (strvec->len + 1));
48 strvec->vec = new_vec;
50 elt = ec_malloc(sizeof(*elt));
54 elt->str = ec_strdup(s);
55 if (elt->str == NULL) {
61 new_vec[strvec->len] = elt;
66 struct ec_strvec *ec_strvec_from_array(const char * const *strarr,
69 struct ec_strvec *strvec = NULL;
76 for (i = 0; i < n; i++) {
77 if (ec_strvec_add(strvec, strarr[i]) < 0)
84 ec_strvec_free(strvec);
88 int ec_strvec_del_last(struct ec_strvec *strvec)
90 struct ec_strvec_elt *elt;
92 if (strvec->len == 0) {
97 elt = strvec->vec[strvec->len - 1];
99 if (elt->refcnt == 0) {
107 struct ec_strvec *ec_strvec_ndup(const struct ec_strvec *strvec, size_t off,
110 struct ec_strvec *copy = NULL;
113 veclen = ec_strvec_len(strvec);
114 if (off + len > veclen)
124 copy->vec = ec_calloc(len, sizeof(*copy->vec));
125 if (copy->vec == NULL)
128 for (i = 0; i < len; i++) {
129 copy->vec[i] = strvec->vec[i + off];
130 copy->vec[i]->refcnt++;
137 ec_strvec_free(copy);
141 struct ec_strvec *ec_strvec_dup(const struct ec_strvec *strvec)
143 return ec_strvec_ndup(strvec, 0, ec_strvec_len(strvec));
146 void ec_strvec_free(struct ec_strvec *strvec)
148 struct ec_strvec_elt *elt;
154 for (i = 0; i < ec_strvec_len(strvec); i++) {
155 elt = strvec->vec[i];
157 if (elt->refcnt == 0) {
163 ec_free(strvec->vec);
167 size_t ec_strvec_len(const struct ec_strvec *strvec)
172 const char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx)
174 if (strvec == NULL || idx >= strvec->len)
177 return strvec->vec[idx]->str;
180 int ec_strvec_cmp(const struct ec_strvec *strvec1,
181 const struct ec_strvec *strvec2)
185 if (ec_strvec_len(strvec1) != ec_strvec_len(strvec2))
188 for (i = 0; i < ec_strvec_len(strvec1); i++) {
189 if (strcmp(ec_strvec_val(strvec1, i),
190 ec_strvec_val(strvec2, i)))
198 cmp_vec_elt(const void *p1, const void *p2, void *arg)
200 int (*str_cmp)(const char *s1, const char *s2) = arg;
201 const struct ec_strvec_elt * const *e1 = p1, * const *e2 = p2;
203 return str_cmp((*e1)->str, (*e2)->str);
206 void ec_strvec_sort(struct ec_strvec *strvec,
207 int (*str_cmp)(const char *s1, const char *s2))
211 qsort_r(strvec->vec, ec_strvec_len(strvec),
212 sizeof(*strvec->vec), cmp_vec_elt, str_cmp);
215 void ec_strvec_dump(FILE *out, const struct ec_strvec *strvec)
219 if (strvec == NULL) {
220 fprintf(out, "none\n");
224 fprintf(out, "strvec (len=%zu) [", strvec->len);
225 for (i = 0; i < ec_strvec_len(strvec); i++) {
227 fprintf(out, "%s", strvec->vec[i]->str);
229 fprintf(out, ", %s", strvec->vec[i]->str);
235 /* LCOV_EXCL_START */
236 static int ec_strvec_testcase(void)
238 struct ec_strvec *strvec = NULL;
239 struct ec_strvec *strvec2 = NULL;
245 strvec = ec_strvec();
246 if (strvec == NULL) {
247 EC_TEST_ERR("cannot create strvec\n");
250 if (ec_strvec_len(strvec) != 0) {
251 EC_TEST_ERR("bad strvec len (0)\n");
254 if (ec_strvec_add(strvec, "0") < 0) {
255 EC_TEST_ERR("cannot add (0) in strvec\n");
258 if (ec_strvec_len(strvec) != 1) {
259 EC_TEST_ERR("bad strvec len (1)\n");
262 if (ec_strvec_add(strvec, "1") < 0) {
263 EC_TEST_ERR("cannot add (1) in strvec\n");
266 if (ec_strvec_len(strvec) != 2) {
267 EC_TEST_ERR("bad strvec len (2)\n");
270 if (strcmp(ec_strvec_val(strvec, 0), "0")) {
271 EC_TEST_ERR("invalid element in strvec (0)\n");
274 if (strcmp(ec_strvec_val(strvec, 1), "1")) {
275 EC_TEST_ERR("invalid element in strvec (1)\n");
278 if (ec_strvec_val(strvec, 2) != NULL) {
279 EC_TEST_ERR("strvec val should be NULL\n");
283 strvec2 = ec_strvec_dup(strvec);
284 if (strvec2 == NULL) {
285 EC_TEST_ERR("cannot create strvec2\n");
288 if (ec_strvec_len(strvec2) != 2) {
289 EC_TEST_ERR("bad strvec2 len (2)\n");
292 if (strcmp(ec_strvec_val(strvec2, 0), "0")) {
293 EC_TEST_ERR("invalid element in strvec2 (0)\n");
296 if (strcmp(ec_strvec_val(strvec2, 1), "1")) {
297 EC_TEST_ERR("invalid element in strvec2 (1)\n");
300 if (ec_strvec_val(strvec2, 2) != NULL) {
301 EC_TEST_ERR("strvec2 val should be NULL\n");
304 ec_strvec_free(strvec2);
306 strvec2 = ec_strvec_ndup(strvec, 0, 0);
307 if (strvec2 == NULL) {
308 EC_TEST_ERR("cannot create strvec2\n");
311 if (ec_strvec_len(strvec2) != 0) {
312 EC_TEST_ERR("bad strvec2 len (0)\n");
315 if (ec_strvec_val(strvec2, 0) != NULL) {
316 EC_TEST_ERR("strvec2 val should be NULL\n");
319 ec_strvec_free(strvec2);
321 strvec2 = ec_strvec_ndup(strvec, 1, 1);
322 if (strvec2 == NULL) {
323 EC_TEST_ERR("cannot create strvec2\n");
326 if (ec_strvec_len(strvec2) != 1) {
327 EC_TEST_ERR("bad strvec2 len (1)\n");
330 if (strcmp(ec_strvec_val(strvec2, 0), "1")) {
331 EC_TEST_ERR("invalid element in strvec2 (1)\n");
334 if (ec_strvec_val(strvec2, 1) != NULL) {
335 EC_TEST_ERR("strvec2 val should be NULL\n");
338 ec_strvec_free(strvec2);
340 strvec2 = ec_strvec_ndup(strvec, 3, 1);
341 if (strvec2 != NULL) {
342 EC_TEST_ERR("strvec2 should be NULL\n");
345 ec_strvec_free(strvec2);
347 strvec2 = EC_STRVEC("0", "1");
348 if (strvec2 == NULL) {
349 EC_TEST_ERR("cannot create strvec from array\n");
352 testres |= EC_TEST_CHECK(ec_strvec_cmp(strvec, strvec2) == 0,
353 "strvec and strvec2 should be equal\n");
354 ec_strvec_free(strvec2);
356 f = open_memstream(&buf, &buflen);
359 ec_strvec_dump(f, strvec);
362 testres |= EC_TEST_CHECK(
363 strstr(buf, "strvec (len=2) [0, 1]"), "bad dump\n");
367 ec_strvec_del_last(strvec);
368 strvec2 = EC_STRVEC("0");
369 if (strvec2 == NULL) {
370 EC_TEST_ERR("cannot create strvec from array\n");
373 testres |= EC_TEST_CHECK(ec_strvec_cmp(strvec, strvec2) == 0,
374 "strvec and strvec2 should be equal\n");
375 ec_strvec_free(strvec2);
378 f = open_memstream(&buf, &buflen);
381 ec_strvec_dump(f, NULL);
384 testres |= EC_TEST_CHECK(
385 strstr(buf, "none"), "bad dump\n");
389 ec_strvec_free(strvec);
391 strvec = EC_STRVEC("e", "a", "f", "d", "b", "c");
392 if (strvec == NULL) {
393 EC_TEST_ERR("cannot create strvec from array\n");
396 ec_strvec_sort(strvec, NULL);
397 strvec2 = EC_STRVEC("a", "b", "c", "d", "e", "f");
398 if (strvec2 == NULL) {
399 EC_TEST_ERR("cannot create strvec from array\n");
402 testres |= EC_TEST_CHECK(ec_strvec_cmp(strvec, strvec2) == 0,
403 "strvec and strvec2 should be equal\n");
404 ec_strvec_free(strvec2);
406 ec_strvec_free(strvec);
414 ec_strvec_free(strvec);
415 ec_strvec_free(strvec2);
422 static struct ec_test ec_node_str_test = {
424 .test = ec_strvec_testcase,
427 EC_TEST_REGISTER(ec_node_str_test);