Module: xenomai-3 Branch: next Commit: e570b31cdb914620c97cbea11368e8f0264de758 URL: http://git.xenomai.org/?p=xenomai-3.git;a=commit;h=e570b31cdb914620c97cbea11368e8f0264de758
Author: Philippe Gerum <r...@xenomai.org> Date: Tue May 31 17:30:21 2016 +0200 boilerplate: add AVL library --- include/boilerplate/Makefile.am | 1 + include/boilerplate/avl.h | 298 ++++++++++++++++++++++++++++++ lib/boilerplate/Makefile.am | 1 + lib/boilerplate/avl.c | 380 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 680 insertions(+) diff --git a/include/boilerplate/Makefile.am b/include/boilerplate/Makefile.am index 2d3ace8..c975509 100644 --- a/include/boilerplate/Makefile.am +++ b/include/boilerplate/Makefile.am @@ -3,6 +3,7 @@ includesubdir = $(includedir)/boilerplate includesub_HEADERS = \ ancillaries.h \ atomic.h \ + avl.h \ compiler.h \ debug.h \ hash.h \ diff --git a/include/boilerplate/avl.h b/include/boilerplate/avl.h new file mode 100644 index 0000000..1aa84bf --- /dev/null +++ b/include/boilerplate/avl.h @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2015 Gilles Chanteperdrix + * + * 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 _BOILERPLATE_AVL_H +#define _BOILERPLATE_AVL_H + +#include <stdlib.h> + +struct avlh { + unsigned int thr: 3; + int type: 2; + int balance :2; + unsigned int flags :25; /* Application-specific */ + struct avlh *link[3]; +}; + +/* Using -1 and 1 for left and right is slightly faster than 0 and 1, using 0 + for "up" is just here for orthogonality... and avoid wasting 4 bytes or + having to use a union in struct avlh. */ +#define AVL_LEFT -1 +#define AVL_UP 0 +#define AVL_RIGHT 1 +/* maps AVL_LEFT to AVL_RIGHT and reciprocally. */ +#define avl_opposite(type) (-(type)) +/* maps AVL_LEFT to -1 and AVL_RIGHT to 1. */ +#define avl_type2sign(type) (type) +/* maps AVL_LEFT and AVL_RIGHT to arrays index (or bit positions). */ +#define avl_type2index(type) ((type)+1) +/* maps <0 to AVL_LEFT and >0 to AVL_RIGHT. */ +#define avl_sign2type(sign) (sign) + +#define AVL_THR_LEFT (1<<avl_type2index(AVL_LEFT)) +#define AVL_THR_RIGHT (1<<avl_type2index(AVL_RIGHT)) + +#define avlh_thr_set(holder, side) ((holder)->thr |= 1 << avl_type2index(side)) +#define avlh_thr_clr(holder, side) ((holder)->thr &= ~(1 << avl_type2index(side))) +#define avlh_thr_tst(holder, side) ((holder)->thr & (1 << avl_type2index(side))) +#define avlh_link(holder, dir) ((holder)->link[avl_type2index(dir)]) +#define avlh_up(holder) avlh_link((holder), AVL_UP) +#define avlh_left(holder) avlh_link((holder), AVL_LEFT) +#define avlh_right(holder) avlh_link((holder), AVL_RIGHT) +#define avlh_parent_link(holder) (avlh_link(avlh_up(holder), (holder)->type)) + +struct avl; + +typedef struct avlh *avl_search_t(const struct avl *, const struct avlh *, int *); + +typedef int avlh_cmp_t(const struct avlh *const, const struct avlh *const); + +struct avl { + struct avlh anchor; + avl_search_t *search; + avlh_cmp_t *cmp; + struct avlh *end[3]; + unsigned int count; + unsigned int height; +}; + +#define avl_searchfn(avl) ((avl)->search) +#define avl_cmp(avl) ((avl)->cmp) +#define avl_count(avl) ((avl)->count) +#define avl_height(avl) ((avl)->height) +#define avl_anchor(avl) (&(avl)->anchor) +#define avl_end(avl, dir) ((avl)->end[avl_type2index(dir)]) +#define avl_top(avl) (avlh_right(avl_anchor(avl))) +#define avl_head(avl) (avl_end((avl), AVL_LEFT)) +#define avl_tail(avl) (avl_end((avl), AVL_RIGHT)) + +#ifdef __cplusplus +extern "C" { +#endif + +void avl_init(struct avl *avl, avl_search_t *search, avlh_cmp_t *cmp); + +void avl_destroy(struct avl *avl); + +void avl_clear(struct avl *avl, void (*destruct)(struct avlh *)); + +int avl_insert(struct avl *avl, struct avlh *holder); + +int avl_prepend(struct avl *avl, struct avlh *holder); + +int avl_append(struct avl *avl, struct avlh *holder); + +struct avlh *avl_update(struct avl *avl, struct avlh *holder); + +struct avlh *avl_set(struct avl *avl, struct avlh *holder); + +int avl_delete(struct avl *avl, struct avlh *node); + +static inline struct avlh *avl_gettop(struct avl *const avl) +{ + struct avlh *const holder = avl_top(avl); + + if (holder != avl_anchor(avl)) + return holder; + + return NULL; +} + +static inline struct avlh *avl_gethead(struct avl *const avl) +{ + struct avlh *const holder = avl_head(avl); + + if (holder != avl_anchor(avl)) + return holder; + + return NULL; +} + +static inline struct avlh *avl_gettail(struct avl *const avl) +{ + struct avlh *const holder = avl_tail(avl); + + if (holder != avl_anchor(avl)) + return holder; + + return NULL; +} + +static inline unsigned avl_getcount(struct avl *const avl) +{ + return avl_count(avl); +} + +static inline struct avlh *avl_inorder(struct avl *const avl, + struct avlh *const holder, + const int dir) +{ + /* Assume dir == AVL_RIGHT in comments. */ + struct avlh *child = avlh_link(holder, dir); + + /* If the current node is not right threaded, then go down left, starting + from its right child. */ + if (!avlh_thr_tst(holder, dir)) { + const int opp_dir = avl_opposite(dir); + while (!avlh_thr_tst(child, opp_dir)) + child = avlh_link(child, opp_dir); + } else + /* Else follow its right thread. */ + if (child != avl_anchor(avl)) + return child; + else + return NULL; + + return child; +} + +static inline struct avlh *avl_postorder(struct avl *const avl, + struct avlh *const holder, const int dir) +{ + /* Assume dir == AVL_RIGHT in comments. */ + struct avlh *next = avlh_up(holder); + + if (holder->type != dir) + /* If the current node is not a right node, follow the nodes in inorder + until we find a right threaded node. */ + while (!avlh_thr_tst(next, dir)) + next = avl_inorder(avl, next, dir); + else + /* else the current node is a right node, its parent is the next in + postorder. */ + if (next != avl_anchor(avl)) + return next; + else + return NULL; + + return next; +} + +static inline struct avlh *avl_preorder(struct avl *const avl, + struct avlh *holder, const int dir) +{ + struct avlh *next; + /* Assume dir == AVL_RIGHT in comments. */ + /* If the current node has a left child (hence is not left threaded), then + return it. */ + if (!avlh_thr_tst(holder, avl_opposite(dir))) + return avlh_link(holder, avl_opposite(dir)); + + /* Else follow the right threads until we find a node which is not right + threaded (hence has a right child) and return its right child. */ + next = holder; + + while (avlh_thr_tst(next, dir)) { + next = avlh_link(next, dir); + if (next == avl_anchor(avl)) + goto ret_null; + } + + return avlh_link(next, dir); +ret_null: + return NULL; +} + +/** + * Get next node in symmetrical a.k.a inorder ordering. + */ +static inline struct avlh *avl_next(struct avl *const avl, struct avlh *const holder) +{ + return avl_inorder(avl, holder, AVL_RIGHT); +} + +/** + * Get previous node in symmetrical a.k.a inorder ordering. + */ +static inline struct avlh *avl_prev(struct avl *const avl, struct avlh *const holder) +{ + return avl_inorder(avl, holder, AVL_LEFT); +} + +static inline struct avlh *avl_postorder_next(struct avl *const avl, struct avlh *const holder) +{ + return avl_postorder(avl, holder, AVL_RIGHT); +} + +static inline struct avlh *avl_postorder_prev(struct avl *const avl, struct avlh *const holder) +{ + return avl_postorder(avl, holder, AVL_LEFT); +} + +static inline struct avlh *avl_preorder_next(struct avl *const avl, struct avlh *const holder) +{ + return avl_preorder(avl, holder, AVL_RIGHT); +} + +static inline struct avlh *avl_preorder_prev(struct avl *const avl, struct avlh *const holder) +{ + return avl_preorder(avl, holder, AVL_LEFT); +} + +static inline void avlh_init(struct avlh *const holder) +{ + *(unsigned long *)holder = 0UL; /* Please valgrind */ + holder->thr = AVL_THR_LEFT | AVL_THR_RIGHT; + holder->balance = 0; + holder->type = 0; +} + +static inline struct avlh *avl_search(struct avl *const avl, const struct avlh *node) +{ + struct avlh *holder; + int delta; + + holder = avl_searchfn(avl)(avl, node, &delta); + if (!delta) + return holder; + + return NULL; +} + +#ifdef __cplusplus +} +#endif + +/** + * Search a node, return its parent if it could not be found. + */ +#define DECLARE_AVL_SEARCH(avl_search_inner, cmp) \ +static struct avlh *avl_search_inner(const struct avl *const avl, \ + const struct avlh *const node, \ + int *const pdelta) \ +{ \ + int delta = avl_type2sign(AVL_RIGHT); \ + struct avlh *holder = avl_top(avl); \ + \ + if (holder != avl_anchor(avl)) { \ + while ((delta = cmp(holder, node))) { \ + delta = delta < 0 ? -1 : 1; \ + if (avlh_thr_tst(holder,avl_sign2type(delta))) \ + break; \ + holder = avlh_link(holder, avl_sign2type(delta)); \ + } \ + } \ + *pdelta = delta; \ + return holder; \ +} \ + +#endif /* !_BOILERPLATE_AVL_H */ diff --git a/lib/boilerplate/Makefile.am b/lib/boilerplate/Makefile.am index 4176c6d..c9f8d41 100644 --- a/lib/boilerplate/Makefile.am +++ b/lib/boilerplate/Makefile.am @@ -5,6 +5,7 @@ libboilerplate_la_LIBADD = libversion.la libiniparser.la libboilerplate_la_SOURCES = \ ancillaries.c \ + avl.c \ hash.c \ obstack.c \ setup.c \ diff --git a/lib/boilerplate/avl.c b/lib/boilerplate/avl.c new file mode 100644 index 0000000..4dcc177 --- /dev/null +++ b/lib/boilerplate/avl.c @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2015 Gilles Chanteperdrix + * + * 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 <stdio.h> +#include <errno.h> +#include <string.h> +#include <boilerplate/avl.h> + +/* Internal functions used for rebalancing (for insertion and deletion). */ +static inline struct avlh *avlh_rotate(struct avlh *const holder, const int dir) +{ + const int opp_dir = avl_opposite(dir); + struct avlh *const nexttop = avlh_link(holder, opp_dir); + + if (!avlh_thr_tst(nexttop, dir)) { + struct avlh *const subtree = avlh_link(nexttop, dir); + + avlh_link(holder, opp_dir) = subtree; + avlh_thr_clr(holder, opp_dir); + avlh_up(subtree) = holder; + subtree->type = opp_dir; + } else + avlh_thr_set(holder, opp_dir); + + avlh_link(nexttop, dir) = holder; + avlh_thr_clr(nexttop, dir); + + avlh_up(nexttop) = avlh_up(holder); + nexttop->type = holder->type; + avlh_up(holder) = nexttop; + holder->type = dir; + + avlh_parent_link(nexttop) = nexttop; + + return nexttop; +} + +static inline struct avlh *avlh_dbl_rotate(struct avlh *const holder, const int dir) +{ + const int opp = avl_opposite(dir); + + avlh_rotate(avlh_link(holder, opp), opp); + return avlh_rotate(holder, dir); +} + +static struct avlh *avlh_rebalance(struct avlh *holder, const int delta) +{ + + int dir = avl_sign2type(delta); + struct avlh *const heavy_side = avlh_link(holder, dir); + + if (heavy_side->balance == -delta) { + /* heavy_side->balance == -delta, double rotation needed. */ + holder = avlh_dbl_rotate(holder, avl_opposite(dir)); + + /* recompute balances, there are three nodes involved, two of which + balances become null.*/ + dir = holder->balance ? avl_sign2type(holder->balance) : AVL_RIGHT; + avlh_link(holder, dir)->balance = 0; + avlh_link(holder, avl_opposite(dir))->balance = -holder->balance; + holder->balance = 0; + } else { + /* heavy_side->balance == delta or 0, simple rotation needed. + the case 0 occurs only when deleting, never when inserting. */ + /* heavy_side becomes the new root. */ + avlh_rotate(holder, avl_opposite(dir)); + + /* recompute balances. */ + holder->balance -= heavy_side->balance; + heavy_side->balance -= delta; + + holder = heavy_side; + } + return holder; +} + +/* The avlh_rebalance functions was split in two parts to allow inlining in + the simplest case. */ +static inline struct avlh *avlh_balance_add(struct avlh *const holder, const int delta) +{ + if (holder->balance == delta) + /* we need to rebalance the current subtree. */ + return avlh_rebalance(holder, delta); + + /* the current subtree does not need rebalancing */ + holder->balance += delta; + return holder; +} + +static inline void avlh_link_child(struct avlh *const oldh, + struct avlh *const newh, const int side) +{ + struct avlh *const child = avlh_link(oldh, side); + + avlh_link(newh, side) = child; + if (!avlh_thr_tst(oldh, side)) { + /* avl_inorder won't use its tree parameter, hence NULL is Ok. */ + struct avlh *const inorder_adj = avl_inorder(NULL, oldh, side); + const int opp_side = avl_opposite(side); + avlh_link(inorder_adj, opp_side) = newh; + /* Do not change child before using avl_prev... */ + avlh_up(child) = newh; + } +} + +static inline void avlh_replace(struct avlh *const oldh, struct avlh *const newh) +{ + newh->thr = oldh->thr; + newh->type = oldh->type; + /* Do not update the balance, this has to be done by the caller. */ + + avlh_up(newh) = avlh_up(oldh); + avlh_parent_link(oldh) = newh; + + avlh_link_child(oldh, newh, AVL_LEFT); + avlh_link_child(oldh, newh, AVL_RIGHT); +} + +/* Deletion helpers. */ +static void avl_delete_leaf(struct avl *const avl, struct avlh *const node) +{ + /* Node has no child at all. It disappears and its father becomes + threaded on the side id was. */ + + struct avlh *const new_node = avlh_up(node); + const int dir = node->type; + + /* Suppress node. */ + avlh_link(new_node, dir) = avlh_link(node, dir); + avlh_thr_set(new_node, dir); + + if (node == avl_end(avl, dir)) + avl_end(avl, dir) = new_node; +} + +static struct avlh *avl_delete_1child(struct avl *const avl, + struct avlh *const node, const int dir) +{ + /* Node is threaded on one side and has a child on the other + side. In this case, node is replaced by its child. */ + + struct avlh *const new_node = avlh_link(node, dir); + + /* Change links as if new_node was suppressed before calling + avlh_replace. */ + avlh_link(node, dir) = avlh_link(new_node, dir); + if (avlh_thr_tst(new_node, dir)) + avlh_thr_set(node, dir); + avlh_replace(node, new_node); + + if (node == avl_end(avl, avl_opposite(dir))) + avl_end(avl, avl_opposite(dir)) = new_node; + /* new_node->balance 0, which is correct. */ + return new_node; +} + +static int avl_delete_2children(struct avl *const avl, struct avlh *const node); + +/* Insertion helpers. */ +static inline void avlh_attach(struct avlh *const parent, + struct avlh *const child, const int side) +{ + avlh_link(child, side) = avlh_link(parent, side); + avlh_link(child, avl_opposite(side)) = parent; + avlh_up(child) = parent; + avlh_link(parent, side) = child; + child->type = side; +} + +/** + * Insert a node, given its parent and the side where it should be inserted. + * Helper for all insertion functions. + */ +static inline void avl_insert_inner(struct avl *const avl, struct avlh *parent, + struct avlh *const node, const int side) +{ + avlh_attach(parent, node, side); + ++avl_count(avl); + + if (parent == avl_anchor(avl)) { + goto insert_first_and_ret; /* Get away from fast path */ + } else if (parent == avl_end(avl, side)) + avl_end(avl, side) = node; + + /* Do not touch these for first insertion. */ + avlh_thr_clr(parent, side); + parent->balance += avl_type2sign(side); + + while (parent->balance) { + const int delta = avl_type2sign(parent->type); + parent = avlh_up(parent); + if (parent == avl_anchor(avl)) + goto inc_height_and_ret; /* Get away from fast path */ + parent = avlh_balance_add(parent, delta); + } + + return; + +insert_first_and_ret: + avl_head(avl) = avl_tail(avl) = node; +inc_height_and_ret: + ++avl_height(avl); + return; +} + +/* External functions. */ + +int avl_delete(struct avl *const avl, struct avlh *node) +{ + if (!--avl_count(avl)) { + goto delete_last_and_ret; + } + + switch(node->thr) { + case (AVL_THR_LEFT|AVL_THR_RIGHT): /* thr is 5 */ + avl_delete_leaf(avl, node); + break; + + case AVL_THR_LEFT: /* only AVL_LEFT bit is on, thr is 1. */ + node = avl_delete_1child(avl, node, AVL_RIGHT); + break; + + case AVL_THR_RIGHT: /* only AVL_RIGHT bit is on, thr is 4. */ + node = avl_delete_1child(avl, node, AVL_LEFT); + break; + + case 0: + return avl_delete_2children(avl, node); + } + + /* node is the first node which needs to be rebalanced. + The tree is rebalanced, and contrarily to what happened for insertion, + the rebalancing stops when a node which is NOT balanced is met. */ + while (!node->balance) { + const int delta = -avl_type2sign(node->type); + node = avlh_up(node); + if (node == avl_anchor(avl)) + goto dec_height_and_ret; + node = avlh_balance_add(node, delta); + } + + return 0; + +delete_last_and_ret: + avl_top(avl) = avl_head(avl) = avl_tail(avl) = avl_anchor(avl); +dec_height_and_ret: + --avl_height(avl); + return 0; +} + +static int avl_delete_2children(struct avl *const avl, struct avlh *const node) +{ + const int dir = avl_sign2type(node->balance ? node->balance : 1); + struct avlh *const new_node = avl_inorder(avl, node, dir); + avl_delete(avl, new_node); + ++avl_count(avl); + avlh_replace(node, new_node); + new_node->balance = node->balance; + if (avl_end(avl, dir) == node) + avl_end(avl, dir) = new_node; + return 0; +} + +int avl_prepend(struct avl *const avl, struct avlh *const holder) +{ + struct avlh *const parent = avl_head(avl); + int type = parent == avl_anchor(avl) ? AVL_RIGHT : AVL_LEFT; + + if (parent == avl_anchor(avl) || avl_cmp(avl)(parent, holder) < 0) { + avl_insert_inner(avl, parent, holder, avl_type2sign(type)); + return 0; + } + + return -EINVAL; +} + +int avl_insert(struct avl *const avl, struct avlh *const holder) +{ + int delta; + struct avlh *parent; + + parent = avl_searchfn(avl)(avl, holder, &delta); + if (delta == 0) + return -EBUSY; + + avl_insert_inner(avl, parent, holder, avl_sign2type(delta)); + return 0; +} + +int avl_append(struct avl *const avl, struct avlh *const holder) +{ + struct avlh *const parent = avl_tail(avl); + + if (parent == avl_anchor(avl) || avl_cmp(avl)(parent, holder) > 0) { + avl_insert_inner(avl, parent, holder, avl_type2sign(AVL_RIGHT)); + return 0; + } + + return -EINVAL; +} + +struct avlh *avl_update(struct avl *const avl, struct avlh *const holder) +{ + int delta; + struct avlh *const oldh = avl_searchfn(avl)(avl, holder, &delta); + + if (!delta) { + avlh_replace(oldh, holder); + holder->balance = oldh->balance; + return oldh; + } + + return NULL; +} + +struct avlh *avl_set(struct avl *const avl, struct avlh *const holder) +{ + int delta; + struct avlh *const oldh = avl_searchfn(avl)(avl, holder, &delta); + + if (delta) { + avl_insert_inner(avl, oldh, holder, avl_sign2type(delta)); + return NULL; + } + + avlh_replace(oldh, holder); + holder->balance = oldh->balance; + return oldh; +} + +void avl_init(struct avl *avl, avl_search_t *search, avlh_cmp_t *cmp) +{ + avlh_init(avl_anchor(avl)); /* Beware of the union; this must be first. */ + avl_cmp(avl) = cmp; + avl_height(avl) = 0; + avl_count(avl) = 0; + avl_searchfn(avl) = search; + avlh_right(avl_anchor(avl)) = avl_anchor(avl); + + avl_head(avl) = avl_tail(avl) = avl_anchor(avl); +} + +void avl_destroy(struct avl *avl) +{ + avl_init(avl, NULL, NULL); +} + +void avl_clear(struct avl *const avl, void (*destruct)(struct avlh *)) +{ + if (destruct) { + struct avlh *next, *holder = avl_gethead(avl); + + while (holder) { + next = avl_postorder_next(avl, holder); + destruct(holder); + holder = next; + } + } + + avl_init(avl, avl_searchfn(avl), avl_cmp(avl)); +} _______________________________________________ Xenomai-git mailing list Xenomai-git@xenomai.org https://xenomai.org/mailman/listinfo/xenomai-git