diff --git a/include/avl_tree.h b/include/avl_tree.h new file mode 100644 index 0000000000000000000000000000000000000000..268bf485a5a559080774adfbedf8e8498904dde5 --- /dev/null +++ b/include/avl_tree.h @@ -0,0 +1,972 @@ +/* + * Copyright (C) 2012 Mail.RU + * Copyright (C) 2010 Teodor Sigaev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _AVLTREE_H_ +#define _AVLTREE_H_ + +#include <sys/types.h> +#include <string.h> +#include <stdlib.h> + +#include <third_party/qsort_arg.h> + +#if defined(__cplusplus) +extern "C" { +#endif /* defined(__cplusplus) */ + +#ifndef AVLTREE_NODE_SELF +/* + * user could suggest pointer's storage himself + */ +typedef u_int32_t avlnode_t; +#define AVLNIL (0x7fffffff) +#define AVLINDEXMASK (0x7fffffff) +#define AVLFLAGMASK (0x80000000) +#define AVLMAXDEPTH (48) + +typedef struct avltree_node_pointers { + u_int32_t left; /* sizeof(avlnode_t) >= sizeof(avltree_node_pointers.left) !!! */ + u_int32_t right; +} avltree_node_pointers; + +#define GET_AVLNODE_LEFT(snp) ((snp)->left & AVLINDEXMASK) +#define SET_AVLNODE_LEFT(snp, v) ((snp)->left = (((snp)->left & AVLFLAGMASK) | (v))) +#define GET_AVLNODE_RIGHT(snp) ((snp)->right & AVLINDEXMASK) +#define SET_AVLNODE_RIGHT(snp, v) ((snp)->right = (((snp)->right & AVLFLAGMASK) | (v))) +#define GET_AVLNODE_BALANCE(snp) (((snp)->right >> 31) ? 1 : ((snp)->left >> 31) ? -1 : 0) +#define SET_AVLNODE_BALANCE(snp, v) ((snp)->left = (((snp)->left & AVLINDEXMASK) | ((v) < 0 ? AVLFLAGMASK : 0))), ((snp)->right = (((snp)->right & AVLINDEXMASK) | ((v) > 0 ? AVLFLAGMASK : 0))) + +#endif /* AVLTREE_NODE_SELF */ + +#define _GET_AVLNODE_LEFT(n) GET_AVLNODE_LEFT( t->lrpointers + (n) ) +#define _SET_AVLNODE_LEFT(n, v) SET_AVLNODE_LEFT( t->lrpointers + (n), (v) ) +#define _GET_AVLNODE_RIGHT(n) GET_AVLNODE_RIGHT( t->lrpointers + (n) ) +#define _SET_AVLNODE_RIGHT(n, v) SET_AVLNODE_RIGHT( t->lrpointers + (n), (v) ) +#define _GET_AVLNODE_BALANCE(n) GET_AVLNODE_BALANCE( t->lrpointers + (n) ) +#define _SET_AVLNODE_BALANCE(n, v) SET_AVLNODE_BALANCE( t->lrpointers + (n), (v) ) + +#define AVLITHELEM(t, i) ( (char *) (t)->members + (t)->elemsize * (i) ) +#define AVLELEMIDX(t, e) ( ((e) - (t)->members) / (t)->elemsize ) + +/* + * makes definition of tree with methods, name should + * be unique across all definitions. + * + * Methods: + * void avltree_NAME_init(avltree_NAME *tree, size_t elemsize, void *array, + * avlnode_t array_len, avlnode_t array_size, + * int (*compare)(const void *key, const void *elem, void *arg), + * int (*elemcompare)(const void *e1, const void *e2, void *arg), + * void *arg) + * + * void avltree_NAME_replace(avltree_NAME *tree, void *value, void **p_oldvalue) + * void avltree_NAME_delete(avltree_NAME *tree, void *value) + * void* avltree_NAME_find(avltree_NAME *tree, void *key) + * + * avlnode_t avltree_NAME_walk(avltree_NAME *t, void* array, avlnode_t limit, avlnode_t offset) + * void avltree_NAME_walk_cb(avltree_NAME *t, int (*cb)(void* cb_arg, void* elem), void *cb_arg) + * + * avltree_NAME_iterator* avltree_NAME_iterator_init(avltree_NAME *t) + * void avltree_NAME_iterator_init_set(avltree_NAME *t, avltree_NAME_iterator **iterator, void *start) + * avltree_NAME_iterator* avltree_NAME_iterator_reverse_init(avltree_NAME *t) + * void avltree_NAME_iterator_reverse_init_set(avltree_NAME *t, avltree_NAME_iterator **iterator, void *start) + * void avltree_NAME_iterator_free(avltree_NAME_iterator *i) + * + * void* avltree_NAME_iterator_next(avltree_NAME_iterator *i) + * void* avltree_NAME_iterator_reverse_next(avltree_NAME_iterator *i) + */ + +#define AVL_DEF(name, realloc) \ +typedef struct avl_##name { \ + void *members; \ + avltree_node_pointers *lrpointers; \ + \ + avlnode_t nmember; \ + avlnode_t ntotal; \ + \ + int (*compare)(const void *key, const void *elem, void *); \ + int (*elemcompare)(const void *e1, const void *e2, void *); \ + void* arg; \ + size_t elemsize; \ + \ + avlnode_t root; \ + avlnode_t garbage_head; \ + avlnode_t size; \ + avlnode_t max_size; \ + avlnode_t max_depth; \ +} avl_##name; \ + \ +static avlnode_t \ +avl_##name##_mktree(avl_##name *t, avlnode_t depth, \ + avlnode_t start, avlnode_t end, int* height) { \ + avlnode_t half = ( (end + start) >> 1 ), tmp; \ + int lh = 0, rh = 0; \ + \ + if (depth > t->max_depth) t->max_depth = depth; \ + \ + if ( half == start || \ + ( tmp = avl_##name##_mktree(t, depth+1, start, half, &lh)) == half ) \ + _SET_AVLNODE_LEFT(half, AVLNIL); \ + else \ + _SET_AVLNODE_LEFT(half, tmp); \ + if ( half+1 >= end || \ + ( tmp = avl_##name##_mktree(t, depth+1, half+1, end, &rh)) == half ) \ + _SET_AVLNODE_RIGHT(half, AVLNIL); \ + else \ + _SET_AVLNODE_RIGHT(half, tmp); \ + \ + _SET_AVLNODE_BALANCE(half, rh - lh); \ + \ + if(height) { \ + *height = (lh > rh ? lh : rh) + 1; \ + } \ + \ + return half; \ +} \ + \ +static inline int \ +avl_##name##_height_of_subtree(avl_##name *t, avlnode_t node) { \ + if (node == AVLNIL) \ + return 0; \ + int l = avl_##name##_height_of_subtree(t, _GET_AVLNODE_LEFT(node)); \ + int r = avl_##name##_height_of_subtree(t, _GET_AVLNODE_RIGHT(node)); \ + return 1 + (l > r ? l : r); \ +} \ + \ +static inline int \ +avl_##name##_check_subtree(avl_##name *t, avlnode_t node) { \ + if (node == AVLNIL) \ + return 0; \ + if(_GET_AVLNODE_LEFT(node) != AVLNIL) { \ + void* l = AVLITHELEM(t, _GET_AVLNODE_LEFT(node)); \ + void* c = AVLITHELEM(t, node); \ + if(t->elemcompare(l, c, t->arg) >= 0) { \ + return 1; \ + } \ + } \ + if(_GET_AVLNODE_RIGHT(node) != AVLNIL) { \ + void* r = AVLITHELEM(t, _GET_AVLNODE_RIGHT(node)); \ + void* c = AVLITHELEM(t, node); \ + if(t->elemcompare(c, r, t->arg) >= 0) { \ + return 2; \ + } \ + } \ + int lh = avl_##name##_height_of_subtree(t, _GET_AVLNODE_LEFT(node)); \ + int rh = avl_##name##_height_of_subtree(t, _GET_AVLNODE_RIGHT(node)); \ + if(rh - lh != _GET_AVLNODE_BALANCE(node)) { \ + return 4; \ + } \ + avlnode_t l = avl_##name##_check_subtree(t, _GET_AVLNODE_LEFT(node)); \ + avlnode_t r = avl_##name##_check_subtree(t, _GET_AVLNODE_RIGHT(node)); \ + return l | r; \ +} \ + \ +static inline int \ +avl_##name##_init(avl_##name *t, size_t elemsize, void *m, \ + avlnode_t nm, avlnode_t nt, \ + int (*compare)(const void *, const void *, void *), \ + int (*elemcompare)(const void *, const void *, void *), \ + void *arg) { \ + memset(t, 0, sizeof(*t)); \ + t->members = m; \ + t->max_size = t->size = t->nmember = nm; \ + t->ntotal = (nt==0) ? nm : nt; \ + t->compare = compare != NULL ? compare : elemcompare; \ + t->elemcompare = elemcompare != NULL ? elemcompare : compare; \ + t->arg = arg; \ + t->elemsize = elemsize; \ + t->garbage_head = t->root = AVLNIL; \ + \ + if (t->ntotal == 0 || t->members == NULL) { /* from scratch */ \ + if (t->ntotal == 0) { \ + t->members = NULL; \ + t->ntotal = 64; \ + } \ + \ + if (t->members == NULL) \ + t->members = realloc(NULL, elemsize * t->ntotal); \ + } \ + t->lrpointers = (avltree_node_pointers *) realloc(NULL, \ + sizeof(avltree_node_pointers) * t->ntotal); \ + \ + if (t->nmember == 1) { \ + t->root = 0; \ + _SET_AVLNODE_RIGHT(0, AVLNIL); \ + _SET_AVLNODE_LEFT(0, AVLNIL); \ + } else if (t->nmember > 1) { \ + qsort_arg(t->members, t->nmember, elemsize, t->elemcompare, t->arg); \ + /* create tree */ \ + t->root = avl_##name##_mktree(t, 1, 0, t->nmember, 0); \ + /*avl_##name##_check_subtree(t, t->root);*/ \ + } \ + if (t->members && t->lrpointers) \ + return 0; \ + else if (t->members) \ + return t->ntotal * sizeof(avltree_node_pointers); \ + else if (t->lrpointers) \ + return t->ntotal * elemsize; \ + else \ + return t->ntotal * (sizeof(avltree_node_pointers) + elemsize); \ +} \ + \ +static inline void \ +avl_##name##_destroy(avl_##name *t) { \ + if (t == NULL) return; \ + t->members = realloc(t->members, 0); \ + t->lrpointers = (avltree_node_pointers *)realloc(t->lrpointers, 0); \ +} \ + \ +/** Nodes in the garbage list have a loop on their right link. */ \ +static inline bool \ +avl_##name##_node_is_deleted(const avl_##name *t, avlnode_t node) { \ + \ + return _GET_AVLNODE_RIGHT(node) == node; \ +} \ + \ +static inline void* \ +avl_##name##_find(const avl_##name *t, void *k) { \ + avlnode_t node = t->root; \ + while(node != AVLNIL) { \ + int r = t->compare(k, AVLITHELEM(t, node), t->arg); \ + if (r > 0) { \ + node = _GET_AVLNODE_RIGHT(node); \ + } else if (r < 0) { \ + node = _GET_AVLNODE_LEFT(node); \ + } else { \ + return AVLITHELEM(t, node); \ + } \ + } \ + return NULL; \ +} \ + \ +static inline void* \ +avl_##name##_first(const avl_##name *t) { \ + avlnode_t node = t->root; \ + avlnode_t first = AVLNIL; \ + while (node != AVLNIL) { \ + first = node; \ + node = _GET_AVLNODE_LEFT(node); \ + } \ + if (first != AVLNIL) \ + return AVLITHELEM(t, first); \ + return NULL; \ +} \ + \ +static inline void* \ +avl_##name##_last(const avl_##name *t) { \ + avlnode_t node = t->root; \ + avlnode_t last = AVLNIL; \ + while (node != AVLNIL) { \ + last = node; \ + node = _GET_AVLNODE_RIGHT(node); \ + } \ + if (last != AVLNIL) \ + return AVLITHELEM(t, last); \ + return NULL; \ +} \ + \ +static inline void* \ +avl_##name##_random(const avl_##name *t, avlnode_t rnd) { \ + for (avlnode_t i = 0; i < t->size; i++, rnd++) { \ + rnd %= t->nmember; \ + if (!avl_##name##_node_is_deleted(t, rnd)) \ + return AVLITHELEM(t, rnd); \ + \ + } \ + \ + return NULL; \ +} \ +static inline avlnode_t \ +avl_##name##_size_of_subtree(avl_##name *t, avlnode_t node) { \ + if (node == AVLNIL) \ + return 0; \ + return 1 + \ + avl_##name##_size_of_subtree(t, _GET_AVLNODE_LEFT(node)) + \ + avl_##name##_size_of_subtree(t, _GET_AVLNODE_RIGHT(node)); \ +} \ + \ +static inline int \ +avl_##name##_reserve_places(avl_##name *t, avlnode_t nreserve) { \ + avlnode_t num_free = t->ntotal - t->size; \ + if (num_free >= nreserve) \ + return 0; \ + avlnode_t new_ntotal = MAX(t->ntotal * 2, t->ntotal + nreserve - num_free); \ + void *new_members = realloc(t->members, new_ntotal * t->elemsize); \ + if (!new_members) \ + return new_ntotal * t->elemsize; \ + t->members = new_members; \ + avltree_node_pointers *new_lrpointers = (avltree_node_pointers *) \ + realloc(t->lrpointers, new_ntotal * sizeof(avltree_node_pointers)); \ + if (!new_lrpointers) \ + return new_ntotal * sizeof(avltree_node_pointers); \ + t->lrpointers = new_lrpointers; \ + t->ntotal = new_ntotal; \ + return 0; \ +} \ + \ +static inline avlnode_t \ +avl_##name##_get_place(avl_##name *t) { \ + avlnode_t node; \ + if (t->garbage_head != AVLNIL) { \ + node = t->garbage_head; \ + t->garbage_head = _GET_AVLNODE_LEFT(t->garbage_head); \ + } else { \ + if (t->nmember >= t->ntotal) { \ + avlnode_t new_ntotal = t->ntotal * 2; \ + t->members = realloc(t->members, new_ntotal * t->elemsize); \ + t->lrpointers = (avltree_node_pointers *) realloc(t->lrpointers, \ + new_ntotal * sizeof(avltree_node_pointers)); \ + t->ntotal = new_ntotal; \ + } \ + \ + node = t->nmember; \ + t->nmember++; \ + } \ + _SET_AVLNODE_LEFT(node, AVLNIL); \ + _SET_AVLNODE_RIGHT(node, AVLNIL); \ + _SET_AVLNODE_BALANCE(node, 0); \ + return node; \ +} \ + \ +static inline bool \ +avl_##name##_rotate_left(avl_##name *t, avlnode_t parent, avlnode_t *new_parent) { \ + avlnode_t node = _GET_AVLNODE_RIGHT(parent); \ + if(_GET_AVLNODE_BALANCE(node) > 0) { \ + _SET_AVLNODE_BALANCE(parent, 0); \ + _SET_AVLNODE_BALANCE(node, 0); \ + _SET_AVLNODE_RIGHT(parent, _GET_AVLNODE_LEFT(node)); \ + _SET_AVLNODE_LEFT(node, parent); \ + *new_parent = node; \ + return true; \ + } else if(_GET_AVLNODE_BALANCE(node) == 0) { \ + _SET_AVLNODE_BALANCE(parent, 1); \ + _SET_AVLNODE_BALANCE(node, -1); \ + _SET_AVLNODE_RIGHT(parent, _GET_AVLNODE_LEFT(node)); \ + _SET_AVLNODE_LEFT(node, parent); \ + *new_parent = node; \ + return false; \ + } else { \ + avlnode_t l = _GET_AVLNODE_LEFT(node); \ + avlnode_t ll = _GET_AVLNODE_LEFT(l); \ + avlnode_t lr = _GET_AVLNODE_RIGHT(l); \ + int l_balance = _GET_AVLNODE_BALANCE(l); \ + _SET_AVLNODE_BALANCE(l, 0); \ + _SET_AVLNODE_BALANCE(node, l_balance < 0 ? 1 : 0); \ + _SET_AVLNODE_BALANCE(parent, l_balance > 0 ? -1 : 0); \ + _SET_AVLNODE_RIGHT(parent, ll); \ + _SET_AVLNODE_LEFT(node, lr); \ + _SET_AVLNODE_LEFT(l, parent); \ + _SET_AVLNODE_RIGHT(l, node); \ + *new_parent = l; \ + return true; \ + } \ +} \ + \ +static inline bool \ +avl_##name##_rotate_right(avl_##name *t, avlnode_t parent, avlnode_t *new_parent) { \ + avlnode_t node = _GET_AVLNODE_LEFT(parent); \ + if(_GET_AVLNODE_BALANCE(node) < 0) { \ + _SET_AVLNODE_BALANCE(parent, 0); \ + _SET_AVLNODE_BALANCE(node, 0); \ + _SET_AVLNODE_LEFT(parent, _GET_AVLNODE_RIGHT(node)); \ + _SET_AVLNODE_RIGHT(node, parent); \ + *new_parent = node; \ + return true; \ + } else if(_GET_AVLNODE_BALANCE(node) == 0) { \ + _SET_AVLNODE_BALANCE(parent, -1); \ + _SET_AVLNODE_BALANCE(node, 1); \ + _SET_AVLNODE_LEFT(parent, _GET_AVLNODE_RIGHT(node)); \ + _SET_AVLNODE_RIGHT(node, parent); \ + *new_parent = node; \ + return false; \ + } else { \ + avlnode_t r = _GET_AVLNODE_RIGHT(node); \ + avlnode_t rl = _GET_AVLNODE_LEFT(r); \ + avlnode_t rr = _GET_AVLNODE_RIGHT(r); \ + int r_balance = _GET_AVLNODE_BALANCE(r); \ + _SET_AVLNODE_BALANCE(r, 0); \ + _SET_AVLNODE_BALANCE(node, r_balance > 0 ? -1 : 0); \ + _SET_AVLNODE_BALANCE(parent, r_balance < 0 ? 1 : 0); \ + _SET_AVLNODE_LEFT(parent, rr); \ + _SET_AVLNODE_RIGHT(node, rl); \ + _SET_AVLNODE_RIGHT(r, parent); \ + _SET_AVLNODE_LEFT(r, node); \ + *new_parent = r; \ + return true; \ + } \ +} \ + \ +static inline int \ +avl_##name##_replace(avl_##name *t, void *v, void **p_old) { \ + avlnode_t node, depth = 0; \ + avlnode_t path[ AVLMAXDEPTH ]; \ + \ + if (t->root == AVLNIL) { \ + _SET_AVLNODE_LEFT(0, AVLNIL); \ + _SET_AVLNODE_RIGHT(0, AVLNIL); \ + _SET_AVLNODE_BALANCE(0, 0); \ + memcpy(t->members, v, t->elemsize); \ + t->root = 0; \ + t->garbage_head = AVLNIL; \ + t->nmember = 1; \ + t->size=1; \ + if (p_old) \ + *p_old = NULL; \ + return 0; \ + } else { \ + avlnode_t parent = t->root; \ + \ + for(;;) { \ + int r = t->elemcompare(v, AVLITHELEM(t, parent), t->arg); \ + if (r==0) { \ + if (p_old) \ + memcpy(*p_old, AVLITHELEM(t, parent), t->elemsize); \ + memcpy(AVLITHELEM(t, parent), v, t->elemsize); \ + return 0; \ + } \ + path[depth] = parent; \ + depth++; \ + if (r>0) { \ + if (_GET_AVLNODE_RIGHT(parent) == AVLNIL) { \ + int reserve_result = avl_##name##_reserve_places(t, 1); \ + if (reserve_result) \ + return reserve_result; \ + node = avl_##name##_get_place(t); \ + memcpy(AVLITHELEM(t, node), v, t->elemsize); \ + _SET_AVLNODE_RIGHT(parent, node); \ + break; \ + } else { \ + parent = _GET_AVLNODE_RIGHT(parent); \ + } \ + } else { \ + if (_GET_AVLNODE_LEFT(parent) == AVLNIL) { \ + int reserve_result = avl_##name##_reserve_places(t, 1); \ + if (reserve_result) \ + return reserve_result; \ + node = avl_##name##_get_place(t); \ + memcpy(AVLITHELEM(t, node), v, t->elemsize); \ + _SET_AVLNODE_LEFT(parent, node); \ + break; \ + } else { \ + parent = _GET_AVLNODE_LEFT(parent); \ + } \ + } \ + } \ + } \ + if (p_old) \ + *p_old = NULL; \ + \ + t->size++; \ + if ( t->size > t->max_size ) \ + t->max_size = t->size; \ + if ( depth > t->max_depth ) \ + t->max_depth = depth; \ + \ + path[depth] = node; \ + while(depth > 0) { \ + avlnode_t node = path[depth]; \ + avlnode_t parent = path[depth - 1]; \ + if(_GET_AVLNODE_RIGHT(parent) == node) { \ + if(_GET_AVLNODE_BALANCE(parent) < 0) { \ + _SET_AVLNODE_BALANCE(parent, 0); \ + break; \ + } else if(_GET_AVLNODE_BALANCE(parent) == 0) { \ + _SET_AVLNODE_BALANCE(parent, 1); \ + } else { \ + bool should_break = \ + avl_##name##_rotate_left(t, parent, path + depth - 1); \ + if(depth > 1) { \ + if(_GET_AVLNODE_LEFT(path[depth-2]) == parent) { \ + _SET_AVLNODE_LEFT(path[depth-2], path[depth-1]); \ + } else { \ + _SET_AVLNODE_RIGHT(path[depth-2], path[depth-1]); \ + } \ + } \ + if(should_break) { \ + break; \ + } \ + } \ + } else { \ + if(_GET_AVLNODE_BALANCE(parent) > 0) { \ + _SET_AVLNODE_BALANCE(parent, 0); \ + break; \ + } else if(_GET_AVLNODE_BALANCE(parent) == 0) { \ + _SET_AVLNODE_BALANCE(parent, -1); \ + } else { \ + bool should_break = \ + avl_##name##_rotate_right(t, parent, path + depth - 1); \ + if(depth > 1) { \ + if(_GET_AVLNODE_LEFT(path[depth-2]) == parent) { \ + _SET_AVLNODE_LEFT(path[depth-2], path[depth-1]); \ + } else { \ + _SET_AVLNODE_RIGHT(path[depth-2], path[depth-1]); \ + } \ + } \ + if(should_break) { \ + break; \ + } \ + } \ + } \ + depth--; \ + } \ + t->root = path[0]; \ + /*avl_##name##_check_subtree(t, t->root);*/ \ + return 0; \ +} \ + \ +static inline void \ +avl_##name##_delete(avl_##name *t, void *k) { \ + avlnode_t node = t->root; \ + avlnode_t parent = AVLNIL; \ + avlnode_t depth = 0; \ + avlnode_t path[AVLMAXDEPTH]; \ + int lr = 0; \ + while(node != AVLNIL) { \ + path[depth++] = node; \ + int r = t->elemcompare(k, AVLITHELEM(t, node), t->arg); \ + if (r > 0) { \ + parent = node; \ + node = _GET_AVLNODE_RIGHT(node); \ + lr = +1; \ + } else if (r < 0) { \ + parent = node; \ + node = _GET_AVLNODE_LEFT(node); \ + lr = -1; \ + } else {/* found */ \ + if (_GET_AVLNODE_LEFT(node) == AVLNIL && _GET_AVLNODE_RIGHT(node) == AVLNIL) {\ + path[depth-1] = AVLNIL; \ + if ( parent == AVLNIL ) \ + t->root = AVLNIL; \ + else if (lr <0) \ + _SET_AVLNODE_LEFT(parent, AVLNIL); \ + else \ + _SET_AVLNODE_RIGHT(parent, AVLNIL); \ + } else if (_GET_AVLNODE_LEFT(node) == AVLNIL) { \ + avlnode_t child = _GET_AVLNODE_RIGHT(node); \ + path[depth-1] = child; \ + if (parent == AVLNIL) t->root = child; \ + else if (lr <0) _SET_AVLNODE_LEFT(parent, child); \ + else _SET_AVLNODE_RIGHT(parent, child); \ + } else if (_GET_AVLNODE_RIGHT(node) == AVLNIL) { \ + avlnode_t child = _GET_AVLNODE_LEFT(node); \ + path[depth-1] = child; \ + if (parent == AVLNIL) t->root = child; \ + else if (lr <0) _SET_AVLNODE_LEFT(parent, child); \ + else _SET_AVLNODE_RIGHT(parent, child); \ + } else { \ + avlnode_t todel; \ + if (_GET_AVLNODE_BALANCE(node) >= 0) { \ + todel = _GET_AVLNODE_RIGHT(node); \ + path[depth++] = todel; \ + parent = AVLNIL; \ + lr = 1; \ + for(;;) { \ + if ( _GET_AVLNODE_LEFT(todel) != AVLNIL ) { \ + parent = todel; \ + todel = _GET_AVLNODE_LEFT(todel); \ + path[depth++] = todel; \ + lr = -1; \ + } else \ + break; \ + } \ + memcpy(AVLITHELEM(t, node), AVLITHELEM(t, todel), t->elemsize); \ + if (parent != AVLNIL) \ + _SET_AVLNODE_LEFT(parent, _GET_AVLNODE_RIGHT(todel)); \ + else \ + _SET_AVLNODE_RIGHT(node, _GET_AVLNODE_RIGHT(todel)); \ + } else { \ + todel = _GET_AVLNODE_LEFT(node); \ + path[depth++] = todel; \ + parent = AVLNIL; \ + lr = -1; \ + for(;;) { \ + if ( _GET_AVLNODE_RIGHT(todel) != AVLNIL ) { \ + parent = todel; \ + todel = _GET_AVLNODE_RIGHT(todel); \ + path[depth++] = todel; \ + lr = 1; \ + } else \ + break; \ + } \ + memcpy(AVLITHELEM(t, node), AVLITHELEM(t, todel), t->elemsize); \ + if (parent != AVLNIL) \ + _SET_AVLNODE_RIGHT(parent, _GET_AVLNODE_LEFT(todel)); \ + else \ + _SET_AVLNODE_LEFT(node, _GET_AVLNODE_LEFT(todel)); \ + } \ + node = todel; /* node to delete */ \ + } \ + \ + _SET_AVLNODE_LEFT(node, t->garbage_head); \ + /* \ + * Loop back on the right link indicates that the node \ + * is in the garbage list. \ + */ \ + _SET_AVLNODE_RIGHT(node, node); \ + t->garbage_head = node; \ + \ + break; \ + } \ + } \ + \ + if (node == AVLNIL) /* not found */ \ + return; \ + \ + t->size --; \ + \ + depth--; \ + while(depth > 0) { \ + avlnode_t node = path[depth]; \ + avlnode_t parent = path[depth - 1]; \ + if(lr == 1 || (lr == 0 && _GET_AVLNODE_RIGHT(parent) == node)) { \ + if(_GET_AVLNODE_BALANCE(parent) == 0) { \ + _SET_AVLNODE_BALANCE(parent, -1); \ + break; \ + } else if(_GET_AVLNODE_BALANCE(parent) == 1) { \ + _SET_AVLNODE_BALANCE(parent, 0); \ + } else { \ + bool should_break = \ + !avl_##name##_rotate_right(t, parent, path + depth - 1); \ + if(depth > 1) { \ + if(_GET_AVLNODE_LEFT(path[depth-2]) == parent) { \ + _SET_AVLNODE_LEFT(path[depth-2], path[depth-1]); \ + } else { \ + _SET_AVLNODE_RIGHT(path[depth-2], path[depth-1]); \ + } \ + } \ + if(should_break) { \ + break; \ + } \ + } \ + } else { \ + if(_GET_AVLNODE_BALANCE(parent) == 0) { \ + _SET_AVLNODE_BALANCE(parent, 1); \ + break; \ + } else if(_GET_AVLNODE_BALANCE(parent) == -1) { \ + _SET_AVLNODE_BALANCE(parent, 0); \ + } else { \ + bool should_break = \ + !avl_##name##_rotate_left(t, parent, path + depth - 1); \ + if(depth > 1) { \ + if(_GET_AVLNODE_LEFT(path[depth-2]) == parent) { \ + _SET_AVLNODE_LEFT(path[depth-2], path[depth-1]); \ + } else { \ + _SET_AVLNODE_RIGHT(path[depth-2], path[depth-1]); \ + } \ + } \ + if(should_break) { \ + break; \ + } \ + } \ + } \ + lr = 0; \ + depth--; \ + } \ + t->root = path[0]; \ + /*avl_##name##_check_subtree(t, t->root);*/ \ + \ +} \ + \ +static inline avlnode_t \ +avl_##name##_walk(avl_##name *t, void* array, avlnode_t limit, avlnode_t offset) { \ + int level = 0; \ + avlnode_t count= 0, \ + node, \ + stack[ t->max_depth + 1 ]; \ + \ + if (t->root == AVLNIL) return 0; \ + stack[0] = t->root; \ + \ + while( (node = _GET_AVLNODE_LEFT( stack[level] )) != AVLNIL ) { \ + level++; \ + stack[level] = node; \ + } \ + \ + while( count < offset + limit && level >= 0 ) { \ + \ + if (count >= offset) \ + memcpy((char *) array + (count-offset) * t->elemsize, \ + AVLITHELEM(t, stack[level]), t->elemsize); \ + count++; \ + \ + node = _GET_AVLNODE_RIGHT( stack[level] ); \ + level--; \ + while( node != AVLNIL ) { \ + level++; \ + stack[level] = node; \ + node = _GET_AVLNODE_LEFT( stack[level] ); \ + } \ + } \ + \ + return (count > offset) ? count - offset : 0; \ +} \ + \ +static inline void \ +avl_##name##_walk_cb(avl_##name *t, int (*cb)(void*, void*), void *cb_arg ) { \ + int level = 0; \ + avlnode_t node, \ + stack[ t->max_depth + 1 ]; \ + \ + if (t->root == AVLNIL) return; \ + stack[0] = t->root; \ + \ + while( (node = _GET_AVLNODE_LEFT( stack[level] )) != AVLNIL ) { \ + level++; \ + stack[level] = node; \ + } \ + \ + while( level >= 0 ) { \ + if ( cb(cb_arg, AVLITHELEM(t, stack[level])) == 0 ) \ + return; \ + \ + node = _GET_AVLNODE_RIGHT( stack[level] ); \ + level--; \ + while( node != AVLNIL ) { \ + level++; \ + stack[level] = node; \ + node = _GET_AVLNODE_LEFT( stack[level] ); \ + } \ + } \ +} \ + \ +typedef struct avl_##name##_iterator { \ + const avl_##name *t; \ + int level; \ + int max_depth; \ + avlnode_t stack[0]; \ +} avl_##name##_iterator; \ + \ +static inline avl_##name##_iterator * \ +avl_##name##_iterator_alloc(avl_##name *t) { \ + avl_##name##_iterator *i = (avl_##name##_iterator *) \ + realloc(NULL, sizeof(*i) + sizeof(avlnode_t) * (t->max_depth + 1)); \ + if (i) { \ + i->t = t; \ + i->level = 0; \ + i->stack[0] = t->root; \ + } \ + return i; \ +} \ + \ +static inline avl_##name##_iterator * \ +avl_##name##_iterator_init(avl_##name *t) { \ + avl_##name##_iterator *i; \ + avlnode_t node; \ + \ + if (t->root == AVLNIL) return NULL; \ + i = avl_##name##_iterator_alloc(t); \ + if (!i) \ + return i; \ + \ + while( (node = _GET_AVLNODE_LEFT( i->stack[i->level] )) != AVLNIL ) { \ + i->level++; \ + i->stack[i->level] = node; \ + } \ + \ + return i; \ +} \ + \ +static inline int \ +avl_##name##_iterator_init_set(const avl_##name *t, avl_##name##_iterator **i, \ + void *k) { \ + avlnode_t node; \ + int lastLevelEq = -1, cmp; \ + \ + if ((*i) == NULL || t->max_depth > (*i)->max_depth) { \ + avl_##name##_iterator *new_i; \ + new_i = (avl_##name##_iterator *) realloc(*i, sizeof(**i) + \ + sizeof(avlnode_t) * (t->max_depth + 31)); \ + if (!new_i) \ + return sizeof(**i) + sizeof(avlnode_t) * (t->max_depth + 31); \ + *i = new_i; \ + } \ + \ + (*i)->t = t; \ + (*i)->level = -1; \ + if (t->root == AVLNIL) { \ + (*i)->max_depth = 0; /* valgrind points out it's used in the check above ^.*/ \ + return 0; \ + } \ + \ + (*i)->max_depth = t->max_depth; \ + (*i)->stack[0] = t->root; \ + \ + node = t->root; \ + while(node != AVLNIL) { \ + cmp = t->compare(k, AVLITHELEM(t, node), t->arg); \ + \ + (*i)->level++; \ + (*i)->stack[(*i)->level] = node; \ + \ + if (cmp > 0) { \ + (*i)->level--; /* exclude current node from path, ie "mark as visited" */ \ + node = _GET_AVLNODE_RIGHT(node); \ + } else if (cmp < 0) { \ + node = _GET_AVLNODE_LEFT(node); \ + } else { \ + lastLevelEq = (*i)->level; \ + node = _GET_AVLNODE_LEFT(node); /* one way iterator: from left to right */ \ + } \ + } \ + \ + if (lastLevelEq >= 0) \ + (*i)->level = lastLevelEq; \ + return 0; \ +} \ + \ +static inline avl_##name##_iterator * \ +avl_##name##_iterator_reverse_init(avl_##name *t) { \ + avl_##name##_iterator *i; \ + avlnode_t node; \ + \ + if (t->root == AVLNIL) return NULL; \ + i = avl_##name##_iterator_alloc(t); \ + if (!i) \ + return i; \ + \ + while( (node = _GET_AVLNODE_RIGHT( i->stack[i->level] )) != AVLNIL ) { \ + i->level++; \ + i->stack[i->level] = node; \ + } \ + \ + return i; \ +} \ + \ +static inline int \ +avl_##name##_iterator_reverse_init_set(const avl_##name *t, \ + avl_##name##_iterator **i, void *k) { \ + avlnode_t node; \ + int lastLevelEq = -1, cmp; \ + \ + if ((*i) == NULL || t->max_depth > (*i)->max_depth) { \ + avl_##name##_iterator *new_i; \ + new_i = (avl_##name##_iterator *) realloc(*i, sizeof(**i) + \ + sizeof(avlnode_t) * (t->max_depth + 31)); \ + if (!new_i) \ + return sizeof(**i) + sizeof(avlnode_t) * (t->max_depth + 31); \ + *i = new_i; \ + } \ + \ + (*i)->t = t; \ + (*i)->level = -1; \ + if (t->root == AVLNIL) { \ + (*i)->max_depth = 0; \ + return 0; \ + } \ + \ + (*i)->max_depth = t->max_depth; \ + (*i)->stack[0] = t->root; \ + \ + node = t->root; \ + while(node != AVLNIL) { \ + cmp = t->compare(k, AVLITHELEM(t, node), t->arg); \ + \ + (*i)->level++; \ + (*i)->stack[(*i)->level] = node; \ + \ + if (cmp < 0) { \ + (*i)->level--; \ + node = _GET_AVLNODE_LEFT(node); \ + } else if (cmp > 0) { \ + node = _GET_AVLNODE_RIGHT(node); \ + } else { \ + lastLevelEq = (*i)->level; \ + node = _GET_AVLNODE_RIGHT(node); \ + } \ + } \ + \ + if (lastLevelEq >= 0) \ + (*i)->level = lastLevelEq; \ + return 0; \ +} \ + \ +static inline void \ +avl_##name##_iterator_free(avl_##name##_iterator *i) { \ + if (i == NULL) return; \ + i = (avl_##name##_iterator *)realloc(i, 0); \ +} \ + \ +/** \ + * Get the last node on the iterator stack, check \ + * if the node is not deleted. \ + */ \ +static inline avlnode_t \ +avl_##name##_iterator_next_node(avl_##name##_iterator *i) { \ + \ + while (i->level >= 0) { \ + avlnode_t return_node = i->stack[i->level--]; \ + if (! avl_##name##_node_is_deleted(i->t, return_node)) \ + return return_node; \ + } \ + return AVLNIL; \ +} \ + \ +static inline void* \ +avl_##name##_iterator_next(avl_##name##_iterator *i) { \ + \ + if (i == NULL) return NULL; \ + \ + const avl_##name *t = i->t; \ + avlnode_t returnNode = avl_##name##_iterator_next_node(i); \ + \ + if (returnNode == AVLNIL) return NULL; \ + \ + avlnode_t node = _GET_AVLNODE_RIGHT(returnNode); \ + while (node != AVLNIL) { \ + i->level++; \ + i->stack[i->level] = node; \ + node = _GET_AVLNODE_LEFT(i->stack[i->level]); \ + } \ + \ + return AVLITHELEM(t, returnNode); \ +} \ + \ +static inline void* \ +avl_##name##_iterator_reverse_next(avl_##name##_iterator *i) { \ + \ + if (i == NULL) return NULL; \ + \ + const avl_##name *t = i->t; \ + avlnode_t returnNode = avl_##name##_iterator_next_node(i); \ + \ + if (returnNode == AVLNIL) return NULL; \ + \ + avlnode_t node = _GET_AVLNODE_LEFT(returnNode); \ + while (node != AVLNIL) { \ + i->level++; \ + i->stack[i->level] = node; \ + node = _GET_AVLNODE_RIGHT(i->stack[i->level]); \ + } \ + return AVLITHELEM(t, returnNode); \ +} +/* + * vim: ts=4 sts=4 et + */ +#endif + +#if defined(__cplusplus) +} +#endif /* defined(__cplusplus) */ diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt index 3df5f3144828eb5c131872b6036b960112d847e3..839df7bbdb8a67d22a483b374410ed0b20939b62 100644 --- a/src/box/CMakeLists.txt +++ b/src/box/CMakeLists.txt @@ -23,6 +23,7 @@ tarantool_module("box" index.cc hash_index.cc tree_index.cc + avl_tree_index.cc bitset_index.cc space.cc port.cc diff --git a/src/box/avl_tree_index.cc b/src/box/avl_tree_index.cc new file mode 100644 index 0000000000000000000000000000000000000000..f00965844884046436144663ab346a66633c7050 --- /dev/null +++ b/src/box/avl_tree_index.cc @@ -0,0 +1,494 @@ +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "avl_tree_index.h" +#include "tuple.h" +#include "space.h" +#include "exception.h" +#include "errinj.h" + +/* {{{ Utilities. *************************************************/ + +struct avl_index_node { + struct tuple *tuple; +}; + +struct avl_index_key_data +{ + const char *key; + uint32_t part_count; +}; + +static inline struct tuple * +avl_index_unfold(const void *node) +{ + if (node == NULL) + return NULL; + + struct avl_index_node *node_x = (struct avl_index_node *) node; + assert (node_x->tuple != NULL); + return node_x->tuple; +} + +static inline void +avl_index_fold(void *node, struct tuple *tuple) +{ + assert (node != NULL); + assert (tuple != NULL); + + struct avl_index_node *node_x = (struct avl_index_node *) node; + node_x->tuple = tuple; +} + +static int +avl_index_node_compare(const void *node_a, const void *node_b, void *arg) +{ + AvlTreeIndex *self = (AvlTreeIndex *) arg; + struct tuple *tuple_a = avl_index_unfold(node_a); + struct tuple *tuple_b = avl_index_unfold(node_b); + + return tuple_compare(tuple_a, tuple_b, self->key_def); +} + +static int +avl_index_node_compare_dup(const void *node_a, const void *node_b, void *arg) +{ + AvlTreeIndex *self = (AvlTreeIndex *) arg; + struct tuple *tuple_a = avl_index_unfold(node_a); + struct tuple *tuple_b = avl_index_unfold(node_b); + + return tuple_compare_dup(tuple_a, tuple_b, self->key_def); +} + +static int +avl_index_node_compare_with_key(const void *key, const void *node, void *arg) +{ + AvlTreeIndex *self = (AvlTreeIndex *) arg; + struct avl_index_key_data *key_data = + (struct avl_index_key_data *) key; + struct tuple *tuple = avl_index_unfold(node); + + /* the result is inverted because arguments are swapped */ + return -tuple_compare_with_key(tuple, key_data->key, + key_data->part_count, self->key_def); +} + +#ifndef NDEBUG +void * +realloc_avl_inject(void *ptr, size_t size) +{ + if (size) + ERROR_INJECT(ERRINJ_TREE_ALLOC, return 0); + return realloc(ptr, size); +} +#endif + +/* {{{ AvlTreeIndex Iterators ****************************************/ + +struct tree_iterator { + struct iterator base; + const AvlTreeIndex *index; + struct avl_index_iterator *iter; + struct avl_index_key_data key_data; +}; + +static void +tree_iterator_free(struct iterator *iterator); + +static inline struct tree_iterator * +tree_iterator(struct iterator *it) +{ + assert(it->free == tree_iterator_free); + return (struct tree_iterator *) it; +} + +static void +tree_iterator_free(struct iterator *iterator) +{ + struct tree_iterator *it = tree_iterator(iterator); + if (it->iter) + avl_index_iterator_free(it->iter); + free(it); +} + +static struct tuple * +tree_iterator_ge(struct iterator *iterator) +{ + struct tree_iterator *it = tree_iterator(iterator); + void *node = avl_index_iterator_next(it->iter); + return avl_index_unfold(node); +} + +static struct tuple * +tree_iterator_le(struct iterator *iterator) +{ + struct tree_iterator *it = tree_iterator(iterator); + void *node = avl_index_iterator_reverse_next(it->iter); + return avl_index_unfold(node); +} + +static struct tuple * +tree_iterator_eq(struct iterator *iterator) +{ + struct tree_iterator *it = tree_iterator(iterator); + + void *node = avl_index_iterator_next(it->iter); + if (node && it->index->tree.compare(&it->key_data, node, + (void *) it->index) == 0) + return avl_index_unfold(node); + + return NULL; +} + +static struct tuple * +tree_iterator_req(struct iterator *iterator) +{ + struct tree_iterator *it = tree_iterator(iterator); + + void *node = avl_index_iterator_reverse_next(it->iter); + if (node != NULL + && it->index->tree.compare(&it->key_data, node, + (void *) it->index) == 0) { + return avl_index_unfold(node); + } + + return NULL; +} + +static struct tuple * +tree_iterator_lt(struct iterator *iterator) +{ + struct tree_iterator *it = tree_iterator(iterator); + + void *node ; + while ((node = avl_index_iterator_reverse_next(it->iter)) != NULL) { + if (it->index->tree.compare(&it->key_data, node, + (void *) it->index) != 0) { + it->base.next = tree_iterator_le; + return avl_index_unfold(node); + } + } + + return NULL; +} + +static struct tuple * +tree_iterator_gt(struct iterator *iterator) +{ + struct tree_iterator *it = tree_iterator(iterator); + + void *node; + while ((node = avl_index_iterator_next(it->iter)) != NULL) { + if (it->index->tree.compare(&it->key_data, node, + (void *) it->index) != 0) { + it->base.next = tree_iterator_ge; + return avl_index_unfold(node); + } + } + + return NULL; +} + +/* }}} */ + +/* {{{ AvlTreeIndex **********************************************************/ + +AvlTreeIndex::AvlTreeIndex(struct key_def *key_def, struct space *space) + : Index(key_def, space) +{ + memset(&tree, 0, sizeof tree); +} + +AvlTreeIndex::~AvlTreeIndex() +{ + avl_index_destroy(&tree); +} + +size_t +AvlTreeIndex::size() const +{ + return tree.size; +} + +size_t +AvlTreeIndex::memsize() const +{ + return tree.size * (8 + sizeof(struct avl_index_node)); +} + +struct tuple * +AvlTreeIndex::min() const +{ + void *node = avl_index_first(&tree); + return avl_index_unfold(node); +} + +struct tuple * +AvlTreeIndex::max() const +{ + void *node = avl_index_last(&tree); + return avl_index_unfold(node); +} + +struct tuple * +AvlTreeIndex::random(uint32_t rnd) const +{ + void *node = avl_index_random(&tree, rnd); + return avl_index_unfold(node); +} + +struct tuple * +AvlTreeIndex::findByKey(const char *key, uint32_t part_count) const +{ + assert(key_def->is_unique && part_count == key_def->part_count); + + struct avl_index_key_data key_data; + key_data.key = key; + key_data.part_count = part_count; + void *node = avl_index_find(&tree, &key_data); + return avl_index_unfold(node); +} + +struct tuple * +AvlTreeIndex::replace(struct tuple *old_tuple, struct tuple *new_tuple, + enum dup_replace_mode mode) +{ + struct avl_index_node new_node; + struct avl_index_node old_node; + uint32_t errcode; + + if (new_tuple) { + struct avl_index_node *p_dup_node = &old_node; + avl_index_fold(&new_node, new_tuple); + + /* Try to optimistically replace the new_tuple. */ + int tree_res = avl_index_replace(&tree, &new_node, + (void **) &p_dup_node); + + if (tree_res) { + tnt_raise(ClientError, ER_MEMORY_ISSUE, tree_res, + "AvlTreeIndex", "replace"); + } + + struct tuple *dup_tuple = avl_index_unfold(p_dup_node); + errcode = replace_check_dup(old_tuple, dup_tuple, mode); + + if (errcode) { + avl_index_delete(&tree, &new_node); + if (p_dup_node != NULL) + avl_index_replace(&tree, p_dup_node, NULL); + tnt_raise(ClientError, errcode, index_n(this)); + } + if (dup_tuple) + return dup_tuple; + } + if (old_tuple) { + avl_index_fold(&old_node, old_tuple); + avl_index_delete(&tree, &old_node); + } + return old_tuple; +} + +struct iterator * +AvlTreeIndex::allocIterator() const +{ + struct tree_iterator *it = (struct tree_iterator *) + calloc(1, sizeof(*it)); + if (it == NULL) { + tnt_raise(ClientError, ER_MEMORY_ISSUE, + sizeof(struct tree_iterator), + "AvlTreeIndex", "iterator"); + } + + it->index = this; + it->base.free = tree_iterator_free; + return (struct iterator *) it; +} + +void +AvlTreeIndex::initIterator(struct iterator *iterator, enum iterator_type type, + const char *key, uint32_t part_count) const +{ + assert (key != NULL || part_count == 0); + struct tree_iterator *it = tree_iterator(iterator); + + if (part_count == 0) { + /* + * If no key is specified, downgrade equality + * iterators to a full range. + */ + type = iterator_type_is_reverse(type) ? ITER_LE : ITER_GE; + key = NULL; + } + it->key_data.key = key; + it->key_data.part_count = part_count; + + if (iterator_type_is_reverse(type)) { + int r = avl_index_iterator_reverse_init_set(&tree, + &it->iter, &it->key_data); + if (r) + tnt_raise(ClientError, ER_MEMORY_ISSUE, + r, "AvlTreeIndex", "init iterator"); + } else { + int r = avl_index_iterator_init_set(&tree, + &it->iter, &it->key_data); + if (r) + tnt_raise(ClientError, ER_MEMORY_ISSUE, + r, "AvlTreeIndex", "init iterator"); + } + + switch (type) { + case ITER_EQ: + it->base.next = tree_iterator_eq; + break; + case ITER_REQ: + it->base.next = tree_iterator_req; + break; + case ITER_ALL: + case ITER_GE: + it->base.next = tree_iterator_ge; + break; + case ITER_GT: + it->base.next = tree_iterator_gt; + break; + case ITER_LE: + it->base.next = tree_iterator_le; + break; + case ITER_LT: + it->base.next = tree_iterator_lt; + break; + default: + tnt_raise(ClientError, ER_UNSUPPORTED, + "Tree index", "requested iterator type"); + } +} + +void +AvlTreeIndex::beginBuild() +{ + assert(index_is_primary(this)); + + tree.size = 0; + tree.max_size = 64; + + size_t sz = tree.max_size * sizeof(struct avl_index_node); + tree.members = malloc(sz); + if (tree.members == NULL) { + panic("malloc(): failed to allocate %" PRI_SZ " bytes", sz); + } +} + +void +AvlTreeIndex::buildNext(struct tuple *tuple) +{ + if (tree.size == tree.max_size) { + tree.max_size = MAX(tree.max_size * 2, 64); + + size_t sz = tree.max_size * sizeof(struct avl_index_node); + tree.members = realloc(tree.members, sz); + if (tree.members == NULL) { + panic("malloc(): failed to allocate %" PRI_SZ " bytes", + sz); + } + } + + struct avl_index_node *node = (struct avl_index_node *) + tree.members + tree.size; + avl_index_fold(node, tuple); + tree.size++; +} + +void +AvlTreeIndex::endBuild() +{ + assert(index_is_primary(this)); + + uint32_t n_tuples = tree.size; + uint32_t estimated_tuples = tree.max_size; + void *nodes = tree.members; + + int tree_res = + avl_index_init(&tree, sizeof(struct avl_index_node), + nodes, n_tuples, estimated_tuples, + avl_index_node_compare_with_key, + avl_index_node_compare, + this); + if (tree_res) { + panic("tree_init: failed to allocate %d bytes", tree_res); + } +} + +void +AvlTreeIndex::build(Index *pk) +{ + uint32_t n_tuples = pk->size(); + uint32_t estimated_tuples = n_tuples * 1.2; + + void *nodes = NULL; + if (n_tuples) { + /* + * Allocate a little extra to avoid + * unnecessary realloc() when more data is + * inserted. + */ + size_t sz = estimated_tuples * sizeof(struct avl_index_node); + nodes = malloc(sz); + if (nodes == NULL) { + panic("malloc(): failed to allocate %" PRI_SZ " bytes", + sz); + } + } + + struct iterator *it = pk->position(); + pk->initIterator(it, ITER_ALL, NULL, 0); + + struct tuple *tuple; + + for (uint32_t i = 0; (tuple = it->next(it)) != NULL; ++i) { + struct avl_index_node *node = (struct avl_index_node *) + nodes + i; + avl_index_fold(node, tuple); + } + + if (n_tuples) { + say_info("Sorting %" PRIu32 " keys in index %" PRIu32 "...", n_tuples, + index_n(this)); + } + + /* If n_tuples == 0 then estimated_tuples = 0, elem == NULL, tree is empty */ + int tree_res = + avl_index_init(&tree, sizeof(struct avl_index_node), + nodes, n_tuples, estimated_tuples, + avl_index_node_compare_with_key, + key_def->is_unique ? avl_index_node_compare + : avl_index_node_compare_dup, + this); + if (tree_res) { + panic("tree_init: failed to allocate %d bytes", tree_res); + } +} diff --git a/src/box/avl_tree_index.h b/src/box/avl_tree_index.h new file mode 100644 index 0000000000000000000000000000000000000000..c32d11174b45a0f548e9f8ae773b679f43f5d8d2 --- /dev/null +++ b/src/box/avl_tree_index.h @@ -0,0 +1,75 @@ +#ifndef TARANTOOL_BOX_AVL_TREE_INDEX_H_INCLUDED +#define TARANTOOL_BOX_AVL_TREE_INDEX_H_INCLUDED +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "index.h" + +#include <avl_tree.h> + +/** + * Instantiate sptree definitions + */ +#ifdef NDEBUG +AVL_DEF(index, realloc); +#else +void * +realloc_avl_inject(void *ptr, size_t size); +AVL_DEF(index, realloc_avl_inject); +#endif + +class AvlTreeIndex: public Index { +public: + AvlTreeIndex(struct key_def *key_def, struct space *space); + virtual ~AvlTreeIndex(); + + virtual void beginBuild(); + virtual void buildNext(struct tuple *tuple); + virtual void endBuild(); + virtual void build(Index *pk); + virtual size_t size() const; + virtual struct tuple *min() const; + virtual struct tuple *max() const; + virtual struct tuple *random(uint32_t rnd) const; + virtual struct tuple *findByKey(const char *key, uint32_t part_count) const; + virtual struct tuple *replace(struct tuple *old_tuple, + struct tuple *new_tuple, + enum dup_replace_mode mode); + + virtual size_t memsize() const; + virtual struct iterator *allocIterator() const; + virtual void initIterator(struct iterator *iterator, + enum iterator_type type, + const char *key, uint32_t part_count) const; + +// protected: + avl_index tree; +}; + +#endif /* TARANTOOL_BOX_AVL_TREE_INDEX_H_INCLUDED */ diff --git a/src/box/index.cc b/src/box/index.cc index 323e200cd9334cb9ad86f4be18a54abcde2f5153..af6518703bc5754987c8064903d1131e47677781 100644 --- a/src/box/index.cc +++ b/src/box/index.cc @@ -29,6 +29,7 @@ #include "index.h" #include "hash_index.h" #include "tree_index.h" +#include "avl_tree_index.h" #include "bitset_index.h" #include "tuple.h" #include "say.h" @@ -71,7 +72,8 @@ key_validate(struct key_def *key_def, enum iterator_type type, const char *key, * - ITERA_ALL iterator type, all index types * - ITER_GE iterator in HASH index (legacy) */ - if (key_def->type == TREE || type == ITER_ALL || + if (key_def->type == TREE || key_def->type == AVLTREE || + type == ITER_ALL || (key_def->type == HASH && type == ITER_GE)) return; /* Fall through. */ @@ -82,7 +84,8 @@ key_validate(struct key_def *key_def, enum iterator_type type, const char *key, key_def->part_count, part_count); /* Partial keys are allowed only for TREE index type. */ - if (key_def->type != TREE && part_count < key_def->part_count) { + if (key_def->type != TREE && key_def->type != AVLTREE && + part_count < key_def->part_count) { tnt_raise(ClientError, ER_EXACT_MATCH, key_def->part_count, part_count); } @@ -144,6 +147,8 @@ Index::factory(enum index_type type, struct key_def *key_def, struct space *spac return new HashIndex(key_def, space); case TREE: return new TreeIndex(key_def, space); + case AVLTREE: + return new AvlTreeIndex(key_def, space); case BITSET: return new BitsetIndex(key_def, space); default: diff --git a/src/box/key_def.h b/src/box/key_def.h index 8cc234082736c727c7afdc99b55e3fb07703162d..75ef8c1a93bfe07168378941511835d65403ce1d 100644 --- a/src/box/key_def.h +++ b/src/box/key_def.h @@ -47,9 +47,10 @@ field_type_maxlen(enum field_type type) } #define INDEX_TYPE(_) \ - _(HASH, 0) /* HASH Index */ \ - _(TREE, 1) /* TREE Index */ \ - _(BITSET, 2) /* BITSET Index */ \ + _(HASH, 0) /* HASH Index */ \ + _(TREE, 1) /* TREE Index */ \ + _(AVLTREE, 2) /* AVL TREE Index */ \ + _(BITSET, 3) /* BITSET Index */ \ ENUM(index_type, INDEX_TYPE); extern const char *index_type_strs[]; diff --git a/src/box/space.cc b/src/box/space.cc index bbf8a5a7bb756ebddb79ab92b749524d8082221c..08618d6dd79c056ed61e1a99ffd64314b36267dd 100644 --- a/src/box/space.cc +++ b/src/box/space.cc @@ -494,6 +494,9 @@ check_spaces(struct tarantool_cfg *conf) case TREE: /* extra check for tree index not needed */ break; + case AVLTREE: + /* extra check for tree index not needed */ + break; case BITSET: /* check bitset index */ /* bitset index must has single-field key */ diff --git a/test/big/tarantool.cfg b/test/big/tarantool.cfg index 7726457bbc6fb376677d69f36dcd30dbea636a26..64c3d7ec910babecffa34626729e2655066bd0b8 100644 --- a/test/big/tarantool.cfg +++ b/test/big/tarantool.cfg @@ -402,3 +402,44 @@ space[28].index[0].type = TREE space[28].index[0].unique = true space[28].index[0].key_field[0].fieldno = 0 space[28].index[0].key_field[0].type = NUM + +# Tests of AVL tree +space[32].enabled = 1 +space[32].index[0].type = "AVLTREE" +space[32].index[0].unique = 1 +space[32].index[0].key_field[0].fieldno = 0 +space[32].index[0].key_field[0].type = "NUM" + +space[33].enabled = 1 +space[33].index[0].type = "AVLTREE" +space[33].index[0].unique = 1 +space[33].index[0].key_field[0].fieldno = 0 +space[33].index[0].key_field[0].type = "STR" + +space[34].enabled = true +space[34].index[0].type = "AVLTREE" +space[34].index[0].unique = true +space[34].index[0].key_field[0].fieldno = 0 +space[34].index[0].key_field[0].type = "STR" + +space[35].enabled = true +space[35].index[0].type = "AVLTREE" +space[35].index[0].unique = true +space[35].index[0].key_field[0].fieldno = 0 +space[35].index[0].key_field[0].type = "NUM" + +space[35].index[1].type = "AVLTREE" +space[35].index[1].unique = true +space[35].index[1].key_field[0].fieldno = 1 +space[35].index[1].key_field[0].type = "NUM" + +space[35].index[2].type = "AVLTREE" +space[35].index[2].unique = false +space[35].index[2].key_field[0].fieldno = 2 +space[35].index[2].key_field[0].type = "NUM" + +space[35].index[3].type = "AVLTREE" +space[35].index[3].unique = true +space[35].index[3].key_field[0].fieldno = 3 +space[35].index[3].key_field[0].type = "NUM" + diff --git a/test/big/tree_pk_avl.result b/test/big/tree_pk_avl.result new file mode 100644 index 0000000000000000000000000000000000000000..33e6c5c9325a813ba1119a5e2d4b8b80c65e7dc5 --- /dev/null +++ b/test/big/tree_pk_avl.result @@ -0,0 +1,283 @@ +insert into t32 values (1, 'tuple') +Insert OK, 1 row affected +save snapshot +--- +ok +... +insert into t32 values (2, 'tuple 2') +Insert OK, 1 row affected +save snapshot +--- +ok +... +insert into t32 values (3, 'tuple 3') +Insert OK, 1 row affected +select * from t32 where k0 = 1 +Found 1 tuple: +[1, 'tuple'] +select * from t32 where k0 = 2 +Found 1 tuple: +[2, 'tuple 2'] +select * from t32 where k0 = 3 +Found 1 tuple: +[3, 'tuple 3'] +delete from t32 where k0 = 1 +Delete OK, 1 row affected +delete from t32 where k0 = 2 +Delete OK, 1 row affected +delete from t32 where k0 = 3 +Delete OK, 1 row affected +insert into t32 VALUES('xxxxxxx') +An error occurred: ER_KEY_FIELD_TYPE, 'Supplied key field type does not match index type: expected NUM' +insert into t32 VALUES('') +An error occurred: ER_KEY_FIELD_TYPE, 'Supplied key field type does not match index type: expected NUM' +insert into t32 VALUES('12') +An error occurred: ER_KEY_FIELD_TYPE, 'Supplied key field type does not match index type: expected NUM' +insert into t33 values ('identifier', 'tuple') +Insert OK, 1 row affected +save snapshot +--- +ok +... +insert into t33 values ('second', 'tuple 2') +Insert OK, 1 row affected +save snapshot +--- +ok +... +call box.select_range('33', '0', '100', 'second') +Found 1 tuple: +['second', 'tuple 2'] +call box.select_range('33', '0', '100', 'identifier') +Found 2 tuples: +['identifier', 'tuple'] +['second', 'tuple 2'] +insert into t33 values ('third', 'tuple 3') +Insert OK, 1 row affected +select * from t33 where k0 = 'identifier' +Found 1 tuple: +['identifier', 'tuple'] +select * from t33 where k0 = 'second' +Found 1 tuple: +['second', 'tuple 2'] +select * from t33 where k0 = 'third' +Found 1 tuple: +['third', 'tuple 3'] +delete from t33 where k0 = 'identifier' +Delete OK, 1 row affected +delete from t33 where k0 = 'second' +Delete OK, 1 row affected +delete from t33 where k0 = 'third' +Delete OK, 1 row affected +insert into t32 values (1, 'tuple') +Insert OK, 1 row affected +insert into t33 values (1, 'tuple') +Insert OK, 1 row affected +insert into t33 values (2, 'tuple') +Insert OK, 1 row affected +lua function box.crossjoin(space0, space1, limit) space0 = tonumber(space0) space1 = tonumber(space1) limit = tonumber(limit) local result = {} for k0, v0 in box.space[space0]:pairs() do for k1, v1 in box.space[space1]:pairs() do if limit <= 0 then return unpack(result) end newtuple = {v0:unpack()} for _, v in v1:pairs() do table.insert(newtuple, v) end table.insert(result, newtuple) limit = limit - 1 end end return unpack(result) end +--- +... +call box.crossjoin('33', '33', '0') +No match +call box.crossjoin('33', '33', '5') +Found 4 tuples: +[1, 'tuple', 1, 'tuple'] +[1, 'tuple', 2, 'tuple'] +[2, 'tuple', 1, 'tuple'] +[2, 'tuple', 2, 'tuple'] +call box.crossjoin('33', '33', '10000') +Found 4 tuples: +[1, 'tuple', 1, 'tuple'] +[1, 'tuple', 2, 'tuple'] +[2, 'tuple', 1, 'tuple'] +[2, 'tuple', 2, 'tuple'] +call box.crossjoin('33', '32', '10000') +Found 2 tuples: +[1, 'tuple', 1, 'tuple'] +[2, 'tuple', 1, 'tuple'] +lua box.space[33]:truncate() +--- +... +insert into t32 values (200, 'select me!') +Insert OK, 1 row affected +select * from t32 where k0 = 200 +Found 1 tuple: +[200, 'select me!'] +select * from t32 where k0 = 199 +No match +select * from t32 where k0 = 201 +No match +insert into t34 values ('abcd') +Insert OK, 1 row affected +insert into t34 values ('abcda') +Insert OK, 1 row affected +insert into t34 values ('abcda_') +Insert OK, 1 row affected +insert into t34 values ('abcdb') +Insert OK, 1 row affected +insert into t34 values ('abcdb_') +Insert OK, 1 row affected +insert into t34 values ('abcdb__') +Insert OK, 1 row affected +insert into t34 values ('abcdb___') +Insert OK, 1 row affected +insert into t34 values ('abcdc') +Insert OK, 1 row affected +insert into t34 values ('abcdc_') +Insert OK, 1 row affected +lua box.space[34].index[0]:select_range(3, 'abcdb') +--- + - 'abcdb': {} + - 'abcdb_': {} + - 'abcdb__': {} +... +lua box.space[34]:truncate() +--- +... +lua box.space[35]:truncate() +--- +... +insert into t35 values (0, 0, 0, 0) +Insert OK, 1 row affected +insert into t35 values (1, 1, 1, 1) +Insert OK, 1 row affected +insert into t35 values (2, 2, 2, 2) +Insert OK, 1 row affected +replace into t35 values (1, 1, 1, 1) +Replace OK, 1 row affected +replace into t35 values (1, 10, 10, 10) +Replace OK, 1 row affected +replace into t35 values (1, 1, 1, 1) +Replace OK, 1 row affected +select * from t35 WHERE k0 = 10 +No match +select * from t35 WHERE k1 = 10 +No match +select * from t35 WHERE k2 = 10 +No match +select * from t35 WHERE k3 = 10 +No match +select * from t35 WHERE k0 = 1 +Found 1 tuple: +[1, 1, 1, 1] +select * from t35 WHERE k1 = 1 +Found 1 tuple: +[1, 1, 1, 1] +select * from t35 WHERE k2 = 1 +Found 1 tuple: +[1, 1, 1, 1] +select * from t35 WHERE k3 = 1 +Found 1 tuple: +[1, 1, 1, 1] +insert into t35 values (10, 10, 10, 10) +Insert OK, 1 row affected +delete from t35 WHERE k0 = 10 +Delete OK, 1 row affected +select * from t35 WHERE k0 = 10 +No match +select * from t35 WHERE k1 = 10 +No match +select * from t35 WHERE k2 = 10 +No match +select * from t35 WHERE k3 = 10 +No match +insert into t35 values (1, 10, 10, 10) +An error occurred: ER_TUPLE_FOUND, 'Duplicate key exists in unique index 0' +select * from t35 WHERE k0 = 10 +No match +select * from t35 WHERE k1 = 10 +No match +select * from t35 WHERE k2 = 10 +No match +select * from t35 WHERE k3 = 10 +No match +select * from t35 WHERE k0 = 1 +Found 1 tuple: +[1, 1, 1, 1] +replace into t35 values (10, 10, 10, 10) +An error occurred: ER_TUPLE_NOT_FOUND, 'Tuple doesn't exist in index 0' +select * from t35 WHERE k0 = 10 +No match +select * from t35 WHERE k1 = 10 +No match +select * from t35 WHERE k2 = 10 +No match +select * from t35 WHERE k3 = 10 +No match +insert into t35 values (10, 0, 10, 10) +An error occurred: ER_TUPLE_FOUND, 'Duplicate key exists in unique index 1' +select * from t35 WHERE k0 = 10 +No match +select * from t35 WHERE k1 = 10 +No match +select * from t35 WHERE k2 = 10 +No match +select * from t35 WHERE k3 = 10 +No match +select * from t35 WHERE k1 = 0 +Found 1 tuple: +[0, 0, 0, 0] +replace into t35 values (2, 0, 10, 10) +An error occurred: ER_TUPLE_FOUND, 'Duplicate key exists in unique index 1' +select * from t35 WHERE k0 = 10 +No match +select * from t35 WHERE k1 = 10 +No match +select * from t35 WHERE k2 = 10 +No match +select * from t35 WHERE k3 = 10 +No match +select * from t35 WHERE k1 = 0 +Found 1 tuple: +[0, 0, 0, 0] +insert into t35 values (10, 10, 10, 0) +An error occurred: ER_TUPLE_FOUND, 'Duplicate key exists in unique index 3' +select * from t35 WHERE k0 = 10 +No match +select * from t35 WHERE k1 = 10 +No match +select * from t35 WHERE k2 = 10 +No match +select * from t35 WHERE k3 = 10 +No match +select * from t35 WHERE k3 = 0 +Found 1 tuple: +[0, 0, 0, 0] +replace into t35 values (2, 10, 10, 0) +An error occurred: ER_TUPLE_FOUND, 'Duplicate key exists in unique index 3' +select * from t35 WHERE k0 = 10 +No match +select * from t35 WHERE k1 = 10 +No match +select * from t35 WHERE k2 = 10 +No match +select * from t35 WHERE k3 = 10 +No match +select * from t35 WHERE k3 = 0 +Found 1 tuple: +[0, 0, 0, 0] +insert into t35 values (4, 4, 0, 4) +Insert OK, 1 row affected +insert into t35 values (5, 5, 0, 5) +Insert OK, 1 row affected +insert into t35 values (6, 6, 0, 6) +Insert OK, 1 row affected +replace into t35 values (5, 5, 0, 5) +Replace OK, 1 row affected +select * from t35 WHERE k2 = 0 +Found 4 tuples: +[0, 0, 0, 0] +[4, 4, 0, 4] +[5, 5, 0, 5] +[6, 6, 0, 6] +delete from t35 WHERE k0 = 5 +Delete OK, 1 row affected +select * from t35 WHERE k2 = 0 +Found 3 tuples: +[0, 0, 0, 0] +[4, 4, 0, 4] +[6, 6, 0, 6] +lua box.space[35]:truncate() +--- +... diff --git a/test/big/tree_pk_avl.test b/test/big/tree_pk_avl.test new file mode 100644 index 0000000000000000000000000000000000000000..d37c8be9df971af3cbf269b23f38b6542daeabf0 --- /dev/null +++ b/test/big/tree_pk_avl.test @@ -0,0 +1,181 @@ +# encoding: tarantool +# +# integer keys +exec sql "insert into t32 values (1, 'tuple')" +exec admin "save snapshot" +exec sql "insert into t32 values (2, 'tuple 2')" +exec admin "save snapshot" + +exec sql "insert into t32 values (3, 'tuple 3')" +exec sql "select * from t32 where k0 = 1" +exec sql "select * from t32 where k0 = 2" +exec sql "select * from t32 where k0 = 3" + +# Cleanup +exec sql "delete from t32 where k0 = 1" +exec sql "delete from t32 where k0 = 2" +exec sql "delete from t32 where k0 = 3" + +# Test incorrect keys - supplied key field type does not match index type +# https://bugs.launchpad.net/tarantool/+bug/1072624 +exec sql "insert into t32 VALUES('xxxxxxx')" +exec sql "insert into t32 VALUES('')" +exec sql "insert into t32 VALUES('12')" + +# string keys +exec sql "insert into t33 values ('identifier', 'tuple')" +exec admin "save snapshot" +exec sql "insert into t33 values ('second', 'tuple 2')" +exec admin "save snapshot" +exec sql "call box.select_range('33', '0', '100', 'second')" +exec sql "call box.select_range('33', '0', '100', 'identifier')" + +exec sql "insert into t33 values ('third', 'tuple 3')" +exec sql "select * from t33 where k0 = 'identifier'" +exec sql "select * from t33 where k0 = 'second'" +exec sql "select * from t33 where k0 = 'third'" + +# Cleanup +exec sql "delete from t33 where k0 = 'identifier'" +exec sql "delete from t33 where k0 = 'second'" +exec sql "delete from t33 where k0 = 'third'" +lua = """ +function box.crossjoin(space0, space1, limit) + space0 = tonumber(space0) + space1 = tonumber(space1) + limit = tonumber(limit) + local result = {} + for k0, v0 in box.space[space0]:pairs() do + for k1, v1 in box.space[space1]:pairs() do + if limit <= 0 then + return unpack(result) + end + newtuple = {v0:unpack()} + for _, v in v1:pairs() do table.insert(newtuple, v) end + table.insert(result, newtuple) + limit = limit - 1 + end + end + return unpack(result) +end""" +exec sql "insert into t32 values (1, 'tuple')" +exec sql "insert into t33 values (1, 'tuple')" +exec sql "insert into t33 values (2, 'tuple')" +exec admin "lua " + lua.replace('\n', ' ') +exec sql "call box.crossjoin('33', '33', '0')" +exec sql "call box.crossjoin('33', '33', '5')" +exec sql "call box.crossjoin('33', '33', '10000')" +exec sql "call box.crossjoin('33', '32', '10000')" +exec admin "lua box.space[33]:truncate()" + +# Bug #922520 - select missing keys +exec sql "insert into t32 values (200, 'select me!')" +exec sql "select * from t32 where k0 = 200" +exec sql "select * from t32 where k0 = 199" +exec sql "select * from t32 where k0 = 201" + +# Test partially specified keys in TREE indexes +exec sql "insert into t34 values ('abcd')" +exec sql "insert into t34 values ('abcda')" +exec sql "insert into t34 values ('abcda_')" +exec sql "insert into t34 values ('abcdb')" +exec sql "insert into t34 values ('abcdb_')" +exec sql "insert into t34 values ('abcdb__')" +exec sql "insert into t34 values ('abcdb___')" +exec sql "insert into t34 values ('abcdc')" +exec sql "insert into t34 values ('abcdc_')" +exec admin "lua box.space[34].index[0]:select_range(3, 'abcdb')" +exec admin "lua box.space[34]:truncate()" + +# +# tree::replace tests +# + +exec admin "lua box.space[35]:truncate()" + +exec sql "insert into t35 values (0, 0, 0, 0)" +exec sql "insert into t35 values (1, 1, 1, 1)" +exec sql "insert into t35 values (2, 2, 2, 2)" + +# OK +exec sql "replace into t35 values (1, 1, 1, 1)" +exec sql "replace into t35 values (1, 10, 10, 10)" +exec sql "replace into t35 values (1, 1, 1, 1)" +exec sql "select * from t35 WHERE k0 = 10" +exec sql "select * from t35 WHERE k1 = 10" +exec sql "select * from t35 WHERE k2 = 10" +exec sql "select * from t35 WHERE k3 = 10" +exec sql "select * from t35 WHERE k0 = 1" +exec sql "select * from t35 WHERE k1 = 1" +exec sql "select * from t35 WHERE k2 = 1" +exec sql "select * from t35 WHERE k3 = 1" + +# OK +exec sql "insert into t35 values (10, 10, 10, 10)" +exec sql "delete from t35 WHERE k0 = 10" +exec sql "select * from t35 WHERE k0 = 10" +exec sql "select * from t35 WHERE k1 = 10" +exec sql "select * from t35 WHERE k2 = 10" +exec sql "select * from t35 WHERE k3 = 10" + + +# TupleFound (primary key) +exec sql "insert into t35 values (1, 10, 10, 10)" +exec sql "select * from t35 WHERE k0 = 10" +exec sql "select * from t35 WHERE k1 = 10" +exec sql "select * from t35 WHERE k2 = 10" +exec sql "select * from t35 WHERE k3 = 10" +exec sql "select * from t35 WHERE k0 = 1" + +# TupleNotFound (primary key) +exec sql "replace into t35 values (10, 10, 10, 10)" +exec sql "select * from t35 WHERE k0 = 10" +exec sql "select * from t35 WHERE k1 = 10" +exec sql "select * from t35 WHERE k2 = 10" +exec sql "select * from t35 WHERE k3 = 10" + +# TupleFound (key #1) +exec sql "insert into t35 values (10, 0, 10, 10)" +exec sql "select * from t35 WHERE k0 = 10" +exec sql "select * from t35 WHERE k1 = 10" +exec sql "select * from t35 WHERE k2 = 10" +exec sql "select * from t35 WHERE k3 = 10" +exec sql "select * from t35 WHERE k1 = 0" + +# TupleFound (key #1) +exec sql "replace into t35 values (2, 0, 10, 10)" +exec sql "select * from t35 WHERE k0 = 10" +exec sql "select * from t35 WHERE k1 = 10" +exec sql "select * from t35 WHERE k2 = 10" +exec sql "select * from t35 WHERE k3 = 10" +exec sql "select * from t35 WHERE k1 = 0" + +# TupleFound (key #3) +exec sql "insert into t35 values (10, 10, 10, 0)" +exec sql "select * from t35 WHERE k0 = 10" +exec sql "select * from t35 WHERE k1 = 10" +exec sql "select * from t35 WHERE k2 = 10" +exec sql "select * from t35 WHERE k3 = 10" +exec sql "select * from t35 WHERE k3 = 0" + +# TupleFound (key #3) +exec sql "replace into t35 values (2, 10, 10, 0)" +exec sql "select * from t35 WHERE k0 = 10" +exec sql "select * from t35 WHERE k1 = 10" +exec sql "select * from t35 WHERE k2 = 10" +exec sql "select * from t35 WHERE k3 = 10" +exec sql "select * from t35 WHERE k3 = 0" + +sql.sort = True +# Non-Uniq test (key #2) +exec sql "insert into t35 values (4, 4, 0, 4)" +exec sql "insert into t35 values (5, 5, 0, 5)" +exec sql "insert into t35 values (6, 6, 0, 6)" +exec sql "replace into t35 values (5, 5, 0, 5)" +exec sql "select * from t35 WHERE k2 = 0" +exec sql "delete from t35 WHERE k0 = 5" +exec sql "select * from t35 WHERE k2 = 0" +sql.sort = False + +exec admin "lua box.space[35]:truncate()" + diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 87e0a68347d29abd3ba061d70cd131d6159a9ce4..f0c2c22d5d5413aafa8d146b7cdeff99e8e7de23 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -25,3 +25,6 @@ add_executable(mempool.test mempool.c) target_link_libraries(mempool.test small) add_executable(small_alloc.test small_alloc.c) target_link_libraries(small_alloc.test small) +add_executable(avl_tree.test avl_tree.c) +target_link_libraries(avl_tree.test m) +target_link_libraries(avl_tree.test misc) diff --git a/test/unit/avl_tree.c b/test/unit/avl_tree.c new file mode 100644 index 0000000000000000000000000000000000000000..2cd864804c404c0a3513c942e70bdbb1307509fa --- /dev/null +++ b/test/unit/avl_tree.c @@ -0,0 +1,109 @@ +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <stdbool.h> + +#include "unit.h" +#include "avl_tree.h" +#include "../third_party/sptree.h" + +#ifndef MAX +#define MAX(a,b) (((a)>(b))?(a):(b)) +#endif //#ifndef MAX + +SPTREE_DEF(test, realloc); +AVL_DEF(test, realloc); + +static int +node_comp(const void *p1, const void *p2, void* unused) +{ + (void)unused; + return *((const int *)p1) - *((const int *)p2); +} + +static void +simple_check() +{ + header(); + + avl_test t; + + int ini[] = {1, 10, 2, 9, 3, 8, 4, 7, 5, 6}; + int *use = (int *)malloc(sizeof(ini)); + memcpy(use, ini, sizeof(ini)); + int nuse = (int)(sizeof(ini) / sizeof(ini[0])); + avl_test_init(&t, sizeof(int), use, nuse, 0, &node_comp, 0, 0); + + int add[] = {-1, 11, 0, 12}; + for (size_t i = 0; i < sizeof(add) / sizeof(add[0]); i++) + avl_test_replace(&t, &add[i], 0); + + int rem[] = {3, 5, 7}; + for (size_t i = 0; i < sizeof(rem) / sizeof(rem[0]); i++) + avl_test_delete(&t, &rem[i]); + + int *val; + + val = avl_test_first(&t); + printf("%d ", *val); + val = avl_test_last(&t); + printf("%d\n", *val); + + avl_test_iterator* itr = avl_test_iterator_init(&t); + while ((val = avl_test_iterator_next(itr))) + printf("%d ", *val); + printf("\n"); + avl_test_iterator_free(itr); + + itr = avl_test_iterator_reverse_init(&t); + while ((val = avl_test_iterator_reverse_next(itr))) + printf("%d ", *val); + printf("\n"); + avl_test_iterator_free(itr); + + avl_test_destroy(&t); + + + footer(); +} + +static void +compare_with_sptree_check() +{ + header(); + + sptree_test spt_test; + sptree_test_init(&spt_test, sizeof(int), 0, 0, 0, &node_comp, 0, 0); + avl_test avl_test; + avl_test_init(&avl_test, sizeof(int), 0, 0, 0, &node_comp, 0, 0); + + for (int i = 0; i < 64 * 1024; i++) { + int rnd = rand() % (16 * 1024); + int find_res1 = sptree_test_find(&spt_test, &rnd) ? 1 : 0; + int find_res2 = avl_test_find(&avl_test, &rnd) ? 1 : 0; + if (find_res1 ^ find_res2) { + fail("trees identity", "false"); + continue; + } + + if (find_res1 == 0) { + sptree_test_replace(&spt_test, &rnd, NULL); + avl_test_replace(&avl_test, &rnd, NULL); + + } else { + sptree_test_delete(&spt_test, &rnd); + avl_test_delete(&avl_test, &rnd); + } + } + sptree_test_destroy(&spt_test); + avl_test_destroy(&avl_test); + + footer(); +} + +int +main(void) +{ + simple_check(); + compare_with_sptree_check(); +} \ No newline at end of file diff --git a/test/unit/avl_tree.result b/test/unit/avl_tree.result new file mode 100644 index 0000000000000000000000000000000000000000..59f971cc7244fbea1d6163b01d7ddd2e49acd8a5 --- /dev/null +++ b/test/unit/avl_tree.result @@ -0,0 +1,8 @@ + *** simple_check *** +-1 12 +-1 0 1 2 4 6 8 9 10 11 12 +12 11 10 9 8 6 4 2 1 0 -1 + *** simple_check: done *** + *** compare_with_sptree_check *** + *** compare_with_sptree_check: done *** + \ No newline at end of file