From 1bc00866d54d42d0e357ebd8f6c4edcde3456119 Mon Sep 17 00:00:00 2001 From: Olivier Matz Date: Thu, 23 Nov 2017 20:38:46 +0100 Subject: [PATCH] save --- lib/Makefile | 4 +- lib/ecoli_assert.c | 47 ++++++++++ lib/ecoli_assert.h | 58 ++++++++++++ lib/ecoli_init.c | 55 +++++++++++ lib/ecoli_init.h | 78 ++++++++++++++++ lib/ecoli_keyval.c | 208 +++++++++++++++++++++++++++++++++-------- lib/ecoli_keyval.h | 88 ++++++++++++++++- lib/ecoli_murmurhash.c | 63 +++++++++++++ lib/ecoli_murmurhash.h | 89 ++++++++++++++++++ lib/ecoli_node_any.c | 109 +++++++++++++++++++++ lib/ecoli_node_any.h | 35 +++++++ lib/ecoli_string.c | 53 +++++++++++ lib/ecoli_string.h | 39 ++++++++ lib/ecoli_strvec.c | 135 +++++++++++--------------- lib/ecoli_test.c | 2 +- lib/ecoli_test.h | 8 +- lib/ecoli_vec.c | 19 ++-- lib/ecoli_vec.h | 1 + lib/main-readline.c | 7 ++ lib/main.c | 8 +- 20 files changed, 969 insertions(+), 137 deletions(-) create mode 100644 lib/ecoli_assert.c create mode 100644 lib/ecoli_assert.h create mode 100644 lib/ecoli_init.c create mode 100644 lib/ecoli_init.h create mode 100644 lib/ecoli_murmurhash.c create mode 100644 lib/ecoli_murmurhash.h create mode 100644 lib/ecoli_node_any.c create mode 100644 lib/ecoli_node_any.h create mode 100644 lib/ecoli_string.c create mode 100644 lib/ecoli_string.h diff --git a/lib/Makefile b/lib/Makefile index b258752..2bf8ec0 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -45,8 +45,10 @@ srcs := srcs += ecoli_assert.c srcs += ecoli_completed.c srcs += ecoli_keyval.c +srcs += ecoli_init.c srcs += ecoli_log.c srcs += ecoli_malloc.c +srcs += ecoli_murmurhash.c srcs += ecoli_strvec.c srcs += ecoli_test.c srcs += ecoli_node.c @@ -79,7 +81,7 @@ ldflags-$(O)test = -rdynamic exe-y-$(O)test = $(srcs) main.c ldflags-$(O)readline = -lreadline -ltermcap -#exe-y-$(O)readline = $(srcs) main-readline.c +exe-y-$(O)readline = $(srcs) main-readline.c include $(ECOLI)/mk/ecoli-post.mk diff --git a/lib/ecoli_assert.c b/lib/ecoli_assert.c new file mode 100644 index 0000000..8b564a7 --- /dev/null +++ b/lib/ecoli_assert.c @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#include +#include +#include +#include + +#include + +void __ec_assert_print(bool expr, const char *expr_str, const char *format, ...) +{ + va_list ap; + + if (expr) + return; + + va_start(ap, format); + fprintf(stderr, "assertion failed: '%s' is false\n", expr_str); + vfprintf(stderr, format, ap); + va_end(ap); + abort(); +} diff --git a/lib/ecoli_assert.h b/lib/ecoli_assert.h new file mode 100644 index 0000000..61989e9 --- /dev/null +++ b/lib/ecoli_assert.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +/** + * Assert API + * + * Helpers to check at runtime if a condition is true, and abort + * otherwise. + */ + +#ifndef ECOLI_ASSERT_ +#define ECOLI_ASSERT_ + +#include + +/** + * Abort if the condition is false. + * + * If expression is false this macro will prints an error message to + * standard error and terminates the program by calling abort(3). + * + * @param expr + * The expression to be checked. + * @param args + * The format string, optionally followed by other arguments. + */ +#define ec_assert_print(expr, args...) \ + __ec_assert_print(expr, #expr, args) + +/* internal */ +void __ec_assert_print(bool expr, const char *expr_str, + const char *format, ...); + +#endif diff --git a/lib/ecoli_init.c b/lib/ecoli_init.c new file mode 100644 index 0000000..f91156e --- /dev/null +++ b/lib/ecoli_init.c @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#include +#include +#include +#include + +#include + +static struct ec_init_list init_list = TAILQ_HEAD_INITIALIZER(init_list); + +/* register an init function */ +void ec_init_register(struct ec_init *init) +{ + TAILQ_INSERT_TAIL(&init_list, init, next); +} + +int ec_init(void) +{ + struct ec_init *init; + + /* XXX sort list by priority */ + + TAILQ_FOREACH(init, &init_list, next) { + if (init->init() < 0) + return -1; + } + + return 0; +} diff --git a/lib/ecoli_init.h b/lib/ecoli_init.h new file mode 100644 index 0000000..9166cdc --- /dev/null +++ b/lib/ecoli_init.h @@ -0,0 +1,78 @@ +/* + * 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. + */ + +#ifndef ECOLI_INIT_ +#define ECOLI_INIT_ + +#include + +#include +#include + +#define EC_INIT_REGISTER(t) \ + static void ec_init_init_##t(void); \ + static void __attribute__((constructor, used)) \ + ec_init_init_##t(void) \ + { \ + ec_init_register(&t); \ + } + +/** + * Type of init function. Return 0 on success, -1 on error. + */ +typedef int (ec_init_t)(void); + +TAILQ_HEAD(ec_init_list, ec_init); + +/** + * A structure describing a test case. + */ +struct ec_init { + TAILQ_ENTRY(ec_init) next; /**< Next in list. */ + ec_init_t *init; /**< Init function. */ + unsigned int priority; /**< Priority (0=first, 99=last) */ +}; + +/** + * Register an initialization function. + * + * @param init + * A pointer to a ec_init structure to be registered. + */ +void ec_init_register(struct ec_init *test); + +/** + * Initialize ecoli library + * + * Must be called before any other function. + * + * @return + * 0 on success, -1 on error (errno is set). + */ +int ec_init(void); + +#endif diff --git a/lib/ecoli_keyval.c b/lib/ecoli_keyval.c index e80dd08..9baba5f 100644 --- a/lib/ecoli_keyval.c +++ b/lib/ecoli_keyval.c @@ -26,26 +26,42 @@ */ #include +#include +#include #include +#include +#include #include #include +#include +#include #include #include #include +#include #include +#define FACTOR 3 + EC_LOG_TYPE_REGISTER(keyval); +static uint32_t ec_keyval_seed; + struct ec_keyval_elt { + LIST_ENTRY(ec_keyval_elt) next; char *key; void *val; + uint32_t hash; ec_keyval_elt_free_t free; }; +LIST_HEAD(ec_keyval_elt_list, ec_keyval_elt); + struct ec_keyval { size_t len; - struct ec_keyval_elt *vec; + size_t table_size; + struct ec_keyval_elt_list *table; }; struct ec_keyval *ec_keyval(void) @@ -63,17 +79,26 @@ static struct ec_keyval_elt *ec_keyval_lookup(const struct ec_keyval *keyval, const char *key) { struct ec_keyval_elt *elt; - size_t i; + uint32_t h, mask = keyval->table_size - 1; - if (keyval == NULL || keyval->vec == NULL) + if (keyval == NULL || key == NULL) { + errno = EINVAL; return NULL; + } + if (keyval->table_size == 0) { + errno = ENOENT; + return NULL; + } - for (i = 0; i < ec_keyval_len(keyval); i++) { - elt = &keyval->vec[i]; - if (strcmp(elt->key, key) == 0) + h = ec_murmurhash3(key, strlen(key), ec_keyval_seed); + LIST_FOREACH(elt, &keyval->table[h & mask], next) { + if (strcmp(elt->key, key) == 0) { + errno = 0; return elt; + } } + errno = ENOENT; return NULL; } @@ -85,6 +110,7 @@ static void ec_keyval_elt_free(struct ec_keyval_elt *elt) ec_free(elt->key); if (elt->free != NULL) elt->free(elt->val); + ec_free(elt); } bool ec_keyval_has_key(const struct ec_keyval *keyval, const char *key) @@ -106,18 +132,47 @@ void *ec_keyval_get(const struct ec_keyval *keyval, const char *key) int ec_keyval_del(struct ec_keyval *keyval, const char *key) { struct ec_keyval_elt *elt; - struct ec_keyval_elt *last = &keyval->vec[keyval->len - 1]; elt = ec_keyval_lookup(keyval, key); if (elt == NULL) - return -ENOENT; + return -1; + + /* we could resize table here */ + LIST_REMOVE(elt, next); ec_keyval_elt_free(elt); + keyval->len--; - if (elt != last) - memcpy(elt, last, sizeof(*elt)); + return 0; +} - keyval->len--; +static int ec_keyval_table_resize(struct ec_keyval *keyval, size_t new_size) +{ + struct ec_keyval_elt_list *new_table; + struct ec_keyval_elt *elt; + size_t i; + + if (new_size == 0 || (new_size & (new_size - 1))) { + errno = EINVAL; + return -1; + } + + new_table = ec_calloc(new_size, sizeof(*keyval->table)); + if (new_table == NULL) + return -1; + + for (i = 0; i < keyval->table_size; i++) { + while (!LIST_EMPTY(&keyval->table[i])) { + elt = LIST_FIRST(&keyval->table[i]); + LIST_REMOVE(elt, next); + LIST_INSERT_HEAD(&new_table[elt->hash & (new_size - 1)], + elt, next); + } + } + + ec_free(keyval->table); + keyval->table = new_table; + keyval->table_size = new_size; return 0; } @@ -125,35 +180,61 @@ int ec_keyval_del(struct ec_keyval *keyval, const char *key) int ec_keyval_set(struct ec_keyval *keyval, const char *key, void *val, ec_keyval_elt_free_t free_cb) { - struct ec_keyval_elt *elt, *new_vec; - - assert(keyval != NULL); - assert(key != NULL); + struct ec_keyval_elt *elt; + uint32_t h, mask; + size_t new_size; + int ret; - ec_keyval_del(keyval, key); + if (keyval == NULL || key == NULL) { + errno = EINVAL; + return -1; + } - new_vec = ec_realloc(keyval->vec, - sizeof(*keyval->vec) * (keyval->len + 1)); - if (new_vec == NULL) - goto fail; + elt = ec_keyval_lookup(keyval, key); + if (elt != NULL) { + if (elt->free != NULL) + elt->free(elt->val); + elt->val = val; + elt->free = free_cb; + return 0; + } - keyval->vec = new_vec; + if (keyval->len >= keyval->table_size) { + if (keyval->table_size != 0) + new_size = keyval->table_size << FACTOR; + else + new_size = 1 << FACTOR; + ret = ec_keyval_table_resize(keyval, new_size); + if (ret < 0) + goto fail; + } - elt = &new_vec[keyval->len]; + elt = ec_calloc(1, sizeof(*elt)); + if (elt == NULL) + goto fail; elt->key = ec_strdup(key); if (elt->key == NULL) goto fail; - elt->val = val; elt->free = free_cb; + h = ec_murmurhash3(key, strlen(key), ec_keyval_seed); + elt->hash = h; + + mask = keyval->table_size - 1; + LIST_INSERT_HEAD(&keyval->table[h & mask], elt, next); keyval->len++; return 0; fail: - if (free_cb) + if (free_cb != NULL) free_cb(val); - return -ENOMEM; + if (elt != NULL) { + ec_free(elt->key); + ec_free(elt); + } + + return ret; } void ec_keyval_free(struct ec_keyval *keyval) @@ -164,11 +245,14 @@ void ec_keyval_free(struct ec_keyval *keyval) if (keyval == NULL) return; - for (i = 0; i < ec_keyval_len(keyval); i++) { - elt = &keyval->vec[i]; - ec_keyval_elt_free(elt); + for (i = 0; i < keyval->table_size; i++) { + while (!LIST_EMPTY(&keyval->table[i])) { + elt = LIST_FIRST(&keyval->table[i]); + LIST_REMOVE(elt, next); + ec_keyval_elt_free(elt); + } } - ec_free(keyval->vec); + ec_free(keyval->table); ec_free(keyval); } @@ -177,8 +261,9 @@ size_t ec_keyval_len(const struct ec_keyval *keyval) return keyval->len; } -void ec_keyval_dump(const struct ec_keyval *keyval, FILE *out) +void ec_keyval_dump(FILE *out, const struct ec_keyval *keyval) { + struct ec_keyval_elt *elt; size_t i; if (keyval == NULL) { @@ -187,18 +272,47 @@ void ec_keyval_dump(const struct ec_keyval *keyval, FILE *out) } fprintf(out, "keyval:\n"); - for (i = 0; i < ec_keyval_len(keyval); i++) { - fprintf(out, " %s: %p\n", - keyval->vec[i].key, - keyval->vec[i].val); + for (i = 0; i < keyval->table_size; i++) { + LIST_FOREACH(elt, &keyval->table[i], next) { + fprintf(out, " %s: %p\n", + elt->key, elt->val); + } } } +static int ec_keyval_init_func(void) +{ + int fd; + ssize_t ret; + + fd = open("/dev/urandom", 0); + if (fd == -1) { + fprintf(stderr, "failed to open /dev/urandom\n"); + return -1; + } + ret = read(fd, &ec_keyval_seed, sizeof(ec_keyval_seed)); + if (ret != sizeof(ec_keyval_seed)) { + fprintf(stderr, "failed to read /dev/urandom\n"); + return -1; + } + close(fd); + return 0; +} + +static struct ec_init ec_keyval_init = { + .init = ec_keyval_init_func, + .priority = 50, +}; + +EC_INIT_REGISTER(ec_keyval_init); + /* LCOV_EXCL_START */ static int ec_keyval_testcase(void) { struct ec_keyval *keyval; char *val; + size_t i; + int ret; keyval = ec_keyval(); if (keyval == NULL) { @@ -207,8 +321,10 @@ static int ec_keyval_testcase(void) } EC_TEST_ASSERT(ec_keyval_len(keyval) == 0); - ec_keyval_set(keyval, "key1", "val1", NULL); - ec_keyval_set(keyval, "key2", ec_strdup("val2"), ec_free2); + ret = ec_keyval_set(keyval, "key1", "val1", NULL); + EC_TEST_ASSERT_STR(ret == 0, "cannot set key"); + ret = ec_keyval_set(keyval, "key2", ec_strdup("val2"), ec_free2); + EC_TEST_ASSERT_STR(ret == 0, "cannot set key"); EC_TEST_ASSERT(ec_keyval_len(keyval) == 2); val = ec_keyval_get(keyval, "key1"); @@ -218,20 +334,34 @@ static int ec_keyval_testcase(void) val = ec_keyval_get(keyval, "key3"); EC_TEST_ASSERT(val == NULL); - ec_keyval_set(keyval, "key1", "another_val1", NULL); - ec_keyval_set(keyval, "key2", ec_strdup("another_val2"), ec_free2); + ret = ec_keyval_set(keyval, "key1", "another_val1", NULL); + EC_TEST_ASSERT_STR(ret == 0, "cannot set key"); + ret = ec_keyval_set(keyval, "key2", ec_strdup("another_val2"), ec_free2); + EC_TEST_ASSERT_STR(ret == 0, "cannot set key"); EC_TEST_ASSERT(ec_keyval_len(keyval) == 2); val = ec_keyval_get(keyval, "key1"); EC_TEST_ASSERT(val != NULL && !strcmp(val, "another_val1")); val = ec_keyval_get(keyval, "key2"); EC_TEST_ASSERT(val != NULL && !strcmp(val, "another_val2")); + EC_TEST_ASSERT(ec_keyval_has_key(keyval, "key1")); - ec_keyval_del(keyval, "key1"); + ret = ec_keyval_del(keyval, "key1"); + EC_TEST_ASSERT_STR(ret == 0, "cannot del key"); EC_TEST_ASSERT(ec_keyval_len(keyval) == 1); + ec_keyval_dump(stdout, NULL); + ec_keyval_dump(stdout, keyval); + + for (i = 0; i < 100; i++) { + char buf[8]; + snprintf(buf, sizeof(buf), "k%zd", i); + ret = ec_keyval_set(keyval, buf, "val", NULL); + EC_TEST_ASSERT_STR(ret == 0, "cannot set key"); + } ec_keyval_free(keyval); + return 0; } /* LCOV_EXCL_STOP */ diff --git a/lib/ecoli_keyval.h b/lib/ecoli_keyval.h index 30232d0..3d739ff 100644 --- a/lib/ecoli_keyval.h +++ b/lib/ecoli_keyval.h @@ -25,6 +25,13 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/** + * Simple hash table API + * + * This file provides functions to store objects in hash tables, using strings + * as keys. + */ + #ifndef ECOLI_KEYVAL_ #define ECOLI_KEYVAL_ @@ -35,16 +42,95 @@ typedef void (*ec_keyval_elt_free_t)(void *); struct ec_keyval; +/** + * Create a hash table. + * + * @return + * The hash table, or NULL on error (errno is set). + */ struct ec_keyval *ec_keyval(void); +/** + * Get a value from the hash table. + * + * @param keyval + * The hash table. + * @param key + * The key string. + * @return + * The element if it is found, or NULL on error (errno is set). + * In case of success but the element is NULL, errno is set to 0. + */ void *ec_keyval_get(const struct ec_keyval *keyval, const char *key); + +/** + * Check if the hash table contains this key. + * + * @param keyval + * The hash table. + * @param key + * The key string. + * @return + * true if it contains the key, else false. + */ bool ec_keyval_has_key(const struct ec_keyval *keyval, const char *key); + +/** + * Delete an object from the hash table. + * + * @param keyval + * The hash table. + * @param key + * The key string. + * @return + * 0 on success, or -1 on error (errno is set). + */ int ec_keyval_del(struct ec_keyval *keyval, const char *key); + +/** + * Add/replace an object in the hash table. + * + * @param keyval + * The hash table. + * @param key + * The key string. + * @param val + * The pointer to be saved in the hash table. + * @param free_cb + * An optional pointer to a destructor function called when an + * object is destroyed (ec_keyval_del() or ec_keyval_free()). + * @return + * 0 on success, or -1 on error (errno is set). + */ int ec_keyval_set(struct ec_keyval *keyval, const char *key, void *val, ec_keyval_elt_free_t free_cb); +/** + * Free a hash table an all its objects. + * + * @param keyval + * The hash table. + */ void ec_keyval_free(struct ec_keyval *keyval); + +/** + * Get the length of a hash table. + * + * @param keyval + * The hash table. + * @return + * The length of the hash table. + */ size_t ec_keyval_len(const struct ec_keyval *keyval); -void ec_keyval_dump(const struct ec_keyval *keyval, FILE *out); + +/** + * Dump a hash table. + * + * @param out + * The stream where the dump is sent. + * @param keyval + * The hash table. + */ +void ec_keyval_dump(FILE *out, const struct ec_keyval *keyval); #endif diff --git a/lib/ecoli_murmurhash.c b/lib/ecoli_murmurhash.c new file mode 100644 index 0000000..0efa3b9 --- /dev/null +++ b/lib/ecoli_murmurhash.c @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#include + +#include + +uint32_t ec_murmurhash3(const void *key, int len, uint32_t seed) +{ + const uint8_t *data = (const uint8_t *)key; + const uint8_t *tail; + const int nblocks = len / 4; + uint32_t h1 = seed; + uint32_t k1; + const uint32_t *blocks = (const uint32_t *)(data + nblocks * 4); + int i; + + for (i = -nblocks; i; i++) { + k1 = blocks[i]; + + h1 = ec_murmurhash3_add32(h1, k1); + h1 = ec_murmurhash3_mix32(h1); + } + + tail = (const uint8_t *)(data + nblocks * 4); + k1 = 0; + + switch(len & 3) { + case 3: k1 ^= tail[2] << 16; + case 2: k1 ^= tail[1] << 8; + case 1: k1 ^= tail[0]; + h1 = ec_murmurhash3_add32(h1, k1); + }; + + /* finalization */ + h1 ^= len; + h1 = ec_murmurhash3_fmix32(h1); + return h1; +} diff --git a/lib/ecoli_murmurhash.h b/lib/ecoli_murmurhash.h new file mode 100644 index 0000000..942c0b3 --- /dev/null +++ b/lib/ecoli_murmurhash.h @@ -0,0 +1,89 @@ +/* + * 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. + */ + +/** + * MurmurHash3 is a hash implementation that was written by Austin Appleby, and + * is placed in the public domain. The author hereby disclaims copyright to this + * source code. + */ + +#ifndef ECOLI_MURMURHASH_H_ +#define ECOLI_MURMURHASH_H_ + +#include + +/** Hash rotation */ +static inline uint32_t ec_murmurhash_rotl32(uint32_t x, int8_t r) +{ + return (x << r) | (x >> (32 - r)); +} + +/** Add 32-bit to the hash */ +static inline uint32_t ec_murmurhash3_add32(uint32_t h, uint32_t data) +{ + data *= 0xcc9e2d51; + data = ec_murmurhash_rotl32(data, 15); + data *= 0x1b873593; + h ^= data; + return h; +} + +/** Intermediate mix */ +static inline uint32_t ec_murmurhash3_mix32(uint32_t h) +{ + h = ec_murmurhash_rotl32(h,13); + h = h * 5 +0xe6546b64; + return h; +} + +/** Final mix: force all bits of a hash block to avalanche */ +static inline uint32_t ec_murmurhash3_fmix32(uint32_t h) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + return h; +} + +/** + * Calculate a 32-bit murmurhash3 + * + * @param key + * The key (the unaligned variable-length array of bytes). + * @param len + * The length of the key, counting by bytes. + * @param seed + * Can be any 4-byte value initialization value. + * @return + * A 32-bit hash. + */ +uint32_t ec_murmurhash3(const void *key, int len, uint32_t seed); + +#endif /* ECOLI_MURMURHASH_H_ */ diff --git a/lib/ecoli_node_any.c b/lib/ecoli_node_any.c new file mode 100644 index 0000000..7fef0c1 --- /dev/null +++ b/lib/ecoli_node_any.c @@ -0,0 +1,109 @@ +/* + * 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. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +EC_LOG_TYPE_REGISTER(node_any); + +struct ec_node_any { + struct ec_node gen; +}; + +static int ec_node_any_parse(const struct ec_node *gen_node, + struct ec_parsed *state, + const struct ec_strvec *strvec) +{ + (void)gen_node; + (void)state; + + if (ec_strvec_len(strvec) == 0) + return EC_PARSED_NOMATCH; + + return 1; +} + +static struct ec_node_type ec_node_any_type = { + .name = "any", + .parse = ec_node_any_parse, + .complete = ec_node_default_complete, + .size = sizeof(struct ec_node_any), +}; + +EC_NODE_TYPE_REGISTER(ec_node_any_type); + +/* LCOV_EXCL_START */ +static int ec_node_any_testcase(void) +{ + struct ec_node *node; + int ret = 0; + + node = ec_node("any", NULL); + if (node == NULL) { + EC_LOG(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_PARSE(node, 1, "foo"); + ret |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar"); + ret |= EC_TEST_CHECK_PARSE(node, -1); + ec_node_free(node); + + /* never completes */ + node = ec_node("any", NULL); + if (node == NULL) { + EC_LOG(EC_LOG_ERR, "cannot create node\n"); + return -1; + } + ret |= EC_TEST_CHECK_COMPLETE(node, + "", EC_NODE_ENDLIST, + EC_NODE_ENDLIST); + ret |= EC_TEST_CHECK_COMPLETE(node, + "foo", EC_NODE_ENDLIST, + EC_NODE_ENDLIST); + ec_node_free(node); + + return ret; +} +/* LCOV_EXCL_STOP */ + +static struct ec_test ec_node_any_test = { + .name = "node_any", + .test = ec_node_any_testcase, +}; + +EC_TEST_REGISTER(ec_node_any_test); diff --git a/lib/ecoli_node_any.h b/lib/ecoli_node_any.h new file mode 100644 index 0000000..e52cbe9 --- /dev/null +++ b/lib/ecoli_node_any.h @@ -0,0 +1,35 @@ +/* + * 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. + */ + +/** + * This node always matches 1 string in the vector + */ + +#ifndef ECOLI_NODE_ANY_ +#define ECOLI_NODE_ANY_ + +#endif diff --git a/lib/ecoli_string.c b/lib/ecoli_string.c new file mode 100644 index 0000000..49e4a37 --- /dev/null +++ b/lib/ecoli_string.c @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#include +#include + +#include + +/* count the number of identical chars at the beginning of 2 strings */ +size_t ec_strcmp_count(const char *s1, const char *s2) +{ + size_t i = 0; + + while (s1[i] && s2[i] && s1[i] == s2[i]) + i++; + + return i; +} + +int ec_str_startswith(const char *s, const char *beginning) +{ + size_t len; + + len = ec_strcmp_count(s, beginning); + if (beginning[len] == '\0') + return 1; + + return 0; +} diff --git a/lib/ecoli_string.h b/lib/ecoli_string.h new file mode 100644 index 0000000..ae92b4d --- /dev/null +++ b/lib/ecoli_string.h @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#ifndef ECOLI_STRING_ +#define ECOLI_STRING_ + +#include + +/* count the number of identical chars at the beginning of 2 strings */ +size_t ec_strcmp_count(const char *s1, const char *s2); + +/* return 1 if 's' starts with 'beginning' */ +int ec_str_startswith(const char *s, const char *beginning); + +#endif diff --git a/lib/ecoli_strvec.c b/lib/ecoli_strvec.c index 32208ad..53017fb 100644 --- a/lib/ecoli_strvec.c +++ b/lib/ecoli_strvec.c @@ -31,9 +31,10 @@ #include #include -#include #include #include +#include +#include EC_LOG_TYPE_REGISTER(strvec); @@ -42,128 +43,97 @@ struct ec_strvec_elt { char *str; }; -struct ec_strvec { - size_t len; - struct ec_strvec_elt **vec; -}; +struct ec_strvec; -struct ec_strvec *ec_strvec(void) +static void ec_strvec_elt_free(void *ptr) { - struct ec_strvec *strvec; + struct ec_strvec_elt **p_elt = ptr; + struct ec_strvec_elt *elt = *p_elt; - strvec = ec_calloc(1, sizeof(*strvec)); - if (strvec == NULL) - return NULL; + elt->refcnt--; + if (elt->refcnt == 0) { + ec_free(elt->str); + ec_free(elt); + } +} + +static void ec_strvec_elt_copy(void *dst, void *src) +{ + struct ec_strvec_elt **p_elt_dst = dst, **p_elt_src = src; + struct ec_strvec_elt *elt = *p_elt_src; - return strvec; + elt->refcnt++; + *p_elt_dst = elt; } -int ec_strvec_add(struct ec_strvec *strvec, const char *s) +struct ec_strvec *ec_strvec(void) { - struct ec_strvec_elt *elt, **new_vec; + struct ec_vec *vec; - new_vec = ec_realloc(strvec->vec, - sizeof(*strvec->vec) * (strvec->len + 1)); - if (new_vec == NULL) - return -1; + vec = ec_vec(sizeof(struct ec_strvec_elt *), 0, + ec_strvec_elt_copy, ec_strvec_elt_free); - strvec->vec = new_vec; + return (struct ec_strvec *)vec; +} + +int ec_strvec_add(struct ec_strvec *strvec, const char *s) +{ + struct ec_strvec_elt *elt = NULL; elt = ec_malloc(sizeof(*elt)); if (elt == NULL) - return -1; + goto fail; elt->str = ec_strdup(s); - if (elt->str == NULL) { - ec_free(elt); - return -1; - } + if (elt->str == NULL) + goto fail; elt->refcnt = 1; - new_vec[strvec->len] = elt; - strvec->len++; + if (ec_vec_add_ptr((struct ec_vec *)strvec, elt) < 0) + goto fail; + return 0; + +fail: + ec_free(elt); + return -1; } 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; - - if (off + len > ec_strvec_len(strvec)) { - errno = EINVAL; - return NULL; - } - - copy = ec_strvec(); - if (copy == NULL) - goto fail; - - if (len == 0) - return copy; - - copy->vec = ec_calloc(len, sizeof(*copy->vec)); - if (copy->vec == NULL) - goto fail; - - for (i = 0; i < len; i++) { - copy->vec[i] = strvec->vec[i + off]; - copy->vec[i]->refcnt++; - copy->len++; - } - - return copy; - -fail: - ec_strvec_free(copy); - return NULL; + return (struct ec_strvec *)ec_vec_ndup((struct ec_vec *)strvec, + off, len); } struct ec_strvec *ec_strvec_dup(const struct ec_strvec *strvec) { - return ec_strvec_ndup(strvec, 0, ec_strvec_len(strvec)); + return (struct ec_strvec *)ec_vec_dup((struct ec_vec *)strvec); } void ec_strvec_free(struct ec_strvec *strvec) { - struct ec_strvec_elt *elt; - size_t i; - - if (strvec == NULL) - return; - - for (i = 0; i < ec_strvec_len(strvec); i++) { - elt = strvec->vec[i]; - elt->refcnt--; - if (elt->refcnt == 0) { - ec_free(elt->str); - ec_free(elt); - } - } - - ec_free(strvec->vec); - ec_free(strvec); + ec_vec_free((struct ec_vec *)strvec); } size_t ec_strvec_len(const struct ec_strvec *strvec) { - if (strvec == NULL) - return 0; - - return strvec->len; + return ec_vec_len((struct ec_vec *)strvec); } const char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx) { - if (strvec == NULL || idx >= strvec->len) + struct ec_strvec_elt *elt; + + if (ec_vec_get(&elt, (struct ec_vec *)strvec, idx) < 0) return NULL; - return strvec->vec[idx]->str; + return elt->str; } void ec_strvec_dump(FILE *out, const struct ec_strvec *strvec) { + const char *s; size_t i; if (strvec == NULL) { @@ -171,12 +141,13 @@ void ec_strvec_dump(FILE *out, const struct ec_strvec *strvec) return; } - fprintf(out, "strvec (len=%zu) [", strvec->len); + fprintf(out, "strvec (len=%zu) [", ec_strvec_len(strvec)); for (i = 0; i < ec_strvec_len(strvec); i++) { + s = ec_strvec_val(strvec, i); if (i == 0) - fprintf(out, "%s", strvec->vec[i]->str); + fprintf(out, "%s", s); else - fprintf(out, ", %s", strvec->vec[i]->str); + fprintf(out, ", %s", s); } fprintf(out, "]\n"); diff --git a/lib/ecoli_test.c b/lib/ecoli_test.c index 53cdf62..9cf216d 100644 --- a/lib/ecoli_test.c +++ b/lib/ecoli_test.c @@ -43,7 +43,7 @@ static struct ec_test_list test_list = TAILQ_HEAD_INITIALIZER(test_list); EC_LOG_TYPE_REGISTER(test); -/* register a driver */ +/* register a test case */ void ec_test_register(struct ec_test *test) { TAILQ_INSERT_TAIL(&test_list, test, next); diff --git a/lib/ecoli_test.h b/lib/ecoli_test.h index 1fa8480..1cebe95 100644 --- a/lib/ecoli_test.h +++ b/lib/ecoli_test.h @@ -77,12 +77,16 @@ int ec_test_check_parse(struct ec_node *node, int expected, ...); EC_LOG(EC_LOG_ERR, "%s:%d: error: " fmt "\n", \ __FILE__, __LINE__, ##__VA_ARGS__); \ -#define EC_TEST_ASSERT(cond) \ +/* XXX this is not an assert, it does not abort */ +#define EC_TEST_ASSERT_STR(cond, fmt, ...) \ do { \ if (!(cond)) \ - EC_TEST_ERR("assertion failure: " #cond); \ + EC_TEST_ERR("assert failure: (" #cond ") " fmt, \ + ##__VA_ARGS__); \ } while (0) +#define EC_TEST_ASSERT(cond) EC_TEST_ASSERT_STR(cond, "") + /* node, input, [expected1, expected2, ...] */ #define EC_TEST_CHECK_PARSE(node, args...) ({ \ int ret_ = ec_test_check_parse(node, args, EC_NODE_ENDLIST); \ diff --git a/lib/ecoli_vec.c b/lib/ecoli_vec.c index 871911d..8126526 100644 --- a/lib/ecoli_vec.c +++ b/lib/ecoli_vec.c @@ -97,10 +97,7 @@ int ec_vec_add_by_ref(struct ec_vec *vec, void *ptr) } vec->vec = new_vec; - if (vec->copy) - vec->copy(get_obj(vec, vec->len), ptr); - else - memcpy(get_obj(vec, vec->len), ptr, vec->elt_size); + memcpy(get_obj(vec, vec->len), ptr, vec->elt_size); vec->len++; return 0; @@ -181,6 +178,9 @@ fail: size_t ec_vec_len(const struct ec_vec *vec) { + if (vec == NULL) + return 0; + return vec->len; } @@ -207,13 +207,12 @@ void ec_vec_free(struct ec_vec *vec) int ec_vec_get(void *ptr, const struct ec_vec *vec, size_t idx) { - if (vec == NULL || idx >= vec->len) - return -EINVAL; + if (vec == NULL || idx >= vec->len) { + errno = EINVAL; + return -1; + } - if (vec->copy) - vec->copy(ptr, get_obj(vec, idx)); - else - memcpy(ptr, get_obj(vec, idx), vec->elt_size); + memcpy(ptr, get_obj(vec, idx), vec->elt_size); return 0; } diff --git a/lib/ecoli_vec.h b/lib/ecoli_vec.h index 493e453..d45db36 100644 --- a/lib/ecoli_vec.h +++ b/lib/ecoli_vec.h @@ -41,6 +41,7 @@ /* if NULL, default does nothing */ typedef void (*ec_vec_elt_free_t)(void *ptr); + /* if NULL, default is: * memcpy(dst, src, vec->elt_size) */ diff --git a/lib/main-readline.c b/lib/main-readline.c index 4601ae8..80ef88d 100644 --- a/lib/main-readline.c +++ b/lib/main-readline.c @@ -28,11 +28,13 @@ #define _GNU_SOURCE /* for asprintf */ #include #include +#include #include #include #include +#include #include #include #include @@ -324,6 +326,11 @@ int main(void) struct ec_parsed *p; char *line; + if (ec_init() < 0) { + fprintf(stderr, "cannot init ecoli: %s\n", strerror(errno)); + return 1; + } + if (create_commands() < 0) return 1; diff --git a/lib/main.c b/lib/main.c index aaf12b3..b44a47d 100644 --- a/lib/main.c +++ b/lib/main.c @@ -31,7 +31,9 @@ #include #include #include +#include +#include #include #include #include @@ -384,13 +386,17 @@ int main(int argc, char **argv) srandom(seed); + if (ec_init() < 0) { + fprintf(stderr, "cannot init ecoli: %s\n", strerror(errno)); + return 1; + } ec_log_fct_register(debug_log, NULL); /* register a new malloc to track memleaks */ TAILQ_INIT(&debug_alloc_hdr_list); if (ec_malloc_register(debug_malloc, debug_free, debug_realloc) < 0) { EC_LOG(EC_LOG_ERR, "cannot register new malloc\n"); - return -1; + return 1; } ret = 0; -- 2.20.1