| /* |
| * librd - Rapid Development C library |
| * |
| * Copyright (c) 2012-2016, Magnus Edenhill |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "rdkafka_int.h" |
| #include "rdavl.h" |
| |
| /* |
| * AVL tree. |
| * Inspired by Ian Piumarta's tree.h implementation. |
| */ |
| |
| #define RD_AVL_NODE_HEIGHT(ran) ((ran) ? (ran)->ran_height : 0) |
| |
| #define RD_AVL_NODE_DELTA(ran) \ |
| (RD_AVL_NODE_HEIGHT((ran)->ran_p[RD_AVL_LEFT]) - \ |
| RD_AVL_NODE_HEIGHT((ran)->ran_p[RD_AVL_RIGHT])) |
| |
| #define RD_DELTA_MAX 1 |
| |
| |
| static rd_avl_node_t *rd_avl_balance_node (rd_avl_node_t *ran); |
| |
| static rd_avl_node_t *rd_avl_rotate (rd_avl_node_t *ran, rd_avl_dir_t dir) { |
| rd_avl_node_t *n; |
| static const rd_avl_dir_t odirmap[] = { /* opposite direction map */ |
| [RD_AVL_RIGHT] = RD_AVL_LEFT, |
| [RD_AVL_LEFT] = RD_AVL_RIGHT |
| }; |
| const int odir = odirmap[dir]; |
| |
| n = ran->ran_p[odir]; |
| ran->ran_p[odir] = n->ran_p[dir]; |
| n->ran_p[dir] = rd_avl_balance_node(ran); |
| |
| return rd_avl_balance_node(n); |
| } |
| |
| static rd_avl_node_t *rd_avl_balance_node (rd_avl_node_t *ran) { |
| const int d = RD_AVL_NODE_DELTA(ran); |
| int h; |
| |
| if (d < -RD_DELTA_MAX) { |
| if (RD_AVL_NODE_DELTA(ran->ran_p[RD_AVL_RIGHT]) > 0) |
| ran->ran_p[RD_AVL_RIGHT] = |
| rd_avl_rotate(ran->ran_p[RD_AVL_RIGHT], |
| RD_AVL_RIGHT); |
| return rd_avl_rotate(ran, RD_AVL_LEFT); |
| |
| } else if (d > RD_DELTA_MAX) { |
| if (RD_AVL_NODE_DELTA(ran->ran_p[RD_AVL_LEFT]) < 0) |
| ran->ran_p[RD_AVL_LEFT] = |
| rd_avl_rotate(ran->ran_p[RD_AVL_LEFT], |
| RD_AVL_LEFT); |
| |
| return rd_avl_rotate(ran, RD_AVL_RIGHT); |
| } |
| |
| ran->ran_height = 0; |
| |
| if ((h = RD_AVL_NODE_HEIGHT(ran->ran_p[RD_AVL_LEFT])) > ran->ran_height) |
| ran->ran_height = h; |
| |
| if ((h = RD_AVL_NODE_HEIGHT(ran->ran_p[RD_AVL_RIGHT])) >ran->ran_height) |
| ran->ran_height = h; |
| |
| ran->ran_height++; |
| |
| return ran; |
| } |
| |
| rd_avl_node_t *rd_avl_insert_node (rd_avl_t *ravl, |
| rd_avl_node_t *parent, |
| rd_avl_node_t *ran, |
| rd_avl_node_t **existing) { |
| rd_avl_dir_t dir; |
| int r; |
| |
| if (!parent) |
| return ran; |
| |
| if ((r = ravl->ravl_cmp(ran->ran_elm, parent->ran_elm)) == 0) { |
| /* Replace existing node with new one. */ |
| ran->ran_p[RD_AVL_LEFT] = parent->ran_p[RD_AVL_LEFT]; |
| ran->ran_p[RD_AVL_RIGHT] = parent->ran_p[RD_AVL_RIGHT]; |
| ran->ran_height = parent->ran_height; |
| *existing = parent; |
| return ran; |
| } |
| |
| if (r < 0) |
| dir = RD_AVL_LEFT; |
| else |
| dir = RD_AVL_RIGHT; |
| |
| parent->ran_p[dir] = rd_avl_insert_node(ravl, parent->ran_p[dir], |
| ran, existing); |
| return rd_avl_balance_node(parent); |
| } |
| |
| |
| static rd_avl_node_t *rd_avl_move (rd_avl_node_t *dst, rd_avl_node_t *src, |
| rd_avl_dir_t dir) { |
| |
| if (!dst) |
| return src; |
| |
| dst->ran_p[dir] = rd_avl_move(dst->ran_p[dir], src, dir); |
| |
| return rd_avl_balance_node(dst); |
| } |
| |
| static rd_avl_node_t *rd_avl_remove_node0 (rd_avl_node_t *ran) { |
| rd_avl_node_t *tmp; |
| |
| tmp = rd_avl_move(ran->ran_p[RD_AVL_LEFT], |
| ran->ran_p[RD_AVL_RIGHT], |
| RD_AVL_RIGHT); |
| |
| ran->ran_p[RD_AVL_LEFT] = ran->ran_p[RD_AVL_RIGHT] = NULL; |
| return tmp; |
| } |
| |
| |
| rd_avl_node_t *rd_avl_remove_elm0 (rd_avl_t *ravl, rd_avl_node_t *parent, |
| const void *elm) { |
| rd_avl_dir_t dir; |
| int r; |
| |
| if (!parent) |
| return NULL; |
| |
| |
| if ((r = ravl->ravl_cmp(elm, parent->ran_elm)) == 0) |
| return rd_avl_remove_node0(parent); |
| else if (r < 0) |
| dir = RD_AVL_LEFT; |
| else /* > 0 */ |
| dir = RD_AVL_RIGHT; |
| |
| parent->ran_p[dir] = |
| rd_avl_remove_elm0(ravl, parent->ran_p[dir], elm); |
| |
| return rd_avl_balance_node(parent); |
| } |
| |
| |
| |
| rd_avl_node_t *rd_avl_find_node (const rd_avl_t *ravl, |
| const rd_avl_node_t *begin, |
| const void *elm) { |
| int r; |
| |
| if (!begin) |
| return NULL; |
| else if (!(r = ravl->ravl_cmp(elm, begin->ran_elm))) |
| return (rd_avl_node_t *)begin; |
| else if (r < 0) |
| return rd_avl_find_node(ravl, begin->ran_p[RD_AVL_LEFT], elm); |
| else /* r > 0 */ |
| return rd_avl_find_node(ravl, begin->ran_p[RD_AVL_RIGHT], elm); |
| } |
| |
| |
| |
| void rd_avl_destroy (rd_avl_t *ravl) { |
| if (ravl->ravl_flags & RD_AVL_F_LOCKS) |
| rwlock_destroy(&ravl->ravl_rwlock); |
| |
| if (ravl->ravl_flags & RD_AVL_F_OWNER) |
| free(ravl); |
| } |
| |
| rd_avl_t *rd_avl_init (rd_avl_t *ravl, rd_avl_cmp_t cmp, int flags) { |
| |
| if (!ravl) { |
| ravl = calloc(1, sizeof(*ravl)); |
| flags |= RD_AVL_F_OWNER; |
| } else { |
| memset(ravl, 0, sizeof(*ravl)); |
| } |
| |
| ravl->ravl_flags = flags; |
| ravl->ravl_cmp = cmp; |
| |
| if (flags & RD_AVL_F_LOCKS) |
| rwlock_init(&ravl->ravl_rwlock); |
| |
| return ravl; |
| } |