blob: 2f58dd4b8e38be8e17f3124a9c0b95612e0e1b72 [file] [log] [blame]
/*
* 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;
}