diff --git a/Makefile b/Makefile index c710f3123..ba217f2bc 100644 --- a/Makefile +++ b/Makefile @@ -962,15 +962,15 @@ OBJS += src/mux_h2.o src/mux_h1.o src/mux_fcgi.o src/log.o \ src/cache.o src/stconn.o src/http_htx.o src/debug.o \ src/check.o src/stats-html.o src/haproxy.o src/listener.o \ src/applet.o src/pattern.o src/cfgparse-listen.o \ - src/flt_spoe.o src/cebuis_tree.o src/http_ext.o \ - src/http_act.o src/http_fetch.o src/cebus_tree.o \ - src/cebuib_tree.o src/http_client.o src/dns.o \ - src/cebub_tree.o src/vars.o src/event_hdl.o src/tcp_rules.o \ + src/flt_spoe.o src/cebis_tree.o src/http_ext.o \ + src/http_act.o src/http_fetch.o src/cebs_tree.o \ + src/cebib_tree.o src/http_client.o src/dns.o \ + src/cebb_tree.o src/vars.o src/event_hdl.o src/tcp_rules.o \ src/trace.o src/stats-proxy.o src/pool.o src/stats.o \ src/cfgparse-global.o src/filters.o src/mux_pt.o \ src/flt_http_comp.o src/sock.o src/h1.o src/sink.o \ - src/cebua_tree.o src/session.o src/payload.o src/htx.o \ - src/cebul_tree.o src/cebu32_tree.o src/cebu64_tree.o \ + src/ceba_tree.o src/session.o src/payload.o src/htx.o \ + src/cebl_tree.o src/ceb32_tree.o src/ceb64_tree.o \ src/server_state.o src/proto_rhttp.o src/flt_trace.o src/fd.o \ src/task.o src/map.o src/fcgi-app.o src/h2.o src/mworker.o \ src/tcp_sample.o src/mjson.o src/h1_htx.o src/tcp_act.o \ diff --git a/include/haproxy/vars-t.h b/include/haproxy/vars-t.h index 7a709bda2..157576585 100644 --- a/include/haproxy/vars-t.h +++ b/include/haproxy/vars-t.h @@ -50,7 +50,7 @@ enum vars_scope { #define VAR_NAME_ROOTS 4 struct vars { - struct ceb_node *name_root[VAR_NAME_ROOTS]; + struct ceb_root *name_root[VAR_NAME_ROOTS]; enum vars_scope scope; unsigned int size; __decl_thread(HA_RWLOCK_T rwlock); diff --git a/include/haproxy/vars.h b/include/haproxy/vars.h index bfadee5ed..bffeb2214 100644 --- a/include/haproxy/vars.h +++ b/include/haproxy/vars.h @@ -22,7 +22,7 @@ #ifndef _HAPROXY_VARS_H #define _HAPROXY_VARS_H -#include +#include #include #include @@ -86,7 +86,7 @@ static inline void vars_prune(struct vars *vars, struct session *sess, struct st int i; for (i = 0; i < VAR_NAME_ROOTS; i++) { - while ((node = cebu64_first(&vars->name_root[i]))) { + while ((node = cebu64_imm_first(&vars->name_root[i]))) { var = container_of(node, struct var, node); size += var_clear(vars, var, 1); } diff --git a/include/import/ceb32_tree.h b/include/import/ceb32_tree.h new file mode 100644 index 000000000..c8522453e --- /dev/null +++ b/include/import/ceb32_tree.h @@ -0,0 +1,426 @@ +/* + * Compact Elastic Binary Trees - exported functions operating on u32 keys + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _CEB32_TREE_H +#define _CEB32_TREE_H + +#include "cebtree.h" +#include + +/* simpler version */ +struct ceb_node *ceb32_imm_insert(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *ceb32_imm_first(struct ceb_root *const *root); +struct ceb_node *ceb32_imm_last(struct ceb_root *const *root); +struct ceb_node *ceb32_imm_lookup(struct ceb_root *const *root, uint32_t key); +struct ceb_node *ceb32_imm_lookup_le(struct ceb_root *const *root, uint32_t key); +struct ceb_node *ceb32_imm_lookup_lt(struct ceb_root *const *root, uint32_t key); +struct ceb_node *ceb32_imm_lookup_ge(struct ceb_root *const *root, uint32_t key); +struct ceb_node *ceb32_imm_lookup_gt(struct ceb_root *const *root, uint32_t key); +struct ceb_node *ceb32_imm_next_unique(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *ceb32_imm_prev_unique(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *ceb32_imm_next_dup(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *ceb32_imm_prev_dup(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *ceb32_imm_next(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *ceb32_imm_prev(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *ceb32_imm_delete(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *ceb32_imm_pick(struct ceb_root **root, uint32_t key); + +struct ceb_node *cebu32_imm_insert(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *cebu32_imm_first(struct ceb_root *const *root); +struct ceb_node *cebu32_imm_last(struct ceb_root *const *root); +struct ceb_node *cebu32_imm_lookup(struct ceb_root *const *root, uint32_t key); +struct ceb_node *cebu32_imm_lookup_le(struct ceb_root *const *root, uint32_t key); +struct ceb_node *cebu32_imm_lookup_lt(struct ceb_root *const *root, uint32_t key); +struct ceb_node *cebu32_imm_lookup_ge(struct ceb_root *const *root, uint32_t key); +struct ceb_node *cebu32_imm_lookup_gt(struct ceb_root *const *root, uint32_t key); +struct ceb_node *cebu32_imm_next(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebu32_imm_prev(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebu32_imm_delete(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *cebu32_imm_pick(struct ceb_root **root, uint32_t key); + +/* generic dump function */ +void ceb32_imm_default_dump(struct ceb_root **ceb_root, const char *label, const void *ctx, int sub); + +/* returns the pointer to the uint32_t key */ +static inline uint32_t *ceb32_imm_key(const struct ceb_node *node) +{ + return (uint32_t *)ceb_key_ptr(node, sizeof(struct ceb_node)); +} + +/* version taking a key offset */ +struct ceb_node *ceb32_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *ceb32_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs); +struct ceb_node *ceb32_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs); +struct ceb_node *ceb32_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, uint32_t key); +struct ceb_node *ceb32_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, uint32_t key); +struct ceb_node *ceb32_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, uint32_t key); +struct ceb_node *ceb32_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, uint32_t key); +struct ceb_node *ceb32_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, uint32_t key); +struct ceb_node *ceb32_ofs_next_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *ceb32_ofs_prev_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *ceb32_ofs_next_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *ceb32_ofs_prev_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *ceb32_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *ceb32_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *ceb32_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *ceb32_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, uint32_t key); + +struct ceb_node *cebu32_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebu32_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs); +struct ceb_node *cebu32_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs); +struct ceb_node *cebu32_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, uint32_t key); +struct ceb_node *cebu32_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, uint32_t key); +struct ceb_node *cebu32_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, uint32_t key); +struct ceb_node *cebu32_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, uint32_t key); +struct ceb_node *cebu32_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, uint32_t key); +struct ceb_node *cebu32_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebu32_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebu32_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebu32_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, uint32_t key); + +/* generic dump function taking a key offset */ +void ceb32_ofs_default_dump(struct ceb_root *const *root, ptrdiff_t kofs, const char *label, const void *ctx, int sub); + +/* returns the pointer to the uint32_t key */ +static inline uint32_t *ceb32_ofs_key(const struct ceb_node *node, ptrdiff_t kofs) +{ + return (uint32_t *)ceb_key_ptr(node, kofs); +} + +/* insert at root , the item using node member and + * key member , and returns either the inserted item, or the one that + * was found there. + */ +#define ceb32_item_insert(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = ceb32_ofs_insert(root, _kofs, &(item_ptr)->nname); \ + (typeof(item_ptr))((char *)(_node) - _nofs); \ +}) + +/* descend root , and return the first item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define ceb32_item_first(root, nname, kname, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = ceb32_ofs_first(root, _kofs); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* descend root , and return the last item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define ceb32_item_last(root, nname, kname, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = ceb32_ofs_last(root, _kofs); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member . Returns the + * pointer to the item, or NULL if not found. + */ +#define ceb32_item_lookup(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = ceb32_ofs_lookup(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the last of + * the greatest keys that are lower than or equal to , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define ceb32_item_lookup_le(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = ceb32_ofs_lookup_le(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the last of + * the greatest keys that are strictly lower than , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define ceb32_item_lookup_lt(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = ceb32_ofs_lookup_lt(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the first of + * the lowest keys key that are greater than or equal to , using node + * member and key member . Returns the pointer to the item, or + * NULL if not found. + */ +#define ceb32_item_lookup_ge(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = ceb32_ofs_lookup_ge(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the first of + * the lowest keys that are strictly greater than , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define ceb32_item_lookup_gt(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = ceb32_ofs_lookup_gt(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item after item that has a + * strictly greater key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define ceb32_item_next_unique(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = ceb32_ofs_next_unique(root, _kofs, &(item_ptr)->nname);\ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item before item that has a + * strictly lower key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define ceb32_item_prev_unique(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = ceb32_ofs_prev_unique(root, _kofs, &(item_ptr)->nname);\ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item after item that has + * exactly the same key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define ceb32_item_next_dup(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = ceb32_ofs_next_dup(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item before item that has + * exactly the same key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define ceb32_item_prev_dup(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = ceb32_ofs_prev_dup(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item immediately after item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define ceb32_item_next(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = ceb32_ofs_next(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item immediately before item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define ceb32_item_prev(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = ceb32_ofs_prev(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item using node member and + * key member , deletes it and returns it. If the item is NULL or absent + * from the tree, NULL is returned. + */ +#define ceb32_item_delete(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + typeof(item_ptr) _item = (item_ptr); \ + struct ceb_node *_node = ceb32_ofs_delete(root, _kofs, _item ? &_item->nname : NULL); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member , deletes it and + * returns it. If the key is not found in the tree, NULL is returned. + */ +#define ceb32_item_pick(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = ceb32_ofs_pick(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/*** versions using unique keys below ***/ + +/* insert at root , the item using node member and key + * member , and returns either the inserted item, or the one that was + * found there. + */ +#define cebu32_item_insert(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebu32_ofs_insert(root, _kofs, &(item_ptr)->nname); \ + (typeof(item_ptr))((char *)(_node) - _nofs); \ +}) + +/* descend root , and return the first item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebu32_item_first(root, nname, kname, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebu32_ofs_first(root, _kofs); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* descend root , and return the last item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebu32_item_last(root, nname, kname, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebu32_ofs_last(root, _kofs); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the key + * , using node member and key member . Returns the pointer + * to the item, or NULL if not found. + */ +#define cebu32_item_lookup(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebu32_ofs_lookup(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the greatest + * key that is lower than or equal to , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebu32_item_lookup_le(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebu32_ofs_lookup_le(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the greatest + * key that is strictly lower than , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebu32_item_lookup_lt(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebu32_ofs_lookup_lt(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the lowest + * key key that is greater than or equal to , using node member + * and key member . Returns the pointer to the item, or NULL if not + * found. + */ +#define cebu32_item_lookup_ge(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebu32_ofs_lookup_ge(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the lowest + * key that is strictly greater than , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebu32_item_lookup_gt(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebu32_ofs_lookup_gt(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item immediately after item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define cebu32_item_next(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebu32_ofs_next(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item immediately before item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define cebu32_item_prev(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebu32_ofs_prev(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item using node member and + * key member , deletes it and returns it. If the item is NULL or absent + * from the tree, NULL is returned. + */ +#define cebu32_item_delete(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + typeof(item_ptr) _item = (item_ptr); \ + struct ceb_node *_node = cebu32_ofs_delete(root, _kofs, _item ? &_item->nname : NULL); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member , deletes it and + * returns it. If the key is not found in the tree, NULL is returned. + */ +#define cebu32_item_pick(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebu32_ofs_pick(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +#endif /* _CEB32_TREE_H */ diff --git a/include/import/ceb64_tree.h b/include/import/ceb64_tree.h new file mode 100644 index 000000000..94cbb046c --- /dev/null +++ b/include/import/ceb64_tree.h @@ -0,0 +1,426 @@ +/* + * Compact Elastic Binary Trees - exported functions for operations on u64 keys + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _CEB64_TREE_H +#define _CEB64_TREE_H + +#include "cebtree.h" +#include + +/* simpler version */ +struct ceb_node *ceb64_imm_insert(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *ceb64_imm_first(struct ceb_root *const *root); +struct ceb_node *ceb64_imm_last(struct ceb_root *const *root); +struct ceb_node *ceb64_imm_lookup(struct ceb_root *const *root, uint64_t key); +struct ceb_node *ceb64_imm_lookup_le(struct ceb_root *const *root, uint64_t key); +struct ceb_node *ceb64_imm_lookup_lt(struct ceb_root *const *root, uint64_t key); +struct ceb_node *ceb64_imm_lookup_ge(struct ceb_root *const *root, uint64_t key); +struct ceb_node *ceb64_imm_lookup_gt(struct ceb_root *const *root, uint64_t key); +struct ceb_node *ceb64_imm_next_unique(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *ceb64_imm_prev_unique(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *ceb64_imm_next_dup(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *ceb64_imm_prev_dup(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *ceb64_imm_next(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *ceb64_imm_prev(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *ceb64_imm_delete(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *ceb64_imm_pick(struct ceb_root **root, uint64_t key); + +struct ceb_node *cebu64_imm_insert(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *cebu64_imm_first(struct ceb_root *const *root); +struct ceb_node *cebu64_imm_last(struct ceb_root *const *root); +struct ceb_node *cebu64_imm_lookup(struct ceb_root *const *root, uint64_t key); +struct ceb_node *cebu64_imm_lookup_le(struct ceb_root *const *root, uint64_t key); +struct ceb_node *cebu64_imm_lookup_lt(struct ceb_root *const *root, uint64_t key); +struct ceb_node *cebu64_imm_lookup_ge(struct ceb_root *const *root, uint64_t key); +struct ceb_node *cebu64_imm_lookup_gt(struct ceb_root *const *root, uint64_t key); +struct ceb_node *cebu64_imm_next(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebu64_imm_prev(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebu64_imm_delete(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *cebu64_imm_pick(struct ceb_root **root, uint64_t key); + +/* generic dump function */ +void ceb64_imm_default_dump(struct ceb_root **ceb_root, const char *label, const void *ctx, int sub); + +/* returns the pointer to the uint64_t key */ +static inline uint64_t *ceb64_imm_key(const struct ceb_node *node) +{ + return (uint64_t *)ceb_key_ptr(node, sizeof(struct ceb_node)); +} + +/* version taking a key offset */ +struct ceb_node *ceb64_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *ceb64_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs); +struct ceb_node *ceb64_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs); +struct ceb_node *ceb64_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, uint64_t key); +struct ceb_node *ceb64_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, uint64_t key); +struct ceb_node *ceb64_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, uint64_t key); +struct ceb_node *ceb64_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, uint64_t key); +struct ceb_node *ceb64_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, uint64_t key); +struct ceb_node *ceb64_ofs_next_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *ceb64_ofs_prev_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *ceb64_ofs_next_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *ceb64_ofs_prev_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *ceb64_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *ceb64_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *ceb64_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *ceb64_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, uint64_t key); + +struct ceb_node *cebu64_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebu64_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs); +struct ceb_node *cebu64_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs); +struct ceb_node *cebu64_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, uint64_t key); +struct ceb_node *cebu64_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, uint64_t key); +struct ceb_node *cebu64_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, uint64_t key); +struct ceb_node *cebu64_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, uint64_t key); +struct ceb_node *cebu64_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, uint64_t key); +struct ceb_node *cebu64_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebu64_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebu64_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebu64_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, uint64_t key); + +/* generic dump function taking a key offset */ +void ceb64_ofs_default_dump(struct ceb_root *const *root, ptrdiff_t kofs, const char *label, const void *ctx, int sub); + +/* returns the pointer to the uint64_t key */ +static inline uint64_t *ceb64_ofs_key(const struct ceb_node *node, ptrdiff_t kofs) +{ + return (uint64_t *)ceb_key_ptr(node, kofs); +} + +/* insert at root , the item using node member and + * key member , and returns either the inserted item, or the one that + * was found there. + */ +#define ceb64_item_insert(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = ceb64_ofs_insert(root, _kofs, &(item_ptr)->nname); \ + (typeof(item_ptr))((char *)(_node) - _nofs); \ +}) + +/* descend root , and return the first item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define ceb64_item_first(root, nname, kname, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = ceb64_ofs_first(root, _kofs); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* descend root , and return the last item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define ceb64_item_last(root, nname, kname, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = ceb64_ofs_last(root, _kofs); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member . Returns the + * pointer to the item, or NULL if not found. + */ +#define ceb64_item_lookup(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = ceb64_ofs_lookup(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the last of + * the greatest keys that are lower than or equal to , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define ceb64_item_lookup_le(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = ceb64_ofs_lookup_le(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the last of + * the greatest keys that are strictly lower than , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define ceb64_item_lookup_lt(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = ceb64_ofs_lookup_lt(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the first of + * the lowest keys key that are greater than or equal to , using node + * member and key member . Returns the pointer to the item, or + * NULL if not found. + */ +#define ceb64_item_lookup_ge(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = ceb64_ofs_lookup_ge(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the first of + * the lowest keys that are strictly greater than , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define ceb64_item_lookup_gt(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = ceb64_ofs_lookup_gt(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item after item that has a + * strictly greater key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define ceb64_item_next_unique(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = ceb64_ofs_next_unique(root, _kofs, &(item_ptr)->nname);\ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item before item that has a + * strictly lower key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define ceb64_item_prev_unique(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = ceb64_ofs_prev_unique(root, _kofs, &(item_ptr)->nname);\ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item after item that has + * exactly the same key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define ceb64_item_next_dup(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = ceb64_ofs_next_dup(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item before item that has + * exactly the same key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define ceb64_item_prev_dup(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = ceb64_ofs_prev_dup(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item immediately after item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define ceb64_item_next(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = ceb64_ofs_next(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item immediately before item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define ceb64_item_prev(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = ceb64_ofs_prev(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item using node member and + * key member , deletes it and returns it. If the item is NULL or absent + * from the tree, NULL is returned. + */ +#define ceb64_item_delete(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + typeof(item_ptr) _item = (item_ptr); \ + struct ceb_node *_node = ceb64_ofs_delete(root, _kofs, _item ? &_item->nname : NULL); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member , deletes it and + * returns it. If the key is not found in the tree, NULL is returned. + */ +#define ceb64_item_pick(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = ceb64_ofs_pick(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/*** versions using unique keys below ***/ + +/* insert at root , the item using node member and key + * member , and returns either the inserted item, or the one that was + * found there. + */ +#define cebu64_item_insert(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebu64_ofs_insert(root, _kofs, &(item_ptr)->nname); \ + (typeof(item_ptr))((char *)(_node) - _nofs); \ +}) + +/* descend root , and return the first item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebu64_item_first(root, nname, kname, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebu64_ofs_first(root, _kofs); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* descend root , and return the last item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebu64_item_last(root, nname, kname, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebu64_ofs_last(root, _kofs); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the key + * , using node member and key member . Returns the pointer + * to the item, or NULL if not found. + */ +#define cebu64_item_lookup(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebu64_ofs_lookup(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the greatest + * key that is lower than or equal to , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebu64_item_lookup_le(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebu64_ofs_lookup_le(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the greatest + * key that is strictly lower than , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebu64_item_lookup_lt(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebu64_ofs_lookup_lt(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the lowest + * key key that is greater than or equal to , using node member + * and key member . Returns the pointer to the item, or NULL if not + * found. + */ +#define cebu64_item_lookup_ge(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebu64_ofs_lookup_ge(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the lowest + * key that is strictly greater than , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebu64_item_lookup_gt(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebu64_ofs_lookup_gt(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item immediately after item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define cebu64_item_next(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebu64_ofs_next(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item immediately before item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define cebu64_item_prev(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebu64_ofs_prev(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item using node member and + * key member , deletes it and returns it. If the item is NULL or absent + * from the tree, NULL is returned. + */ +#define cebu64_item_delete(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + typeof(item_ptr) _item = (item_ptr); \ + struct ceb_node *_node = cebu64_ofs_delete(root, _kofs, _item ? &_item->nname : NULL); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member , deletes it and + * returns it. If the key is not found in the tree, NULL is returned. + */ +#define cebu64_item_pick(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebu64_ofs_pick(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +#endif /* _CEB64_TREE_H */ diff --git a/include/import/ceba_tree.h b/include/import/ceba_tree.h new file mode 100644 index 000000000..99d7d7015 --- /dev/null +++ b/include/import/ceba_tree.h @@ -0,0 +1,55 @@ +/* + * Compact Elastic Binary Trees - exported functions operating on addr keys + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _CEBA_TREE_H +#define _CEBA_TREE_H + +#include "cebtree.h" + +/* simpler version */ +struct ceb_node *cebua_imm_insert(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *cebua_imm_first(struct ceb_root *const *root); +struct ceb_node *cebua_imm_last(struct ceb_root *const *root); +struct ceb_node *cebua_imm_lookup(struct ceb_root *const *root, const void *key); +struct ceb_node *cebua_imm_lookup_le(struct ceb_root *const *root, const void *key); +struct ceb_node *cebua_imm_lookup_lt(struct ceb_root *const *root, const void *key); +struct ceb_node *cebua_imm_lookup_ge(struct ceb_root *const *root, const void *key); +struct ceb_node *cebua_imm_lookup_gt(struct ceb_root *const *root, const void *key); +struct ceb_node *cebua_imm_next(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebua_imm_prev(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebua_imm_delete(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *cebua_imm_pick(struct ceb_root **root, const void *key); + +/* generic dump function */ +void ceba_imm_default_dump(struct ceb_root *const *root, const char *label, const void *ctx, int sub); + +/* returns the pointer to the area that immediately follows the node */ +static inline void *ceba_imm_key(const struct ceb_node *node) +{ + return (void *)ceb_key_ptr(node, sizeof(struct ceb_node)); +} + +#endif /* _CEBA_TREE_H */ diff --git a/include/import/cebb_tree.h b/include/import/cebb_tree.h new file mode 100644 index 000000000..6474c49e6 --- /dev/null +++ b/include/import/cebb_tree.h @@ -0,0 +1,419 @@ +/* + * Compact Elastic Binary Trees - exported functions operating on mb keys + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _CEBB_TREE_H +#define _CEBB_TREE_H + +#include "cebtree.h" + +/* simpler version */ +struct ceb_node *cebb_imm_insert(struct ceb_root **root, struct ceb_node *node, size_t len); +struct ceb_node *cebb_imm_first(struct ceb_root *const *root, size_t len); +struct ceb_node *cebb_imm_last(struct ceb_root *const *root, size_t len); +struct ceb_node *cebb_imm_lookup(struct ceb_root *const *root, const void *key, size_t len); +struct ceb_node *cebb_imm_lookup_le(struct ceb_root *const *root, const void *key, size_t len); +struct ceb_node *cebb_imm_lookup_lt(struct ceb_root *const *root, const void *key, size_t len); +struct ceb_node *cebb_imm_lookup_ge(struct ceb_root *const *root, const void *key, size_t len); +struct ceb_node *cebb_imm_lookup_gt(struct ceb_root *const *root, const void *key, size_t len); +struct ceb_node *cebb_imm_next_unique(struct ceb_root *const *root, struct ceb_node *node, size_t len); +struct ceb_node *cebb_imm_prev_unique(struct ceb_root *const *root, struct ceb_node *node, size_t len); +struct ceb_node *cebb_imm_next_dup(struct ceb_root *const *root, struct ceb_node *node, size_t len); +struct ceb_node *cebb_imm_prev_dup(struct ceb_root *const *root, struct ceb_node *node, size_t len); +struct ceb_node *cebb_imm_next(struct ceb_root *const *root, struct ceb_node *node, size_t len); +struct ceb_node *cebb_imm_prev(struct ceb_root *const *root, struct ceb_node *node, size_t len); +struct ceb_node *cebb_imm_delete(struct ceb_root **root, struct ceb_node *node, size_t len); +struct ceb_node *cebb_imm_pick(struct ceb_root **root, const void *key, size_t len); + +struct ceb_node *cebub_imm_insert(struct ceb_root **root, struct ceb_node *node, size_t len); +struct ceb_node *cebub_imm_first(struct ceb_root *const *root, size_t len); +struct ceb_node *cebub_imm_last(struct ceb_root *const *root, size_t len); +struct ceb_node *cebub_imm_lookup(struct ceb_root *const *root, const void *key, size_t len); +struct ceb_node *cebub_imm_lookup_le(struct ceb_root *const *root, const void *key, size_t len); +struct ceb_node *cebub_imm_lookup_lt(struct ceb_root *const *root, const void *key, size_t len); +struct ceb_node *cebub_imm_lookup_ge(struct ceb_root *const *root, const void *key, size_t len); +struct ceb_node *cebub_imm_lookup_gt(struct ceb_root *const *root, const void *key, size_t len); +struct ceb_node *cebub_imm_next(struct ceb_root *const *root, struct ceb_node *node, size_t len); +struct ceb_node *cebub_imm_prev(struct ceb_root *const *root, struct ceb_node *node, size_t len); +struct ceb_node *cebub_imm_delete(struct ceb_root **root, struct ceb_node *node, size_t len); +struct ceb_node *cebub_imm_pick(struct ceb_root **root, const void *key, size_t len); + +/* returns the pointer to the void* key that follows the node */ +static inline void *cebb_imm_key(const struct ceb_node *node) +{ + return (void *)ceb_key_ptr(node, sizeof(struct ceb_node)); +} + +/* version taking a key offset */ +struct ceb_node *cebb_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebb_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs, size_t len); +struct ceb_node *cebb_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs, size_t len); +struct ceb_node *cebb_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len); +struct ceb_node *cebb_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len); +struct ceb_node *cebb_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len); +struct ceb_node *cebb_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len); +struct ceb_node *cebb_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len); +struct ceb_node *cebb_ofs_next_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebb_ofs_prev_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebb_ofs_next_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebb_ofs_prev_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebb_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebb_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebb_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebb_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, const void *key, size_t len); + +struct ceb_node *cebub_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebub_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs, size_t len); +struct ceb_node *cebub_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs, size_t len); +struct ceb_node *cebub_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len); +struct ceb_node *cebub_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len); +struct ceb_node *cebub_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len); +struct ceb_node *cebub_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len); +struct ceb_node *cebub_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len); +struct ceb_node *cebub_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebub_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebub_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebub_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, const void *key, size_t len); + +/* returns the pointer to the void* key*/ +static inline void *cebb_ofs_key(const struct ceb_node *node, ptrdiff_t kofs) +{ + return (void*)ceb_key_ptr(node, kofs); +} + +/* insert at root , the item using node member and key + * member , and length and returns either the inserted item, or + * the one that was found there. + */ +#define cebb_item_insert(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebb_ofs_insert(root, _kofs, &(item_ptr)->nname, len); \ + (typeof(item_ptr))((char *)(_node) - _nofs); \ +}) + +/* descend root , and return the first item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebb_item_first(root, nname, kname, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebb_ofs_first(root, _kofs, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* descend root , and return the last item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebb_item_last(root, nname, kname, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebb_ofs_last(root, _kofs, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member . Returns the + * pointer to the item, or NULL if not found. + */ +#define cebb_item_lookup(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebb_ofs_lookup(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the last of + * the greatest keys that are lower than or equal to , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define cebb_item_lookup_le(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebb_ofs_lookup_le(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the last of + * the greatest keys that are strictly lower than , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define cebb_item_lookup_lt(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebb_ofs_lookup_lt(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the first of + * the lowest keys key that are greater than or equal to , using node + * member and key member . Returns the pointer to the item, or + * NULL if not found. + */ +#define cebb_item_lookup_ge(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebb_ofs_lookup_ge(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the first of + * the lowest keys that are strictly greater than , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define cebb_item_lookup_gt(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebb_ofs_lookup_gt(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item after item that has a + * strictly greater key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define cebb_item_next_unique(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebb_ofs_next_unique(root, _kofs, &(item_ptr)->nname, len); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item before item that has a + * strictly lower key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define cebb_item_prev_unique(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebb_ofs_prev_unique(root, _kofs, &(item_ptr)->nname, len); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item after item that has + * exactly the same key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define cebb_item_next_dup(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebb_ofs_next_dup(root, _kofs, &(item_ptr)->nname, len); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item before item that has + * exactly the same key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define cebb_item_prev_dup(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebb_ofs_prev_dup(root, _kofs, &(item_ptr)->nname, len); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item immediately after item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define cebb_item_next(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebb_ofs_next(root, _kofs, &(item_ptr)->nname, len); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item immediately before item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define cebb_item_prev(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebb_ofs_prev(root, _kofs, &(item_ptr)->nname, len); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item using node member and + * key member , deletes it and returns it. If the item is NULL or absent + * from the tree, NULL is returned. + */ +#define cebb_item_delete(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + typeof(item_ptr) _item = (item_ptr); \ + struct ceb_node *_node = cebb_ofs_delete(root, _kofs, _item ? &_item->nname : NULL, len); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member , deletes it and + * returns it. If the key is not found in the tree, NULL is returned. + */ +#define cebb_item_pick(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebb_ofs_pick(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/*** versions using unique keys below ***/ + +/* insert at root , the item using node member and key + * member , and returns either the inserted item, or the one that was + * found there. + */ +#define cebub_item_insert(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebub_ofs_insert(root, _kofs, &(item_ptr)->nname, len); \ + (typeof(item_ptr))((char *)(_node) - _nofs); \ +}) + +/* descend root , and return the first item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebub_item_first(root, nname, kname, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebub_ofs_first(root, _kofs, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* descend root , and return the last item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebub_item_last(root, nname, kname, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebub_ofs_last(root, _kofs, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the key + * , using node member and key member . Returns the pointer + * to the item, or NULL if not found. + */ +#define cebub_item_lookup(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebub_ofs_lookup(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the greatest + * key that is lower than or equal to , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebub_item_lookup_le(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebub_ofs_lookup_le(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the greatest + * key that is strictly lower than , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebub_item_lookup_lt(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebub_ofs_lookup_lt(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the lowest + * key key that is greater than or equal to , using node member + * and key member . Returns the pointer to the item, or NULL if not + * found. + */ +#define cebub_item_lookup_ge(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebub_ofs_lookup_ge(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the lowest + * key that is strictly greater than , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebub_item_lookup_gt(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebub_ofs_lookup_gt(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item immediately after item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define cebub_item_next(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebub_ofs_next(root, _kofs, &(item_ptr)->nname, len); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item immediately before item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define cebub_item_prev(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebub_ofs_prev(root, _kofs, &(item_ptr)->nname, len); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item using node member and + * key member , deletes it and returns it. If the item is NULL or absent + * from the tree, NULL is returned. + */ +#define cebub_item_delete(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + typeof(item_ptr) _item = (item_ptr); \ + struct ceb_node *_node = cebub_ofs_delete(root, _kofs, _item ? &_item->nname : NULL, len); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member , deletes it and + * returns it. If the key is not found in the tree, NULL is returned. + */ +#define cebub_item_pick(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebub_ofs_pick(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +#endif /* _CEBB_TREE_H */ diff --git a/include/import/cebib_tree.h b/include/import/cebib_tree.h new file mode 100644 index 000000000..75ee5b91d --- /dev/null +++ b/include/import/cebib_tree.h @@ -0,0 +1,425 @@ +/* + * Compact Elastic Binary Trees - exported functions operating on indirect blocks + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _CEBIB_TREE_H +#define _CEBIB_TREE_H + +#include "cebtree.h" + +/* simpler version */ +struct ceb_node *cebib_imm_insert(struct ceb_root **root, struct ceb_node *node, size_t len); +struct ceb_node *cebib_imm_first(struct ceb_root *const *root, size_t len); +struct ceb_node *cebib_imm_last(struct ceb_root *const *root, size_t len); +struct ceb_node *cebib_imm_lookup(struct ceb_root *const *root, const void *key, size_t len); +struct ceb_node *cebib_imm_lookup_le(struct ceb_root *const *root, const void *key, size_t len); +struct ceb_node *cebib_imm_lookup_lt(struct ceb_root *const *root, const void *key, size_t len); +struct ceb_node *cebib_imm_lookup_ge(struct ceb_root *const *root, const void *key, size_t len); +struct ceb_node *cebib_imm_lookup_gt(struct ceb_root *const *root, const void *key, size_t len); +struct ceb_node *cebib_imm_next_unique(struct ceb_root *const *root, struct ceb_node *node, size_t len); +struct ceb_node *cebib_imm_prev_unique(struct ceb_root *const *root, struct ceb_node *node, size_t len); +struct ceb_node *cebib_imm_next_dup(struct ceb_root *const *root, struct ceb_node *node, size_t len); +struct ceb_node *cebib_imm_prev_dup(struct ceb_root *const *root, struct ceb_node *node, size_t len); +struct ceb_node *cebib_imm_next(struct ceb_root *const *root, struct ceb_node *node, size_t len); +struct ceb_node *cebib_imm_prev(struct ceb_root *const *root, struct ceb_node *node, size_t len); +struct ceb_node *cebib_imm_delete(struct ceb_root **root, struct ceb_node *node, size_t len); +struct ceb_node *cebib_imm_pick(struct ceb_root **root, const void *key, size_t len); + +struct ceb_node *cebuib_imm_insert(struct ceb_root **root, struct ceb_node *node, size_t len); +struct ceb_node *cebuib_imm_first(struct ceb_root *const *root, size_t len); +struct ceb_node *cebuib_imm_last(struct ceb_root *const *root, size_t len); +struct ceb_node *cebuib_imm_lookup(struct ceb_root *const *root, const void *key, size_t len); +struct ceb_node *cebuib_imm_lookup_le(struct ceb_root *const *root, const void *key, size_t len); +struct ceb_node *cebuib_imm_lookup_lt(struct ceb_root *const *root, const void *key, size_t len); +struct ceb_node *cebuib_imm_lookup_ge(struct ceb_root *const *root, const void *key, size_t len); +struct ceb_node *cebuib_imm_lookup_gt(struct ceb_root *const *root, const void *key, size_t len); +struct ceb_node *cebuib_imm_next(struct ceb_root *const *root, struct ceb_node *node, size_t len); +struct ceb_node *cebuib_imm_prev(struct ceb_root *const *root, struct ceb_node *node, size_t len); +struct ceb_node *cebuib_imm_delete(struct ceb_root **root, struct ceb_node *node, size_t len); +struct ceb_node *cebuib_imm_pick(struct ceb_root **root, const void *key, size_t len); + +/* returns the pointer to the indirect void* key, not the pointer itself, + * or NULL if node is NULL (note that the indirect pointer cannot be NULL + * if node is non-NULL). + */ +static inline void *cebib_imm_key(const struct ceb_node *node) +{ + return node ? *(void **)_ceb_key_ptr(node, sizeof(struct ceb_node)) : NULL; +} + +/* version taking a key offset */ +struct ceb_node *cebib_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebib_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs, size_t len); +struct ceb_node *cebib_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs, size_t len); +struct ceb_node *cebib_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len); +struct ceb_node *cebib_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len); +struct ceb_node *cebib_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len); +struct ceb_node *cebib_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len); +struct ceb_node *cebib_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len); +struct ceb_node *cebib_ofs_next_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebib_ofs_prev_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebib_ofs_next_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebib_ofs_prev_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebib_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebib_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebib_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebib_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, const void *key, size_t len); + +struct ceb_node *cebuib_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebuib_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs, size_t len); +struct ceb_node *cebuib_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs, size_t len); +struct ceb_node *cebuib_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len); +struct ceb_node *cebuib_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len); +struct ceb_node *cebuib_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len); +struct ceb_node *cebuib_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len); +struct ceb_node *cebuib_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len); +struct ceb_node *cebuib_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebuib_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebuib_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node, size_t len); +struct ceb_node *cebuib_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, const void *key, size_t len); + +/* returns the pointer to the indirect void* key, not the pointer itself, + * or NULL if node is NULL (note that the indirect pointer cannot be NULL + * if node is non-NULL). + */ +static inline void *cebib_ofs_key(const struct ceb_node *node, ptrdiff_t kofs) +{ + return node ? *(void **)_ceb_key_ptr(node, kofs) : NULL; +} + +/* insert at root , the item using node member and key + * member , and length and returns either the inserted item, or + * the one that was found there. + */ +#define cebib_item_insert(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebib_ofs_insert(root, _kofs, &(item_ptr)->nname, len);\ + (typeof(item_ptr))((char *)(_node) - _nofs); \ +}) + +/* descend root , and return the first item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebib_item_first(root, nname, kname, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebib_ofs_first(root, _kofs, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* descend root , and return the last item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebib_item_last(root, nname, kname, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebib_ofs_last(root, _kofs, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member . Returns the + * pointer to the item, or NULL if not found. + */ +#define cebib_item_lookup(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebib_ofs_lookup(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the last of + * the greatest keys that are lower than or equal to , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define cebib_item_lookup_le(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebib_ofs_lookup_le(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the last of + * the greatest keys that are strictly lower than , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define cebib_item_lookup_lt(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebib_ofs_lookup_lt(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the first of + * the lowest keys key that are greater than or equal to , using node + * member and key member . Returns the pointer to the item, or + * NULL if not found. + */ +#define cebib_item_lookup_ge(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebib_ofs_lookup_ge(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the first of + * the lowest keys that are strictly greater than , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define cebib_item_lookup_gt(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebib_ofs_lookup_gt(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type after item + * that has a strictly greater key, using node member and key member + * , and returns either the found item, or NULL if none exists. + */ +#define cebib_item_next_unique(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebib_ofs_next_unique(root, _kofs, &(item_ptr)->nname, len); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item of type before item + * that has a strictly lower key, using node member and key member + * , and returns either the found item, or NULL if none exists. + */ +#define cebib_item_prev_unique(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebib_ofs_prev_unique(root, _kofs, &(item_ptr)->nname, len); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type after item + * that has exactly the same key, using node member and key member + * , and returns either the found item, or NULL if none exists. + */ +#define cebib_item_next_dup(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebib_ofs_next_dup(root, _kofs, &(item_ptr)->nname, len); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item of type before item + * that has exactly the same key, using node member and key member + * , and returns either the found item, or NULL if none exists. + */ +#define cebib_item_prev_dup(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebib_ofs_prev_dup(root, _kofs, &(item_ptr)->nname, len); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type immediately after + * item , using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define cebib_item_next(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebib_ofs_next(root, _kofs, &(item_ptr)->nname, len); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item of type immediately before + * item , using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define cebib_item_prev(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebib_ofs_prev(root, _kofs, &(item_ptr)->nname, len); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item using node member and + * key member , deletes it and returns it. If the item is NULL or absent + * from the tree, NULL is returned. + */ +#define cebib_item_delete(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + typeof(item_ptr) _item = (item_ptr); \ + struct ceb_node *_node = cebib_ofs_delete(root, _kofs, _item ? &_item->nname : NULL, len); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member , deletes it and + * returns it. If the key is not found in the tree, NULL is returned. + */ +#define cebib_item_pick(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebib_ofs_pick(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/*** versions using unique keys below ***/ + +/* insert at root , the item using node member and key + * member , and returns either the inserted item, or the one that was + * found there. + */ +#define cebuib_item_insert(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebuib_ofs_insert(root, _kofs, &(item_ptr)->nname, len); \ + (typeof(item_ptr))((char *)(_node) - _nofs); \ +}) + +/* descend root , and return the first item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebuib_item_first(root, nname, kname, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebuib_ofs_first(root, _kofs, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* descend root , and return the last item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebuib_item_last(root, nname, kname, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebuib_ofs_last(root, _kofs, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the key + * , using node member and key member . Returns the pointer + * to the item, or NULL if not found. + */ +#define cebuib_item_lookup(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebuib_ofs_lookup(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the greatest + * key that is lower than or equal to , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebuib_item_lookup_le(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebuib_ofs_lookup_le(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the greatest + * key that is strictly lower than , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebuib_item_lookup_lt(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebuib_ofs_lookup_lt(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the lowest + * key key that is greater than or equal to , using node member + * and key member . Returns the pointer to the item, or NULL if not + * found. + */ +#define cebuib_item_lookup_ge(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebuib_ofs_lookup_ge(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the lowest + * key that is strictly greater than , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebuib_item_lookup_gt(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebuib_ofs_lookup_gt(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type immediately after + * item , using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define cebuib_item_next(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebuib_ofs_next(root, _kofs, &(item_ptr)->nname, len); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item of type immediately before + * item , using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define cebuib_item_prev(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebuib_ofs_prev(root, _kofs, &(item_ptr)->nname, len); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item using node member and + * key member , deletes it and returns it. If the item is NULL or absent + * from the tree, NULL is returned. + */ +#define cebuib_item_delete(root, nname, kname, len, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + typeof(item_ptr) _item = (item_ptr); \ + struct ceb_node *_node = cebuib_ofs_delete(root, _kofs, _item ? &_item->nname : NULL, len); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member , deletes it and + * returns it. If the key is not found in the tree, NULL is returned. + */ +#define cebuib_item_pick(root, nname, kname, key, len, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebuib_ofs_pick(root, _kofs, key, len); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +#endif /* _CEBIB_TREE_H */ diff --git a/include/import/cebis_tree.h b/include/import/cebis_tree.h new file mode 100644 index 000000000..5d6fc1193 --- /dev/null +++ b/include/import/cebis_tree.h @@ -0,0 +1,431 @@ +/* + * Compact Elastic Binary Trees - exported functions operating on indirect strings + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _CEBIS_TREE_H +#define _CEBIS_TREE_H + +#include "cebtree.h" + +/* simpler version */ +struct ceb_node *cebis_imm_insert(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *cebis_imm_first(struct ceb_root *const *root); +struct ceb_node *cebis_imm_last(struct ceb_root *const *root); +struct ceb_node *cebis_imm_lookup(struct ceb_root *const *root, const void *key); +struct ceb_node *cebis_imm_lookup_le(struct ceb_root *const *root, const void *key); +struct ceb_node *cebis_imm_lookup_lt(struct ceb_root *const *root, const void *key); +struct ceb_node *cebis_imm_lookup_ge(struct ceb_root *const *root, const void *key); +struct ceb_node *cebis_imm_lookup_gt(struct ceb_root *const *root, const void *key); +struct ceb_node *cebis_imm_next_unique(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebis_imm_prev_unique(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebis_imm_next_dup(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebis_imm_prev_dup(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebis_imm_next(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebis_imm_prev(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebis_imm_delete(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *cebis_imm_pick(struct ceb_root **root, const void *key); + +struct ceb_node *cebuis_imm_insert(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *cebuis_imm_first(struct ceb_root *const *root); +struct ceb_node *cebuis_imm_last(struct ceb_root *const *root); +struct ceb_node *cebuis_imm_lookup(struct ceb_root *const *root, const void *key); +struct ceb_node *cebuis_imm_lookup_le(struct ceb_root *const *root, const void *key); +struct ceb_node *cebuis_imm_lookup_lt(struct ceb_root *const *root, const void *key); +struct ceb_node *cebuis_imm_lookup_ge(struct ceb_root *const *root, const void *key); +struct ceb_node *cebuis_imm_lookup_gt(struct ceb_root *const *root, const void *key); +struct ceb_node *cebuis_imm_next(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebuis_imm_prev(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebuis_imm_delete(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *cebuis_imm_pick(struct ceb_root **root, const void *key); + +/* generic dump function */ +void cebis_imm_default_dump(struct ceb_root *const *root, const char *label, const void *ctx, int sub); + +/* returns the pointer to the indirect char* key, not the pointer itself, + * or NULL if node is NULL (note that the indirect pointer cannot be NULL + * if node is non-NULL). + */ +static inline char *cebis_imm_key(const struct ceb_node *node) +{ + return node ? *(char **)_ceb_key_ptr(node, sizeof(struct ceb_node)) : NULL; +} + +/* version taking a key offset */ +struct ceb_node *cebis_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebis_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs); +struct ceb_node *cebis_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs); +struct ceb_node *cebis_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, const void *key); +struct ceb_node *cebis_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, const void *key); +struct ceb_node *cebis_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key); +struct ceb_node *cebis_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, const void *key); +struct ceb_node *cebis_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key); +struct ceb_node *cebis_ofs_next_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebis_ofs_prev_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebis_ofs_next_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebis_ofs_prev_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebis_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebis_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebis_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebis_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, const void *key); + +struct ceb_node *cebuis_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebuis_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs); +struct ceb_node *cebuis_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs); +struct ceb_node *cebuis_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, const void *key); +struct ceb_node *cebuis_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, const void *key); +struct ceb_node *cebuis_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key); +struct ceb_node *cebuis_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, const void *key); +struct ceb_node *cebuis_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key); +struct ceb_node *cebuis_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebuis_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebuis_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebuis_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, const void *key); + +/* generic dump function taking a key offset */ +void cebis_ofs_default_dump(struct ceb_root *const *root, ptrdiff_t kofs, const char *label, const void *ctx, int sub); + +/* returns the pointer to the indirect char* key, not the pointer itself, + * or NULL if node is NULL (note that the indirect pointer cannot be NULL + * if node is non-NULL). + */ +static inline char *cebis_ofs_key(const struct ceb_node *node, ptrdiff_t kofs) +{ + return node ? *(char **)_ceb_key_ptr(node, kofs) : NULL; +} + +/* insert at root , the item using node member and key + * member , and returns either the inserted item, or the one that was + * found there. + */ +#define cebis_item_insert(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebis_ofs_insert(root, _kofs, &(item_ptr)->nname); \ + (typeof(item_ptr))((char *)(_node) - _nofs); \ +}) + +/* descend root , and return the first item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebis_item_first(root, nname, kname, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebis_ofs_first(root, _kofs); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* descend root , and return the last item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebis_item_last(root, nname, kname, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebis_ofs_last(root, _kofs); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member . Returns the + * pointer to the item, or NULL if not found. + */ +#define cebis_item_lookup(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebis_ofs_lookup(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the last of + * the greatest keys that are lower than or equal to , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define cebis_item_lookup_le(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebis_ofs_lookup_le(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the last of + * the greatest keys that are strictly lower than , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define cebis_item_lookup_lt(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebis_ofs_lookup_lt(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the first of + * the lowest keys key that are greater than or equal to , using node + * member and key member . Returns the pointer to the item, or + * NULL if not found. + */ +#define cebis_item_lookup_ge(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebis_ofs_lookup_ge(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the first of + * the lowest keys that are strictly greater than , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define cebis_item_lookup_gt(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebis_ofs_lookup_gt(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item after item that has a + * strictly greater key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define cebis_item_next_unique(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebis_ofs_next_unique(root, _kofs, &(item_ptr)->nname);\ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item before item that has a + * strictly lower key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define cebis_item_prev_unique(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebis_ofs_prev_unique(root, _kofs, &(item_ptr)->nname);\ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item after item that has + * exactly the same key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define cebis_item_next_dup(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebis_ofs_next_dup(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item before item that has + * exactly the same key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define cebis_item_prev_dup(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebis_ofs_prev_dup(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item immediately after item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define cebis_item_next(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebis_ofs_next(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item immediately before item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define cebis_item_prev(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebis_ofs_prev(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item using node member and + * key member , deletes it and returns it. If the item is NULL or absent + * from the tree, NULL is returned. + */ +#define cebis_item_delete(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + typeof(item_ptr) _item = (item_ptr); \ + struct ceb_node *_node = cebis_ofs_delete(root, _kofs, _item ? &_item->nname : NULL); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member , deletes it and + * returns it. If the key is not found in the tree, NULL is returned. + */ +#define cebis_item_pick(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebis_ofs_pick(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/*** versions using unique keys below ***/ + +/* insert at root , the item using node member and key + * member , and returns either the inserted item, or the one that was + * found there. + */ +#define cebuis_item_insert(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebuis_ofs_insert(root, _kofs, &(item_ptr)->nname); \ + (typeof(item_ptr))((char *)(_node) - _nofs); \ +}) + +/* descend root , and return the first item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebuis_item_first(root, nname, kname, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebuis_ofs_first(root, _kofs); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* descend root , and return the last item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebuis_item_last(root, nname, kname, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebuis_ofs_last(root, _kofs); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the key + * , using node member and key member . Returns the pointer + * to the item, or NULL if not found. + */ +#define cebuis_item_lookup(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebuis_ofs_lookup(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the greatest + * key that is lower than or equal to , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebuis_item_lookup_le(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebuis_ofs_lookup_le(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the greatest + * key that is strictly lower than , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebuis_item_lookup_lt(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebuis_ofs_lookup_lt(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the lowest + * key key that is greater than or equal to , using node member + * and key member . Returns the pointer to the item, or NULL if not + * found. + */ +#define cebuis_item_lookup_ge(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebuis_ofs_lookup_ge(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the lowest + * key that is strictly greater than , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebuis_item_lookup_gt(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebuis_ofs_lookup_gt(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item immediately after item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define cebuis_item_next(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebuis_ofs_next(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item immediately before item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define cebuis_item_prev(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebuis_ofs_prev(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item using node member and + * key member , deletes it and returns it. If the item is NULL or absent + * from the tree, NULL is returned. + */ +#define cebuis_item_delete(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + typeof(item_ptr) _item = (item_ptr); \ + struct ceb_node *_node = cebuis_ofs_delete(root, _kofs, _item ? &_item->nname : NULL); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member , deletes it and + * returns it. If the key is not found in the tree, NULL is returned. + */ +#define cebuis_item_pick(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebuis_ofs_pick(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +#endif /* _CEBIS_TREE_H */ diff --git a/include/import/cebl_tree.h b/include/import/cebl_tree.h new file mode 100644 index 000000000..1ba4102e7 --- /dev/null +++ b/include/import/cebl_tree.h @@ -0,0 +1,425 @@ +/* + * Compact Elastic Binary Trees - exported functions operating on ulong keys + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _CEBL_TREE_H +#define _CEBL_TREE_H + +#include "cebtree.h" + +/* simpler version */ +struct ceb_node *cebl_imm_insert(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *cebl_imm_first(struct ceb_root *const *root); +struct ceb_node *cebl_imm_last(struct ceb_root *const *root); +struct ceb_node *cebl_imm_lookup(struct ceb_root *const *root, unsigned long key); +struct ceb_node *cebl_imm_lookup_le(struct ceb_root *const *root, unsigned long key); +struct ceb_node *cebl_imm_lookup_lt(struct ceb_root *const *root, unsigned long key); +struct ceb_node *cebl_imm_lookup_ge(struct ceb_root *const *root, unsigned long key); +struct ceb_node *cebl_imm_lookup_gt(struct ceb_root *const *root, unsigned long key); +struct ceb_node *cebl_imm_next_unique(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebl_imm_prev_unique(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebl_imm_next_dup(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebl_imm_prev_dup(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebl_imm_next(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebl_imm_prev(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebl_imm_delete(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *cebl_imm_pick(struct ceb_root **root, unsigned long key); + +struct ceb_node *cebul_imm_insert(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebul_imm_first(struct ceb_root *const *root); +struct ceb_node *cebul_imm_last(struct ceb_root *const *root); +struct ceb_node *cebul_imm_lookup(struct ceb_root *const *root, unsigned long key); +struct ceb_node *cebul_imm_lookup_le(struct ceb_root *const *root, unsigned long key); +struct ceb_node *cebul_imm_lookup_lt(struct ceb_root *const *root, unsigned long key); +struct ceb_node *cebul_imm_lookup_ge(struct ceb_root *const *root, unsigned long key); +struct ceb_node *cebul_imm_lookup_gt(struct ceb_root *const *root, unsigned long key); +struct ceb_node *cebul_imm_next(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebul_imm_prev(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebul_imm_delete(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *cebul_imm_pick(struct ceb_root **root, unsigned long key); + +/* generic dump function */ +void cebl_imm_default_dump(struct ceb_root **ceb_root, const char *label, const void *ctx, int sub); + +/* returns the pointer to the unsigned long key */ +static inline unsigned long *cebl_imm_key(const struct ceb_node *node) +{ + return (unsigned long *)ceb_key_ptr(node, sizeof(struct ceb_node)); +} + +/* version taking a key offset */ +struct ceb_node *cebl_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebl_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs); +struct ceb_node *cebl_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs); +struct ceb_node *cebl_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key); +struct ceb_node *cebl_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key); +struct ceb_node *cebl_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key); +struct ceb_node *cebl_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key); +struct ceb_node *cebl_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key); +struct ceb_node *cebl_ofs_next_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebl_ofs_prev_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebl_ofs_next_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebl_ofs_prev_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebl_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebl_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebl_ofs_delete(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebl_ofs_pick(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key); + +struct ceb_node *cebul_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebul_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs); +struct ceb_node *cebul_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs); +struct ceb_node *cebul_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key); +struct ceb_node *cebul_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key); +struct ceb_node *cebul_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key); +struct ceb_node *cebul_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key); +struct ceb_node *cebul_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key); +struct ceb_node *cebul_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebul_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebul_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebul_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, unsigned long key); + +/* generic dump function taking a key offset */ +void cebl_ofs_default_dump(struct ceb_root *const *root, ptrdiff_t kofs, const char *label, const void *ctx, int sub); + +/* returns the pointer to the unsigned long key */ +static inline unsigned long *cebl_ofs_key(const struct ceb_node *node, ptrdiff_t kofs) +{ + return (unsigned long *)ceb_key_ptr(node, kofs); +} + +/* insert at root , the item using node member and key + * member , and returns either the inserted item, or the one that was + * found there. + */ +#define cebl_item_insert(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebl_ofs_insert(root, _kofs, &(item_ptr)->nname); \ + (typeof(item_ptr))((char *)(_node) - _nofs); \ +}) + +/* descend root , and return the first item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebl_item_first(root, nname, kname, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebl_ofs_first(root, _kofs); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* descend root , and return the last item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebl_item_last(root, nname, kname, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebl_ofs_last(root, _kofs); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member . Returns the + * pointer to the item, or NULL if not found. + */ +#define cebl_item_lookup(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebl_ofs_lookup(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the last of + * the greatest keys that are lower than or equal to , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define cebl_item_lookup_le(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebl_ofs_lookup_le(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the last of + * the greatest keys that are strictly lower than , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define cebl_item_lookup_lt(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebl_ofs_lookup_lt(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the first of + * the lowest keys key that are greater than or equal to , using node + * member and key member . Returns the pointer to the item, or + * NULL if not found. + */ +#define cebl_item_lookup_ge(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebl_ofs_lookup_ge(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the first of + * the lowest keys that are strictly greater than , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define cebl_item_lookup_gt(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebl_ofs_lookup_gt(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item after item that has a + * strictly greater key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define cebl_item_next_unique(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebl_ofs_next_unique(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item before item that has a + * strictly lower key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define cebl_item_prev_unique(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebl_ofs_prev_unique(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item after item that has + * exactly the same key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define cebl_item_next_dup(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebl_ofs_next_dup(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item before item that has + * exactly the same key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define cebl_item_prev_dup(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebl_ofs_prev_dup(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item immediately after item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define cebl_item_next(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebl_ofs_next(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item immediately before item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define cebl_item_prev(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebl_ofs_prev(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item using node member and + * key member , deletes it and returns it. If the item is NULL or absent + * from the tree, NULL is returned. + */ +#define cebl_item_delete(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + typeof(item_ptr) _item = (item_ptr); \ + struct ceb_node *_node = cebl_ofs_delete(root, _kofs, _item ? &_item->nname : NULL); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member , deletes it and + * returns it. If the key is not found in the tree, NULL is returned. + */ +#define cebl_item_pick(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebl_ofs_pick(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/*** versions using unique keys below ***/ + +/* insert at root , the item using node member and key + * member , and returns either the inserted item, or the one that was + * found there. + */ +#define cebul_item_insert(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebul_ofs_insert(root, _kofs, &(item_ptr)->nname); \ + (typeof(item_ptr))((char *)(_node) - _nofs); \ +}) + +/* descend root , and return the first item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebul_item_first(root, nname, kname, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebul_ofs_first(root, _kofs); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* descend root , and return the last item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebul_item_last(root, nname, kname, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebul_ofs_last(root, _kofs); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the key + * , using node member and key member . Returns the pointer + * to the item, or NULL if not found. + */ +#define cebul_item_lookup(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebul_ofs_lookup(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the greatest + * key that is lower than or equal to , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebul_item_lookup_le(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebul_ofs_lookup_le(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the greatest + * key that is strictly lower than , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebul_item_lookup_lt(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebul_ofs_lookup_lt(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the lowest + * key key that is greater than or equal to , using node member + * and key member . Returns the pointer to the item, or NULL if not + * found. + */ +#define cebul_item_lookup_ge(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebul_ofs_lookup_ge(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the lowest + * key that is strictly greater than , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebul_item_lookup_gt(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebul_ofs_lookup_gt(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item immediately after item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define cebul_item_next(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebul_ofs_next(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item immediately before item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define cebul_item_prev(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebul_ofs_prev(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item using node member and + * key member , deletes it and returns it. If the item is NULL or absent + * from the tree, NULL is returned. + */ +#define cebul_item_delete(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + typeof(item_ptr) _item = (item_ptr); \ + struct ceb_node *_node = cebul_ofs_delete(root, _kofs, _item ? &_item->nname : NULL); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member , deletes it and + * returns it. If the key is not found in the tree, NULL is returned. + */ +#define cebul_item_pick(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebul_ofs_pick(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +#endif /* _CEBL_TREE_H */ diff --git a/include/import/cebs_tree.h b/include/import/cebs_tree.h new file mode 100644 index 000000000..0c1f0b657 --- /dev/null +++ b/include/import/cebs_tree.h @@ -0,0 +1,425 @@ +/* + * Compact Elastic Binary Trees - exported functions operating on string keys + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _CEBS_TREE_H +#define _CEBS_TREE_H + +#include "cebtree.h" + +/* simpler version */ +struct ceb_node *cebs_imm_insert(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *cebs_imm_first(struct ceb_root *const *root); +struct ceb_node *cebs_imm_last(struct ceb_root *const *root); +struct ceb_node *cebs_imm_lookup(struct ceb_root *const *root, const void *key); +struct ceb_node *cebs_imm_lookup_le(struct ceb_root *const *root, const void *key); +struct ceb_node *cebs_imm_lookup_lt(struct ceb_root *const *root, const void *key); +struct ceb_node *cebs_imm_lookup_ge(struct ceb_root *const *root, const void *key); +struct ceb_node *cebs_imm_lookup_gt(struct ceb_root *const *root, const void *key); +struct ceb_node *cebs_imm_next_unique(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebs_imm_prev_unique(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebs_imm_next_dup(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebs_imm_prev_dup(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebs_imm_next(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebs_imm_prev(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebs_imm_delete(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *cebs_imm_pick(struct ceb_root **root, const void *key); + +struct ceb_node *cebus_imm_insert(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *cebus_imm_first(struct ceb_root *const *root); +struct ceb_node *cebus_imm_last(struct ceb_root *const *root); +struct ceb_node *cebus_imm_lookup(struct ceb_root *const *root, const void *key); +struct ceb_node *cebus_imm_lookup_le(struct ceb_root *const *root, const void *key); +struct ceb_node *cebus_imm_lookup_lt(struct ceb_root *const *root, const void *key); +struct ceb_node *cebus_imm_lookup_ge(struct ceb_root *const *root, const void *key); +struct ceb_node *cebus_imm_lookup_gt(struct ceb_root *const *root, const void *key); +struct ceb_node *cebus_imm_next(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebus_imm_prev(struct ceb_root *const *root, struct ceb_node *node); +struct ceb_node *cebus_imm_delete(struct ceb_root **root, struct ceb_node *node); +struct ceb_node *cebus_imm_pick(struct ceb_root **root, const void *key); + +/* generic dump function */ +void cebs_imm_default_dump(struct ceb_root *const *root, const char *label, const void *ctx, int sub); + +/* returns the pointer to the char* key that follows the node */ +static inline char *cebs_imm_key(const struct ceb_node *node) +{ + return (char *)ceb_key_ptr(node, sizeof(struct ceb_node)); +} + +/* version taking a key offset */ +struct ceb_node *cebs_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebs_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs); +struct ceb_node *cebs_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs); +struct ceb_node *cebs_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, const void *key); +struct ceb_node *cebs_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, const void *key); +struct ceb_node *cebs_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key); +struct ceb_node *cebs_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, const void *key); +struct ceb_node *cebs_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key); +struct ceb_node *cebs_ofs_next_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebs_ofs_prev_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebs_ofs_next_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebs_ofs_prev_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebs_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebs_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebs_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebs_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, const void *key); + +struct ceb_node *cebus_ofs_insert(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebus_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs); +struct ceb_node *cebus_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs); +struct ceb_node *cebus_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, const void *key); +struct ceb_node *cebus_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, const void *key); +struct ceb_node *cebus_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key); +struct ceb_node *cebus_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, const void *key); +struct ceb_node *cebus_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key); +struct ceb_node *cebus_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebus_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebus_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node); +struct ceb_node *cebus_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, const void *key); + +/* generic dump function taking a key offset */ +void cebs_ofs_default_dump(struct ceb_root *const *root, ptrdiff_t kofs, const char *label, const void *ctx, int sub); + +/* returns the pointer to the char* key*/ +static inline char *cebs_ofs_key(const struct ceb_node *node, ptrdiff_t kofs) +{ + return (char *)ceb_key_ptr(node, kofs); +} + +/* insert at root , the item using node member and key + * member , and returns either the inserted item, or the one that was + * found there. + */ +#define cebs_item_insert(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebs_ofs_insert(root, _kofs, &(item_ptr)->nname); \ + (typeof(item_ptr))((char *)(_node) - _nofs); \ +}) + +/* descend root , and return the first item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebs_item_first(root, nname, kname, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebs_ofs_first(root, _kofs); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* descend root , and return the last item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebs_item_last(root, nname, kname, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebs_ofs_last(root, _kofs); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member . Returns the + * pointer to the item, or NULL if not found. + */ +#define cebs_item_lookup(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebs_ofs_lookup(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the last of + * the greatest keys that are lower than or equal to , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define cebs_item_lookup_le(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebs_ofs_lookup_le(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the last of + * the greatest keys that are strictly lower than , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define cebs_item_lookup_lt(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebs_ofs_lookup_lt(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the first of + * the lowest keys key that are greater than or equal to , using node + * member and key member . Returns the pointer to the item, or + * NULL if not found. + */ +#define cebs_item_lookup_ge(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebs_ofs_lookup_ge(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the first of + * the lowest keys that are strictly greater than , using node member + * and key member . Returns the pointer to the item, or NULL if + * not found. + */ +#define cebs_item_lookup_gt(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebs_ofs_lookup_gt(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item after item that has a + * strictly greater key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define cebs_item_next_unique(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebs_ofs_next_unique(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item before item that has a + * strictly lower key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define cebs_item_prev_unique(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebs_ofs_prev_unique(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item after item that has + * exactly the same key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define cebs_item_next_dup(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebs_ofs_next_dup(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item before item that has + * exactly the same key, using node member and key member , and + * returns either the found item, or NULL if none exists. + */ +#define cebs_item_prev_dup(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebs_ofs_prev_dup(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item immediately after item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define cebs_item_next(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebs_ofs_next(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item immediately before item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define cebs_item_prev(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebs_ofs_prev(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item using node member and + * key member , deletes it and returns it. If the item is NULL or absent + * from the tree, NULL is returned. + */ +#define cebs_item_delete(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + typeof(item_ptr) _item = (item_ptr); \ + struct ceb_node *_node = cebs_ofs_delete(root, _kofs, _item ? &_item->nname : NULL); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member , deletes it and + * returns it. If the key is not found in the tree, NULL is returned. + */ +#define cebs_item_pick(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebs_ofs_pick(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/*** versions using unique keys below ***/ + +/* insert at root , the item using node member and key + * member , and returns either the inserted item, or the one that was + * found there. + */ +#define cebus_item_insert(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebus_ofs_insert(root, _kofs, &(item_ptr)->nname); \ + (typeof(item_ptr))((char *)(_node) - _nofs); \ +}) + +/* descend root , and return the first item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebus_item_first(root, nname, kname, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebus_ofs_first(root, _kofs); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* descend root , and return the last item of type , using node + * member and key member , or NULL if the tree is empty. + */ +#define cebus_item_last(root, nname, kname, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebus_ofs_last(root, _kofs); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the key + * , using node member and key member . Returns the pointer + * to the item, or NULL if not found. + */ +#define cebus_item_lookup(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebus_ofs_lookup(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the greatest + * key that is lower than or equal to , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebus_item_lookup_le(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebus_ofs_lookup_le(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the greatest + * key that is strictly lower than , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebus_item_lookup_lt(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebus_ofs_lookup_lt(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the lowest + * key key that is greater than or equal to , using node member + * and key member . Returns the pointer to the item, or NULL if not + * found. + */ +#define cebus_item_lookup_ge(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebus_ofs_lookup_ge(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item of type which contains the lowest + * key that is strictly greater than , using node member and key + * member . Returns the pointer to the item, or NULL if not found. + */ +#define cebus_item_lookup_gt(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebus_ofs_lookup_gt(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item immediately after item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define cebus_item_next(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebus_ofs_next(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the last item immediately before item , + * using node member and key member , and returns either the + * found item, or NULL if none exists. + */ +#define cebus_item_prev(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + struct ceb_node *_node = cebus_ofs_prev(root, _kofs, &(item_ptr)->nname); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the item using node member and + * key member , deletes it and returns it. If the item is NULL or absent + * from the tree, NULL is returned. + */ +#define cebus_item_delete(root, nname, kname, item_ptr) ({ \ + ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \ + ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \ + typeof(item_ptr) _item = (item_ptr); \ + struct ceb_node *_node = cebus_ofs_delete(root, _kofs, _item ? &_item->nname : NULL); \ + _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \ +}) + +/* lookup from root , the first item of type which contains the + * key , using node member and key member , deletes it and + * returns it. If the key is not found in the tree, NULL is returned. + */ +#define cebus_item_pick(root, nname, kname, key, type) ({ \ + ptrdiff_t _nofs = offsetof(type, nname); \ + ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \ + struct ceb_node *_node = cebus_ofs_pick(root, _kofs, key); \ + _node ? (type *)((char *)(_node) - _nofs) : NULL; \ +}) + +#endif /* _CEBS_TREE_H */ diff --git a/include/import/cebtree-prv.h b/include/import/cebtree-prv.h index 0309e7891..221968af5 100644 --- a/include/import/cebtree-prv.h +++ b/include/import/cebtree-prv.h @@ -1,7 +1,7 @@ /* * Compact Elastic Binary Trees - internal functions and types * - * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -63,7 +63,7 @@ * the same split bit as its parent node, it is necessary its associated leaf * * When descending along the tree, it is possible to know that a search key is - * not present, because its XOR with both of the branches is strictly higher + * not present, because its XOR with both of the branches is stricly higher * than the inter-branch XOR. The reason is simple : the inter-branch XOR will * have its highest bit set indicating the split bit. Since it's the bit that * differs between the two branches, the key cannot have it both set and @@ -83,23 +83,296 @@ #ifndef _CEBTREE_PRV_H #define _CEBTREE_PRV_H +#include #include +#include #include #include "cebtree.h" -/* If DEBUG is set, we'll print additional debugging info during the descent */ -#ifdef DEBUG -#define CEBDBG(x, ...) fprintf(stderr, x, ##__VA_ARGS__) -#else -#define CEBDBG(x, ...) do { } while (0) +/* A few utility functions and macros that we need below */ + +/* This is used to test if a macro is defined and equals 1. The principle is + * that the macro is passed as a value and its value concatenated to the word + * "comma_for_one" to form a new macro name. The macro "comma_for_one1" equals + * one comma, which, once used in an argument, will shift all of them by one, + * so that we can use this to concatenate both a 1 and a 0 and always pick the + * second one. + */ +#define comma_for_one1 , +#define _____equals_1(x, y, ...) (y) +#define ____equals_1(x, ...) _____equals_1(x, 0) +#define ___equals_1(x) ____equals_1(comma_for_one ## x 1) +#define __equals_1(x) ___equals_1(x) + +/* gcc 5 and clang 3 brought __has_attribute(), which is not well documented in + * the case of gcc, but is convenient since handled at the preprocessor level. + * In both cases it's possible to test for __has_attribute() using ifdef. When + * not defined we remap this to the __has_attribute_ macro so that we'll + * later be able to implement on a per-compiler basis those which are missing, + * by defining __has_attribute_ to 1. + */ +#ifndef __has_attribute +#define __has_attribute(x) __equals_1(__has_attribute_ ## x) #endif +/* gcc 10 and clang 3 brought __has_builtin() to test if a builtin exists. + * Just like above, if it doesn't exist, we remap it to a macro allowing us + * to define these ourselves by defining __has_builtin_ to 1. + */ +#ifndef __has_builtin +#define __has_builtin(x) __equals_1(__has_builtin_ ## x) +#endif + +#if !defined(__GNUC__) +/* Some versions of glibc irresponsibly redefine __attribute__() to empty for + * non-gcc compilers, and as such, silently break all constructors with other + * other compilers. Let's make sure such incompatibilities are detected if any, + * or that the attribute is properly enforced. + */ +#undef __attribute__ +#define __attribute__(x) __attribute__(x) +#endif + +/* Define the missing __builtin_prefetch() for tcc. */ +#if defined(__TINYC__) && !defined(__builtin_prefetch) +#define __builtin_prefetch(addr, ...) do { } while (0) +#endif + +/* __builtin_unreachable() was added in gcc 4.5 */ +#if defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define __has_builtin___builtin_unreachable 1 /* make __builtin_unreachable() return 1 */ +#elif !__has_builtin(__builtin_unreachable) +#define __builtin_unreachable() do { } while (1) +#endif + +/* FLSNZ: find last set bit for non-zero value. "Last" here means the highest + * one. It returns a value from 1 to 32 for 1<<0 to 1<<31. + */ + +#if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2))) +/* gcc >= 4.2 brings __builtin_clz() and __builtin_clzl(), also usable for + * non-x86. However on x86 gcc does bad stuff if not properly handled. It xors + * the bsr return with 31 and since it doesn't know how to deal with a xor + * followed by a negation, it adds two instructions when using 32-clz(). Thus + * instead we first cancel the xor using another one then add one. Even on ARM + * that provides a clz instruction, it saves one register to proceed like this. + */ + +#define flsnz8(x) flsnz32((unsigned char)x) + +static inline __attribute__((always_inline)) unsigned int flsnz32(unsigned int x) +{ + return (__builtin_clz(x) ^ 31) + 1; +} + +static inline __attribute__((always_inline)) unsigned int flsnz64(unsigned long long x) +{ + return (__builtin_clzll(x) ^ 63) + 1; +} + +#elif (defined(__i386__) || defined(__x86_64__)) && !defined(__atom__) /* Not gcc >= 4.2 */ +/* DO NOT USE ON ATOM! The instruction is emulated and is several times slower + * than doing the math by hand. + */ +#define flsnz8(x) flsnz32((unsigned char)x) + +static inline __attribute__((always_inline)) unsigned int flsnz32(unsigned int x) +{ + unsigned int r; + __asm__("bsrl %1,%0\n" + : "=r" (r) : "rm" (x)); + return r + 1; +} + +#if defined(__x86_64__) +static inline __attribute__((always_inline)) unsigned int flsnz64(unsigned long long x) +{ + unsigned long long r; + __asm__("bsrq %1,%0\n" + : "=r" (r) : "rm" (x)); + return r + 1; +} +#else +static inline __attribute__((always_inline)) unsigned int flsnz64(unsigned long long x) +{ + unsigned int h; + unsigned int bits = 32; + + h = x >> 32; + if (!h) { + h = x; + bits = 0; + } + return flsnz32(h) + bits; +} +#endif + +#else /* Neither gcc >= 4.2 nor x86, use generic code */ + +static inline __attribute__((always_inline)) unsigned int flsnz8(unsigned int x) +{ + unsigned int ret = 0; + if (x >> 4) { x >>= 4; ret += 4; } + return ret + ((0xFFFFAA50U >> (x << 1)) & 3) + 1; +} + +#define flsnz32(___a) ({ \ + register unsigned int ___x, ___bits = 0; \ + ___x = (___a); \ + if (___x & 0xffff0000) { ___x &= 0xffff0000; ___bits += 16;} \ + if (___x & 0xff00ff00) { ___x &= 0xff00ff00; ___bits += 8;} \ + if (___x & 0xf0f0f0f0) { ___x &= 0xf0f0f0f0; ___bits += 4;} \ + if (___x & 0xcccccccc) { ___x &= 0xcccccccc; ___bits += 2;} \ + if (___x & 0xaaaaaaaa) { ___x &= 0xaaaaaaaa; ___bits += 1;} \ + ___bits + 1; \ + }) + +static inline __attribute__((always_inline)) unsigned int flsnz64(unsigned long long x) +{ + unsigned int h; + unsigned int bits = 32; + + h = x >> 32; + if (!h) { + h = x; + bits = 0; + } + return flsnz32(h) + bits; +} + +#endif + +#define flsnz_long(x) ((sizeof(long) > 4) ? flsnz64(x) : flsnz32(x)) +#define flsnz(x) ((sizeof(x) > 4) ? flsnz64(x) : (sizeof(x) > 1) ? flsnz32(x) : flsnz8(x)) + +/* Compare blocks and byte-to-byte, from bit to bit . + * Return the number of equal bits between strings, assuming that the first + * bits are already identical. It is possible to return slightly more + * than bits if does not stop on a byte boundary and we find exact + * bytes. Note that parts or all of bits may be rechecked. It is only + * passed here as a hint to speed up the check. + */ +static +#if defined(__OPTIMIZE_SIZE__) +__attribute__((noinline)) +#else +inline __attribute__((always_inline)) +#endif +size_t equal_bits(const unsigned char *a, + const unsigned char *b, + size_t ignore, size_t len) +{ + for (ignore >>= 3, a += ignore, b += ignore, ignore <<= 3; + ignore < len; ) { + unsigned char c; + + a++; b++; + ignore += 8; + c = b[-1] ^ a[-1]; + + if (c) { + /* OK now we know that old and new differ at byte and that holds + * the bit differences. We have to find what bit is differing and report + * it as the number of identical bits. Note that low bit numbers are + * assigned to high positions in the byte, as we compare them as strings. + */ + ignore -= flsnz_long(c); + break; + } + } + return ignore; +} + +/* Compare strings and byte-to-byte, from bit to the last 0. + * Return the number of equal bits between strings, assuming that the first + * bits are already identical. Note that parts or all of bits + * may be rechecked. It is only passed here as a hint to speed up the check. + * The caller is responsible for not passing an value larger than any + * of the two strings. However, referencing any bit from the trailing zero is + * permitted. Equal strings are reported as a negative number of bits, which + * indicates the end was reached. + */ +static +#if defined(__OPTIMIZE_SIZE__) +__attribute__((noinline)) +#else +inline __attribute__((always_inline)) +#endif +size_t string_equal_bits(const unsigned char *a, + const unsigned char *b, + size_t ignore) +{ + unsigned char c, d; + size_t beg; + + beg = ignore >> 3; + + /* skip known and identical bits. We stop at the first different byte + * or at the first zero we encounter on either side. + */ + for (;; beg += 2) { + c = a[beg + 0]; + d = b[beg + 0]; + c ^= d; + if (__builtin_expect(c != 0, 0)) + goto brk1; + if (!d) + goto same; + c = a[beg + 1]; + d = b[beg + 1]; + c ^= d; + if (__builtin_expect(c != 0, 0)) + goto brk2; + if (!d) + goto same; + } +brk2: + beg++; +brk1: + + /* OK now we know that a and b differ at byte . + * We have to find what bit is differing and report it as the number of + * identical bits. Note that low bit numbers are assigned to high positions + * in the byte, as we compare them as strings. + */ + return (beg << 3) + ((flsnz(c) - 1) ^ 7); +same: + return (size_t)-1; +} + +/* pointer tagging / untagging, to turn ceb_root to ceb_node and conversely */ + +/* tag an untagged pointer (node -> root) */ +static inline struct ceb_root *_ceb_dotag(const struct ceb_node *node, const uintptr_t tag) +{ + return (struct ceb_root *)((uintptr_t)node + tag); +} + +/* untag a tagged pointer (root -> node) */ +static inline struct ceb_node *_ceb_untag(const struct ceb_root *node, const uintptr_t tag) +{ + return (struct ceb_node *)((uintptr_t)node - tag); +} + +/* clear a pointer's tag, regardless of its previous value */ +static inline struct ceb_node *_ceb_clrtag(const struct ceb_root *node) +{ + return (struct ceb_node *)((uintptr_t)node & ~(uintptr_t)1); +} + +/* report the pointer's tag */ +static inline uintptr_t _ceb_gettag(const struct ceb_root *node) +{ + return (uintptr_t)node & (uintptr_t)1; +} + /* These macros are used by upper level files to create two variants of their * exported functions: * - one which uses sizeof(struct ceb_node) as the key offset, for nodes with - * adjacent keys ; these ones are named (root, ...) + * adjacent keys ; these ones are named (root, ...). This is + * defined when CEB_USE_BASE is defined. * - one with an explicit key offset passed by the caller right after the - * root. + * root. This is defined when CEB_USE_OFST is defined. * Both rely on a forced inline version with a body that immediately follows * the declaration, so that the declaration looks like a single decorated * function while 2 are built in practice. There are variants for the basic one @@ -108,45 +381,82 @@ * first variant, it's always replaced by sizeof(struct ceb_node) in the calls * to the inline version. */ -#define CEB_FDECL2(type, pfx, sfx, type1, arg1, type2, arg2) \ +#if defined(CEB_USE_BASE) +# define _CEB_DEF_BASE(x) x +#else +# define _CEB_DEF_BASE(x) +#endif + +#if defined(CEB_USE_OFST) +# define _CEB_DEF_OFST(x) x +#else +# define _CEB_DEF_OFST(x) +#endif + +#define CEB_FDECL2(type, pfx, sfx, type1, arg1, type2, arg2) \ + _CEB_FDECL2(type, pfx, sfx, type1, arg1, type2, arg2) + +#define _CEB_FDECL2(type, pfx, sfx, type1, arg1, type2, arg2) \ static inline __attribute__((always_inline)) \ type _##pfx##sfx(type1 arg1, type2 arg2); \ - type pfx##sfx(type1 arg1) { \ + _CEB_DEF_BASE(type pfx##_imm##sfx(type1 arg1) { \ return _##pfx##sfx(arg1, sizeof(struct ceb_node)); \ - } \ - type pfx##_ofs##sfx(type1 arg1, type2 arg2) { \ + }) \ + _CEB_DEF_OFST(type pfx##_ofs##sfx(type1 arg1, type2 arg2) { \ return _##pfx##sfx(arg1, arg2); \ - } \ + }) \ static inline __attribute__((always_inline)) \ type _##pfx##sfx(type1 arg1, type2 arg2) /* function body follows */ #define CEB_FDECL3(type, pfx, sfx, type1, arg1, type2, arg2, type3, arg3) \ + _CEB_FDECL3(type, pfx, sfx, type1, arg1, type2, arg2, type3, arg3) + +#define _CEB_FDECL3(type, pfx, sfx, type1, arg1, type2, arg2, type3, arg3) \ static inline __attribute__((always_inline)) \ type _##pfx##sfx(type1 arg1, type2 arg2, type3 arg3); \ - type pfx##sfx(type1 arg1, type3 arg3) { \ + _CEB_DEF_BASE(type pfx##_imm##sfx(type1 arg1, type3 arg3) { \ return _##pfx##sfx(arg1, sizeof(struct ceb_node), arg3); \ - } \ - type pfx##_ofs##sfx(type1 arg1, type2 arg2, type3 arg3) { \ + }) \ + _CEB_DEF_OFST(type pfx##_ofs##sfx(type1 arg1, type2 arg2, type3 arg3) { \ return _##pfx##sfx(arg1, arg2, arg3); \ - } \ + }) \ static inline __attribute__((always_inline)) \ type _##pfx##sfx(type1 arg1, type2 arg2, type3 arg3) /* function body follows */ #define CEB_FDECL4(type, pfx, sfx, type1, arg1, type2, arg2, type3, arg3, type4, arg4) \ + _CEB_FDECL4(type, pfx, sfx, type1, arg1, type2, arg2, type3, arg3, type4, arg4) + +#define _CEB_FDECL4(type, pfx, sfx, type1, arg1, type2, arg2, type3, arg3, type4, arg4) \ static inline __attribute__((always_inline)) \ type _##pfx##sfx(type1 arg1, type2 arg2, type3 arg3, type4 arg4); \ - type pfx##sfx(type1 arg1, type3 arg3, type4 arg4) { \ + _CEB_DEF_BASE(type pfx##_imm##sfx(type1 arg1, type3 arg3, type4 arg4) { \ return _##pfx##sfx(arg1, sizeof(struct ceb_node), arg3, arg4); \ - } \ - type pfx##_ofs##sfx(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + }) \ + _CEB_DEF_OFST(type pfx##_ofs##sfx(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ return _##pfx##sfx(arg1, arg2, arg3, arg4); \ - } \ + }) \ static inline __attribute__((always_inline)) \ type _##pfx##sfx(type1 arg1, type2 arg2, type3 arg3, type4 arg4) /* function body follows */ +#define CEB_FDECL5(type, pfx, sfx, type1, arg1, type2, arg2, type3, arg3, type4, arg4, type5, arg5) \ + _CEB_FDECL5(type, pfx, sfx, type1, arg1, type2, arg2, type3, arg3, type4, arg4, type5, arg5) + +#define _CEB_FDECL5(type, pfx, sfx, type1, arg1, type2, arg2, type3, arg3, type4, arg4, type5, arg5) \ + static inline __attribute__((always_inline)) \ + type _##pfx##sfx(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5); \ + _CEB_DEF_BASE(type pfx##_imm##sfx(type1 arg1, type3 arg3, type4 arg4, type5 arg5) { \ + return _##pfx##sfx(arg1, sizeof(struct ceb_node), arg3, arg4, arg5); \ + }) \ + _CEB_DEF_OFST(type pfx##_ofs##sfx(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) { \ + return _##pfx##sfx(arg1, arg2, arg3, arg4, arg5); \ + }) \ + static inline __attribute__((always_inline)) \ + type _##pfx##sfx(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) + /* function body follows */ + /* tree walk method: key, left, right */ enum ceb_walk_meth { CEB_WM_FST, /* look up "first" (walk left only) */ @@ -185,180 +495,6 @@ union ceb_key_storage { /* returns the ceb_key_storage pointer for node and offset */ #define NODEK(n, o) ((union ceb_key_storage*)(((char *)(n)) + (o))) -/* Returns the xor (or common length) between the two sides and if both - * are non-null, otherwise between the first non-null one and the value in the - * associate key. As a reminder, memory blocks place their length in key_u64. - * This is only intended for internal use, essentially for debugging. - * - * contains the offset between the key and the node's base. When simply - * adjacent, this would just be sizeof(ceb_node). - */ -__attribute__((unused)) -static inline uint64_t _xor_branches(ptrdiff_t kofs, enum ceb_key_type key_type, uint32_t key_u32, - uint64_t key_u64, const void *key_ptr, - const struct ceb_node *l, - const struct ceb_node *r) -{ - if (l && r) { - if (key_type == CEB_KT_MB) - return equal_bits(NODEK(l, kofs)->mb, NODEK(r, kofs)->mb, 0, key_u64 << 3); - else if (key_type == CEB_KT_IM) - return equal_bits(NODEK(l, kofs)->mb, NODEK(r, kofs)->ptr, 0, key_u64 << 3); - else if (key_type == CEB_KT_ST) - return string_equal_bits(NODEK(l, kofs)->str, NODEK(r, kofs)->str, 0); - else if (key_type == CEB_KT_IS) - return string_equal_bits(NODEK(l, kofs)->ptr, NODEK(r, kofs)->ptr, 0); - else if (key_type == CEB_KT_U64) - return NODEK(l, kofs)->u64 ^ NODEK(r, kofs)->u64; - else if (key_type == CEB_KT_U32) - return NODEK(l, kofs)->u32 ^ NODEK(r, kofs)->u32; - else if (key_type == CEB_KT_ADDR) - return ((uintptr_t)l ^ (uintptr_t)r); - else - return 0; - } - - if (!l) - l = r; - - if (key_type == CEB_KT_MB) - return equal_bits(key_ptr, NODEK(l, kofs)->mb, 0, key_u64 << 3); - else if (key_type == CEB_KT_IM) - return equal_bits(key_ptr, NODEK(l, kofs)->ptr, 0, key_u64 << 3); - else if (key_type == CEB_KT_ST) - return string_equal_bits(key_ptr, NODEK(l, kofs)->str, 0); - else if (key_type == CEB_KT_IS) - return string_equal_bits(key_ptr, NODEK(l, kofs)->ptr, 0); - else if (key_type == CEB_KT_U64) - return key_u64 ^ NODEK(l, kofs)->u64; - else if (key_type == CEB_KT_U32) - return key_u32 ^ NODEK(l, kofs)->u32; - else if (key_type == CEB_KT_ADDR) - return ((uintptr_t)key_ptr ^ (uintptr_t)r); - else - return 0; -} - -#ifdef DEBUG -__attribute__((unused)) -static void dbg(int line, - const char *pfx, - enum ceb_walk_meth meth, - ptrdiff_t kofs, - enum ceb_key_type key_type, - struct ceb_node * const *root, - const struct ceb_node *p, - uint32_t key_u32, - uint64_t key_u64, - const void *key_ptr, - uint32_t px32, - uint64_t px64, - size_t plen) -{ - const char *meths[] = { - [CEB_WM_FST] = "FST", - [CEB_WM_NXT] = "NXT", - [CEB_WM_PRV] = "PRV", - [CEB_WM_LST] = "LST", - [CEB_WM_KEQ] = "KEQ", - [CEB_WM_KGE] = "KGE", - [CEB_WM_KGT] = "KGT", - [CEB_WM_KLE] = "KLE", - [CEB_WM_KLT] = "KLT", - [CEB_WM_KNX] = "KNX", - [CEB_WM_KPR] = "KPR", - }; - const char *ktypes[] = { - [CEB_KT_ADDR] = "ADDR", - [CEB_KT_U32] = "U32", - [CEB_KT_U64] = "U64", - [CEB_KT_MB] = "MB", - [CEB_KT_IM] = "IM", - [CEB_KT_ST] = "ST", - [CEB_KT_IS] = "IS", - }; - const char *kstr __attribute__((unused)) = ktypes[key_type]; - const char *mstr __attribute__((unused)) = meths[meth]; - long long nlen __attribute__((unused)) = 0; - long long llen __attribute__((unused)) = 0; - long long rlen __attribute__((unused)) = 0; - long long xlen __attribute__((unused)) = 0; - - if (p) - nlen = _xor_branches(kofs, key_type, key_u32, key_u64, key_ptr, p, NULL); - - if (p && p->b[0]) - llen = _xor_branches(kofs, key_type, key_u32, key_u64, key_ptr, p->b[0], NULL); - - if (p && p->b[1]) - rlen = _xor_branches(kofs, key_type, key_u32, key_u64, key_ptr, NULL, p->b[1]); - - if (p && p->b[0] && p->b[1]) - xlen = _xor_branches(kofs, key_type, key_u32, key_u64, key_ptr, p->b[0], p->b[1]); - - switch (key_type) { - case CEB_KT_U32: - CEBDBG("%04d (%8s) m=%s.%s key=%#x root=%p pxor=%#x p=%p,%#x(^%#llx) l=%p,%#x(^%#llx) r=%p,%#x(^%#llx) l^r=%#llx\n", - line, pfx, kstr, mstr, key_u32, root, px32, - p, p ? NODEK(p, kofs)->u32 : 0, nlen, - p ? p->b[0] : NULL, p ? NODEK(p->b[0], kofs)->u32 : 0, llen, - p ? p->b[1] : NULL, p ? NODEK(p->b[1], kofs)->u32 : 0, rlen, - xlen); - break; - case CEB_KT_U64: - CEBDBG("%04d (%8s) m=%s.%s key=%#llx root=%p pxor=%#llx p=%p,%#llx(^%#llx) l=%p,%#llx(^%#llx) r=%p,%#llx(^%#llx) l^r=%#llx\n", - line, pfx, kstr, mstr, (long long)key_u64, root, (long long)px64, - p, (long long)(p ? NODEK(p, kofs)->u64 : 0), nlen, - p ? p->b[0] : NULL, (long long)(p ? NODEK(p->b[0], kofs)->u64 : 0), llen, - p ? p->b[1] : NULL, (long long)(p ? NODEK(p->b[1], kofs)->u64 : 0), rlen, - xlen); - break; - case CEB_KT_MB: - CEBDBG("%04d (%8s) m=%s.%s key=%p root=%p plen=%ld p=%p,%p(^%llu) l=%p,%p(^%llu) r=%p,%p(^%llu) l^r=%llu\n", - line, pfx, kstr, mstr, key_ptr, root, (long)plen, - p, p ? NODEK(p, kofs)->mb : 0, nlen, - p ? p->b[0] : NULL, p ? NODEK(p->b[0], kofs)->mb : 0, llen, - p ? p->b[1] : NULL, p ? NODEK(p->b[1], kofs)->mb : 0, rlen, - xlen); - break; - case CEB_KT_IM: - CEBDBG("%04d (%8s) m=%s.%s key=%p root=%p plen=%ld p=%p,%p(^%llu) l=%p,%p(^%llu) r=%p,%p(^%llu) l^r=%llu\n", - line, pfx, kstr, mstr, key_ptr, root, (long)plen, - p, p ? NODEK(p, kofs)->ptr : 0, nlen, - p ? p->b[0] : NULL, p ? NODEK(p->b[0], kofs)->ptr : 0, llen, - p ? p->b[1] : NULL, p ? NODEK(p->b[1], kofs)->ptr : 0, rlen, - xlen); - break; - case CEB_KT_ST: - CEBDBG("%04d (%8s) m=%s.%s key='%s' root=%p plen=%ld p=%p,%s(^%llu) l=%p,%s(^%llu) r=%p,%s(^%llu) l^r=%llu\n", - line, pfx, kstr, mstr, key_ptr ? (const char *)key_ptr : "", root, (long)plen, - p, p ? (const char *)NODEK(p, kofs)->str : "-", nlen, - p ? p->b[0] : NULL, p ? (const char *)NODEK(p->b[0], kofs)->str : "-", llen, - p ? p->b[1] : NULL, p ? (const char *)NODEK(p->b[1], kofs)->str : "-", rlen, - xlen); - break; - case CEB_KT_IS: - CEBDBG("%04d (%8s) m=%s.%s key='%s' root=%p plen=%ld p=%p,%s(^%llu) l=%p,%s(^%llu) r=%p,%s(^%llu) l^r=%llu\n", - line, pfx, kstr, mstr, key_ptr ? (const char *)key_ptr : "", root, (long)plen, - p, p ? (const char *)NODEK(p, kofs)->ptr : "-", nlen, - p ? p->b[0] : NULL, p ? (const char *)NODEK(p->b[0], kofs)->ptr : "-", llen, - p ? p->b[1] : NULL, p ? (const char *)NODEK(p->b[1], kofs)->ptr : "-", rlen, - xlen); - break; - case CEB_KT_ADDR: - /* key type is the node's address */ - CEBDBG("%04d (%8s) m=%s.%s key=%#llx root=%p pxor=%#llx p=%p,%#llx(^%#llx) l=%p,%#llx(^%#llx) r=%p,%#llx(^%#llx) l^r=%#llx\n", - line, pfx, kstr, mstr, (long long)(uintptr_t)key_ptr, root, (long long)px64, - p, (long long)(uintptr_t)p, nlen, - p ? p->b[0] : NULL, p ? (long long)(uintptr_t)p->b[0] : 0, llen, - p ? p->b[1] : NULL, p ? (long long)(uintptr_t)p->b[1] : 0, rlen, - xlen); - } -} -#else -#define dbg(...) do { } while (0) -#endif - /* Generic tree descent function. It must absolutely be inlined so that the * compiler can eliminate the tests related to the various return pointers, * which must either point to a local variable in the caller, or be NULL. @@ -370,26 +506,30 @@ static void dbg(int line, * size arrays are passed in key_ptr with their length in key_u64. For keyless * nodes whose address serves as the key, the pointer needs to be passed in * key_ptr, and pxor64 will be used internally. + * The support for duplicates is advertised by ret_is_dup not being null; it + * will be filled on return with an indication whether the node belongs to a + * duplicate list or not. */ static inline __attribute__((always_inline)) -struct ceb_node *_cebu_descend(struct ceb_node **root, - enum ceb_walk_meth meth, - ptrdiff_t kofs, - enum ceb_key_type key_type, - uint32_t key_u32, - uint64_t key_u64, - const void *key_ptr, - int *ret_nside, - struct ceb_node ***ret_root, - struct ceb_node **ret_lparent, - int *ret_lpside, - struct ceb_node **ret_nparent, - int *ret_npside, - struct ceb_node **ret_gparent, - int *ret_gpside, - struct ceb_node **ret_back) +struct ceb_node *_ceb_descend(struct ceb_root **root, + enum ceb_walk_meth meth, + ptrdiff_t kofs, + enum ceb_key_type key_type, + uint32_t key_u32, + uint64_t key_u64, + const void *key_ptr, + int *ret_nside, + struct ceb_root ***ret_root, + struct ceb_node **ret_lparent, + int *ret_lpside, + struct ceb_node **ret_nparent, + int *ret_npside, + struct ceb_node **ret_gparent, + int *ret_gpside, + struct ceb_root **ret_back, + int *ret_is_dup) { -#if !defined(__OPTIMIZE__) && __GNUC_PREREQ__(12, 0) +#if defined(__GNUC__) && (__GNUC__ >= 12) && !defined(__OPTIMIZE__) /* Avoid a bogus warning with gcc 12 and above: it warns about negative * memcmp() length in non-existing code paths at -O0, as reported here: * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114622 @@ -397,30 +537,30 @@ struct ceb_node *_cebu_descend(struct ceb_node **root, #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstringop-overread" #endif - struct ceb_node *p; - union ceb_key_storage *l, *r, *k; + struct ceb_node *node; + union ceb_key_storage *k; struct ceb_node *gparent = NULL; - struct ceb_node *nparent = NULL; struct ceb_node *bnode = NULL; struct ceb_node *lparent; - uint32_t pxor32 = ~0U; // previous xor between branches - uint64_t pxor64 = ~0ULL; // previous xor between branches + uint32_t pxor32 __attribute__((unused)) = ~0U; // previous xor between branches + uint64_t pxor64 __attribute__((unused)) = ~0ULL; // previous xor between branches int gpside = 0; // side on the grand parent - int npside = 0; // side on the node's parent long lpside = 0; // side on the leaf's parent long brside = 0; // branch side when descending size_t llen = 0; // left vs key matching length size_t rlen = 0; // right vs key matching length size_t plen = 0; // previous common len between branches - int found = 0; // key was found (saves an extra strcmp for arrays) - - dbg(__LINE__, "_enter__", meth, kofs, key_type, root, NULL, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); + int is_leaf = 0; // set if the current node is a leaf /* the parent will be the (possibly virtual) node so that - * &lparent->l == root. + * &lparent->l == root, i.e. container_of(root, struct ceb_node, b[0]). */ - lparent = container_of(root, struct ceb_node, b[0]); - gparent = nparent = lparent; + lparent = (struct ceb_node *)((char *)root - (long)&((struct ceb_node *)0)->b[0]); + gparent = lparent; + if (ret_nparent) + *ret_nparent = NULL; + if (ret_npside) + *ret_npside = 0; /* for key-less descents we need to set the initial branch to take */ switch (meth) { @@ -435,38 +575,67 @@ struct ceb_node *_cebu_descend(struct ceb_node **root, break; } + /* In case of deletion, we need the node's parent and side. It's + * normally discovered during the descent while comparing branches, + * but there's a case where it's not possible, it's when the root + * is the node's parent because the first node is the one we're + * looking for. So we have to perform this check here. + */ + if (meth >= CEB_WM_KEQ && ret_nparent && ret_npside) { + union ceb_key_storage *k = NODEK(_ceb_clrtag(*root), kofs); + + if (((key_type == CEB_KT_MB || key_type == CEB_KT_IM) && + (memcmp(key_ptr, ((key_type == CEB_KT_MB) ? k->mb : k->ptr), key_u64) == 0)) || + ((key_type == CEB_KT_ST || key_type == CEB_KT_IS) && + (strcmp(key_ptr, (const void *)((key_type == CEB_KT_ST) ? k->str : k->ptr)) == 0))) { + *ret_nparent = lparent; + *ret_npside = lpside; + } + } + /* the previous xor is initialized to the largest possible inter-branch * value so that it can never match on the first test as we want to use * it to detect a leaf vs node. That's achieved with plen==0 for arrays * and pxorXX==~0 for scalars. */ - while (1) { - p = *root; + node = _ceb_clrtag(*root); + is_leaf = _ceb_gettag(*root); - /* Tests have shown that for write-intensive workloads (many - * insertions/deletion), prefetching for reads is counter - * productive (-10% perf) but that prefetching only the next - * nodes for writes when deleting can yield around 3% extra - * boost. - */ - if (ret_lpside) { - /* this is a deletion, prefetch for writes */ - __builtin_prefetch(p->b[0], 1); - __builtin_prefetch(p->b[1], 1); - } + if (ret_lpside) { + /* this is a deletion, benefits from prefetching */ + __builtin_prefetch(node->b[0], 0); + __builtin_prefetch(node->b[1], 0); + } + + while (1) { + union ceb_key_storage *lks, *rks; + struct ceb_node *ln, *rn, *next; + struct ceb_root *lr, *rr; + int next_leaf, lnl, rnl; + + lr = node->b[0]; // tagged versions + rr = node->b[1]; + + /* get a copy of the corresponding nodes */ + lnl = _ceb_gettag(lr); + ln = _ceb_clrtag(lr); + rnl = _ceb_gettag(rr); + rn = _ceb_clrtag(rr); /* neither pointer is tagged */ - k = NODEK(p, kofs); - l = NODEK(p->b[0], kofs); - r = NODEK(p->b[1], kofs); + k = NODEK(node, kofs); - dbg(__LINE__, "newp", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); - - /* two equal pointers identifies the nodeless leaf. */ - if (l == r) { - dbg(__LINE__, "l==r", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); + if (is_leaf) break; - } + + /* Tests show that this is the most optimal location to start + * a prefetch for adjacent nodes. + */ + __builtin_prefetch(ln, 0); + __builtin_prefetch(rn, 0); + + lks = NODEK(ln, kofs); + rks = NODEK(rn, kofs); /* In the following block, we're dealing with type-specific * operations which follow the same construct for each type: @@ -509,40 +678,39 @@ struct ceb_node *_cebu_descend(struct ceb_node **root, * - if we're deleting, it could be the key we were * looking for so we have to check for it as long as * it's still possible to keep a copy of the node's - * parent. is set int this case for expensive - * types. + * parent. */ if (key_type == CEB_KT_U32) { uint32_t xor32; // left vs right branch xor uint32_t kl, kr; - kl = l->u32; kr = r->u32; - xor32 = kl ^ kr; - - if (xor32 > pxor32) { // test using 2 4 6 4 - dbg(__LINE__, "xor>", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); - break; - } - + kl = lks->u32; kr = rks->u32; if (meth >= CEB_WM_KEQ) { - /* "found" is not used here */ kl ^= key_u32; kr ^= key_u32; brside = kl >= kr; + } + xor32 = kl ^ kr; + if (meth >= CEB_WM_KEQ) { /* let's stop if our key is not there */ - - if (kl > xor32 && kr > xor32) { - dbg(__LINE__, "mismatch", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); + if (kl > xor32 && kr > xor32) break; + + if (ret_nparent && !*ret_nparent && ret_npside) { + if (key_u32 == k->u32) { + *ret_nparent = lparent; + *ret_npside = lpside; + } } - if (ret_npside || ret_nparent) { - if (key_u32 == k->u32) { - dbg(__LINE__, "equal", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); - nparent = lparent; - npside = lpside; - } + /* for pure lookups, no need to go down the leaf + * if we've found the key. + */ + if (!ret_root && !ret_lpside && !ret_lparent && + !ret_gpside && !ret_gparent && !ret_back) { + if (key_u32 == k->u32) + break; } } pxor32 = xor32; @@ -551,288 +719,215 @@ struct ceb_node *_cebu_descend(struct ceb_node **root, uint64_t xor64; // left vs right branch xor uint64_t kl, kr; - kl = l->u64; kr = r->u64; - xor64 = kl ^ kr; - - if (xor64 > pxor64) { // test using 2 4 6 4 - dbg(__LINE__, "xor>", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); - break; - } - + kl = lks->u64; kr = rks->u64; if (meth >= CEB_WM_KEQ) { - /* "found" is not used here */ kl ^= key_u64; kr ^= key_u64; brside = kl >= kr; + } + xor64 = kl ^ kr; + if (meth >= CEB_WM_KEQ) { /* let's stop if our key is not there */ - - if (kl > xor64 && kr > xor64) { - dbg(__LINE__, "mismatch", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); + if (kl > xor64 && kr > xor64) break; + + if (ret_nparent && !*ret_nparent && ret_npside) { + if (key_u64 == k->u64) { + *ret_nparent = lparent; + *ret_npside = lpside; + } } - if (ret_npside || ret_nparent) { - if (key_u64 == k->u64) { - dbg(__LINE__, "equal", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); - nparent = lparent; - npside = lpside; - } + /* for pure lookups, no need to go down the leaf + * if we've found the key. + */ + if (!ret_root && !ret_lpside && !ret_lparent && + !ret_gpside && !ret_gparent && !ret_back) { + if (key_u64 == k->u64) + break; } } pxor64 = xor64; } - else if (key_type == CEB_KT_MB) { - size_t xlen = 0; // left vs right matching length - - if (meth >= CEB_WM_KEQ) { - /* measure identical lengths */ - llen = equal_bits(key_ptr, l->mb, 0, key_u64 << 3); - rlen = equal_bits(key_ptr, r->mb, 0, key_u64 << 3); - brside = llen <= rlen; - if (llen == rlen && (uint64_t)llen == key_u64 << 3) - found = 1; - } - - xlen = equal_bits(l->mb, r->mb, 0, key_u64 << 3); - if (xlen < plen) { - /* this is a leaf. E.g. triggered using 2 4 6 4 */ - dbg(__LINE__, "xor>", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); - break; - } - - if (meth >= CEB_WM_KEQ) { - /* let's stop if our key is not there */ - - if (llen < xlen && rlen < xlen) { - dbg(__LINE__, "mismatch", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); - break; - } - - if (ret_npside || ret_nparent) { // delete ? - size_t mlen = llen > rlen ? llen : rlen; - - if (mlen > xlen) - mlen = xlen; - - if ((uint64_t)xlen / 8 == key_u64 || memcmp(key_ptr + mlen / 8, k->mb + mlen / 8, key_u64 - mlen / 8) == 0) { - dbg(__LINE__, "equal", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); - nparent = lparent; - npside = lpside; - found = 1; - } - } - } - plen = xlen; - } - else if (key_type == CEB_KT_IM) { - size_t xlen = 0; // left vs right matching length - - if (meth >= CEB_WM_KEQ) { - /* measure identical lengths */ - llen = equal_bits(key_ptr, l->ptr, 0, key_u64 << 3); - rlen = equal_bits(key_ptr, r->ptr, 0, key_u64 << 3); - brside = llen <= rlen; - if (llen == rlen && (uint64_t)llen == key_u64 << 3) - found = 1; - } - - xlen = equal_bits(l->ptr, r->ptr, 0, key_u64 << 3); - if (xlen < plen) { - /* this is a leaf. E.g. triggered using 2 4 6 4 */ - dbg(__LINE__, "xor>", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); - break; - } - - if (meth >= CEB_WM_KEQ) { - /* let's stop if our key is not there */ - - if (llen < xlen && rlen < xlen) { - dbg(__LINE__, "mismatch", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); - break; - } - - if (ret_npside || ret_nparent) { // delete ? - size_t mlen = llen > rlen ? llen : rlen; - - if (mlen > xlen) - mlen = xlen; - - if ((uint64_t)xlen / 8 == key_u64 || memcmp(key_ptr + mlen / 8, k->ptr + mlen / 8, key_u64 - mlen / 8) == 0) { - dbg(__LINE__, "equal", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); - nparent = lparent; - npside = lpside; - found = 1; - } - } - } - plen = xlen; - } - else if (key_type == CEB_KT_ST) { - size_t xlen = 0; // left vs right matching length - - if (meth >= CEB_WM_KEQ) { - /* Note that a negative length indicates an - * equal value with the final zero reached, but - * it is still needed to descend to find the - * leaf. We take that negative length for an - * infinite one, hence the uint cast. - */ - llen = string_equal_bits(key_ptr, l->str, 0); - rlen = string_equal_bits(key_ptr, r->str, 0); - brside = (size_t)llen <= (size_t)rlen; - if ((ssize_t)llen < 0 || (ssize_t)rlen < 0) - found = 1; - } - - xlen = string_equal_bits(l->str, r->str, 0); - if (xlen < plen) { - /* this is a leaf. E.g. triggered using 2 4 6 4 */ - dbg(__LINE__, "xor>", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); - break; - } - - if (meth >= CEB_WM_KEQ) { - /* let's stop if our key is not there */ - - if ((unsigned)llen < (unsigned)xlen && (unsigned)rlen < (unsigned)xlen) { - dbg(__LINE__, "mismatch", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); - break; - } - - if (ret_npside || ret_nparent) { // delete ? - size_t mlen = llen > rlen ? llen : rlen; - - if (mlen > xlen) - mlen = xlen; - - if (strcmp(key_ptr + mlen / 8, (const void *)k->str + mlen / 8) == 0) { - /* strcmp() still needed. E.g. 1 2 3 4 10 11 4 3 2 1 10 11 fails otherwise */ - dbg(__LINE__, "equal", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); - nparent = lparent; - npside = lpside; - found = 1; - } - } - } - plen = xlen; - } - else if (key_type == CEB_KT_IS) { - size_t xlen = 0; // left vs right matching length - - if (meth >= CEB_WM_KEQ) { - /* Note that a negative length indicates an - * equal value with the final zero reached, but - * it is still needed to descend to find the - * leaf. We take that negative length for an - * infinite one, hence the uint cast. - */ - llen = string_equal_bits(key_ptr, l->ptr, 0); - rlen = string_equal_bits(key_ptr, r->ptr, 0); - brside = (size_t)llen <= (size_t)rlen; - if ((ssize_t)llen < 0 || (ssize_t)rlen < 0) - found = 1; - } - - xlen = string_equal_bits(l->ptr, r->ptr, 0); - if (xlen < plen) { - /* this is a leaf. E.g. triggered using 2 4 6 4 */ - dbg(__LINE__, "xor>", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); - break; - } - - if (meth >= CEB_WM_KEQ) { - /* let's stop if our key is not there */ - - if ((unsigned)llen < (unsigned)xlen && (unsigned)rlen < (unsigned)xlen) { - dbg(__LINE__, "mismatch", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); - break; - } - - if (ret_npside || ret_nparent) { // delete ? - size_t mlen = llen > rlen ? llen : rlen; - - if (mlen > xlen) - mlen = xlen; - - if (strcmp(key_ptr + mlen / 8, (const void *)k->ptr + mlen / 8) == 0) { - /* strcmp() still needed. E.g. 1 2 3 4 10 11 4 3 2 1 10 11 fails otherwise */ - dbg(__LINE__, "equal", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); - nparent = lparent; - npside = lpside; - found = 1; - } - } - } - plen = xlen; - } else if (key_type == CEB_KT_ADDR) { uintptr_t xoraddr; // left vs right branch xor uintptr_t kl, kr; - kl = (uintptr_t)l; kr = (uintptr_t)r; - xoraddr = kl ^ kr; - - if (xoraddr > (uintptr_t)pxor64) { // test using 2 4 6 4 - dbg(__LINE__, "xor>", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); - break; - } - + kl = (uintptr_t)lks; kr = (uintptr_t)rks; if (meth >= CEB_WM_KEQ) { - /* "found" is not used here */ kl ^= (uintptr_t)key_ptr; kr ^= (uintptr_t)key_ptr; brside = kl >= kr; + } + xoraddr = kl ^ kr; + if (meth >= CEB_WM_KEQ) { /* let's stop if our key is not there */ - - if (kl > xoraddr && kr > xoraddr) { - dbg(__LINE__, "mismatch", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); + if (kl > xoraddr && kr > xoraddr) break; + + if (ret_nparent && !*ret_nparent && ret_npside) { + if ((uintptr_t)key_ptr == (uintptr_t)node) { + *ret_nparent = lparent; + *ret_npside = lpside; + } } - if (ret_npside || ret_nparent) { - if ((uintptr_t)key_ptr == (uintptr_t)p) { - dbg(__LINE__, "equal", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); - nparent = lparent; - npside = lpside; - } + /* for pure lookups, no need to go down the leaf + * if we've found the key. + */ + if (!ret_root && !ret_lpside && !ret_lparent && + !ret_gpside && !ret_gparent && !ret_back) { + if ((uintptr_t)key_ptr == (uintptr_t)node) + break; } } pxor64 = xoraddr; } + else if (key_type == CEB_KT_MB || key_type == CEB_KT_IM) { + size_t xlen = 0; // left vs right matching length + + if (meth >= CEB_WM_KEQ) { + /* measure identical lengths */ + llen = equal_bits(key_ptr, (key_type == CEB_KT_MB) ? lks->mb : lks->ptr, plen, key_u64 << 3); + rlen = equal_bits(key_ptr, (key_type == CEB_KT_MB) ? rks->mb : rks->ptr, plen, key_u64 << 3); + brside = llen <= rlen; + } + + xlen = equal_bits((key_type == CEB_KT_MB) ? lks->mb : lks->ptr, + (key_type == CEB_KT_MB) ? rks->mb : rks->ptr, plen, key_u64 << 3); + + if (meth >= CEB_WM_KEQ) { + /* let's stop if our key is not there */ + if (llen < xlen && rlen < xlen) + break; + + if (ret_nparent && ret_npside && !*ret_nparent && + ((llen == key_u64 << 3) || (rlen == key_u64 << 3))) { + *ret_nparent = node; + *ret_npside = brside; + } + + /* for pure lookups, no need to go down the leaf + * if we've found the key. + */ + if (!ret_root && !ret_lpside && !ret_lparent && + !ret_gpside && !ret_gparent && !ret_back) { + if (llen == key_u64 << 3) { + node = ln; + plen = llen; + break; + } + if (rlen == key_u64 << 3) { + node = rn; + plen = rlen; + break; + } + } + } + plen = xlen; + } + else if (key_type == CEB_KT_ST || key_type == CEB_KT_IS) { + size_t xlen = 0; // left vs right matching length + + if (meth >= CEB_WM_KEQ) { + /* Note that a negative length indicates an + * equal value with the final zero reached, but + * it is still needed to descend to find the + * leaf. We take that negative length for an + * infinite one, hence the uint cast. + */ + llen = string_equal_bits(key_ptr, (key_type == CEB_KT_ST) ? lks->str : lks->ptr, plen); + rlen = string_equal_bits(key_ptr, (key_type == CEB_KT_ST) ? rks->str : rks->ptr, plen); + brside = (size_t)llen <= (size_t)rlen; + if (ret_nparent && ret_npside && !*ret_nparent && + ((ssize_t)llen < 0 || (ssize_t)rlen < 0)) { + *ret_nparent = node; + *ret_npside = brside; + } + + /* for pure lookups, no need to go down the leaf + * if we've found the key. + */ + if (!ret_root && !ret_lpside && !ret_lparent && + !ret_gpside && !ret_gparent && !ret_back) { + if ((ssize_t)llen < 0) { + node = ln; + plen = llen; + break; + } + if ((ssize_t)rlen < 0) { + node = rn; + plen = rlen; + break; + } + } + } + + /* the compiler cannot know this never happens and this helps it optimize the code */ + if ((ssize_t)plen < 0) + __builtin_unreachable(); + + xlen = string_equal_bits((key_type == CEB_KT_ST) ? lks->str : lks->ptr, + (key_type == CEB_KT_ST) ? rks->str : rks->ptr, plen); + + /* let's stop if our key is not there */ + if (meth >= CEB_WM_KEQ && llen < xlen && rlen < xlen) + break; + + plen = xlen; + } /* shift all copies by one */ gparent = lparent; gpside = lpside; - lparent = p; + lparent = node; lpside = brside; if (brside) { if (meth == CEB_WM_KPR || meth == CEB_WM_KLE || meth == CEB_WM_KLT) - bnode = p; - root = &p->b[1]; + bnode = node; + next = rn; + next_leaf = rnl; + root = &node->b[1]; /* change branch for key-less walks */ if (meth == CEB_WM_NXT) brside = 0; - - dbg(__LINE__, "side1", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); } else { if (meth == CEB_WM_KNX || meth == CEB_WM_KGE || meth == CEB_WM_KGT) - bnode = p; - root = &p->b[0]; + bnode = node; + next = ln; + next_leaf = lnl; + root = &node->b[0]; /* change branch for key-less walks */ if (meth == CEB_WM_PRV) brside = 1; - - dbg(__LINE__, "side0", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); } - if (p == *root) { - /* loops over itself, it's a leaf */ - dbg(__LINE__, "loop", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); + if (next == node) { + /* loops over itself, it's either a leaf or the single and last list element of a dup sub-tree */ break; } + + /* let the compiler know there's no NULL in the tree */ + if (!next) + __builtin_unreachable(); + + node = next; + is_leaf = next_leaf; + } + + if (ret_is_dup) { + if (is_leaf && _ceb_gettag(node->b[0]) && _ceb_gettag(node->b[1]) && + (_ceb_clrtag(node->b[0]) != node || _ceb_clrtag(node->b[1]) != node)) { + /* This leaf has two tagged pointers, with at least one not pointing + * to itself, it's not the nodeless leaf, it's a duplicate. + */ + *ret_is_dup = 1; + } else { + *ret_is_dup = 0; + } } /* here we're on the closest node from the requested value. It may be @@ -841,17 +936,6 @@ struct ceb_node *_cebu_descend(struct ceb_node **root, * still deserved, depending on the matching method. */ - /* if we've exited on an exact match after visiting a regular node - * (i.e. not the nodeless leaf), we'll avoid checking the string again. - * However if it doesn't match, we must make sure to compare from - * within the key (which can be shorter than the ones already there), - * so we restart the check from the longest of the two lengths, which - * guarantees these bits exist. Test with "100", "10", "1" to see where - * this is needed. - */ - if ((key_type == CEB_KT_ST || key_type == CEB_KT_IS) && meth >= CEB_WM_KEQ && !found) - plen = (llen > rlen) ? llen : rlen; - /* update the pointers needed for modifications (insert, delete) */ if (ret_nside && meth >= CEB_WM_KEQ) { switch (key_type) { @@ -861,39 +945,38 @@ struct ceb_node *_cebu_descend(struct ceb_node **root, case CEB_KT_U64: *ret_nside = key_u64 >= k->u64; break; - case CEB_KT_MB: - *ret_nside = (uint64_t)plen / 8 == key_u64 || memcmp(key_ptr + plen / 8, k->mb + plen / 8, key_u64 - plen / 8) >= 0; - break; - case CEB_KT_IM: - *ret_nside = (uint64_t)plen / 8 == key_u64 || memcmp(key_ptr + plen / 8, k->ptr + plen / 8, key_u64 - plen / 8) >= 0; - break; - case CEB_KT_ST: - *ret_nside = found || strcmp(key_ptr + plen / 8, (const void *)k->str + plen / 8) >= 0; - break; - case CEB_KT_IS: - *ret_nside = found || strcmp(key_ptr + plen / 8, (const void *)k->ptr + plen / 8) >= 0; - break; case CEB_KT_ADDR: - *ret_nside = (uintptr_t)key_ptr >= (uintptr_t)p; + *ret_nside = (uintptr_t)key_ptr >= (uintptr_t)node; + break; + case CEB_KT_MB: + case CEB_KT_IM: + *ret_nside = (uint64_t)plen / 8 == key_u64 || + memcmp(key_ptr + plen / 8, ((key_type == CEB_KT_MB) ? k->mb : k->ptr) + plen / 8, key_u64 - plen / 8) >= 0; + break; + + case CEB_KT_ST: + case CEB_KT_IS: + *ret_nside = (ssize_t)plen < 0 || + strcmp(key_ptr + plen / 8, (const void *)((key_type == CEB_KT_ST) ? k->str : k->ptr) + plen / 8) >= 0; break; } } - if (ret_root) + if (ret_root) { + /* this node is going to be changed */ *ret_root = root; + __builtin_prefetch(root, 1); + } /* info needed by delete */ if (ret_lpside) *ret_lpside = lpside; - if (ret_lparent) + if (ret_lparent) { + /* this node is going to be changed */ *ret_lparent = lparent; - - if (ret_npside) - *ret_npside = npside; - - if (ret_nparent) - *ret_nparent = nparent; + __builtin_prefetch(lparent, 1); + } if (ret_gpside) *ret_gpside = gpside; @@ -902,9 +985,7 @@ struct ceb_node *_cebu_descend(struct ceb_node **root, *ret_gparent = gparent; if (ret_back) - *ret_back = bnode; - - dbg(__LINE__, "_ret____", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); + *ret_back = _ceb_dotag(bnode, 0); if (meth >= CEB_WM_KEQ) { /* For lookups, an equal value means an instant return. For insertions, @@ -920,7 +1001,7 @@ struct ceb_node *_cebu_descend(struct ceb_node **root, (meth == CEB_WM_KGT && k->u32 > key_u32) || (meth == CEB_WM_KLE && k->u32 <= key_u32) || (meth == CEB_WM_KLT && k->u32 < key_u32)) - return p; + return node; } else if (key_type == CEB_KT_U64) { if ((meth == CEB_WM_KEQ && k->u64 == key_u64) || @@ -930,90 +1011,56 @@ struct ceb_node *_cebu_descend(struct ceb_node **root, (meth == CEB_WM_KGT && k->u64 > key_u64) || (meth == CEB_WM_KLE && k->u64 <= key_u64) || (meth == CEB_WM_KLT && k->u64 < key_u64)) - return p; - } - else if (key_type == CEB_KT_MB) { - int diff; - - if ((uint64_t)plen / 8 == key_u64) - diff = 0; - else - diff = memcmp(k->mb + plen / 8, key_ptr + plen / 8, key_u64 - plen / 8); - - if ((meth == CEB_WM_KEQ && diff == 0) || - (meth == CEB_WM_KNX && diff == 0) || - (meth == CEB_WM_KPR && diff == 0) || - (meth == CEB_WM_KGE && diff >= 0) || - (meth == CEB_WM_KGT && diff > 0) || - (meth == CEB_WM_KLE && diff <= 0) || - (meth == CEB_WM_KLT && diff < 0)) - return p; - } - else if (key_type == CEB_KT_IM) { - int diff; - - if ((uint64_t)plen / 8 == key_u64) - diff = 0; - else - diff = memcmp(k->ptr + plen / 8, key_ptr + plen / 8, key_u64 - plen / 8); - - if ((meth == CEB_WM_KEQ && diff == 0) || - (meth == CEB_WM_KNX && diff == 0) || - (meth == CEB_WM_KPR && diff == 0) || - (meth == CEB_WM_KGE && diff >= 0) || - (meth == CEB_WM_KGT && diff > 0) || - (meth == CEB_WM_KLE && diff <= 0) || - (meth == CEB_WM_KLT && diff < 0)) - return p; - } - else if (key_type == CEB_KT_ST) { - int diff; - - if (found) - diff = 0; - else - diff = strcmp((const void *)k->str + plen / 8, key_ptr + plen / 8); - - if ((meth == CEB_WM_KEQ && diff == 0) || - (meth == CEB_WM_KNX && diff == 0) || - (meth == CEB_WM_KPR && diff == 0) || - (meth == CEB_WM_KGE && diff >= 0) || - (meth == CEB_WM_KGT && diff > 0) || - (meth == CEB_WM_KLE && diff <= 0) || - (meth == CEB_WM_KLT && diff < 0)) - return p; - } - else if (key_type == CEB_KT_IS) { - int diff; - - if (found) - diff = 0; - else - diff = strcmp((const void *)k->ptr + plen / 8, key_ptr + plen / 8); - - if ((meth == CEB_WM_KEQ && diff == 0) || - (meth == CEB_WM_KNX && diff == 0) || - (meth == CEB_WM_KPR && diff == 0) || - (meth == CEB_WM_KGE && diff >= 0) || - (meth == CEB_WM_KGT && diff > 0) || - (meth == CEB_WM_KLE && diff <= 0) || - (meth == CEB_WM_KLT && diff < 0)) - return p; + return node; } else if (key_type == CEB_KT_ADDR) { - if ((meth == CEB_WM_KEQ && (uintptr_t)p == (uintptr_t)key_ptr) || - (meth == CEB_WM_KNX && (uintptr_t)p == (uintptr_t)key_ptr) || - (meth == CEB_WM_KPR && (uintptr_t)p == (uintptr_t)key_ptr) || - (meth == CEB_WM_KGE && (uintptr_t)p >= (uintptr_t)key_ptr) || - (meth == CEB_WM_KGT && (uintptr_t)p > (uintptr_t)key_ptr) || - (meth == CEB_WM_KLE && (uintptr_t)p <= (uintptr_t)key_ptr) || - (meth == CEB_WM_KLT && (uintptr_t)p < (uintptr_t)key_ptr)) - return p; + if ((meth == CEB_WM_KEQ && (uintptr_t)node == (uintptr_t)key_ptr) || + (meth == CEB_WM_KNX && (uintptr_t)node == (uintptr_t)key_ptr) || + (meth == CEB_WM_KPR && (uintptr_t)node == (uintptr_t)key_ptr) || + (meth == CEB_WM_KGE && (uintptr_t)node >= (uintptr_t)key_ptr) || + (meth == CEB_WM_KGT && (uintptr_t)node > (uintptr_t)key_ptr) || + (meth == CEB_WM_KLE && (uintptr_t)node <= (uintptr_t)key_ptr) || + (meth == CEB_WM_KLT && (uintptr_t)node < (uintptr_t)key_ptr)) + return node; + } + else if (key_type == CEB_KT_MB || key_type == CEB_KT_IM) { + int diff; + + if ((uint64_t)plen / 8 == key_u64) + diff = 0; + else + diff = memcmp(((key_type == CEB_KT_MB) ? k->mb : k->ptr) + plen / 8, key_ptr + plen / 8, key_u64 - plen / 8); + + if ((meth == CEB_WM_KEQ && diff == 0) || + (meth == CEB_WM_KNX && diff == 0) || + (meth == CEB_WM_KPR && diff == 0) || + (meth == CEB_WM_KGE && diff >= 0) || + (meth == CEB_WM_KGT && diff > 0) || + (meth == CEB_WM_KLE && diff <= 0) || + (meth == CEB_WM_KLT && diff < 0)) + return node; + } + else if (key_type == CEB_KT_ST || key_type == CEB_KT_IS) { + int diff; + + if ((ssize_t)plen < 0) + diff = 0; + else + diff = strcmp((const void *)((key_type == CEB_KT_ST) ? k->str : k->ptr) + plen / 8, key_ptr + plen / 8); + + if ((meth == CEB_WM_KEQ && diff == 0) || + (meth == CEB_WM_KNX && diff == 0) || + (meth == CEB_WM_KPR && diff == 0) || + (meth == CEB_WM_KGE && diff >= 0) || + (meth == CEB_WM_KGT && diff > 0) || + (meth == CEB_WM_KLE && diff <= 0) || + (meth == CEB_WM_KLT && diff < 0)) + return node; } } else if (meth == CEB_WM_FST || meth == CEB_WM_LST) { - return p; + return node; } else if (meth == CEB_WM_PRV || meth == CEB_WM_NXT) { - return p; + return node; } /* lookups and deletes fail here */ @@ -1023,37 +1070,43 @@ struct ceb_node *_cebu_descend(struct ceb_node **root, * caller to proceed since the element is not there. */ return NULL; -#if __GNUC_PREREQ__(12, 0) +#if defined(__GNUC__) && (__GNUC__ >= 12) && !defined(__OPTIMIZE__) #pragma GCC diagnostic pop #endif } +/* + * Below are the functions that support duplicate keys (_ceb_*) + */ -/* Generic tree insertion function for trees with unique keys. Inserts node +/* Generic tree insertion function for trees with duplicate keys. Inserts node * into tree , with key type and key . * Returns the inserted node or the one that already contains the same key. + * If is non-null, then duplicates are permitted and this variable + * is used to temporarily carry an internal state. */ static inline __attribute__((always_inline)) -struct ceb_node *_cebu_insert(struct ceb_node **root, - struct ceb_node *node, - ptrdiff_t kofs, - enum ceb_key_type key_type, - uint32_t key_u32, - uint64_t key_u64, - const void *key_ptr) +struct ceb_node *_ceb_insert(struct ceb_root **root, + struct ceb_node *node, + ptrdiff_t kofs, + enum ceb_key_type key_type, + uint32_t key_u32, + uint64_t key_u64, + const void *key_ptr, + int *is_dup_ptr) { - struct ceb_node **parent; + struct ceb_root **parent; struct ceb_node *ret; int nside; if (!*root) { /* empty tree, insert a leaf only */ - node->b[0] = node->b[1] = node; - *root = node; + node->b[0] = node->b[1] = _ceb_dotag(node, 1); + *root = _ceb_dotag(node, 1); return node; } - ret = _cebu_descend(root, CEB_WM_KEQ, kofs, key_type, key_u32, key_u64, key_ptr, &nside, &parent, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + ret = _ceb_descend(root, CEB_WM_KEQ, kofs, key_type, key_u32, key_u64, key_ptr, &nside, &parent, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr); if (!ret) { /* The key was not in the tree, we can insert it. Better use an @@ -1062,44 +1115,75 @@ struct ceb_node *_cebu_insert(struct ceb_node **root, * optimizes it a bit. */ if (nside) { - node->b[1] = node; + node->b[1] = _ceb_dotag(node, 1); node->b[0] = *parent; } else { - node->b[0] = node; + node->b[0] = _ceb_dotag(node, 1); node->b[1] = *parent; } - *parent = node; + *parent = _ceb_dotag(node, 0); + ret = node; + } else if (is_dup_ptr) { + /* The key was found. We must insert after it as the last + * element of the dups list, which means that our left branch + * will point to the key, the right one to the first dup + * (i.e. previous dup's right if it exists, otherwise ourself) + * and the parent must point to us. + */ + node->b[0] = *parent; + + if (*is_dup_ptr) { + node->b[1] = _ceb_untag(*parent, 1)->b[1]; + _ceb_untag(*parent, 1)->b[1] = _ceb_dotag(node, 1); + } else { + node->b[1] = _ceb_dotag(node, 1); + } + *parent = _ceb_dotag(node, 1); ret = node; } return ret; } /* Returns the first node or NULL if not found, assuming a tree made of keys of - * type . + * type , and optionally for fixed-size arrays (otherwise 0). + * If the tree starts with duplicates, the first of them is returned. */ static inline __attribute__((always_inline)) -struct ceb_node *_cebu_first(struct ceb_node **root, - ptrdiff_t kofs, - enum ceb_key_type key_type) +struct ceb_node *_ceb_first(struct ceb_root *const *root, + ptrdiff_t kofs, + enum ceb_key_type key_type, + uint64_t key_len, + int *is_dup_ptr) { + struct ceb_node *node; + if (!*root) return NULL; - return _cebu_descend(root, CEB_WM_FST, kofs, key_type, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + node = _ceb_descend((struct ceb_root **)root, CEB_WM_FST, kofs, key_type, 0, key_len, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr); + if (node && is_dup_ptr && *is_dup_ptr) { + /* on a duplicate, the first node is right->left and it's a leaf */ + node = _ceb_untag(_ceb_untag(node->b[1], 1)->b[0], 1); + } + return node; } /* Returns the last node or NULL if not found, assuming a tree made of keys of - * type . + * type , and optionally for fixed-size arrays (otherwise 0). + * If the tree ends with duplicates, the last of them is returned. */ static inline __attribute__((always_inline)) -struct ceb_node *_cebu_last(struct ceb_node **root, - ptrdiff_t kofs, - enum ceb_key_type key_type) +struct ceb_node *_ceb_last(struct ceb_root *const *root, + ptrdiff_t kofs, + enum ceb_key_type key_type, + uint64_t key_len, + int *is_dup_ptr) { if (!*root) return NULL; - return _cebu_descend(root, CEB_WM_LST, kofs, key_type, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + /* note for duplicates: the current scheme always returns the last one by default */ + return _ceb_descend((struct ceb_root **)root, CEB_WM_LST, kofs, key_type, 0, key_len, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr); } /* Searches in the tree made of keys of type , for the next @@ -1110,25 +1194,26 @@ struct ceb_node *_cebu_last(struct ceb_node **root, * that fork. */ static inline __attribute__((always_inline)) -struct ceb_node *_cebu_next(struct ceb_node **root, - ptrdiff_t kofs, - enum ceb_key_type key_type, - uint32_t key_u32, - uint64_t key_u64, - const void *key_ptr) +struct ceb_node *_ceb_next_unique(struct ceb_root *const *root, + ptrdiff_t kofs, + enum ceb_key_type key_type, + uint32_t key_u32, + uint64_t key_u64, + const void *key_ptr, + int *is_dup_ptr) { - struct ceb_node *restart; + struct ceb_root *restart; if (!*root) return NULL; - if (!_cebu_descend(root, CEB_WM_KNX, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart)) + if (!_ceb_descend((struct ceb_root **)root, CEB_WM_KNX, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart, is_dup_ptr)) return NULL; if (!restart) return NULL; - return _cebu_descend(&restart, CEB_WM_NXT, kofs, key_type, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + return _ceb_descend(&restart, CEB_WM_NXT, kofs, key_type, 0, key_u64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr); } /* Searches in the tree made of keys of type , for the prev @@ -1139,156 +1224,373 @@ struct ceb_node *_cebu_next(struct ceb_node **root, * that fork. */ static inline __attribute__((always_inline)) -struct ceb_node *_cebu_prev(struct ceb_node **root, - ptrdiff_t kofs, - enum ceb_key_type key_type, - uint32_t key_u32, - uint64_t key_u64, - const void *key_ptr) +struct ceb_node *_ceb_prev_unique(struct ceb_root *const *root, + ptrdiff_t kofs, + enum ceb_key_type key_type, + uint32_t key_u32, + uint64_t key_u64, + const void *key_ptr, + int *is_dup_ptr) { - struct ceb_node *restart; + struct ceb_root *restart; if (!*root) return NULL; - if (!_cebu_descend(root, CEB_WM_KPR, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart)) + if (!_ceb_descend((struct ceb_root **)root, CEB_WM_KPR, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart, is_dup_ptr)) return NULL; if (!restart) return NULL; - return _cebu_descend(&restart, CEB_WM_PRV, kofs, key_type, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + return _ceb_descend(&restart, CEB_WM_PRV, kofs, key_type, 0, key_u64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr); } -/* Searches in the tree made of keys of type , for the node - * containing the key . Returns NULL if not found. +/* Searches in the tree made of keys of type , for the next + * node after also containing key . Returns NULL if not found. + * It's up to the caller to pass the current node's key in . */ static inline __attribute__((always_inline)) -struct ceb_node *_cebu_lookup(struct ceb_node **root, - ptrdiff_t kofs, - enum ceb_key_type key_type, - uint32_t key_u32, - uint64_t key_u64, - const void *key_ptr) +struct ceb_node *_ceb_next_dup(struct ceb_root *const *root, + ptrdiff_t kofs, + enum ceb_key_type key_type, + uint32_t key_u32, + uint64_t key_u64, + const void *key_ptr, + const struct ceb_node *from) { - if (!*root) - return NULL; - - return _cebu_descend(root, CEB_WM_KEQ, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); -} - -/* Searches in the tree made of keys of type , for the node - * containing the key or the highest one that's lower than it. Returns - * NULL if not found. - */ -static inline __attribute__((always_inline)) -struct ceb_node *_cebu_lookup_le(struct ceb_node **root, - ptrdiff_t kofs, - enum ceb_key_type key_type, - uint32_t key_u32, - uint64_t key_u64, - const void *key_ptr) -{ - struct ceb_node *ret = NULL; - struct ceb_node *restart; + struct ceb_node *node; + int is_dup; if (!*root) return NULL; - ret = _cebu_descend(root, CEB_WM_KLE, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart); - if (ret) - return ret; + node = _ceb_descend((struct ceb_root **)root, CEB_WM_KNX, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &is_dup); + if (!node) + return NULL; + /* Normally at this point, if node != from, we've found a node that + * differs from the one we're starting from, which indicates that + * the starting point belongs to a dup list and is not the last one. + * We must then visit the other members. We cannot navigate from the + * regular leaf node (the first one) but we can easily verify if we're + * on that one by checking if it's node->b[1]->b[0], in which case we + * jump to node->b[1]. Otherwise we take from->b[1]. + */ + if (node != from) { + if (_ceb_untag(node->b[1], 1)->b[0] == _ceb_dotag(from, 1)) + return _ceb_untag(node->b[1], 1); + else + return _ceb_untag(from->b[1], 1); + } + + /* there's no other dup here */ + return NULL; +} + +/* Searches in the tree made of keys of type , for the prev + * node before also containing key . Returns NULL if not found. + * It's up to the caller to pass the current node's key in . + */ +static inline __attribute__((always_inline)) +struct ceb_node *_ceb_prev_dup(struct ceb_root *const *root, + ptrdiff_t kofs, + enum ceb_key_type key_type, + uint32_t key_u32, + uint64_t key_u64, + const void *key_ptr, + const struct ceb_node *from) +{ + struct ceb_node *node; + int is_dup; + + if (!*root) + return NULL; + + node = _ceb_descend((struct ceb_root **)root, CEB_WM_KPR, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &is_dup); + if (!node) + return NULL; + + /* Here we have several possibilities: + * - from == node => we've found our node. It may be a unique node, + * or the last one of a dup series. We'll sort that out thanks to + * is_dup, and if it's a dup, we'll use node->b[0]. + * - from is not the first dup, so we haven't visited them all yet, + * hence we visit node->b[0] to switch to the previous dup. + * - from is the first dup so we've visited them all. + */ + if (is_dup && (node == from || _ceb_untag(node->b[1], 1)->b[0] != _ceb_dotag(from, 1))) + return _ceb_untag(from->b[0], 1); + + /* there's no other dup here */ + return NULL; +} + +/* Searches in the tree made of keys of type , for the next + * node after which contains key . Returns NULL if not found. + * It's up to the caller to pass the current node's key in . The + * approach consists in looking up that node first, recalling the last time a + * left turn was made, and returning the first node along the right branch at + * that fork. In case the current node belongs to a duplicate list, all dups + * will be visited in insertion order prior to jumping to different keys. + */ +static inline __attribute__((always_inline)) +struct ceb_node *_ceb_next(struct ceb_root *const *root, + ptrdiff_t kofs, + enum ceb_key_type key_type, + uint32_t key_u32, + uint64_t key_u64, + const void *key_ptr, + const struct ceb_node *from, + int *is_dup_ptr) +{ + struct ceb_root *restart; + struct ceb_node *node; + + if (!*root) + return NULL; + + node = _ceb_descend((struct ceb_root **)root, CEB_WM_KNX, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart, is_dup_ptr); + if (!node) + return NULL; + + /* Normally at this point, if node != from, we've found a node that + * differs from the one we're starting from, which indicates that + * the starting point belongs to a dup list and is not the last one. + * We must then visit the other members. We cannot navigate from the + * regular leaf node (the first one) but we can easily verify if we're + * on that one by checking if it's _ceb_untag(node->b[1], 0)->b[0], in which case we + * jump to node->b[1]. Otherwise we take from->b[1]. + */ + if (node != from) { + if (_ceb_untag(node->b[1], 1)->b[0] == _ceb_dotag(from, 1)) + return _ceb_untag(node->b[1], 1); + else + return _ceb_untag(from->b[1], 1); + } + + /* Here the looked up node was found (node == from) and we can look up + * the next unique one if any. + */ if (!restart) return NULL; - return _cebu_descend(&restart, CEB_WM_PRV, kofs, key_type, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + /* this look up will stop on the topmost dup in a sub-tree which is + * also the last one. Thanks to restart we know that this entry exists. + */ + node = _ceb_descend(&restart, CEB_WM_NXT, kofs, key_type, 0, key_u64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr); + if (node && is_dup_ptr && *is_dup_ptr) { + /* on a duplicate, the first node is right->left and it's a leaf */ + node = _ceb_untag(_ceb_untag(node->b[1], 1)->b[0], 1); + } + return node; } -/* Searches in the tree made of keys of type , for the node - * containing the greatest key that is strictly lower than . Returns - * NULL if not found. It's very similar to next() except that the looked up - * value doesn't need to exist. +/* Searches in the tree made of keys of type , for the prev + * node before the one containing the key . Returns NULL if not found. + * It's up to the caller to pass the current node's key in . The + * approach consists in looking up that node first, recalling the last time a + * right turn was made, and returning the last node along the left branch at + * that fork. In case the current node belongs to a duplicate list, all dups + * will be visited in reverse insertion order prior to jumping to different + * keys. */ static inline __attribute__((always_inline)) -struct ceb_node *_cebu_lookup_lt(struct ceb_node **root, - ptrdiff_t kofs, - enum ceb_key_type key_type, - uint32_t key_u32, - uint64_t key_u64, - const void *key_ptr) +struct ceb_node *_ceb_prev(struct ceb_root *const *root, + ptrdiff_t kofs, + enum ceb_key_type key_type, + uint32_t key_u32, + uint64_t key_u64, + const void *key_ptr, + const struct ceb_node *from, + int *is_dup_ptr) { - struct ceb_node *ret = NULL; - struct ceb_node *restart; + struct ceb_root *restart; + struct ceb_node *node; if (!*root) return NULL; - ret = _cebu_descend(root, CEB_WM_KLT, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart); - if (ret) - return ret; + node = _ceb_descend((struct ceb_root **)root, CEB_WM_KPR, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart, is_dup_ptr); + if (!node) + return NULL; + /* Here we have several possibilities: + * - from == node => we've found our node. It may be a unique node, + * or the last one of a dup series. We'll sort that out thanks to + * is_dup, and if it's a dup, we'll use node->b[0]. + * - from is not the first dup, so we haven't visited them all yet, + * hence we visit node->b[0] to switch to the previous dup. + * - from is the first dup so we've visited them all, we now need + * to jump to the previous unique value. + */ + if (is_dup_ptr && *is_dup_ptr && (node == from || _ceb_untag(node->b[1], 1)->b[0] != _ceb_dotag(from, 1))) + return _ceb_untag(from->b[0], 1); + + /* look up the previous unique entry */ if (!restart) return NULL; - return _cebu_descend(&restart, CEB_WM_PRV, kofs, key_type, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + /* Note that the descent stops on the last dup which is the one we want */ + return _ceb_descend(&restart, CEB_WM_PRV, kofs, key_type, 0, key_u64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr); } -/* Searches in the tree made of keys of type , for the node - * containing the key or the smallest one that's greater than it. +/* Searches in the tree made of keys of type , for the first + * node containing the key . Returns NULL if not found. + */ +static inline __attribute__((always_inline)) +struct ceb_node *_ceb_lookup(struct ceb_root *const *root, + ptrdiff_t kofs, + enum ceb_key_type key_type, + uint32_t key_u32, + uint64_t key_u64, + const void *key_ptr, + int *is_dup_ptr) +{ + struct ceb_node *ret; + + if (!*root) + return NULL; + + ret = _ceb_descend((struct ceb_root **)root, CEB_WM_KEQ, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr); + if (ret && is_dup_ptr && *is_dup_ptr) { + /* on a duplicate, the first node is right->left and it's a leaf */ + ret = _ceb_untag(_ceb_untag(ret->b[1], 1)->b[0], 1); + } + return ret; +} + +/* Searches in the tree made of keys of type , for the last + * node containing the key or the highest one that's lower than it. * Returns NULL if not found. */ static inline __attribute__((always_inline)) -struct ceb_node *_cebu_lookup_ge(struct ceb_node **root, - ptrdiff_t kofs, - enum ceb_key_type key_type, - uint32_t key_u32, - uint64_t key_u64, - const void *key_ptr) +struct ceb_node *_ceb_lookup_le(struct ceb_root *const *root, + ptrdiff_t kofs, + enum ceb_key_type key_type, + uint32_t key_u32, + uint64_t key_u64, + const void *key_ptr, + int *is_dup_ptr) { struct ceb_node *ret = NULL; - struct ceb_node *restart; + struct ceb_root *restart; if (!*root) return NULL; - ret = _cebu_descend(root, CEB_WM_KGE, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart); + /* note that for duplicates, we already find the last one */ + ret = _ceb_descend((struct ceb_root **)root, CEB_WM_KLE, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart, is_dup_ptr); if (ret) return ret; if (!restart) return NULL; - return _cebu_descend(&restart, CEB_WM_NXT, kofs, key_type, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + return _ceb_descend(&restart, CEB_WM_PRV, kofs, key_type, 0, key_u64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr); } -/* Searches in the tree made of keys of type , for the node - * containing the lowest key that is strictly greater than . Returns - * NULL if not found. It's very similar to prev() except that the looked up - * value doesn't need to exist. +/* Searches in the tree made of keys of type , for the last + * node containing the greatest key that is strictly lower than . + * Returns NULL if not found. It's very similar to next() except that the + * looked up value doesn't need to exist. */ static inline __attribute__((always_inline)) -struct ceb_node *_cebu_lookup_gt(struct ceb_node **root, - ptrdiff_t kofs, - enum ceb_key_type key_type, - uint32_t key_u32, - uint64_t key_u64, - const void *key_ptr) +struct ceb_node *_ceb_lookup_lt(struct ceb_root *const *root, + ptrdiff_t kofs, + enum ceb_key_type key_type, + uint32_t key_u32, + uint64_t key_u64, + const void *key_ptr, + int *is_dup_ptr) { struct ceb_node *ret = NULL; - struct ceb_node *restart; + struct ceb_root *restart; if (!*root) return NULL; - ret = _cebu_descend(root, CEB_WM_KGT, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart); + /* note that for duplicates, we already find the last one */ + ret = _ceb_descend((struct ceb_root **)root, CEB_WM_KLT, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart, is_dup_ptr); if (ret) return ret; if (!restart) return NULL; - return _cebu_descend(&restart, CEB_WM_NXT, kofs, key_type, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + return _ceb_descend(&restart, CEB_WM_PRV, kofs, key_type, 0, key_u64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr); +} + +/* Searches in the tree made of keys of type , for the first + * node containing the key or the smallest one that's greater than it. + * Returns NULL if not found. If is non-null, then duplicates are + * permitted and this variable is used to temporarily carry an internal state. + + */ +static inline __attribute__((always_inline)) +struct ceb_node *_ceb_lookup_ge(struct ceb_root *const *root, + ptrdiff_t kofs, + enum ceb_key_type key_type, + uint32_t key_u32, + uint64_t key_u64, + const void *key_ptr, + int *is_dup_ptr) +{ + struct ceb_node *ret = NULL; + struct ceb_root *restart; + + if (!*root) + return NULL; + + ret = _ceb_descend((struct ceb_root **)root, CEB_WM_KGE, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart, is_dup_ptr); + if (!ret) { + if (!restart) + return NULL; + + ret = _ceb_descend(&restart, CEB_WM_NXT, kofs, key_type, 0, key_u64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr); + } + + if (ret && is_dup_ptr && *is_dup_ptr) { + /* on a duplicate, the first node is right->left and it's a leaf */ + ret = _ceb_untag(_ceb_untag(ret->b[1], 1)->b[0], 1); + } + return ret; +} + +/* Searches in the tree made of keys of type , for the first + * node containing the lowest key that is strictly greater than . Returns + * NULL if not found. It's very similar to prev() except that the looked up + * value doesn't need to exist. If is non-null, then duplicates are + * permitted and this variable is used to temporarily carry an internal state. + */ +static inline __attribute__((always_inline)) +struct ceb_node *_ceb_lookup_gt(struct ceb_root *const *root, + ptrdiff_t kofs, + enum ceb_key_type key_type, + uint32_t key_u32, + uint64_t key_u64, + const void *key_ptr, + int *is_dup_ptr) +{ + struct ceb_node *ret = NULL; + struct ceb_root *restart; + + if (!*root) + return NULL; + + ret = _ceb_descend((struct ceb_root **)root, CEB_WM_KGT, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart, is_dup_ptr); + if (!ret) { + if (!restart) + return NULL; + + ret = _ceb_descend(&restart, CEB_WM_NXT, kofs, key_type, 0, key_u64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr); + } + + if (ret && is_dup_ptr && *is_dup_ptr) { + /* on a duplicate, the first node is right->left and it's a leaf */ + ret = _ceb_untag(_ceb_untag(ret->b[1], 1)->b[0], 1); + } + return ret; } /* Searches in the tree made of keys of type , for the node @@ -1298,16 +1600,18 @@ struct ceb_node *_cebu_lookup_gt(struct ceb_node **root, * node is detected since it has b[0]==NULL, which this functions also clears * after operation. The function is idempotent, so it's safe to attempt to * delete an already deleted node (NULL is returned in this case since the node - * was not in the tree). + * was not in the tree). If is non-null, then duplicates are + * permitted and this variable is used to temporarily carry an internal state. */ static inline __attribute__((always_inline)) -struct ceb_node *_cebu_delete(struct ceb_node **root, - struct ceb_node *node, - ptrdiff_t kofs, - enum ceb_key_type key_type, - uint32_t key_u32, - uint64_t key_u64, - const void *key_ptr) +struct ceb_node *_ceb_delete(struct ceb_root **root, + struct ceb_node *node, + ptrdiff_t kofs, + enum ceb_key_type key_type, + uint32_t key_u32, + uint64_t key_u64, + const void *key_ptr, + int *is_dup_ptr) { struct ceb_node *lparent, *nparent, *gparent; int lpside, npside, gpside; @@ -1323,14 +1627,85 @@ struct ceb_node *_cebu_delete(struct ceb_node **root, goto done; } - ret = _cebu_descend(root, CEB_WM_KEQ, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, - &lparent, &lpside, &nparent, &npside, &gparent, &gpside, NULL); + ret = _ceb_descend(root, CEB_WM_KEQ, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, + &lparent, &lpside, &nparent, &npside, &gparent, &gpside, NULL, is_dup_ptr); if (!ret) { /* key not found */ goto done; } + if (is_dup_ptr && *is_dup_ptr) { + /* the node to be deleted belongs to a dup sub-tree whose ret + * is the last. The possibilities here are: + * 1) node==NULL => unspecified, we delete the first one, + * which is the tree leaf. The tree node (if it exists) + * is replaced by the first dup. There's nothing else to + * change. + * 2) node is the tree leaf. The tree node (if it exists) + * is replaced by the first dup. + * 3) node is a dup. We just delete the dup. + * In order to delete a dup, there are 4 cases: + * a) node==last and there's a single dup, it's this one + * -> *parent = node->b[0]; + * b) node==last and there's another dup: + * -> *parent = node->b[0]; + * node->b[0]->b[1] = node->b[1]; + * (or (*parent)->b[1] = node->b[1] covers a and b) + * c) node==first != last: + * -> node->b[1]->b[0] = node->b[0]; + * last->b[1] = node->b[1]; + * (or (*parent)->b[1] = node->b[1] covers a,b,c) + * d) node!=first && !=last: + * -> node->b[1]->b[0] = node->b[0]; + * node->b[0]->b[1] = node->b[1]; + * a,b,c,d can be simplified as: + * ((node == first) ? last : node->b[0])->b[1] = node->b[1]; + * *((node == last) ? parent : &node->b[1]->b[0]) = node->b[0]; + */ + struct ceb_node *first, *last; + + last = ret; + first = _ceb_untag(last->b[1], 1); + + /* cases 1 and 2 below */ + if (!node || node == _ceb_untag(first->b[0], 1)) { + /* node unspecified or the first, remove the leaf and + * convert the first entry to it. + */ + ret = _ceb_untag(first->b[0], 1); // update return node + last->b[1] = first->b[1]; // new first (remains OK if last==first) + + if (ret->b[0] != _ceb_dotag(ret, 1) || ret->b[1] != _ceb_dotag(ret, 1)) { + /* not the nodeless leaf, a node exists, put it + * on the first and update its parent. + */ + first->b[0] = ret->b[0]; + first->b[1] = ret->b[1]; + nparent->b[npside] = _ceb_dotag(first, 0); + } + else { + /* first becomes the nodeless leaf since we only keep its leaf */ + first->b[0] = first->b[1] = _ceb_dotag(first, 1); + } + /* first becomes a leaf, it must be tagged */ + if (last != first) + _ceb_untag(last->b[1], 1)->b[0] = _ceb_dotag(first, 1); + /* done */ + } else { + /* case 3: the node to delete is a dup, we only have to + * manipulate the list. + */ + ret = node; + ((node == first) ? last : _ceb_untag(node->b[0], 1))->b[1] = node->b[1]; + *((node == last) ? &lparent->b[lpside] : &_ceb_untag(node->b[1], 1)->b[0]) = node->b[0]; + /* done */ + } + goto mark_and_leave; + } + + /* ok below the returned value is a real leaf, we have to adjust the tree */ + if (ret == node || !node) { if (&lparent->b[0] == root) { /* there was a single entry, this one, so we're just @@ -1354,7 +1729,7 @@ struct ceb_node *_cebu_delete(struct ceb_node **root, /* we're removing the node-less item, the parent will * take this role. */ - lparent->b[0] = lparent->b[1] = lparent; + lparent->b[0] = lparent->b[1] = _ceb_dotag(lparent, 1); goto mark_and_leave; } @@ -1364,7 +1739,7 @@ struct ceb_node *_cebu_delete(struct ceb_node **root, */ lparent->b[0] = ret->b[0]; lparent->b[1] = ret->b[1]; - nparent->b[npside] = lparent; + nparent->b[npside] = _ceb_dotag(lparent, 0); mark_and_leave: /* now mark the node as deleted */ @@ -1374,239 +1749,19 @@ done: return ret; } -/* - * Functions used to dump trees in Dot format. - */ - -/* dump the root and its link to the first node or leaf */ -__attribute__((unused)) -static void cebu_default_dump_root(ptrdiff_t kofs, enum ceb_key_type key_type, struct ceb_node *const *root, const void *ctx) -{ - const struct ceb_node *node; - - printf(" \"%lx_n\" [label=\"root\\n%lx\"]\n", (long)root, (long)root); - - node = *root; - if (node) { - /* under the root we've either a node or the first leaf */ - printf(" \"%lx_n\" -> \"%lx_%c\" [label=\"B\" arrowsize=0.66];\n", - (long)root, (long)node, - (node->b[0] == node->b[1]) ? 'l' : 'n'); - } -} - -/* dump a node */ -__attribute__((unused)) -static void cebu_default_dump_node(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx) -{ - unsigned long long int_key = 0; - uint64_t pxor, lxor, rxor; - - switch (key_type) { - case CEB_KT_ADDR: - int_key = (uintptr_t)node; - break; - case CEB_KT_U32: - int_key = NODEK(node, kofs)->u32; - break; - case CEB_KT_U64: - int_key = NODEK(node, kofs)->u64; - break; - default: - break; - } - - /* xor of the keys of the two lower branches */ - pxor = _xor_branches(kofs, key_type, 0, 0, NULL, - node->b[0], node->b[1]); - - /* xor of the keys of the left branch's lower branches */ - lxor = _xor_branches(kofs, key_type, 0, 0, NULL, - (((struct ceb_node*)node->b[0])->b[0]), - (((struct ceb_node*)node->b[0])->b[1])); - - /* xor of the keys of the right branch's lower branches */ - rxor = _xor_branches(kofs, key_type, 0, 0, NULL, - (((struct ceb_node*)node->b[1])->b[0]), - (((struct ceb_node*)node->b[1])->b[1])); - - switch (key_type) { - case CEB_KT_ADDR: - case CEB_KT_U32: - case CEB_KT_U64: - printf(" \"%lx_n\" [label=\"%lx\\nlev=%d bit=%d\\nkey=%llu\" fillcolor=\"lightskyblue1\"%s];\n", - (long)node, (long)node, level, flsnz(pxor) - 1, int_key, (ctx == node) ? " color=red" : ""); - - printf(" \"%lx_n\" -> \"%lx_%c\" [label=\"L\" arrowsize=0.66 %s];\n", - (long)node, (long)node->b[0], - (lxor < pxor && ((struct ceb_node*)node->b[0])->b[0] != ((struct ceb_node*)node->b[0])->b[1]) ? 'n' : 'l', - (node == node->b[0]) ? " dir=both" : ""); - - printf(" \"%lx_n\" -> \"%lx_%c\" [label=\"R\" arrowsize=0.66 %s];\n", - (long)node, (long)node->b[1], - (rxor < pxor && ((struct ceb_node*)node->b[1])->b[0] != ((struct ceb_node*)node->b[1])->b[1]) ? 'n' : 'l', - (node == node->b[1]) ? " dir=both" : ""); - break; - case CEB_KT_MB: - break; - case CEB_KT_IM: - break; - case CEB_KT_ST: - printf(" \"%lx_n\" [label=\"%lx\\nlev=%d bit=%ld\\nkey=\\\"%s\\\"\" fillcolor=\"lightskyblue1\"%s];\n", - (long)node, (long)node, level, (long)pxor, NODEK(node, kofs)->str, (ctx == node) ? " color=red" : ""); - - printf(" \"%lx_n\" -> \"%lx_%c\" [label=\"L\" arrowsize=0.66 %s];\n", - (long)node, (long)node->b[0], - (lxor > pxor && ((struct ceb_node*)node->b[0])->b[0] != ((struct ceb_node*)node->b[0])->b[1]) ? 'n' : 'l', - (node == node->b[0]) ? " dir=both" : ""); - - printf(" \"%lx_n\" -> \"%lx_%c\" [label=\"R\" arrowsize=0.66 %s];\n", - (long)node, (long)node->b[1], - (rxor > pxor && ((struct ceb_node*)node->b[1])->b[0] != ((struct ceb_node*)node->b[1])->b[1]) ? 'n' : 'l', - (node == node->b[1]) ? " dir=both" : ""); - break; - case CEB_KT_IS: - printf(" \"%lx_n\" [label=\"%lx\\nlev=%d bit=%ld\\nkey=\\\"%s\\\"\" fillcolor=\"lightskyblue1\"%s];\n", - (long)node, (long)node, level, (long)pxor, NODEK(node, kofs)->ptr, (ctx == node) ? " color=red" : ""); - - printf(" \"%lx_n\" -> \"%lx_%c\" [label=\"L\" arrowsize=0.66 %s];\n", - (long)node, (long)node->b[0], - (lxor > pxor && ((struct ceb_node*)node->b[0])->b[0] != ((struct ceb_node*)node->b[0])->b[1]) ? 'n' : 'l', - (node == node->b[0]) ? " dir=both" : ""); - - printf(" \"%lx_n\" -> \"%lx_%c\" [label=\"R\" arrowsize=0.66 %s];\n", - (long)node, (long)node->b[1], - (rxor > pxor && ((struct ceb_node*)node->b[1])->b[0] != ((struct ceb_node*)node->b[1])->b[1]) ? 'n' : 'l', - (node == node->b[1]) ? " dir=both" : ""); - break; - } -} - -/* dump a leaf */ -__attribute__((unused)) -static void cebu_default_dump_leaf(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx) -{ - unsigned long long int_key = 0; - uint64_t pxor; - - switch (key_type) { - case CEB_KT_ADDR: - int_key = (uintptr_t)node; - break; - case CEB_KT_U32: - int_key = NODEK(node, kofs)->u32; - break; - case CEB_KT_U64: - int_key = NODEK(node, kofs)->u64; - break; - default: - break; - } - - /* xor of the keys of the two lower branches */ - pxor = _xor_branches(kofs, key_type, 0, 0, NULL, - node->b[0], node->b[1]); - - switch (key_type) { - case CEB_KT_ADDR: - case CEB_KT_U32: - case CEB_KT_U64: - if (node->b[0] == node->b[1]) - printf(" \"%lx_l\" [label=\"%lx\\nlev=%d\\nkey=%llu\\n\" fillcolor=\"green\"%s];\n", - (long)node, (long)node, level, int_key, (ctx == node) ? " color=red" : ""); - else - printf(" \"%lx_l\" [label=\"%lx\\nlev=%d bit=%d\\nkey=%llu\\n\" fillcolor=\"yellow\"%s];\n", - (long)node, (long)node, level, flsnz(pxor) - 1, int_key, (ctx == node) ? " color=red" : ""); - break; - case CEB_KT_MB: - break; - case CEB_KT_IM: - break; - case CEB_KT_ST: - if (node->b[0] == node->b[1]) - printf(" \"%lx_l\" [label=\"%lx\\nlev=%d\\nkey=\\\"%s\\\"\\n\" fillcolor=\"green\"%s];\n", - (long)node, (long)node, level, NODEK(node, kofs)->str, (ctx == node) ? " color=red" : ""); - else - printf(" \"%lx_l\" [label=\"%lx\\nlev=%d bit=%ld\\nkey=\\\"%s\\\"\\n\" fillcolor=\"yellow\"%s];\n", - (long)node, (long)node, level, (long)pxor, NODEK(node, kofs)->str, (ctx == node) ? " color=red" : ""); - break; - case CEB_KT_IS: - if (node->b[0] == node->b[1]) - printf(" \"%lx_l\" [label=\"%lx\\nlev=%d\\nkey=\\\"%s\\\"\\n\" fillcolor=\"green\"%s];\n", - (long)node, (long)node, level, NODEK(node, kofs)->ptr, (ctx == node) ? " color=red" : ""); - else - printf(" \"%lx_l\" [label=\"%lx\\nlev=%d bit=%ld\\nkey=\\\"%s\\\"\\n\" fillcolor=\"yellow\"%s];\n", - (long)node, (long)node, level, (long)pxor, NODEK(node, kofs)->ptr, (ctx == node) ? " color=red" : ""); - break; - } -} - -/* Dumps a tree through the specified callbacks, falling back to the default - * callbacks above if left NULL. - */ -__attribute__((unused)) -static const struct ceb_node *cebu_default_dump_tree(ptrdiff_t kofs, enum ceb_key_type key_type, struct ceb_node *const *root, - uint64_t pxor, const void *last, int level, const void *ctx, - void (*root_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, struct ceb_node *const *root, const void *ctx), - void (*node_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx), - void (*leaf_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx)) -{ - const struct ceb_node *node = *root; - uint64_t xor; - - if (!node) /* empty tree */ - return node; - - if (!root_dump) - root_dump = cebu_default_dump_root; - - if (!node_dump) - node_dump = cebu_default_dump_node; - - if (!leaf_dump) - leaf_dump = cebu_default_dump_leaf; - - if (!level) { - /* dump the first arrow */ - root_dump(kofs, key_type, root, ctx); - } - - /* regular nodes, all branches are canonical */ - - if (node->b[0] == node->b[1]) { - /* first inserted leaf */ - leaf_dump(kofs, key_type, node, level, ctx); - return node; - } - - xor = _xor_branches(kofs, key_type, 0, 0, NULL, - node->b[0], node->b[1]); - - switch (key_type) { - case CEB_KT_ADDR: - case CEB_KT_U32: - case CEB_KT_U64: - if (pxor && xor >= pxor) { - /* that's a leaf for a scalar type */ - leaf_dump(kofs, key_type, node, level, ctx); - return node; - } - break; - default: - if (pxor && xor <= pxor) { - /* that's a leaf for a non-scalar type */ - leaf_dump(kofs, key_type, node, level, ctx); - return node; - } - break; - } - - /* that's a regular node */ - node_dump(kofs, key_type, node, level, ctx); - - last = cebu_default_dump_tree(kofs, key_type, &node->b[0], xor, last, level + 1, ctx, root_dump, node_dump, leaf_dump); - return cebu_default_dump_tree(kofs, key_type, &node->b[1], xor, last, level + 1, ctx, root_dump, node_dump, leaf_dump); -} +//#if defined(CEB_ENABLE_DUMP) +/* The dump functions are in cebtree-dbg.c */ +void ceb_imm_default_dump_root(ptrdiff_t kofs, enum ceb_key_type key_type, struct ceb_root *const *root, const void *ctx, int sub); +void ceb_imm_default_dump_node(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub); +void ceb_imm_default_dump_dups(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub); +void ceb_imm_default_dump_leaf(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub); +const struct ceb_node *ceb_imm_default_dump_tree(ptrdiff_t kofs, enum ceb_key_type key_type, struct ceb_root *const *root, + uint64_t pxor, const void *last, int level, const void *ctx, int sub, + void (*root_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, struct ceb_root *const *root, const void *ctx, int sub), + void (*node_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub), + void (*dups_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub), + void (*leaf_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub)); +//#endif /* CEB_ENABLE_DUMP */ #endif /* _CEBTREE_PRV_H */ diff --git a/include/import/cebtree.h b/include/import/cebtree.h index 25d3853dd..5ebb9abae 100644 --- a/include/import/cebtree.h +++ b/include/import/cebtree.h @@ -28,56 +28,83 @@ #define _CEBTREE_H #include -#include "ebtree.h" + +/* offsetof() is provided as a builtin starting with gcc-4.1 */ +#ifndef offsetof +# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) +# define offsetof(type, field) __builtin_offsetof(type, field) +# else +# define offsetof(type, field) ((__size_t)(__uintptr_t)((const volatile void *)&((type *)0)->field)) +# endif +#endif + +/* Linux-like "container_of". It returns a pointer to the structure of type + * which has its member stored at address . + */ +#ifndef container_of +#define container_of(ptr, type, name) ((type *)(((char *)(ptr)) - offsetof(type, name))) +#endif + +/* returns a pointer to the structure of type which has its member + * stored at address , unless is 0, in which case 0 is returned. + */ +#ifndef container_of_safe +#define container_of_safe(ptr, type, name) \ + ({ void *__p = (ptr); \ + __p ? container_of(__p, type, name) : (type *)0; \ + }) +#endif + +/* This is what a tagged pointer points to, as found on the root or any branch. + * It's only a forward declaration so that it is never directly dereferenced. + */ +struct ceb_root; /* Standard node when using absolute pointers */ struct ceb_node { - struct ceb_node *b[2]; /* branches: 0=left, 1=right */ + struct ceb_root *b[2]; /* branches: 0=left, 1=right */ }; +/* initializes a root as an empty tree */ +static inline void ceb_init_root(struct ceb_root **root) +{ + *root = NULL; +} + +/* initializes a node as not belonging to a tree */ +static inline void ceb_init_node(struct ceb_node *node) +{ + node->b[0] = NULL; +} + /* indicates whether a valid node is in a tree or not */ static inline int ceb_intree(const struct ceb_node *node) { return !!node->b[0]; } -/* tag an untagged pointer */ -static inline struct ceb_node *__ceb_dotag(const struct ceb_node *node) +/* indicates whether a root is empty or not */ +static inline int ceb_isempty(struct ceb_root * const*root) { - return (struct ceb_node *)((size_t)node + 1); + return !*root; } -/* untag a tagged pointer */ -static inline struct ceb_node *__ceb_untag(const struct ceb_node *node) +/* returns a pointer to the key from the node and offset, where node is + * assumed to be non-null. + */ +static inline void *_ceb_key_ptr(const struct ceb_node *node, ptrdiff_t kofs) { - return (struct ceb_node *)((size_t)node - 1); + return (void*)((char *)node + kofs); } -/* clear a pointer's tag */ -static inline struct ceb_node *__ceb_clrtag(const struct ceb_node *node) +/* returns a pointer to the key from the node and offset if node is non-null, + * otherwise null. I.e. this is made to safely return a pointer to the key + * location from the return of a lookup operation. + */ +static inline void *ceb_key_ptr(const struct ceb_node *node, ptrdiff_t kofs) { - return (struct ceb_node *)((size_t)node & ~((size_t)1)); + return node ? _ceb_key_ptr(node, kofs) : NULL; } -/* returns whether a pointer is tagged */ -static inline int __ceb_tagged(const struct ceb_node *node) -{ - return !!((size_t)node & 1); -} - -/* returns an integer equivalent of the pointer */ -static inline size_t __ceb_intptr(struct ceb_node *tree) -{ - return (size_t)tree; -} - -///* returns true if at least one of the branches is a subtree node, indicating -// * that the current node is at the top of a duplicate sub-tree and that all -// * values below it are the same. -// */ -//static inline int __ceb_is_dup(const struct ceb_node *node) -//{ -// return __ceb_tagged((struct ceb_node *)(__ceb_intptr(node->l) | __ceb_intptr(node->r))); -//} #endif /* _CEBTREE_H */ diff --git a/include/import/cebu32_tree.h b/include/import/cebu32_tree.h deleted file mode 100644 index 5972a0a26..000000000 --- a/include/import/cebu32_tree.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Compact Elastic Binary Trees - exported functions operating on u32 keys - * - * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "cebtree.h" -#include - -/* simpler version */ -struct ceb_node *cebu32_insert(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebu32_first(struct ceb_node **root); -struct ceb_node *cebu32_last(struct ceb_node **root); -struct ceb_node *cebu32_lookup(struct ceb_node **root, uint32_t key); -struct ceb_node *cebu32_lookup_le(struct ceb_node **root, uint32_t key); -struct ceb_node *cebu32_lookup_lt(struct ceb_node **root, uint32_t key); -struct ceb_node *cebu32_lookup_ge(struct ceb_node **root, uint32_t key); -struct ceb_node *cebu32_lookup_gt(struct ceb_node **root, uint32_t key); -struct ceb_node *cebu32_next(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebu32_prev(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebu32_delete(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebu32_pick(struct ceb_node **root, uint32_t key); -void cebu32_default_dump(struct ceb_node **ceb_root, const char *label, const void *ctx); - -/* version taking a key offset */ -struct ceb_node *cebu32_ofs_insert(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebu32_ofs_first(struct ceb_node **root, ptrdiff_t kofs); -struct ceb_node *cebu32_ofs_last(struct ceb_node **root, ptrdiff_t kofs); -struct ceb_node *cebu32_ofs_lookup(struct ceb_node **root, ptrdiff_t kofs, uint32_t key); -struct ceb_node *cebu32_ofs_lookup_le(struct ceb_node **root, ptrdiff_t kofs, uint32_t key); -struct ceb_node *cebu32_ofs_lookup_lt(struct ceb_node **root, ptrdiff_t kofs, uint32_t key); -struct ceb_node *cebu32_ofs_lookup_ge(struct ceb_node **root, ptrdiff_t kofs, uint32_t key); -struct ceb_node *cebu32_ofs_lookup_gt(struct ceb_node **root, ptrdiff_t kofs, uint32_t key); -struct ceb_node *cebu32_ofs_next(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebu32_ofs_prev(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebu32_ofs_delete(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebu32_ofs_pick(struct ceb_node **root, ptrdiff_t kofs, uint32_t key); -void cebu32_ofs_default_dump(struct ceb_node **root, ptrdiff_t kofs, const char *label, const void *ctx); diff --git a/include/import/cebu64_tree.h b/include/import/cebu64_tree.h deleted file mode 100644 index 15f8c0f8a..000000000 --- a/include/import/cebu64_tree.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Compact Elastic Binary Trees - exported functions for operations on u64 keys - * - * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "cebtree.h" -#include - -/* simpler version */ -struct ceb_node *cebu64_insert(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebu64_first(struct ceb_node **root); -struct ceb_node *cebu64_last(struct ceb_node **root); -struct ceb_node *cebu64_lookup(struct ceb_node **root, uint64_t key); -struct ceb_node *cebu64_lookup_le(struct ceb_node **root, uint64_t key); -struct ceb_node *cebu64_lookup_lt(struct ceb_node **root, uint64_t key); -struct ceb_node *cebu64_lookup_ge(struct ceb_node **root, uint64_t key); -struct ceb_node *cebu64_lookup_gt(struct ceb_node **root, uint64_t key); -struct ceb_node *cebu64_next(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebu64_prev(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebu64_delete(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebu64_pick(struct ceb_node **root, uint64_t key); -void cebu64_default_dump(struct ceb_node **ceb_root, const char *label, const void *ctx); - -/* version taking a key offset */ -struct ceb_node *cebu64_ofs_insert(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebu64_ofs_first(struct ceb_node **root, ptrdiff_t kofs); -struct ceb_node *cebu64_ofs_last(struct ceb_node **root, ptrdiff_t kofs); -struct ceb_node *cebu64_ofs_lookup(struct ceb_node **root, ptrdiff_t kofs, uint64_t key); -struct ceb_node *cebu64_ofs_lookup_le(struct ceb_node **root, ptrdiff_t kofs, uint64_t key); -struct ceb_node *cebu64_ofs_lookup_lt(struct ceb_node **root, ptrdiff_t kofs, uint64_t key); -struct ceb_node *cebu64_ofs_lookup_ge(struct ceb_node **root, ptrdiff_t kofs, uint64_t key); -struct ceb_node *cebu64_ofs_lookup_gt(struct ceb_node **root, ptrdiff_t kofs, uint64_t key); -struct ceb_node *cebu64_ofs_next(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebu64_ofs_prev(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebu64_ofs_delete(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebu64_ofs_pick(struct ceb_node **root, ptrdiff_t kofs, uint64_t key); -void cebu64_ofs_default_dump(struct ceb_node **root, ptrdiff_t kofs, const char *label, const void *ctx); diff --git a/include/import/cebua_tree.h b/include/import/cebua_tree.h deleted file mode 100644 index 86758b599..000000000 --- a/include/import/cebua_tree.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Compact Elastic Binary Trees - exported functions operating on addr keys - * - * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "cebtree.h" - -/* simpler version */ -struct ceb_node *cebua_insert(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebua_first(struct ceb_node **root); -struct ceb_node *cebua_last(struct ceb_node **root); -struct ceb_node *cebua_lookup(struct ceb_node **root, const void *key); -struct ceb_node *cebua_lookup_le(struct ceb_node **root, const void *key); -struct ceb_node *cebua_lookup_lt(struct ceb_node **root, const void *key); -struct ceb_node *cebua_lookup_ge(struct ceb_node **root, const void *key); -struct ceb_node *cebua_lookup_gt(struct ceb_node **root, const void *key); -struct ceb_node *cebua_next(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebua_prev(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebua_delete(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebua_pick(struct ceb_node **root, const void *key); -void cebua_default_dump(struct ceb_node **root, const char *label, const void *ctx); - -/* version taking a key offset */ -struct ceb_node *cebua_ofs_insert(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebua_ofs_first(struct ceb_node **root, ptrdiff_t kofs); -struct ceb_node *cebua_ofs_last(struct ceb_node **root, ptrdiff_t kofs); -struct ceb_node *cebua_ofs_lookup(struct ceb_node **root, ptrdiff_t kofs, const void *key); -struct ceb_node *cebua_ofs_lookup_le(struct ceb_node **root, ptrdiff_t kofs, const void *key); -struct ceb_node *cebua_ofs_lookup_lt(struct ceb_node **root, ptrdiff_t kofs, const void *key); -struct ceb_node *cebua_ofs_lookup_ge(struct ceb_node **root, ptrdiff_t kofs, const void *key); -struct ceb_node *cebua_ofs_lookup_gt(struct ceb_node **root, ptrdiff_t kofs, const void *key); -struct ceb_node *cebua_ofs_next(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebua_ofs_prev(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebua_ofs_delete(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebua_ofs_pick(struct ceb_node **root, ptrdiff_t kofs, const void *key); -void cebua_ofs_default_dump(struct ceb_node **root, ptrdiff_t kofs, const char *label, const void *ctx); diff --git a/include/import/cebub_tree.h b/include/import/cebub_tree.h deleted file mode 100644 index 55a0be044..000000000 --- a/include/import/cebub_tree.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Compact Elastic Binary Trees - exported functions operating on mb keys - * - * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "cebtree.h" - -/* simpler version */ -struct ceb_node *cebub_insert(struct ceb_node **root, struct ceb_node *node, size_t len); -struct ceb_node *cebub_first(struct ceb_node **root); -struct ceb_node *cebub_last(struct ceb_node **root); -struct ceb_node *cebub_lookup(struct ceb_node **root, const void *key, size_t len); -struct ceb_node *cebub_lookup_le(struct ceb_node **root, const void *key, size_t len); -struct ceb_node *cebub_lookup_lt(struct ceb_node **root, const void *key, size_t len); -struct ceb_node *cebub_lookup_ge(struct ceb_node **root, const void *key, size_t len); -struct ceb_node *cebub_lookup_gt(struct ceb_node **root, const void *key, size_t len); -struct ceb_node *cebub_next(struct ceb_node **root, struct ceb_node *node, size_t len); -struct ceb_node *cebub_prev(struct ceb_node **root, struct ceb_node *node, size_t len); -struct ceb_node *cebub_delete(struct ceb_node **root, struct ceb_node *node, size_t len); -struct ceb_node *cebub_pick(struct ceb_node **root, const void *key, size_t len); - -/* version taking a key offset */ -struct ceb_node *cebub_ofs_insert(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node, size_t len); -struct ceb_node *cebub_ofs_first(struct ceb_node **root, ptrdiff_t kofs, size_t len); -struct ceb_node *cebub_ofs_last(struct ceb_node **root, ptrdiff_t kofs, size_t len); -struct ceb_node *cebub_ofs_lookup(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len); -struct ceb_node *cebub_ofs_lookup_le(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len); -struct ceb_node *cebub_ofs_lookup_lt(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len); -struct ceb_node *cebub_ofs_lookup_ge(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len); -struct ceb_node *cebub_ofs_lookup_gt(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len); -struct ceb_node *cebub_ofs_next(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node, size_t len); -struct ceb_node *cebub_ofs_prev(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node, size_t len); -struct ceb_node *cebub_ofs_delete(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node, size_t len); -struct ceb_node *cebub_ofs_pick(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len); diff --git a/include/import/cebuib_tree.h b/include/import/cebuib_tree.h deleted file mode 100644 index 6d6c2c1ea..000000000 --- a/include/import/cebuib_tree.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Compact Elastic Binary Trees - exported functions operating on indirect blocks - * - * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "cebtree.h" - -/* simpler version */ -struct ceb_node *cebuib_insert(struct ceb_node **root, struct ceb_node *node, size_t len); -struct ceb_node *cebuib_first(struct ceb_node **root); -struct ceb_node *cebuib_last(struct ceb_node **root); -struct ceb_node *cebuib_lookup(struct ceb_node **root, const void *key, size_t len); -struct ceb_node *cebuib_lookup_le(struct ceb_node **root, const void *key, size_t len); -struct ceb_node *cebuib_lookup_lt(struct ceb_node **root, const void *key, size_t len); -struct ceb_node *cebuib_lookup_ge(struct ceb_node **root, const void *key, size_t len); -struct ceb_node *cebuib_lookup_gt(struct ceb_node **root, const void *key, size_t len); -struct ceb_node *cebuib_next(struct ceb_node **root, struct ceb_node *node, size_t len); -struct ceb_node *cebuib_prev(struct ceb_node **root, struct ceb_node *node, size_t len); -struct ceb_node *cebuib_delete(struct ceb_node **root, struct ceb_node *node, size_t len); -struct ceb_node *cebuib_pick(struct ceb_node **root, const void *key, size_t len); - -/* version taking a key offset */ -struct ceb_node *cebuib_ofs_insert(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node, size_t len); -struct ceb_node *cebuib_ofs_first(struct ceb_node **root, ptrdiff_t kofs, size_t len); -struct ceb_node *cebuib_ofs_last(struct ceb_node **root, ptrdiff_t kofs, size_t len); -struct ceb_node *cebuib_ofs_lookup(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len); -struct ceb_node *cebuib_ofs_lookup_le(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len); -struct ceb_node *cebuib_ofs_lookup_lt(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len); -struct ceb_node *cebuib_ofs_lookup_ge(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len); -struct ceb_node *cebuib_ofs_lookup_gt(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len); -struct ceb_node *cebuib_ofs_next(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node, size_t len); -struct ceb_node *cebuib_ofs_prev(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node, size_t len); -struct ceb_node *cebuib_ofs_delete(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node, size_t len); -struct ceb_node *cebuib_ofs_pick(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len); diff --git a/include/import/cebuis_tree.h b/include/import/cebuis_tree.h deleted file mode 100644 index 377bb85a9..000000000 --- a/include/import/cebuis_tree.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Compact Elastic Binary Trees - exported functions operating on indirect strings - * - * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "cebtree.h" - -/* simpler version */ -struct ceb_node *cebuis_insert(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebuis_first(struct ceb_node **root); -struct ceb_node *cebuis_last(struct ceb_node **root); -struct ceb_node *cebuis_lookup(struct ceb_node **root, const void *key); -struct ceb_node *cebuis_lookup_le(struct ceb_node **root, const void *key); -struct ceb_node *cebuis_lookup_lt(struct ceb_node **root, const void *key); -struct ceb_node *cebuis_lookup_ge(struct ceb_node **root, const void *key); -struct ceb_node *cebuis_lookup_gt(struct ceb_node **root, const void *key); -struct ceb_node *cebuis_next(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebuis_prev(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebuis_delete(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebuis_pick(struct ceb_node **root, const void *key); -void cebuis_default_dump(struct ceb_node **root, const char *label, const void *ctx); - -/* version taking a key offset */ -struct ceb_node *cebuis_ofs_insert(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebuis_ofs_first(struct ceb_node **root, ptrdiff_t kofs); -struct ceb_node *cebuis_ofs_last(struct ceb_node **root, ptrdiff_t kofs); -struct ceb_node *cebuis_ofs_lookup(struct ceb_node **root, ptrdiff_t kofs, const void *key); -struct ceb_node *cebuis_ofs_lookup_le(struct ceb_node **root, ptrdiff_t kofs, const void *key); -struct ceb_node *cebuis_ofs_lookup_lt(struct ceb_node **root, ptrdiff_t kofs, const void *key); -struct ceb_node *cebuis_ofs_lookup_ge(struct ceb_node **root, ptrdiff_t kofs, const void *key); -struct ceb_node *cebuis_ofs_lookup_gt(struct ceb_node **root, ptrdiff_t kofs, const void *key); -struct ceb_node *cebuis_ofs_next(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebuis_ofs_prev(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebuis_ofs_delete(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebuis_ofs_pick(struct ceb_node **root, ptrdiff_t kofs, const void *key); -void cebuis_ofs_default_dump(struct ceb_node **root, ptrdiff_t kofs, const char *label, const void *ctx); diff --git a/include/import/cebul_tree.h b/include/import/cebul_tree.h deleted file mode 100644 index 2080b6e19..000000000 --- a/include/import/cebul_tree.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Compact Elastic Binary Trees - exported functions operating on ulong keys - * - * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "cebtree.h" - -/* simpler version */ -struct ceb_node *cebul_insert(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebul_first(struct ceb_node **root); -struct ceb_node *cebul_last(struct ceb_node **root); -struct ceb_node *cebul_lookup(struct ceb_node **root, unsigned long key); -struct ceb_node *cebul_lookup_le(struct ceb_node **root, unsigned long key); -struct ceb_node *cebul_lookup_lt(struct ceb_node **root, unsigned long key); -struct ceb_node *cebul_lookup_ge(struct ceb_node **root, unsigned long key); -struct ceb_node *cebul_lookup_gt(struct ceb_node **root, unsigned long key); -struct ceb_node *cebul_next(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebul_prev(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebul_delete(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebul_pick(struct ceb_node **root, unsigned long key); -void cebul_default_dump(struct ceb_node **ceb_root, const char *label, const void *ctx); - -/* version taking a key offset */ -struct ceb_node *cebul_ofs_insert(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebul_ofs_first(struct ceb_node **root, ptrdiff_t kofs); -struct ceb_node *cebul_ofs_last(struct ceb_node **root, ptrdiff_t kofs); -struct ceb_node *cebul_ofs_lookup(struct ceb_node **root, ptrdiff_t kofs, unsigned long key); -struct ceb_node *cebul_ofs_lookup_le(struct ceb_node **root, ptrdiff_t kofs, unsigned long key); -struct ceb_node *cebul_ofs_lookup_lt(struct ceb_node **root, ptrdiff_t kofs, unsigned long key); -struct ceb_node *cebul_ofs_lookup_ge(struct ceb_node **root, ptrdiff_t kofs, unsigned long key); -struct ceb_node *cebul_ofs_lookup_gt(struct ceb_node **root, ptrdiff_t kofs, unsigned long key); -struct ceb_node *cebul_ofs_next(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebul_ofs_prev(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebul_ofs_delete(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebul_ofs_pick(struct ceb_node **root, ptrdiff_t kofs, unsigned long key); -void cebul_ofs_default_dump(struct ceb_node **root, ptrdiff_t kofs, const char *label, const void *ctx); diff --git a/include/import/cebus_tree.h b/include/import/cebus_tree.h deleted file mode 100644 index f88f1d6da..000000000 --- a/include/import/cebus_tree.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Compact Elastic Binary Trees - exported functions operating on string keys - * - * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "cebtree.h" - -/* simpler version */ -struct ceb_node *cebus_insert(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebus_first(struct ceb_node **root); -struct ceb_node *cebus_last(struct ceb_node **root); -struct ceb_node *cebus_lookup(struct ceb_node **root, const void *key); -struct ceb_node *cebus_lookup_le(struct ceb_node **root, const void *key); -struct ceb_node *cebus_lookup_lt(struct ceb_node **root, const void *key); -struct ceb_node *cebus_lookup_ge(struct ceb_node **root, const void *key); -struct ceb_node *cebus_lookup_gt(struct ceb_node **root, const void *key); -struct ceb_node *cebus_next(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebus_prev(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebus_delete(struct ceb_node **root, struct ceb_node *node); -struct ceb_node *cebus_pick(struct ceb_node **root, const void *key); -void cebus_default_dump(struct ceb_node **root, const char *label, const void *ctx); - -/* version taking a key offset */ -struct ceb_node *cebus_ofs_insert(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebus_ofs_first(struct ceb_node **root, ptrdiff_t kofs); -struct ceb_node *cebus_ofs_last(struct ceb_node **root, ptrdiff_t kofs); -struct ceb_node *cebus_ofs_lookup(struct ceb_node **root, ptrdiff_t kofs, const void *key); -struct ceb_node *cebus_ofs_lookup_le(struct ceb_node **root, ptrdiff_t kofs, const void *key); -struct ceb_node *cebus_ofs_lookup_lt(struct ceb_node **root, ptrdiff_t kofs, const void *key); -struct ceb_node *cebus_ofs_lookup_ge(struct ceb_node **root, ptrdiff_t kofs, const void *key); -struct ceb_node *cebus_ofs_lookup_gt(struct ceb_node **root, ptrdiff_t kofs, const void *key); -struct ceb_node *cebus_ofs_next(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebus_ofs_prev(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebus_ofs_delete(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node); -struct ceb_node *cebus_ofs_pick(struct ceb_node **root, ptrdiff_t kofs, const void *key); -void cebus_ofs_default_dump(struct ceb_node **root, ptrdiff_t kofs, const char *label, const void *ctx); diff --git a/src/_ceb_addr.c b/src/_ceb_addr.c new file mode 100644 index 000000000..cced39f39 --- /dev/null +++ b/src/_ceb_addr.c @@ -0,0 +1,190 @@ +/* + * Compact Elastic Binary Trees - exported functions operating on addr keys + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/* NOTE: this file is only meant to be included from other C files. It will + * use the following private macros that must be defined by the caller: + * - CEB_KEY_TYPE: CEB_KT_ADDR + * - CEB_MKEY_PFX: function name prefix for multi-key (ceba) + * - CEB_UKEY_PFX: function name prefix for unique keys (cebua) + * + * There's no support for duplicates (we're indexing the node's address), nor + * for offsets (it makes no sense to apply an offset to the node's address). + */ +#include "cebtree-prv.h" + +/* + * Below are the functions that only support unique keys (_cebu_*) + */ + +/*****************************************************************************\ + * The declarations below always cause two functions to be declared, one * + * starting with "cebua_*" and one with "cebua_ofs_*" which takes a key * + * offset just after the root. The one without kofs just has this argument * + * omitted from its declaration and replaced with sizeof(struct ceb_node) in * + * the call to the underlying functions. * +\*****************************************************************************/ + +/* Inserts node into unique tree based on its own address + * Returns the inserted node or the one that has the same address. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _insert, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + return _ceb_insert(root, node, kofs, CEB_KEY_TYPE, 0, 0, node, NULL); +} + +/* return the first node or NULL if not found. */ +CEB_FDECL2(struct ceb_node *, CEB_UKEY_PFX, _first, struct ceb_root *const *, root, ptrdiff_t, kofs) +{ + return _ceb_first(root, kofs, CEB_KEY_TYPE, 0, NULL); +} + +/* return the last node or NULL if not found. */ +CEB_FDECL2(struct ceb_node *, CEB_UKEY_PFX, _last, struct ceb_root *const *, root, ptrdiff_t, kofs) +{ + return _ceb_last(root, kofs, CEB_KEY_TYPE, 0, NULL); +} + +/* look up the specified key, and returns either the node containing it, or + * NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key) +{ + return _ceb_lookup(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL); +} + +/* look up the specified key or the highest below it, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_le, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key) +{ + return _ceb_lookup_le(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL); +} + +/* look up highest key below the specified one, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_lt, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key) +{ + return _ceb_lookup_lt(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL); +} + +/* look up the specified key or the smallest above it, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_ge, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key) +{ + return _ceb_lookup_ge(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL); +} + +/* look up the smallest key above the specified one, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_gt, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key) +{ + return _ceb_lookup_gt(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL); +} + +/* search for the next node after the specified one, and return it, or NULL if + * not found. The approach consists in looking up that node, recalling the last + * time a left turn was made, and returning the first node along the right + * branch at that fork. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _next, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + return _ceb_next_unique(root, kofs, CEB_KEY_TYPE, 0, 0, node, NULL); +} + +/* search for the prev node before the specified one, and return it, or NULL if + * not found. The approach consists in looking up that node, recalling the last + * time a right turn was made, and returning the last node along the left + * branch at that fork. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _prev, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + return _ceb_prev_unique(root, kofs, CEB_KEY_TYPE, 0, 0, node, NULL); +} + +/* look up the specified node with its key and deletes it if found, and in any + * case, returns the node. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _delete, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + return _ceb_delete(root, node, kofs, CEB_KEY_TYPE, 0, 0, node, NULL); +} + +/* look up the specified key, and detaches it and returns it if found, or NULL + * if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _pick, struct ceb_root **, root, ptrdiff_t, kofs, const void *, key) +{ + return _ceb_delete(root, NULL, kofs, CEB_KEY_TYPE, 0, 0, key, NULL); +} + +/* + * Functions used to dump trees in Dot format. These are only enabled if + * CEB_ENABLE_DUMP is defined. + */ + +#if defined(CEB_ENABLE_DUMP) + +#include +#define TO_STR(x) _TO_STR(x) +#define _TO_STR(x) #x + +/* dumps a ceb_node tree using the default functions above. If a node matches + * , this one will be highlighted in red. If the value is non-null, + * only a subgraph will be printed. If it's null, and root is non-null, then + * the tree is dumped at once, otherwise if root is NULL, then a prologue is + * dumped when label is not NULL, or the epilogue when label is NULL. As a + * summary: + * sub root label + * 0 NULL NULL epilogue only (closing brace and LF) + * 0 NULL text prologue with as label + * 0 tree * prologue+tree+epilogue at once + * N>0 tree * only the tree, after a prologue and before an epilogue + */ +CEB_FDECL5(void, CEB_MKEY_PFX, _default_dump, struct ceb_root *const *, root, ptrdiff_t, kofs, const char *, label, const void *, ctx, int, sub) +{ + if (!sub && label) { + printf("\ndigraph " TO_STR(CEB_MKEY_PFX) "_tree {\n" + " fontname=\"fixed\";\n" + " fontsize=8\n" + " label=\"%s\"\n" + "", label); + + printf(" node [fontname=\"fixed\" fontsize=8 shape=\"box\" style=\"filled\" color=\"black\" fillcolor=\"white\"];\n" + " edge [fontname=\"fixed\" fontsize=8 style=\"solid\" color=\"magenta\" dir=\"forward\"];\n"); + } else + printf("\n### sub %d ###\n\n", sub); + + if (root) + ceb_imm_default_dump_tree(kofs, CEB_KEY_TYPE, root, 0, NULL, 0, ctx, sub, NULL, NULL, NULL, NULL); + + if (!sub && (root || !label)) + printf("}\n"); +} + +#endif /* CEB_ENABLE_DUMP */ diff --git a/src/_ceb_blk.c b/src/_ceb_blk.c new file mode 100644 index 000000000..d3683c929 --- /dev/null +++ b/src/_ceb_blk.c @@ -0,0 +1,342 @@ +/* + * Compact Elastic Binary Trees - exported functions operating on mb keys + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/* NOTE: this file is only meant to be included from other C files. It will + * use the following private macros that must be defined by the caller: + * - CEB_KEY_TYPE: CEB_KT_IM, CEB_KT_MB + * - CEB_KEY_MEMBER: member of the struct ceb_node holding the key + * - CEB_MKEY_PFX: function name prefix for multi-key + * - CEB_UKEY_PFX: function name prefix for unique keys + */ +#include "cebtree-prv.h" + +/* + * Below are the functions that support duplicate keys (_ceb_*) + */ + +/*****************************************************************************\ + * The declarations below always cause two functions to be declared, one * + * starting with "cebs_*" and one with "cebs_ofs_*" which takes a key offset * + * just after the root. The one without kofs just has this argument omitted * + * from its declaration and replaced with sizeof(struct ceb_node) in the * + * call to the underlying functions. * +\*****************************************************************************/ + +/* Inserts node into tree based on its key that immediately + * follows the node and for bytes. Returns the inserted node or the one + * that already contains the same key. + */ +CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _insert, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + int is_dup; + + return _ceb_insert(root, node, kofs, CEB_KEY_TYPE, 0, len, key, &is_dup); +} + +/* return the first node or NULL if not found. */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _first, struct ceb_root *const *, root, ptrdiff_t, kofs, size_t, len) +{ + int is_dup; + + return _ceb_first(root, kofs, CEB_KEY_TYPE, len, &is_dup); +} + +/* return the last node or NULL if not found. */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _last, struct ceb_root *const *, root, ptrdiff_t, kofs, size_t, len) +{ + int is_dup; + + return _ceb_last(root, kofs, CEB_KEY_TYPE, len, &is_dup); +} + +/* look up the specified key of length , and returns either the node + * containing it, or NULL if not found. + */ +CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _lookup, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key, size_t, len) +{ + int is_dup; + + return _ceb_lookup(root, kofs, CEB_KEY_TYPE, 0, len, key, &is_dup); +} + +/* look up the specified key or the highest below it, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _lookup_le, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key, size_t, len) +{ + int is_dup; + + return _ceb_lookup_le(root, kofs, CEB_KEY_TYPE, 0, len, key, &is_dup); +} + +/* look up highest key below the specified one, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _lookup_lt, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key, size_t, len) +{ + int is_dup; + + return _ceb_lookup_lt(root, kofs, CEB_KEY_TYPE, 0, len, key, &is_dup); +} + +/* look up the specified key or the smallest above it, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _lookup_ge, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key, size_t, len) +{ + int is_dup; + + return _ceb_lookup_ge(root, kofs, CEB_KEY_TYPE, 0, len, key, &is_dup); +} + +/* look up the smallest key above the specified one, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _lookup_gt, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key, size_t, len) +{ + int is_dup; + + return _ceb_lookup_gt(root, kofs, CEB_KEY_TYPE, 0, len, key, &is_dup); +} + +/* search for the next node after the specified one, and return it, or NULL if + * not found. The approach consists in looking up that node, recalling the last + * time a left turn was made, and returning the first node along the right + * branch at that fork. The field must correspond to the key length in + * bytes. + */ +CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _next_unique, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + int is_dup; + + return _ceb_next_unique(root, kofs, CEB_KEY_TYPE, 0, len, key, &is_dup); +} + +/* search for the prev node before the specified one, and return it, or NULL if + * not found. The approach consists in looking up that node, recalling the last + * time a right turn was made, and returning the last node along the left + * branch at that fork. The field must correspond to the key length in + * bytes. + */ +CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _prev_unique, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + int is_dup; + + return _ceb_prev_unique(root, kofs, CEB_KEY_TYPE, 0, len, key, &is_dup); +} + +/* search for the next node after the specified one containing the same value, + * and return it, or NULL if not found. + */ +CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _next_dup, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + + return _ceb_next_dup(root, kofs, CEB_KEY_TYPE, 0, len, key, node); +} + +/* search for the prev node before the specified one containing the same value, + * and return it, or NULL if not found. + */ +CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _prev_dup, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + + return _ceb_prev_dup(root, kofs, CEB_KEY_TYPE, 0, len, key, node); +} + +/* search for the next node after the specified one, and return it, or NULL if + * not found. The approach consists in looking up that node, recalling the last + * time a left turn was made, and returning the first node along the right + * branch at that fork. The field must correspond to the key length in + * bytes. + */ +CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _next, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + int is_dup; + + return _ceb_next(root, kofs, CEB_KEY_TYPE, 0, len, key, node, &is_dup); +} + +/* search for the prev node before the specified one, and return it, or NULL if + * not found. The approach consists in looking up that node, recalling the last + * time a right turn was made, and returning the last node along the left + * branch at that fork. The field must correspond to the key length in + * bytes. + */ +CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _prev, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + int is_dup; + + return _ceb_prev(root, kofs, CEB_KEY_TYPE, 0, len, key, node, &is_dup); +} + +/* look up the specified node with its key and deletes it if found, and in any + * case, returns the node. The field must correspond to the key length in + * bytes. + */ +CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _delete, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + int is_dup; + + return _ceb_delete(root, node, kofs, CEB_KEY_TYPE, 0, len, key, &is_dup); +} + +/* look up the specified key, and detaches it and returns it if found, or NULL + * if not found. The field must correspond to the key length in bytes. + */ +CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _pick, struct ceb_root **, root, ptrdiff_t, kofs, const void *, key, size_t, len) +{ + int is_dup; + + return _ceb_delete(root, NULL, kofs, CEB_KEY_TYPE, 0, len, key, &is_dup); +} + +/* + * Below are the functions that only support unique keys (_cebu_*) + */ + +/*****************************************************************************\ + * The declarations below always cause two functions to be declared, one * + * starting with "cebub_*" and one with "cebub_ofs_*" which takes a key * + * offset just after the root. The one without kofs just has this argument * + * omitted from its declaration and replaced with sizeof(struct ceb_node) in * + * the call to the underlying functions. * +\*****************************************************************************/ + +/* Inserts node into unique tree based on its key that + * immediately follows the node and for bytes. Returns the + * inserted node or the one that already contains the same key. + */ +CEB_FDECL4(struct ceb_node *, CEB_UKEY_PFX, _insert, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + + return _ceb_insert(root, node, kofs, CEB_KEY_TYPE, 0, len, key, NULL); +} + +/* return the first node or NULL if not found. */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _first, struct ceb_root *const *, root, ptrdiff_t, kofs, size_t, len) +{ + return _ceb_first(root, kofs, CEB_KEY_TYPE, len, NULL); +} + +/* return the last node or NULL if not found. */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _last, struct ceb_root *const *, root, ptrdiff_t, kofs, size_t, len) +{ + return _ceb_last(root, kofs, CEB_KEY_TYPE, len, NULL); +} + +/* look up the specified key of length , and returns either the node + * containing it, or NULL if not found. + */ +CEB_FDECL4(struct ceb_node *, CEB_UKEY_PFX, _lookup, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key, size_t, len) +{ + return _ceb_lookup(root, kofs, CEB_KEY_TYPE, 0, len, key, NULL); +} + +/* look up the specified key or the highest below it, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL4(struct ceb_node *, CEB_UKEY_PFX, _lookup_le, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key, size_t, len) +{ + return _ceb_lookup_le(root, kofs, CEB_KEY_TYPE, 0, len, key, NULL); +} + +/* look up highest key below the specified one, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL4(struct ceb_node *, CEB_UKEY_PFX, _lookup_lt, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key, size_t, len) +{ + return _ceb_lookup_lt(root, kofs, CEB_KEY_TYPE, 0, len, key, NULL); +} + +/* look up the specified key or the smallest above it, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL4(struct ceb_node *, CEB_UKEY_PFX, _lookup_ge, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key, size_t, len) +{ + return _ceb_lookup_ge(root, kofs, CEB_KEY_TYPE, 0, len, key, NULL); +} + +/* look up the smallest key above the specified one, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL4(struct ceb_node *, CEB_UKEY_PFX, _lookup_gt, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key, size_t, len) +{ + return _ceb_lookup_gt(root, kofs, CEB_KEY_TYPE, 0, len, key, NULL); +} + +/* search for the next node after the specified one, and return it, or NULL if + * not found. The approach consists in looking up that node, recalling the last + * time a left turn was made, and returning the first node along the right + * branch at that fork. The field must correspond to the key length in + * bytes. + */ +CEB_FDECL4(struct ceb_node *, CEB_UKEY_PFX, _next, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + + return _ceb_next_unique(root, kofs, CEB_KEY_TYPE, 0, len, key, NULL); +} + +/* search for the prev node before the specified one, and return it, or NULL if + * not found. The approach consists in looking up that node, recalling the last + * time a right turn was made, and returning the last node along the left + * branch at that fork. The field must correspond to the key length in + * bytes. + */ +CEB_FDECL4(struct ceb_node *, CEB_UKEY_PFX, _prev, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + + return _ceb_prev_unique(root, kofs, CEB_KEY_TYPE, 0, len, key, NULL); +} + +/* look up the specified node with its key and deletes it if found, and in any + * case, returns the node. The field must correspond to the key length in + * bytes. + */ +CEB_FDECL4(struct ceb_node *, CEB_UKEY_PFX, _delete, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + + return _ceb_delete(root, node, kofs, CEB_KEY_TYPE, 0, len, key, NULL); +} + +/* look up the specified key, and detaches it and returns it if found, or NULL + * if not found. The field must correspond to the key length in bytes. + */ +CEB_FDECL4(struct ceb_node *, CEB_UKEY_PFX, _pick, struct ceb_root **, root, ptrdiff_t, kofs, const void *, key, size_t, len) +{ + return _ceb_delete(root, NULL, kofs, CEB_KEY_TYPE, 0, len, key, NULL); +} diff --git a/src/_ceb_int.c b/src/_ceb_int.c new file mode 100644 index 000000000..086313f6a --- /dev/null +++ b/src/_ceb_int.c @@ -0,0 +1,466 @@ +/* + * Compact Elastic Binary Trees - exported functions operating on integer keys + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/* NOTE: this file is only meant to be included from other C files. It will + * use the following private macros that must be defined by the caller: + * - CEB_KEY_TYPE: uint32_t, uint64_t, unsigned long + * - CEB_KEY_MEMBER: member of the struct ceb_node holding the key + * - CEB_MKEY_PFX: function name prefix for multi-key + * - CEB_UKEY_PFX: function name prefix for unique keys + * + * The dump functions will only be build if CEB_ENABLE_DUMP is defined. + */ +#include "cebtree-prv.h" + +/* + * Below are the functions that support duplicate keys (_ceb_*) + */ + +/*****************************************************************************\ + * The declarations below always cause two functions to be declared, one * + * starting with "cebs_*" and one with "cebs_ofs_*" which takes a key offset * + * just after the root. The one without kofs just has this argument omitted * + * from its declaration and replaced with sizeof(struct ceb_node) in the * + * call to the underlying functions. * +\*****************************************************************************/ + +/* Inserts node into tree based on its key that immediately + * follows the node. Returns the inserted node or the one that already contains + * the same key. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _insert, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER; + int is_dup; + + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_insert(root, node, kofs, CEB_KT_U32, key, 0, NULL, &is_dup); + else + return _ceb_insert(root, node, kofs, CEB_KT_U64, 0, key, NULL, &is_dup); +} + +/* return the first node or NULL if not found. */ +CEB_FDECL2(struct ceb_node *, CEB_MKEY_PFX, _first, struct ceb_root *const *, root, ptrdiff_t, kofs) +{ + int is_dup; + + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_first(root, kofs, CEB_KT_U32, 0, &is_dup); + else + return _ceb_first(root, kofs, CEB_KT_U64, 0, &is_dup); +} + +/* return the last node or NULL if not found. */ +CEB_FDECL2(struct ceb_node *, CEB_MKEY_PFX, _last, struct ceb_root *const *, root, ptrdiff_t, kofs) +{ + int is_dup; + + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_last(root, kofs, CEB_KT_U32, 0, &is_dup); + else + return _ceb_last(root, kofs, CEB_KT_U64, 0, &is_dup); +} + +/* look up the specified key, and returns either the node containing it, or + * NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _lookup, struct ceb_root *const *, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key) +{ + int is_dup; + + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_lookup(root, kofs, CEB_KT_U32, key, 0, NULL, &is_dup); + else + return _ceb_lookup(root, kofs, CEB_KT_U64, 0, key, NULL, &is_dup); +} + +/* look up the specified key or the highest below it, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _lookup_le, struct ceb_root *const *, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key) +{ + int is_dup; + + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_lookup_le(root, kofs, CEB_KT_U32, key, 0, NULL, &is_dup); + else + return _ceb_lookup_le(root, kofs, CEB_KT_U64, 0, key, NULL, &is_dup); +} + +/* look up highest key below the specified one, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _lookup_lt, struct ceb_root *const *, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key) +{ + int is_dup; + + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_lookup_lt(root, kofs, CEB_KT_U32, key, 0, NULL, &is_dup); + else + return _ceb_lookup_lt(root, kofs, CEB_KT_U64, 0, key, NULL, &is_dup); +} + +/* look up the specified key or the smallest above it, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _lookup_ge, struct ceb_root *const *, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key) +{ + int is_dup; + + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_lookup_ge(root, kofs, CEB_KT_U32, key, 0, NULL, &is_dup); + else + return _ceb_lookup_ge(root, kofs, CEB_KT_U64, 0, key, NULL, &is_dup); +} + +/* look up the smallest key above the specified one, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _lookup_gt, struct ceb_root *const *, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key) +{ + int is_dup; + + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_lookup_gt(root, kofs, CEB_KT_U32, key, 0, NULL, &is_dup); + else + return _ceb_lookup_gt(root, kofs, CEB_KT_U64, 0, key, NULL, &is_dup); +} + +/* search for the next node after the specified one, and return it, or NULL if + * not found. The approach consists in looking up that node, recalling the last + * time a left turn was made, and returning the first node along the right + * branch at that fork. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _next_unique, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER; + int is_dup; + + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_next_unique(root, kofs, CEB_KT_U32, key, 0, NULL, &is_dup); + else + return _ceb_next_unique(root, kofs, CEB_KT_U64, 0, key, NULL, &is_dup); +} + +/* search for the prev node before the specified one, and return it, or NULL if + * not found. The approach consists in looking up that node, recalling the last + * time a right turn was made, and returning the last node along the left + * branch at that fork. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _prev_unique, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER; + int is_dup; + + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_prev_unique(root, kofs, CEB_KT_U32, key, 0, NULL, &is_dup); + else + return _ceb_prev_unique(root, kofs, CEB_KT_U64, 0, key, NULL, &is_dup); +} + +/* search for the next node after the specified one containing the same value, + * and return it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _next_dup, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER; + + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_next_dup(root, kofs, CEB_KT_U32, key, 0, NULL, node); + else + return _ceb_next_dup(root, kofs, CEB_KT_U64, 0, key, NULL, node); +} + +/* search for the prev node before the specified one containing the same value, + * and return it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _prev_dup, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER; + + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_prev_dup(root, kofs, CEB_KT_U32, key, 0, NULL, node); + else + return _ceb_prev_dup(root, kofs, CEB_KT_U64, 0, key, NULL, node); +} + +/* search for the next node after the specified one, and return it, or NULL if + * not found. The approach consists in looking up that node, recalling the last + * time a left turn was made, and returning the first node along the right + * branch at that fork. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _next, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER; + int is_dup; + + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_next(root, kofs, CEB_KT_U32, key, 0, NULL, node, &is_dup); + else + return _ceb_next(root, kofs, CEB_KT_U64, 0, key, NULL, node, &is_dup); +} + +/* search for the prev node before the specified one, and return it, or NULL if + * not found. The approach consists in looking up that node, recalling the last + * time a right turn was made, and returning the last node along the left + * branch at that fork. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _prev, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER; + int is_dup; + + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_prev(root, kofs, CEB_KT_U32, key, 0, NULL, node, &is_dup); + else + return _ceb_prev(root, kofs, CEB_KT_U64, 0, key, NULL, node, &is_dup); +} + +/* look up the specified node with its key and deletes it if found, and in any + * case, returns the node. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _delete, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER; + int is_dup; + + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_delete(root, node, kofs, CEB_KT_U32, key, 0, NULL, &is_dup); + else + return _ceb_delete(root, node, kofs, CEB_KT_U64, 0, key, NULL, &is_dup); +} + +/* look up the specified key, and detaches it and returns it if found, or NULL + * if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _pick, struct ceb_root **, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key) +{ + int is_dup; + + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_delete(root, NULL, kofs, CEB_KT_U32, key, 0, NULL, &is_dup); + else + return _ceb_delete(root, NULL, kofs, CEB_KT_U64, 0, key, NULL, &is_dup); +} + +/* + * Below are the functions that only support unique keys (_cebu_*) + */ + +/*****************************************************************************\ + * The declarations below always cause two functions to be declared, one * + * starting with "cebu32_*" and one with "cebu32_ofs_*" which takes a key * + * offset just after the root. The one without kofs just has this argument * + * omitted from its declaration and replaced with sizeof(struct ceb_node) in * + * the call to the underlying functions. * +\*****************************************************************************/ + +/* Inserts node into unique tree based on its key that + * immediately follows the node. Returns the inserted node or the one + * that already contains the same key. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _insert, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER; + + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_insert(root, node, kofs, CEB_KT_U32, key, 0, NULL, NULL); + else + return _ceb_insert(root, node, kofs, CEB_KT_U64, 0, key, NULL, NULL); +} + +/* return the first node or NULL if not found. */ +CEB_FDECL2(struct ceb_node *, CEB_UKEY_PFX, _first, struct ceb_root *const *, root, ptrdiff_t, kofs) +{ + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_first(root, kofs, CEB_KT_U32, 0, NULL); + else + return _ceb_first(root, kofs, CEB_KT_U64, 0, NULL); +} + +/* return the last node or NULL if not found. */ +CEB_FDECL2(struct ceb_node *, CEB_UKEY_PFX, _last, struct ceb_root *const *, root, ptrdiff_t, kofs) +{ + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_last(root, kofs, CEB_KT_U32, 0, NULL); + else + return _ceb_last(root, kofs, CEB_KT_U64, 0, NULL); +} + +/* look up the specified key, and returns either the node containing it, or + * NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup, struct ceb_root *const *, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key) +{ + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_lookup(root, kofs, CEB_KT_U32, key, 0, NULL, NULL); + else + return _ceb_lookup(root, kofs, CEB_KT_U64, 0, key, NULL, NULL); +} + +/* look up the specified key or the highest below it, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_le, struct ceb_root *const *, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key) +{ + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_lookup_le(root, kofs, CEB_KT_U32, key, 0, NULL, NULL); + else + return _ceb_lookup_le(root, kofs, CEB_KT_U64, 0, key, NULL, NULL); +} + +/* look up highest key below the specified one, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_lt, struct ceb_root *const *, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key) +{ + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_lookup_lt(root, kofs, CEB_KT_U32, key, 0, NULL, NULL); + else + return _ceb_lookup_lt(root, kofs, CEB_KT_U64, 0, key, NULL, NULL); +} + +/* look up the specified key or the smallest above it, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_ge, struct ceb_root *const *, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key) +{ + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_lookup_ge(root, kofs, CEB_KT_U32, key, 0, NULL, NULL); + else + return _ceb_lookup_ge(root, kofs, CEB_KT_U64, 0, key, NULL, NULL); +} + +/* look up the smallest key above the specified one, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_gt, struct ceb_root *const *, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key) +{ + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_lookup_gt(root, kofs, CEB_KT_U32, key, 0, NULL, NULL); + else + return _ceb_lookup_gt(root, kofs, CEB_KT_U64, 0, key, NULL, NULL); +} + +/* search for the next node after the specified one, and return it, or NULL if + * not found. The approach consists in looking up that node, recalling the last + * time a left turn was made, and returning the first node along the right + * branch at that fork. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _next, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER; + + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_next_unique(root, kofs, CEB_KT_U32, key, 0, NULL, NULL); + else + return _ceb_next_unique(root, kofs, CEB_KT_U64, 0, key, NULL, NULL); +} + +/* search for the prev node before the specified one, and return it, or NULL if + * not found. The approach consists in looking up that node, recalling the last + * time a right turn was made, and returning the last node along the left + * branch at that fork. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _prev, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER; + + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_prev_unique(root, kofs, CEB_KT_U32, key, 0, NULL, NULL); + else + return _ceb_prev_unique(root, kofs, CEB_KT_U64, 0, key, NULL, NULL); +} + +/* look up the specified node with its key and deletes it if found, and in any + * case, returns the node. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _delete, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER; + + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_delete(root, node, kofs, CEB_KT_U32, key, 0, NULL, NULL); + else + return _ceb_delete(root, node, kofs, CEB_KT_U64, 0, key, NULL, NULL); +} + +/* look up the specified key, and detaches it and returns it if found, or NULL + * if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _pick, struct ceb_root **, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key) +{ + if (sizeof(CEB_KEY_TYPE) <= 4) + return _ceb_delete(root, NULL, kofs, CEB_KT_U32, key, 0, NULL, NULL); + else + return _ceb_delete(root, NULL, kofs, CEB_KT_U64, 0, key, NULL, NULL); +} + +/* + * Functions used to dump trees in Dot format. These are only enabled if + * CEB_ENABLE_DUMP is defined. + */ + +#if defined(CEB_ENABLE_DUMP) + +#include +#define TO_STR(x) _TO_STR(x) +#define _TO_STR(x) #x + +/* dumps a ceb_node tree using the default functions above. If a node matches + * , this one will be highlighted in red. If the value is non-null, + * only a subgraph will be printed. If it's null, and root is non-null, then + * the tree is dumped at once, otherwise if root is NULL, then a prologue is + * dumped when label is not NULL, or the epilogue when label is NULL. As a + * summary: + * sub root label + * 0 NULL NULL epilogue only (closing brace and LF) + * 0 NULL text prologue with as label + * 0 tree * prologue+tree+epilogue at once + * N>0 tree * only the tree, after a prologue and before an epilogue + */ +CEB_FDECL5(void, CEB_MKEY_PFX, _default_dump, struct ceb_root *const *, root, ptrdiff_t, kofs, const char *, label, const void *, ctx, int, sub) +{ + if (!sub && label) { + printf("\ndigraph " TO_STR(CEB_MKEY_PFX) "_tree {\n" + " fontname=\"fixed\";\n" + " fontsize=8\n" + " label=\"%s\"\n" + "", label); + + printf(" node [fontname=\"fixed\" fontsize=8 shape=\"box\" style=\"filled\" color=\"black\" fillcolor=\"white\"];\n" + " edge [fontname=\"fixed\" fontsize=8 style=\"solid\" color=\"magenta\" dir=\"forward\"];\n"); + } else + printf("\n### sub %d ###\n\n", sub); + + if (root) + ceb_imm_default_dump_tree(kofs, sizeof(CEB_KEY_TYPE) <= 4 ? CEB_KT_U32 : CEB_KT_U64, root, 0, NULL, 0, ctx, sub, NULL, NULL, NULL, NULL); + + if (!sub && (root || !label)) + printf("}\n"); +} + +#endif /* CEB_ENABLE_DUMP */ diff --git a/src/_ceb_str.c b/src/_ceb_str.c new file mode 100644 index 000000000..f188abd28 --- /dev/null +++ b/src/_ceb_str.c @@ -0,0 +1,386 @@ +/* + * Compact Elastic Binary Trees - exported functions operating on string keys + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/* NOTE: this file is only meant to be included from other C files. It will + * use the following private macros that must be defined by the caller: + * - CEB_KEY_TYPE: CEB_KT_ST, CEB_KT_IS + * - CEB_KEY_MEMBER: str, ptr + * - CEB_MKEY_PFX: function name prefix for multi-key (cebs, cebis) + * - CEB_UKEY_PFX: function name prefix for unique keys (cebus, cebuis) + */ +#include +#include +#include +#include "cebtree-prv.h" + +#define TO_STR(x) _TO_STR(x) +#define _TO_STR(x) #x + +/* + * Below are the functions that support duplicate keys (_ceb_*) + */ + +/*****************************************************************************\ + * The declarations below always cause two functions to be declared, one * + * starting with "cebs_*" and one with "cebs_ofs_*" which takes a key offset * + * just after the root. The one without kofs just has this argument omitted * + * from its declaration and replaced with sizeof(struct ceb_node) in the * + * call to the underlying functions. * +\*****************************************************************************/ + +/* Inserts node into tree based on its key that immediately + * follows the node. Returns the inserted node or the one that already contains + * the same key. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _insert, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + int is_dup; + + return _ceb_insert(root, node, kofs, CEB_KEY_TYPE, 0, 0, key, &is_dup); +} + +/* return the first node or NULL if not found. */ +CEB_FDECL2(struct ceb_node *, CEB_MKEY_PFX, _first, struct ceb_root *const *, root, ptrdiff_t, kofs) +{ + int is_dup; + + return _ceb_first(root, kofs, CEB_KEY_TYPE, 0, &is_dup); +} + +/* return the last node or NULL if not found. */ +CEB_FDECL2(struct ceb_node *, CEB_MKEY_PFX, _last, struct ceb_root *const *, root, ptrdiff_t, kofs) +{ + int is_dup; + + return _ceb_last(root, kofs, CEB_KEY_TYPE, 0, &is_dup); +} + +/* look up the specified key, and returns either the node containing it, or + * NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _lookup, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key) +{ + int is_dup; + + return _ceb_lookup(root, kofs, CEB_KEY_TYPE, 0, 0, key, &is_dup); +} + +/* look up the specified key or the highest below it, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _lookup_le, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key) +{ + int is_dup; + + return _ceb_lookup_le(root, kofs, CEB_KEY_TYPE, 0, 0, key, &is_dup); +} + +/* look up highest key below the specified one, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _lookup_lt, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key) +{ + int is_dup; + + return _ceb_lookup_lt(root, kofs, CEB_KEY_TYPE, 0, 0, key, &is_dup); +} + +/* look up the specified key or the smallest above it, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _lookup_ge, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key) +{ + int is_dup; + + return _ceb_lookup_ge(root, kofs, CEB_KEY_TYPE, 0, 0, key, &is_dup); +} + +/* look up the smallest key above the specified one, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _lookup_gt, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key) +{ + int is_dup; + + return _ceb_lookup_gt(root, kofs, CEB_KEY_TYPE, 0, 0, key, &is_dup); +} + +/* search for the next node after the specified one, and return it, or NULL if + * not found. The approach consists in looking up that node, recalling the last + * time a left turn was made, and returning the first node along the right + * branch at that fork. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _next_unique, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + int is_dup; + + return _ceb_next_unique(root, kofs, CEB_KEY_TYPE, 0, 0, key, &is_dup); +} + +/* search for the prev node before the specified one, and return it, or NULL if + * not found. The approach consists in looking up that node, recalling the last + * time a right turn was made, and returning the last node along the left + * branch at that fork. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _prev_unique, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + int is_dup; + + return _ceb_prev_unique(root, kofs, CEB_KEY_TYPE, 0, 0, key, &is_dup); +} + +/* search for the next node after the specified one containing the same value, + * and return it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _next_dup, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + + return _ceb_next_dup(root, kofs, CEB_KEY_TYPE, 0, 0, key, node); +} + +/* search for the prev node before the specified one containing the same value, + * and return it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _prev_dup, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + + return _ceb_prev_dup(root, kofs, CEB_KEY_TYPE, 0, 0, key, node); +} + +/* search for the next node after the specified one, and return it, or NULL if + * not found. The approach consists in looking up that node, recalling the last + * time a left turn was made, and returning the first node along the right + * branch at that fork. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _next, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + int is_dup; + + return _ceb_next(root, kofs, CEB_KEY_TYPE, 0, 0, key, node, &is_dup); +} + +/* search for the prev node before the specified one, and return it, or NULL if + * not found. The approach consists in looking up that node, recalling the last + * time a right turn was made, and returning the last node along the left + * branch at that fork. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _prev, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + int is_dup; + + return _ceb_prev(root, kofs, CEB_KEY_TYPE, 0, 0, key, node, &is_dup); +} + +/* look up the specified node with its key and deletes it if found, and in any + * case, returns the node. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _delete, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + int is_dup; + + return _ceb_delete(root, node, kofs, CEB_KEY_TYPE, 0, 0, key, &is_dup); +} + +/* look up the specified key, and detaches it and returns it if found, or NULL + * if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _pick, struct ceb_root **, root, ptrdiff_t, kofs, const void *, key) +{ + int is_dup; + + return _ceb_delete(root, NULL, kofs, CEB_KEY_TYPE, 0, 0, key, &is_dup); +} + +/* + * Below are the functions that only support unique keys (_cebu_*) + */ + +/*****************************************************************************\ + * The declarations below always cause two functions to be declared, one * + * starting with "cebus_*" and one with "cebus_ofs_*" which takes a key * + * offset just after the root. The one without kofs just has this argument * + * omitted from its declaration and replaced with sizeof(struct ceb_node) in * + * the call to the underlying functions. * +\*****************************************************************************/ + +/* Inserts node into unique tree based on its key that + * immediately follows the node. Returns the inserted node or the one + * that already contains the same key. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _insert, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + + return _ceb_insert(root, node, kofs, CEB_KEY_TYPE, 0, 0, key, NULL); +} + +/* return the first node or NULL if not found. */ +CEB_FDECL2(struct ceb_node *, CEB_UKEY_PFX, _first, struct ceb_root *const *, root, ptrdiff_t, kofs) +{ + return _ceb_first(root, kofs, CEB_KEY_TYPE, 0, NULL); +} + +/* return the last node or NULL if not found. */ +CEB_FDECL2(struct ceb_node *, CEB_UKEY_PFX, _last, struct ceb_root *const *, root, ptrdiff_t, kofs) +{ + return _ceb_last(root, kofs, CEB_KEY_TYPE, 0, NULL); +} + +/* look up the specified key, and returns either the node containing it, or + * NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key) +{ + return _ceb_lookup(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL); +} + +/* look up the specified key or the highest below it, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_le, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key) +{ + return _ceb_lookup_le(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL); +} + +/* look up highest key below the specified one, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_lt, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key) +{ + return _ceb_lookup_lt(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL); +} + +/* look up the specified key or the smallest above it, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_ge, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key) +{ + return _ceb_lookup_ge(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL); +} + +/* look up the smallest key above the specified one, and returns either the + * node containing it, or NULL if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_gt, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key) +{ + return _ceb_lookup_gt(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL); +} + +/* search for the next node after the specified one, and return it, or NULL if + * not found. The approach consists in looking up that node, recalling the last + * time a left turn was made, and returning the first node along the right + * branch at that fork. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _next, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + + return _ceb_next_unique(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL); +} + +/* search for the prev node before the specified one, and return it, or NULL if + * not found. The approach consists in looking up that node, recalling the last + * time a right turn was made, and returning the last node along the left + * branch at that fork. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _prev, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + + return _ceb_prev_unique(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL); +} + +/* look up the specified node with its key and deletes it if found, and in any + * case, returns the node. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _delete, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node) +{ + const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER; + + return _ceb_delete(root, node, kofs, CEB_KEY_TYPE, 0, 0, key, NULL); +} + +/* look up the specified key, and detaches it and returns it if found, or NULL + * if not found. + */ +CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _pick, struct ceb_root **, root, ptrdiff_t, kofs, const void *, key) +{ + return _ceb_delete(root, NULL, kofs, CEB_KEY_TYPE, 0, 0, key, NULL); +} + +/* + * Functions used to dump trees in Dot format. These are only enabled if + * CEB_ENABLE_DUMP is defined. + */ + +#if defined(CEB_ENABLE_DUMP) + +#include +#define TO_STR(x) _TO_STR(x) +#define _TO_STR(x) #x + +/* dumps a ceb_node tree using the default functions above. If a node matches + * , this one will be highlighted in red. If the value is non-null, + * only a subgraph will be printed. If it's null, and root is non-null, then + * the tree is dumped at once, otherwise if root is NULL, then a prologue is + * dumped when label is not NULL, or the epilogue when label is NULL. As a + * summary: + * sub root label + * 0 NULL NULL epilogue only (closing brace and LF) + * 0 NULL text prologue with as label + * 0 tree * prologue+tree+epilogue at once + * N>0 tree * only the tree, after a prologue and before an epilogue + */ +CEB_FDECL5(void, CEB_MKEY_PFX, _default_dump, struct ceb_root *const *, root, ptrdiff_t, kofs, const char *, label, const void *, ctx, int, sub) +{ + if (!sub && label) { + printf("\ndigraph " TO_STR(CEB_MKEY_PFX) "_tree {\n" + " fontname=\"fixed\";\n" + " fontsize=8\n" + " label=\"%s\"\n" + "", label); + + printf(" node [fontname=\"fixed\" fontsize=8 shape=\"box\" style=\"filled\" color=\"black\" fillcolor=\"white\"];\n" + " edge [fontname=\"fixed\" fontsize=8 style=\"solid\" color=\"magenta\" dir=\"forward\"];\n"); + } else + printf("\n### sub %d ###\n\n", sub); + + if (root) + ceb_imm_default_dump_tree(kofs, CEB_KEY_TYPE, root, 0, NULL, 0, ctx, sub, NULL, NULL, NULL, NULL); + + if (!sub && (root || !label)) + printf("}\n"); +} + +#endif /* CEB_ENABLE_DUMP */ diff --git a/src/ceb32_tree.c b/src/ceb32_tree.c new file mode 100644 index 000000000..5be8ac4df --- /dev/null +++ b/src/ceb32_tree.c @@ -0,0 +1,39 @@ +/* + * Compact Elastic Binary Trees - exported functions operating on u32 keys + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef CEBTREE_DEV +/* just to verify API compatibility */ +#include "ceb32_tree.h" +#endif + +#define CEB_USE_BASE +#define CEB_USE_OFST +#define CEB_KEY_TYPE uint32_t +#define CEB_KEY_MEMBER u32 +#define CEB_MKEY_PFX ceb32 +#define CEB_UKEY_PFX cebu32 + +#include "_ceb_int.c" diff --git a/src/ceb64_tree.c b/src/ceb64_tree.c new file mode 100644 index 000000000..17eeb4413 --- /dev/null +++ b/src/ceb64_tree.c @@ -0,0 +1,39 @@ +/* + * Compact Elastic Binary Trees - exported functions operating on u64 keys + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef CEBTREE_DEV +/* just to verify API compatibility */ +#include "ceb64_tree.h" +#endif + +#define CEB_USE_BASE +#define CEB_USE_OFST +#define CEB_KEY_TYPE uint64_t +#define CEB_KEY_MEMBER u64 +#define CEB_MKEY_PFX ceb64 +#define CEB_UKEY_PFX cebu64 + +#include "_ceb_int.c" diff --git a/src/ceba_tree.c b/src/ceba_tree.c new file mode 100644 index 000000000..30c34cf76 --- /dev/null +++ b/src/ceba_tree.c @@ -0,0 +1,37 @@ +/* + * Compact Elastic Binary Trees - exported functions operating on addr keys + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef CEBTREE_DEV +/* just to verify API compatibility */ +#include "ceba_tree.h" +#endif + +#define CEB_USE_BASE +#define CEB_KEY_TYPE CEB_KT_ADDR +#define CEB_MKEY_PFX ceba +#define CEB_UKEY_PFX cebua + +#include "_ceb_addr.c" diff --git a/src/cebb_tree.c b/src/cebb_tree.c new file mode 100644 index 000000000..57b78f755 --- /dev/null +++ b/src/cebb_tree.c @@ -0,0 +1,39 @@ +/* + * Compact Elastic Binary Trees - exported functions operating on mb keys + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef CEBTREE_DEV +/* just to verify API compatibility */ +#include "cebb_tree.h" +#endif + +#define CEB_USE_BASE +#define CEB_USE_OFST +#define CEB_KEY_TYPE CEB_KT_MB +#define CEB_KEY_MEMBER mb +#define CEB_MKEY_PFX cebb +#define CEB_UKEY_PFX cebub + +#include "_ceb_blk.c" diff --git a/src/cebib_tree.c b/src/cebib_tree.c new file mode 100644 index 000000000..da06d2cf3 --- /dev/null +++ b/src/cebib_tree.c @@ -0,0 +1,39 @@ +/* + * Compact Elastic Binary Trees - exported functions operating on indirect blocks + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef CEBTREE_DEV +/* just to verify API compatibility */ +#include "cebib_tree.h" +#endif + +#define CEB_USE_BASE +#define CEB_USE_OFST +#define CEB_KEY_TYPE CEB_KT_IM +#define CEB_KEY_MEMBER ptr +#define CEB_MKEY_PFX cebib +#define CEB_UKEY_PFX cebuib + +#include "_ceb_blk.c" diff --git a/src/cebis_tree.c b/src/cebis_tree.c new file mode 100644 index 000000000..97d73fa8f --- /dev/null +++ b/src/cebis_tree.c @@ -0,0 +1,39 @@ +/* + * Compact Elastic Binary Trees - exported functions operating on indirect strings + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef CEBTREE_DEV +/* just to verify API compatibility */ +#include "cebis_tree.h" +#endif + +#define CEB_USE_BASE +#define CEB_USE_OFST +#define CEB_KEY_TYPE CEB_KT_IS +#define CEB_KEY_MEMBER ptr +#define CEB_MKEY_PFX cebis +#define CEB_UKEY_PFX cebuis + +#include "_ceb_str.c" diff --git a/src/cebl_tree.c b/src/cebl_tree.c new file mode 100644 index 000000000..ba4128a45 --- /dev/null +++ b/src/cebl_tree.c @@ -0,0 +1,39 @@ +/* + * Compact Elastic Binary Trees - exported functions operating on ulong keys + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef CEBTREE_DEV +/* just to verify API compatibility */ +#include "cebl_tree.h" +#endif + +#define CEB_USE_BASE +#define CEB_USE_OFST +#define CEB_KEY_TYPE unsigned long +#define CEB_KEY_MEMBER ul +#define CEB_MKEY_PFX cebl +#define CEB_UKEY_PFX cebul + +#include "_ceb_int.c" diff --git a/src/cebs_tree.c b/src/cebs_tree.c new file mode 100644 index 000000000..e4e84d9e6 --- /dev/null +++ b/src/cebs_tree.c @@ -0,0 +1,39 @@ +/* + * Compact Elastic Binary Trees - exported functions operating on string keys + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef CEBTREE_DEV +/* just to verify API compatibility */ +#include "cebs_tree.h" +#endif + +#define CEB_USE_BASE +#define CEB_USE_OFST +#define CEB_KEY_TYPE CEB_KT_ST +#define CEB_KEY_MEMBER str +#define CEB_MKEY_PFX cebs +#define CEB_UKEY_PFX cebus + +#include "_ceb_str.c" diff --git a/src/cebtree-dbg.c b/src/cebtree-dbg.c new file mode 100644 index 000000000..b82baa009 --- /dev/null +++ b/src/cebtree-dbg.c @@ -0,0 +1,401 @@ +/* + * Compact Elastic Binary Trees - debugging functions + * + * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/* + * Functions used to dump trees in Dot format. + */ +#include +#include "cebtree-prv.h" + +#if defined(CEB_ENABLE_DUMP) + +/* Returns the xor (or the complement of the common length for strings) between + * the two sides and if both are non-null, otherwise between the first + * non-null one and the value in the associate key. As a reminder, memory + * blocks place their length in key_u64. This is only intended for internal + * use, essentially for debugging. It only returns zero when the keys are + * identical, and returns a greater value for keys that are more distant. + * + * contains the offset between the key and the node's base. When simply + * adjacent, this would just be sizeof(ceb_node). + */ +static uint64_t _xor_branches(ptrdiff_t kofs, enum ceb_key_type key_type, uint32_t key_u32, + uint64_t key_u64, const void *key_ptr, + const struct ceb_root *_l, + const struct ceb_root *_r) +{ + const struct ceb_node *l, *r; + + l = _ceb_clrtag(_l); + r = _ceb_clrtag(_r); + + if (l && r) { + if (key_type == CEB_KT_MB) + return (key_u64 << 3) - equal_bits(NODEK(l, kofs)->mb, NODEK(r, kofs)->mb, 0, key_u64 << 3); + else if (key_type == CEB_KT_IM) + return (key_u64 << 3) - equal_bits(NODEK(l, kofs)->mb, NODEK(r, kofs)->ptr, 0, key_u64 << 3); + else if (key_type == CEB_KT_ST) + return ~string_equal_bits(NODEK(l, kofs)->str, NODEK(r, kofs)->str, 0); + else if (key_type == CEB_KT_IS) + return ~string_equal_bits(NODEK(l, kofs)->ptr, NODEK(r, kofs)->ptr, 0); + else if (key_type == CEB_KT_U64) + return NODEK(l, kofs)->u64 ^ NODEK(r, kofs)->u64; + else if (key_type == CEB_KT_U32) + return NODEK(l, kofs)->u32 ^ NODEK(r, kofs)->u32; + else if (key_type == CEB_KT_ADDR) + return ((uintptr_t)l ^ (uintptr_t)r); + else + return 0; + } + + if (!l) + l = r; + + if (key_type == CEB_KT_MB) + return (key_u64 << 3) - equal_bits(key_ptr, NODEK(l, kofs)->mb, 0, key_u64 << 3); + else if (key_type == CEB_KT_IM) + return (key_u64 << 3) - equal_bits(key_ptr, NODEK(l, kofs)->ptr, 0, key_u64 << 3); + else if (key_type == CEB_KT_ST) + return ~string_equal_bits(key_ptr, NODEK(l, kofs)->str, 0); + else if (key_type == CEB_KT_IS) + return ~string_equal_bits(key_ptr, NODEK(l, kofs)->ptr, 0); + else if (key_type == CEB_KT_U64) + return key_u64 ^ NODEK(l, kofs)->u64; + else if (key_type == CEB_KT_U32) + return key_u32 ^ NODEK(l, kofs)->u32; + else if (key_type == CEB_KT_ADDR) + return ((uintptr_t)key_ptr ^ (uintptr_t)r); + else + return 0; +} + +/* dump the root and its link to the first node or leaf */ +void ceb_imm_default_dump_root(ptrdiff_t kofs, enum ceb_key_type key_type, struct ceb_root *const *root, const void *ctx, int sub) +{ + const struct ceb_node *node; + uint64_t pxor; + + if (!sub) + printf(" \"%lx_n_%d\" [label=\"root\\n%lx\"]\n", (long)root, sub, (long)root); + else + printf(" \"%lx_n_%d\" [label=\"root\\n%lx\\ntree #%d\"]\n", (long)root, sub, (long)root, sub); + + node = _ceb_clrtag(*root); + if (node) { + /* under the root we've either a node or the first leaf */ + + /* xor of the keys of the two lower branches */ + pxor = _xor_branches(kofs, key_type, 0, 0, NULL, + node->b[0], node->b[1]); + + printf(" \"%lx_n_%d\" -> \"%lx_%c_%d\" [label=\"B\" arrowsize=0.66%s];\n", + (long)root, sub, (long)node, + (node->b[0] == node->b[1] || !pxor) ? 'l' : 'n', sub, + (ctx == node) ? " color=red" : ""); + } +} + +/* dump a node */ +void ceb_imm_default_dump_node(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub) +{ + unsigned long long int_key = 0; + uint64_t pxor, lxor, rxor; + const char *str_key = NULL; + + switch (key_type) { + case CEB_KT_ADDR: + int_key = (uintptr_t)node; + break; + case CEB_KT_U32: + int_key = NODEK(node, kofs)->u32; + break; + case CEB_KT_U64: + int_key = NODEK(node, kofs)->u64; + break; + case CEB_KT_ST: + str_key = (char*)NODEK(node, kofs)->str; + break; + case CEB_KT_IS: + str_key = (char*)NODEK(node, kofs)->ptr; + break; + default: + break; + } + + /* xor of the keys of the two lower branches */ + pxor = _xor_branches(kofs, key_type, 0, 0, NULL, + node->b[0], node->b[1]); + + /* xor of the keys of the left branch's lower branches */ + lxor = _xor_branches(kofs, key_type, 0, 0, NULL, + (_ceb_clrtag(node->b[0])->b[0]), + (_ceb_clrtag(node->b[0])->b[1])); + + /* xor of the keys of the right branch's lower branches */ + rxor = _xor_branches(kofs, key_type, 0, 0, NULL, + (_ceb_clrtag(node->b[1])->b[0]), + (_ceb_clrtag(node->b[1])->b[1])); + + switch (key_type) { + case CEB_KT_ADDR: + case CEB_KT_U32: + case CEB_KT_U64: + printf(" \"%lx_n_%d\" [label=\"%lx\\nlev=%d bit=%d\\nkey=%llu\" fillcolor=\"lightskyblue1\"%s];\n", + (long)node, sub, (long)node, level, flsnz(pxor) - 1, int_key, (ctx == node) ? " color=red" : ""); + + printf(" \"%lx_n_%d\" -> \"%lx_%c_%d\" [label=\"L\" arrowsize=0.66%s%s];\n", + (long)node, sub, (long)_ceb_clrtag(node->b[0]), + (lxor < pxor && _ceb_clrtag(node->b[0])->b[0] != _ceb_clrtag(node->b[0])->b[1] && lxor) ? 'n' : 'l', + sub, (node == _ceb_clrtag(node->b[0])) ? " dir=both" : "", (ctx == _ceb_clrtag(node->b[0])) ? " color=red" : ""); + + printf(" \"%lx_n_%d\" -> \"%lx_%c_%d\" [label=\"R\" arrowsize=0.66%s%s];\n", + (long)node, sub, (long)_ceb_clrtag(node->b[1]), + (rxor < pxor && _ceb_clrtag(node->b[1])->b[0] != _ceb_clrtag(node->b[1])->b[1] && rxor) ? 'n' : 'l', + sub, (node == _ceb_clrtag(node->b[1])) ? " dir=both" : "", (ctx == _ceb_clrtag(node->b[1])) ? " color=red" : ""); + break; + case CEB_KT_MB: + break; + case CEB_KT_IM: + break; + case CEB_KT_ST: + case CEB_KT_IS: + printf(" \"%lx_n_%d\" [label=\"%lx\\nlev=%d bit=%ld\\nkey=\\\"%s\\\"\" fillcolor=\"lightskyblue1\"%s];\n", + (long)node, sub, (long)node, level, (long)~pxor, str_key, (ctx == node) ? " color=red" : ""); + + printf(" \"%lx_n_%d\" -> \"%lx_%c_%d\" [label=\"L\" arrowsize=0.66%s%s];\n", + (long)node, sub, (long)_ceb_clrtag(node->b[0]), + (lxor < pxor && _ceb_clrtag(node->b[0])->b[0] != _ceb_clrtag(node->b[0])->b[1] && lxor) ? 'n' : 'l', + sub, (node == _ceb_clrtag(node->b[0])) ? " dir=both" : "", (ctx == _ceb_clrtag(node->b[0])) ? " color=red" : ""); + + printf(" \"%lx_n_%d\" -> \"%lx_%c_%d\" [label=\"R\" arrowsize=0.66%s%s];\n", + (long)node, sub, (long)_ceb_clrtag(node->b[1]), + (rxor < pxor && _ceb_clrtag(node->b[1])->b[0] != _ceb_clrtag(node->b[1])->b[1] && rxor) ? 'n' : 'l', + sub, (node == _ceb_clrtag(node->b[1])) ? " dir=both" : "", (ctx == _ceb_clrtag(node->b[1])) ? " color=red" : ""); + break; + } +} + +/* dump a duplicate entry */ +void ceb_imm_default_dump_dups(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub) +{ + unsigned long long int_key = 0; + const struct ceb_node *leaf; + const char *str_key = NULL; + int is_last; + + switch (key_type) { + case CEB_KT_ADDR: + int_key = (uintptr_t)node; + break; + case CEB_KT_U32: + int_key = NODEK(node, kofs)->u32; + break; + case CEB_KT_U64: + int_key = NODEK(node, kofs)->u64; + break; + case CEB_KT_ST: + str_key = (char*)NODEK(node, kofs)->str; + break; + case CEB_KT_IS: + str_key = (char*)NODEK(node, kofs)->ptr; + break; + default: + break; + } + + /* Let's try to determine which one is the last of the series. The + * right node's left node is a tree leaf in this only case. This means + * that node either has both sides equal to itself, or a distinct + * neighbours. + */ + leaf = _ceb_clrtag(_ceb_untag(node->b[1], 1)->b[0]); + + is_last = 1; + if (leaf->b[0] != _ceb_dotag(leaf, 1) || leaf->b[1] != _ceb_dotag(leaf, 1)) + is_last = _xor_branches(kofs, key_type, 0, 0, NULL, + leaf->b[0], leaf->b[1]) != 0; + + switch (key_type) { + case CEB_KT_ADDR: + case CEB_KT_U32: + case CEB_KT_U64: + printf(" \"%lx_l_%d\" [label=\"%lx\\nlev=%d\\nkey=%llu\" fillcolor=\"wheat1\"%s];\n", + (long)node, sub, (long)node, level, int_key, (ctx == node) ? " color=red" : ""); + + printf(" \"%lx_l_%d\":sw -> \"%lx_l_%d\":n [taillabel=\"L\" arrowsize=0.66%s];\n", + (long)node, sub, (long)_ceb_clrtag(node->b[0]), sub, (ctx == _ceb_clrtag(node->b[0])) ? " color=red" : ""); + + printf(" \"%lx_l_%d\":%s -> \"%lx_l_%d\":%s [taillabel=\"R\" arrowsize=0.66%s];\n", + (long)node, sub, is_last ? "se" : "ne", + (long)_ceb_clrtag(node->b[1]), sub, is_last ? "e" : "s", (ctx == _ceb_clrtag(node->b[1])) ? " color=red" : ""); + break; + case CEB_KT_MB: + break; + case CEB_KT_IM: + break; + case CEB_KT_ST: + case CEB_KT_IS: + printf(" \"%lx_l_%d\" [label=\"%lx\\nlev=%d\\nkey=\\\"%s\\\"\" fillcolor=\"wheat1\"%s];\n", + (long)node, sub, (long)node, level, str_key, (ctx == node) ? " color=red" : ""); + + printf(" \"%lx_l_%d\":sw -> \"%lx_l_%d\":n [taillabel=\"L\" arrowsize=0.66%s];\n", + (long)node, sub, (long)_ceb_clrtag(node->b[0]), sub, (ctx == _ceb_clrtag(node->b[0])) ? " color=red" : ""); + + printf(" \"%lx_l_%d\":%s -> \"%lx_l_%d\":%s [taillabel=\"R\" arrowsize=0.66%s];\n", + (long)node, sub, is_last ? "se" : "ne", + (long)_ceb_clrtag(node->b[1]), sub, is_last ? "e" : "s", (ctx == _ceb_clrtag(node->b[1])) ? " color=red" : ""); + break; + } +} + +/* dump a leaf */ +void ceb_imm_default_dump_leaf(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub) +{ + unsigned long long int_key = 0; + const char *str_key = NULL; + uint64_t pxor; + + switch (key_type) { + case CEB_KT_ADDR: + int_key = (uintptr_t)node; + break; + case CEB_KT_U32: + int_key = NODEK(node, kofs)->u32; + break; + case CEB_KT_U64: + int_key = NODEK(node, kofs)->u64; + break; + case CEB_KT_ST: + str_key = (char*)NODEK(node, kofs)->str; + break; + case CEB_KT_IS: + str_key = (char*)NODEK(node, kofs)->ptr; + break; + default: + break; + } + + /* xor of the keys of the two lower branches */ + pxor = _xor_branches(kofs, key_type, 0, 0, NULL, + node->b[0], node->b[1]); + + switch (key_type) { + case CEB_KT_ADDR: + case CEB_KT_U32: + case CEB_KT_U64: + if (node->b[0] == node->b[1]) + printf(" \"%lx_l_%d\" [label=\"%lx\\nlev=%d\\nkey=%llu\\n\" fillcolor=\"green\"%s];\n", + (long)node, sub, (long)node, level, int_key, (ctx == node) ? " color=red" : ""); + else + printf(" \"%lx_l_%d\" [label=\"%lx\\nlev=%d bit=%d\\nkey=%llu\\n\" fillcolor=\"yellow\"%s];\n", + (long)node, sub, (long)node, level, flsnz(pxor) - 1, int_key, (ctx == node) ? " color=red" : ""); + break; + case CEB_KT_MB: + break; + case CEB_KT_IM: + break; + case CEB_KT_ST: + case CEB_KT_IS: + if (node->b[0] == node->b[1]) + printf(" \"%lx_l_%d\" [label=\"%lx\\nlev=%d\\nkey=\\\"%s\\\"\\n\" fillcolor=\"green\"%s];\n", + (long)node, sub, (long)node, level, str_key, (ctx == node) ? " color=red" : ""); + else + printf(" \"%lx_l_%d\" [label=\"%lx\\nlev=%d bit=%ld\\nkey=\\\"%s\\\"\\n\" fillcolor=\"yellow\"%s];\n", + (long)node, sub, (long)node, level, (long)~pxor, str_key, (ctx == node) ? " color=red" : ""); + break; + } +} + +/* Dumps a tree through the specified callbacks, falling back to the default + * callbacks above if left NULL. + */ + +const struct ceb_node *ceb_imm_default_dump_tree(ptrdiff_t kofs, enum ceb_key_type key_type, struct ceb_root *const *root, + uint64_t pxor, const void *last, int level, const void *ctx, int sub, + void (*root_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, struct ceb_root *const *root, const void *ctx, int sub), + void (*node_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub), + void (*dups_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub), + void (*leaf_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub)) +{ + const struct ceb_node *node = _ceb_clrtag(*root); + uint64_t xor; + + if (!node) /* empty tree */ + return node; + + if (!root_dump) + root_dump = ceb_imm_default_dump_root; + + if (!node_dump) + node_dump = ceb_imm_default_dump_node; + + if (!dups_dump) + dups_dump = ceb_imm_default_dump_dups; + + if (!leaf_dump) + leaf_dump = ceb_imm_default_dump_leaf; + + if (!level) { + /* dump the first arrow */ + root_dump(kofs, key_type, root, ctx, sub); + } + + /* regular nodes, all branches are canonical */ + + while (1) { + if (node->b[0] == _ceb_dotag(node, 1) && node->b[1] == _ceb_dotag(node, 1)) { + /* first inserted leaf */ + leaf_dump(kofs, key_type, node, level, ctx, sub); + return node; + } + + xor = _xor_branches(kofs, key_type, 0, 0, NULL, + node->b[0], node->b[1]); + if (xor) + break; + + /* a zero XOR with different branches indicates a list element, + * we dump it and walk to the left until we find the node. + */ + dups_dump(kofs, key_type, node, level, ctx, sub); + node = _ceb_clrtag(node->b[0]); + } + + if (pxor && xor >= pxor) { + /* that's a leaf */ + leaf_dump(kofs, key_type, node, level, ctx, sub); + return node; + } + + /* that's a regular node */ + node_dump(kofs, key_type, node, level, ctx, sub); + + last = ceb_imm_default_dump_tree(kofs, key_type, &node->b[0], xor, last, level + 1, ctx, sub, root_dump, node_dump, dups_dump, leaf_dump); + return ceb_imm_default_dump_tree(kofs, key_type, &node->b[1], xor, last, level + 1, ctx, sub, root_dump, node_dump, dups_dump, leaf_dump); +} + +#endif /* CEB_ENABLE_DUMP */ diff --git a/src/cebu32_tree.c b/src/cebu32_tree.c deleted file mode 100644 index b1598a566..000000000 --- a/src/cebu32_tree.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Compact Elastic Binary Trees - exported functions operating on u32 keys - * - * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include "cebtree-prv.h" - -/*****************************************************************************\ - * The declarations below always cause two functions to be declared, one * - * starting with "cebu32_*" and one with "cebu32_ofs_*" which takes a key * - * offset just after the root. The one without kofs just has this argument * - * omitted from its declaration and replaced with sizeof(struct ceb_node) in * - * the call to the underlying functions. * -\*****************************************************************************/ - -/* Inserts node into unique tree based on its key that - * immediately follows the node. Returns the inserted node or the one - * that already contains the same key. - */ -CEB_FDECL3(struct ceb_node *, cebu32, _insert, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - uint32_t key = NODEK(node, kofs)->u32; - - return _cebu_insert(root, node, kofs, CEB_KT_U32, key, 0, NULL); -} - -/* return the first node or NULL if not found. */ -CEB_FDECL2(struct ceb_node *, cebu32, _first, struct ceb_node **, root, ptrdiff_t, kofs) -{ - return _cebu_first(root, kofs, CEB_KT_U32); -} - -/* return the last node or NULL if not found. */ -CEB_FDECL2(struct ceb_node *, cebu32, _last, struct ceb_node **, root, ptrdiff_t, kofs) -{ - return _cebu_last(root, kofs, CEB_KT_U32); -} - -/* look up the specified key, and returns either the node containing it, or - * NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebu32, _lookup, struct ceb_node **, root, ptrdiff_t, kofs, uint32_t, key) -{ - return _cebu_lookup(root, kofs, CEB_KT_U32, key, 0, NULL); -} - -/* look up the specified key or the highest below it, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebu32, _lookup_le, struct ceb_node **, root, ptrdiff_t, kofs, uint32_t, key) -{ - return _cebu_lookup_le(root, kofs, CEB_KT_U32, key, 0, NULL); -} - -/* look up highest key below the specified one, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebu32, _lookup_lt, struct ceb_node **, root, ptrdiff_t, kofs, uint32_t, key) -{ - return _cebu_lookup_lt(root, kofs, CEB_KT_U32, key, 0, NULL); -} - -/* look up the specified key or the smallest above it, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebu32, _lookup_ge, struct ceb_node **, root, ptrdiff_t, kofs, uint32_t, key) -{ - return _cebu_lookup_ge(root, kofs, CEB_KT_U32, key, 0, NULL); -} - -/* look up the smallest key above the specified one, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebu32, _lookup_gt, struct ceb_node **, root, ptrdiff_t, kofs, uint32_t, key) -{ - return _cebu_lookup_gt(root, kofs, CEB_KT_U32, key, 0, NULL); -} - -/* search for the next node after the specified one, and return it, or NULL if - * not found. The approach consists in looking up that node, recalling the last - * time a left turn was made, and returning the first node along the right - * branch at that fork. - */ -CEB_FDECL3(struct ceb_node *, cebu32, _next, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - uint32_t key = NODEK(node, kofs)->u32; - - return _cebu_next(root, kofs, CEB_KT_U32, key, 0, NULL); -} - -/* search for the prev node before the specified one, and return it, or NULL if - * not found. The approach consists in looking up that node, recalling the last - * time a right turn was made, and returning the last node along the left - * branch at that fork. - */ -CEB_FDECL3(struct ceb_node *, cebu32, _prev, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - uint32_t key = NODEK(node, kofs)->u32; - - return _cebu_prev(root, kofs, CEB_KT_U32, key, 0, NULL); -} - -/* look up the specified node with its key and deletes it if found, and in any - * case, returns the node. - */ -CEB_FDECL3(struct ceb_node *, cebu32, _delete, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - uint32_t key = NODEK(node, kofs)->u32; - - return _cebu_delete(root, node, kofs, CEB_KT_U32, key, 0, NULL); -} - -/* look up the specified key, and detaches it and returns it if found, or NULL - * if not found. - */ -CEB_FDECL3(struct ceb_node *, cebu32, _pick, struct ceb_node **, root, ptrdiff_t, kofs, uint32_t, key) -{ - return _cebu_delete(root, NULL, kofs, CEB_KT_U32, key, 0, NULL); -} - -/* dumps a ceb_node tree using the default functions above. If a node matches - * , this one will be highlighted in red. - */ -CEB_FDECL4(void, cebu32, _default_dump, struct ceb_node **, root, ptrdiff_t, kofs, const char *, label, const void *, ctx) -{ - printf("\ndigraph cebu32_tree {\n" - " fontname=\"fixed\";\n" - " fontsize=8\n" - " label=\"%s\"\n" - "", label); - - printf(" node [fontname=\"fixed\" fontsize=8 shape=\"box\" style=\"filled\" color=\"black\" fillcolor=\"white\"];\n" - " edge [fontname=\"fixed\" fontsize=8 style=\"solid\" color=\"magenta\" dir=\"forward\"];\n"); - - cebu_default_dump_tree(kofs, CEB_KT_U32, root, 0, NULL, 0, ctx, NULL, NULL, NULL); - - printf("}\n"); -} diff --git a/src/cebu64_tree.c b/src/cebu64_tree.c deleted file mode 100644 index 0b6729ad3..000000000 --- a/src/cebu64_tree.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Compact Elastic Binary Trees - exported functions operating on u64 keys - * - * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include "cebtree-prv.h" - -/*****************************************************************************\ - * The declarations below always cause two functions to be declared, one * - * starting with "cebu64_*" and one with "cebu64_ofs_*" which takes a key * - * offset just after the root. The one without kofs just has this argument * - * omitted from its declaration and replaced with sizeof(struct ceb_node) in * - * the call to the underlying functions. * -\*****************************************************************************/ - -/* Inserts node into unique tree based on its key that - * immediately follows the node. Returns the inserted node or the one - * that already contains the same key. - */ -CEB_FDECL3(struct ceb_node *, cebu64, _insert, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - uint64_t key = NODEK(node, kofs)->u64; - - return _cebu_insert(root, node, kofs, CEB_KT_U64, 0, key, NULL); -} - -/* return the first node or NULL if not found. */ -CEB_FDECL2(struct ceb_node *, cebu64, _first, struct ceb_node **, root, ptrdiff_t, kofs) -{ - return _cebu_first(root, kofs, CEB_KT_U64); -} - -/* return the last node or NULL if not found. */ -CEB_FDECL2(struct ceb_node *, cebu64, _last, struct ceb_node **, root, ptrdiff_t, kofs) -{ - return _cebu_last(root, kofs, CEB_KT_U64); -} - -/* look up the specified key, and returns either the node containing it, or - * NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebu64, _lookup, struct ceb_node **, root, ptrdiff_t, kofs, uint64_t, key) -{ - return _cebu_lookup(root, kofs, CEB_KT_U64, 0, key, NULL); -} - -/* look up the specified key or the highest below it, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebu64, _lookup_le, struct ceb_node **, root, ptrdiff_t, kofs, uint64_t, key) -{ - return _cebu_lookup_le(root, kofs, CEB_KT_U64, 0, key, NULL); -} - -/* look up highest key below the specified one, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebu64, _lookup_lt, struct ceb_node **, root, ptrdiff_t, kofs, uint64_t, key) -{ - return _cebu_lookup_lt(root, kofs, CEB_KT_U64, 0, key, NULL); -} - -/* look up the specified key or the smallest above it, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebu64, _lookup_ge, struct ceb_node **, root, ptrdiff_t, kofs, uint64_t, key) -{ - return _cebu_lookup_ge(root, kofs, CEB_KT_U64, 0, key, NULL); -} - -/* look up the smallest key above the specified one, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebu64, _lookup_gt, struct ceb_node **, root, ptrdiff_t, kofs, uint64_t, key) -{ - return _cebu_lookup_gt(root, kofs, CEB_KT_U64, 0, key, NULL); -} - -/* search for the next node after the specified one, and return it, or NULL if - * not found. The approach consists in looking up that node, recalling the last - * time a left turn was made, and returning the first node along the right - * branch at that fork. - */ -CEB_FDECL3(struct ceb_node *, cebu64, _next, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - uint64_t key = NODEK(node, kofs)->u64; - - return _cebu_next(root, kofs, CEB_KT_U64, 0, key, NULL); -} - -/* search for the prev node before the specified one, and return it, or NULL if - * not found. The approach consists in looking up that node, recalling the last - * time a right turn was made, and returning the last node along the left - * branch at that fork. - */ -CEB_FDECL3(struct ceb_node *, cebu64, _prev, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - uint64_t key = NODEK(node, kofs)->u64; - - return _cebu_prev(root, kofs, CEB_KT_U64, 0, key, NULL); -} - -/* look up the specified node with its key and deletes it if found, and in any - * case, returns the node. - */ -CEB_FDECL3(struct ceb_node *, cebu64, _delete, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - uint64_t key = NODEK(node, kofs)->u64; - - return _cebu_delete(root, node, kofs, CEB_KT_U64, 0, key, NULL); -} - -/* look up the specified key, and detaches it and returns it if found, or NULL - * if not found. - */ -CEB_FDECL3(struct ceb_node *, cebu64, _pick, struct ceb_node **, root, ptrdiff_t, kofs, uint64_t, key) -{ - return _cebu_delete(root, NULL, kofs, CEB_KT_U64, 0, key, NULL); -} - -/* dumps a ceb_node tree using the default functions above. If a node matches - * , this one will be highlighted in red. - */ -CEB_FDECL4(void, cebu64, _default_dump, struct ceb_node **, root, ptrdiff_t, kofs, const char *, label, const void *, ctx) -{ - printf("\ndigraph cebu64_tree {\n" - " fontname=\"fixed\";\n" - " fontsize=8\n" - " label=\"%s\"\n" - "", label); - - printf(" node [fontname=\"fixed\" fontsize=8 shape=\"box\" style=\"filled\" color=\"black\" fillcolor=\"white\"];\n" - " edge [fontname=\"fixed\" fontsize=8 style=\"solid\" color=\"magenta\" dir=\"forward\"];\n"); - - cebu_default_dump_tree(kofs, CEB_KT_U64, root, 0, NULL, 0, ctx, NULL, NULL, NULL); - - printf("}\n"); -} diff --git a/src/cebua_tree.c b/src/cebua_tree.c deleted file mode 100644 index ca2665e71..000000000 --- a/src/cebua_tree.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Compact Elastic Binary Trees - exported functions operating on addr keys - * - * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include "cebtree-prv.h" - -/*****************************************************************************\ - * The declarations below always cause two functions to be declared, one * - * starting with "cebua_*" and one with "cebua_ofs_*" which takes a key * - * offset just after the root. The one without kofs just has this argument * - * omitted from its declaration and replaced with sizeof(struct ceb_node) in * - * the call to the underlying functions. * -\*****************************************************************************/ - -/* Inserts node into unique tree based on its own address - * Returns the inserted node or the one that has the same address. - */ -CEB_FDECL3(struct ceb_node *, cebua, _insert, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - return _cebu_insert(root, node, kofs, CEB_KT_ADDR, 0, 0, node); -} - -/* return the first node or NULL if not found. */ -CEB_FDECL2(struct ceb_node *, cebua, _first, struct ceb_node **, root, ptrdiff_t, kofs) -{ - return _cebu_first(root, kofs, CEB_KT_ADDR); -} - -/* return the last node or NULL if not found. */ -CEB_FDECL2(struct ceb_node *, cebua, _last, struct ceb_node **, root, ptrdiff_t, kofs) -{ - return _cebu_last(root, kofs, CEB_KT_ADDR); -} - -/* look up the specified key, and returns either the node containing it, or - * NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebua, _lookup, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key) -{ - return _cebu_lookup(root, kofs, CEB_KT_ADDR, 0, 0, key); -} - -/* look up the specified key or the highest below it, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebua, _lookup_le, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key) -{ - return _cebu_lookup_le(root, kofs, CEB_KT_ADDR, 0, 0, key); -} - -/* look up highest key below the specified one, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebua, _lookup_lt, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key) -{ - return _cebu_lookup_lt(root, kofs, CEB_KT_ADDR, 0, 0, key); -} - -/* look up the specified key or the smallest above it, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebua, _lookup_ge, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key) -{ - return _cebu_lookup_ge(root, kofs, CEB_KT_ADDR, 0, 0, key); -} - -/* look up the smallest key above the specified one, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebua, _lookup_gt, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key) -{ - return _cebu_lookup_gt(root, kofs, CEB_KT_ADDR, 0, 0, key); -} - -/* search for the next node after the specified one, and return it, or NULL if - * not found. The approach consists in looking up that node, recalling the last - * time a left turn was made, and returning the first node along the right - * branch at that fork. - */ -CEB_FDECL3(struct ceb_node *, cebua, _next, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - return _cebu_next(root, kofs, CEB_KT_ADDR, 0, 0, node); -} - -/* search for the prev node before the specified one, and return it, or NULL if - * not found. The approach consists in looking up that node, recalling the last - * time a right turn was made, and returning the last node along the left - * branch at that fork. - */ -CEB_FDECL3(struct ceb_node *, cebua, _prev, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - return _cebu_prev(root, kofs, CEB_KT_ADDR, 0, 0, node); -} - -/* look up the specified node with its key and deletes it if found, and in any - * case, returns the node. - */ -CEB_FDECL3(struct ceb_node *, cebua, _delete, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - return _cebu_delete(root, node, kofs, CEB_KT_ADDR, 0, 0, node); -} - -/* look up the specified key, and detaches it and returns it if found, or NULL - * if not found. - */ -CEB_FDECL3(struct ceb_node *, cebua, _pick, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key) -{ - return _cebu_delete(root, NULL, kofs, CEB_KT_ADDR, 0, 0, key); -} - -/* dumps a ceb_node tree using the default functions above. If a node matches - * , this one will be highlighted in red. - */ -CEB_FDECL4(void, cebua, _default_dump, struct ceb_node **, root, ptrdiff_t, kofs, const char *, label, const void *, ctx) -{ - printf("\ndigraph cebua_tree {\n" - " fontname=\"fixed\";\n" - " fontsize=8\n" - " label=\"%s\"\n" - "", label); - - printf(" node [fontname=\"fixed\" fontsize=8 shape=\"box\" style=\"filled\" color=\"black\" fillcolor=\"white\"];\n" - " edge [fontname=\"fixed\" fontsize=8 style=\"solid\" color=\"magenta\" dir=\"forward\"];\n"); - - cebu_default_dump_tree(kofs, CEB_KT_ADDR, root, 0, NULL, 0, ctx, NULL, NULL, NULL); - - printf("}\n"); -} diff --git a/src/cebub_tree.c b/src/cebub_tree.c deleted file mode 100644 index f11a507ff..000000000 --- a/src/cebub_tree.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Compact Elastic Binary Trees - exported functions operating on mb keys - * - * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include -#include "cebtree-prv.h" - -/*****************************************************************************\ - * The declarations below always cause two functions to be declared, one * - * starting with "cebub_*" and one with "cebub_ofs_*" which takes a key * - * offset just after the root. The one without kofs just has this argument * - * omitted from its declaration and replaced with sizeof(struct ceb_node) in * - * the call to the underlying functions. * -\*****************************************************************************/ - -/* Inserts node into unique tree based on its key that - * immediately follows the node and for bytes. Returns the - * inserted node or the one that already contains the same key. - */ -CEB_FDECL4(struct ceb_node *, cebub, _insert, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len) -{ - const void *key = NODEK(node, kofs)->mb; - - return _cebu_insert(root, node, kofs, CEB_KT_MB, 0, len, key); -} - -/* return the first node or NULL if not found. */ -CEB_FDECL2(struct ceb_node *, cebub, _first, struct ceb_node **, root, ptrdiff_t, kofs) -{ - return _cebu_first(root, kofs, CEB_KT_MB); -} - -/* return the last node or NULL if not found. */ -CEB_FDECL2(struct ceb_node *, cebub, _last, struct ceb_node **, root, ptrdiff_t, kofs) -{ - return _cebu_last(root, kofs, CEB_KT_MB); -} - -/* look up the specified key of length , and returns either the node - * containing it, or NULL if not found. - */ -CEB_FDECL4(struct ceb_node *, cebub, _lookup, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len) -{ - return _cebu_lookup(root, kofs, CEB_KT_MB, 0, len, key); -} - -/* look up the specified key or the highest below it, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL4(struct ceb_node *, cebub, _lookup_le, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len) -{ - return _cebu_lookup_le(root, kofs, CEB_KT_MB, 0, len, key); -} - -/* look up highest key below the specified one, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL4(struct ceb_node *, cebub, _lookup_lt, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len) -{ - return _cebu_lookup_lt(root, kofs, CEB_KT_MB, 0, len, key); -} - -/* look up the specified key or the smallest above it, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL4(struct ceb_node *, cebub, _lookup_ge, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len) -{ - return _cebu_lookup_ge(root, kofs, CEB_KT_MB, 0, len, key); -} - -/* look up the smallest key above the specified one, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL4(struct ceb_node *, cebub, _lookup_gt, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len) -{ - return _cebu_lookup_gt(root, kofs, CEB_KT_MB, 0, len, key); -} - -/* search for the next node after the specified one, and return it, or NULL if - * not found. The approach consists in looking up that node, recalling the last - * time a left turn was made, and returning the first node along the right - * branch at that fork. The field must correspond to the key length in - * bytes. - */ -CEB_FDECL4(struct ceb_node *, cebub, _next, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len) -{ - const void *key = NODEK(node, kofs)->mb; - - return _cebu_next(root, kofs, CEB_KT_MB, 0, len, key); -} - -/* search for the prev node before the specified one, and return it, or NULL if - * not found. The approach consists in looking up that node, recalling the last - * time a right turn was made, and returning the last node along the left - * branch at that fork. The field must correspond to the key length in - * bytes. - */ -CEB_FDECL4(struct ceb_node *, cebub, _prev, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len) -{ - const void *key = NODEK(node, kofs)->mb; - - return _cebu_prev(root, kofs, CEB_KT_MB, 0, len, key); -} - -/* look up the specified node with its key and deletes it if found, and in any - * case, returns the node. The field must correspond to the key length in - * bytes. - */ -CEB_FDECL4(struct ceb_node *, cebub, _delete, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len) -{ - const void *key = NODEK(node, kofs)->mb; - - return _cebu_delete(root, node, kofs, CEB_KT_MB, 0, len, key); -} - -/* look up the specified key, and detaches it and returns it if found, or NULL - * if not found. The field must correspond to the key length in bytes. - */ -CEB_FDECL4(struct ceb_node *, cebub, _pick, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len) -{ - return _cebu_delete(root, NULL, kofs, CEB_KT_MB, 0, len, key); -} diff --git a/src/cebuib_tree.c b/src/cebuib_tree.c deleted file mode 100644 index 2aa6f8ba3..000000000 --- a/src/cebuib_tree.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Compact Elastic Binary Trees - exported functions operating on indirect blocks - * - * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include -#include "cebtree-prv.h" - -/*****************************************************************************\ - * The declarations below always cause two functions to be declared, one * - * starting with "cebuib_*" and one with "cebuib_ofs_*" which takes a key * - * offset just after the root. The one without kofs just has this argument * - * omitted from its declaration and replaced with sizeof(struct ceb_node) in * - * the call to the underlying functions. * -\*****************************************************************************/ - -/* Inserts node into unique tree based on its key whose pointer - * immediately follows the node and for bytes. Returns the inserted node - * or the one that already contains the same key. - */ -CEB_FDECL4(struct ceb_node *, cebuib, _insert, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len) -{ - const void *key = NODEK(node, kofs)->ptr; - - return _cebu_insert(root, node, kofs, CEB_KT_IM, 0, len, key); -} - -/* return the first node or NULL if not found. */ -CEB_FDECL2(struct ceb_node *, cebuib, _first, struct ceb_node **, root, ptrdiff_t, kofs) -{ - return _cebu_first(root, kofs, CEB_KT_IM); -} - -/* return the last node or NULL if not found. */ -CEB_FDECL2(struct ceb_node *, cebuib, _last, struct ceb_node **, root, ptrdiff_t, kofs) -{ - return _cebu_last(root, kofs, CEB_KT_IM); -} - -/* look up the specified key of length , and returns either the node - * containing it, or NULL if not found. - */ -CEB_FDECL4(struct ceb_node *, cebuib, _lookup, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len) -{ - return _cebu_lookup(root, kofs, CEB_KT_IM, 0, len, key); -} - -/* look up the specified key or the highest below it, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL4(struct ceb_node *, cebuib, _lookup_le, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len) -{ - return _cebu_lookup_le(root, kofs, CEB_KT_IM, 0, len, key); -} - -/* look up highest key below the specified one, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL4(struct ceb_node *, cebuib, _lookup_lt, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len) -{ - return _cebu_lookup_lt(root, kofs, CEB_KT_IM, 0, len, key); -} - -/* look up the specified key or the smallest above it, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL4(struct ceb_node *, cebuib, _lookup_ge, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len) -{ - return _cebu_lookup_ge(root, kofs, CEB_KT_IM, 0, len, key); -} - -/* look up the smallest key above the specified one, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL4(struct ceb_node *, cebuib, _lookup_gt, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len) -{ - return _cebu_lookup_gt(root, kofs, CEB_KT_IM, 0, len, key); -} - -/* search for the next node after the specified one, and return it, or NULL if - * not found. The approach consists in looking up that node, recalling the last - * time a left turn was made, and returning the first node along the right - * branch at that fork. The field must correspond to the key length in - * bytes. - */ -CEB_FDECL4(struct ceb_node *, cebuib, _next, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len) -{ - const void *key = NODEK(node, kofs)->ptr; - - return _cebu_next(root, kofs, CEB_KT_IM, 0, len, key); -} - -/* search for the prev node before the specified one, and return it, or NULL if - * not found. The approach consists in looking up that node, recalling the last - * time a right turn was made, and returning the last node along the left - * branch at that fork. The field must correspond to the key length in - * bytes. - */ -CEB_FDECL4(struct ceb_node *, cebuib, _prev, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len) -{ - const void *key = NODEK(node, kofs)->ptr; - - return _cebu_prev(root, kofs, CEB_KT_IM, 0, len, key); -} - -/* look up the specified node with its key and deletes it if found, and in any - * case, returns the node. The field must correspond to the key length in - * bytes. - */ -CEB_FDECL4(struct ceb_node *, cebuib, _delete, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len) -{ - const void *key = NODEK(node, kofs)->ptr; - - return _cebu_delete(root, node, kofs, CEB_KT_IM, 0, len, key); -} - -/* look up the specified key, and detaches it and returns it if found, or NULL - * if not found. The field must correspond to the key length in bytes. - */ -CEB_FDECL4(struct ceb_node *, cebuib, _pick, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len) -{ - return _cebu_delete(root, NULL, kofs, CEB_KT_IM, 0, len, key); -} diff --git a/src/cebuis_tree.c b/src/cebuis_tree.c deleted file mode 100644 index 300218da6..000000000 --- a/src/cebuis_tree.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Compact Elastic Binary Trees - exported functions operating on indirect strings - * - * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include -#include "cebtree-prv.h" - -/*****************************************************************************\ - * The declarations below always cause two functions to be declared, one * - * starting with "cebuis_*" and one with "cebuis_ofs_*" which takes a key * - * offset just after the root. The one without kofs just has this argument * - * omitted from its declaration and replaced with sizeof(struct ceb_node) in * - * the call to the underlying functions. * -\*****************************************************************************/ - -/* Inserts node into unique tree based on its key whose pointer - * immediately follows the node. Returns the inserted node or the one that - * already contains the same key. - */ -CEB_FDECL3(struct ceb_node *, cebuis, _insert, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - const void *key = NODEK(node, kofs)->ptr; - - return _cebu_insert(root, node, kofs, CEB_KT_IS, 0, 0, key); -} - -/* return the first node or NULL if not found. */ -CEB_FDECL2(struct ceb_node *, cebuis, _first, struct ceb_node **, root, ptrdiff_t, kofs) -{ - return _cebu_first(root, kofs, CEB_KT_IS); -} - -/* return the last node or NULL if not found. */ -CEB_FDECL2(struct ceb_node *, cebuis, _last, struct ceb_node **, root, ptrdiff_t, kofs) -{ - return _cebu_last(root, kofs, CEB_KT_IS); -} - -/* look up the specified key, and returns either the node containing it, or - * NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebuis, _lookup, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key) -{ - return _cebu_lookup(root, kofs, CEB_KT_IS, 0, 0, key); -} - -/* look up the specified key or the highest below it, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebuis, _lookup_le, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key) -{ - return _cebu_lookup_le(root, kofs, CEB_KT_IS, 0, 0, key); -} - -/* look up highest key below the specified one, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebuis, _lookup_lt, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key) -{ - return _cebu_lookup_lt(root, kofs, CEB_KT_IS, 0, 0, key); -} - -/* look up the specified key or the smallest above it, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebuis, _lookup_ge, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key) -{ - return _cebu_lookup_ge(root, kofs, CEB_KT_IS, 0, 0, key); -} - -/* look up the smallest key above the specified one, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebuis, _lookup_gt, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key) -{ - return _cebu_lookup_gt(root, kofs, CEB_KT_IS, 0, 0, key); -} - -/* search for the next node after the specified one, and return it, or NULL if - * not found. The approach consists in looking up that node, recalling the last - * time a left turn was made, and returning the first node along the right - * branch at that fork. - */ -CEB_FDECL3(struct ceb_node *, cebuis, _next, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - const void *key = NODEK(node, kofs)->ptr; - - return _cebu_next(root, kofs, CEB_KT_IS, 0, 0, key); -} - -/* search for the prev node before the specified one, and return it, or NULL if - * not found. The approach consists in looking up that node, recalling the last - * time a right turn was made, and returning the last node along the left - * branch at that fork. - */ -CEB_FDECL3(struct ceb_node *, cebuis, _prev, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - const void *key = NODEK(node, kofs)->ptr; - - return _cebu_prev(root, kofs, CEB_KT_IS, 0, 0, key); -} - -/* look up the specified node with its key and deletes it if found, and in any - * case, returns the node. - */ -CEB_FDECL3(struct ceb_node *, cebuis, _delete, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - const void *key = NODEK(node, kofs)->ptr; - - return _cebu_delete(root, node, kofs, CEB_KT_IS, 0, 0, key); -} - -/* look up the specified key, and detaches it and returns it if found, or NULL - * if not found. - */ -CEB_FDECL3(struct ceb_node *, cebuis, _pick, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key) -{ - return _cebu_delete(root, NULL, kofs, CEB_KT_IS, 0, 0, key); -} diff --git a/src/cebul_tree.c b/src/cebul_tree.c deleted file mode 100644 index d1c2ad08b..000000000 --- a/src/cebul_tree.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Compact Elastic Binary Trees - exported functions operating on ulong keys - * - * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include "cebtree-prv.h" - -/*****************************************************************************\ - * The declarations below always cause two functions to be declared, one * - * starting with "cebu32_*" and one with "cebu32_ofs_*" which takes a key * - * offset just after the root. The one without kofs just has this argument * - * omitted from its declaration and replaced with sizeof(struct ceb_node) in * - * the call to the underlying functions. * -\*****************************************************************************/ - -/* Inserts node into unique tree based on its key that - * immediately follows the node. Returns the inserted node or the one - * that already contains the same key. - */ -CEB_FDECL3(struct ceb_node *, cebul, _insert, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - unsigned long key = NODEK(node, kofs)->ul; - - if (sizeof(long) <= 4) - return _cebu_insert(root, node, kofs, CEB_KT_U32, key, 0, NULL); - else - return _cebu_insert(root, node, kofs, CEB_KT_U64, 0, key, NULL); -} - -/* return the first node or NULL if not found. */ -CEB_FDECL2(struct ceb_node *, cebul, _first, struct ceb_node **, root, ptrdiff_t, kofs) -{ - if (sizeof(long) <= 4) - return _cebu_first(root, kofs, CEB_KT_U32); - else - return _cebu_first(root, kofs, CEB_KT_U64); -} - -/* return the last node or NULL if not found. */ -CEB_FDECL2(struct ceb_node *, cebul, _last, struct ceb_node **, root, ptrdiff_t, kofs) -{ - if (sizeof(long) <= 4) - return _cebu_last(root, kofs, CEB_KT_U32); - else - return _cebu_last(root, kofs, CEB_KT_U64); -} - -/* look up the specified key, and returns either the node containing it, or - * NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebul, _lookup, struct ceb_node **, root, ptrdiff_t, kofs, unsigned long, key) -{ - if (sizeof(long) <= 4) - return _cebu_lookup(root, kofs, CEB_KT_U32, key, 0, NULL); - else - return _cebu_lookup(root, kofs, CEB_KT_U64, 0, key, NULL); -} - -/* look up the specified key or the highest below it, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebul, _lookup_le, struct ceb_node **, root, ptrdiff_t, kofs, unsigned long, key) -{ - if (sizeof(long) <= 4) - return _cebu_lookup_le(root, kofs, CEB_KT_U32, key, 0, NULL); - else - return _cebu_lookup_le(root, kofs, CEB_KT_U64, 0, key, NULL); -} - -/* look up highest key below the specified one, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebul, _lookup_lt, struct ceb_node **, root, ptrdiff_t, kofs, unsigned long, key) -{ - if (sizeof(long) <= 4) - return _cebu_lookup_lt(root, kofs, CEB_KT_U32, key, 0, NULL); - else - return _cebu_lookup_lt(root, kofs, CEB_KT_U64, 0, key, NULL); -} - -/* look up the specified key or the smallest above it, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebul, _lookup_ge, struct ceb_node **, root, ptrdiff_t, kofs, unsigned long, key) -{ - if (sizeof(long) <= 4) - return _cebu_lookup_ge(root, kofs, CEB_KT_U32, key, 0, NULL); - else - return _cebu_lookup_ge(root, kofs, CEB_KT_U64, 0, key, NULL); -} - -/* look up the smallest key above the specified one, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebul, _lookup_gt, struct ceb_node **, root, ptrdiff_t, kofs, unsigned long, key) -{ - if (sizeof(long) <= 4) - return _cebu_lookup_gt(root, kofs, CEB_KT_U32, key, 0, NULL); - else - return _cebu_lookup_gt(root, kofs, CEB_KT_U64, 0, key, NULL); -} - -/* search for the next node after the specified one, and return it, or NULL if - * not found. The approach consists in looking up that node, recalling the last - * time a left turn was made, and returning the first node along the right - * branch at that fork. - */ -CEB_FDECL3(struct ceb_node *, cebul, _next, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - unsigned long key = NODEK(node, kofs)->ul; - - if (sizeof(long) <= 4) - return _cebu_next(root, kofs, CEB_KT_U32, key, 0, NULL); - else - return _cebu_next(root, kofs, CEB_KT_U64, 0, key, NULL); -} - -/* search for the prev node before the specified one, and return it, or NULL if - * not found. The approach consists in looking up that node, recalling the last - * time a right turn was made, and returning the last node along the left - * branch at that fork. - */ -CEB_FDECL3(struct ceb_node *, cebul, _prev, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - unsigned long key = NODEK(node, kofs)->ul; - - if (sizeof(long) <= 4) - return _cebu_prev(root, kofs, CEB_KT_U32, key, 0, NULL); - else - return _cebu_prev(root, kofs, CEB_KT_U64, 0, key, NULL); -} - -/* look up the specified node with its key and deletes it if found, and in any - * case, returns the node. - */ -CEB_FDECL3(struct ceb_node *, cebul, _delete, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - unsigned long key = NODEK(node, kofs)->ul; - - if (sizeof(long) <= 4) - return _cebu_delete(root, node, kofs, CEB_KT_U32, key, 0, NULL); - else - return _cebu_delete(root, node, kofs, CEB_KT_U64, 0, key, NULL); -} - -/* look up the specified key, and detaches it and returns it if found, or NULL - * if not found. - */ -CEB_FDECL3(struct ceb_node *, cebul, _pick, struct ceb_node **, root, ptrdiff_t, kofs, unsigned long, key) -{ - if (sizeof(long) <= 4) - return _cebu_delete(root, NULL, kofs, CEB_KT_U32, key, 0, NULL); - else - return _cebu_delete(root, NULL, kofs, CEB_KT_U64, 0, key, NULL); -} - -/* dumps a ceb_node tree using the default functions above. If a node matches - * , this one will be highlighted in red. - */ -CEB_FDECL4(void, cebul, _default_dump, struct ceb_node **, root, ptrdiff_t, kofs, const char *, label, const void *, ctx) -{ - printf("\ndigraph cebul_tree {\n" - " fontname=\"fixed\";\n" - " fontsize=8\n" - " label=\"%s\"\n" - "", label); - - printf(" node [fontname=\"fixed\" fontsize=8 shape=\"box\" style=\"filled\" color=\"black\" fillcolor=\"white\"];\n" - " edge [fontname=\"fixed\" fontsize=8 style=\"solid\" color=\"magenta\" dir=\"forward\"];\n"); - - cebu_default_dump_tree(kofs, sizeof(long) <= 4 ? CEB_KT_U32 : CEB_KT_U64, root, 0, NULL, 0, ctx, NULL, NULL, NULL); - - printf("}\n"); -} diff --git a/src/cebus_tree.c b/src/cebus_tree.c deleted file mode 100644 index a981d3afc..000000000 --- a/src/cebus_tree.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Compact Elastic Binary Trees - exported functions operating on string keys - * - * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include -#include "cebtree-prv.h" - -/*****************************************************************************\ - * The declarations below always cause two functions to be declared, one * - * starting with "cebus_*" and one with "cebus_ofs_*" which takes a key * - * offset just after the root. The one without kofs just has this argument * - * omitted from its declaration and replaced with sizeof(struct ceb_node) in * - * the call to the underlying functions. * -\*****************************************************************************/ - -/* Inserts node into unique tree based on its key that - * immediately follows the node. Returns the inserted node or the one - * that already contains the same key. - */ -CEB_FDECL3(struct ceb_node *, cebus, _insert, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - const void *key = NODEK(node, kofs)->str; - - return _cebu_insert(root, node, kofs, CEB_KT_ST, 0, 0, key); -} - -/* return the first node or NULL if not found. */ -CEB_FDECL2(struct ceb_node *, cebus, _first, struct ceb_node **, root, ptrdiff_t, kofs) -{ - return _cebu_first(root, kofs, CEB_KT_ST); -} - -/* return the last node or NULL if not found. */ -CEB_FDECL2(struct ceb_node *, cebus, _last, struct ceb_node **, root, ptrdiff_t, kofs) -{ - return _cebu_last(root, kofs, CEB_KT_ST); -} - -/* look up the specified key, and returns either the node containing it, or - * NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebus, _lookup, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key) -{ - return _cebu_lookup(root, kofs, CEB_KT_ST, 0, 0, key); -} - -/* look up the specified key or the highest below it, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebus, _lookup_le, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key) -{ - return _cebu_lookup_le(root, kofs, CEB_KT_ST, 0, 0, key); -} - -/* look up highest key below the specified one, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebus, _lookup_lt, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key) -{ - return _cebu_lookup_lt(root, kofs, CEB_KT_ST, 0, 0, key); -} - -/* look up the specified key or the smallest above it, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebus, _lookup_ge, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key) -{ - return _cebu_lookup_ge(root, kofs, CEB_KT_ST, 0, 0, key); -} - -/* look up the smallest key above the specified one, and returns either the - * node containing it, or NULL if not found. - */ -CEB_FDECL3(struct ceb_node *, cebus, _lookup_gt, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key) -{ - return _cebu_lookup_gt(root, kofs, CEB_KT_ST, 0, 0, key); -} - -/* search for the next node after the specified one, and return it, or NULL if - * not found. The approach consists in looking up that node, recalling the last - * time a left turn was made, and returning the first node along the right - * branch at that fork. - */ -CEB_FDECL3(struct ceb_node *, cebus, _next, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - const void *key = NODEK(node, kofs)->str; - - return _cebu_next(root, kofs, CEB_KT_ST, 0, 0, key); -} - -/* search for the prev node before the specified one, and return it, or NULL if - * not found. The approach consists in looking up that node, recalling the last - * time a right turn was made, and returning the last node along the left - * branch at that fork. - */ -CEB_FDECL3(struct ceb_node *, cebus, _prev, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - const void *key = NODEK(node, kofs)->str; - - return _cebu_prev(root, kofs, CEB_KT_ST, 0, 0, key); -} - -/* look up the specified node with its key and deletes it if found, and in any - * case, returns the node. - */ -CEB_FDECL3(struct ceb_node *, cebus, _delete, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node) -{ - const void *key = NODEK(node, kofs)->str; - - return _cebu_delete(root, node, kofs, CEB_KT_ST, 0, 0, key); -} - -/* look up the specified key, and detaches it and returns it if found, or NULL - * if not found. - */ -CEB_FDECL3(struct ceb_node *, cebus, _pick, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key) -{ - return _cebu_delete(root, NULL, kofs, CEB_KT_ST, 0, 0, key); -} - -/* dumps a ceb_node tree using the default functions above. If a node matches - * , this one will be highlighted in red. - */ -CEB_FDECL4(void, cebus, _default_dump, struct ceb_node **, root, ptrdiff_t, kofs, const char *, label, const void *, ctx) -{ - printf("\ndigraph cebus_tree {\n" - " fontname=\"fixed\";\n" - " fontsize=8\n" - " label=\"%s\"\n" - "", label); - - printf(" node [fontname=\"fixed\" fontsize=8 shape=\"box\" style=\"filled\" color=\"black\" fillcolor=\"white\"];\n" - " edge [fontname=\"fixed\" fontsize=8 style=\"solid\" color=\"magenta\" dir=\"forward\"];\n"); - - cebu_default_dump_tree(kofs, CEB_KT_ST, root, 0, NULL, 0, ctx, NULL, NULL, NULL); - - printf("}\n"); -} diff --git a/src/tools.c b/src/tools.c index e6a076770..929d93512 100644 --- a/src/tools.c +++ b/src/tools.c @@ -58,7 +58,7 @@ extern void *__elf_aux_vector; #include #endif -#include +#include #include #include #include @@ -127,7 +127,7 @@ int build_is_static = 0; /* known file names, made of file_name_node, to be used with file_name_*() */ struct { - struct ceb_node *root; // file names tree, used with cebus_*() + struct ceb_root *root; // file names tree, used with cebus_*() __decl_thread(HA_RWLOCK_T lock); } file_names = { 0 }; @@ -7254,7 +7254,7 @@ const char *copy_file_name(const char *name) return NULL; HA_RWLOCK_RDLOCK(OTHER_LOCK, &file_names.lock); - node = cebus_lookup(&file_names.root, name); + node = cebus_imm_lookup(&file_names.root, name); HA_RWLOCK_RDUNLOCK(OTHER_LOCK, &file_names.lock); if (node) { @@ -7269,7 +7269,7 @@ const char *copy_file_name(const char *name) memcpy(file->name, name, len + 1); HA_RWLOCK_WRLOCK(OTHER_LOCK, &file_names.lock); - node = cebus_insert(&file_names.root, &file->node); + node = cebus_imm_insert(&file_names.root, &file->node); HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &file_names.lock); if (node != &file->node) { @@ -7288,9 +7288,9 @@ void free_all_file_names() HA_RWLOCK_WRLOCK(OTHER_LOCK, &file_names.lock); - while ((node = cebus_first(&file_names.root))) { + while ((node = cebus_imm_first(&file_names.root))) { file = container_of(node, struct file_name_node, node); - cebus_delete(&file_names.root, node); + cebus_imm_delete(&file_names.root, node); free(file); } diff --git a/src/vars.c b/src/vars.c index 5cef93586..8e7ac8239 100644 --- a/src/vars.c +++ b/src/vars.c @@ -20,7 +20,7 @@ #include #include -#include +#include /* This contains a pool of struct vars */ @@ -190,7 +190,7 @@ unsigned int var_clear(struct vars *vars, struct var *var, int force) var->data.type = SMP_T_ANY; if (!(var->flags & VF_PERMANENT) || force) { - cebu64_delete(&vars->name_root[var->name_hash % VAR_NAME_ROOTS], &var->node); + cebu64_imm_delete(&vars->name_root[var->name_hash % VAR_NAME_ROOTS], &var->node); pool_free(var_pool, var); size += sizeof(struct var); } @@ -208,7 +208,7 @@ void vars_prune_per_sess(struct vars *vars) int i; for (i = 0; i < VAR_NAME_ROOTS; i++) { - while ((node = cebu64_first(&vars->name_root[i]))) { + while ((node = cebu64_imm_first(&vars->name_root[i]))) { var = container_of(node, struct var, node); size += var_clear(vars, var, 1); } @@ -336,7 +336,7 @@ static struct var *var_get(struct vars *vars, uint64_t name_hash) { struct ceb_node *node; - node = cebu64_lookup(&vars->name_root[name_hash % VAR_NAME_ROOTS], name_hash); + node = cebu64_imm_lookup(&vars->name_root[name_hash % VAR_NAME_ROOTS], name_hash); if (node) return container_of(node, struct var, node); @@ -439,7 +439,7 @@ int var_set(const struct var_desc *desc, struct sample *smp, uint flags) var->name_hash = desc->name_hash; var->flags = flags & VF_PERMANENT; var->data.type = SMP_T_ANY; - cebu64_insert(&vars->name_root[var->name_hash % VAR_NAME_ROOTS], &var->node); + cebu64_imm_insert(&vars->name_root[var->name_hash % VAR_NAME_ROOTS], &var->node); } /* A variable of type SMP_T_ANY is considered as unset (either created