From a94a0cdc29c82a06755e78746b922e14a511673b Mon Sep 17 00:00:00 2001 From: "G. B. Versiani" Date: Thu, 24 Mar 2016 19:25:31 -0300 Subject: [PATCH] - Created radixdb_make_* functions; - Fixed the bugs found in unit tests; - Organized the command line file functions in the radixdb_util module. --- Makefile | 20 +-- radixdb.c | 323 ++++++++++--------------------------------- radixdb.h | 30 +++- radixdb2dot.c | 77 +---------- radixdb_make.c | 165 ++++++++++++++++++++++ radixdb_util.c | 82 +++++++++++ radixdb_util.h | 21 +++ radixdbdump.c | 77 +---------- radixdbget.c | 77 +---------- radixdbmatch.c | 77 +---------- radixdbmk.c | 9 +- tests/check_insert.c | 62 ++++++--- 12 files changed, 443 insertions(+), 577 deletions(-) create mode 100644 radixdb_make.c create mode 100644 radixdb_util.c create mode 100644 radixdb_util.h diff --git a/Makefile b/Makefile index 5abe02b..ca82f1c 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,7 @@ INSTALLPROG = radixdb CP = cp -LIB_SRCS = radixdb.c +LIB_SRCS = radixdb.c radixdb_make.c DISTFILES = Makefile radixdb.h $(LIB_SRCS) all: static @@ -48,19 +48,19 @@ $(LIB): $(LIB_OBJS) -$(RANLIB) $@ radixdbmk: radixdbmk.o $(USELIB) - $(LD) $(LDFLAGS) -o $@ radixdbmk.o $(USELIB) + $(LD) $(LDFLAGS) -o $@ $^ -radixdbget: radixdbget.o $(USELIB) - $(LD) $(LDFLAGS) -o $@ radixdbget.o $(USELIB) +radixdbget: radixdbget.o radixdb_util.o $(USELIB) + $(LD) $(LDFLAGS) -o $@ $^ -radixdbmatch: radixdbmatch.o $(USELIB) - $(LD) $(LDFLAGS) -o $@ radixdbmatch.o $(USELIB) +radixdbmatch: radixdbmatch.o radixdb_util.o $(USELIB) + $(LD) $(LDFLAGS) -o $@ $^ -radixdbdump: radixdbdump.o $(USELIB) - $(LD) $(LDFLAGS) -o $@ radixdbdump.o $(USELIB) +radixdbdump: radixdbdump.o radixdb_util.o $(USELIB) + $(LD) $(LDFLAGS) -o $@ $^ -radixdb2dot: radixdb2dot.o $(USELIB) - $(LD) $(LDFLAGS) -o $@ radixdb2dot.o $(USELIB) +radixdb2dot: radixdb2dot.o radixdb_util.o $(USELIB) + $(LD) $(LDFLAGS) -o $@ $^ .SUFFIXES: .SUFFIXES: .c .o .lo diff --git a/radixdb.c b/radixdb.c index 308abc9..06e61b5 100644 --- a/radixdb.c +++ b/radixdb.c @@ -10,14 +10,6 @@ #include "radixdb.h" -static void -uint32_pack(unsigned char *buf, uint32_t i) { - buf[0] = i & 255; i >>= 8; - buf[1] = i & 255; i >>= 8; - buf[2] = i & 255; - buf[3] = i >> 8; -} - static uint32_t uint32_unpack(const unsigned char *buf) { uint32_t n = buf[3]; @@ -35,187 +27,12 @@ get_bit(uint32_t b, const char *key, uint32_t klen) { return key[index] & (1 << (7 - (b & 7))); } -static uint8_t -leftmost_bit(uint8_t v) { - static uint8_t debruijn[8] = {7, 2, 6, 1, 3, 4, 5, 0}; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - return debruijn[(uint8_t)(0x1du * v) >> 5]; -} - -static int -diff_bit(const unsigned char* key, uint32_t klen, - const unsigned char* prefix, uint32_t prefixlen) { - int r = 0; - unsigned char u1, u2 = 0; - - /* Compare bytes */ - for (; klen && prefixlen; key++, prefix++, r++) { - u1 = *key; - u2 = *prefix; - if (u1 != u2) { - /* Found a diff byte, return index */ - return (r << 3) + leftmost_bit(~(~u1 ^ u2)); - } - klen--; - prefixlen--; - } - - if (klen == 0 && prefixlen == 0) { - return -1; /* Same entry */ - } else if (prefixlen == 0) { - return r << 3; /* Prefix is smaller than key */ - } else { - /* Prefix is larger than key */ - return (r << 3) + leftmost_bit(u2); - } -} - -static uint32_t -log2ceil(uint32_t i) { - uint32_t l = 0; - if (i & (i - 1)) { l += 1; } - if (i >> 16) { l += 16; i >>= 16; } - if (i >> 8) { l += 8; i >>= 8; } - if (i >> 4) { l += 4; i >>= 4; } - if (i >> 2) { l += 2; i >>= 2; } - if (i >> 1) { l += 1; } - return l; -} - -static int -ensure_capacity(struct radixdb* tp, size_t extra_size) { - unsigned char* mem; - uint32_t new_size; - if ((uint32_t)(tp->dend + extra_size) < tp->dend) { - return -1; /* memory overflow */ - } else if ((uint32_t)(tp->dend + extra_size) > 0x80000000ul) { - new_size = 0xfffffffful; /* allocate max size */ - } else { - new_size = 1 << log2ceil(tp->dend + extra_size); - } - if (new_size > tp->size) { - mem = (unsigned char*) realloc(tp->mem, new_size); - if (!mem) - return -1; - tp->size = new_size; - tp->mem = mem; - } - return 0; -} - -static uint32_t -insert_between_inner(struct radixdb* tp, const char *key, uint32_t klen, - uint32_t pos, uint32_t n, uint32_t b0, int diff) { - uint32_t b1, nextpos; - unsigned char *tmp; - b1 = uint32_unpack(tp->mem + pos); - if (b1 <= b0 || diff < (int)b1) { - uint32_pack(tp->mem + n + (get_bit(diff, key, klen) ? 4 : 8), pos); - return n; - } - tmp = tp->mem + pos + (get_bit(b1, key, klen) ? 8 : 4); - nextpos = uint32_unpack(tmp); - uint32_pack(tmp, insert_between_inner(tp, key, klen, nextpos, n, b1, diff)); - return pos; -} - -static uint32_t -insert_between(struct radixdb* tp, const char *key, uint32_t klen, - uint32_t n, int diff) { - uint32_t pos = uint32_unpack(tp->mem); - uint32_t b0 = uint32_unpack(tp->mem + pos); - unsigned char *tmp = tp->mem + pos + (get_bit(b0, key, klen) ? 8 : 4); - if (diff < (int)b0) { - uint32_pack(tp->mem + n + (get_bit(diff, key, klen) ? 8 : 4), - uint32_unpack(tmp)); - pos = n; - } else { - n = insert_between_inner(tp, key, klen, uint32_unpack(tmp), n, b0, diff); - } - uint32_pack(tmp, n); - return pos; -} - -static uint32_t -search_node(const struct radixdb *tp, const char *key, uint32_t klen) { - uint32_t pos, nextpos, b0, b1; - pos = uint32_unpack(tp->mem); - b0 = uint32_unpack(tp->mem + pos); - nextpos = uint32_unpack(tp->mem + pos + (get_bit(b0, key, klen) ? 8 : 4)); - for (;;) { - b1 = uint32_unpack(tp->mem + nextpos); - if (b1 <= b0 || b1 > (klen << 3)) - break; - pos = nextpos; - nextpos = uint32_unpack(tp->mem + pos + (get_bit(b1, key, klen) ? 8 : 4)); - b0 = b1; - } - return pos; -} - static uint32_t -find_prefix(const char *a, uint32_t alen, const unsigned char *b, uint32_t blen) { - uint32_t r = 0; - unsigned char u1, u2; - - for (; alen-- && blen--; a++, b++, r++) { - u1 = *(unsigned char*)a; - u2 = *b; - if (u1 != u2) - break; - } - - return r; -} - -static uint32_t -search_largest_prefix_inner(const struct radixdb *tp, - const char *key, uint32_t klen, uint32_t pos, uint32_t b0, - uint32_t *max_length) { - uint32_t nextpos, poskeylen = uint32_unpack(tp->mem + pos + 12); - uint32_t nextmatch, b1 = uint32_unpack(tp->mem + pos); - if (b1 <= b0 || b1 > (klen << 3)) - return 0xfffffffful; - nextpos = uint32_unpack(tp->mem + pos + (get_bit(b1, key, klen) ? 8 : 4)); - nextmatch = search_largest_prefix_inner(tp, key, klen, nextpos, b1, - max_length); - if (nextmatch == 0xfffffffful - && poskeylen <= *max_length) { - *max_length = find_prefix(key, *max_length, tp->mem + pos + 20, poskeylen); - if (*max_length == poskeylen) - return pos; - } - return nextmatch; -} - -static uint32_t -search_largest_prefix(const struct radixdb *tp, - const char *key, uint32_t klen) { - uint32_t max_length = 0xfffffffful; - uint32_t pos = uint32_unpack(tp->mem), nextpos; - uint32_t b0 = uint32_unpack(tp->mem + pos); - uint32_t poskeylen = uint32_unpack(tp->mem + pos + 12); - uint32_t nextmatch; - nextpos = uint32_unpack(tp->mem + pos + (get_bit(b0, key, klen) ? 8 : 4)); - nextmatch = search_largest_prefix_inner(tp, key, klen, nextpos, b0, &max_length); - if (nextmatch == 0xfffffffful - && poskeylen <= max_length) { - max_length = find_prefix(key, max_length, tp->mem + pos + 20, poskeylen); - if (max_length == poskeylen) - return pos; - } - return nextmatch; -} - -int radixdb_init(struct radixdb* tp) { - tp->mem = NULL; - tp->size = 0ul; - tp->dend = 4; - if (ensure_capacity(tp, 2000) == -1) - return -1; - return 0; +prefix(const char* x, uint32_t n, const char* key, uint32_t m) { + for (uint32_t k = 0; k < n; k++) + if (k == m || x[k] != key[k]) + return k; + return n; } void radixdb_free(struct radixdb* tp) { @@ -230,7 +47,7 @@ int radixdb_check(struct radixdb* tp) { right = uint32_unpack(tp->mem + pos + 8); klen = uint32_unpack(tp->mem + pos + 12); vlen = uint32_unpack(tp->mem + pos + 16); - if (((bit - 1) >> 3) >= klen + if ((bit > 0 && ((bit - 1) >> 3) >= klen) || left >= tp->dend || right >= tp->dend || pos + 4 + 8 + 8 + klen + vlen > tp->dend) { @@ -242,58 +59,27 @@ int radixdb_check(struct radixdb* tp) { return 0; } -int radixdb_add(struct radixdb* tp, - const char *key, size_t klen, - const char *val, size_t vlen) { - int diff; - uint32_t n, pos, nodesize; - - nodesize = 4 + 8 + 8 + klen + vlen; - if (ensure_capacity(tp, nodesize) == -1) - return -1; - - /* copy the key and value to the new node */ - n = tp->dend; - uint32_pack(tp->mem + n, klen << 3); - uint32_pack(tp->mem + n + 4, tp->dend); - uint32_pack(tp->mem + n + 8, tp->dend); - uint32_pack(tp->mem + n + 12, klen); - uint32_pack(tp->mem + n + 16, vlen); - memcpy(tp->mem + n + 20, key, klen); - memcpy(tp->mem + n + 20 + klen, val, vlen); - - if (tp->dend == 4) { - /* first node inserted */ - uint32_pack(tp->mem, tp->dend); - } else { - /* find insert position */ - pos = search_node(tp, key, klen); - diff = diff_bit(tp->mem + pos + 20, uint32_unpack(tp->mem + pos + 12), - (const unsigned char*)key, klen); - if (diff == -1) { - errno = EEXIST; - return -1; /* entry already exists */ - } - uint32_pack(tp->mem + n, (uint32_t)diff); - uint32_pack(tp->mem, insert_between(tp, key, klen, n, diff)); - } - - tp->dend += nodesize; - return 0; -} - int radixdb_lookup(const struct radixdb* tp, const char *key, size_t klen, const char **val, size_t *vlen) { - uint32_t pos; - - if (tp->dend > 4) { - pos = search_node(tp, key, klen); - if (klen == uint32_unpack(tp->mem + pos + 12) - && memcmp(tp->mem + pos + 20, key, klen) == 0) { - *val = (const char*)(tp->mem + pos + 20 + klen); - *vlen = uint32_unpack(tp->mem + pos + 16); - return 0; + if (tp->dend > 4 && klen > 0) { + uint32_t pos, nextpos, b0, b1; + pos = uint32_unpack(tp->mem); + b0 = uint32_unpack(tp->mem + pos); + nextpos = uint32_unpack(tp->mem + pos + (get_bit(b0, key, klen) ? 8 : 4)); + for (;;) { + if (uint32_unpack(tp->mem + pos + 12) == klen + && memcmp(tp->mem + pos + 20, key, klen) == 0) { + *val = (const char*)(tp->mem + pos + 20 + klen); + *vlen = uint32_unpack(tp->mem + pos + 16); + return 0; + } + b1 = uint32_unpack(tp->mem + nextpos); + if (b1 <= b0 || b1 > (klen << 3)) + break; + pos = nextpos; + nextpos = uint32_unpack(tp->mem + pos + (get_bit(b1, key, klen) ? 8 : 4)); + b0 = b1; } } @@ -305,23 +91,53 @@ int radixdb_longest_match(const struct radixdb* tp, const char *key, size_t klen, const char **keymatch, size_t *matchlen, const char **val, size_t *vlen) { - uint32_t pos; - - if (tp->dend > 4) { - pos = search_largest_prefix(tp, key, klen); - if (pos != 0xfffffffful) { - *keymatch = (const char*)(tp->mem + pos + 20); - *matchlen = uint32_unpack(tp->mem + pos + 12); - *val = (const char*)(tp->mem + pos + 20 + *matchlen); - *vlen = uint32_unpack(tp->mem + pos + 16); - return 0; + if (tp->dend > 4 && klen > 0) { + uint32_t pos, nextpos, b0, b1, k, n_klen, best = 0; + pos = uint32_unpack(tp->mem); + b0 = uint32_unpack(tp->mem + pos); + nextpos = uint32_unpack(tp->mem + pos + (get_bit(b0, key, klen) ? 8 : 4)); + for (;;) { + n_klen = uint32_unpack(tp->mem + pos + 12); + if (n_klen <= klen) { + k = prefix((const char*)(tp->mem + pos + 20), n_klen, key, klen); + if (k == n_klen && k > best) { + *keymatch = (const char*)(tp->mem + pos + 20); + *matchlen = best = uint32_unpack(tp->mem + pos + 12); + *val = (const char*)(tp->mem + pos + 20 + *matchlen); + *vlen = uint32_unpack(tp->mem + pos + 16); + } + } + b1 = uint32_unpack(tp->mem + nextpos); + if (b1 <= b0 || b1 > (klen << 3)) + break; + pos = nextpos; + nextpos = uint32_unpack(tp->mem + pos + (get_bit(b1, key, klen) ? 8 : 4)); + b0 = b1; } + if (best > 0) + return 0; } errno = ENOENT; return -1; } +static void +dot_escape(const char* s, uint32_t len) { + while (len > 0) { + switch (*s) { + case '{': case '}': case '(': case ')': case '<': case '>': case '&': + fputc('\\', stdout); fputc(*s, stdout); + break; + default: + fputc(*s, stdout); + break; + } + s++; + len--; + } +} + void radixdb_dump2dot(const struct radixdb* tp) { uint32_t pos = 4, bit, left, right, klen, vlen; printf("digraph G {\n"); @@ -334,12 +150,13 @@ void radixdb_dump2dot(const struct radixdb* tp) { right = uint32_unpack(tp->mem + pos + 8); klen = uint32_unpack(tp->mem + pos + 12); vlen = uint32_unpack(tp->mem + pos + 16); - printf(" n%lu [label=\"{{%lu|%lu|\\\"%.*s\\\"→\\\"%.*s\\\"}" - "|{left|right}}\"];\n", + printf(" n%lu [label=\"{{%lu|%lu|\\\"", (unsigned long)pos, - (unsigned long)(bit>>3), (unsigned long)(bit&7), - (int)klen, (const char*)(tp->mem + pos + 20), - (int)vlen, (const char*)(tp->mem + pos + 20 + klen)); + (unsigned long)(bit>>3), (unsigned long)(bit&7)); + dot_escape((const char*)(tp->mem + pos + 20), klen); + printf("\\\"→\\\""); + dot_escape((const char*)(tp->mem + pos + 20 + klen), vlen); + printf("\\\"}|{left|right}}\"];\n"); printf(" n%lu:l -> n%lu;\n" " n%lu:r -> n%lu;\n", (unsigned long)pos, (unsigned long)left, diff --git a/radixdb.h b/radixdb.h index 4991ac3..22a2867 100644 --- a/radixdb.h +++ b/radixdb.h @@ -16,11 +16,9 @@ extern "C" { struct radixdb { unsigned char* mem; /* used memory */ - uint32_t size; /* memory size */ uint32_t dend; /* end of data ptr */ }; -int radixdb_init(struct radixdb* tp); void radixdb_free(struct radixdb* tp); /* Check if the RadixDB structure is valid (i.e. if the loaded database is not @@ -28,10 +26,6 @@ void radixdb_free(struct radixdb* tp); */ int radixdb_check(struct radixdb* tp); -int radixdb_add(struct radixdb* tp, - const char *key, size_t klen, - const char *val, size_t vlen); - int radixdb_lookup(const struct radixdb* tp, const char *key, size_t klen, const char **val, size_t *vlen); @@ -45,7 +39,7 @@ void radixdb_dump2dot(const struct radixdb* tp); void radixdb_dump(const struct radixdb* tp); -/* Simple sata iterator. Returns key-values in the order they were added. The +/* Simple data iterator. Returns key-values in the order they were added. The * iterator parameter must be initialized with 4. */ int radixdb_iter_next(const struct radixdb* tp, @@ -53,6 +47,28 @@ int radixdb_iter_next(const struct radixdb* tp, const char **key, size_t *klen, const char **val, size_t *vlen); +/* radixdb_make */ + +struct radixdb_node { + int b; + char *key, *val; + size_t klen, vlen; + struct radixdb_node *left, *right; +}; + +struct radixdb_make { + struct radixdb_node head; + unsigned char* mem; /* used memory */ + uint32_t size; /* memory size */ + uint32_t dend; /* end of data ptr */ +}; + +int radixdb_make_start(struct radixdb_make* tpm); +int radixdb_make_add(struct radixdb_make* tpm, + const char *key, size_t klen, + const char *val, size_t vlen); +int radixdb_make_finish(struct radixdb_make *tpm, struct radixdb* tp); + #ifdef __cplusplus } #endif diff --git a/radixdb2dot.c b/radixdb2dot.c index 6d1672c..53d6fcd 100644 --- a/radixdb2dot.c +++ b/radixdb2dot.c @@ -2,89 +2,20 @@ * Public domain. */ -#include -#include #include -#include #include -#include -#include -#include -#ifdef _WIN32 -# include -#else -# include -# ifndef MAP_FAILED -# define MAP_FAILED ((void*)-1) -# endif -#endif -#include #include "radixdb.h" +#include "radixdb_util.h" int main(int argc, char **argv) { - struct stat st; struct radixdb db; - int fd; -#ifdef _WIN32 - HANDLE hFile, hMapping; -#endif - if (argc != 2) { - printf("usage: radixdbdump f"); - return 1; - } - - /* open and get file size */ - fd = open(argv[1], O_RDONLY); - if (fd < 0 || fstat(fd, &st) < 0) { - fprintf(stderr, "unable to open `%s'\n", argv[1]); - return 1; - } - - /* trivial sanity check: at least root should be here */ - if (st.st_size < 4 || st.st_size >= 0xfffffffful) { - fprintf(stderr, "invalid file\n"); - return 1; - } - - db.dend = st.st_size; - /* memory-map file */ -#ifdef _WIN32 - hFile = (HANDLE) _get_osfhandle(fd); - if (hFile == (HANDLE) -1) { - fprintf(stderr, "_get_osfhandle error\n"); + fprintf(stderr, "usage: radixdbdump f\n"); return 1; } - hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); - if (!hMapping) { - fprintf(stderr, "CreateFileMapping error\n"); - return 1; - } - db.mem = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0); - CloseHandle(hMapping); - if (!mem) { - fprintf(stderr, "MapViewOfFile error\n"); - return 1; - } -#else - db.mem = (unsigned char*)mmap(NULL, db.dend, PROT_READ, MAP_SHARED, fd, 0); - if (db.mem == MAP_FAILED) { - fprintf(stderr, "mmap error"); - return 1; - } -#endif /* _WIN32 */ - - db.size = 0; /* file is read-only */ - + openfile(argv[1], &db); radixdb_dump2dot(&db); - -#ifdef _WIN32 - UnmapViewOfFile((void*) db.mem); -#else - munmap((void*)db.mem, db.dend); -#endif /* _WIN32 */ - - close(fd); + closefile(&db); return 0; } diff --git a/radixdb_make.c b/radixdb_make.c new file mode 100644 index 0000000..f7b53c0 --- /dev/null +++ b/radixdb_make.c @@ -0,0 +1,165 @@ +/* This file is a part of radixdb package by G. B. Versiani, guibv@yahoo.com. + * Public domain. + */ + +#include +#include +#include +#include +#include + +#include "radixdb.h" + +static void +uint32_pack(unsigned char *buf, uint32_t i) { + buf[0] = i & 255; i >>= 8; + buf[1] = i & 255; i >>= 8; + buf[2] = i & 255; + buf[3] = i >> 8; +} + +static int +bit(uint32_t b, const char *key, uint32_t klen) { + size_t index = b >> 3; + if (index >= klen) + return 0; + return key[index] & (1 << (7 - (b & 7))); +} + +static struct radixdb_node* +insert_between(struct radixdb_node *h, struct radixdb_node *n, + int d, struct radixdb_node *p) { + if ((h->b >= d) || (h->b <= p->b)) { + n->b = d; + n->left = bit(d, n->key, n->klen) ? h : n; + n->right = bit(d, n->key, n->klen) ? n : h; + return n; + } + + if (bit(h->b, n->key, n->klen)) + h->right = insert_between(h->right, n, d, h); + else + h->left = insert_between(h->left, n, d, h); + return h; +} + +int radixdb_make_start(struct radixdb_make* tpm) { + memset(tpm, 0, sizeof(*tpm)); + tpm->head.left = tpm->head.right = &tpm->head; + return 0; +} + +int radixdb_make_add(struct radixdb_make* tpm, + const char *key, size_t klen, + const char *val, size_t vlen) { + size_t i; + struct radixdb_node *t, *n, *head; + + n = (struct radixdb_node*) malloc(sizeof(*n)); + n->klen = klen; + n->vlen = vlen; + n->key = (char*) malloc(klen); + memcpy(n->key, key, klen); + if (vlen) { + n->val = (char*) malloc(vlen); + memcpy(n->val, val, vlen); + } else { + n->val = NULL; + } + tpm->dend += sizeof(*n) + klen + vlen; + + t = head = &tpm->head; + do { + i = t->b; + t = bit(t->b, key, klen) ? t->right : t->left; + } while (i < t->b); + + if (klen == t->klen && memcmp(key, t->key, klen) == 0) { + errno = EEXIST; + return -1; /* entry already exists */ + } + + i = 0; + while (i < (klen << 3)) { + if (bit(i, key, klen) != bit(i, t->key, t->klen)) + break; + i++; + } + + if (bit(head->b, key, klen)) + head->right = insert_between(head->right, n, i, head); + else + head->left = insert_between(head->left, n, i, head); + return 0; +} + +static size_t +memcount(struct radixdb_node *t, int b) { + size_t size; + if (t->b <= b) + return 0; + size = 4 + 8 + 8 + t->klen + t->vlen; + size += memcount(t->left, t->b); + size += memcount(t->right, t->b); + return size; +} + +static uint32_t +to_db(struct radixdb_node *t, int b, struct radixdb* db) { + uint32_t n; + + if (t->b <= b) + return 4; + + n = db->dend; + uint32_pack(db->mem + n, t->b); + uint32_pack(db->mem + n + 12, t->klen); + uint32_pack(db->mem + n + 16, t->vlen); + memcpy(db->mem + n + 20, t->key, t->klen); + if (t->vlen > 0) + memcpy(db->mem + n + 20 + t->klen, t->val, t->vlen); + db->dend += 4 + 8 + 8 + t->klen + t->vlen; + + uint32_pack(db->mem + n + 4, to_db(t->left, t->b, db)); + uint32_pack(db->mem + n + 8, to_db(t->right, t->b, db)); + return n; +} + +static void +free_nodes(struct radixdb_node *t, int b) { + if (t->b <= b) + return; + + free_nodes(t->left, t->b); + free_nodes(t->right, t->b); + + free(t->key); + free(t->val); + free(t); +} + +int radixdb_make_finish(struct radixdb_make* tpm, struct radixdb* tp) { + uint32_t n; + size_t memsize; + + memsize = memcount(&tpm->head, -1); + tp->mem = (unsigned char*) malloc(4 + memsize); + if (!tp->mem) + return -1; + + tp->dend = 4; + uint32_pack(tp->mem, 4); /* point to root */ + + n = tp->dend; + uint32_pack(tp->mem + n, 0); + uint32_pack(tp->mem + n + 12, 0); + uint32_pack(tp->mem + n + 16, 0); + tp->dend += 4 + 8 + 8; + + uint32_pack(tp->mem + n + 4, to_db(tpm->head.left, 0, tp)); + uint32_pack(tp->mem + n + 8, to_db(tpm->head.right, 0, tp)); + + free_nodes(tpm->head.left, 0); + free_nodes(tpm->head.right, 0); + return 0; +} diff --git a/radixdb_util.c b/radixdb_util.c new file mode 100644 index 0000000..e17b9e1 --- /dev/null +++ b/radixdb_util.c @@ -0,0 +1,82 @@ +/* This file is a part of radixdb package by G. B. Versiani, guibv@yahoo.com. + * Public domain. + */ + +#include "radixdb_util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +# include +#else +# include +# ifndef MAP_FAILED +# define MAP_FAILED ((void*)-1) +# endif +#endif +#include + +static int __fd; + +void openfile(const char* filename, struct radixdb* db) { + struct stat st; +#ifdef _WIN32 + static HANDLE hFile, hMapping; +#endif + + /* open and get file size */ + __fd = open(filename, O_RDONLY); + if (__fd < 0 || fstat(__fd, &st) < 0) { + fprintf(stderr, "unable to open `%s'\n", filename); + exit(1); + } + + /* trivial sanity check: at least root should be here */ + if (st.st_size < 4 || st.st_size >= 0xfffffffful) { + fprintf(stderr, "invalid file\n"); + exit(1); + } + + db->dend = st.st_size; + /* memory-map file */ +#ifdef _WIN32 + hFile = (HANDLE) _get_osfhandle(__fd); + if (hFile == (HANDLE) -1) { + fprintf(stderr, "_get_osfhandle error\n"); + exit(1); + } + hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (!hMapping) { + fprintf(stderr, "CreateFileMapping error\n"); + exit(1); + } + db->mem = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0); + CloseHandle(hMapping); + if (!mem) { + fprintf(stderr, "MapViewOfFile error\n"); + exit(1); + } +#else + db->mem = (unsigned char*)mmap(NULL, db->dend, PROT_READ, MAP_SHARED, __fd, 0); + if (db->mem == MAP_FAILED) { + fprintf(stderr, "mmap error"); + exit(1); + } +#endif /* _WIN32 */ +} + +void closefile(struct radixdb* db) { +#ifdef _WIN32 + UnmapViewOfFile((void*) db->mem); +#else + munmap((void*)db->mem, db->dend); +#endif /* _WIN32 */ + + close(__fd); +} diff --git a/radixdb_util.h b/radixdb_util.h new file mode 100644 index 0000000..ae8aab4 --- /dev/null +++ b/radixdb_util.h @@ -0,0 +1,21 @@ +/* This file is a part of radixdb package by G. B. Versiani, guibv@yahoo.com. + * Public domain. + */ + +#ifndef RADIXDB_UTIL_H +#define RADIXDB_UTIL_H + +#include "radixdb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void openfile(const char* filename, struct radixdb* db); +void closefile(struct radixdb* db); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/radixdbdump.c b/radixdbdump.c index 22c0fa0..79f2b1b 100644 --- a/radixdbdump.c +++ b/radixdbdump.c @@ -2,92 +2,23 @@ * Public domain. */ -#include -#include #include -#include #include -#include -#include -#include -#ifdef _WIN32 -# include -#else -# include -# ifndef MAP_FAILED -# define MAP_FAILED ((void*)-1) -# endif -#endif -#include #include "radixdb.h" +#include "radixdb_util.h" int main(int argc, char **argv) { - struct stat st; struct radixdb db; - int fd; -#ifdef _WIN32 - HANDLE hFile, hMapping; -#endif - if (argc != 2) { - printf("usage: radixdbdump f"); - return 1; - } - - /* open and get file size */ - fd = open(argv[1], O_RDONLY); - if (fd < 0 || fstat(fd, &st) < 0) { - fprintf(stderr, "unable to open `%s'\n", argv[1]); - return 1; - } - - /* trivial sanity check: at least root should be here */ - if (st.st_size < 4 || st.st_size >= 0xfffffffful) { - fprintf(stderr, "invalid file\n"); - return 1; - } - - db.dend = st.st_size; - /* memory-map file */ -#ifdef _WIN32 - hFile = (HANDLE) _get_osfhandle(fd); - if (hFile == (HANDLE) -1) { - fprintf(stderr, "_get_osfhandle error\n"); + fprintf(stderr, "usage: radixdbdump f\n"); return 1; } - hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); - if (!hMapping) { - fprintf(stderr, "CreateFileMapping error\n"); - return 1; - } - db.mem = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0); - CloseHandle(hMapping); - if (!mem) { - fprintf(stderr, "MapViewOfFile error\n"); - return 1; - } -#else - db.mem = (unsigned char*)mmap(NULL, db.dend, PROT_READ, MAP_SHARED, fd, 0); - if (db.mem == MAP_FAILED) { - fprintf(stderr, "mmap error"); - return 1; - } -#endif /* _WIN32 */ - - db.size = 0; /* file is read-only */ - + openfile(argv[1], &db); if (radixdb_check(&db) == 0) radixdb_dump(&db); else fprintf(stderr, "Invalid db"); - -#ifdef _WIN32 - UnmapViewOfFile((void*) db.mem); -#else - munmap((void*)db.mem, db.dend); -#endif /* _WIN32 */ - - close(fd); + closefile(&db); return 0; } diff --git a/radixdbget.c b/radixdbget.c index 2ad12a1..02e0748 100644 --- a/radixdbget.c +++ b/radixdbget.c @@ -2,95 +2,32 @@ * Public domain. */ -#include -#include #include -#include #include -#include -#include -#include -#ifdef _WIN32 -# include -#else -# include -# ifndef MAP_FAILED -# define MAP_FAILED ((void*)-1) -# endif -#endif -#include #include "radixdb.h" +#include "radixdb_util.h" int main(int argc, char **argv) { - struct stat st; struct radixdb db; - int fd; const char *val; size_t vlen; -#ifdef _WIN32 - HANDLE hFile, hMapping; -#endif + int result = 0; if (argc != 3) { - printf("usage: radixdbget f key"); + fprintf(stderr, "usage: radixdbget f key\n"); return 1; } - /* open and get file size */ - fd = open(argv[1], O_RDONLY); - if (fd < 0 || fstat(fd, &st) < 0) { - fprintf(stderr, "unable to open `%s'\n", argv[1]); - return 1; - } - - /* trivial sanity check: at least root should be here */ - if (st.st_size < 4 || st.st_size >= 0xfffffffful) { - fprintf(stderr, "invalid file\n"); - return 1; - } - - db.dend = st.st_size; - /* memory-map file */ -#ifdef _WIN32 - hFile = (HANDLE) _get_osfhandle(fd); - if (hFile == (HANDLE) -1) { - fprintf(stderr, "_get_osfhandle error\n"); - return 1; - } - hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); - if (!hMapping) { - fprintf(stderr, "CreateFileMapping error\n"); - return 1; - } - db.mem = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0); - CloseHandle(hMapping); - if (!mem) { - fprintf(stderr, "MapViewOfFile error\n"); - return 1; - } -#else - db.mem = (unsigned char*)mmap(NULL, db.dend, PROT_READ, MAP_SHARED, fd, 0); - if (db.mem == MAP_FAILED) { - fprintf(stderr, "mmap error"); - return 1; - } -#endif /* _WIN32 */ - - db.size = 0; /* file is read-only */ + openfile(argv[1], &db); if (radixdb_lookup(&db, argv[2], strlen(argv[2]), &val, &vlen) == 0) { printf("%.*s", (int)vlen, val); } else { - return 2; + result = 2; } -#ifdef _WIN32 - UnmapViewOfFile((void*) db.mem); -#else - munmap((void*)db.mem, db.dend); -#endif /* _WIN32 */ + closefile(&db); - close(fd); - return 0; + return result; } diff --git a/radixdbmatch.c b/radixdbmatch.c index 5bf0a49..147122e 100644 --- a/radixdbmatch.c +++ b/radixdbmatch.c @@ -2,82 +2,24 @@ * Public domain. */ -#include -#include #include -#include #include -#include -#include -#include -#ifdef _WIN32 -# include -#else -# include -# ifndef MAP_FAILED -# define MAP_FAILED ((void*)-1) -# endif -#endif -#include #include "radixdb.h" +#include "radixdb_util.h" int main(int argc, char **argv) { - struct stat st; struct radixdb db; - int fd; const char *val, *key; size_t vlen, klen; -#ifdef _WIN32 - HANDLE hFile, hMapping; -#endif + int result = 0; if (argc != 3) { - printf("usage: radixdbmatch f key"); + fprintf(stderr, "usage: radixdbmatch f key\n"); return 1; } - /* open and get file size */ - fd = open(argv[1], O_RDONLY); - if (fd < 0 || fstat(fd, &st) < 0) { - fprintf(stderr, "unable to open `%s'\n", argv[1]); - return 1; - } - - /* trivial sanity check: at least root should be here */ - if (st.st_size < 4 || st.st_size >= 0xfffffffful) { - fprintf(stderr, "invalid file\n"); - return 1; - } - - db.dend = st.st_size; - /* memory-map file */ -#ifdef _WIN32 - hFile = (HANDLE) _get_osfhandle(fd); - if (hFile == (HANDLE) -1) { - fprintf(stderr, "_get_osfhandle error\n"); - return 1; - } - hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); - if (!hMapping) { - fprintf(stderr, "CreateFileMapping error\n"); - return 1; - } - db.mem = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0); - CloseHandle(hMapping); - if (!mem) { - fprintf(stderr, "MapViewOfFile error\n"); - return 1; - } -#else - db.mem = (unsigned char*)mmap(NULL, db.dend, PROT_READ, MAP_SHARED, fd, 0); - if (db.mem == MAP_FAILED) { - fprintf(stderr, "mmap error"); - return 1; - } -#endif /* _WIN32 */ - - db.size = 0; /* file is read-only */ + openfile(argv[1], &db); if (radixdb_longest_match(&db, argv[2], strlen(argv[2]), &key, &klen, &val, &vlen) == 0) { @@ -86,15 +28,10 @@ int main(int argc, char **argv) { (int)klen, key, (int)vlen, val); } else { - return 2; + result = 2; } -#ifdef _WIN32 - UnmapViewOfFile((void*) db.mem); -#else - munmap((void*)db.mem, db.dend); -#endif /* _WIN32 */ + closefile(&db); - close(fd); - return 0; + return result; } diff --git a/radixdbmk.c b/radixdbmk.c index bdaf597..369c1f3 100644 --- a/radixdbmk.c +++ b/radixdbmk.c @@ -40,10 +40,11 @@ int main(int argc, char **argv) { char c; char* buf = NULL; struct radixdb db; + struct radixdb_make db_make; int line = 1; uint32_t i; - radixdb_init(&db); + radixdb_make_start(&db_make); while((c = getc(stdin)) == '+') { uint32_t klen, vlen = 0; if (getnum(stdin, &klen, &c) < 0 || c != ',' @@ -56,7 +57,10 @@ int main(int argc, char **argv) { || fread(buf + klen, 1, vlen, stdin) != vlen || getc(stdin) != '\n') badinput(line); - radixdb_add(&db, buf, klen, buf + klen, vlen); + if (radixdb_make_add(&db_make, buf, klen, buf + klen, vlen) != 0) { + fprintf(stderr, "found duplicated keys\n"); + badinput(line); + } line += 1; /* Handle cases where the key or the value contain newlines */ for (i = 0; i < klen + vlen; i++) { @@ -64,6 +68,7 @@ int main(int argc, char **argv) { line++; } } + radixdb_make_finish(&db_make, &db); fwrite(db.mem, db.dend, 1, stdout); radixdb_free(&db); diff --git a/tests/check_insert.c b/tests/check_insert.c index 3da3b19..cb75632 100644 --- a/tests/check_insert.c +++ b/tests/check_insert.c @@ -485,17 +485,36 @@ static const char *test_keys[] = { "idutcygDo", }; +START_TEST(test_single_entry) { + struct radixdb db; + struct radixdb_make db_make; + const char *keymatch, *val; + size_t matchlen, vlen; + const char *key = "wheshivGowghisheex)"; + + radixdb_make_start(&db_make); + ck_assert_int_eq(radixdb_make_add(&db_make, key, strlen(key), key, strlen(key)), 0); + radixdb_make_finish(&db_make, &db); + + ck_assert_int_eq(radixdb_lookup(&db, key, strlen(key), &val, &vlen), 0); + ck_assert_int_eq(radixdb_longest_match(&db, key, strlen(key), + &keymatch, &matchlen, &val, &vlen), 0); + + radixdb_free(&db); +} END_TEST + START_TEST(test_insert_then_get) { size_t i; struct radixdb db; + struct radixdb_make db_make; - radixdb_init(&db); - + radixdb_make_start(&db_make); for (i = 0; i < sizeof(test_keys) / sizeof(test_keys[0]); i++) { const char *key = test_keys[i]; size_t klen = strlen(key); - ck_assert_int_eq(radixdb_add(&db, key, klen, key, klen), 0); + ck_assert_int_eq(radixdb_make_add(&db_make, key, klen, key, klen), 0); } + radixdb_make_finish(&db_make, &db); for (i = 0; i < sizeof(test_keys) / sizeof(test_keys[0]); i++) { /* All entered keys should be there */ @@ -521,24 +540,26 @@ START_TEST(test_insert_then_get) { START_TEST(test_insert_duplicate_key) { struct radixdb db; + struct radixdb_make db_make; - radixdb_init(&db); - - ck_assert_int_eq(radixdb_add(&db, "a", 1, "b", 1), 0); - ck_assert_int_eq(radixdb_add(&db, "a", 1, "c", 1), -1); + radixdb_make_start(&db_make); + ck_assert_int_eq(radixdb_make_add(&db_make, "a", 1, "b", 1), 0); + ck_assert_int_eq(radixdb_make_add(&db_make, "a", 1, "c", 1), -1); + radixdb_make_finish(&db_make, &db); radixdb_free(&db); } END_TEST START_TEST(test_match_first_node) { struct radixdb db; + struct radixdb_make db_make; const char *keymatch, *val; size_t matchlen, vlen; - radixdb_init(&db); - - radixdb_add(&db, "123", 3, "a", 1); - radixdb_add(&db, "1234", 4, "b", 1); + radixdb_make_start(&db_make); + radixdb_make_add(&db_make, "123", 3, "a", 1); + radixdb_make_add(&db_make, "1234", 4, "b", 1); + radixdb_make_finish(&db_make, &db); ck_assert_int_eq(radixdb_longest_match(&db, "1230", 4, &keymatch, &matchlen, &val, &vlen), 0); @@ -552,13 +573,14 @@ START_TEST(test_match_first_node) { START_TEST(test_longest_match_ordered) { struct radixdb db; + struct radixdb_make db_make; const char *keymatch, *val; size_t matchlen, vlen; - radixdb_init(&db); - - radixdb_add(&db, "123", 3, "a", 1); - radixdb_add(&db, "1234", 4, "b", 1); + radixdb_make_start(&db_make); + radixdb_make_add(&db_make, "123", 3, "a", 1); + radixdb_make_add(&db_make, "1234", 4, "b", 1); + radixdb_make_finish(&db_make, &db); ck_assert_int_eq(radixdb_longest_match(&db, "12345", 5, &keymatch, &matchlen, &val, &vlen), 0); @@ -572,13 +594,14 @@ START_TEST(test_longest_match_ordered) { START_TEST(test_longest_match_reverse) { struct radixdb db; + struct radixdb_make db_make; const char *keymatch, *val; size_t matchlen, vlen; - radixdb_init(&db); - - radixdb_add(&db, "1234", 4, "b", 1); - radixdb_add(&db, "123", 3, "a", 1); + radixdb_make_start(&db_make); + radixdb_make_add(&db_make, "1234", 4, "b", 1); + radixdb_make_add(&db_make, "123", 3, "a", 1); + radixdb_make_finish(&db_make, &db); ck_assert_int_eq(radixdb_longest_match(&db, "12345", 5, &keymatch, &matchlen, &val, &vlen), 0); @@ -600,6 +623,7 @@ radixdb_suite() { /* Core test case */ tc_core = tcase_create("Core"); + tcase_add_test(tc_core, test_single_entry); tcase_add_test(tc_core, test_insert_then_get); tcase_add_test(tc_core, test_insert_duplicate_key); tcase_add_test(tc_core, test_match_first_node);