├── example.png ├── .gitignore ├── radixdbstats.c ├── radixdb2dot.c ├── radixdbdump.c ├── radixdb_util.h ├── tests ├── Makefile └── check_insert.c ├── radixdbget.c ├── radixdbmatch.c ├── radixdbmk.c ├── radixdb.h ├── Makefile ├── radixdb_make.c ├── radixdb.c ├── README.md └── radixdb_util.c /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balena/radixdb/HEAD/example.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | radixdbmk 4 | radixdbdump 5 | radixdbget 6 | radixdbmatch 7 | radixdb2dot 8 | tests/check_insert 9 | -------------------------------------------------------------------------------- /radixdbstats.c: -------------------------------------------------------------------------------- 1 | /* This file is a part of radixdb package by G. B. Versiani, guibv@yahoo.com. 2 | * Public domain. 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include "radixdb.h" 9 | #include "radixdb_util.h" 10 | 11 | int main(int argc, char **argv) { 12 | struct radixdb db; 13 | if (argc != 2) { 14 | fprintf(stderr, "usage: radixdbdump f\n"); 15 | return 1; 16 | } 17 | openfile(argv[1], &db); 18 | radixdb_stats(&db); 19 | closefile(&db); 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /radixdb2dot.c: -------------------------------------------------------------------------------- 1 | /* This file is a part of radixdb package by G. B. Versiani, guibv@yahoo.com. 2 | * Public domain. 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include "radixdb.h" 9 | #include "radixdb_util.h" 10 | 11 | int main(int argc, char **argv) { 12 | struct radixdb db; 13 | if (argc != 2) { 14 | fprintf(stderr, "usage: radixdbdump f\n"); 15 | return 1; 16 | } 17 | openfile(argv[1], &db); 18 | radixdb_dump2dot(&db); 19 | closefile(&db); 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /radixdbdump.c: -------------------------------------------------------------------------------- 1 | /* This file is a part of radixdb package by G. B. Versiani, guibv@yahoo.com. 2 | * Public domain. 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include "radixdb.h" 9 | #include "radixdb_util.h" 10 | 11 | int main(int argc, char **argv) { 12 | struct radixdb db; 13 | if (argc != 2) { 14 | fprintf(stderr, "usage: radixdbdump f\n"); 15 | return 1; 16 | } 17 | openfile(argv[1], &db); 18 | if (radixdb_check(&db) == 0) 19 | radixdb_dump(&db); 20 | else 21 | fprintf(stderr, "Invalid db"); 22 | closefile(&db); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /radixdb_util.h: -------------------------------------------------------------------------------- 1 | /* This file is a part of radixdb package by G. B. Versiani, guibv@yahoo.com. 2 | * Public domain. 3 | */ 4 | 5 | #ifndef RADIXDB_UTIL_H 6 | #define RADIXDB_UTIL_H 7 | 8 | #include "radixdb.h" 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | void openfile(const char* filename, struct radixdb* db); 15 | void closefile(struct radixdb* db); 16 | 17 | void radixdb_dump2dot(const struct radixdb* tp); 18 | 19 | void radixdb_dump(const struct radixdb* tp); 20 | 21 | void radixdb_stats(const struct radixdb* tp); 22 | 23 | 24 | #ifdef __cplusplus 25 | } 26 | #endif 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | CC = cc 2 | CFLAGS = -std=c99 -O0 -g -Wall 3 | CDEFS = -D_FILE_OFFSET_BITS=64 4 | LD = $(CC) 5 | 6 | LIBBASE = libradixdb 7 | LIB = $(LIBBASE).a 8 | 9 | UNAME := $(shell uname) 10 | ifeq ($(UNAME), Linux) 11 | LDFLAGS = -pthread -lm -lrt 12 | endif 13 | ifeq ($(UNAME),Darwin) 14 | LDFLAGS = -lm 15 | endif 16 | 17 | all: check_insert run_all_tests 18 | 19 | check_insert: check_insert.o ../$(LIB) 20 | $(LD) $(CFLAGS) -o $@ check_insert.o ../$(LIB) -lcheck $(LDFLAGS) 21 | 22 | run_all_tests: 23 | ./check_insert 24 | 25 | clean: 26 | rm -f *.o check_insert 27 | 28 | .c.o: 29 | $(CC) $(CFLAGS) $(CDEFS) -c $< 30 | -------------------------------------------------------------------------------- /radixdbget.c: -------------------------------------------------------------------------------- 1 | /* This file is a part of radixdb package by G. B. Versiani, guibv@yahoo.com. 2 | * Public domain. 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include "radixdb.h" 9 | #include "radixdb_util.h" 10 | 11 | int main(int argc, char **argv) { 12 | struct radixdb db; 13 | const char *val; 14 | size_t vlen; 15 | int result = 0; 16 | 17 | if (argc != 3) { 18 | fprintf(stderr, "usage: radixdbget f key\n"); 19 | return 1; 20 | } 21 | 22 | openfile(argv[1], &db); 23 | 24 | if (radixdb_lookup(&db, argv[2], strlen(argv[2]), &val, &vlen) == 0) { 25 | printf("%.*s", (int)vlen, val); 26 | } else { 27 | result = 2; 28 | } 29 | 30 | closefile(&db); 31 | 32 | return result; 33 | } 34 | -------------------------------------------------------------------------------- /radixdbmatch.c: -------------------------------------------------------------------------------- 1 | /* This file is a part of radixdb package by G. B. Versiani, guibv@yahoo.com. 2 | * Public domain. 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include "radixdb.h" 9 | #include "radixdb_util.h" 10 | 11 | int main(int argc, char **argv) { 12 | struct radixdb db; 13 | const char *val, *key; 14 | size_t vlen, klen; 15 | int result = 0; 16 | 17 | if (argc != 3) { 18 | fprintf(stderr, "usage: radixdbmatch f key\n"); 19 | return 1; 20 | } 21 | 22 | openfile(argv[1], &db); 23 | 24 | if (radixdb_longest_match(&db, argv[2], strlen(argv[2]), 25 | &key, &klen, &val, &vlen) == 0) { 26 | printf("+%lu,%lu:%.*s->%.*s\n", 27 | (unsigned long)klen, (unsigned long)vlen, 28 | (int)klen, key, 29 | (int)vlen, val); 30 | } else { 31 | result = 2; 32 | } 33 | 34 | closefile(&db); 35 | 36 | return result; 37 | } 38 | -------------------------------------------------------------------------------- /radixdbmk.c: -------------------------------------------------------------------------------- 1 | /* This file is a part of radixdb package by G. B. Versiani, guibv@yahoo.com. 2 | * Public domain. 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "radixdb.h" 16 | 17 | static int getnum(FILE *f, uint32_t *np, char *nextchar) { 18 | int c, i = 0; 19 | uint32_t n = 0; 20 | while ((c = getc(f)) >= '0' && c <= '9') { 21 | c -= '0'; 22 | if (0xffffffff / 10 - c < n) { 23 | errno = EBADMSG; 24 | return -1; 25 | } 26 | n = n * 10 + c; 27 | ++i; 28 | } 29 | *np = n; 30 | *nextchar = c; 31 | return i; 32 | } 33 | 34 | static void badinput(int line) { 35 | fprintf(stderr, "bad input (line %d)\n", line); 36 | exit(1); 37 | } 38 | 39 | int main(int argc, char **argv) { 40 | char c; 41 | char* buf = NULL; 42 | struct radixdb db; 43 | struct radixdb_make db_make; 44 | int line = 1; 45 | uint32_t i; 46 | 47 | radixdb_make_start(&db_make); 48 | while((c = getc(stdin)) == '+') { 49 | uint32_t klen, vlen = 0; 50 | if (getnum(stdin, &klen, &c) < 0 || c != ',' 51 | || getnum(stdin, &vlen, &c) < 0 || c != ':' 52 | || 0xffffffff - klen < vlen) 53 | badinput(line); 54 | buf = (char*) realloc(buf, klen + vlen); 55 | if (fread(buf, 1, klen, stdin) != klen 56 | || getc(stdin) != '-' || getc(stdin) != '>' 57 | || fread(buf + klen, 1, vlen, stdin) != vlen 58 | || getc(stdin) != '\n') 59 | badinput(line); 60 | if (radixdb_make_add(&db_make, buf, klen, buf + klen, vlen) != 0) { 61 | fprintf(stderr, "ignored duplicate (line %d)\n", line); 62 | } 63 | line += 1; 64 | /* Handle cases where the key or the value contain newlines */ 65 | for (i = 0; i < klen + vlen; i++) { 66 | if (buf[i] == '\n') 67 | line++; 68 | } 69 | } 70 | radixdb_make_finish(&db_make, &db); 71 | fwrite(db.mem, db.dend, 1, stdout); 72 | radixdb_free(&db); 73 | 74 | free(buf); 75 | return 0; 76 | } 77 | -------------------------------------------------------------------------------- /radixdb.h: -------------------------------------------------------------------------------- 1 | /* This file is a part of radixdb package by G. B. Versiani, guibv@yahoo.com. 2 | * Public domain. 3 | */ 4 | 5 | #ifndef RADIXDB_H 6 | #define RADIXDB_H 7 | 8 | #include 9 | #include 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | #define RADIXDB_MAX_KEY_LENGTH 0x0ffffffful 16 | 17 | struct radixdb { 18 | unsigned char* mem; /* used memory */ 19 | uint32_t dend; /* end of data ptr */ 20 | }; 21 | 22 | void radixdb_free(struct radixdb* tp); 23 | 24 | /* Check if the RadixDB structure is valid (i.e. if the loaded database is not 25 | * corrupted). This is a O(n) operation. 26 | */ 27 | int radixdb_check(struct radixdb* tp); 28 | 29 | int radixdb_lookup(const struct radixdb* tp, 30 | const char *key, size_t klen, 31 | const char **val, size_t *vlen); 32 | 33 | int radixdb_longest_match(const struct radixdb* tp, 34 | const char *key, size_t klen, 35 | const char **keymatch, size_t *matchlen, 36 | const char **val, size_t *vlen); 37 | 38 | /* Simple data iterator. Returns key-values in the order they were added. The 39 | * iterator parameter must be initialized with 4. 40 | */ 41 | int radixdb_iter_next(const struct radixdb* tp, 42 | uint32_t *iterator, 43 | const char **key, size_t *klen, 44 | const char **val, size_t *vlen); 45 | 46 | /* radixdb_make */ 47 | 48 | struct radixdb_node { 49 | int b; 50 | char *key, *val; 51 | size_t klen, vlen; 52 | struct radixdb_node *left, *right; 53 | }; 54 | 55 | struct radixdb_make { 56 | struct radixdb_node head; 57 | unsigned char* mem; /* used memory */ 58 | uint32_t size; /* memory size */ 59 | uint32_t dend; /* end of data ptr */ 60 | }; 61 | 62 | int radixdb_make_start(struct radixdb_make* tpm); 63 | int radixdb_make_add(struct radixdb_make* tpm, 64 | const char *key, size_t klen, 65 | const char *val, size_t vlen); 66 | int radixdb_make_finish(struct radixdb_make *tpm, struct radixdb* tp); 67 | 68 | #ifdef __cplusplus 69 | } 70 | #endif 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #! /usr/bin/make -rf 2 | # Makefile: make file for radixdb package 3 | # 4 | # This file is a part of radixdb package by G. B. Versiani, guibv@yahoo.com. 5 | # Public domain. 6 | 7 | VERSION = 0.27 8 | 9 | prefix=/usr/local 10 | exec_prefix=$(prefix) 11 | bindir=$(exec_prefix)/bin 12 | libdir=$(exec_prefix)/lib 13 | syslibdir=$(libdir) 14 | sysconfdir=/etc 15 | includedir=$(prefix)/include 16 | mandir=$(prefix)/man 17 | DESTDIR= 18 | 19 | CC = cc 20 | CFLAGS = -std=c99 -O0 -g -Wall 21 | CDEFS = -D_FILE_OFFSET_BITS=64 22 | LD = $(CC) 23 | LDFLAGS = $(CFLAGS) 24 | 25 | AR = ar 26 | ARFLAGS = rv 27 | RANLIB = ranlib 28 | 29 | LIBBASE = libradixdb 30 | LIB = $(LIBBASE).a 31 | USELIB = $(LIB) 32 | INSTALLPROG = radixdb 33 | 34 | CP = cp 35 | 36 | LIB_SRCS = radixdb.c radixdb_make.c 37 | 38 | DISTFILES = Makefile radixdb.h $(LIB_SRCS) 39 | all: static 40 | static: staticlib radixdbmk radixdbget radixdbmatch radixdbdump radixdb2dot radixdbstats 41 | staticlib: $(LIB) 42 | 43 | LIB_OBJS = $(LIB_SRCS:.c=.o) 44 | 45 | $(LIB): $(LIB_OBJS) 46 | -rm -f $@ 47 | $(AR) $(ARFLAGS) $@ $(LIB_OBJS) 48 | -$(RANLIB) $@ 49 | 50 | radixdbmk: radixdbmk.o $(USELIB) 51 | $(LD) $(LDFLAGS) -o $@ $^ 52 | 53 | radixdbget: radixdbget.o radixdb_util.o $(USELIB) 54 | $(LD) $(LDFLAGS) -o $@ $^ 55 | 56 | radixdbmatch: radixdbmatch.o radixdb_util.o $(USELIB) 57 | $(LD) $(LDFLAGS) -o $@ $^ 58 | 59 | radixdbdump: radixdbdump.o radixdb_util.o $(USELIB) 60 | $(LD) $(LDFLAGS) -o $@ $^ 61 | 62 | radixdb2dot: radixdb2dot.o radixdb_util.o $(USELIB) 63 | $(LD) $(LDFLAGS) -o $@ $^ 64 | 65 | radixdbstats: radixdbstats.o radixdb_util.o $(USELIB) 66 | $(LD) $(LDFLAGS) -o $@ $^ 67 | 68 | .SUFFIXES: 69 | .SUFFIXES: .c .o .lo 70 | 71 | .c.o: 72 | $(CC) $(CFLAGS) $(CDEFS) -c $< 73 | 74 | radixdb.o: radixdb.h 75 | radixdbmk.o: radixdb.h 76 | radixdbget.o: radixdb.h 77 | radixdbmatch.o: radixdb.h 78 | radixdbdump.o: radixdb.h 79 | radixdb2dot.o: radixdb.h 80 | radixdbstats.o: radixdb.h 81 | $(LIB_OBJS): radixdb.h 82 | 83 | clean: 84 | -rm -f *.o core *~ $(LIBBASE)[._][aps]* radixdbmk radixdbget radixdbmatch radixdbdump radixdb2dot radixdbstats 85 | $(MAKE) -C tests clean 86 | 87 | DNAME = radixdb-$(VERSION) 88 | dist: $(DNAME).tar.gz 89 | $(DNAME).tar.gz: $(DISTFILES) 90 | mkdir $(DNAME) 91 | ln $(DISTFILES) $(DNAME)/ 92 | tar cfz $@ $(DNAME) 93 | rm -fr $(DNAME) 94 | 95 | tests: $(LIB) 96 | $(MAKE) -C tests 97 | 98 | .PHONY: all clean realclean dist 99 | .PHONY: tests 100 | .PHONY: static staticlib 101 | -------------------------------------------------------------------------------- /radixdb_make.c: -------------------------------------------------------------------------------- 1 | /* This file is a part of radixdb package by G. B. Versiani, guibv@yahoo.com. 2 | * Public domain. 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "radixdb.h" 12 | 13 | static void 14 | uint32_pack(unsigned char *buf, uint32_t i) { 15 | buf[0] = i & 255; i >>= 8; 16 | buf[1] = i & 255; i >>= 8; 17 | buf[2] = i & 255; 18 | buf[3] = i >> 8; 19 | } 20 | 21 | static int 22 | bit(uint32_t b, const char *key, uint32_t klen) { 23 | size_t index = b >> 3; 24 | if (index >= klen) 25 | return 0; 26 | return key[index] & (1 << (7 - (b & 7))); 27 | } 28 | 29 | static struct radixdb_node* 30 | insert_between(struct radixdb_node *h, struct radixdb_node *n, 31 | int d, struct radixdb_node *p) { 32 | if ((h->b >= d) || (h->b <= p->b)) { 33 | n->b = d; 34 | n->left = bit(d, n->key, n->klen) ? h : n; 35 | n->right = bit(d, n->key, n->klen) ? n : h; 36 | return n; 37 | } 38 | 39 | if (bit(h->b, n->key, n->klen)) 40 | h->right = insert_between(h->right, n, d, h); 41 | else 42 | h->left = insert_between(h->left, n, d, h); 43 | return h; 44 | } 45 | 46 | int radixdb_make_start(struct radixdb_make* tpm) { 47 | memset(tpm, 0, sizeof(*tpm)); 48 | tpm->head.left = tpm->head.right = &tpm->head; 49 | return 0; 50 | } 51 | 52 | int radixdb_make_add(struct radixdb_make* tpm, 53 | const char *key, size_t klen, 54 | const char *val, size_t vlen) { 55 | size_t i; 56 | struct radixdb_node *t, *n, *head; 57 | 58 | n = (struct radixdb_node*) malloc(sizeof(*n)); 59 | n->klen = klen; 60 | n->vlen = vlen; 61 | n->key = (char*) malloc(klen); 62 | memcpy(n->key, key, klen); 63 | if (vlen) { 64 | n->val = (char*) malloc(vlen); 65 | memcpy(n->val, val, vlen); 66 | } else { 67 | n->val = NULL; 68 | } 69 | tpm->dend += sizeof(*n) + klen + vlen; 70 | 71 | t = head = &tpm->head; 72 | do { 73 | i = t->b; 74 | t = bit(t->b, key, klen) ? t->right : t->left; 75 | } while (i < t->b); 76 | 77 | if (klen == t->klen && memcmp(key, t->key, klen) == 0) { 78 | errno = EEXIST; 79 | return -1; /* entry already exists */ 80 | } 81 | 82 | i = 0; 83 | while (i < (klen << 3)) { 84 | if (bit(i, key, klen) != bit(i, t->key, t->klen)) 85 | break; 86 | i++; 87 | } 88 | 89 | if (bit(head->b, key, klen)) 90 | head->right = insert_between(head->right, n, i, head); 91 | else 92 | head->left = insert_between(head->left, n, i, head); 93 | return 0; 94 | } 95 | 96 | static size_t 97 | memcount(struct radixdb_node *t, int b) { 98 | size_t size; 99 | if (t->b <= b) 100 | return 0; 101 | size = 4 + 8 + 8 + t->klen + t->vlen; 102 | size += memcount(t->left, t->b); 103 | size += memcount(t->right, t->b); 104 | return size; 105 | } 106 | 107 | static uint32_t 108 | to_db(struct radixdb_node *t, int b, struct radixdb* db) { 109 | uint32_t n; 110 | 111 | if (t->b <= b) 112 | return 4; 113 | 114 | n = db->dend; 115 | uint32_pack(db->mem + n, t->b); 116 | uint32_pack(db->mem + n + 12, t->klen); 117 | uint32_pack(db->mem + n + 16, t->vlen); 118 | memcpy(db->mem + n + 20, t->key, t->klen); 119 | if (t->vlen > 0) 120 | memcpy(db->mem + n + 20 + t->klen, t->val, t->vlen); 121 | db->dend += 4 + 8 + 8 + t->klen + t->vlen; 122 | 123 | uint32_pack(db->mem + n + 4, to_db(t->left, t->b, db)); 124 | uint32_pack(db->mem + n + 8, to_db(t->right, t->b, db)); 125 | return n; 126 | } 127 | 128 | static void 129 | free_nodes(struct radixdb_node *t, int b) { 130 | if (t->b <= b) 131 | return; 132 | 133 | free_nodes(t->left, t->b); 134 | free_nodes(t->right, t->b); 135 | 136 | free(t->key); 137 | free(t->val); 138 | free(t); 139 | } 140 | 141 | int radixdb_make_finish(struct radixdb_make* tpm, struct radixdb* tp) { 142 | uint32_t n; 143 | size_t memsize; 144 | 145 | memsize = memcount(&tpm->head, -1); 146 | tp->mem = (unsigned char*) malloc(4 + memsize); 147 | if (!tp->mem) 148 | return -1; 149 | 150 | tp->dend = 4; 151 | uint32_pack(tp->mem, 4); /* point to root */ 152 | 153 | n = tp->dend; 154 | uint32_pack(tp->mem + n, 0); 155 | uint32_pack(tp->mem + n + 12, 0); 156 | uint32_pack(tp->mem + n + 16, 0); 157 | tp->dend += 4 + 8 + 8; 158 | 159 | uint32_pack(tp->mem + n + 4, to_db(tpm->head.left, 0, tp)); 160 | uint32_pack(tp->mem + n + 8, to_db(tpm->head.right, 0, tp)); 161 | 162 | free_nodes(tpm->head.left, 0); 163 | free_nodes(tpm->head.right, 0); 164 | return 0; 165 | } 166 | -------------------------------------------------------------------------------- /radixdb.c: -------------------------------------------------------------------------------- 1 | /* This file is a part of radixdb package by G. B. Versiani, guibv@yahoo.com. 2 | * Public domain. 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "radixdb.h" 12 | 13 | static uint32_t 14 | uint32_unpack(const unsigned char *buf) { 15 | uint32_t n = buf[3]; 16 | n <<= 8; n |= buf[2]; 17 | n <<= 8; n |= buf[1]; 18 | n <<= 8; n |= buf[0]; 19 | return n; 20 | } 21 | 22 | static int 23 | get_bit(uint32_t b, const char *key, uint32_t klen) { 24 | size_t index = b >> 3; 25 | if (index >= klen) 26 | return 0; 27 | return key[index] & (1 << (7 - (b & 7))); 28 | } 29 | 30 | void radixdb_free(struct radixdb* tp) { 31 | free(tp->mem); 32 | } 33 | 34 | int radixdb_check(struct radixdb* tp) { 35 | uint32_t pos = 4, bit, left, right, klen, vlen; 36 | while (pos < tp->dend) { 37 | bit = uint32_unpack(tp->mem + pos); 38 | left = uint32_unpack(tp->mem + pos + 4); 39 | right = uint32_unpack(tp->mem + pos + 8); 40 | klen = uint32_unpack(tp->mem + pos + 12); 41 | vlen = uint32_unpack(tp->mem + pos + 16); 42 | if ((bit > 0 && ((bit - 1) >> 3) >= klen) 43 | || left >= tp->dend 44 | || right >= tp->dend 45 | || pos + 4 + 8 + 8 + klen + vlen > tp->dend) { 46 | errno = EINVAL; 47 | return -1; 48 | } 49 | pos += 4 + 8 + 8 + klen + vlen; 50 | } 51 | return 0; 52 | } 53 | 54 | int radixdb_lookup(const struct radixdb* tp, 55 | const char *key, size_t klen, 56 | const char **val, size_t *vlen) { 57 | if (tp->dend > 4 && klen > 0) { 58 | uint32_t pos, nextpos, b0, b1; 59 | pos = uint32_unpack(tp->mem); 60 | b0 = uint32_unpack(tp->mem + pos); 61 | nextpos = uint32_unpack(tp->mem + pos + (get_bit(b0, key, klen) ? 8 : 4)); 62 | for (;;) { 63 | if (uint32_unpack(tp->mem + pos + 12) == klen) { 64 | #if 0 65 | printf("cmp(\"%.*s\",\"%.*s\")\n", (int)klen, tp->mem + pos + 20, (int)klen, key); 66 | #endif 67 | if (memcmp(tp->mem + pos + 20, key, klen) == 0) { 68 | *val = (const char*)(tp->mem + pos + 20 + klen); 69 | *vlen = uint32_unpack(tp->mem + pos + 16); 70 | return 0; 71 | } 72 | } 73 | b1 = uint32_unpack(tp->mem + nextpos); 74 | if (b1 <= b0 || b1 > (klen << 3)) 75 | break; 76 | pos = nextpos; 77 | nextpos = uint32_unpack(tp->mem + pos + (get_bit(b1, key, klen) ? 8 : 4)); 78 | b0 = b1; 79 | } 80 | } 81 | 82 | errno = ENOENT; 83 | return -1; 84 | } 85 | 86 | int radixdb_longest_match(const struct radixdb* tp, 87 | const char *key, size_t klen, 88 | const char **keymatch, size_t *matchlen, 89 | const char **val, size_t *vlen) { 90 | if (tp->dend > 4 && klen > 0) { 91 | uint32_t pos, nextpos, b0, b1, n_klen, best = 0; 92 | pos = uint32_unpack(tp->mem); 93 | b0 = uint32_unpack(tp->mem + pos); 94 | nextpos = uint32_unpack(tp->mem + pos + (get_bit(b0, key, klen) ? 8 : 4)); 95 | for (;;) { 96 | n_klen = uint32_unpack(tp->mem + pos + 12); 97 | if (n_klen > 0 && n_klen <= klen && n_klen > best) { 98 | #if 0 99 | printf("cmp(\"%.*s\",\"%.*s\")", (int)n_klen, tp->mem + pos + 20, (int)klen, key); 100 | #endif 101 | if (memcmp(key, tp->mem + pos + 20, n_klen) == 0) { 102 | #if 0 103 | printf(" -> best is \"%.*s\"", (int)n_klen, tp->mem + pos + 20); 104 | #endif 105 | *keymatch = (const char*)(tp->mem + pos + 20); 106 | *matchlen = best = uint32_unpack(tp->mem + pos + 12); 107 | *val = (const char*)(tp->mem + pos + 20 + *matchlen); 108 | *vlen = uint32_unpack(tp->mem + pos + 16); 109 | } 110 | #if 0 111 | printf("\n"); 112 | #endif 113 | } 114 | b1 = uint32_unpack(tp->mem + nextpos); 115 | if (b1 <= b0 || b1 > (klen << 3)) 116 | break; 117 | pos = nextpos; 118 | nextpos = uint32_unpack(tp->mem + pos + (get_bit(b1, key, klen) ? 8 : 4)); 119 | b0 = b1; 120 | } 121 | if (best > 0) 122 | return 0; 123 | } 124 | 125 | errno = ENOENT; 126 | return -1; 127 | } 128 | 129 | int radixdb_iter_next(const struct radixdb* tp, 130 | uint32_t *iterator, 131 | const char **key, size_t *klen, 132 | const char **val, size_t *vlen) { 133 | if (*iterator < tp->dend) { 134 | *klen = uint32_unpack(tp->mem + *iterator + 12); 135 | *vlen = uint32_unpack(tp->mem + *iterator + 16); 136 | *key = (const char*)(tp->mem + *iterator + 20); 137 | *val = (const char*)(tp->mem + *iterator + 20 + *klen); 138 | *iterator += 4 + 8 + 8 + *klen + *vlen; 139 | return 0; 140 | } 141 | errno = ENOENT; 142 | return -1; 143 | } 144 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | radixdb 2 | ======= 3 | 4 | RadixDB is a key-value static (read-only) database that provides fast lookups 5 | and "longest matching" operation. 6 | 7 | 8 | Overview 9 | -------- 10 | 11 | This library provides a C99 implementation of the PATRICIA Trie for NUL 12 | terminated strings. Each node store a bit position and two children. The 13 | position is the next bit location in which two members differ. For a given set 14 | of elements there is an unique tree that satisfies the given conditions, so the 15 | structure does not need complex balancing algorithms. The depth tree is 16 | bounded by the length of the longest element, rather than the number of 17 | elements (as with an unbalanced tree). This implementation does not use 18 | external nodes, so data is found, while traversing the tree, at a node where 19 | the parent node has a bit position which is equal or greater than the node bit 20 | position. 21 | 22 | The implementation uses 3 control words of 32-bit each (bit position then left 23 | and right children), followed by the key length and the value length in bytes 24 | also in 32-bit words, then the key and the value. With this configuration the 25 | overhead of each key-value pair added to the tree is 20 bytes (5 words of 26 | 32-bits). 27 | 28 | It provides the following: 29 | * O(k) operations. In some cases, this can be faster than a hash table. 30 | * Optimized largest prefix match operation. 31 | 32 | This implementation does not support deletion, as it was conceived for usage in 33 | large read-only databases. 34 | 35 | 36 | Build 37 | ----- 38 | 39 | $ make 40 | 41 | This will generate six binary files: `radixdbmk`, `radixdbget`, `radixdbmatch`, 42 | `radixdbdump`, `radixdbstats` and `radixdb2dot`. 43 | 44 | 45 | The radixdbmk program 46 | --------------------- 47 | 48 | In order to generate a radixdb file, do: 49 | 50 | $ ./radixdbmk < f > f.radixdb 51 | 52 | A record is encoded for radixdbmk as `+klen,dlen:key->data` followed by a 53 | newline. Here `klen` is the number of bytes in `key` and `dlen` is the number 54 | of bytes in `data`. The end of `data` is indicated by an extra newline. For 55 | example: 56 | 57 | +3,5:one->Hello 58 | +3,7:two->Goodbye 59 | 60 | Keys and data do not have to fit into memory, but `radixdbmk` needs at least 20 61 | bytes of memory per record. A database cannot exceed 4 gigabytes. 62 | 63 | `f` is portable across machines. 64 | 65 | 66 | The radixdbget program 67 | ---------------------- 68 | 69 | $ ./radixdbget f.radixdb k 70 | 71 | `radixdbget` searches for a record with key `k` in a constant database 72 | `f.radixdb`. The constant database must be readable. 73 | 74 | Normally `radixdbget` prints the data with key `k` and exits 0. If there is no 75 | record with the key `k`, `radixdbget` exits 2. 76 | 77 | If `radixdbget` encounters a read error, write error, or database format error, 78 | it complains and exits 1. 79 | 80 | 81 | The radixdbmatch program 82 | ------------------------ 83 | 84 | $ ./radixdbmatch f.radixdb k 85 | 86 | `radixdbmatch` searches for the longest prefix available in the constant 87 | database `f.radixdb` matching `k`, in the format `+klen,dlen:key->data`. 88 | 89 | Say `f.radixdb` has the following: 90 | 91 | +4,1:roma->1 92 | +7,1:romanus->2 93 | 94 | Then, the following are true: 95 | 96 | $ ./radixdbmatch f.radixdb romae 97 | +4,1:roma->1 98 | $ ./radixdbmatch f.radixdb romanuseos 99 | +7,1:romanus->2 100 | 101 | 102 | The radixdbdump program 103 | ----------------------- 104 | 105 | $ ./radixdbdump f.radixdb 106 | 107 | `radixdbdump` reverses the operation performed by `radixdbmk`. The order of 108 | the database records is preserved. 109 | 110 | 111 | The radixdbstats program 112 | ------------------------ 113 | 114 | $ ./radixdbstats f.radixdb 115 | 116 | `radixdbstats` shows useful statistics about the database records contained in 117 | the constant database `f.radixdb`. Example: 118 | 119 | number of records: 11 120 | key min/avg/max length: 2/4/4 121 | val min/avg/max length: 8/9/9 122 | max depth: 4 123 | max bit depth: 30 124 | 125 | The key and value lengths are given in bytes, whereas the max bit depth is 126 | given in bits. In the example above, the created trie can reach any leaf by 127 | checking a max of 4 bits, up to the 30th bit of the input key. 128 | 129 | 130 | The radixdb2dot program 131 | ----------------------- 132 | 133 | $ ./radixdb2dot f.radixdb | dot -Tpng -of.png 134 | 135 | `radixdb2dot` dumps the database in a format recognized by the Graphviz `dot` 136 | tool. This tool has informational/academic/toy purposes, and does not perform 137 | well on very long databases. 138 | 139 | Here's a toy example: 140 | 141 | ![Dot output example](https://raw.githubusercontent.com/balena/radixdb/master/example.png) 142 | 143 | On each record, you will find: 144 | * First line: 145 | * 1st field: bit position, byte index 146 | * 2nd field: bit position, bit index 147 | * 3rd field: key -> value 148 | * Second line: 149 | * Left and right pointers 150 | 151 | Note the format above is not how the code has been implemented internally, but 152 | a way to ease the visualization. Particularly, the 1st and 2nd field 153 | represented in each record are stored as a single 32-bit integer 154 | (`byte index * 8 + bit index`). 155 | 156 | 157 | Have fun! 158 | -------------------------------------------------------------------------------- /radixdb_util.c: -------------------------------------------------------------------------------- 1 | /* This file is a part of radixdb package by G. B. Versiani, guibv@yahoo.com. 2 | * Public domain. 3 | */ 4 | 5 | #include "radixdb_util.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #ifdef _WIN32 16 | # include 17 | #else 18 | # include 19 | # ifndef MAP_FAILED 20 | # define MAP_FAILED ((void*)-1) 21 | # endif 22 | #endif 23 | #include 24 | 25 | static uint32_t 26 | uint32_unpack(const unsigned char *buf) { 27 | uint32_t n = buf[3]; 28 | n <<= 8; n |= buf[2]; 29 | n <<= 8; n |= buf[1]; 30 | n <<= 8; n |= buf[0]; 31 | return n; 32 | } 33 | 34 | static int __fd; 35 | 36 | void openfile(const char* filename, struct radixdb* db) { 37 | struct stat st; 38 | #ifdef _WIN32 39 | static HANDLE hFile, hMapping; 40 | #endif 41 | 42 | /* open and get file size */ 43 | __fd = open(filename, O_RDONLY); 44 | if (__fd < 0 || fstat(__fd, &st) < 0) { 45 | fprintf(stderr, "unable to open `%s'\n", filename); 46 | exit(1); 47 | } 48 | 49 | /* trivial sanity check: at least root should be here */ 50 | if (st.st_size < 4 || st.st_size >= 0xfffffffful) { 51 | fprintf(stderr, "invalid file\n"); 52 | exit(1); 53 | } 54 | 55 | db->dend = st.st_size; 56 | /* memory-map file */ 57 | #ifdef _WIN32 58 | hFile = (HANDLE) _get_osfhandle(__fd); 59 | if (hFile == (HANDLE) -1) { 60 | fprintf(stderr, "_get_osfhandle error\n"); 61 | exit(1); 62 | } 63 | hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); 64 | if (!hMapping) { 65 | fprintf(stderr, "CreateFileMapping error\n"); 66 | exit(1); 67 | } 68 | db->mem = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0); 69 | CloseHandle(hMapping); 70 | if (!mem) { 71 | fprintf(stderr, "MapViewOfFile error\n"); 72 | exit(1); 73 | } 74 | #else 75 | db->mem = (unsigned char*)mmap(NULL, db->dend, PROT_READ, MAP_SHARED, __fd, 0); 76 | if (db->mem == MAP_FAILED) { 77 | fprintf(stderr, "mmap error"); 78 | exit(1); 79 | } 80 | #endif /* _WIN32 */ 81 | } 82 | 83 | void closefile(struct radixdb* db) { 84 | #ifdef _WIN32 85 | UnmapViewOfFile((void*) db->mem); 86 | #else 87 | munmap((void*)db->mem, db->dend); 88 | #endif /* _WIN32 */ 89 | 90 | close(__fd); 91 | } 92 | 93 | static void 94 | dot_escape(const char* s, uint32_t len) { 95 | while (len > 0) { 96 | switch (*s) { 97 | case '{': case '}': case '(': case ')': case '<': case '>': case '&': 98 | fputc('\\', stdout); fputc(*s, stdout); 99 | break; 100 | default: 101 | fputc(*s, stdout); 102 | break; 103 | } 104 | s++; 105 | len--; 106 | } 107 | } 108 | 109 | void radixdb_dump2dot(const struct radixdb* tp) { 110 | uint32_t pos = 4, bit, left, right, klen, vlen; 111 | printf("digraph G {\n"); 112 | printf(" node [shape=record];\n"); 113 | printf(" root -> n%lu;\n", 114 | (unsigned long)uint32_unpack(tp->mem)); 115 | while (pos < tp->dend) { 116 | bit = uint32_unpack(tp->mem + pos); 117 | left = uint32_unpack(tp->mem + pos + 4); 118 | right = uint32_unpack(tp->mem + pos + 8); 119 | klen = uint32_unpack(tp->mem + pos + 12); 120 | vlen = uint32_unpack(tp->mem + pos + 16); 121 | printf(" n%lu [label=\"{{%lu|%lu|\\\"", 122 | (unsigned long)pos, 123 | (unsigned long)(bit>>3), (unsigned long)(bit&7)); 124 | dot_escape((const char*)(tp->mem + pos + 20), klen); 125 | printf("\\\"→\\\""); 126 | dot_escape((const char*)(tp->mem + pos + 20 + klen), vlen); 127 | printf("\\\"}|{left|right}}\"];\n"); 128 | printf(" n%lu:l -> n%lu;\n" 129 | " n%lu:r -> n%lu;\n", 130 | (unsigned long)pos, (unsigned long)left, 131 | (unsigned long)pos, (unsigned long)right); 132 | pos += 4 + 8 + 8 + klen + vlen; 133 | } 134 | printf("}\n"); 135 | } 136 | 137 | void radixdb_dump(const struct radixdb* tp) { 138 | uint32_t pos = 4, klen, vlen; 139 | while (pos < tp->dend) { 140 | klen = uint32_unpack(tp->mem + pos + 12); 141 | vlen = uint32_unpack(tp->mem + pos + 16); 142 | printf("+%lu,%lu:%.*s->%.*s\n", 143 | (unsigned long)klen, (unsigned long)vlen, 144 | (int)klen, tp->mem + pos + 20, 145 | (int)vlen, tp->mem + pos + 20 + klen); 146 | pos += 4 + 8 + 8 + klen + vlen; 147 | } 148 | printf("\n"); 149 | } 150 | 151 | struct radixdb_stats { 152 | uint32_t cnt; 153 | uint32_t kmin, kmax, ktot; 154 | uint32_t vmin, vmax, vtot; 155 | uint32_t dmax, bmax; 156 | }; 157 | 158 | static void 159 | collectstats(const struct radixdb* tp, uint32_t pos, int32_t b0, 160 | int d, struct radixdb_stats* stats) { 161 | int32_t b1; 162 | uint32_t klen, vlen; 163 | 164 | b1 = (int32_t)uint32_unpack(tp->mem + pos); 165 | if (b1 <= b0) 166 | return; 167 | 168 | klen = uint32_unpack(tp->mem + pos + 12); 169 | vlen = uint32_unpack(tp->mem + pos + 16); 170 | 171 | stats->cnt++; 172 | 173 | if (klen < stats->kmin) 174 | stats->kmin = klen; 175 | if (klen > stats->kmax) 176 | stats->kmax = klen; 177 | stats->ktot += klen; 178 | 179 | if (vlen < stats->vmin) 180 | stats->vmin = vlen; 181 | if (vlen > stats->vmax) 182 | stats->vmax = vlen; 183 | stats->vtot += vlen; 184 | 185 | if (d > stats->dmax) 186 | stats->dmax = d; 187 | if (b1 > stats->bmax) 188 | stats->bmax = b1; 189 | 190 | collectstats(tp, uint32_unpack(tp->mem + pos + 4), b1, d+1, stats); 191 | collectstats(tp, uint32_unpack(tp->mem + pos + 8), b1, d+1, stats); 192 | } 193 | 194 | void radixdb_stats(const struct radixdb* tp) { 195 | struct radixdb_stats st; 196 | 197 | memset(&st, 0, sizeof(st)); 198 | st.kmin = 0xfffffffful; 199 | st.vmin = 0xfffffffful; 200 | 201 | collectstats(tp, uint32_unpack(tp->mem + 4 + 4), 0, 0, &st); 202 | collectstats(tp, uint32_unpack(tp->mem + 4 + 8), 0, 0, &st); 203 | 204 | printf("number of records: %u\n", st.cnt); 205 | printf("key min/avg/max length: %u/%u/%u\n", st.kmin, 206 | st.cnt ? (st.ktot + st.cnt / 2) / st.cnt : 0, st.kmax); 207 | printf("val min/avg/max length: %u/%u/%u\n", st.vmin, 208 | st.cnt ? (st.vtot + st.cnt / 2) / st.cnt : 0, st.vmax); 209 | printf("max depth: %u\n", st.dmax); 210 | printf("max bit depth: %u\n", st.bmax); 211 | } 212 | -------------------------------------------------------------------------------- /tests/check_insert.c: -------------------------------------------------------------------------------- 1 | /* This file is a part of radixdb package by G. B. Versiani, guibv@yahoo.com. 2 | * Public domain. 3 | */ 4 | 5 | #include 6 | #include 7 | #include "../radixdb.h" 8 | 9 | static const char *test_keys[] = { 10 | "wheshivGowghisheex)", "noibbeDyrumvoteecig}", "firtyis", 11 | "EfElRagsujemphIb~", "AyWev", "RotUv", "OctilGanUkKoj", "NikJeizjoTyitVejmo", 12 | "GadsocdojVenlo", "Jiwrodaithquek_", "rajWiaHiUgMytyurEcha", "frebdaz^", 13 | "Dipipcys", "Knukfa", "TeevtybtynovGo", "SavFiawkacga", "dulRugEt", 14 | "jawyimsyujak0", "taHiHifaycsim2", "Ug5SetkouffOmpAtbye", "leumheitcoHay", 15 | "HauvdeuptIndOo", "rigetogdior", "netEjren", "wubEgzy", "oawb5", "peishcon", 16 | "ShanBiOghuedjo", "voirf", "najCyreGhautgoucNu", "CejOkByxUshcithGiect", 17 | "Cushyuk", "obsum+", "cicsigVodOotJidKinow", "oagg-", "tranbi", "VefAr7old", 18 | "DoymWotMimomkiHyov", "ninExrotAmdum", "GuerIckCodVel", "IajPaned", 19 | "AjcicogtibMoog|", "wajejya", "dicguwea", "KnawpUzgeek\"", 20 | "RinkeuvagBaucUv<", "cofcoGrojya", "SeinWeo", "UjIkJeyd", "quoyctEyldic", 21 | "ejkudjewHalb", "lautDydnewdIfEr", "viagOwOsk&", "gazyoax3", 22 | "owoicebviethnoik", "RedcyobmirOdojRaffoc", "PloGriptickdeHa", "KidtoiHazPi", 23 | "byftyornIb", "NethNeb[", "vebamajBashtOz", "PaHuReOthFigg", 24 | "yiGreitjicFifeert", "KevFom#", "RuthjetOisAphorthUv!", "GuHup", "weHimya", 25 | "crodWoc", "Nepbyn2", "WofOdfeanHikBir", "migidwoilWenOk", "3fredBecOawbUs", 26 | "ricEvtobmid6", "Fes>opOfeftojIsturl", "fownyinhabOph,", "yidpyfsEdgu", 27 | "Fayshuec3", "BebgarjOf", "Dierfom4", "udHyfruOkronn0", "2Swet0", "becob", 28 | "GrygciWosBufAsya", "HyewAd`", "OjNifWaw9", "dyikub", "cartergyolt", 29 | "gerk>On\\", "wytfawv;", "TraivyovkofIgyeiff", "sledemDuad", 30 | "ClaHajBighCouzsern", "UtEptAbr9", "VoiptIkothTan8", "aydJocCir", 31 | "fusdityoghap", "Cishked4MubCeakimyim", "iteectEsDakDotLauc", "EabdidMos", 32 | "cofmoyppyotyue", "EredHucjeopdib", "drelCu", "Kehis", "JeatNaspon", "tools", 33 | "eydHoquewnof", "TawlojimWu", "BotColniHyShib,", "Tovufvabco", 34 | "UpetpodPeciabFilk", "ryebyov", "Nudkevchu", "Hir4Ov", "thrappArpAk", 35 | "luedMourOc", "Teup2", "NewHicfig", "ryinOf$", "VeorvAn9", "lyts)", 36 | "lugUpyacNefAtyemFiaz", "Barg{", "yodifAdNeoshgeticav!", "tepyipetgu", 37 | "awmIvIdifguk", "powkyid4", "neosojLabVepaik7Jub", "odwik", 38 | "swirUzvapquep5Quo", "GefCu", "wehab", "yathajCukTemEdLa", "EetsyunOsAyhyn", 39 | "MonOoHedAcGic", "VicshaimEj>", "DashtEcUp<", "acnidboowwiv", "oujMonria", 40 | "Oytee", "idchoHoshcyRu", "LinOoquewdyideycsod0", "yikDy", "wukMadmapOm<", 41 | "yaifyeervEjpot", "KanthEi", "WakpopJilNeemFeig", "LytLilforshyeasZuor", 42 | "edUzcibif", "TylfAdFo", "anOdOudPeebyatyilv-", "tavCuwed7", 43 | "SucjiJalrucich2", "GoyctyanCovBaptArn", "biSlarepOiHojMyb", 44 | "utMackuishkort", "AynpicbamFiOjTidBoc", "IkBinjivUsAthkew", "WysIajTob", 45 | "fihevVu", "Ejhon", "lettUfniOlyu", "pibr^", "ShmalWolRagnorf}", "iawyil", 46 | "dosAigJeekshiatIb", "Bavig", "swalvidwuwred", "Gewugdyidjumvo", "kojTeirp9", 47 | "fevImserj:oceskaj", "lifOzwokheyp(", "doutphArdocHal8", "BliQuairdOoBy", 48 | "DaulrauriphgimUp3", "Tocca", "dolmyaivmyecsirv", "yetcofipkardout", 49 | "woagIljArb", "Oaggacew7", "RiejyeOtdig", "yifEo", "AddosFidFienvo", 50 | "mofWudCush", "Cockoccye", "regTeckShoomBocga", "udwaHybTuAnFamOstOb{", 51 | "eimforashboum", "CeyHeeghiv0", "whiaggIanjeewgadId", "CurvibcifyecorpingUs", 52 | "hildOjBa", "vebcasyeirzOrOshCeHu", "EnsyepnabUg2", "fecOrt0", "cheynRoshk*", 53 | "mabOctednev5", "GengAdsOrArvoforn", "igdorbUctur", "FeesUbjuv", 54 | "Je*Driurretbech", "niKrios`", "CralowjodtucJit", "AphgheHuviled.", 55 | "]FrovkudifdarpUvoos", "guhuethays", "snoglir", "ciefWya", 56 | "CacsIsjeevetUnQuiWy", "cyofwyk", "gick!quelidEk", "babjapGod", 57 | "daHicyoodJi", "Renva", "BoquoiHebyaubbivwyp", "Phretceidegejbinn@", 58 | "irgevroitocUvyon", "yicCak1swejEi", "&twecCedjawrarlyi", "diOdiffi", 59 | "Whepref", "yuotonrir7", "Whawf1", "liggaifMijOun#oxIv", "bacguwrohoc", 60 | "dixetKilrevwojOsHayd", "EvpaduvmabOym", "ItyeTa", "TwyadsIj", 61 | "wryagyashkyiv", "NufUv", "pepEdsad", "neocojMarvyadBodnod", "evpobcymbur", 62 | "IkUtitNiddojNehom", "DeavAdfottIgyut<", "CekyokkowxawCimvim5", 63 | "grothpyugwi", "WresEaf", "rinthAsbujedLu", "GresOc", "denRapsEdBodUdsagg", 64 | "jagoynsofPacfoj}", "GagbicEudditni", "ThaffUnren", "leimIbBeOdCabishAw", 65 | "Oirpafanirs>", "gitduvew", "biovoibgedath", "EerrocDycsupAifs", "RiSwops", 66 | "EdWacjuWob", "quifOs", "ubAdIdAnFirc", "FeObbavAjKeygNuorv", 67 | "ErsyuqueHykPeu", "rojRu", "Noct5", "yaiksixag_", "gicHek", 68 | "KuegIkmoccicEnej3", "4okgonAphOkquitCi", "OocainexnibMoct", "Utyic", 69 | "egCymbocshijItBac", "ivgekurdElUkVeynd", "obsyeapNibCid>", 70 | "dydLefCebPoajpan", "yeepkuAm", "skisAdpadVafvek.", "MicnopCogHeOnJaQueid", 71 | "obryesadZugBatCi", "LosIfGod", "yaddicEwhyobNisvu", "VovfedkubsEequis", 72 | "tweycsijId(", "LowupMov", "podyenenjothatLushby", "nasOvHidGewim3", 73 | "waifPyzUbJeo", "rofkapWotvaitshUg", "LicOjIvyagOwlaptaif", "uthnoanabear", 74 | "pyFrimOdsiavQuo", "BlykIn", "shmufdakeim5", "epsAi", "VacRotOc", "pilKar", 75 | "dodfebyocjialyoikop", "KnidIgmufwalpya", "DebhyljojVadcaicfac{", 76 | "valcargheneudOvByash", "VifKupMeyppeirya", "wubbuheavVuKnayg", 77 | "tryaggawtAnEfekkec9", "IdneynneaphFod?", "udeenkyalNutyebNirut", 78 | "beyRutwyWicIrojketh7", "ayGhivAnyie", "kiryejcofpolp\"", "nidoo", 79 | "LaltoocJanyie", "bianryRerryemt", "vanvugsUcbudMonk", "filOsh7", 80 | "pomNacEwkAcViWykdok", "WyimpImnecOj", "TeOtebheyplun", 81 | "JadmutBirawgoypInJeb", "IsViesyefDapdikFoo", "yeshly", "guItveyHeurt", 82 | "kekowpIk", "DootGiQueghopinn8ac+", "IgNoilRevryb", "lumUkio", 83 | "Udyodfeymthoik", "TunIv0", "dedsAwgeigsyeo", "Neusog6", "Nipatyo", 84 | "AjWyEng", "ShlannAfnafeyhiv]", "keHamIa", "GijlullEnirfacoak,", "dryFlag", 85 | "EtBajOfyefCocyorkug", "Mapt|ochcojobnay", "4Blufrya", "simLaxRorz%", 86 | "WeshWyaw", "ajNekaid-", "TyeljUbudmoifPef", "Lutact", "Owovye", 87 | "vaggAdgozesDo", "HosEt5", "necogsejUnHask_", "foagries21knazKov{", 88 | "WyhaHophauphiv1", "geitpyo", "onyueHuocNeArkAfMi", "MytsulQuakejyaipVet}", 89 | "joSwugsUbga", "leersElsopty", "bitUcethOzhai", "icdeph", "asvobr", 90 | "shiOsyuf", "ijHoyRafjalb5", "Jep7ogchymdoa", "DurloovKiOdWabpo", 91 | "AnHavvinafOok", "OkciweltAifRo", "wurcUrGoibfuj1", "azatByctunmyip'", 92 | "LalgagojbypAnd", "figfemonph", "yidNig1", "PrinyesElyed", 93 | "vubthofyonidonhacFav", "norbudbivriAvShey", "CyWijAfVu", 94 | "EvVamcyochtythessOc", "tectaHynEanes7", "OodOykibAfvujUv", 95 | "klothgivpooldya", "bahertofye", "RuheffidMeofGhont", "watbinRycs\"Twok", 96 | "AjVocbingib", "AcbejauftOaccaj", "fleOwOomHoyg0", "jobImWajWof`", 97 | "ebtouwecfednians8", "NisjobowCee", "BubzyxguIt", "Mipfep<", 98 | "undecsodsacteshga", "JiHanejShujamGheng", "UrbAcwetMytNokku", "lonAutt8", 99 | "giFrityin", "Kircyu", "IcNiktiesBu", "asJofImordEtyo", "EppEtitnif", 100 | "irOoHiad", "PanFeang", "HydUchagMenbeHeccaf", "yoipRyg", 101 | "JiudNacDyomgabSaf", "yasfoptEbun", "TiajRecarHendyobr", "yabKu", "EnHetId", 102 | "5OwHimWugog", "RiemkapsyemJajmoj", "Wamt{", "egNijCyhyfyedro", "Batkim1", 103 | "eysEidVeskea", "LeepNog#", "ibhiasFoyshtatGa", "acNejcapt", "Biewch:", 104 | "eucIgpa", "urjAn", "LiptUch", "BanyivPiamitdilAtNo", "jilEtDawCyfheOd[", 105 | "ApEnardyibvirursh6", "Anhyegg:", "shnuSibFoysAwyous,", 106 | "tanVigrappyadbetag@", "nospErc&", "traldyibOug", "Meidquicdamyilzow", 107 | "Grersuv", "dreKaHomLidam6Ov;", "FacksOa", "satIkappyoj&", "SkyRyg>", 108 | "InacUtsAcWetTonyoum", "Uthedhaj", "smytDaldOwlyer", "canendufdef8", 109 | "craggOacshindidNoy", "vaydOlnisOyds", "idBydIdkirHuf", "EwGhomjader", 110 | "endyoi", "Betcee", "ceblekOckrithedIksAf", "hoheGrejNijRu", "Povjonlu", 111 | "PryidEcPhadOpJoig<", "CiowcasJoy", "WodNoifci", "donviehesyond{", 112 | "rist5owas{", "aujFaveaj1knenkEv", "EsthobtoxyebkukVu", "huAsIcawtUjpo", 113 | "goshyewkajNondIc", "crowvye", "axosOlQueenyead@", "PirryimnaspIjajOit", 114 | "runBif2", "aynukto", "Mos&Twell'", "bawginarUksawvirtAb", "dreetvoog9", 115 | "Kaunrorad", "codsyickEutBisVoHik3", "Vehar+", "ranvens", 116 | "GourkilUd'Ogneamt", "cyghilerbyodJifCirl,", "uxDygdyResyiecgolt@", 117 | "thuel9Ajhis", "cugBuchCy", "UkFawoad-Gripyk", "IdVapyotEe", "geytgieboy", 118 | "MiavmesoucOvCybvevar", "clonWopir", "cecpifeiven!", "akVuromyecKa", 119 | "ajMunfumEylf7Wokye", "5ogwibrOug", "Needd2", "paycseuct", "doishSeikcoog0", 120 | "LipBurjAchHorervyig", "OosOdVikya", "plojhikyeicterdopp", "Lenped", "yitch", 121 | "CoajaskesgoogNeg", "fiphanIpoyktya", "sodinjocWyubs", "yadkiUpDim", 122 | "EwsOlrutbokvedFoc", "FrotaphJinoitvilye", "noygadfi", "geckwynic5", 123 | "TrantorIc", "drosFoshtoav", "bipvalCo", "derWegnedoiktetKiff", 124 | "CetJercabfuft(", "OigIcye", "TridibGotCu", "BifchedweivKedQuas", 125 | "neifnuewfEbditNatRug", "GeurzopBand", "krogdutMeyrocNee", 126 | "KerjEurumfifDiav}", "UdneaHicijSibtav)", "naibwukTofoc", "OsockasFid0", 127 | "soyryfsoinpie", "temgak", "kawk8", "wocev7", "tydHyesghet0", "ubAfChuj1", 128 | "awHickHoytduthEk", "GimimHiopNoawap|", "opJodJert", "guemtyiafogUgdo", 129 | "HoFladsik", "5Dyri", "Knap4", "ukWoadVorIa", "odCuIdWa", 130 | "BohosbuewfInritOk", "RolceiTwocfucVu", "nedAkJoc", "TimyaifWo", 131 | "VahitVekhobBanen", "BlagWicyexCyrith1", "coixaj7SwerHivVugJi", "skyrieksak", 132 | "bocreo", "EcixHotyirAkdyen", "IkEpwovfiebVafIa", "foiFit", "EpWudr", 133 | "WugujBepyodfirfacfek", "nadAfeapiterhiepMeif", "yeadziHissyeil", 134 | "HuvByffubAvIgpye", "goHyt/Quacnich$", "DiodLerErudkemran", "byovseubWalv9", 135 | "Sypkop~osjigmirdOc5", "mebdapNa", "cildAijivcob-", "mekCimetOk", 136 | "dreerudtitOptot", "jarvajyic?", "WejAjCu", "adojAcepHakyek3", "givIjbeal", 137 | "Preocyunred", "chisUbtyk", "julpyijCazOghids9", "diOdosbis", 138 | "MivlicklawAwv", "cuajeitPoar", "ivEbUkti", "FuzigNeadAyk", 139 | "DosBynZedWuasitsh$ja", "agictaiQuimoinHiat", "udwesOvewhekGia", "HetGa", 140 | "pittidCaj?", "arvOaxfoyrobTumosh", "wecEbrOwnyuIdnopDoog", "AbOtodcy", 141 | "nihokdanikTiamfuIjwo", "Hopdyzbeigvij", "yacsIxkujRi", "upFocWunn9", 142 | "Tytshockorg", "Goopnusit", "EtpapCemRucwoho", "8okOl", "sirWoodOlHy", 143 | "udoyFrokfubIv8", "fekdepp", "Ipnunsh2", "VidudawnUticCafuc4", "atOab'", 144 | "SyftiponJakogDib", "mecraynGheb6", "JuWadGi", "CenyilAmOabcej5", 145 | "boijpoic-", "zeth}ogjicMov1", "blactUb2", "NottApyaw5", "CronBazAgpifvosBa", 146 | "molurnupneiz", "yiecbi", "Catseryiajyat1", "snodgemBy", 147 | "witZenkigId4SwuKut", "kebEkajObcaytyHeaf", "CrorgAit", "neShreogwa", 148 | "pivZangevoxOn", "vumIrvag1", "EvNeol", "FocboagdotmipHyn", 149 | "neyttIvBicAygpa", "HyRemDaUkTums", "Nauvtown3quoa", "bicgolEpdeani", 150 | "iavyivmytefOxEtyaydd", "tihilfolusEk", "yamRewIfegwufufchir", "ganlaj8", 151 | "nuejyonkeggUnId", "JeghoowdAiclIbrEj", "dryursIfPysBeTwoj\"", 152 | "craHidyivVovGoon1", "Ushfeidrec", "ec.Ocheyg+", "CeWriwuikovUsDetan^", 153 | "DupsUnWyovadWupWov", "neirvofAdtuseb1", "gupJongAr", "buvCuojoodJij>", 154 | "RogMitMawMeyd", "SikEvkotDab", "GhiadFejkay", "AdHimutPhi", 155 | "evJuquadwadKenJur3", "FoddArugvib3", "FifPygbakTebs", "loquefbuShrekWyac", 156 | "VisOdd", "Loommit4", "CukFuewdIcOuKni", "furashtogebCephDet", "Geopyoym", 157 | "kreichUgHyWrixEnia", "EdCiPyukwavSuryo", "decnarcIaceijteam", 158 | "IcotDyflaibJuwan4", "lihatgakph*", "Whowyashtov:", "FlejVig6", 159 | "CiackNusDyim$", "GreypdypUcmivmu", "eeccajajheut", "ToalyidVev8", "cicteu", 160 | "deheyRelusbamGofs", "crenvijfoab", "knedyityeHeyn", "WreOvyic~Flepp?", 161 | "OapodJesh3Glo", "Ebdaukcat1", "OgOgPon", "voawwemGhic", "OuHek1", 162 | "lokMylbuc", "lalcavafdetak<", "WridIlibrArEewf&", "viawf|BlolRirya", 163 | "Goidim1", "wumacenVakVaijro", "SpoylvEkIfs", "EfDytboncilIdTo", "WharEmp", 164 | "nughyFrodhopEmpUbvi", "kevIashEgOjabzudbo", "AjMav6Cuph9", 165 | "EgJitCiddagudUg", "veddups", "Normebs", "wert0", "yeuslalnifKumnoav>", 166 | "nisejIkfubyey", "UdCebCotfoalEa", "istyam", "Pedid(", "cicAfJivgishtuptOk8", 167 | "nufyuhiocceupgog", "OckJetzatEzBo", "JajOricAfKop4", "latAf%", "Nac?Odyu", 168 | "vohou", "ShertOfIj", "FrobBebAiduvqueHet", "NogniUngEjIagau", 169 | "ubadtirbabcaiddUgOdd", "awpIvNojCatCetonzen", "Scorlaf", 170 | "rushipyeuHebhoacesk", "byikgackWid]FrieGher", "ThejatcegFihotKess\"", 171 | "outec7", "JoyHymficHishgarl6", "odegopNejAdIr", "FudvuipokKa", "grevrey", 172 | "JebVerhytNunof", "gheanloc", "yuejuc", "deadogTash0", "Isryct4", 173 | "deSwedTeijHojeghigs", "Dypsyej", "enGhenOt*", "bajabs5", "Mieweglyac0", 174 | "wiasAvlicGoff", "idyeaharjOckaphfiaj", "EbUdsojWocks", "FadNagepVewvIg", 175 | "AdDicyonAf&", "bicyijRir7", "kiWarfEmyuPajmas", "koujyiefIdto", 176 | "Vackreirillimyik;", "Jijpienfeghavcen", "JuhiphtojArpIpOcJev", 177 | "ViryajbogboGra", "vavqueybvunRijDoDraf", "yinifjoiv0", "OukGuWokcisca", 178 | "ocHarwyopJof", "JoihusjuexOuj7", "scejOvbadniv", "ScemEecigyu", 179 | "LuvCuikDufs", "HoshGucwukHuOrvavTyo", "jojFajIdefna", "TibTovhedJournObcet", 180 | "PigAnkOv=", "irtEbningibweg", "JafUddAtgajRetnai", "yabeiboors'", 181 | "ghedjumDycsOpIfKirs", "Mofkodraij", "CatMorUkebFocEdba", "FiMaw", 182 | "Cysk+OfcavDic", "guvFidouxHibOuj3", "Wynlealt", "piwamvusder", 183 | "nacbiefAwyoylm0", "ShwectecMy", "tajCads", "PooslalCanevJunoos", 184 | "LuBacawWa", "PindircEg", "ebalgAshistokFiLemm", "osFord,", "jintaiv1", 185 | "Tref&", "JooldirtOmRoHaj", "BiadUnOtJagCylkug", "ipgapOxRopwak/", 186 | "1DutulceSwyns", "bahoclafVejCucBij", "tysewabTapabvach", "onhobsEdHyrachKa", 187 | "gahivip", "Hijevbymjuluvyafow", "yughLep", "RuiphFooriakWas,", 188 | "OkvacAcefkaxyefuszio", "ruWrekweebijAvHok8", "dojdyp4OmAg", ",sloufDi", 189 | "TrawJavdu", "iskErcAfvirfyoigDa", "VapDesyevocfotItju", "Tuickacdeeb7", 190 | "VeHigtiHidLoceukboav", "MijAcOkkuced", "IjReot", "KoDratlo", 191 | "yujopFeghibJa", "tygMadNaubgasjul", "ecktejIttheusdydBi", "wenEbEbfotCu", 192 | "InomgimdyRevNift", "OajcolHuijDymyarEs", "uchut\"Ockatigap7quix", 193 | "gupepitpy", "moidfevnuppye", "WypyiarrUfKektug", "padeawmeal", 194 | "vigDaphodNiajUlsh", "WykMizujnirUtTid", "KiavRyk", "ushawyiacGehetep^", 195 | "AnnosbaifDissirmep", "hithAshdok1", "Iryum", "VuvlevDinnyarj>", 196 | "ednoHicKic", "Ducsobboo", "WrennendebJafol", "JochafAj>", 197 | "codJivNenVovGisJel", "newbUccaf6", "wabQueicta", "Fradser9", 198 | "kogh?DrobHenCarth3", "FejChetdoodaicbal4", "PixByethPifs", 199 | "dafhotwanziabWadAr", "RokAbVo", "eyljAjIn", "lorvekibac", 200 | "twowcurryuabnazEn8", "Hessecyipyov", "fadJodghowsEv", "CritNasFirIm", 201 | "KroHoonPidoapdod", "CebHyacbo", "keyFraytnoavViv", "IddeutKulk", "GliOffye", 202 | "ejocGotaksyup", "ujGhalyaJil", "ReadNep", "WreakHagCyrywedTa", 203 | "CravDonJosudBu", "^HandUfyeapjonRagAv", "thanVoovgoucDetGopon", "IgDorhef3", 204 | "emobgidefmifJajicni", "JaQuakjumrimpEaph8", "NadJashJeabryafUc4", 205 | "CrojdolgEf", "FawnEthyacyakZun5", "PyddabsabCyshmeQui", "beshCecs5oc2", 206 | "Vekoan", "woros]", "udAid@", "emwespitAk", "DouphUjto", "yuhynejtabtecbewf", 207 | "OtCiowAv", "JiveshOk", "rogijKoind", "cliocHyfficsInirm", 208 | "opbustEbrOgniWee", "RochupnepnoQuedd", "Ben^Omroj", "fevtojLue", 209 | "nafwocnoas3", "DryzpopDohoHak", "CovErim", "AwyirjajcoujEdFedd", "Kevvej", 210 | "taxDas", "BuvdegEjMi", "fafBion", "zeftucamcy", "umeckJean", "Ac2Kna", 211 | "gheisk6", "gheerk", "EcAt6", "Myavlyuryirn", "abTagduHujhowIb}", "poctev9", 212 | "TykvubsyuOj3", "EnciakApfa", "alomNurewonjeuc", "yijSundyeggodBavquoi", 213 | "7twekWyatCew2", "rybuf5", "TwasDehutNabni", "TrejSuth+", 214 | "BaigCicervopDifWiaj", "turmyatOgyelk\\", "ebdirIrf", "CedVoGryhubWygAuct", 215 | "IajnolciWu", "godVahouxeib2", "OcCeekCygipFilm", "rawrenk7", "nagDau", 216 | "buIckabacGa", "UvgugErgIlWuhanri", "bleebres:Quor", "ijhobr", 217 | "OckMoxcajAgyey", "DivJa", "quivCudWojIs", "Ingonyefexfelnu", 218 | "DecsojDojcakOtKogWy", "CheunenAtEsIkCo", "fiasisAykup&", 219 | "ClarmIloawbintApkib", "nertoaGrouc1", "BemGejigdamyoag", 220 | "quivtutVidruHossOt2", "FitDikUr", "tipt.", "GijenghaivEjnosGond", 221 | "owWuibvoundyookKicvo", "rakyuteagIcs2", "EikVu", "whimuc", 222 | "pi#queelufjaHastif|", "OpkewEwgAcjoyb", "KegCujogNiddUvobdo", 223 | "dargowCuOcOank\"odads", "CubNag4", "QuovSynryec", "Iawkess", 224 | "igTugsiss*Fra", "DeagBubEfsoimWugiv2", "Choctic/", "covdeaTidpainendOr", 225 | "piHerOwkatfedIdoo", "PiWann", "yaskEumkefUsGeinkaqu", "oirfaijCewHo", 226 | "SadWait", "ceasimVoytFu", "CecvavEe", "yirkEbUvCa", "Crisee", 227 | "meinkunCydhysOo", "vic8plezEwtyoFrickfa", "otOducEv", "swepovFebmyt0", 228 | "fiwutimgoakcubecThia", "Niowen", "Socnomar8", "FutNumya", 229 | "didfootyains:Fra", "jeft1", "essyu", "ojnoxcheutBeybJam\"", "hepsIb", 230 | "HythybrOkfoltya", "SamDycsEytnemtIcgec", "Aikopdef8", "lojAkOcOr", "quimp9", 231 | "secsEkya", "povLoinyashDafVivwy", "gawtItActagjor", "FilcunHampAbgonij", 232 | "yaHedfagIckRasghelg/", "eavyocsUddIlbewcak", "orsOa", "midrerUk>", 233 | "kacCotMidban", "loabjivucyeHuJa", "lypBeHeit", "wroKosarujomFiad", 234 | "risJovquak", "deejyoafVickyebOv?", "yeyryxfu", "oajAnujryatro", "Scansapp", 235 | "drauckIlt", "teloacOshk9", "larrias", "wicApCedZoccydVotjo", 236 | "paryamacHyac;", "Wisp0", "hefOwyelm", "egJoutvied", "pabuv:", 237 | "AzjiwriryotLam~", "tetHuedKetabiagHa", "phufovCojRij", "netdeesIr<", 238 | "UkwinevshugjerOj", "edsarcavOtIbub", "jefPoottebHuWaf.", "jimtEdBiUp0", 239 | "AchaddArrIther6", "Ipyij-", "yagVifPilshaydvis", "buhancit\"", 240 | "wepOft1QuigHea", "spofijwithomyaVet", "KnawckyiGryahackiwo", 241 | "ratdudBacweHai", "codGi", "OcutAxugCacyuefZugbi", "quebnoglaHit", "yadWytt", 242 | "Chruesfiajwijfaj", "jeltEvanyel", "Hipcit(", "Ingurf2", "WetObVerfankAi", 243 | "yedrob+", "yijNo", "yusVafAr", "heecpeDrezekBan=", "HoinjocFoibEymVo", 244 | "yuilvyarrOmboynsAk}", "jidag9otZaisyitcedd", "Doythok2", "vorlyogmerp4", 245 | "yeyworhaHoit", "evKecHojiacJap", "vagnobBicdoHonaimRy", "pylvEj", 246 | "zwabUksa", "Itpok", "sutGinyac", "FadtaisjaQuewnEcJif", "BinyocZehi", 247 | "Erdevserk&", "Grovquehi", "RafevsecnartAsedAb\"", "FeanowsApDeadrigTyk", 248 | "twaudou", "youmtepyiv", "munFeul,otnad", ",fraHewf", "treErsh", 249 | "IlbAigoddyipyibsarz%", "picJuxWalIgInPimim8", "feHashyev", "eabEcgotDu", 250 | "mectOrjOdcygto", "RynAupvegOmRuvtit1", "hebyutGhoalv7writEts", 251 | "ninHiockEnnovkuf7", "vefbu", "giudEdNi", "KejdeichVogyauckmu", 252 | "DycsinSegfiptOk", "Renwodnovyawjaink", "akEltEnk", "cassyeOxiagCiaws", 253 | "sheocThoorrIboketGie", "bifJapJonra", "Navyoph`", "BytobModseejKorpimaw", 254 | "wunbekunsij_", "RocNi", "elleexiac", "CilelEdvolf]", "doorf5", 255 | "NabHogBykwekenyis2", "RetOmm", "PhiphAts", "RurnoatfobEcphofs", 256 | "yocKilylpEaput$", "pacOts", "jiep6Swu#ha", "bi2Swodsyaslos", 257 | "MatjunrelNupoyts", "Akjuew", "doihyitHathQuig", "UcVeownIdFojDovric", 258 | "klifhanEetBewAtCoc4", "ekjap*", "FreghajTa", "bevEaHacsAdibya", 259 | "DoobsItGiEnFebcyk", "nueczuvAcJo", "tygrib@", "EkTisibrUmisItna", 260 | "JekarchOts", "HadyoffIsUf]", "vidyicjicNovHirk", "yeharkedUkKo", 261 | "tafryopNifs6OwUc>", "eepsyosEk0", "creeth", "Eps_quaybduggAi", "Ghimnufdis", 262 | "Knubboocsudotveu", "VecthalshyubBo", "NevugUtVod]", "Vushwounupnem", 263 | "paysnIcnu", "KlecjegBynpea", "icsecWarEbgachco", "kockba", "BoygjeOpnio", 264 | "Jompyokhec", "ChovShiubdegasaukid", "DourvItMou", "OgoogJeews8tygbycs", 265 | "Uddyoha", "thiFleckiOtFap,", "CerhyapIj'Obag{", "pyivacKicanbicevjeks", 266 | "TyatJiOlv3wrey", "BoanWiphhof", "JaderrukUrAgdovogOs", "bunisibgodBy", 267 | "VohakDayg", "jijNezgogAshAc6gralp", "acvivkecgiltukvouhuc", 268 | "iafagshyefgapIgav7", "osyayReldem", "JuenshogAy", "AdLic", "kuap3", 269 | "jarmosVanbydsosbimm", "yonpoavAsIt", "Stan+ojoj", "cloupyobwojruvtajMai", 270 | "cukyudEygOrsUc\\", "PrenBeonanFoussAkZo", "4shluwyRicyemroubTu", 271 | "erposhtOjMovEytvic", "dogCotkatComyimBiahi", "daygcer", "ouhificGiats", 272 | "lepyedreed5", "CrodCoorHifDonut8", "BatNivIkRespOtbio", "SowfyiegNeyns8", 273 | "idefgasaifyikTeb", "HyRetFovHootetar", "jiwafvuctUc1", "bijVioldyagFatJef4", 274 | "Getguk", "OkabHeOddyekthogs2", "Priojli", "neiveduryafbyeSwom", 275 | "CanupIsHushGiktur6", "BokTyStedPelyenCye", "norgutiltEmcealpEa", 276 | "rhetzooGlavJivenyaHa", "pagli", "ivErIcJibockEpda", "vibyattokVefsok", 277 | "UryotJaptOfnipoap", "6QuitDobomeubRic", "whidPydnid'", "Krisht", 278 | "vedemKoltyif5", "JipmytDityaifwobdig@", "GrevEubhashcof9", "afHirc2ogjag/", 279 | "WinFuawbaseo", "usUnmafDyp4quagfowv", "euhoc&", "gairtIlf", 280 | "finnAbrAljyekBor3", "MijOaph", "knidJu", "ojvatJafilNa", 281 | "MymyarbOnFepp/ond", "ClojeavNerdav3", "Etoans@", "Wosna", "fiolAr", 282 | "peavPecipGawkEgPyHy", "BowfidBiater", "Elujradsab", "WonRokciv", 283 | "tesworyugBosteckust", "yigHis", "ecdarhimghir", "IpBocofivjoichDag-", 284 | "osyidvodpip)", "thotdymRuHo", "elucmewio", "FlacomOot=OckCett", 285 | "yegdejhodPip", "flavHut{", "twetNa", "UchadveekTiWie", "Aygphivgou", 286 | "LashJokOrtasbiervEu", "ShidnavocNegJujPosau", "crocs", "hetAisJofjevoys-", 287 | "ScelyebnifDiWosawEnn", "DowpyipCakGadriccyri", "jodFectyuWocOvdo", 288 | "ridcawfaikVeol9", "TafEnumAjvam", "Kevoc", "Rhivchy", "Dayms", "ikmee", 316 | "krilpOtBuzt", "Shicroigsemnib3", "whonHujBal5", "MyabmyovVek$", 317 | "ubEfhadAidRo", "gritkaddakDensidCac", "KnyorpyamkaiftIgsuk", "okUng,oc3", 318 | "iphcyokpiotAcEc^", "soxnuddyercesh;", "bouWyctyoc4", "Bedlydvo", 319 | "ChrainOcwy", "nothnadsyuGeit", "myRurOygeemtAjlis\\", "SoftOsDybMowedVis@", 320 | "dyevJowvAphAb", "mepnegaijutt^", "CiEcks", "Weutdog", "DudTenAdUft]", 321 | "ghem/", "DroheHevjophaHig", "Menorp", "AkCouc", "diVesot5", "HeshyacOn0", 322 | "joyltEp", "NieHedheajQuikyewxef", "SadejLoarg", "pijoogOyns7", 323 | "riFliafjapotFu", "LykvapIth+", "Danhavnet", "abpojWitkugEedchovey", 324 | "FithUb8", "Rydhyllunalgobphid", "NeizditLidMeoc", "TriUdjajWobyinha", 325 | "-twudneOtJujnac", "kovdaf", "lipdecgiOrmib9on", "dafNeivcharWoncasp", 326 | "UrshIl", "attofnuWyitgelIn", "GhirtItHypyijcyavgau", "niawAlreutEp", 327 | "OjBubecDoctyekEit", "NakJudnahou", "EgeacCobeuFrievJutap", 328 | "rierdEehyelthEnd", "myng5", "awvEvNel", "patcicjocEuksh", "pacEemyecichdio", 329 | "efyukhunVicyemRo", "RoojreunIgDujyas;", "ishIadjamral", "IluvDegeesJogasIj", 330 | "Mojdan", "joirvughiflushvomt", "vopryk", "MedNawip6", "nawcosivWatKoybEcJi", 331 | "yokanIrWodta", "UfaijthedAfospashUp-", "CipCocnaijlukdo", 332 | "keWitDoisfaydKecoul", "dudjiel}", "guWitFieksavyieHi", "GattIf", 333 | "ocPydFadunru", "wejfilixot", "hattEcEtJojCurduvcon", "uv1quouff[", 334 | "Nahefwup", "Travyu", "grysUthjoyk", "BavelnekEbEshk!bla", 335 | "byntowvEurcOuv.Of[", "JuomrictevyeHacUm", "codHu", "EwUlodId4", "IadtoufKu", 336 | "yoacwo", "IrdididjerbamcejCo", "9ObgenHuIdva", "hasJittopWap8", "omtOs", 337 | "CaHutJifmap8", "idsaufhannowk", "VarIdmag7", "VoobawVigbytsetfu", 338 | "femNejBibIcCiOc9", "napechUbs8", "tracdyenciFu", "DyrabKusJo", "IranUtt", 339 | "dyraphboowAw9", "RhoptItdogOvPyguv,", "jaryorpEphBay", "IsShemCy", 340 | "daiWrytsics0", "FucEjbesBu", "Apidwo", "Idas1", "EcIanOvCukdugAuvsobs", 341 | "erIkphevshecni", "SheorkEpil1", "KivDeshAs", "javLepeng", "Ligti", "loctic", 342 | "nawmibjeojEb", "ecGegsIc_fremGha", "emciapofhatJem", "cinhemNijIldAdQuon", 343 | "LintafthushGeph", "mecbefnetnacots^", "byct'", "PievjotConzeo", 344 | "deakWeutAynOckibrubr", "meryoyhof", "galwobcijfalzeij?", "yeymgab", 345 | "gradfassAvacUc9", "derdyahytVunn@", "LeUmDi", "an6Dri", "scogajyalNeynjib", 346 | "weruAbvoocHict", "ockheedat9", "Repol4", "AwowCip6od", "mashGep8quan", 347 | "Oc1ObwefGhoanOmdew1", "yivPheOdTo", "Wojrys\"Li", "pibNa", 348 | "clodJobthukfejKag", "cadyis", "tucafemGoijyom", "ajwoybJiOrryij2", 349 | "Fodvecos", "nilCataydmoghEen[", "nidcisk", "wevViHaufiv8", 350 | "yijerIcyimtEjLia", "yeyffosdetManVotem", "exkodFuchkaigWa", "ikghoudVa", 351 | "RigrartyenRiowwelner", "NipKiavDebgupin'", "howCahijbieduth2", 352 | "fevEdseyHye", "Frecdygmerirsyo", "VufJi", "FruljojEsGerlAfViJou", 353 | "titIjOgIss", "RawlifogDy", "KredichFicesjocishfu", "tiorucnagsodedwel", 354 | "3grukneEdbee", "FeochMajconByRec4", "viskIvJan8", "idpoctAc", "yacEckpy", 355 | "Erryew5Gruheik", "8wropFidHomEa", "dogferk%Blofrer", "dek0Och", 356 | "MombajTeytMas", "BlyuvFulgicIn5Slos9", "fumyoakvinjeinVowx", "yejdoushk", 357 | "Lohuj", "PeambykotEpdeks<", "crukWywachArs", "RecHajwuexrihy", 358 | "CeacKockhish3", "mockco", "vaivcybUj4", "WecyimrettemunpoHap", "slyrycs3", 359 | "Tevdot", "gaphKijujSepyuraudUm", "CusnOyb8", "RohajTipgucyerf", 360 | "ShriajtabGilibgut", "DykfejLijonjodot", "IfJacOadsOcmuj", "DajBayk", 361 | "olHejAxBubPef", "yeaduthOdsOdyetCeik", "futTunMiccobEfya", 362 | "yem>OfhuShreelixJow", "neicHalbObEulEut+", "BiJuheibsEkmazJo", 363 | "agNonbyShynnOvye", "TemMo", "RyazFoGlubNaj1", "fucJatabAmt", 364 | "Froothkaxazmy", "PhuvWenAjdofyo", "yebTavGish", "Racjabyusfalm#", 365 | "jedigEjyelg", "LipOadNokadEkoxEtEo", "DyHiebWyp", "UfIdyelgAwUrvye", 366 | "KrerpofeckwyinFok", "oomDiwawufcocsotBat9", "KroubImtItt", "EigIdku", 367 | "duAmcubOlbajTycs1", "TheHy", "jivdecamgojmuvJag~", "drojAfJivNer", "mavSo", 368 | "DavWefPhiheen2", "LifGa", "shelj", "biBladGecUjnosVef9", "ghaimKadvibli", 369 | "dawJighigoyWerOuchya", "etDofdabterAdelp0Oc0", "apGefGukomobyitodpia", 370 | "TyffEcHobcoaHadBi", "lirc3", "yuWoashsoot", "dejojsabbuchoc", 371 | "poksultafcendeun", "jajmik", "Coawghobtig", "narikculol", "pycheev", 372 | "tocNob+", "Rendau", "WuvveriajAkEwsEtVa", "VicetpiadAs^", "VesArteax", 373 | "AcEyg", "TobGochAfim3", "Tujreiv2QuoksUds", "ot\\kniorcIabba", 374 | "Akreindegbofewcaj", "fryRopyuOcGovvix", "wedorpidNec", "UvHamreattufio", 375 | "1ovdysopNoheFreitjev", "emmyaHipUbWerfOv\\", "JanjegVa", 376 | "DroickOgjipNanciol9", "TeTwokIr", "yikurv", "migAv[", "evvishjubWiek6", 377 | "Apgovudmivghek!", "rigvedajoshBawl", "spivyatka", "EtCoy", "jehulkobyam", 378 | "JicHet8", "NilCeufdalb", "UggAigVourj(onph", "gaHogFa", "nulgyidnalcefs", 379 | "yoymBiwegha", "cithAkbecbarth#", "HannIjFuigOt7", "@Bleijdaufnegyonec", 380 | "Avpodhyb8", "toavyegbufbue", "Fimfin]QuaidryelZasp", "glavyoawckAv", 381 | "CujcujyojungyaHabsyi", "UdLecAfhixLidoicPa", "fesdesh", "glauquilj", 382 | "EbwufGhad", "ProvCiavVegEy", "uforyunbicpet*", "kojrydrynn", 383 | "IavCuotmimcagg", "suher?Quiewyaun0", "motGidGinhacpi", "HobHyham", 384 | "abbogzatfem[", "lygmon0", "StyksuphIthMuvuObau", "CravnotGucJue", 385 | "netpyHeipirn@", "JujBojdan", "TomWiho", "opidwotAkAv", "VogTubhadbee", 386 | "Slusp7", "otbupSheenIpzin", "AgMibfanJisdejos1", "CewfeptEulfoathdowt;", 387 | "odUdChad", "lowmAgcutEnd;", "EdLop6Ofij3", "TuecobZugoacgigJef", 388 | "UdwudWamt", "CemEfGuitLinyefowEb\\", "EannedJocsagewnAccal", "mimiagCend", 389 | "Gibai", "AwdOlt4", "giweb", "nakGuAdGazLelElk5", "TaxWovNig", 390 | "dibrUlcumgawnOik", "igFicvetgiUtNiOrp{", "BuptOmDolWyRok9", 391 | "DitLeydibDarEciss", "KeQuicVowItbuAl", "oyltIbr+Quormok1", "NeuWershIasji", 392 | "Clirwinyoyff", "yefdo", "JowyinhovghindEf", "Grobcicemyenpi", 393 | "RerjattOgdakvush\\", "Joowval", "yossyoahed", "UlwunoGreusjahujodeo", 394 | "Boylk", "cryod", "yetudJoi", "LeHybkihoysbeipJeys", "peexRonMikEnoikdetwy", 395 | "Atfavcomjiosh5", "yousDu", "yerwofad", "frogdimOcjet0", "BofyokTipCewl", 396 | "imfed:", "GaydVejyegcyg", "riupOmNuespOrd", "FavRa", "myHybAwkAmgob", 397 | "vuWreHotsav3", "luwup", "2Queitnu", "hokPidesIcModLucDoom", 398 | "vawhuojMawyarmuvij1", "WyctUrd3flofIp@", "kaniarcAfvedsen", "fowyagJad", 399 | "revor", "Lafdafleug2", "roammeskiddyuhav8", "feavrocug#", "Uccenjav#", 400 | "DobOcKog6tr", "JufwatnujphealNea", "LiedBuHoskegg", "pandUsyowgyan>", 401 | "igPeg", "keak7", "Krothjac", "yirdAf8", "ranthArl{", "DibyWoockOwsanarlEw", 402 | "EiksAggavTus7", "nawmalavid", "JapJededwaHaun", "byojhurf", 403 | "iroowcetJafnox", "frailtacvu", "tuweshCad", "Pennaun%", "IkhiebMowt", 404 | "VeHinrielshidJoan", "fudAjdof*", "tirpeuhyephhicsefbix", 405 | "DiwyzFaymveemshawdac", "oijMeryagOdjic", "FrisvagditNic", "Mofeinniakfa", 406 | "OicDodNergOireccaf", "RyHughoGriUtNebfidd", "clisDothKird", 407 | "MydEgIdnimAjrek", "Vouc4", "JartirpErladOrIdTeyk", "Ityoirwig", 408 | "jastyundAganyehor", "EjHejHafmud;", "DyokatOn", "ondOldaddeashAj|", 409 | "hakendyobobjicEym", "Jobsosp", "lyGhadIalNaygIdCyar", "Pidth>", "Seafoyk5", 410 | "UgCakDis2", "LiastevOjlagiken", "twunegs", "CujReabTebUfeg3", 411 | "NejagjuesFaj", "LyuchBekBanbet", "Odorvyuasyoi", "thalvyadbeibHegot", 412 | "GlakMor", "EnArWo", "orkAbFomedUkKov)", "VoingExmysbarkUbWeaf", "OcIfsaic", 413 | "TidlotVabnarAkOi", "dafdessuzJorucgat", "Kidsurbyatirropjut", "SceerlEmub", 414 | ".Tweug", "ReunAnkad", "RuWijkuc<", "trirkicCyn", "pynRyHodJorkyuca", 415 | "Preurtyin", "grigpyes", "Olsed", "pecnel,", "liutCewthokvav$", "GicJet5", 416 | "vobKegMa", "EcBishjisBuvPem", "bettyijIlciGed", "moHelnew`", 417 | "EjTesVol5queewrauch", "IdGejDomNowmOmjem9", "HasBifPed", 418 | "doowyechtafEijuvRun", "drekbyumeurp:", "smujatCisVaHewkya", "quokzi", 419 | "deGruob2", "tetmyFlitLydsic", "IdvogulGhozjas5", "eesyowCeogovGenMij", 420 | "KnighRavEf", "WremRocHidro", "yarEcdodCecheitsOm", "latrecs8Glic0", 421 | "WiacfisVabIo", "cywyow", "runBivniHebci", "cajFejevHydecsej7", 422 | "GoomIpjeDredPou", "EcHewfetEshBi", "NickgehennEcBuseuph5", "ugMiofVabCa", 423 | "]shnihifhatcodwy", "plurphoins", "smerc'qua", "wharsharn", 424 | "cicUchjoaztoifbenkat", "avWonliWewEnCu", "natvekOkpoof", "DujPytPifoitt^", 425 | "Giacaivdarrog", "RoivdukEanLikhidr", "raveuhuvowAckwiarn[", 426 | "sagAuWytyavsid8", "KafGaugviKenBumCyu", "KodImOjAcdian", "crizJacPeask", 427 | "yurjAfer", "KeheQuad$", "KnulCysen8", "NuetOo", "imnoopucsyerlIrrye", 428 | "Egtheb", "JadUssEjBactAfWoc7", "juAcdabheipjerVuAsk", "etnixAlceOcmij", 429 | "ShyurrUg", "MeHunras", "AptInleHethaybOkAib", "yuekAtjixByibsArsOl", 430 | "CusjehuvLednitpemyey", "ConcebnoxTopCurt", "shwanumayRogigdo", 431 | "tuchDoyftAwl", "LymImtibfaWrep", "yequetNuondaskejci", 432 | "naifHuhogBugtazSikto", "NojBen?ovRaiphUb^", "koynRivshuj", "ezdaj", 433 | "ruevWij", "BegPounrainPysayreb", "afEvUtleObr", "provdelvEmKest3", 434 | "fethdunUcdyhakMagyi", "HewOisObruvfek", "Cofdajzo", "hefkalfIdfiquofalkaw", 435 | "CopNej", "BilbyacemDam", "custUrdakItOc\\", "yidUgyirjou", "icderfOwfektev", 436 | "hoosIrrAfvafivNo", "yoveetsat", "Sugar=owx\"", "geegUttOicWyu", "Jiphkol0", 437 | "KaddAfdipNegsuc0", "yoifjiv", "Cheggoigwonoiqueef", "mibvaifdultAir", 438 | "peOs0AppErsodAfGa", "twyevfig", "nuOrm", "8GryubCuk", "OrvyivpolsyopyepIf9", 439 | "cykpyotufDiwash\\", "drutt3Quol'", "NusEjtuAmyiegPeft", "tu\"swacks", 440 | "WroGrylkObHynvevar", "CycsesAfjatDulafjof^", "naDroucMiectAfsyeg", 441 | "sonCycsobusckUmrys", "yufub(", "DordItNonoxCaljAym", "OnnyojyunfurWiOmDy", 442 | "TwechtefvedkanBowv", "WedNejUvSilmOmoniduc", "WrengIbrokEjUt5", 443 | "kiWiacViaphzihakigs", "NityiSwowmovBepIdHi", "atdon^", "Teats", 444 | "VepbehufKedsEj|", "vuryudtuszedco", "MopphifFijdiusBot", 445 | "NifEvyeedakOtyadNu", "Dujpi", "OkgarghawvOk2", "idwouphjadPhuwakAwk", 446 | "dekchicWor", "einvaFrewWow4", "enwarr", "OofErcInwifOjAkrio", 447 | "RihoghCuOt,od", "WiapOaWremDed", "OveacbyifUtvaHi", "PeHetkej", 448 | "FamLodObIsjuQuof+", "RhekGhigekEqu", "Shefeernya", "priWev9", "Me9Rer", 449 | "necIvbaindEanWicEj", "eksIvWicDoysekAvda", "AgbawbEzGim", "nilkowUbso", 450 | "gejReetok8OkBygTo", "CoHircecOodfakeit", "NulsElj", "Deiksonsyob8", 451 | "joc8Gliup", "rebNecks", "jurodPobIjwonIcEn", "BiOfjovCerdAcKoiKej", 452 | "Kehyobvov:", "CukHacElEe", "akpeiph}grysckentip5", "Edgubeuv&", "GibSha", 453 | "IgyaicJikcaddacwed", "jaFlib", "Wath2", "mosh7", "Befvi", "Sybidyejeewb", 454 | "ghegdyftorcitTa", "CyTwok7", "NebJeafNutvoidyinAf;", "cowlormumfoi", 455 | "sheashels", "NiewEe", "necuvpojeudMil@", "Peodnig", "CagNeffatApKuvji", 456 | "UcFijAgcuwyctasFeuj", "VaWract4", "emnaucef", "RockamOfijpenyokadyo", 457 | "NumDoSweBylchobpy", "jeyljojAj", "oaryoushOnrugit", "AnLoy", "tenRa", 458 | "hybEs", "afguvpehyryidsEc", "Ijubs]", "LecnagNadjoveus\"", "TowufAw", 459 | "onEkwuwonbowya", "Novyipwapdyn", "Phrawn$", "shiesShic", "giudhenk", 460 | "Eudli", "nidmysJiryipwysHif", "Lejfial\\", "TheimyegBip3", "idUnjiotJapom)", 461 | "nacan", "UgJeGul", "pyweacdovHy", "yidMarHujHarEckga", "ucIfMegwofda", 462 | "BeagaiblibGoy", "todsumUt", "krebWeacWiwovriztyie", "pretdycsuphooksajCo", 463 | "umOshpojtentadWicmol", "Cott9", "acCumZusvedcayxNo", "TowisabmofWy", 464 | "Ceelunnyan", "&Tejyiekyojuk", "omBeskOgonla", "uphCofJin$", 465 | "AirasAibjipbila", "Nogjolebdazomug]", "KoSwyoryoi", "Tymdithehag", 466 | "EegniFuvAin", "Grav2AbAfmeavCad7", "EnHynvenFiOcEnJuCr", "RotJi", "Udvid5", 467 | "igocKeyWydlacphijteg", "ujufvuvEbpheec", "VitlyreujdigVoc", 468 | "SygbanthuvGutAc", "Ooflavyue", "Rhairg=omdegjeegnem+", "ReenVosekUcCug", 469 | "VoymDeip", "osyoshAvVicheotafs", "KnyljIgyekjibHor|", "JoggelAndOsBokVu", 470 | "Upjovurhens", "Kij6om", "phauljyoHia", "kash6", "KiFrewIdNocOord{", 471 | "tenrob", "loGryowvit", "goir}SwoinIaj{", "gix1DruKnorrEys<", "icIrsAf", 472 | "ovlagItErnebya", "DimpakIcathbyg", "dabrinwyheefCaigliaf", "yirvonBatec6", 473 | "afEignuc", "ovUcIpyoifs", "mekIfOttaddAx5", "reydodyafVadOdfilya", 474 | "OafyutsUg<", "tenn\"", "Tronrucmidvaddup4", "PackjogdakasigpejKue", 475 | "NiabkatCoyldoipshes", "RuOkofma", "frejVenjavepdin", "amsop", 476 | "molberukvofKi", "OfAyctocJelloc", "JivhubAdEednobfabr", "ilHenen", 477 | "IvhairvIvko", "botwolcanPoimnirc~", "Dekef", "flamUresoakkofEj}", 478 | "skerdOt.", "darets3", "zanejcizutyicbygdi", "eafDueshigubleog", "vetcy", 479 | "vequickgitdekganLys}", "UdCudkaHoam(", "viecvit", "geckrijleKnip", 480 | "zolmeiphAn%", "EfOdKecJorrUmAst", "waHytelyinCekauj8", "notIkWeuHerOicOdin", 481 | "EcsAjComs=OnJilyof\"", "NaphlocCepIc", "AtobEs'", "dyeshVogshEynd", 482 | "pypjepmersAryafUjhul", "EebhanyajvobvawriOct", "yinUtcye", 483 | "Modlygjennarravar", "keirdIzHudBuocwacCy", "rorjyarbinhiatJic", "odGoukfad", 484 | "cophMotuvgicdib/", "yeebtekKot3", "tiUnrohivEvCoatMif", "cipoyraryec7", 485 | "idutcygDo", 486 | }; 487 | 488 | START_TEST(test_single_entry) { 489 | struct radixdb db; 490 | struct radixdb_make db_make; 491 | const char *keymatch, *val; 492 | size_t matchlen, vlen; 493 | const char *key = "wheshivGowghisheex)"; 494 | 495 | radixdb_make_start(&db_make); 496 | ck_assert_int_eq(radixdb_make_add(&db_make, key, strlen(key), key, strlen(key)), 0); 497 | radixdb_make_finish(&db_make, &db); 498 | 499 | ck_assert_int_eq(radixdb_lookup(&db, key, strlen(key), &val, &vlen), 0); 500 | ck_assert_int_eq(radixdb_longest_match(&db, key, strlen(key), 501 | &keymatch, &matchlen, &val, &vlen), 0); 502 | 503 | radixdb_free(&db); 504 | } END_TEST 505 | 506 | START_TEST(test_insert_then_get) { 507 | size_t i; 508 | struct radixdb db; 509 | struct radixdb_make db_make; 510 | 511 | radixdb_make_start(&db_make); 512 | for (i = 0; i < sizeof(test_keys) / sizeof(test_keys[0]); i++) { 513 | const char *key = test_keys[i]; 514 | size_t klen = strlen(key); 515 | ck_assert_int_eq(radixdb_make_add(&db_make, key, klen, key, klen), 0); 516 | } 517 | radixdb_make_finish(&db_make, &db); 518 | 519 | for (i = 0; i < sizeof(test_keys) / sizeof(test_keys[0]); i++) { 520 | /* All entered keys should be there */ 521 | const char *key = test_keys[i], *val; 522 | size_t klen = strlen(key), vlen; 523 | ck_assert_int_eq(radixdb_lookup(&db, key, klen, &val, &vlen), 0); 524 | ck_assert_int_eq(vlen, klen); 525 | ck_assert(memcmp(key, val, klen) == 0); 526 | } 527 | 528 | for (i = 0; i < sizeof(test_keys) / sizeof(test_keys[0]); i++) { 529 | /* Longest match function should work as normal lookup for exact keys */ 530 | const char *key = test_keys[i], *keymatch, *val; 531 | size_t klen = strlen(key), matchlen, vlen; 532 | ck_assert_int_eq(radixdb_longest_match(&db, key, klen, 533 | &keymatch, &matchlen, &val, &vlen), 0); 534 | ck_assert_int_eq(vlen, klen); 535 | ck_assert_int_eq(memcmp(key, val, klen), 0); 536 | } 537 | 538 | radixdb_free(&db); 539 | } END_TEST 540 | 541 | START_TEST(test_insert_duplicate_key) { 542 | struct radixdb db; 543 | struct radixdb_make db_make; 544 | 545 | radixdb_make_start(&db_make); 546 | ck_assert_int_eq(radixdb_make_add(&db_make, "a", 1, "b", 1), 0); 547 | ck_assert_int_eq(radixdb_make_add(&db_make, "a", 1, "c", 1), -1); 548 | radixdb_make_finish(&db_make, &db); 549 | 550 | radixdb_free(&db); 551 | } END_TEST 552 | 553 | START_TEST(test_match_first_node) { 554 | struct radixdb db; 555 | struct radixdb_make db_make; 556 | const char *keymatch, *val; 557 | size_t matchlen, vlen; 558 | 559 | radixdb_make_start(&db_make); 560 | radixdb_make_add(&db_make, "123", 3, "a", 1); 561 | radixdb_make_add(&db_make, "1234", 4, "b", 1); 562 | radixdb_make_finish(&db_make, &db); 563 | 564 | ck_assert_int_eq(radixdb_longest_match(&db, "1230", 4, 565 | &keymatch, &matchlen, &val, &vlen), 0); 566 | ck_assert_int_eq(matchlen, 3); 567 | ck_assert_int_eq(memcmp("123", keymatch, 3), 0); 568 | ck_assert_int_eq(vlen, 1); 569 | ck_assert_int_eq(memcmp("a", val, 1), 0); 570 | 571 | radixdb_free(&db); 572 | } END_TEST 573 | 574 | START_TEST(test_longest_match_ordered) { 575 | struct radixdb db; 576 | struct radixdb_make db_make; 577 | const char *keymatch, *val; 578 | size_t matchlen, vlen; 579 | 580 | radixdb_make_start(&db_make); 581 | radixdb_make_add(&db_make, "123", 3, "a", 1); 582 | radixdb_make_add(&db_make, "1234", 4, "b", 1); 583 | radixdb_make_finish(&db_make, &db); 584 | 585 | ck_assert_int_eq(radixdb_longest_match(&db, "12345", 5, 586 | &keymatch, &matchlen, &val, &vlen), 0); 587 | ck_assert_int_eq(matchlen, 4); 588 | ck_assert_int_eq(memcmp("1234", keymatch, 4), 0); 589 | ck_assert_int_eq(vlen, 1); 590 | ck_assert_int_eq(memcmp("b", val, 1), 0); 591 | 592 | radixdb_free(&db); 593 | } END_TEST 594 | 595 | START_TEST(test_longest_match_reverse) { 596 | struct radixdb db; 597 | struct radixdb_make db_make; 598 | const char *keymatch, *val; 599 | size_t matchlen, vlen; 600 | 601 | radixdb_make_start(&db_make); 602 | radixdb_make_add(&db_make, "1234", 4, "b", 1); 603 | radixdb_make_add(&db_make, "123", 3, "a", 1); 604 | radixdb_make_finish(&db_make, &db); 605 | 606 | ck_assert_int_eq(radixdb_longest_match(&db, "12345", 5, 607 | &keymatch, &matchlen, &val, &vlen), 0); 608 | ck_assert_int_eq(matchlen, 4); 609 | ck_assert_int_eq(memcmp("1234", keymatch, 4), 0); 610 | ck_assert_int_eq(vlen, 1); 611 | ck_assert_int_eq(memcmp("b", val, 1), 0); 612 | 613 | radixdb_free(&db); 614 | } END_TEST 615 | 616 | static Suite * 617 | radixdb_suite() { 618 | Suite *s; 619 | TCase *tc_core; 620 | 621 | s = suite_create("RadixDB"); 622 | 623 | /* Core test case */ 624 | tc_core = tcase_create("Core"); 625 | 626 | tcase_add_test(tc_core, test_single_entry); 627 | tcase_add_test(tc_core, test_insert_then_get); 628 | tcase_add_test(tc_core, test_insert_duplicate_key); 629 | tcase_add_test(tc_core, test_match_first_node); 630 | tcase_add_test(tc_core, test_longest_match_ordered); 631 | tcase_add_test(tc_core, test_longest_match_reverse); 632 | suite_add_tcase(s, tc_core); 633 | 634 | return s; 635 | } 636 | 637 | int main() { 638 | int number_failed; 639 | Suite *s; 640 | SRunner *sr; 641 | 642 | s = radixdb_suite(); 643 | sr = srunner_create(s); 644 | 645 | srunner_run_all(sr, CK_NORMAL); 646 | number_failed = srunner_ntests_failed(sr); 647 | srunner_free(sr); 648 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 649 | } 650 | --------------------------------------------------------------------------------