--- /dev/null
+/*
+ * critbit89 - A crit-bit tree implementation for strings in C89
+ * Written by Jonas Gehring <jonas@jgehring.net>
+ */
+
+/*
+ * 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.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "critbit.h"
+
+#ifdef _MSC_VER /* MSVC */
+ typedef unsigned __int8 uint8_t;
+ typedef unsigned __int32 uint32_t;
+ #ifdef _WIN64
+ typedef signed __int64 intptr_t;
+ #else
+ typedef _W64 signed int intptr_t;
+ #endif
+#else /* Not MSVC */
+ #include <stdint.h>
+#endif
+
+
+typedef struct {
+ void *child[2];
+ uint32_t byte;
+ uint8_t otherbits;
+} cb_node_t;
+
+/* Standard memory allocation functions */
+static void *malloc_std(size_t size, void *baton) {
+ (void)baton; /* Prevent compiler warnings */
+ return malloc(size);
+}
+
+static void free_std(void *ptr, void *baton) {
+ (void)baton; /* Prevent compiler warnings */
+ free(ptr);
+}
+
+/* Static helper functions */
+static void cbt_traverse_delete(cb_tree_t *tree, 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);
+ } else {
+ tree->free(p, tree->baton);
+ }
+}
+
+static int cbt_traverse_prefixed(uint8_t *top,
+ int (*callback)(const char *, void *), void *baton)
+{
+ if (1 & (intptr_t)top) {
+ cb_node_t *q = (void *)(top - 1);
+ int ret = 0;
+
+ ret = cbt_traverse_prefixed(q->child[0], callback, baton);
+ if (ret != 0) {
+ return ret;
+ }
+ ret = cbt_traverse_prefixed(q->child[1], callback, baton);
+ if (ret != 0) {
+ return ret;
+ }
+ return 0;
+ }
+
+ return (callback)((const char *)top, baton);
+}
+
+
+/*! Creates a new, empty critbit tree */
+cb_tree_t cb_tree_make()
+{
+ cb_tree_t tree;
+ tree.root = NULL;
+ tree.malloc = &malloc_std;
+ tree.free = &free_std;
+ tree.baton = NULL;
+ return tree;
+}
+
+/*! Returns non-zero if tree contains str */
+int cb_tree_contains(cb_tree_t *tree, const char *str)
+{
+ const uint8_t *ubytes = (void *)str;
+ const size_t ulen = strlen(str);
+ uint8_t *p = tree->root;
+
+ if (p == NULL) {
+ return 0;
+ }
+
+ while (1 & (intptr_t)p) {
+ cb_node_t *q = (void *)(p - 1);
+ uint8_t c = 0;
+ int direction;
+
+ if (q->byte < ulen) {
+ c = ubytes[q->byte];
+ }
+ direction = (1 + (q->otherbits | c)) >> 8;
+
+ p = q->child[direction];
+ }
+
+ return (strcmp(str, (const char *)p) == 0);
+}
+
+/*! Inserts str into tree, returns 0 on success */
+int cb_tree_insert(cb_tree_t *tree, const char *str)
+{
+ 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;
+
+ if (p == NULL) {
+ x = tree->malloc(ulen + 1, tree->baton);
+ if (x == 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);
+ c = 0;
+ if (q->byte < ulen) {
+ c = ubytes[q->byte];
+ }
+ direction = (1 + (q->otherbits | c)) >> 8;
+
+ p = q->child[direction];
+ }
+
+ for (newbyte = 0; newbyte < ulen; ++newbyte) {
+ if (p[newbyte] != ubytes[newbyte]) {
+ newotherbits = p[newbyte] ^ ubytes[newbyte];
+ goto different_byte_found;
+ }
+ }
+
+ if (p[newbyte] != 0) {
+ newotherbits = p[newbyte];
+ goto different_byte_found;
+ }
+ return 1;
+
+different_byte_found:
+ newotherbits |= newotherbits >> 1;
+ newotherbits |= newotherbits >> 2;
+ newotherbits |= newotherbits >> 4;
+ newotherbits = (newotherbits & ~(newotherbits >> 1)) ^ 255;
+ c = p[newbyte];
+ newdirection = (1 + (newotherbits | c)) >> 8;
+
+ newnode = tree->malloc(sizeof(cb_node_t), tree->baton);
+ if (newnode == NULL) {
+ return ENOMEM;
+ }
+
+ x = tree->malloc(ulen + 1, tree->baton);
+ if (x == NULL) {
+ tree->free(newnode, tree->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;
+ for (;;) {
+ cb_node_t *q;
+ p = *wherep;
+ if (!(1 & (intptr_t)p)) {
+ break;
+ }
+
+ q = (void *)(p - 1);
+ if (q->byte > newbyte) {
+ break;
+ }
+ if (q->byte == newbyte && q->otherbits > newotherbits) {
+ break;
+ }
+
+ c = 0;
+ if (q->byte < ulen) {
+ c = ubytes[q->byte];
+ }
+ direction = (1 + (q->otherbits | c)) >> 8;
+ wherep = q->child + direction;
+ }
+
+ newnode->child[newdirection] = *wherep;
+ *wherep = (void *)(1 + (char *)newnode);
+ return 0;
+}
+
+/*! Deletes str from the tree, returns 0 on success */
+int cb_tree_delete(cb_tree_t *tree, 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;
+ int direction = 0;
+
+ if (tree->root == NULL) {
+ return 1;
+ }
+ wherep = &tree->root;
+
+ while (1 & (intptr_t)p) {
+ uint8_t c = 0;
+ whereq = wherep;
+ q = (void *)(p - 1);
+
+ if (q->byte < ulen) {
+ c = ubytes[q->byte];
+ }
+ direction = (1 + (q->otherbits | c)) >> 8;
+ wherep = q->child + direction;
+ p = *wherep;
+ }
+
+ if (strcmp(str, (const char *)p) != 0) {
+ return 1;
+ }
+ tree->free(p, tree->baton);
+
+ if (!whereq) {
+ tree->root = NULL;
+ return 0;
+ }
+
+ *whereq = q->child[1 - direction];
+ tree->free(q, tree->baton);
+ return 0;
+}
+
+/*! Clears the given tree */
+void cb_tree_clear(cb_tree_t *tree)
+{
+ if (tree->root) {
+ cbt_traverse_delete(tree, tree->root);
+ }
+ tree->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,
+ 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;
+
+ if (p == NULL) {
+ return 0;
+ }
+
+ while (1 & (intptr_t)p) {
+ cb_node_t *q = (void *)(p - 1);
+ uint8_t c = 0;
+ int direction;
+
+ if (q->byte < ulen) {
+ c = ubytes[q->byte];
+ }
+ direction = (1 + (q->otherbits | c)) >> 8;
+
+ p = q->child[direction];
+ if (q->byte < ulen) {
+ top = p;
+ }
+ }
+
+ if (strlen((const char *)p) < ulen || memcmp(p, prefix, ulen) != 0) {
+ /* No strings match */
+ return 0;
+ }
+
+ return cbt_traverse_prefixed(top, callback, baton);
+}
--- /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