1 1.5 christos /* $NetBSD: ipf_rb.h,v 1.5 2013/01/09 13:23:20 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* 4 1.3 darrenr * Copyright (C) 2012 by Darren Reed. 5 1.1 christos * 6 1.1 christos * See the IPFILTER.LICENCE file for details on licencing. 7 1.1 christos * 8 1.1 christos */ 9 1.4 christos 10 1.4 christos /* 11 1.4 christos * If the OS has a red-black tree implementation, use it. 12 1.4 christos */ 13 1.4 christos #ifdef HAVE_RBTREE 14 1.4 christos 15 1.4 christos #include <sys/rbtree.h> 16 1.4 christos 17 1.4 christos # define RBI_LINK(_n, _t) 18 1.4 christos # define RBI_FIELD(_n) rb_node_t 19 1.4 christos # define RBI_HEAD(_n, _t) rb_tree_t 20 1.4 christos 21 1.4 christos /* Define adapter code between the ipf-specific and the system rb impls. */ 22 1.4 christos # define RBI_CODE(_n, _t, _f, _cmp) \ 23 1.4 christos signed int _n##_compare_nodes(void *ctx, const void *n1, const void *n2);\ 24 1.4 christos signed int _n##_compare_key(void *ctx, const void *n1, const void *key);\ 25 1.4 christos typedef void (*_n##_rb_walker_t)(_t *, void *); \ 26 1.4 christos void _n##_rb_walktree(rb_tree_t *, _n##_rb_walker_t, void *); \ 27 1.4 christos \ 28 1.4 christos static const rb_tree_ops_t _n##_tree_ops = { \ 29 1.4 christos .rbto_compare_nodes = _n##_compare_nodes, \ 30 1.4 christos .rbto_compare_key = _n##_compare_key, \ 31 1.4 christos .rbto_node_offset = offsetof(_t, _f), \ 32 1.4 christos .rbto_context = NULL \ 33 1.4 christos }; \ 34 1.4 christos \ 35 1.4 christos int \ 36 1.4 christos _n##_compare_nodes(void *ctx, const void *n1, const void *n2) { \ 37 1.4 christos return _cmp(n1, n2); \ 38 1.4 christos } \ 39 1.4 christos \ 40 1.4 christos int \ 41 1.4 christos _n##_compare_key(void *ctx, const void *n1, const void *key) { \ 42 1.4 christos return _cmp(n1, key); \ 43 1.4 christos } \ 44 1.4 christos \ 45 1.4 christos void \ 46 1.4 christos _n##_rb_walktree(rb_tree_t *head, _n##_rb_walker_t func, void *arg) \ 47 1.4 christos { \ 48 1.4 christos _t *rb; \ 49 1.4 christos /* Take advantage of the fact that the ipf code only uses this \ 50 1.4 christos method to clear the tree, in order to do it more safely. */ \ 51 1.4 christos while ((rb = rb_tree_iterate(head, NULL, RB_DIR_RIGHT)) != NULL) {\ 52 1.4 christos rb_tree_remove_node(head, rb); \ 53 1.4 christos func(rb, arg); \ 54 1.4 christos } \ 55 1.4 christos } 56 1.4 christos 57 1.4 christos # define RBI_DELETE(_n, _h, _v) rb_tree_remove_node(_h, _v) 58 1.4 christos # define RBI_INIT(_n, _h) rb_tree_init(_h, &_n##_tree_ops) 59 1.4 christos # define RBI_INSERT(_n, _h, _v) rb_tree_insert_node(_h, _v) 60 1.4 christos # define RBI_ISEMPTY(_h) (rb_tree_iterate(_h, NULL, RB_DIR_RIGHT) == NULL) 61 1.4 christos # define RBI_SEARCH(_n, _h, _k) rb_tree_find_node(_h, _k) 62 1.4 christos # define RBI_WALK(_n, _h, _w, _a) _n##_rb_walktree(_h, _w, _a) 63 1.4 christos 64 1.4 christos #else 65 1.4 christos 66 1.1 christos typedef enum rbcolour_e { 67 1.1 christos C_BLACK = 0, 68 1.1 christos C_RED = 1 69 1.1 christos } rbcolour_t; 70 1.1 christos 71 1.4 christos #define RBI_LINK(_n, _t) \ 72 1.1 christos struct _n##_rb_link { \ 73 1.1 christos struct _t *left; \ 74 1.1 christos struct _t *right; \ 75 1.1 christos struct _t *parent; \ 76 1.1 christos rbcolour_t colour; \ 77 1.1 christos } 78 1.1 christos 79 1.1 christos #define RBI_HEAD(_n, _t) \ 80 1.1 christos struct _n##_rb_head { \ 81 1.1 christos struct _t top; \ 82 1.1 christos int count; \ 83 1.1 christos int (* compare)(struct _t *, struct _t *); \ 84 1.1 christos } 85 1.1 christos 86 1.4 christos #define RBI_FIELD(_n) struct _n##_rb_link 87 1.4 christos 88 1.1 christos #define RBI_CODE(_n, _t, _f, _cmp) \ 89 1.1 christos \ 90 1.4 christos _t RBI_ZERO(_n); \ 91 1.4 christos \ 92 1.1 christos typedef void (*_n##_rb_walker_t)(_t *, void *); \ 93 1.1 christos \ 94 1.1 christos _t * _n##_rb_delete(struct _n##_rb_head *, _t *); \ 95 1.1 christos void _n##_rb_init(struct _n##_rb_head *); \ 96 1.1 christos void _n##_rb_insert(struct _n##_rb_head *, _t *); \ 97 1.1 christos _t * _n##_rb_search(struct _n##_rb_head *, void *); \ 98 1.1 christos void _n##_rb_walktree(struct _n##_rb_head *, _n##_rb_walker_t, void *);\ 99 1.1 christos \ 100 1.1 christos static void \ 101 1.1 christos rotate_left(struct _n##_rb_head *head, _t *node) \ 102 1.1 christos { \ 103 1.1 christos _t *parent, *tmp1, *tmp2; \ 104 1.1 christos \ 105 1.1 christos parent = node->_f.parent; \ 106 1.1 christos tmp1 = node->_f.right; \ 107 1.1 christos tmp2 = tmp1->_f.left; \ 108 1.1 christos node->_f.right = tmp2; \ 109 1.1 christos if (tmp2 != & _n##_rb_zero) \ 110 1.1 christos tmp2->_f.parent = node; \ 111 1.1 christos if (parent == & _n##_rb_zero) \ 112 1.1 christos head->top._f.right = tmp1; \ 113 1.1 christos else if (parent->_f.right == node) \ 114 1.1 christos parent->_f.right = tmp1; \ 115 1.1 christos else \ 116 1.1 christos parent->_f.left = tmp1; \ 117 1.1 christos tmp1->_f.left = node; \ 118 1.1 christos tmp1->_f.parent = parent; \ 119 1.1 christos node->_f.parent = tmp1; \ 120 1.1 christos } \ 121 1.1 christos \ 122 1.1 christos static void \ 123 1.1 christos rotate_right(struct _n##_rb_head *head, _t *node) \ 124 1.1 christos { \ 125 1.1 christos _t *parent, *tmp1, *tmp2; \ 126 1.1 christos \ 127 1.1 christos parent = node->_f.parent; \ 128 1.1 christos tmp1 = node->_f.left; \ 129 1.1 christos tmp2 = tmp1->_f.right; \ 130 1.1 christos node->_f.left = tmp2; \ 131 1.1 christos if (tmp2 != &_n##_rb_zero) \ 132 1.1 christos tmp2->_f.parent = node; \ 133 1.1 christos if (parent == &_n##_rb_zero) \ 134 1.1 christos head->top._f.right = tmp1; \ 135 1.1 christos else if (parent->_f.right == node) \ 136 1.1 christos parent->_f.right = tmp1; \ 137 1.1 christos else \ 138 1.1 christos parent->_f.left = tmp1; \ 139 1.1 christos tmp1->_f.right = node; \ 140 1.1 christos tmp1->_f.parent = parent; \ 141 1.1 christos node->_f.parent = tmp1; \ 142 1.1 christos } \ 143 1.1 christos \ 144 1.1 christos void \ 145 1.1 christos _n##_rb_insert(struct _n##_rb_head *head, _t *node) \ 146 1.1 christos { \ 147 1.1 christos _t *n, *parent, **p, *tmp1, *gparent; \ 148 1.1 christos \ 149 1.1 christos parent = &head->top; \ 150 1.1 christos node->_f.left = &_n##_rb_zero; \ 151 1.1 christos node->_f.right = &_n##_rb_zero; \ 152 1.1 christos p = &head->top._f.right; \ 153 1.1 christos while ((n = *p) != &_n##_rb_zero) { \ 154 1.1 christos if (_cmp(node, n) < 0) \ 155 1.1 christos p = &n->_f.left; \ 156 1.1 christos else \ 157 1.1 christos p = &n->_f.right; \ 158 1.1 christos parent = n; \ 159 1.1 christos } \ 160 1.1 christos *p = node; \ 161 1.1 christos node->_f.colour = C_RED; \ 162 1.1 christos node->_f.parent = parent; \ 163 1.1 christos \ 164 1.1 christos while ((node != &_n##_rb_zero) && (parent->_f.colour == C_RED)){\ 165 1.1 christos gparent = parent->_f.parent; \ 166 1.1 christos if (parent == gparent->_f.left) { \ 167 1.1 christos tmp1 = gparent->_f.right; \ 168 1.1 christos if (tmp1->_f.colour == C_RED) { \ 169 1.1 christos parent->_f.colour = C_BLACK; \ 170 1.1 christos tmp1->_f.colour = C_BLACK; \ 171 1.1 christos gparent->_f.colour = C_RED; \ 172 1.1 christos node = gparent; \ 173 1.1 christos } else { \ 174 1.1 christos if (node == parent->_f.right) { \ 175 1.1 christos node = parent; \ 176 1.1 christos rotate_left(head, node); \ 177 1.1 christos parent = node->_f.parent; \ 178 1.1 christos } \ 179 1.1 christos parent->_f.colour = C_BLACK; \ 180 1.1 christos gparent->_f.colour = C_RED; \ 181 1.1 christos rotate_right(head, gparent); \ 182 1.1 christos } \ 183 1.1 christos } else { \ 184 1.1 christos tmp1 = gparent->_f.left; \ 185 1.1 christos if (tmp1->_f.colour == C_RED) { \ 186 1.1 christos parent->_f.colour = C_BLACK; \ 187 1.1 christos tmp1->_f.colour = C_BLACK; \ 188 1.1 christos gparent->_f.colour = C_RED; \ 189 1.1 christos node = gparent; \ 190 1.1 christos } else { \ 191 1.1 christos if (node == parent->_f.left) { \ 192 1.1 christos node = parent; \ 193 1.1 christos rotate_right(head, node); \ 194 1.1 christos parent = node->_f.parent; \ 195 1.1 christos } \ 196 1.1 christos parent->_f.colour = C_BLACK; \ 197 1.1 christos gparent->_f.colour = C_RED; \ 198 1.1 christos rotate_left(head, parent->_f.parent); \ 199 1.1 christos } \ 200 1.1 christos } \ 201 1.1 christos parent = node->_f.parent; \ 202 1.1 christos } \ 203 1.1 christos head->top._f.right->_f.colour = C_BLACK; \ 204 1.1 christos head->count++; \ 205 1.1 christos } \ 206 1.1 christos \ 207 1.1 christos static void \ 208 1.1 christos deleteblack(struct _n##_rb_head *head, _t *parent, _t *node) \ 209 1.1 christos { \ 210 1.1 christos _t *tmp; \ 211 1.1 christos \ 212 1.1 christos while ((node == &_n##_rb_zero || node->_f.colour == C_BLACK) && \ 213 1.1 christos node != &head->top) { \ 214 1.1 christos if (parent->_f.left == node) { \ 215 1.1 christos tmp = parent->_f.right; \ 216 1.1 christos if (tmp->_f.colour == C_RED) { \ 217 1.1 christos tmp->_f.colour = C_BLACK; \ 218 1.1 christos parent->_f.colour = C_RED; \ 219 1.1 christos rotate_left(head, parent); \ 220 1.1 christos tmp = parent->_f.right; \ 221 1.1 christos } \ 222 1.1 christos if ((tmp->_f.left == &_n##_rb_zero || \ 223 1.1 christos tmp->_f.left->_f.colour == C_BLACK) && \ 224 1.1 christos (tmp->_f.right == &_n##_rb_zero || \ 225 1.1 christos tmp->_f.right->_f.colour == C_BLACK)) { \ 226 1.1 christos tmp->_f.colour = C_RED; \ 227 1.1 christos node = parent; \ 228 1.1 christos parent = node->_f.parent; \ 229 1.1 christos } else { \ 230 1.1 christos if (tmp->_f.right == &_n##_rb_zero || \ 231 1.1 christos tmp->_f.right->_f.colour == C_BLACK) {\ 232 1.1 christos _t *tmp2 = tmp->_f.left; \ 233 1.1 christos \ 234 1.1 christos if (tmp2 != &_n##_rb_zero) \ 235 1.1 christos tmp2->_f.colour = C_BLACK;\ 236 1.1 christos tmp->_f.colour = C_RED; \ 237 1.1 christos rotate_right(head, tmp); \ 238 1.1 christos tmp = parent->_f.right; \ 239 1.1 christos } \ 240 1.1 christos tmp->_f.colour = parent->_f.colour; \ 241 1.1 christos parent->_f.colour = C_BLACK; \ 242 1.1 christos if (tmp->_f.right != &_n##_rb_zero) \ 243 1.1 christos tmp->_f.right->_f.colour = C_BLACK;\ 244 1.1 christos rotate_left(head, parent); \ 245 1.1 christos node = head->top._f.right; \ 246 1.1 christos } \ 247 1.1 christos } else { \ 248 1.1 christos tmp = parent->_f.left; \ 249 1.1 christos if (tmp->_f.colour == C_RED) { \ 250 1.1 christos tmp->_f.colour = C_BLACK; \ 251 1.1 christos parent->_f.colour = C_RED; \ 252 1.1 christos rotate_right(head, parent); \ 253 1.1 christos tmp = parent->_f.left; \ 254 1.1 christos } \ 255 1.1 christos if ((tmp->_f.left == &_n##_rb_zero || \ 256 1.1 christos tmp->_f.left->_f.colour == C_BLACK) && \ 257 1.1 christos (tmp->_f.right == &_n##_rb_zero || \ 258 1.1 christos tmp->_f.right->_f.colour == C_BLACK)) { \ 259 1.1 christos tmp->_f.colour = C_RED; \ 260 1.1 christos node = parent; \ 261 1.1 christos parent = node->_f.parent; \ 262 1.1 christos } else { \ 263 1.1 christos if (tmp->_f.left == &_n##_rb_zero || \ 264 1.1 christos tmp->_f.left->_f.colour == C_BLACK) {\ 265 1.1 christos _t *tmp2 = tmp->_f.right; \ 266 1.1 christos \ 267 1.1 christos if (tmp2 != &_n##_rb_zero) \ 268 1.1 christos tmp2->_f.colour = C_BLACK;\ 269 1.1 christos tmp->_f.colour = C_RED; \ 270 1.1 christos rotate_left(head, tmp); \ 271 1.1 christos tmp = parent->_f.left; \ 272 1.1 christos } \ 273 1.1 christos tmp->_f.colour = parent->_f.colour; \ 274 1.1 christos parent->_f.colour = C_BLACK; \ 275 1.1 christos if (tmp->_f.left != &_n##_rb_zero) \ 276 1.1 christos tmp->_f.left->_f.colour = C_BLACK;\ 277 1.1 christos rotate_right(head, parent); \ 278 1.1 christos node = head->top._f.right; \ 279 1.1 christos break; \ 280 1.1 christos } \ 281 1.1 christos } \ 282 1.1 christos } \ 283 1.1 christos if (node != &_n##_rb_zero) \ 284 1.1 christos node->_f.colour = C_BLACK; \ 285 1.1 christos } \ 286 1.1 christos \ 287 1.1 christos _t * \ 288 1.1 christos _n##_rb_delete(struct _n##_rb_head *head, _t *node) \ 289 1.1 christos { \ 290 1.1 christos _t *child, *parent, *old = node, *left; \ 291 1.1 christos rbcolour_t color; \ 292 1.1 christos \ 293 1.1 christos if (node->_f.left == &_n##_rb_zero) { \ 294 1.1 christos child = node->_f.right; \ 295 1.1 christos } else if (node->_f.right == &_n##_rb_zero) { \ 296 1.1 christos child = node->_f.left; \ 297 1.1 christos } else { \ 298 1.1 christos node = node->_f.right; \ 299 1.1 christos while ((left = node->_f.left) != &_n##_rb_zero) \ 300 1.1 christos node = left; \ 301 1.1 christos child = node->_f.right; \ 302 1.1 christos parent = node->_f.parent; \ 303 1.1 christos color = node->_f.colour; \ 304 1.1 christos if (child != &_n##_rb_zero) \ 305 1.1 christos child->_f.parent = parent; \ 306 1.1 christos if (parent != &_n##_rb_zero) { \ 307 1.1 christos if (parent->_f.left == node) \ 308 1.1 christos parent->_f.left = child; \ 309 1.1 christos else \ 310 1.1 christos parent->_f.right = child; \ 311 1.1 christos } else { \ 312 1.1 christos head->top._f.right = child; \ 313 1.1 christos } \ 314 1.1 christos if (node->_f.parent == old) \ 315 1.1 christos parent = node; \ 316 1.1 christos *node = *old; \ 317 1.1 christos if (old->_f.parent != &_n##_rb_zero) { \ 318 1.1 christos if (old->_f.parent->_f.left == old) \ 319 1.1 christos old->_f.parent->_f.left = node; \ 320 1.1 christos else \ 321 1.1 christos old->_f.parent->_f.right = node; \ 322 1.1 christos } else { \ 323 1.1 christos head->top._f.right = child; \ 324 1.1 christos } \ 325 1.1 christos old->_f.left->_f.parent = node; \ 326 1.1 christos if (old->_f.right != &_n##_rb_zero) \ 327 1.1 christos old->_f.right->_f.parent = node; \ 328 1.1 christos if (parent != &_n##_rb_zero) { \ 329 1.1 christos left = parent; \ 330 1.1 christos } \ 331 1.1 christos goto colour; \ 332 1.1 christos } \ 333 1.1 christos parent = node->_f.parent; \ 334 1.1 christos color= node->_f.colour; \ 335 1.1 christos if (child != &_n##_rb_zero) \ 336 1.1 christos child->_f.parent = parent; \ 337 1.1 christos if (parent != &_n##_rb_zero) { \ 338 1.1 christos if (parent->_f.left == node) \ 339 1.1 christos parent->_f.left = child; \ 340 1.1 christos else \ 341 1.1 christos parent->_f.right = child; \ 342 1.1 christos } else { \ 343 1.1 christos head->top._f.right = child; \ 344 1.1 christos } \ 345 1.1 christos colour: \ 346 1.1 christos if (color == C_BLACK) \ 347 1.1 christos deleteblack(head, parent, node); \ 348 1.1 christos head->count--; \ 349 1.1 christos return old; \ 350 1.1 christos } \ 351 1.1 christos \ 352 1.1 christos void \ 353 1.1 christos _n##_rb_init(struct _n##_rb_head *head) \ 354 1.1 christos { \ 355 1.1 christos memset(&_n##_rb_zero, 0, sizeof(_n##_rb_zero)); \ 356 1.1 christos head->top._f.left = &_n##_rb_zero; \ 357 1.1 christos head->top._f.right = &_n##_rb_zero; \ 358 1.1 christos head->top._f.parent = &head->top; \ 359 1.1 christos _n##_rb_zero._f.left = &_n##_rb_zero; \ 360 1.1 christos _n##_rb_zero._f.right = &_n##_rb_zero; \ 361 1.1 christos _n##_rb_zero._f.parent = &_n##_rb_zero; \ 362 1.1 christos } \ 363 1.1 christos \ 364 1.1 christos void \ 365 1.1 christos _n##_rb_walktree(struct _n##_rb_head *head, _n##_rb_walker_t func, void *arg)\ 366 1.1 christos { \ 367 1.1 christos _t *prev; \ 368 1.1 christos _t *next; \ 369 1.1 christos _t *node = head->top._f.right; \ 370 1.1 christos _t *base; \ 371 1.1 christos \ 372 1.1 christos while (node != &_n##_rb_zero) \ 373 1.1 christos node = node->_f.left; \ 374 1.1 christos \ 375 1.1 christos for (;;) { \ 376 1.1 christos base = node; \ 377 1.1 christos prev = node; \ 378 1.1 christos while ((node->_f.parent->_f.right == node) && \ 379 1.1 christos (node != &_n##_rb_zero)) { \ 380 1.1 christos prev = node; \ 381 1.1 christos node = node->_f.parent; \ 382 1.1 christos } \ 383 1.1 christos \ 384 1.1 christos node = prev; \ 385 1.1 christos for (node = node->_f.parent->_f.right; node != &_n##_rb_zero;\ 386 1.1 christos node = node->_f.left) \ 387 1.1 christos prev = node; \ 388 1.1 christos next = prev; \ 389 1.1 christos \ 390 1.1 christos if (node != &_n##_rb_zero) \ 391 1.1 christos func(node, arg); \ 392 1.1 christos \ 393 1.1 christos node = next; \ 394 1.1 christos if (node == &_n##_rb_zero) \ 395 1.1 christos break; \ 396 1.1 christos } \ 397 1.1 christos } \ 398 1.1 christos \ 399 1.1 christos _t * \ 400 1.1 christos _n##_rb_search(struct _n##_rb_head *head, void *key) \ 401 1.1 christos { \ 402 1.2 christos int match = 0; \ 403 1.1 christos _t *node; \ 404 1.1 christos node = head->top._f.right; \ 405 1.1 christos while (node != &_n##_rb_zero) { \ 406 1.1 christos match = _cmp(key, node); \ 407 1.1 christos if (match == 0) \ 408 1.1 christos break; \ 409 1.1 christos if (match< 0) \ 410 1.1 christos node = node->_f.left; \ 411 1.1 christos else \ 412 1.1 christos node = node->_f.right; \ 413 1.1 christos } \ 414 1.1 christos if (node == &_n##_rb_zero || match != 0) \ 415 1.1 christos return (NULL); \ 416 1.1 christos return (node); \ 417 1.1 christos } 418 1.1 christos 419 1.1 christos #define RBI_DELETE(_n, _h, _v) _n##_rb_delete(_h, _v) 420 1.1 christos #define RBI_INIT(_n, _h) _n##_rb_init(_h) 421 1.1 christos #define RBI_INSERT(_n, _h, _v) _n##_rb_insert(_h, _v) 422 1.1 christos #define RBI_ISEMPTY(_h) ((_h)->count == 0) 423 1.1 christos #define RBI_SEARCH(_n, _h, _k) _n##_rb_search(_h, _k) 424 1.1 christos #define RBI_WALK(_n, _h, _w, _a) _n##_rb_walktree(_h, _w, _a) 425 1.1 christos #define RBI_ZERO(_n) _n##_rb_zero 426 1.4 christos 427 1.4 christos #endif 428