summaryrefslogtreecommitdiff
path: root/src/rax.c
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2018-06-04 17:26:16 +0200
committerantirez <antirez@gmail.com>2018-06-04 17:26:16 +0200
commit05a29966410c82bd3603ae290a342e856158e18a (patch)
treef04be87a62d79217f9d06cae47765d7df6b2f1c9 /src/rax.c
parent7c6f1be5df4b3f453ab3928b2239ed60138e1cef (diff)
downloadredis-05a29966410c82bd3603ae290a342e856158e18a.tar.gz
Rax library updated.
Diffstat (limited to 'src/rax.c')
-rw-r--r--src/rax.c67
1 files changed, 57 insertions, 10 deletions
diff --git a/src/rax.c b/src/rax.c
index 4e45fd2c3..8764dd8c9 100644
--- a/src/rax.c
+++ b/src/rax.c
@@ -359,7 +359,18 @@ raxNode *raxCompressNode(raxNode *n, unsigned char *s, size_t len, raxNode **chi
* parent's node is returned as '*plink' if not NULL. Finally, if the
* search stopped in a compressed node, '*splitpos' returns the index
* inside the compressed node where the search ended. This is useful to
- * know where to split the node for insertion. */
+ * know where to split the node for insertion.
+ *
+ * Note that when we stop in the middle of a compressed node with
+ * a perfect match, this function will return a length equal to the
+ * 'len' argument (all the key matched), and will return a *splitpos which is
+ * always positive (that will represent the index of the character immediately
+ * *after* the last match in the current compressed node).
+ *
+ * When instead we stop at a compressed node and *splitpos is zero, it
+ * means that the current node represents the key (that is, none of the
+ * compressed node characters are needed to represent the key, just all
+ * its parents nodes). */
static inline size_t raxLowWalk(rax *rax, unsigned char *s, size_t len, raxNode **stopnode, raxNode ***plink, int *splitpos, raxStack *ts) {
raxNode *h = rax->head;
raxNode **parentlink = &rax->head;
@@ -405,10 +416,12 @@ static inline size_t raxLowWalk(rax *rax, unsigned char *s, size_t len, raxNode
/* Insert the element 's' of size 'len', setting as auxiliary data
* the pointer 'data'. If the element is already present, the associated
- * 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
- * sets errno to ENOMEM, otherwise errno will be set to 0. */
-int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) {
+ * data is updated (only if 'overwrite' is set to 1), and 0 is returned,
+ * otherwise the element is inserted 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.
+ */
+int raxGenericInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old, int overwrite) {
size_t i;
int j = 0; /* Split position. If raxLowWalk() stops in a compressed
node, the index 'j' represents the char we stopped within the
@@ -426,7 +439,8 @@ int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) {
* data pointer. */
if (i == len && (!h->iscompr || j == 0 /* not in the middle if j is 0 */)) {
debugf("### Insert: node representing key exists\n");
- if (!h->iskey || h->isnull) {
+ /* Make space for the value pointer if needed. */
+ if (!h->iskey || (h->isnull && overwrite)) {
h = raxReallocForData(h,data);
if (h) memcpy(parentlink,&h,sizeof(h));
}
@@ -434,12 +448,17 @@ int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) {
errno = ENOMEM;
return 0;
}
+
+ /* Update the existing key if there is already one. */
if (h->iskey) {
if (old) *old = raxGetData(h);
- raxSetData(h,data);
+ if (overwrite) raxSetData(h,data);
errno = 0;
return 0; /* Element already exists. */
}
+
+ /* Otherwise set the node as a key. Note that raxSetData()
+ * will set h->iskey. */
raxSetData(h,data);
rax->numele++;
return 1; /* Element inserted. */
@@ -793,6 +812,19 @@ oom:
return 0;
}
+/* Overwriting insert. Just a wrapper for raxGenericInsert() that will
+ * update the element if there is already one for the same key. */
+int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) {
+ return raxGenericInsert(rax,s,len,data,old,1);
+}
+
+/* Non overwriting insert function: this if an element with the same key
+ * exists, the value is not updated and the function returns 0.
+ * This is a just a wrapper for raxGenericInsert(). */
+int raxTryInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) {
+ return raxGenericInsert(rax,s,len,data,old,0);
+}
+
/* Find a key in the rax, returns raxNotFound special void pointer value
* if the item was not found, otherwise the value associated with the
* item is returned. */
@@ -1523,11 +1555,26 @@ int raxSeek(raxIterator *it, const char *op, unsigned char *ele, size_t len) {
/* If there was no mismatch we are into a node representing the
* key, (but which is not a key or the seek operator does not
* include 'eq'), or we stopped in the middle of a compressed node
- * after processing all the key. Cotinue iterating as this was
+ * after processing all the key. Continue iterating as this was
* a legitimate key we stopped at. */
it->flags &= ~RAX_ITER_JUST_SEEKED;
- if (gt && !raxIteratorNextStep(it,0)) return 0;
- if (lt && !raxIteratorPrevStep(it,0)) return 0;
+ if (it->node->iscompr && it->node->iskey && splitpos && lt) {
+ /* If we stopped in the middle of a compressed node with
+ * perfect match, and the condition is to seek a key "<" than
+ * the specified one, then if this node is a key it already
+ * represents our match. For instance we may have nodes:
+ *
+ * "f" -> "oobar" = 1 -> "" = 2
+ *
+ * Representing keys "f" = 1, "foobar" = 2. A seek for
+ * the key < "foo" will stop in the middle of the "oobar"
+ * node, but will be our match, representing the key "f".
+ *
+ * So in that case, we don't seek backward. */
+ } else {
+ if (gt && !raxIteratorNextStep(it,0)) return 0;
+ if (lt && !raxIteratorPrevStep(it,0)) return 0;
+ }
it->flags |= RAX_ITER_JUST_SEEKED; /* Ignore next call. */
}
} else {