X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=lib%2Fecoli_strvec.c;h=c574b1b63764ea5e79a40198905e3cd17ba484b0;hb=dec2d7fa17ae1e8b3d1dec8e396d057757baf4f3;hp=c50f4a60031d4aa6aaea990d5d7030255792cca8;hpb=c1eb3099a1a7f4c9b9f8c6238336ee5b69147fd6;p=protos%2Flibecoli.git diff --git a/lib/ecoli_strvec.c b/lib/ecoli_strvec.c index c50f4a6..c574b1b 100644 --- a/lib/ecoli_strvec.c +++ b/lib/ecoli_strvec.c @@ -1,38 +1,20 @@ -/* - * Copyright (c) 2016, Olivier MATZ - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the University of California, Berkeley nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright 2016, Olivier MATZ */ +#define _GNU_SOURCE /* qsort_r */ #include #include #include #include #include +#include +#include #include +EC_LOG_TYPE_REGISTER(strvec); + struct ec_strvec_elt { unsigned int refcnt; char *str; @@ -61,18 +43,18 @@ int ec_strvec_add(struct ec_strvec *strvec, const char *s) new_vec = ec_realloc(strvec->vec, sizeof(*strvec->vec) * (strvec->len + 1)); if (new_vec == NULL) - return -ENOMEM; + return -1; strvec->vec = new_vec; elt = ec_malloc(sizeof(*elt)); if (elt == NULL) - return -ENOMEM; + return -1; elt->str = ec_strdup(s); if (elt->str == NULL) { ec_free(elt); - return -ENOMEM; + return -1; } elt->refcnt = 1; @@ -81,22 +63,61 @@ int ec_strvec_add(struct ec_strvec *strvec, const char *s) return 0; } +struct ec_strvec *ec_strvec_from_array(const char * const *strarr, + size_t n) +{ + struct ec_strvec *strvec = NULL; + size_t i; + + strvec = ec_strvec(); + if (strvec == NULL) + goto fail; + + for (i = 0; i < n; i++) { + if (ec_strvec_add(strvec, strarr[i]) < 0) + goto fail; + } + + return strvec; + +fail: + ec_strvec_free(strvec); + return NULL; +} + +int ec_strvec_del_last(struct ec_strvec *strvec) +{ + struct ec_strvec_elt *elt; + + if (strvec->len == 0) { + errno = EINVAL; + return -1; + } + + elt = strvec->vec[strvec->len - 1]; + elt->refcnt--; + if (elt->refcnt == 0) { + ec_free(elt->str); + ec_free(elt); + } + strvec->len--; + return 0; +} + struct ec_strvec *ec_strvec_ndup(const struct ec_strvec *strvec, size_t off, size_t len) { struct ec_strvec *copy = NULL; size_t i, veclen; + veclen = ec_strvec_len(strvec); + if (off + len > veclen) + return NULL; + copy = ec_strvec(); if (copy == NULL) goto fail; - veclen = ec_strvec_len(strvec); - if (off >= veclen) - len = 0; - else if (off + len > veclen) - len -= (veclen - off); - if (len == 0) return copy; @@ -107,8 +128,8 @@ struct ec_strvec *ec_strvec_ndup(const struct ec_strvec *strvec, size_t off, for (i = 0; i < len; i++) { copy->vec[i] = strvec->vec[i + off]; copy->vec[i]->refcnt++; - copy->len++; } + copy->len = len; return copy; @@ -148,7 +169,7 @@ size_t ec_strvec_len(const struct ec_strvec *strvec) return strvec->len; } -char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx) +const char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx) { if (strvec == NULL || idx >= strvec->len) return NULL; @@ -156,19 +177,251 @@ char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx) return strvec->vec[idx]->str; } +int ec_strvec_cmp(const struct ec_strvec *strvec1, + const struct ec_strvec *strvec2) +{ + size_t i; + + if (ec_strvec_len(strvec1) != ec_strvec_len(strvec2)) + return -1; + + for (i = 0; i < ec_strvec_len(strvec1); i++) { + if (strcmp(ec_strvec_val(strvec1, i), + ec_strvec_val(strvec2, i))) + return -1; + } + + return 0; +} + +static int +cmp_vec_elt(const void *p1, const void *p2, void *arg) +{ + int (*str_cmp)(const char *s1, const char *s2) = arg; + const struct ec_strvec_elt * const *e1 = p1, * const *e2 = p2; + + return str_cmp((*e1)->str, (*e2)->str); +} + +void ec_strvec_sort(struct ec_strvec *strvec, + int (*str_cmp)(const char *s1, const char *s2)) +{ + if (str_cmp == NULL) + str_cmp = strcmp; + qsort_r(strvec->vec, ec_strvec_len(strvec), + sizeof(*strvec->vec), cmp_vec_elt, str_cmp); +} + void ec_strvec_dump(FILE *out, const struct ec_strvec *strvec) { size_t i; if (strvec == NULL) { - fprintf(out, "empty strvec\n"); + fprintf(out, "none\n"); return; } - fprintf(out, "strvec:\n"); - for (i = 0; i < ec_strvec_len(strvec); i++) - fprintf(out, " %zd: %s (refcnt=%d)\n", i, - strvec->vec[i]->str, strvec->vec[i]->refcnt); + fprintf(out, "strvec (len=%zu) [", strvec->len); + for (i = 0; i < ec_strvec_len(strvec); i++) { + if (i == 0) + fprintf(out, "%s", strvec->vec[i]->str); + else + fprintf(out, ", %s", strvec->vec[i]->str); + } + fprintf(out, "]\n"); + } -/* XXX test case */ +/* LCOV_EXCL_START */ +static int ec_strvec_testcase(void) +{ + struct ec_strvec *strvec = NULL; + struct ec_strvec *strvec2 = NULL; + FILE *f = NULL; + char *buf = NULL; + size_t buflen = 0; + int testres = 0; + + strvec = ec_strvec(); + if (strvec == NULL) { + EC_TEST_ERR("cannot create strvec\n"); + goto fail; + } + if (ec_strvec_len(strvec) != 0) { + EC_TEST_ERR("bad strvec len (0)\n"); + goto fail; + } + if (ec_strvec_add(strvec, "0") < 0) { + EC_TEST_ERR("cannot add (0) in strvec\n"); + goto fail; + } + if (ec_strvec_len(strvec) != 1) { + EC_TEST_ERR("bad strvec len (1)\n"); + goto fail; + } + if (ec_strvec_add(strvec, "1") < 0) { + EC_TEST_ERR("cannot add (1) in strvec\n"); + goto fail; + } + if (ec_strvec_len(strvec) != 2) { + EC_TEST_ERR("bad strvec len (2)\n"); + goto fail; + } + if (strcmp(ec_strvec_val(strvec, 0), "0")) { + EC_TEST_ERR("invalid element in strvec (0)\n"); + goto fail; + } + if (strcmp(ec_strvec_val(strvec, 1), "1")) { + EC_TEST_ERR("invalid element in strvec (1)\n"); + goto fail; + } + if (ec_strvec_val(strvec, 2) != NULL) { + EC_TEST_ERR("strvec val should be NULL\n"); + goto fail; + } + + strvec2 = ec_strvec_dup(strvec); + if (strvec2 == NULL) { + EC_TEST_ERR("cannot create strvec2\n"); + goto fail; + } + if (ec_strvec_len(strvec2) != 2) { + EC_TEST_ERR("bad strvec2 len (2)\n"); + goto fail; + } + if (strcmp(ec_strvec_val(strvec2, 0), "0")) { + EC_TEST_ERR("invalid element in strvec2 (0)\n"); + goto fail; + } + if (strcmp(ec_strvec_val(strvec2, 1), "1")) { + EC_TEST_ERR("invalid element in strvec2 (1)\n"); + goto fail; + } + if (ec_strvec_val(strvec2, 2) != NULL) { + EC_TEST_ERR("strvec2 val should be NULL\n"); + goto fail; + } + ec_strvec_free(strvec2); + + strvec2 = ec_strvec_ndup(strvec, 0, 0); + if (strvec2 == NULL) { + EC_TEST_ERR("cannot create strvec2\n"); + goto fail; + } + if (ec_strvec_len(strvec2) != 0) { + EC_TEST_ERR("bad strvec2 len (0)\n"); + goto fail; + } + if (ec_strvec_val(strvec2, 0) != NULL) { + EC_TEST_ERR("strvec2 val should be NULL\n"); + goto fail; + } + ec_strvec_free(strvec2); + + strvec2 = ec_strvec_ndup(strvec, 1, 1); + if (strvec2 == NULL) { + EC_TEST_ERR("cannot create strvec2\n"); + goto fail; + } + if (ec_strvec_len(strvec2) != 1) { + EC_TEST_ERR("bad strvec2 len (1)\n"); + goto fail; + } + if (strcmp(ec_strvec_val(strvec2, 0), "1")) { + EC_TEST_ERR("invalid element in strvec2 (1)\n"); + goto fail; + } + if (ec_strvec_val(strvec2, 1) != NULL) { + EC_TEST_ERR("strvec2 val should be NULL\n"); + goto fail; + } + ec_strvec_free(strvec2); + + strvec2 = ec_strvec_ndup(strvec, 3, 1); + if (strvec2 != NULL) { + EC_TEST_ERR("strvec2 should be NULL\n"); + goto fail; + } + ec_strvec_free(strvec2); + + strvec2 = EC_STRVEC("0", "1"); + if (strvec2 == NULL) { + EC_TEST_ERR("cannot create strvec from array\n"); + goto fail; + } + testres |= EC_TEST_CHECK(ec_strvec_cmp(strvec, strvec2) == 0, + "strvec and strvec2 should be equal\n"); + ec_strvec_free(strvec2); + + f = open_memstream(&buf, &buflen); + if (f == NULL) + goto fail; + ec_strvec_dump(f, strvec); + fclose(f); + f = NULL; + testres |= EC_TEST_CHECK( + strstr(buf, "strvec (len=2) [0, 1]"), "bad dump\n"); + free(buf); + buf = NULL; + + ec_strvec_del_last(strvec); + strvec2 = EC_STRVEC("0"); + if (strvec2 == NULL) { + EC_TEST_ERR("cannot create strvec from array\n"); + goto fail; + } + testres |= EC_TEST_CHECK(ec_strvec_cmp(strvec, strvec2) == 0, + "strvec and strvec2 should be equal\n"); + ec_strvec_free(strvec2); + strvec2 = NULL; + + f = open_memstream(&buf, &buflen); + if (f == NULL) + goto fail; + ec_strvec_dump(f, NULL); + fclose(f); + f = NULL; + testres |= EC_TEST_CHECK( + strstr(buf, "none"), "bad dump\n"); + free(buf); + buf = NULL; + + ec_strvec_free(strvec); + + strvec = EC_STRVEC("e", "a", "f", "d", "b", "c"); + if (strvec == NULL) { + EC_TEST_ERR("cannot create strvec from array\n"); + goto fail; + } + ec_strvec_sort(strvec, NULL); + strvec2 = EC_STRVEC("a", "b", "c", "d", "e", "f"); + if (strvec2 == NULL) { + EC_TEST_ERR("cannot create strvec from array\n"); + goto fail; + } + testres |= EC_TEST_CHECK(ec_strvec_cmp(strvec, strvec2) == 0, + "strvec and strvec2 should be equal\n"); + ec_strvec_free(strvec); + strvec = NULL; + ec_strvec_free(strvec2); + strvec2 = NULL; + + return testres; + +fail: + if (f != NULL) + fclose(f); + ec_strvec_free(strvec); + ec_strvec_free(strvec2); + free(buf); + + return -1; +} +/* LCOV_EXCL_STOP */ + +static struct ec_test ec_node_str_test = { + .name = "strvec", + .test = ec_strvec_testcase, +}; + +EC_TEST_REGISTER(ec_node_str_test);