save
authorOlivier Matz <zer0@droids-corp.org>
Thu, 23 Nov 2017 19:38:46 +0000 (20:38 +0100)
committerOlivier Matz <zer0@droids-corp.org>
Thu, 23 Nov 2017 19:38:46 +0000 (20:38 +0100)
20 files changed:
lib/Makefile
lib/ecoli_assert.c [new file with mode: 0644]
lib/ecoli_assert.h [new file with mode: 0644]
lib/ecoli_init.c [new file with mode: 0644]
lib/ecoli_init.h [new file with mode: 0644]
lib/ecoli_keyval.c
lib/ecoli_keyval.h
lib/ecoli_murmurhash.c [new file with mode: 0644]
lib/ecoli_murmurhash.h [new file with mode: 0644]
lib/ecoli_node_any.c [new file with mode: 0644]
lib/ecoli_node_any.h [new file with mode: 0644]
lib/ecoli_string.c [new file with mode: 0644]
lib/ecoli_string.h [new file with mode: 0644]
lib/ecoli_strvec.c
lib/ecoli_test.c
lib/ecoli_test.h
lib/ecoli_vec.c
lib/ecoli_vec.h
lib/main-readline.c
lib/main.c

index b258752..2bf8ec0 100644 (file)
@@ -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 (file)
index 0000000..8b564a7
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * 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 <stdarg.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <ecoli_assert.h>
+
+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 (file)
index 0000000..61989e9
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * 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 <stdbool.h>
+
+/**
+ * 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 (file)
index 0000000..f91156e
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include <ecoli_init.h>
+
+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 (file)
index 0000000..9166cdc
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * 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 <sys/queue.h>
+
+#include <ecoli_log.h>
+#include <ecoli_node.h>
+
+#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
index e80dd08..9baba5f 100644 (file)
  */
 
 #include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
 #include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
 #include <assert.h>
 #include <errno.h>
+#include <fcntl.h>
 
+#include <ecoli_init.h>
 #include <ecoli_malloc.h>
 #include <ecoli_log.h>
 #include <ecoli_test.h>
+#include <ecoli_murmurhash.h>
 #include <ecoli_keyval.h>
 
+#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 */
index 30232d0..3d739ff 100644 (file)
  * 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 (file)
index 0000000..0efa3b9
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * 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 <stdint.h>
+
+#include <ecoli_murmurhash.h>
+
+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 (file)
index 0000000..942c0b3
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * 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 <stdint.h>
+
+/** 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 (file)
index 0000000..7fef0c1
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
+#include <ecoli_strvec.h>
+#include <ecoli_node.h>
+#include <ecoli_parsed.h>
+#include <ecoli_completed.h>
+#include <ecoli_node_any.h>
+
+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 (file)
index 0000000..e52cbe9
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * 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 (file)
index 0000000..49e4a37
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * 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 <stddef.h>
+#include <string.h>
+
+#include <ecoli_string.h>
+
+/* 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 (file)
index 0000000..ae92b4d
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * 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 <stddef.h>
+
+/* 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
index 32208ad..53017fb 100644 (file)
 #include <errno.h>
 
 #include <ecoli_malloc.h>
-#include <ecoli_strvec.h>
 #include <ecoli_test.h>
 #include <ecoli_log.h>
+#include <ecoli_vec.h>
+#include <ecoli_strvec.h>
 
 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");
 
index 53cdf62..9cf216d 100644 (file)
@@ -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);
index 1fa8480..1cebe95 100644 (file)
@@ -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);    \
index 871911d..8126526 100644 (file)
@@ -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;
 }
index 493e453..d45db36 100644 (file)
@@ -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)
  */
index 4601ae8..80ef88d 100644 (file)
 #define _GNU_SOURCE /* for asprintf */
 #include <stdlib.h>
 #include <stdio.h>
+#include <errno.h>
 #include <assert.h>
 
 #include <readline/readline.h>
 #include <readline/history.h>
 
+#include <ecoli_init.h>
 #include <ecoli_node.h>
 #include <ecoli_parsed.h>
 #include <ecoli_completed.h>
@@ -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;
 
index aaf12b3..b44a47d 100644 (file)
@@ -31,7 +31,9 @@
 #include <getopt.h>
 #include <limits.h>
 #include <execinfo.h>
+#include <errno.h>
 
+#include <ecoli_init.h>
 #include <ecoli_log.h>
 #include <ecoli_test.h>
 #include <ecoli_malloc.h>
@@ -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;