mirror of
https://github.com/fluencelabs/redis
synced 2025-03-19 17:10:50 +00:00
Rax library updated.
This commit is contained in:
parent
1409c545da
commit
3f9e2322ec
12
src/db.c
12
src/db.c
@ -1315,9 +1315,9 @@ void slotToKeyUpdateKey(robj *key, int add) {
|
|||||||
indexed[1] = hashslot & 0xff;
|
indexed[1] = hashslot & 0xff;
|
||||||
memcpy(indexed+2,key->ptr,keylen);
|
memcpy(indexed+2,key->ptr,keylen);
|
||||||
if (add) {
|
if (add) {
|
||||||
raxInsert(server.cluster->slots_to_keys,indexed,keylen+2,NULL);
|
raxInsert(server.cluster->slots_to_keys,indexed,keylen+2,NULL,NULL);
|
||||||
} else {
|
} else {
|
||||||
raxRemove(server.cluster->slots_to_keys,indexed,keylen+2);
|
raxRemove(server.cluster->slots_to_keys,indexed,keylen+2,NULL);
|
||||||
}
|
}
|
||||||
if (indexed != buf) zfree(indexed);
|
if (indexed != buf) zfree(indexed);
|
||||||
}
|
}
|
||||||
@ -1348,8 +1348,8 @@ unsigned int getKeysInSlot(unsigned int hashslot, robj **keys, unsigned int coun
|
|||||||
indexed[0] = (hashslot >> 8) & 0xff;
|
indexed[0] = (hashslot >> 8) & 0xff;
|
||||||
indexed[1] = hashslot & 0xff;
|
indexed[1] = hashslot & 0xff;
|
||||||
raxStart(&iter,server.cluster->slots_to_keys);
|
raxStart(&iter,server.cluster->slots_to_keys);
|
||||||
raxSeek(&iter,indexed,2,">=");
|
raxSeek(&iter,">=",indexed,2);
|
||||||
while(count-- && raxNext(&iter,NULL,0,NULL)) {
|
while(count-- && raxNext(&iter)) {
|
||||||
if (iter.key[0] != indexed[0] || iter.key[1] != indexed[1]) break;
|
if (iter.key[0] != indexed[0] || iter.key[1] != indexed[1]) break;
|
||||||
keys[j++] = createStringObject((char*)iter.key+2,iter.key_len-2);
|
keys[j++] = createStringObject((char*)iter.key+2,iter.key_len-2);
|
||||||
}
|
}
|
||||||
@ -1368,8 +1368,8 @@ unsigned int delKeysInSlot(unsigned int hashslot) {
|
|||||||
indexed[1] = hashslot & 0xff;
|
indexed[1] = hashslot & 0xff;
|
||||||
raxStart(&iter,server.cluster->slots_to_keys);
|
raxStart(&iter,server.cluster->slots_to_keys);
|
||||||
while(server.cluster->slots_keys_count[hashslot]) {
|
while(server.cluster->slots_keys_count[hashslot]) {
|
||||||
raxSeek(&iter,indexed,2,">=");
|
raxSeek(&iter,">=",indexed,2);
|
||||||
raxNext(&iter,NULL,0,NULL);
|
raxNext(&iter);
|
||||||
|
|
||||||
robj *key = createStringObject((char*)iter.key+2,iter.key_len-2);
|
robj *key = createStringObject((char*)iter.key+2,iter.key_len-2);
|
||||||
dbDelete(&server.db[0],key);
|
dbDelete(&server.db[0],key);
|
||||||
|
393
src/rax.c
393
src/rax.c
@ -33,8 +33,14 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <math.h>
|
||||||
#include "rax.h"
|
#include "rax.h"
|
||||||
#include "rax_malloc.h"
|
|
||||||
|
#ifndef RAX_MALLOC_INCLUDE
|
||||||
|
#define RAX_MALLOC_INCLUDE "rax_malloc.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include RAX_MALLOC_INCLUDE
|
||||||
|
|
||||||
/* This is a special pointer that is guaranteed to never have the same value
|
/* This is a special pointer that is guaranteed to never have the same value
|
||||||
* of a radix tree node. It's used in order to report "not found" error without
|
* of a radix tree node. It's used in order to report "not found" error without
|
||||||
@ -84,6 +90,7 @@ static inline int raxStackPush(raxStack *ts, void *ptr) {
|
|||||||
if (ts->stack == NULL) {
|
if (ts->stack == NULL) {
|
||||||
ts->stack = ts->static_items;
|
ts->stack = ts->static_items;
|
||||||
ts->oom = 1;
|
ts->oom = 1;
|
||||||
|
errno = ENOMEM;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
memcpy(ts->stack,ts->static_items,sizeof(void*)*ts->maxitems);
|
memcpy(ts->stack,ts->static_items,sizeof(void*)*ts->maxitems);
|
||||||
@ -91,6 +98,7 @@ static inline int raxStackPush(raxStack *ts, void *ptr) {
|
|||||||
void **newalloc = rax_realloc(ts->stack,sizeof(void*)*ts->maxitems*2);
|
void **newalloc = rax_realloc(ts->stack,sizeof(void*)*ts->maxitems*2);
|
||||||
if (newalloc == NULL) {
|
if (newalloc == NULL) {
|
||||||
ts->oom = 1;
|
ts->oom = 1;
|
||||||
|
errno = ENOMEM;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
ts->stack = newalloc;
|
ts->stack = newalloc;
|
||||||
@ -205,7 +213,7 @@ void *raxGetData(raxNode *n) {
|
|||||||
* On success the new parent node pointer is returned (it may change because
|
* On success the new parent node pointer is returned (it may change because
|
||||||
* of the realloc, so the caller should discard 'n' and use the new value).
|
* of the realloc, so the caller should discard 'n' and use the new value).
|
||||||
* On out of memory NULL is returned, and the old node is still valid. */
|
* On out of memory NULL is returned, and the old node is still valid. */
|
||||||
raxNode *raxAddChild(raxNode *n, char c, raxNode **childptr, raxNode ***parentlink) {
|
raxNode *raxAddChild(raxNode *n, unsigned char c, raxNode **childptr, raxNode ***parentlink) {
|
||||||
assert(n->iscompr == 0);
|
assert(n->iscompr == 0);
|
||||||
|
|
||||||
size_t curlen = sizeof(raxNode)+
|
size_t curlen = sizeof(raxNode)+
|
||||||
@ -330,7 +338,7 @@ raxNode *raxCompressNode(raxNode *n, unsigned char *s, size_t len, raxNode **chi
|
|||||||
memcpy(n->data,s,len);
|
memcpy(n->data,s,len);
|
||||||
if (n->iskey) raxSetData(n,data);
|
if (n->iskey) raxSetData(n,data);
|
||||||
raxNode **childfield = raxNodeLastChildPtr(n);
|
raxNode **childfield = raxNodeLastChildPtr(n);
|
||||||
memcpy(childfield,&child,sizeof(child));
|
memcpy(childfield,child,sizeof(*child));
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,7 +407,7 @@ static inline size_t raxLowWalk(rax *rax, unsigned char *s, size_t len, raxNode
|
|||||||
* data is updated, and 0 is returned, otherwise the element is inserted
|
* data is updated, and 0 is returned, otherwise the element is inserted
|
||||||
* and 1 is returned. On out of memory the function returns 0 as well but
|
* and 1 is returned. On out of memory the function returns 0 as well but
|
||||||
* sets errno to ENOMEM, otherwise errno will be set to 0. */
|
* sets errno to ENOMEM, otherwise errno will be set to 0. */
|
||||||
int raxInsert(rax *rax, unsigned char *s, size_t len, void *data) {
|
int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) {
|
||||||
size_t i;
|
size_t i;
|
||||||
int j = 0; /* Split position. If raxLowWalk() stops in a compressed
|
int j = 0; /* Split position. If raxLowWalk() stops in a compressed
|
||||||
node, the index 'j' represents the char we stopped within the
|
node, the index 'j' represents the char we stopped within the
|
||||||
@ -417,6 +425,7 @@ int raxInsert(rax *rax, unsigned char *s, size_t len, void *data) {
|
|||||||
* data pointer. */
|
* data pointer. */
|
||||||
if (i == len && (!h->iscompr || j == 0 /* not in the middle if j is 0 */)) {
|
if (i == len && (!h->iscompr || j == 0 /* not in the middle if j is 0 */)) {
|
||||||
if (h->iskey) {
|
if (h->iskey) {
|
||||||
|
if (old) *old = raxGetData(h);
|
||||||
raxSetData(h,data);
|
raxSetData(h,data);
|
||||||
errno = 0;
|
errno = 0;
|
||||||
return 0; /* Element already exists. */
|
return 0; /* Element already exists. */
|
||||||
@ -720,6 +729,7 @@ int raxInsert(rax *rax, unsigned char *s, size_t len, void *data) {
|
|||||||
/* Finish! We don't need to contine with the insertion
|
/* Finish! We don't need to contine with the insertion
|
||||||
* algorithm for ALGO 2. The key is already inserted. */
|
* algorithm for ALGO 2. The key is already inserted. */
|
||||||
rax->numele++;
|
rax->numele++;
|
||||||
|
rax_free(h);
|
||||||
return 1; /* Key inserted. */
|
return 1; /* Key inserted. */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -729,7 +739,6 @@ int raxInsert(rax *rax, unsigned char *s, size_t len, void *data) {
|
|||||||
* since i == len. */
|
* since i == len. */
|
||||||
while(i < len) {
|
while(i < len) {
|
||||||
raxNode *child;
|
raxNode *child;
|
||||||
rax->numnodes++;
|
|
||||||
|
|
||||||
/* If this node is going to have a single child, and there
|
/* If this node is going to have a single child, and there
|
||||||
* are other characters, so that that would result in a chain
|
* are other characters, so that that would result in a chain
|
||||||
@ -755,6 +764,7 @@ int raxInsert(rax *rax, unsigned char *s, size_t len, void *data) {
|
|||||||
parentlink = new_parentlink;
|
parentlink = new_parentlink;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
rax->numnodes++;
|
||||||
h = child;
|
h = child;
|
||||||
}
|
}
|
||||||
raxNode *newh = raxReallocForData(h,data);
|
raxNode *newh = raxReallocForData(h,data);
|
||||||
@ -767,10 +777,16 @@ int raxInsert(rax *rax, unsigned char *s, size_t len, void *data) {
|
|||||||
|
|
||||||
oom:
|
oom:
|
||||||
/* This code path handles out of memory after part of the sub-tree was
|
/* This code path handles out of memory after part of the sub-tree was
|
||||||
* already added. Set the node as a key, and then remove it. */
|
* already modified. Set the node as a key, and then remove it. However we
|
||||||
h->isnull = 1;
|
* do that only if the node is a terminal node, otherwise if the OOM
|
||||||
h->iskey = 1;
|
* happened reallocating a node in the middle, we don't need to free
|
||||||
raxRemove(rax,s,i);
|
* anything. */
|
||||||
|
if (h->size == 0) {
|
||||||
|
h->isnull = 1;
|
||||||
|
h->iskey = 1;
|
||||||
|
rax->numele++; /* Compensate the next remove. */
|
||||||
|
assert(raxRemove(rax,s,i,NULL) != 0);
|
||||||
|
}
|
||||||
errno = ENOMEM;
|
errno = ENOMEM;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -830,7 +846,7 @@ raxNode *raxRemoveChild(raxNode *parent, raxNode *child) {
|
|||||||
*
|
*
|
||||||
* 1. To start we seek the first element in both the children
|
* 1. To start we seek the first element in both the children
|
||||||
* pointers and edge bytes in the node. */
|
* pointers and edge bytes in the node. */
|
||||||
raxNode **cp = raxNodeLastChildPtr(parent) - (parent->size-1);
|
raxNode **cp = raxNodeFirstChildPtr(parent);
|
||||||
raxNode **c = cp;
|
raxNode **c = cp;
|
||||||
unsigned char *e = parent->data;
|
unsigned char *e = parent->data;
|
||||||
|
|
||||||
@ -855,7 +871,7 @@ raxNode *raxRemoveChild(raxNode *parent, raxNode *child) {
|
|||||||
memmove(((char*)cp)-1,cp,(parent->size-taillen-1)*sizeof(raxNode**));
|
memmove(((char*)cp)-1,cp,(parent->size-taillen-1)*sizeof(raxNode**));
|
||||||
|
|
||||||
/* Move the remaining "tail" pointer at the right position as well. */
|
/* Move the remaining "tail" pointer at the right position as well. */
|
||||||
memmove(((char*)c)-1,c+1,taillen*sizeof(raxNode**));
|
memmove(((char*)c)-1,c+1,taillen*sizeof(raxNode**)+parent->iskey*sizeof(void*));
|
||||||
|
|
||||||
/* 4. Update size. */
|
/* 4. Update size. */
|
||||||
parent->size--;
|
parent->size--;
|
||||||
@ -863,7 +879,9 @@ raxNode *raxRemoveChild(raxNode *parent, raxNode *child) {
|
|||||||
/* realloc the node according to the theoretical memory usage, to free
|
/* realloc the node according to the theoretical memory usage, to free
|
||||||
* data if we are over-allocating right now. */
|
* data if we are over-allocating right now. */
|
||||||
raxNode *newnode = rax_realloc(parent,raxNodeCurrentLength(parent));
|
raxNode *newnode = rax_realloc(parent,raxNodeCurrentLength(parent));
|
||||||
debugnode("raxRemoveChild after", newnode);
|
if (newnode) {
|
||||||
|
debugnode("raxRemoveChild after", newnode);
|
||||||
|
}
|
||||||
/* Note: if rax_realloc() fails we just return the old address, which
|
/* Note: if rax_realloc() fails we just return the old address, which
|
||||||
* is valid. */
|
* is valid. */
|
||||||
return newnode ? newnode : parent;
|
return newnode ? newnode : parent;
|
||||||
@ -871,18 +889,19 @@ raxNode *raxRemoveChild(raxNode *parent, raxNode *child) {
|
|||||||
|
|
||||||
/* Remove the specified item. Returns 1 if the item was found and
|
/* Remove the specified item. Returns 1 if the item was found and
|
||||||
* deleted, 0 otherwise. */
|
* deleted, 0 otherwise. */
|
||||||
int raxRemove(rax *rax, unsigned char *s, size_t len) {
|
int raxRemove(rax *rax, unsigned char *s, size_t len, void **old) {
|
||||||
raxNode *h;
|
raxNode *h;
|
||||||
raxStack ts;
|
raxStack ts;
|
||||||
|
|
||||||
debugf("### Delete: %.*s\n", (int)len, s);
|
debugf("### Delete: %.*s\n", (int)len, s);
|
||||||
raxStackInit(&ts);
|
raxStackInit(&ts);
|
||||||
int splitpos = 0;
|
int splitpos = 0;
|
||||||
size_t i = raxLowWalk(rax,s,len,&h,NULL,NULL,&ts);
|
size_t i = raxLowWalk(rax,s,len,&h,NULL,&splitpos,&ts);
|
||||||
if (i != len || (h->iscompr && splitpos != 0) || !h->iskey) {
|
if (i != len || (h->iscompr && splitpos != 0) || !h->iskey) {
|
||||||
raxStackFree(&ts);
|
raxStackFree(&ts);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (old) *old = raxGetData(h);
|
||||||
h->iskey = 0;
|
h->iskey = 0;
|
||||||
rax->numele--;
|
rax->numele--;
|
||||||
|
|
||||||
@ -1009,6 +1028,9 @@ int raxRemove(rax *rax, unsigned char *s, size_t len) {
|
|||||||
raxNode **cp = raxNodeLastChildPtr(h);
|
raxNode **cp = raxNodeLastChildPtr(h);
|
||||||
memcpy(&h,cp,sizeof(h));
|
memcpy(&h,cp,sizeof(h));
|
||||||
if (h->iskey || (!h->iscompr && h->size != 1)) break;
|
if (h->iskey || (!h->iscompr && h->size != 1)) break;
|
||||||
|
/* Stop here if going to the next node would result into
|
||||||
|
* a compressed node larger than h->size can hold. */
|
||||||
|
if (comprsize + h->size > RAX_NODE_MAX_SIZE) break;
|
||||||
nodes++;
|
nodes++;
|
||||||
comprsize += h->size;
|
comprsize += h->size;
|
||||||
}
|
}
|
||||||
@ -1115,6 +1137,7 @@ int raxIteratorAddChars(raxIterator *it, unsigned char *s, size_t len) {
|
|||||||
it->key = rax_realloc(old,new_max);
|
it->key = rax_realloc(old,new_max);
|
||||||
if (it->key == NULL) {
|
if (it->key == NULL) {
|
||||||
it->key = (!old) ? it->key_static_string : old;
|
it->key = (!old) ? it->key_static_string : old;
|
||||||
|
errno = ENOMEM;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (old == NULL) memcpy(it->key,it->key_static_string,it->key_len);
|
if (old == NULL) memcpy(it->key,it->key_static_string,it->key_len);
|
||||||
@ -1190,6 +1213,8 @@ int raxIteratorNextStep(raxIterator *it, int noup) {
|
|||||||
* children representing keys lexicographically greater than the
|
* children representing keys lexicographically greater than the
|
||||||
* current key. */
|
* current key. */
|
||||||
while(1) {
|
while(1) {
|
||||||
|
int old_noup = noup;
|
||||||
|
|
||||||
/* Already on head? Can't go up, iteration finished. */
|
/* Already on head? Can't go up, iteration finished. */
|
||||||
if (!noup && it->node == it->rt->head) {
|
if (!noup && it->node == it->rt->head) {
|
||||||
it->flags |= RAX_ITER_EOF;
|
it->flags |= RAX_ITER_EOF;
|
||||||
@ -1211,9 +1236,9 @@ int raxIteratorNextStep(raxIterator *it, int noup) {
|
|||||||
int todel = it->node->iscompr ? it->node->size : 1;
|
int todel = it->node->iscompr ? it->node->size : 1;
|
||||||
raxIteratorDelChars(it,todel);
|
raxIteratorDelChars(it,todel);
|
||||||
|
|
||||||
/* Try visitng the next child if there was at least one
|
/* Try visiting the next child if there was at least one
|
||||||
* additional child. */
|
* additional child. */
|
||||||
if (!it->node->iscompr && it->node->size > 1) {
|
if (!it->node->iscompr && it->node->size > (old_noup ? 0 : 1)) {
|
||||||
raxNode **cp = raxNodeFirstChildPtr(it->node);
|
raxNode **cp = raxNodeFirstChildPtr(it->node);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (i < it->node->size) {
|
while (i < it->node->size) {
|
||||||
@ -1276,6 +1301,8 @@ int raxIteratorPrevStep(raxIterator *it, int noup) {
|
|||||||
raxNode *orig_node = it->node;
|
raxNode *orig_node = it->node;
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
|
int old_noup = noup;
|
||||||
|
|
||||||
/* Already on head? Can't go up, iteration finished. */
|
/* Already on head? Can't go up, iteration finished. */
|
||||||
if (!noup && it->node == it->rt->head) {
|
if (!noup && it->node == it->rt->head) {
|
||||||
it->flags |= RAX_ITER_EOF;
|
it->flags |= RAX_ITER_EOF;
|
||||||
@ -1297,9 +1324,9 @@ int raxIteratorPrevStep(raxIterator *it, int noup) {
|
|||||||
int todel = it->node->iscompr ? it->node->size : 1;
|
int todel = it->node->iscompr ? it->node->size : 1;
|
||||||
raxIteratorDelChars(it,todel);
|
raxIteratorDelChars(it,todel);
|
||||||
|
|
||||||
/* Try visiting the prev child if there was at least one
|
/* Try visiting the prev child if there is at least one
|
||||||
* additional child. */
|
* child. */
|
||||||
if (!it->node->iscompr && it->node->size > 1) {
|
if (!it->node->iscompr && it->node->size > (old_noup ? 0 : 1)) {
|
||||||
raxNode **cp = raxNodeLastChildPtr(it->node);
|
raxNode **cp = raxNodeLastChildPtr(it->node);
|
||||||
int i = it->node->size-1;
|
int i = it->node->size-1;
|
||||||
while (i >= 0) {
|
while (i >= 0) {
|
||||||
@ -1334,8 +1361,9 @@ int raxIteratorPrevStep(raxIterator *it, int noup) {
|
|||||||
|
|
||||||
/* Seek an iterator at the specified element.
|
/* Seek an iterator at the specified element.
|
||||||
* Return 0 if the seek failed for syntax error or out of memory. Otherwise
|
* Return 0 if the seek failed for syntax error or out of memory. Otherwise
|
||||||
* 1 is returned. */
|
* 1 is returned. When 0 is returned for out of memory, errno is set to
|
||||||
int raxSeek(raxIterator *it, unsigned char *ele, size_t len, const char *op) {
|
* the ENOMEM value. */
|
||||||
|
int raxSeek(raxIterator *it, const char *op, unsigned char *ele, size_t len) {
|
||||||
int eq = 0, lt = 0, gt = 0, first = 0, last = 0;
|
int eq = 0, lt = 0, gt = 0, first = 0, last = 0;
|
||||||
|
|
||||||
it->stack.items = 0; /* Just resetting. Intialized by raxStart(). */
|
it->stack.items = 0; /* Just resetting. Intialized by raxStart(). */
|
||||||
@ -1358,6 +1386,7 @@ int raxSeek(raxIterator *it, unsigned char *ele, size_t len, const char *op) {
|
|||||||
} else if (op[0] == '$') {
|
} else if (op[0] == '$') {
|
||||||
last = 1;
|
last = 1;
|
||||||
} else {
|
} else {
|
||||||
|
errno = 0;
|
||||||
return 0; /* Error. */
|
return 0; /* Error. */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1371,7 +1400,7 @@ int raxSeek(raxIterator *it, unsigned char *ele, size_t len, const char *op) {
|
|||||||
if (first) {
|
if (first) {
|
||||||
/* Seeking the first key greater or equal to the empty string
|
/* Seeking the first key greater or equal to the empty string
|
||||||
* is equivalent to seeking the smaller key available. */
|
* is equivalent to seeking the smaller key available. */
|
||||||
return raxSeek(it,NULL,0,">=");
|
return raxSeek(it,">=",NULL,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (last) {
|
if (last) {
|
||||||
@ -1398,7 +1427,7 @@ int raxSeek(raxIterator *it, unsigned char *ele, size_t len, const char *op) {
|
|||||||
/* We found our node, since the key matches and we have an
|
/* We found our node, since the key matches and we have an
|
||||||
* "equal" condition. */
|
* "equal" condition. */
|
||||||
if (!raxIteratorAddChars(it,ele,len)) return 0; /* OOM. */
|
if (!raxIteratorAddChars(it,ele,len)) return 0; /* OOM. */
|
||||||
} else {
|
} else if (lt || gt) {
|
||||||
/* Exact key not found or eq flag not set. We have to set as current
|
/* Exact key not found or eq flag not set. We have to set as current
|
||||||
* key the one represented by the node we stopped at, and perform
|
* key the one represented by the node we stopped at, and perform
|
||||||
* a next/prev operation to seek. To reconstruct the key at this node
|
* a next/prev operation to seek. To reconstruct the key at this node
|
||||||
@ -1490,6 +1519,10 @@ int raxSeek(raxIterator *it, unsigned char *ele, size_t len, const char *op) {
|
|||||||
if (lt && !raxIteratorPrevStep(it,0)) return 0;
|
if (lt && !raxIteratorPrevStep(it,0)) return 0;
|
||||||
it->flags |= RAX_ITER_JUST_SEEKED; /* Ignore next call. */
|
it->flags |= RAX_ITER_JUST_SEEKED; /* Ignore next call. */
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
/* If we are here just eq was set but no match was found. */
|
||||||
|
it->flags |= RAX_ITER_EOF;
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -1497,7 +1530,7 @@ int raxSeek(raxIterator *it, unsigned char *ele, size_t len, const char *op) {
|
|||||||
/* Go to the next element in the scope of the iterator 'it'.
|
/* Go to the next element in the scope of the iterator 'it'.
|
||||||
* If EOF (or out of memory) is reached, 0 is returned, otherwise 1 is
|
* If EOF (or out of memory) is reached, 0 is returned, otherwise 1 is
|
||||||
* returned. In case 0 is returned because of OOM, errno is set to ENOMEM. */
|
* returned. In case 0 is returned because of OOM, errno is set to ENOMEM. */
|
||||||
int raxNext(raxIterator *it, unsigned char *stop, size_t stoplen, char *op) {
|
int raxNext(raxIterator *it) {
|
||||||
if (!raxIteratorNextStep(it,0)) {
|
if (!raxIteratorNextStep(it,0)) {
|
||||||
errno = ENOMEM;
|
errno = ENOMEM;
|
||||||
return 0;
|
return 0;
|
||||||
@ -1512,7 +1545,7 @@ int raxNext(raxIterator *it, unsigned char *stop, size_t stoplen, char *op) {
|
|||||||
/* Go to the previous element in the scope of the iterator 'it'.
|
/* Go to the previous element in the scope of the iterator 'it'.
|
||||||
* If EOF (or out of memory) is reached, 0 is returned, otherwise 1 is
|
* If EOF (or out of memory) is reached, 0 is returned, otherwise 1 is
|
||||||
* returned. In case 0 is returned because of OOM, errno is set to ENOMEM. */
|
* returned. In case 0 is returned because of OOM, errno is set to ENOMEM. */
|
||||||
int raxPrev(raxIterator *it, unsigned char *stop, size_t stoplen, char *op) {
|
int raxPrev(raxIterator *it) {
|
||||||
if (!raxIteratorPrevStep(it,0)) {
|
if (!raxIteratorPrevStep(it,0)) {
|
||||||
errno = ENOMEM;
|
errno = ENOMEM;
|
||||||
return 0;
|
return 0;
|
||||||
@ -1524,6 +1557,87 @@ int raxPrev(raxIterator *it, unsigned char *stop, size_t stoplen, char *op) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Perform a random walk starting in the current position of the iterator.
|
||||||
|
* Return 0 if the tree is empty or on out of memory. Otherwise 1 is returned
|
||||||
|
* and the iterator is set to the node reached after doing a random walk
|
||||||
|
* of 'steps' steps. If the 'steps' argument is 0, the random walk is performed
|
||||||
|
* using a random number of steps between 1 and two times the logarithm of
|
||||||
|
* the number of elements.
|
||||||
|
*
|
||||||
|
* NOTE: if you use this function to generate random elements from the radix
|
||||||
|
* tree, expect a disappointing distribution. A random walk produces good
|
||||||
|
* random elements if the tree is not sparse, however in the case of a radix
|
||||||
|
* tree certain keys will be reported much more often than others. At least
|
||||||
|
* this function should be able to expore every possible element eventually. */
|
||||||
|
int raxRandomWalk(raxIterator *it, size_t steps) {
|
||||||
|
if (it->rt->numele == 0) {
|
||||||
|
it->flags |= RAX_ITER_EOF;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (steps == 0) {
|
||||||
|
size_t fle = floor(log(it->rt->numele));
|
||||||
|
fle *= 2;
|
||||||
|
steps = 1 + rand() % fle;
|
||||||
|
}
|
||||||
|
|
||||||
|
raxNode *n = it->node;
|
||||||
|
while(steps > 0 || !n->iskey) {
|
||||||
|
int numchildren = n->iscompr ? 1 : n->size;
|
||||||
|
int r = rand() % (numchildren+(n != it->rt->head));
|
||||||
|
|
||||||
|
if (r == numchildren) {
|
||||||
|
/* Go up to parent. */
|
||||||
|
n = raxStackPop(&it->stack);
|
||||||
|
int todel = n->iscompr ? n->size : 1;
|
||||||
|
raxIteratorDelChars(it,todel);
|
||||||
|
} else {
|
||||||
|
/* Select a random child. */
|
||||||
|
if (n->iscompr) {
|
||||||
|
if (!raxIteratorAddChars(it,n->data,n->size)) return 0;
|
||||||
|
} else {
|
||||||
|
if (!raxIteratorAddChars(it,n->data+r,1)) return 0;
|
||||||
|
}
|
||||||
|
raxNode **cp = raxNodeFirstChildPtr(n)+r;
|
||||||
|
if (!raxStackPush(&it->stack,n)) return 0;
|
||||||
|
memcpy(&n,cp,sizeof(n));
|
||||||
|
}
|
||||||
|
if (n->iskey) steps--;
|
||||||
|
}
|
||||||
|
it->node = n;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compare the key currently pointed by the iterator to the specified
|
||||||
|
* key according to the specified operator. Returns 1 if the comparison is
|
||||||
|
* true, otherwise 0 is returned. */
|
||||||
|
int raxCompare(raxIterator *iter, const char *op, unsigned char *key, size_t key_len) {
|
||||||
|
int eq = 0, lt = 0, gt = 0;
|
||||||
|
|
||||||
|
if (op[0] == '=' || op[1] == '=') eq = 1;
|
||||||
|
if (op[1] == '>') gt = 1;
|
||||||
|
else if (op[1] == '<') lt = 1;
|
||||||
|
else if (op[1] != '=') return 0; /* Syntax error. */
|
||||||
|
|
||||||
|
size_t minlen = key_len < iter->key_len ? key_len : iter->key_len;
|
||||||
|
int cmp = memcmp(iter->key,key,minlen);
|
||||||
|
|
||||||
|
/* Handle == */
|
||||||
|
if (lt == 0 && gt == 0) return cmp == 0 && key_len == iter->key_len;
|
||||||
|
|
||||||
|
/* Handle >, >=, <, <= */
|
||||||
|
if (cmp == 0) {
|
||||||
|
/* Same prefix: longer wins. */
|
||||||
|
if (eq && key_len == iter->key_len) return 1;
|
||||||
|
else if (lt) return iter->key_len < key_len;
|
||||||
|
else if (gt) return iter->key_len > key_len;
|
||||||
|
} if (cmp > 0) {
|
||||||
|
return gt ? 1 : 0;
|
||||||
|
} else /* (cmp < 0) */ {
|
||||||
|
return lt ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Free the iterator. */
|
/* Free the iterator. */
|
||||||
void raxStop(raxIterator *it) {
|
void raxStop(raxIterator *it) {
|
||||||
if (it->key != it->key_static_string) rax_free(it->key);
|
if (it->key != it->key_static_string) rax_free(it->key);
|
||||||
@ -1613,233 +1727,4 @@ void raxDebugShowNode(const char *msg, raxNode *n) {
|
|||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BENCHMARK_MAIN
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
|
|
||||||
/* This is a simple Feistel network in order to turn every possible
|
|
||||||
* uint32_t input into another "randomly" looking uint32_t. It is a
|
|
||||||
* one to one map so there are no repetitions. */
|
|
||||||
static uint32_t int2int(uint32_t input) {
|
|
||||||
uint16_t l = input & 0xffff;
|
|
||||||
uint16_t r = input >> 16;
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
uint16_t nl = r;
|
|
||||||
uint16_t F = (((r * 31) + (r >> 5) + 7 * 371) ^ r) & 0xffff;
|
|
||||||
r = l ^ F;
|
|
||||||
l = nl;
|
|
||||||
}
|
|
||||||
return (r<<16)|l;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Turn an uint32_t integer into an alphanumerical key and return its
|
|
||||||
* length. This function is used in order to generate keys that have
|
|
||||||
* a large charset, so that the radix tree can be testsed with many
|
|
||||||
* children per node. */
|
|
||||||
static size_t int2alphakey(char *s, size_t maxlen, uint32_t i) {
|
|
||||||
const char *set = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
||||||
"abcdefghijklmnopqrstuvwxyz"
|
|
||||||
"0123456789";
|
|
||||||
const size_t setlen = 62;
|
|
||||||
|
|
||||||
if (maxlen == 0) return 0;
|
|
||||||
maxlen--; /* Space for null term char. */
|
|
||||||
size_t len = 0;
|
|
||||||
while(len < maxlen) {
|
|
||||||
s[len++] = set[i%setlen];
|
|
||||||
i /= setlen;
|
|
||||||
if (i == 0) break;
|
|
||||||
}
|
|
||||||
s[len] = '\0';
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the UNIX time in microseconds */
|
|
||||||
static long long ustime(void) {
|
|
||||||
struct timeval tv;
|
|
||||||
long long ust;
|
|
||||||
|
|
||||||
gettimeofday(&tv, NULL);
|
|
||||||
ust = ((long long)tv.tv_sec)*1000000;
|
|
||||||
ust += tv.tv_usec;
|
|
||||||
return ust;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Turn the integer 'i' into a key according to 'mode'.
|
|
||||||
* mode = 0, just represents the integer as a string.
|
|
||||||
* mode = 1, turn it into a random-looking alphanumerical string
|
|
||||||
* according to the int2alphakey() function. */
|
|
||||||
static size_t int2key(char *s, size_t maxlen, uint32_t i, int mode) {
|
|
||||||
if (mode == 0) {
|
|
||||||
return snprintf(s,maxlen,"%lu",(unsigned long)i);
|
|
||||||
} else {
|
|
||||||
i = int2int(i);
|
|
||||||
return int2alphakey(s,maxlen,i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
for (int mode = 0; mode < 2; mode++) {
|
|
||||||
printf("Benchmark with %s keys:\n",
|
|
||||||
(mode == 0) ? "integer" : "alphanumerical");
|
|
||||||
rax *t = raxNew();
|
|
||||||
long long start = ustime();
|
|
||||||
for (int i = 0; i < 5000000; i++) {
|
|
||||||
char buf[64];
|
|
||||||
int len = int2key(buf,sizeof(buf),i,mode);
|
|
||||||
raxInsert(t,(unsigned char*)buf,len,(void*)(long)i);
|
|
||||||
}
|
|
||||||
printf("Insert: %f\n", (double)(ustime()-start)/1000000);
|
|
||||||
printf("%llu total nodes\n", (unsigned long long)t->numnodes);
|
|
||||||
printf("%llu total elements\n", (unsigned long long)t->numele);
|
|
||||||
|
|
||||||
start = ustime();
|
|
||||||
for (int i = 0; i < 5000000; i++) {
|
|
||||||
char buf[64];
|
|
||||||
int len = int2key(buf,sizeof(buf),i,mode);
|
|
||||||
void *data = raxFind(t,(unsigned char*)buf,len);
|
|
||||||
if (data != (void*)(long)i) {
|
|
||||||
printf("Issue with %s: %p instead of %p\n", buf,
|
|
||||||
data, (void*)(long)i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("Lookup: %f\n", (double)(ustime()-start)/1000000);
|
|
||||||
|
|
||||||
start = ustime();
|
|
||||||
for (int i = 0; i < 5000000; i++) {
|
|
||||||
char buf[64];
|
|
||||||
int r = rand() % 5000000;
|
|
||||||
int len = int2key(buf,sizeof(buf),r,mode);
|
|
||||||
void *data = raxFind(t,(unsigned char*)buf,len);
|
|
||||||
if (data != (void*)(long)r) {
|
|
||||||
printf("Issue with %s: %p instead of %p\n", buf,
|
|
||||||
data, (void*)(long)r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("Random lookup: %f\n", (double)(ustime()-start)/1000000);
|
|
||||||
|
|
||||||
start = ustime();
|
|
||||||
int count = 0;
|
|
||||||
for (int i = 0; i < 5000000; i++) {
|
|
||||||
char buf[64];
|
|
||||||
int len = int2key(buf,sizeof(buf),i,mode);
|
|
||||||
buf[i%len] = '!'; /* "!" is never set into keys. */
|
|
||||||
void *data = raxFind(t,(unsigned char*) buf,len);
|
|
||||||
if (data != (void*)(long)i) count++;
|
|
||||||
}
|
|
||||||
printf("Failed lookup: %f\n", (double)(ustime()-start)/1000000);
|
|
||||||
|
|
||||||
start = ustime();
|
|
||||||
for (int i = 0; i < 5000000; i++) {
|
|
||||||
char buf[64];
|
|
||||||
int len = int2key(buf,sizeof(buf),i,mode);
|
|
||||||
int retval = raxRemove(t,(unsigned char*)buf,len);
|
|
||||||
assert(retval == 1);
|
|
||||||
}
|
|
||||||
printf("Deletion: %f\n", (double)(ustime()-start)/1000000);
|
|
||||||
|
|
||||||
printf("%llu total nodes\n", (unsigned long long)t->numnodes);
|
|
||||||
printf("%llu total elements\n", (unsigned long long)t->numele);
|
|
||||||
raxFree(t);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef TEST_MAIN
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
printf("notfound = %p\n", raxNotFound);
|
|
||||||
rax *t = raxNew();
|
|
||||||
char *toadd[] = {"alligator","alien","baloon","chromodynamic","romane","romanus","romulus","rubens","ruber","rubicon","rubicundus","all","rub","ba",NULL};
|
|
||||||
|
|
||||||
srand(time(NULL));
|
|
||||||
for (int x = 0; x < 10000; x++) rand();
|
|
||||||
|
|
||||||
long items = 0;
|
|
||||||
while(toadd[items] != NULL) items++;
|
|
||||||
|
|
||||||
for (long i = 0; i < items; i++) {
|
|
||||||
raxInsert(t,(unsigned char*)toadd[i],strlen(toadd[i]),(void*)i);
|
|
||||||
printf("Added %s\n", toadd[i]);
|
|
||||||
}
|
|
||||||
raxShow(t);
|
|
||||||
|
|
||||||
raxIterator iter;
|
|
||||||
raxStart(&iter,t);
|
|
||||||
|
|
||||||
// OK: all this tests will need to go in the Rax unit test.
|
|
||||||
// raxSeek(&iter,(unsigned char*)"rpxxx",5,"<=");
|
|
||||||
// raxSeek(&iter,(unsigned char*)"rom",3,">=");
|
|
||||||
// raxSeek(&iter,(unsigned char*)"rub",3,">=");
|
|
||||||
// raxSeek(&iter,(unsigned char*)"rub",3,">");
|
|
||||||
// raxSeek(&iter,(unsigned char*)"rub",3,"<");
|
|
||||||
// raxSeek(&iter,(unsigned char*)"rom",3,">");
|
|
||||||
// raxSeek(&iter,(unsigned char*)"chro",4,">");
|
|
||||||
// raxSeek(&iter,(unsigned char*)"chro",4,"<");
|
|
||||||
// raxSeek(&iter,(unsigned char*)"chromz",6,"<");
|
|
||||||
// raxSeek(&iter,NULL,0,"^");
|
|
||||||
// raxSeek(&iter,"zorro",5,"<=");
|
|
||||||
// raxSeek(&iter,"zorro",5,"<");
|
|
||||||
// raxSeek(&iter,NULL,0,"$");
|
|
||||||
|
|
||||||
// STILL TO TEST
|
|
||||||
raxSeek(&iter,(unsigned char*)"ro",2,">=");
|
|
||||||
printf("EOF: %d\n", (iter.flags & RAX_ITER_EOF) != 0);
|
|
||||||
|
|
||||||
printf("SEEKED: %.*s, val %p\n", (int)iter.key_len,
|
|
||||||
(char*)iter.key,
|
|
||||||
iter.data);
|
|
||||||
|
|
||||||
printf("NEXT\n");
|
|
||||||
while(raxNext(&iter,NULL,0,NULL)) {
|
|
||||||
printf("--- key: %.*s, val %p\n", (int)iter.key_len,
|
|
||||||
(char*)iter.key,
|
|
||||||
iter.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("After EOF element is: %.*s\n", (int)iter.key_len,
|
|
||||||
(char*)iter.key);
|
|
||||||
printf("~~~~~~~~~~~~~~\n");
|
|
||||||
|
|
||||||
printf("PREV\n");
|
|
||||||
raxSeek(&iter,iter.key,iter.key_len,"==");
|
|
||||||
while(raxPrev(&iter,NULL,0,NULL)) {
|
|
||||||
printf("--- key: %.*s, val %p\n", (int)iter.key_len,
|
|
||||||
(char*)iter.key,
|
|
||||||
iter.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("After EOF element is: %.*s\n", (int)iter.key_len,
|
|
||||||
(char*)iter.key);
|
|
||||||
raxStop(&iter);
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
raxStop(&iter);
|
|
||||||
#endif
|
|
||||||
exit(0);
|
|
||||||
|
|
||||||
int rnum = rand();
|
|
||||||
int survivor = rnum % items;
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
printf("Removing everything but %s in random order\n", toadd[survivor]);
|
|
||||||
for (long i = 0; i < 1000; i++) {
|
|
||||||
int r = rand() % items;
|
|
||||||
if (r == survivor) continue;
|
|
||||||
raxRemove(t,(unsigned char*)toadd[r],strlen(toadd[r]));
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
printf("Removing rubicon\n");
|
|
||||||
raxRemove(t,(unsigned char*)"rubicon",7);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
printf("%llu total nodes\n", (unsigned long long)t->numnodes);
|
|
||||||
printf("%llu total elements\n", (unsigned long long)t->numele);
|
|
||||||
|
|
||||||
raxShow(t);
|
|
||||||
raxFree(t);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
12
src/rax.h
12
src/rax.h
@ -144,14 +144,16 @@ extern void *raxNotFound;
|
|||||||
|
|
||||||
/* Exported API. */
|
/* Exported API. */
|
||||||
rax *raxNew(void);
|
rax *raxNew(void);
|
||||||
int raxInsert(rax *rax, unsigned char *s, size_t len, void *data);
|
int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old);
|
||||||
int raxRemove(rax *rax, unsigned char *s, size_t len);
|
int raxRemove(rax *rax, unsigned char *s, size_t len, void **old);
|
||||||
void *raxFind(rax *rax, unsigned char *s, size_t len);
|
void *raxFind(rax *rax, unsigned char *s, size_t len);
|
||||||
void raxFree(rax *rax);
|
void raxFree(rax *rax);
|
||||||
void raxStart(raxIterator *it, rax *rt);
|
void raxStart(raxIterator *it, rax *rt);
|
||||||
int raxSeek(raxIterator *it, unsigned char *ele, size_t len, const char *op);
|
int raxSeek(raxIterator *it, const char *op, unsigned char *ele, size_t len);
|
||||||
int raxNext(raxIterator *it, unsigned char *stop, size_t stoplen, char *op);
|
int raxNext(raxIterator *it);
|
||||||
int raxPrev(raxIterator *it, unsigned char *stop, size_t stoplen, char *op);
|
int raxPrev(raxIterator *it);
|
||||||
|
int raxRandomWalk(raxIterator *it, size_t steps);
|
||||||
|
int raxCompare(raxIterator *iter, const char *op, unsigned char *key, size_t key_len);
|
||||||
void raxStop(raxIterator *it);
|
void raxStop(raxIterator *it);
|
||||||
void raxShow(rax *rax);
|
void raxShow(rax *rax);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user