# Tools
CC ?= cc
-CFLAGS += -std=c99 -D_GNU_SOURCE -Wall -fPIC -I$(abspath .)
+CFLAGS += -std=c99 -D_GNU_SOURCE -Wall -fPIC -I$(abspath .) -I$(abspath lib/generic)
CFLAGS += -DPACKAGE_VERSION="\"$(MAJOR).$(MINOR)\"" -DPREFIX="\"$(PREFIX)\"" -DMODULEDIR="\"$(MODULEDIR)\""
RM := rm -f
LN := ln -s
Data structures
~~~~~~~~~~~~~~~
-- Dynamic arrays
+* ``array`` - a set of simple macros to make working with dynamic arrays easier.
+* ``set`` - a `Crit-bit tree`_ simple implementation (public domain) that comes with tests.
+* ``map`` - key-value map implemented on top of ``set``.
API reference and examples
~~~~~~~~~~~~~~~~~~~~~~~~~~
.. doxygengroup:: generics
:project: libkresolve
+
+.. _`Crit-bit tree`: http://cr.yp.to/critbit.html
\ No newline at end of file
* @return 0 on success, <0 on failure
*/
#define array_del(array, i) \
- (i) < (array).len ? ((array).at[i] = (array).at[(array).len], (array).len -= 1, 0) : -1
+ (i) < (array).len ? ((array).len -= 1,(array).at[i] = (array).at[(array).len], 0) : -1
+
+/**
+ * Return last element of the array.
+ * @warning Undefined if the array is empty.
+ */
+#define array_tail(array) \
+ (array).at[(array).len - 1]
/** @} */
+++ /dev/null
-/*
- * critbit89 - A crit-bit tree implementation for strings in C89
- * Written by Jonas Gehring <jonas@jgehring.net>
- */
-
-
-#ifndef CRITBIT_H_
-#define CRITBIT_H_
-
-#include <stddef.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-/*! Main data structure */
-typedef struct {
- void *root;
- void *(*malloc)(size_t size, void *baton);
- void (*free)(void *ptr, void *baton);
- void *baton; /*! Passed to malloc() and free() */
-} cb_tree_t;
-
-/*! Creates an new, empty critbit tree */
-extern cb_tree_t cb_tree_make();
-
-/*! Returns non-zero if tree contains str */
-extern int cb_tree_contains(cb_tree_t *tree, const char *str);
-
-/*! Inserts str into tree, returns 0 on suceess */
-extern int cb_tree_insert(cb_tree_t *tree, const char *str);
-
-/*! Deletes str from the tree, returns 0 on suceess */
-extern int cb_tree_delete(cb_tree_t *tree, const char *str);
-
-/*! Clears the given tree */
-extern void cb_tree_clear(cb_tree_t *tree);
-
-/*! Calls callback for all strings in tree with the given prefix */
-extern int cb_tree_walk_prefixed(cb_tree_t *tree, const char *prefix,
- int (*callback)(const char *, void *), void *baton);
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* CRITBIT_H_ */
\ No newline at end of file
/*
* critbit89 - A crit-bit tree implementation for strings in C89
* Written by Jonas Gehring <jonas@jgehring.net>
- */
-
-/*
+ * Implemented key-value storing by Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
* The code makes the assumption that malloc returns pointers aligned at at
* least a two-byte boundary. Since the C standard requires that malloc return
* pointers that can store any type, there are no commonly-used toolchains for
* which this assumption is false.
+ *
+ * See https://github.com/agl/critbit/blob/master/critbit.pdf for reference.
*/
#include <errno.h>
#include <string.h>
#include <stdlib.h>
-#include "critbit.h"
+#include "map.h"
#ifdef _MSC_VER /* MSVC */
typedef unsigned __int8 uint8_t;
#include <stdint.h>
#endif
+typedef struct {
+ void* value;
+ uint8_t key[];
+} cb_data_t;
typedef struct {
void *child[2];
uint8_t otherbits;
} cb_node_t;
+/* Return true if ptr is internal node. */
+static inline int ref_is_internal(uint8_t *p)
+{
+ return 1 & (intptr_t)p;
+}
+
+/* Get internal node. */
+static inline cb_node_t *ref_get_internal(uint8_t *p)
+{
+ return (cb_node_t *)(p - 1);
+}
+
/* Standard memory allocation functions */
static void *malloc_std(size_t size, void *baton) {
(void)baton; /* Prevent compiler warnings */
}
/* Static helper functions */
-static void cbt_traverse_delete(cb_tree_t *tree, void *top)
+static void cbt_traverse_delete(map_t *map, void *top)
{
uint8_t *p = top;
- if (1 & (intptr_t)p) {
- cb_node_t *q = (void *)(p - 1);
- cbt_traverse_delete(tree, q->child[0]);
- cbt_traverse_delete(tree, q->child[1]);
- tree->free(q, tree->baton);
+ if (ref_is_internal(p)) {
+ cb_node_t *q = ref_get_internal(p);
+ cbt_traverse_delete(map, q->child[0]);
+ cbt_traverse_delete(map, q->child[1]);
+ map->free(q, map->baton);
} else {
- tree->free(p, tree->baton);
+ map->free(p, map->baton);
}
}
-static int cbt_traverse_prefixed(uint8_t *top,
+static int cbt_traverse_prefixed(void *top,
int (*callback)(const char *, void *), void *baton)
{
- if (1 & (intptr_t)top) {
- cb_node_t *q = (void *)(top - 1);
+ uint8_t *p = top;
+ cb_data_t *x = (cb_data_t *)top;
+
+ if (ref_is_internal(p)) {
+ cb_node_t *q = ref_get_internal(p);
int ret = 0;
ret = cbt_traverse_prefixed(q->child[0], callback, baton);
return 0;
}
- return (callback)((const char *)top, baton);
+ return (callback)((const char *)x->key, baton);
}
+static cb_data_t *cbt_make_data(map_t *map, const uint8_t *str, size_t len, void *value)
+{
+ cb_data_t *x = map->malloc(sizeof(cb_data_t) + len, map->baton);
+ if (x != NULL) {
+ x->value = value;
+ memcpy(x->key, str, len);
+ }
+ return x;
+}
-/*! Creates a new, empty critbit tree */
-cb_tree_t cb_tree_make()
+/*! Creates a new, empty critbit map */
+map_t map_make(void)
{
- cb_tree_t tree;
- tree.root = NULL;
- tree.malloc = &malloc_std;
- tree.free = &free_std;
- tree.baton = NULL;
- return tree;
+ map_t map;
+ map.root = NULL;
+ map.malloc = &malloc_std;
+ map.free = &free_std;
+ map.baton = NULL;
+ return map;
}
-/*! Returns non-zero if tree contains str */
-int cb_tree_contains(cb_tree_t *tree, const char *str)
+/*! Returns non-zero if map contains str */
+int map_contains(map_t *map, const char *str)
+{
+ return map_get(map, str) != NULL;
+}
+
+void *map_get(map_t *map, const char *str)
{
const uint8_t *ubytes = (void *)str;
const size_t ulen = strlen(str);
- uint8_t *p = tree->root;
+ uint8_t *p = map->root;
+ cb_data_t *x = NULL;
if (p == NULL) {
- return 0;
+ return NULL;
}
- while (1 & (intptr_t)p) {
- cb_node_t *q = (void *)(p - 1);
+ while (ref_is_internal(p)) {
+ cb_node_t *q = ref_get_internal(p);
uint8_t c = 0;
int direction;
p = q->child[direction];
}
- return (strcmp(str, (const char *)p) == 0);
+ x = (cb_data_t *)p;
+ if (strcmp(str, (const char *)x->key) == 0) {
+ return x->value;
+ }
+
+ return NULL;
}
-/*! Inserts str into tree, returns 0 on success */
-int cb_tree_insert(cb_tree_t *tree, const char *str)
+/*! Inserts str into map, returns 0 on success */
+int map_set(map_t *map, const char *str, void *value)
{
const uint8_t *const ubytes = (void *)str;
const size_t ulen = strlen(str);
- uint8_t *p = tree->root;
- uint8_t c, *x;
- uint32_t newbyte;
- uint32_t newotherbits;
- int direction, newdirection;
- cb_node_t *newnode;
- void **wherep;
+ uint8_t *p = map->root;
+ uint8_t c = 0, *x = NULL;
+ uint32_t newbyte = 0;
+ uint32_t newotherbits = 0;
+ int direction = 0, newdirection = 0;
+ cb_node_t *newnode = NULL;
+ cb_data_t *data = NULL;
+ void **wherep = NULL;
if (p == NULL) {
- x = tree->malloc(ulen + 1, tree->baton);
- if (x == NULL) {
+ map->root = cbt_make_data(map, (const uint8_t *)str, ulen + 1, value);
+ if (map->root == NULL) {
return ENOMEM;
}
- memcpy(x, str, ulen + 1);
- tree->root = x;
return 0;
}
- while (1 & (intptr_t)p) {
- cb_node_t *q = (void *)(p - 1);
+ while (ref_is_internal(p)) {
+ cb_node_t *q = ref_get_internal(p);
c = 0;
if (q->byte < ulen) {
c = ubytes[q->byte];
p = q->child[direction];
}
+ data = (cb_data_t *)p;
for (newbyte = 0; newbyte < ulen; ++newbyte) {
- if (p[newbyte] != ubytes[newbyte]) {
- newotherbits = p[newbyte] ^ ubytes[newbyte];
+ if (data->key[newbyte] != ubytes[newbyte]) {
+ newotherbits = data->key[newbyte] ^ ubytes[newbyte];
goto different_byte_found;
}
}
- if (p[newbyte] != 0) {
- newotherbits = p[newbyte];
+ if (data->key[newbyte] != 0) {
+ newotherbits = data->key[newbyte];
goto different_byte_found;
}
return 1;
newotherbits |= newotherbits >> 2;
newotherbits |= newotherbits >> 4;
newotherbits = (newotherbits & ~(newotherbits >> 1)) ^ 255;
- c = p[newbyte];
+ c = data->key[newbyte];
newdirection = (1 + (newotherbits | c)) >> 8;
- newnode = tree->malloc(sizeof(cb_node_t), tree->baton);
+ newnode = map->malloc(sizeof(cb_node_t), map->baton);
if (newnode == NULL) {
return ENOMEM;
}
- x = tree->malloc(ulen + 1, tree->baton);
+ x = (uint8_t *)cbt_make_data(map, ubytes, ulen + 1, value);
if (x == NULL) {
- tree->free(newnode, tree->baton);
+ map->free(newnode, map->baton);
return ENOMEM;
}
- memcpy(x, ubytes, ulen + 1);
newnode->byte = newbyte;
newnode->otherbits = newotherbits;
newnode->child[1 - newdirection] = x;
- /* Insert into tree */
- wherep = &tree->root;
+ /* Insert into map */
+ wherep = &map->root;
for (;;) {
cb_node_t *q;
p = *wherep;
- if (!(1 & (intptr_t)p)) {
+ if (!ref_is_internal(p)) {
break;
}
- q = (void *)(p - 1);
+ q = ref_get_internal(p);
if (q->byte > newbyte) {
break;
}
return 0;
}
-/*! Deletes str from the tree, returns 0 on success */
-int cb_tree_delete(cb_tree_t *tree, const char *str)
+/*! Deletes str from the map, returns 0 on success */
+int map_del(map_t *map, const char *str)
{
const uint8_t *ubytes = (void *)str;
const size_t ulen = strlen(str);
- uint8_t *p = tree->root;
- void **wherep = 0, **whereq = 0;
- cb_node_t *q = 0;
+ uint8_t *p = map->root;
+ void **wherep = NULL, **whereq = NULL;
+ cb_node_t *q = NULL;
+ cb_data_t *data = NULL;
int direction = 0;
- if (tree->root == NULL) {
+ if (map->root == NULL) {
return 1;
}
- wherep = &tree->root;
+ wherep = &map->root;
- while (1 & (intptr_t)p) {
+ while (ref_is_internal(p)) {
uint8_t c = 0;
whereq = wherep;
- q = (void *)(p - 1);
+ q = ref_get_internal(p);
if (q->byte < ulen) {
c = ubytes[q->byte];
p = *wherep;
}
- if (strcmp(str, (const char *)p) != 0) {
+ data = (cb_data_t *)p;
+ if (strcmp(str, (const char *)data->key) != 0) {
return 1;
}
- tree->free(p, tree->baton);
+ map->free(p, map->baton);
if (!whereq) {
- tree->root = NULL;
+ map->root = NULL;
return 0;
}
*whereq = q->child[1 - direction];
- tree->free(q, tree->baton);
+ map->free(q, map->baton);
return 0;
}
-/*! Clears the given tree */
-void cb_tree_clear(cb_tree_t *tree)
+/*! Clears the given map */
+void map_clear(map_t *map)
{
- if (tree->root) {
- cbt_traverse_delete(tree, tree->root);
+ if (map->root) {
+ cbt_traverse_delete(map, map->root);
}
- tree->root = NULL;
+ map->root = NULL;
}
-/*! Calls callback for all strings in tree with the given prefix */
-int cb_tree_walk_prefixed(cb_tree_t *tree, const char *prefix,
+/*! Calls callback for all strings in map with the given prefix */
+int map_walk_prefixed(map_t *map, const char *prefix,
int (*callback)(const char *, void *), void *baton)
{
const uint8_t *ubytes = (void *)prefix;
const size_t ulen = strlen(prefix);
- uint8_t *p = tree->root;
- uint8_t *top = p;
+ uint8_t *p = map->root;
+ uint8_t *top = (uint8_t *)p;
+ cb_data_t *data = NULL;
if (p == NULL) {
return 0;
}
- while (1 & (intptr_t)p) {
- cb_node_t *q = (void *)(p - 1);
+ while (ref_is_internal(p)) {
+ cb_node_t *q = ref_get_internal(p);
uint8_t c = 0;
int direction;
}
}
- if (strlen((const char *)p) < ulen || memcmp(p, prefix, ulen) != 0) {
- /* No strings match */
- return 0;
+ data = (cb_data_t *)p;
+ if (strlen((const char *)data->key) < ulen || memcmp(data->key, prefix, ulen) != 0) {
+ return 0; /* No strings match */
}
return cbt_traverse_prefixed(top, callback, baton);
--- /dev/null
+/*
+ * critbit89 - A crit-bit map implementation for strings in C89
+ * Written by Jonas Gehring <jonas@jgehring.net>
+ */
+
+/**
+ * Generics - A Crit-bit tree 'map' implementation.
+ *
+ * @warning If the user provides a custom allocator, it must return addresses aligned to 2B boundary.
+ *
+ * Example usage:
+ *
+ * map_t map = map_make();
+ *
+ * // Custom allocator (optional)
+ * map.malloc = &mymalloc;
+ * map.baton = &mymalloc_context;
+ *
+ * // Insert keys
+ * if (map_set(&map, "princess") != 0 ||
+ * map_set(&map, "prince") != 0 ||
+ * map_set(&map, "leia") != 0) {
+ * fail();
+ * }
+ *
+ * // Test membership
+ * if (map_contains(&map, "leia")) {
+ * success();
+ * }
+ *
+ * // Prefix search
+ * int i = 0;
+ * int count(const char *s, void *n) { (*(int *)n)++; return 0; }
+ * if (map_walk_prefixed(map, "princ", count, &i) == 0) {
+ * printf("%d matches\n", i);
+ * }
+ *
+ * // Delete
+ * if (map_del(&map, "badkey") != 0) {
+ * fail(); // No such key
+ * }
+ *
+ * // Clear the map
+ * map_clear(&map);
+ *
+ * \addtogroup generics
+ * @{
+ */
+
+#pragma once
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*! Main data structure */
+typedef struct {
+ void *root;
+ void *(*malloc)(size_t size, void *baton);
+ void (*free)(void *ptr, void *baton);
+ void *baton; /*! Passed to malloc() and free() */
+} map_t;
+
+/*! Creates an new, empty critbit map */
+map_t map_make(void);
+
+/*! Returns non-zero if map contains str */
+int map_contains(map_t *map, const char *str);
+
+/*! Returns value if map contains str */
+void *map_get(map_t *map, const char *str);
+
+/*! Inserts str into map, returns 0 on suceess */
+int map_set(map_t *map, const char *str, void *val);
+
+/*! Deletes str from the map, returns 0 on suceess */
+int map_del(map_t *map, const char *str);
+
+/*! Clears the given map */
+void map_clear(map_t *map);
+
+/*! Calls callback for all strings in map with the given prefix */
+int map_walk_prefixed(map_t *map, const char *prefix,
+ int (*callback)(const char *, void *), void *baton);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+/** @} */
\ No newline at end of file
--- /dev/null
+/**
+ * Generics - A Crit-bit set implementation.
+ *
+ * @note The API is based on @fn map.h, see it for more examples.
+ *
+ * Example usage:
+ *
+ * set_t set = set_make();
+ *
+ * // Insert keys
+ * if (set_add(&set, "princess") != 0 ||
+ * set_add(&set, "prince") != 0 ||
+ * set_add(&set, "leia") != 0) {
+ * fail();
+ * }
+ *
+ * // Test membership
+ * if (set_contains(&set, "leia")) {
+ * success();
+ * }
+ *
+ * // Prefix search
+ * int i = 0;
+ * int count(const char *s, void *n) { (*(int *)n)++; return 0; }
+ * if (set_walk_prefixed(set, "princ", count, &i) == 0) {
+ * printf("%d matches\n", i);
+ * }
+ *
+ * // Delete
+ * if (set_del(&set, "badkey") != 0) {
+ * fail(); // No such key
+ * }
+ *
+ * // Clear the set
+ * set_clear(&set);
+ *
+ * \addtogroup generics
+ * @{
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include "map.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef map_t set_t;
+typedef int (set_walk_cb)(const char *, void *);
+
+/*! Creates an new, empty critbit set */
+#define set_make() \
+ map_make()
+
+/*! Returns non-zero if set contains str */
+#define set_contains(set, str) \
+ map_contains((set), (str))
+
+/*! Inserts str into set, returns 0 on suceess */
+#define set_add(set, str) \
+ map_set((set), (str), (void *)1)
+
+/*! Deletes str from the set, returns 0 on suceess */
+#define set_del(set, str) \
+ map_del((set), (str))
+
+/*! Clears the given set */
+#define set_clear(set) \
+ map_clear(set)
+
+/*! Calls callback for all strings in set with the given prefix */
+#define set_walk_prefixed(set, prefix, callback, baton) \
+ map_walk_prefixed((set), (prefix), (callback), baton)
+
+
+#ifdef __cplusplus
+}
+#endif
+
+/** @} */
\ No newline at end of file
libkresolve_SOURCES := \
- lib/generic/critbit.c \
+ lib/generic/map.c \
lib/layer/iterate.c \
lib/layer/itercache.c \
lib/utils.c \
libkresolve_HEADERS := \
lib/generic/array.h \
- lib/generic/critbit.h \
+ lib/generic/map.h \
+ lib/generic/set.h \
lib/layer.h \
lib/utils.h \
lib/nsrep.h \
#
tests_BIN := \
- test_critbit \
- test_generics \
+ test_set \
+ test_map \
+ test_array \
test_utils \
test_module \
test_rplan \
assert_int_equal(arr.len, 0);
assert_int_equal(array_push(arr, 5), 0);
assert_int_equal(arr.at[0], 5);
+ assert_int_equal(array_tail(arr), 5);
array_clear(arr);
/* Reserve capacity and fill. */
+++ /dev/null
-/*
- * critbit89 - A crit-bit tree implementation for strings in C89
- * Written by Jonas Gehring <jonas@jgehring.net>
- */
-
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "critbit.h"
-
-
-/*
- * Sample dictionary: 100 random words from /usr/share/dict/words
- * Generated using random.org:
- * MAX=`wc -l < /usr/share/dict/words | tr -d " "`
- * for i in `curl "http://www.random.org/integers/?num=100&min=1&max=$MAX&col=1&base=10&format=plain&rnd=new"`; do
- * nl /usr/share/dict/words | grep -w $i | tr -d "0-9\t "
- * done
- */
-static const char *dict[] = {
- "catagmatic", "prevaricator", "statoscope", "workhand", "benzamide",
- "alluvia", "fanciful", "bladish", "Tarsius", "unfast", "appropriative",
- "seraphically", "monkeypod", "deflectometer", "tanglesome", "zodiacal",
- "physiologically", "economizer", "forcepslike", "betrumpet",
- "Danization", "broadthroat", "randir", "usherette", "nephropyosis",
- "hematocyanin", "chrysohermidin", "uncave", "mirksome", "podophyllum",
- "siphonognathous", "indoor", "featheriness", "forwardation",
- "archruler", "soricoid", "Dailamite", "carmoisin", "controllability",
- "unpragmatical", "childless", "transumpt", "productive",
- "thyreotoxicosis", "oversorrow", "disshadow", "osse", "roar",
- "pantomnesia", "talcer", "hydrorrhoea", "Satyridae", "undetesting",
- "smoothbored", "widower", "sivathere", "pendle", "saltation",
- "autopelagic", "campfight", "unexplained", "Macrorhamphosus",
- "absconsa", "counterflory", "interdependent", "triact", "reconcentration",
- "oversharpness", "sarcoenchondroma", "superstimulate", "assessory",
- "pseudepiscopacy", "telescopically", "ventriloque", "politicaster",
- "Caesalpiniaceae", "inopportunity", "Helion", "uncompatible",
- "cephaloclasia", "oversearch", "Mahayanistic", "quarterspace",
- "bacillogenic", "hamartite", "polytheistical", "unescapableness",
- "Pterophorus", "cradlemaking", "Hippoboscidae", "overindustrialize",
- "perishless", "cupidity", "semilichen", "gadge", "detrimental",
- "misencourage", "toparchia", "lurchingly", "apocatastasis"
-};
-
-static int tnum = 0;
-
-
-/* Insertions */
-static void test_insert(cb_tree_t *tree)
-{
- int dict_size = sizeof(dict) / sizeof(const char *);
- int i;
-
- for (i = 0; i < dict_size; i++) {
- if (cb_tree_insert(tree, dict[i]) != 0) {
- fprintf(stderr, "Insertion failed\n");
- abort();
- }
- }
-}
-
-/* Insertion of duplicate element */
-static void test_insert_dup(cb_tree_t *tree)
-{
- int dict_size = sizeof(dict) / sizeof(const char *);
- int i;
-
- for (i = 0; i < dict_size; i++) {
- if (!cb_tree_contains(tree, dict[i])) {
- continue;
- }
- if (cb_tree_insert(tree, dict[i]) != 1) {
- fprintf(stderr, "Insertion of duplicate '%s' should fail\n", dict[i]);
- abort();
- }
- }
-}
-
-/* Searching */
-static void test_contains(cb_tree_t *tree)
-{
- char *in;
- const char *notin = "not in tree";
-
- in = malloc(strlen(dict[23])+1);
- strcpy(in, dict[23]);
-
- if (cb_tree_contains(tree, in) != 1) {
- fprintf(stderr, "Tree should contain '%s'\n", in);
- abort();
- }
- if (cb_tree_contains(tree, notin) != 0) {
- fprintf(stderr, "Tree should not contain '%s'\n", notin);
- abort();
- }
- if (cb_tree_contains(tree, "") != 0) {
- fprintf(stderr, "Tree should not contain empty string\n");
- abort();
- }
- in[strlen(in)/2] = '\0';
- if (cb_tree_contains(tree, in) != 0) {
- fprintf(stderr, "Tree should not contain prefix of '%s'\n", in);
- abort();
- }
-
- free(in);
-}
-
-/* Count number of items */
-static int count_cb(const char *s, void *n) { (*(int *)n)++; return 0; }
-static void test_complete(cb_tree_t *tree, int n)
-{
- int i = 0;
- if (cb_tree_walk_prefixed(tree, "", count_cb, &i) != 0) {
- fprintf(stderr, "Walking with empty prefix failed\n");
- abort();
- }
- if (i != n) {
- fprintf(stderr, "%d items expected, but %d walked\n", n, i);
- abort();
- }
-}
-
-/* Deletion */
-static void test_delete(cb_tree_t *tree)
-{
- if (cb_tree_delete(tree, dict[91]) != 0) {
- fprintf(stderr, "Deletion failed\n");
- abort();
- }
- if (cb_tree_delete(tree, "most likely not in tree") != 1) {
- fprintf(stderr, "Deletion of item not in tree should fail\n");
- abort();
- }
-}
-
-/* Complete deletion */
-static void test_delete_all(cb_tree_t *tree)
-{
- int dict_size = sizeof(dict) / sizeof(const char *);
- int i;
-
- for (i = 0; i < dict_size; i++) {
- if (!cb_tree_contains(tree, dict[i])) {
- continue;
- }
- if (cb_tree_delete(tree, dict[i]) != 0) {
- fprintf(stderr, "Deletion of '%s' failed\n", dict[i]);
- abort();
- }
- }
-}
-
-/* Fake allocator */
-static void *fake_malloc(size_t s, void *b) { return NULL; }
-static void test_allocator(cb_tree_t *unused)
-{
- cb_tree_t tree = cb_tree_make();
- tree.malloc = fake_malloc;
- if (cb_tree_insert(&tree, dict[0]) != ENOMEM) {
- fprintf(stderr, "ENOMEM failure expected\n");
- abort();
- }
-}
-
-/* Empty tree */
-static void test_empty(cb_tree_t *tree)
-{
- if (cb_tree_contains(tree, dict[1]) != 0) {
- fprintf(stderr, "Empty tree expected\n");
- abort();
- }
- if (cb_tree_delete(tree, dict[1]) == 0) {
- fprintf(stderr, "Empty tree expected\n");
- abort();
- }
-}
-
-/* Prefix walking */
-static void test_prefixes(cb_tree_t *tree)
-{
- int i = 0;
- if ((cb_tree_insert(tree, "1str") != 0) ||
- (cb_tree_insert(tree, "11str2") != 0) ||
- (cb_tree_insert(tree, "12str") != 0) ||
- (cb_tree_insert(tree, "11str") != 0)) {
- fprintf(stderr, "Insertion failed\n");
- abort();
- }
-
- if (cb_tree_walk_prefixed(tree, "11", count_cb, &i) != 0) {
- fprintf(stderr, "Walking with prefix failed\n");
- abort();
- }
- if (i != 2) {
- fprintf(stderr, "2 items expected, but %d walked\n", i);
- abort();
- }
-
- i = 0;
- if (cb_tree_walk_prefixed(tree, "13", count_cb, &i) != 0) {
- fprintf(stderr, "Walking with non-matching prefix failed\n");
- abort();
- }
- if (i != 0) {
- fprintf(stderr, "0 items expected, but %d walked\n", i);
- abort();
- }
-
- i = 0;
- if (cb_tree_walk_prefixed(tree, "12345678", count_cb, &i) != 0) {
- fprintf(stderr, "Walking with long prefix failed\n");
- abort();
- }
- if (i != 0) {
- fprintf(stderr, "0 items expected, but %d walked\n", i);
- abort();
- }
-
- i = 0;
- if (cb_tree_walk_prefixed(tree, "11str", count_cb, &i) != 0) {
- fprintf(stderr, "Walking with exactly matching prefix failed\n");
- abort();
- }
- if (i != 2) {
- fprintf(stderr, "2 items expected, but %d walked\n", i);
- abort();
- }
-}
-
-
-/* Program entry point */
-int main(int argc, char **argv)
-{
- cb_tree_t tree = cb_tree_make();
-
- printf("%d ", ++tnum); fflush(stdout);
- test_insert(&tree);
-
- printf("%d ", ++tnum); fflush(stdout);
- test_complete(&tree, sizeof(dict) / sizeof(const char *));
-
- printf("%d ", ++tnum); fflush(stdout);
- test_insert_dup(&tree);
-
- printf("%d ", ++tnum); fflush(stdout);
- test_contains(&tree);
-
- printf("%d ", ++tnum); fflush(stdout);
- test_delete(&tree);
-
- printf("%d ", ++tnum); fflush(stdout);
- cb_tree_clear(&tree);
- test_insert(&tree);
- test_complete(&tree, sizeof(dict) / sizeof(const char *));
-
- printf("%d ", ++tnum); fflush(stdout);
- test_delete_all(&tree);
-
- printf("%d ", ++tnum); fflush(stdout);
- test_complete(&tree, 0);
-
- printf("%d ", ++tnum); fflush(stdout);
- test_allocator(&tree);
-
- printf("%d ", ++tnum); fflush(stdout);
- cb_tree_clear(&tree);
- test_empty(&tree);
-
- printf("%d ", ++tnum); fflush(stdout);
- test_insert(&tree);
- test_prefixes(&tree);
-
- cb_tree_clear(&tree);
- printf("ok\n");
- return 0;
-}
\ No newline at end of file
--- /dev/null
+/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tests/test.h"
+#include "lib/generic/map.h"
+
+/*
+ * Sample dictionary
+ */
+static const char *dict[] = {
+ "catagmatic", "prevaricator", "statoscope", "workhand", "benzamide",
+ "alluvia", "fanciful", "bladish", "Tarsius", "unfast", "appropriative",
+ "seraphically", "monkeypod", "deflectometer", "tanglesome", "zodiacal",
+ "physiologically", "economizer", "forcepslike", "betrumpet",
+ "Danization", "broadthroat", "randir", "usherette", "nephropyosis",
+ "hematocyanin", "chrysohermidin", "uncave", "mirksome", "podophyllum",
+ "siphonognathous", "indoor", "featheriness", "forwardation",
+ "archruler", "soricoid", "Dailamite", "carmoisin", "controllability",
+ "unpragmatical", "childless", "transumpt", "productive",
+ "thyreotoxicosis", "oversorrow", "disshadow", "osse", "roar",
+ "pantomnesia", "talcer", "hydrorrhoea", "Satyridae", "undetesting",
+ "smoothbored", "widower", "sivathere", "pendle", "saltation",
+ "autopelagic", "campfight", "unexplained", "Macrorhamphosus",
+ "absconsa", "counterflory", "interdependent", "triact", "reconcentration",
+ "oversharpness", "sarcoenchondroma", "superstimulate", "assessory",
+ "pseudepiscopacy", "telescopically", "ventriloque", "politicaster",
+ "Caesalpiniaceae", "inopportunity", "Helion", "uncompatible",
+ "cephaloclasia", "oversearch", "Mahayanistic", "quarterspace",
+ "bacillogenic", "hamartite", "polytheistical", "unescapableness",
+ "Pterophorus", "cradlemaking", "Hippoboscidae", "overindustrialize",
+ "perishless", "cupidity", "semilichen", "gadge", "detrimental",
+ "misencourage", "toparchia", "lurchingly", "apocatastasis"
+};
+
+/* Insertions */
+static void test_insert(void **state)
+{
+ map_t *tree = *state;
+ int dict_size = sizeof(dict) / sizeof(const char *);
+ int i;
+
+ for (i = 0; i < dict_size; i++) {
+ assert_int_equal(map_set(tree, dict[i], (void *)dict[i]), 0);
+ }
+}
+
+/* Searching */
+static void test_get(void **state)
+{
+ map_t *tree = *state;
+ char *in;
+ const char *notin = "not in tree";
+
+ in = malloc(strlen(dict[23])+1);
+ strcpy(in, dict[23]);
+
+ assert_int_equal(map_get(tree, in), dict[23]);
+ assert_int_equal(map_get(tree, notin), NULL);
+ assert_int_equal(map_get(tree, ""), NULL);
+ in[strlen(in)/2] = '\0';
+ assert_int_equal(map_get(tree, in), NULL);
+
+ free(in);
+}
+
+/* Deletion */
+static void test_delete(void **state)
+{
+ map_t *tree = *state;
+ assert_int_equal(map_del(tree, dict[91]), 0);
+ assert_false(map_contains(tree, dict[91]));
+ assert_int_equal(map_del(tree, "most likely not in tree"), 1);
+}
+
+static void test_init(void **state)
+{
+ static map_t tree;
+ tree = map_make();
+ *state = &tree;
+ assert_non_null(*state);
+}
+
+static void test_deinit(void **state)
+{
+ map_t *tree = *state;
+ map_clear(tree);
+}
+
+/* Program entry point */
+int main(int argc, char **argv)
+{
+ const UnitTest tests[] = {
+ group_test_setup(test_init),
+ unit_test(test_insert),
+ unit_test(test_get),
+ unit_test(test_delete),
+ group_test_teardown(test_deinit)
+ };
+
+ return run_group_tests(tests);
+}
\ No newline at end of file
--- /dev/null
+/*
+ * critbit89 - A crit-bit tree implementation for strings in C89
+ * Written by Jonas Gehring <jonas@jgehring.net>
+ */
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tests/test.h"
+#include "lib/generic/set.h"
+
+
+/*
+ * Sample dictionary: 100 random words from /usr/share/dict/words
+ * Generated using random.org:
+ * MAX=`wc -l < /usr/share/dict/words | tr -d " "`
+ * for i in `curl "http://www.random.org/integers/?num=100&min=1&max=$MAX&col=1&base=10&format=plain&rnd=new"`; do
+ * nl /usr/share/dict/words | grep -w $i | tr -d "0-9\t "
+ * done
+ */
+static const char *dict[] = {
+ "catagmatic", "prevaricator", "statoscope", "workhand", "benzamide",
+ "alluvia", "fanciful", "bladish", "Tarsius", "unfast", "appropriative",
+ "seraphically", "monkeypod", "deflectometer", "tanglesome", "zodiacal",
+ "physiologically", "economizer", "forcepslike", "betrumpet",
+ "Danization", "broadthroat", "randir", "usherette", "nephropyosis",
+ "hematocyanin", "chrysohermidin", "uncave", "mirksome", "podophyllum",
+ "siphonognathous", "indoor", "featheriness", "forwardation",
+ "archruler", "soricoid", "Dailamite", "carmoisin", "controllability",
+ "unpragmatical", "childless", "transumpt", "productive",
+ "thyreotoxicosis", "oversorrow", "disshadow", "osse", "roar",
+ "pantomnesia", "talcer", "hydrorrhoea", "Satyridae", "undetesting",
+ "smoothbored", "widower", "sivathere", "pendle", "saltation",
+ "autopelagic", "campfight", "unexplained", "Macrorhamphosus",
+ "absconsa", "counterflory", "interdependent", "triact", "reconcentration",
+ "oversharpness", "sarcoenchondroma", "superstimulate", "assessory",
+ "pseudepiscopacy", "telescopically", "ventriloque", "politicaster",
+ "Caesalpiniaceae", "inopportunity", "Helion", "uncompatible",
+ "cephaloclasia", "oversearch", "Mahayanistic", "quarterspace",
+ "bacillogenic", "hamartite", "polytheistical", "unescapableness",
+ "Pterophorus", "cradlemaking", "Hippoboscidae", "overindustrialize",
+ "perishless", "cupidity", "semilichen", "gadge", "detrimental",
+ "misencourage", "toparchia", "lurchingly", "apocatastasis"
+};
+
+/* Insertions */
+static void test_insert(void **state)
+{
+ set_t *set = *state;
+ int dict_size = sizeof(dict) / sizeof(const char *);
+ int i;
+
+ for (i = 0; i < dict_size; i++) {
+ assert_int_equal(set_add(set, dict[i]), 0);
+ }
+}
+
+/* Insertion of duplicate element */
+static void test_insert_dup(void **state)
+{
+ set_t *set = *state;
+ int dict_size = sizeof(dict) / sizeof(const char *);
+ int i;
+
+ for (i = 0; i < dict_size; i++) {
+ if (!set_contains(set, dict[i])) {
+ continue;
+ }
+ assert_int_equal(set_add(set, dict[i]), 1);
+ }
+}
+
+/* Searching */
+static void test_contains(void **state)
+{
+ set_t *set = *state;
+ char *in;
+ const char *notin = "not in set";
+
+ in = malloc(strlen(dict[23])+1);
+ strcpy(in, dict[23]);
+
+ assert_true(set_contains(set, in));
+ assert_false(set_contains(set, notin));
+ assert_false(set_contains(set, ""));
+ in[strlen(in)/2] = '\0';
+ assert_false(set_contains(set, in));
+
+ free(in);
+}
+
+/* Count number of items */
+static int count_cb(const char *s, void *n) { (*(int *)n)++; return 0; }
+static void test_complete(set_t *set, int n)
+{
+ int i = 0;
+ if (set_walk_prefixed(set, "", count_cb, &i) != 0) {
+ abort();
+ }
+ if (i != n) {
+ abort();
+ }
+}
+static void test_complete_full(void **state) { test_complete(*state, sizeof(dict) / sizeof(const char *)); }
+static void test_complete_zero(void **state) { test_complete(*state, 0); }
+
+/* Deletion */
+static void test_delete(void **state)
+{
+ set_t *set = *state;
+ assert_int_equal(set_del(set, dict[91]), 0);
+ assert_int_equal(set_del(set, "most likely not in set"), 1);
+}
+
+/* Complete deletion */
+static void test_delete_all(void **state)
+{
+ set_t *set = *state;
+ int dict_size = sizeof(dict) / sizeof(const char *);
+ int i;
+
+ for (i = 0; i < dict_size; i++) {
+ if (!set_contains(set, dict[i])) {
+ continue;
+ }
+ assert_int_equal(set_del(set, dict[i]), 0);
+ }
+}
+
+/* Fake allocator */
+static void *fake_malloc(size_t s, void *b) { return NULL; }
+static void test_allocator(void **state)
+{
+ set_t set = set_make();
+ set.malloc = fake_malloc;
+ assert_int_equal(set_add(&set, dict[0]), ENOMEM);
+}
+
+/* Empty set */
+static void test_empty(void **state)
+{
+ set_t *set = *state;
+ assert_int_equal(set_contains(set, dict[1]), 0);
+ assert_int_not_equal(set_del(set, dict[1]), 0);
+}
+
+/* Prefix walking */
+static void test_prefixes(void **state)
+{
+ set_t *set = *state;
+ int i = 0;
+ if ((set_add(set, "1str") != 0) ||
+ (set_add(set, "11str2") != 0) ||
+ (set_add(set, "12str") != 0) ||
+ (set_add(set, "11str") != 0)) {
+ assert_int_equal(1, 0);
+ }
+
+ assert_int_equal(set_walk_prefixed(set, "11", count_cb, &i), 0);
+ assert_int_equal(i, 2);
+ i = 0;
+ assert_int_equal(set_walk_prefixed(set, "13", count_cb, &i), 0);
+ assert_int_equal(i, 0);
+ i = 0;
+ assert_int_equal(set_walk_prefixed(set, "12345678", count_cb, &i), 0);
+ assert_int_equal(i, 0);
+ i = 0;
+ assert_int_equal(set_walk_prefixed(set, "11str", count_cb, &i), 0);
+ assert_int_equal(i, 2);
+}
+
+static void test_clear(void **state)
+{
+ set_t *set = *state;
+ set_clear(set);
+}
+
+static void test_init(void **state)
+{
+ static set_t set;
+ set = set_make();
+ *state = &set;
+ assert_non_null(*state);
+}
+
+static void test_deinit(void **state)
+{
+ set_t *set = *state;
+ set_clear(set);
+}
+
+/* Program entry point */
+int main(int argc, char **argv)
+{
+ const UnitTest tests[] = {
+ group_test_setup(test_init),
+ unit_test(test_insert),
+ unit_test(test_complete_full),
+ unit_test(test_insert_dup),
+ unit_test(test_contains),
+ unit_test(test_delete),
+ unit_test(test_clear),
+ unit_test(test_insert),
+ unit_test(test_complete_full),
+ unit_test(test_delete_all),
+ unit_test(test_complete_zero),
+ unit_test(test_allocator),
+ unit_test(test_clear),
+ unit_test(test_empty),
+ unit_test(test_insert),
+ unit_test(test_prefixes),
+ group_test_teardown(test_deinit)
+ };
+
+ return run_group_tests(tests);
+}
\ No newline at end of file