├── .gitignore ├── Makefile ├── avl.c ├── avl.h ├── avl_impl.h ├── polar2afl.c └── safe_alloc.h /.gitignore: -------------------------------------------------------------------------------- 1 | polar2afl 2 | polar2afl.exe 3 | *.o 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # CDDL HEADER START 2 | # 3 | # This file and its contents are supplied under the terms of the 4 | # Common Development and Distribution License ("CDDL"), version 1.0. 5 | # You may only use this file in accordance with the terms of version 6 | # 1.0 of the CDDL. 7 | # 8 | # A full copy of the text of the CDDL should have accompanied this 9 | # source. A copy of the CDDL is also available via the Internet at 10 | # http://www.illumos.org/license/CDDL. 11 | # 12 | # CDDL HEADER END 13 | # 14 | # Copyright 2019 Saso Kiselkov. All rights reserved. 15 | 16 | OBJS = polar2afl.o avl.o 17 | OUTBIN=polar2afl 18 | 19 | CFLAGS=-W -Wall -Werror -g -O2 20 | LIBS=-lm 21 | 22 | ifeq ($(findstring mingw,$(CC)),mingw) 23 | OUTBIN := $(OUTBIN).exe 24 | endif 25 | 26 | all : $(OUTBIN) 27 | 28 | clean : 29 | rm -f $(OUTBIN) $(OBJS) 30 | 31 | $(OUTBIN) : $(OBJS) 32 | $(CC) -W -Wall -Werror -o $@ $^ $(LIBS) 33 | 34 | %.o : %.c 35 | $(CC) $(CFLAGS) -c -o $@ $^ 36 | -------------------------------------------------------------------------------- /avl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * CDDL HEADER START 3 | * 4 | * The contents of this file are subject to the terms of the 5 | * Common Development and Distribution License (the "License"). 6 | * You may not use this file except in compliance with the License. 7 | * 8 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 | * or http://www.opensolaris.org/os/licensing. 10 | * See the License for the specific language governing permissions 11 | * and limitations under the License. 12 | * 13 | * When distributing Covered Code, include this CDDL HEADER in each 14 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 | * If applicable, add the following below this CDDL HEADER, with the 16 | * fields enclosed by brackets "[]" replaced with your own identifying 17 | * information: Portions Copyright [yyyy] [name of copyright owner] 18 | * 19 | * CDDL HEADER END 20 | */ 21 | /* 22 | * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 | * Use is subject to license terms. 24 | */ 25 | 26 | /* 27 | * AVL - generic AVL tree implementation for kernel use 28 | * 29 | * A complete description of AVL trees can be found in many CS textbooks. 30 | * 31 | * Here is a very brief overview. An AVL tree is a binary search tree that is 32 | * almost perfectly balanced. By "almost" perfectly balanced, we mean that at 33 | * any given node, the left and right subtrees are allowed to differ in height 34 | * by at most 1 level. 35 | * 36 | * This relaxation from a perfectly balanced binary tree allows doing 37 | * insertion and deletion relatively efficiently. Searching the tree is 38 | * still a fast operation, roughly O(log(N)). 39 | * 40 | * The key to insertion and deletion is a set of tree maniuplations called 41 | * rotations, which bring unbalanced subtrees back into the semi-balanced state. 42 | * 43 | * This implementation of AVL trees has the following peculiarities: 44 | * 45 | * - The AVL specific data structures are physically embedded as fields 46 | * in the "using" data structures. To maintain generality the code 47 | * must constantly translate between "avl_node_t *" and containing 48 | * data structure "void *"s by adding/subracting the avl_offset. 49 | * 50 | * - Since the AVL data is always embedded in other structures, there is 51 | * no locking or memory allocation in the AVL routines. This must be 52 | * provided for by the enclosing data structure's semantics. Typically, 53 | * avl_insert()/_add()/_remove()/avl_insert_here() require some kind of 54 | * exclusive write lock. Other operations require a read lock. 55 | * 56 | * - The implementation uses iteration instead of explicit recursion, 57 | * since it is intended to run on limited size kernel stacks. Since 58 | * there is no recursion stack present to move "up" in the tree, 59 | * there is an explicit "parent" link in the avl_node_t. 60 | * 61 | * - The left/right children pointers of a node are in an array. 62 | * In the code, variables (instead of constants) are used to represent 63 | * left and right indices. The implementation is written as if it only 64 | * dealt with left handed manipulations. By changing the value assigned 65 | * to "left", the code also works for right handed trees. The 66 | * following variables/terms are frequently used: 67 | * 68 | * int left; // 0 when dealing with left children, 69 | * // 1 for dealing with right children 70 | * 71 | * int left_heavy; // -1 when left subtree is taller at some node, 72 | * // +1 when right subtree is taller 73 | * 74 | * int right; // will be the opposite of left (0 or 1) 75 | * int right_heavy;// will be the opposite of left_heavy (-1 or 1) 76 | * 77 | * int direction; // 0 for "<" (ie. left child); 1 for ">" (right) 78 | * 79 | * Though it is a little more confusing to read the code, the approach 80 | * allows using half as much code (and hence cache footprint) for tree 81 | * manipulations and eliminates many conditional branches. 82 | * 83 | * - The avl_index_t is an opaque "cookie" used to find nodes at or 84 | * adjacent to where a new value would be inserted in the tree. The value 85 | * is a modified "avl_node_t *". The bottom bit (normally 0 for a 86 | * pointer) is set to indicate if that the new node has a value greater 87 | * than the value of the indicated "avl_node_t *". 88 | */ 89 | 90 | #include 91 | #include 92 | #include 93 | 94 | #include "avl.h" 95 | 96 | #ifndef UNUSED 97 | #define UNUSED(x) (void)(x) 98 | #endif 99 | 100 | /* 101 | * Small arrays to translate between balance (or diff) values and child indeces. 102 | * 103 | * Code that deals with binary tree data structures will randomly use 104 | * left and right children when examining a tree. C "if()" statements 105 | * which evaluate randomly suffer from very poor hardware branch prediction. 106 | * In this code we avoid some of the branch mispredictions by using the 107 | * following translation arrays. They replace random branches with an 108 | * additional memory reference. Since the translation arrays are both very 109 | * small the data should remain efficiently in cache. 110 | */ 111 | static const int avl_child2balance[2] = {-1, 1}; 112 | static const int avl_balance2child[] = {0, 0, 1}; 113 | 114 | 115 | /* 116 | * Walk from one node to the previous valued node (ie. an infix walk 117 | * towards the left). At any given node we do one of 2 things: 118 | * 119 | * - If there is a left child, go to it, then to it's rightmost descendant. 120 | * 121 | * - otherwise we return thru parent nodes until we've come from a right child. 122 | * 123 | * Return Value: 124 | * NULL - if at the end of the nodes 125 | * otherwise next node 126 | */ 127 | void * 128 | avl_walk(const avl_tree_t *tree, const void *oldnode, int left) 129 | { 130 | size_t off = tree->avl_offset; 131 | const avl_node_t *node = AVL_DATA2NODE(oldnode, off); 132 | int right = 1 - left; 133 | int was_child; 134 | 135 | 136 | /* 137 | * nowhere to walk to if tree is empty 138 | */ 139 | if (node == NULL) 140 | return (NULL); 141 | 142 | /* 143 | * Visit the previous valued node. There are two possibilities: 144 | * 145 | * If this node has a left child, go down one left, then all 146 | * the way right. 147 | */ 148 | if (node->avl_child[left] != NULL) { 149 | for (node = node->avl_child[left]; 150 | node->avl_child[right] != NULL; 151 | node = node->avl_child[right]) 152 | ; 153 | /* 154 | * Otherwise, return thru left children as far as we can. 155 | */ 156 | } else { 157 | for (;;) { 158 | was_child = AVL_XCHILD(node); 159 | node = AVL_XPARENT(node); 160 | if (node == NULL) 161 | return (NULL); 162 | if (was_child == right) 163 | break; 164 | } 165 | } 166 | 167 | return (AVL_NODE2DATA(node, off)); 168 | } 169 | 170 | /* 171 | * Return the lowest valued node in a tree or NULL. 172 | * (leftmost child from root of tree) 173 | */ 174 | void * 175 | avl_first(const avl_tree_t *tree) 176 | { 177 | const avl_node_t *node; 178 | const avl_node_t *prev = NULL; 179 | size_t off = tree->avl_offset; 180 | 181 | for (node = tree->avl_root; node != NULL; node = node->avl_child[0]) 182 | prev = node; 183 | 184 | if (prev != NULL) 185 | return (AVL_NODE2DATA(prev, off)); 186 | return (NULL); 187 | } 188 | 189 | /* 190 | * Return the highest valued node in a tree or NULL. 191 | * (rightmost child from root of tree) 192 | */ 193 | void * 194 | avl_last(const avl_tree_t *tree) 195 | { 196 | const avl_node_t *node; 197 | const avl_node_t *prev = NULL; 198 | size_t off = tree->avl_offset; 199 | 200 | for (node = tree->avl_root; node != NULL; node = node->avl_child[1]) 201 | prev = node; 202 | 203 | if (prev != NULL) 204 | return (AVL_NODE2DATA(prev, off)); 205 | return (NULL); 206 | } 207 | 208 | /* 209 | * Access the node immediately before or after an insertion point. 210 | * 211 | * "avl_index_t" is a (avl_node_t *) with the bottom bit indicating a child 212 | * 213 | * Return value: 214 | * NULL: no node in the given direction 215 | * "void *" of the found tree node 216 | */ 217 | void * 218 | avl_nearest(const avl_tree_t *tree, avl_index_t where, int direction) 219 | { 220 | int child = AVL_INDEX2CHILD(where); 221 | avl_node_t *node = AVL_INDEX2NODE(where); 222 | void *data; 223 | size_t off = tree->avl_offset; 224 | 225 | if (node == NULL) { 226 | assert(tree->avl_root == NULL); 227 | return (NULL); 228 | } 229 | data = AVL_NODE2DATA(node, off); 230 | if (child != direction) 231 | return (data); 232 | 233 | return (avl_walk(tree, data, direction)); 234 | } 235 | 236 | 237 | /* 238 | * Search for the node which contains "value". The algorithm is a 239 | * simple binary tree search. 240 | * 241 | * return value: 242 | * NULL: the value is not in the AVL tree 243 | * *where (if not NULL) is set to indicate the insertion point 244 | * "void *" of the found tree node 245 | */ 246 | void * 247 | avl_find(const avl_tree_t *tree, const void *value, avl_index_t *where) 248 | { 249 | avl_node_t *node; 250 | avl_node_t *prev = NULL; 251 | int child = 0; 252 | int diff; 253 | size_t off = tree->avl_offset; 254 | 255 | for (node = tree->avl_root; node != NULL; 256 | node = node->avl_child[child]) { 257 | 258 | prev = node; 259 | 260 | diff = tree->avl_compar(value, AVL_NODE2DATA(node, off)); 261 | assert(-1 <= diff && diff <= 1); 262 | if (diff == 0) { 263 | #ifdef DEBUG 264 | if (where != NULL) 265 | *where = 0; 266 | #endif 267 | return (AVL_NODE2DATA(node, off)); 268 | } 269 | child = avl_balance2child[1 + diff]; 270 | 271 | } 272 | 273 | if (where != NULL) 274 | *where = AVL_MKINDEX(prev, child); 275 | 276 | return (NULL); 277 | } 278 | 279 | 280 | /* 281 | * Perform a rotation to restore balance at the subtree given by depth. 282 | * 283 | * This routine is used by both insertion and deletion. The return value 284 | * indicates: 285 | * 0 : subtree did not change height 286 | * !0 : subtree was reduced in height 287 | * 288 | * The code is written as if handling left rotations, right rotations are 289 | * symmetric and handled by swapping values of variables right/left[_heavy] 290 | * 291 | * On input balance is the "new" balance at "node". This value is either 292 | * -2 or +2. 293 | */ 294 | static int 295 | avl_rotation(avl_tree_t *tree, avl_node_t *node, int balance) 296 | { 297 | int left = !(balance < 0); /* when balance = -2, left will be 0 */ 298 | int right = 1 - left; 299 | int left_heavy = balance >> 1; 300 | int right_heavy = -left_heavy; 301 | avl_node_t *parent = AVL_XPARENT(node); 302 | avl_node_t *child = node->avl_child[left]; 303 | avl_node_t *cright; 304 | avl_node_t *gchild; 305 | avl_node_t *gright; 306 | avl_node_t *gleft; 307 | int which_child = AVL_XCHILD(node); 308 | int child_bal = AVL_XBALANCE(child); 309 | 310 | /* BEGIN CSTYLED */ 311 | /* 312 | * case 1 : node is overly left heavy, the left child is balanced or 313 | * also left heavy. This requires the following rotation. 314 | * 315 | * (node bal:-2) 316 | * / \ 317 | * / \ 318 | * (child bal:0 or -1) 319 | * / \ 320 | * / \ 321 | * cright 322 | * 323 | * becomes: 324 | * 325 | * (child bal:1 or 0) 326 | * / \ 327 | * / \ 328 | * (node bal:-1 or 0) 329 | * / \ 330 | * / \ 331 | * cright 332 | * 333 | * we detect this situation by noting that child's balance is not 334 | * right_heavy. 335 | */ 336 | /* END CSTYLED */ 337 | if (child_bal != right_heavy) { 338 | 339 | /* 340 | * compute new balance of nodes 341 | * 342 | * If child used to be left heavy (now balanced) we reduced 343 | * the height of this sub-tree -- used in "return...;" below 344 | */ 345 | child_bal += right_heavy; /* adjust towards right */ 346 | 347 | /* 348 | * move "cright" to be node's left child 349 | */ 350 | cright = child->avl_child[right]; 351 | node->avl_child[left] = cright; 352 | if (cright != NULL) { 353 | AVL_SETPARENT(cright, node); 354 | AVL_SETCHILD(cright, left); 355 | } 356 | 357 | /* 358 | * move node to be child's right child 359 | */ 360 | child->avl_child[right] = node; 361 | AVL_SETBALANCE(node, -child_bal); 362 | AVL_SETCHILD(node, right); 363 | AVL_SETPARENT(node, child); 364 | 365 | /* 366 | * update the pointer into this subtree 367 | */ 368 | AVL_SETBALANCE(child, child_bal); 369 | AVL_SETCHILD(child, which_child); 370 | AVL_SETPARENT(child, parent); 371 | if (parent != NULL) 372 | parent->avl_child[which_child] = child; 373 | else 374 | tree->avl_root = child; 375 | 376 | return (child_bal == 0); 377 | } 378 | 379 | /* BEGIN CSTYLED */ 380 | /* 381 | * case 2 : When node is left heavy, but child is right heavy we use 382 | * a different rotation. 383 | * 384 | * (node b:-2) 385 | * / \ 386 | * / \ 387 | * / \ 388 | * (child b:+1) 389 | * / \ 390 | * / \ 391 | * (gchild b: != 0) 392 | * / \ 393 | * / \ 394 | * gleft gright 395 | * 396 | * becomes: 397 | * 398 | * (gchild b:0) 399 | * / \ 400 | * / \ 401 | * / \ 402 | * (child b:?) (node b:?) 403 | * / \ / \ 404 | * / \ / \ 405 | * gleft gright 406 | * 407 | * computing the new balances is more complicated. As an example: 408 | * if gchild was right_heavy, then child is now left heavy 409 | * else it is balanced 410 | */ 411 | /* END CSTYLED */ 412 | gchild = child->avl_child[right]; 413 | gleft = gchild->avl_child[left]; 414 | gright = gchild->avl_child[right]; 415 | 416 | /* 417 | * move gright to left child of node and 418 | * 419 | * move gleft to right child of node 420 | */ 421 | node->avl_child[left] = gright; 422 | if (gright != NULL) { 423 | AVL_SETPARENT(gright, node); 424 | AVL_SETCHILD(gright, left); 425 | } 426 | 427 | child->avl_child[right] = gleft; 428 | if (gleft != NULL) { 429 | AVL_SETPARENT(gleft, child); 430 | AVL_SETCHILD(gleft, right); 431 | } 432 | 433 | /* 434 | * move child to left child of gchild and 435 | * 436 | * move node to right child of gchild and 437 | * 438 | * fixup parent of all this to point to gchild 439 | */ 440 | balance = AVL_XBALANCE(gchild); 441 | gchild->avl_child[left] = child; 442 | AVL_SETBALANCE(child, (balance == right_heavy ? left_heavy : 0)); 443 | AVL_SETPARENT(child, gchild); 444 | AVL_SETCHILD(child, left); 445 | 446 | gchild->avl_child[right] = node; 447 | AVL_SETBALANCE(node, (balance == left_heavy ? right_heavy : 0)); 448 | AVL_SETPARENT(node, gchild); 449 | AVL_SETCHILD(node, right); 450 | 451 | AVL_SETBALANCE(gchild, 0); 452 | AVL_SETPARENT(gchild, parent); 453 | AVL_SETCHILD(gchild, which_child); 454 | if (parent != NULL) 455 | parent->avl_child[which_child] = gchild; 456 | else 457 | tree->avl_root = gchild; 458 | 459 | return (1); /* the new tree is always shorter */ 460 | } 461 | 462 | 463 | /* 464 | * Insert a new node into an AVL tree at the specified (from avl_find()) place. 465 | * 466 | * Newly inserted nodes are always leaf nodes in the tree, since avl_find() 467 | * searches out to the leaf positions. The avl_index_t indicates the node 468 | * which will be the parent of the new node. 469 | * 470 | * After the node is inserted, a single rotation further up the tree may 471 | * be necessary to maintain an acceptable AVL balance. 472 | */ 473 | void 474 | avl_insert(avl_tree_t *tree, void *new_data, avl_index_t where) 475 | { 476 | avl_node_t *node; 477 | avl_node_t *parent = AVL_INDEX2NODE(where); 478 | int old_balance; 479 | int new_balance; 480 | int which_child = AVL_INDEX2CHILD(where); 481 | size_t off = tree->avl_offset; 482 | 483 | assert(tree); 484 | #ifdef _LP64 485 | assert(((uintptr_t)new_data & 0x7) == 0); 486 | #endif 487 | 488 | node = AVL_DATA2NODE(new_data, off); 489 | 490 | /* 491 | * First, add the node to the tree at the indicated position. 492 | */ 493 | ++tree->avl_numnodes; 494 | 495 | node->avl_child[0] = NULL; 496 | node->avl_child[1] = NULL; 497 | 498 | AVL_SETCHILD(node, which_child); 499 | AVL_SETBALANCE(node, 0); 500 | AVL_SETPARENT(node, parent); 501 | if (parent != NULL) { 502 | assert(parent->avl_child[which_child] == NULL); 503 | parent->avl_child[which_child] = node; 504 | } else { 505 | assert(tree->avl_root == NULL); 506 | tree->avl_root = node; 507 | } 508 | /* 509 | * Now, back up the tree modifying the balance of all nodes above the 510 | * insertion point. If we get to a highly unbalanced ancestor, we 511 | * need to do a rotation. If we back out of the tree we are done. 512 | * If we brought any subtree into perfect balance (0), we are also done. 513 | */ 514 | for (;;) { 515 | node = parent; 516 | if (node == NULL) 517 | return; 518 | 519 | /* 520 | * Compute the new balance 521 | */ 522 | old_balance = AVL_XBALANCE(node); 523 | new_balance = old_balance + avl_child2balance[which_child]; 524 | 525 | /* 526 | * If we introduced equal balance, then we are done immediately 527 | */ 528 | if (new_balance == 0) { 529 | AVL_SETBALANCE(node, 0); 530 | return; 531 | } 532 | 533 | /* 534 | * If both old and new are not zero we went 535 | * from -1 to -2 balance, do a rotation. 536 | */ 537 | if (old_balance != 0) 538 | break; 539 | 540 | AVL_SETBALANCE(node, new_balance); 541 | parent = AVL_XPARENT(node); 542 | which_child = AVL_XCHILD(node); 543 | } 544 | 545 | /* 546 | * perform a rotation to fix the tree and return 547 | */ 548 | (void) avl_rotation(tree, node, new_balance); 549 | } 550 | 551 | /* 552 | * Insert "new_data" in "tree" in the given "direction" either after or 553 | * before (AVL_AFTER, AVL_BEFORE) the data "here". 554 | * 555 | * Insertions can only be done at empty leaf points in the tree, therefore 556 | * if the given child of the node is already present we move to either 557 | * the AVL_PREV or AVL_NEXT and reverse the insertion direction. Since 558 | * every other node in the tree is a leaf, this always works. 559 | * 560 | * To help developers using this interface, we assert that the new node 561 | * is correctly ordered at every step of the way in DEBUG kernels. 562 | */ 563 | void 564 | avl_insert_here( 565 | avl_tree_t *tree, 566 | void *new_data, 567 | void *here, 568 | int direction) 569 | { 570 | avl_node_t *node; 571 | int child = direction; /* rely on AVL_BEFORE == 0, AVL_AFTER == 1 */ 572 | #ifdef DEBUG 573 | int diff; 574 | #endif 575 | 576 | assert(tree != NULL); 577 | assert(new_data != NULL); 578 | assert(here != NULL); 579 | assert(direction == AVL_BEFORE || direction == AVL_AFTER); 580 | 581 | /* 582 | * If corresponding child of node is not NULL, go to the neighboring 583 | * node and reverse the insertion direction. 584 | */ 585 | node = AVL_DATA2NODE(here, tree->avl_offset); 586 | 587 | #ifdef DEBUG 588 | diff = tree->avl_compar(new_data, here); 589 | assert(-1 <= diff && diff <= 1); 590 | assert(diff != 0); 591 | assert(diff > 0 ? child == 1 : child == 0); 592 | #endif 593 | 594 | if (node->avl_child[child] != NULL) { 595 | node = node->avl_child[child]; 596 | child = 1 - child; 597 | while (node->avl_child[child] != NULL) { 598 | #ifdef DEBUG 599 | diff = tree->avl_compar(new_data, 600 | AVL_NODE2DATA(node, tree->avl_offset)); 601 | assert(-1 <= diff && diff <= 1); 602 | assert(diff != 0); 603 | assert(diff > 0 ? child == 1 : child == 0); 604 | #endif 605 | node = node->avl_child[child]; 606 | } 607 | #ifdef DEBUG 608 | diff = tree->avl_compar(new_data, 609 | AVL_NODE2DATA(node, tree->avl_offset)); 610 | assert(-1 <= diff && diff <= 1); 611 | assert(diff != 0); 612 | assert(diff > 0 ? child == 1 : child == 0); 613 | #endif 614 | } 615 | assert(node->avl_child[child] == NULL); 616 | 617 | avl_insert(tree, new_data, AVL_MKINDEX(node, child)); 618 | } 619 | 620 | /* 621 | * Add a new node to an AVL tree. 622 | */ 623 | void 624 | avl_add(avl_tree_t *tree, void *new_node) 625 | { 626 | avl_index_t where; 627 | 628 | /* 629 | * This is unfortunate. We want to call panic() here, even for 630 | * non-DEBUG kernels. In userland, however, we can't depend on anything 631 | * in libc or else the rtld build process gets confused. So, all we can 632 | * do in userland is resort to a normal assert(). 633 | */ 634 | memset(&where, 0, sizeof (where)); 635 | if (avl_find(tree, new_node, &where) != NULL) 636 | #ifdef _KERNEL 637 | panic("avl_find() succeeded inside avl_add()"); 638 | #else 639 | abort(); 640 | #endif 641 | avl_insert(tree, new_node, where); 642 | } 643 | 644 | /* 645 | * Delete a node from the AVL tree. Deletion is similar to insertion, but 646 | * with 2 complications. 647 | * 648 | * First, we may be deleting an interior node. Consider the following subtree: 649 | * 650 | * d c c 651 | * / \ / \ / \ 652 | * b e b e b e 653 | * / \ / \ / 654 | * a c a a 655 | * 656 | * When we are deleting node (d), we find and bring up an adjacent valued leaf 657 | * node, say (c), to take the interior node's place. In the code this is 658 | * handled by temporarily swapping (d) and (c) in the tree and then using 659 | * common code to delete (d) from the leaf position. 660 | * 661 | * Secondly, an interior deletion from a deep tree may require more than one 662 | * rotation to fix the balance. This is handled by moving up the tree through 663 | * parents and applying rotations as needed. The return value from 664 | * avl_rotation() is used to detect when a subtree did not change overall 665 | * height due to a rotation. 666 | */ 667 | void 668 | avl_remove(avl_tree_t *tree, void *data) 669 | { 670 | avl_node_t *delete; 671 | avl_node_t *parent; 672 | avl_node_t *node; 673 | avl_node_t tmp; 674 | int old_balance; 675 | int new_balance; 676 | int left; 677 | int right; 678 | int which_child; 679 | size_t off = tree->avl_offset; 680 | 681 | assert(tree); 682 | 683 | delete = AVL_DATA2NODE(data, off); 684 | 685 | /* 686 | * Deletion is easiest with a node that has at most 1 child. 687 | * We swap a node with 2 children with a sequentially valued 688 | * neighbor node. That node will have at most 1 child. Note this 689 | * has no effect on the ordering of the remaining nodes. 690 | * 691 | * As an optimization, we choose the greater neighbor if the tree 692 | * is right heavy, otherwise the left neighbor. This reduces the 693 | * number of rotations needed. 694 | */ 695 | if (delete->avl_child[0] != NULL && delete->avl_child[1] != NULL) { 696 | 697 | /* 698 | * choose node to swap from whichever side is taller 699 | */ 700 | old_balance = AVL_XBALANCE(delete); 701 | left = avl_balance2child[old_balance + 1]; 702 | right = 1 - left; 703 | 704 | /* 705 | * get to the previous value'd node 706 | * (down 1 left, as far as possible right) 707 | */ 708 | for (node = delete->avl_child[left]; 709 | node->avl_child[right] != NULL; 710 | node = node->avl_child[right]) 711 | ; 712 | 713 | /* 714 | * create a temp placeholder for 'node' 715 | * move 'node' to delete's spot in the tree 716 | */ 717 | tmp = *node; 718 | 719 | *node = *delete; 720 | if (node->avl_child[left] == node) 721 | node->avl_child[left] = &tmp; 722 | 723 | parent = AVL_XPARENT(node); 724 | if (parent != NULL) 725 | parent->avl_child[AVL_XCHILD(node)] = node; 726 | else 727 | tree->avl_root = node; 728 | AVL_SETPARENT(node->avl_child[left], node); 729 | AVL_SETPARENT(node->avl_child[right], node); 730 | 731 | /* 732 | * Put tmp where node used to be (just temporary). 733 | * It always has a parent and at most 1 child. 734 | */ 735 | delete = &tmp; 736 | parent = AVL_XPARENT(delete); 737 | parent->avl_child[AVL_XCHILD(delete)] = delete; 738 | which_child = (delete->avl_child[1] != 0); 739 | if (delete->avl_child[which_child] != NULL) 740 | AVL_SETPARENT(delete->avl_child[which_child], delete); 741 | } 742 | 743 | 744 | /* 745 | * Here we know "delete" is at least partially a leaf node. It can 746 | * be easily removed from the tree. 747 | */ 748 | assert(tree->avl_numnodes > 0); 749 | --tree->avl_numnodes; 750 | parent = AVL_XPARENT(delete); 751 | which_child = AVL_XCHILD(delete); 752 | if (delete->avl_child[0] != NULL) 753 | node = delete->avl_child[0]; 754 | else 755 | node = delete->avl_child[1]; 756 | 757 | /* 758 | * Connect parent directly to node (leaving out delete). 759 | */ 760 | if (node != NULL) { 761 | AVL_SETPARENT(node, parent); 762 | AVL_SETCHILD(node, which_child); 763 | } 764 | if (parent == NULL) { 765 | tree->avl_root = node; 766 | return; 767 | } 768 | parent->avl_child[which_child] = node; 769 | 770 | 771 | /* 772 | * Since the subtree is now shorter, begin adjusting parent balances 773 | * and performing any needed rotations. 774 | */ 775 | do { 776 | 777 | /* 778 | * Move up the tree and adjust the balance 779 | * 780 | * Capture the parent and which_child values for the next 781 | * iteration before any rotations occur. 782 | */ 783 | node = parent; 784 | old_balance = AVL_XBALANCE(node); 785 | new_balance = old_balance - avl_child2balance[which_child]; 786 | parent = AVL_XPARENT(node); 787 | which_child = AVL_XCHILD(node); 788 | 789 | /* 790 | * If a node was in perfect balance but isn't anymore then 791 | * we can stop, since the height didn't change above this point 792 | * due to a deletion. 793 | */ 794 | if (old_balance == 0) { 795 | AVL_SETBALANCE(node, new_balance); 796 | break; 797 | } 798 | 799 | /* 800 | * If the new balance is zero, we don't need to rotate 801 | * else 802 | * need a rotation to fix the balance. 803 | * If the rotation doesn't change the height 804 | * of the sub-tree we have finished adjusting. 805 | */ 806 | if (new_balance == 0) 807 | AVL_SETBALANCE(node, new_balance); 808 | else if (!avl_rotation(tree, node, new_balance)) 809 | break; 810 | } while (parent != NULL); 811 | } 812 | 813 | #define AVL_REINSERT(tree, obj) \ 814 | avl_remove((tree), (obj)); \ 815 | avl_add((tree), (obj)) 816 | 817 | bool 818 | avl_update_lt(avl_tree_t *t, void *obj) 819 | { 820 | void *neighbor; 821 | 822 | assert(((neighbor = AVL_NEXT(t, obj)) == NULL) || 823 | (t->avl_compar(obj, neighbor) <= 0)); 824 | 825 | neighbor = AVL_PREV(t, obj); 826 | if ((neighbor != NULL) && (t->avl_compar(obj, neighbor) < 0)) { 827 | AVL_REINSERT(t, obj); 828 | return (true); 829 | } 830 | 831 | return (false); 832 | } 833 | 834 | bool 835 | avl_update_gt(avl_tree_t *t, void *obj) 836 | { 837 | void *neighbor; 838 | 839 | assert(((neighbor = AVL_PREV(t, obj)) == NULL) || 840 | (t->avl_compar(obj, neighbor) >= 0)); 841 | 842 | neighbor = AVL_NEXT(t, obj); 843 | if ((neighbor != NULL) && (t->avl_compar(obj, neighbor) > 0)) { 844 | AVL_REINSERT(t, obj); 845 | return (true); 846 | } 847 | 848 | return (false); 849 | } 850 | 851 | bool 852 | avl_update(avl_tree_t *t, void *obj) 853 | { 854 | void *neighbor; 855 | 856 | neighbor = AVL_PREV(t, obj); 857 | if ((neighbor != NULL) && (t->avl_compar(obj, neighbor) < 0)) { 858 | AVL_REINSERT(t, obj); 859 | return (true); 860 | } 861 | 862 | neighbor = AVL_NEXT(t, obj); 863 | if ((neighbor != NULL) && (t->avl_compar(obj, neighbor) > 0)) { 864 | AVL_REINSERT(t, obj); 865 | return (true); 866 | } 867 | 868 | return (false); 869 | } 870 | 871 | /* 872 | * initialize a new AVL tree 873 | */ 874 | void 875 | avl_create(avl_tree_t *tree, int (*compar) (const void *, const void *), 876 | size_t size, size_t offset) 877 | { 878 | assert(tree); 879 | assert(compar); 880 | assert(size > 0); 881 | assert(size >= offset + sizeof (avl_node_t)); 882 | #ifdef _LP64 883 | assert((offset & 0x7) == 0); 884 | #endif 885 | 886 | tree->avl_compar = compar; 887 | tree->avl_root = NULL; 888 | tree->avl_numnodes = 0; 889 | tree->avl_size = size; 890 | tree->avl_offset = offset; 891 | } 892 | 893 | /* 894 | * Delete a tree. 895 | */ 896 | /* ARGSUSED */ 897 | void 898 | avl_destroy(avl_tree_t *tree) 899 | { 900 | UNUSED(tree); 901 | assert(tree); 902 | assert(tree->avl_numnodes == 0); 903 | assert(tree->avl_root == NULL); 904 | } 905 | 906 | 907 | /* 908 | * Return the number of nodes in an AVL tree. 909 | */ 910 | unsigned long 911 | avl_numnodes(const avl_tree_t *tree) 912 | { 913 | assert(tree); 914 | return (tree->avl_numnodes); 915 | } 916 | 917 | bool 918 | avl_is_empty(avl_tree_t *tree) 919 | { 920 | assert(tree); 921 | return (tree->avl_numnodes == 0); 922 | } 923 | 924 | #define CHILDBIT (1L) 925 | 926 | /* 927 | * Post-order tree walk used to visit all tree nodes and destroy the tree 928 | * in post order. This is used for destroying a tree w/o paying any cost 929 | * for rebalancing it. 930 | * 931 | * example: 932 | * 933 | * void *cookie = NULL; 934 | * my_data_t *node; 935 | * 936 | * while ((node = avl_destroy_nodes(tree, &cookie)) != NULL) 937 | * free(node); 938 | * avl_destroy(tree); 939 | * 940 | * The cookie is really an avl_node_t to the current node's parent and 941 | * an indication of which child you looked at last. 942 | * 943 | * On input, a cookie value of CHILDBIT indicates the tree is done. 944 | */ 945 | void * 946 | avl_destroy_nodes(avl_tree_t *tree, void **cookie) 947 | { 948 | avl_node_t *node; 949 | avl_node_t *parent; 950 | int child; 951 | void *first; 952 | size_t off = tree->avl_offset; 953 | 954 | /* 955 | * Initial calls go to the first node or it's right descendant. 956 | */ 957 | if (*cookie == NULL) { 958 | first = avl_first(tree); 959 | 960 | /* 961 | * deal with an empty tree 962 | */ 963 | if (first == NULL) { 964 | *cookie = (void *)CHILDBIT; 965 | return (NULL); 966 | } 967 | 968 | node = AVL_DATA2NODE(first, off); 969 | parent = AVL_XPARENT(node); 970 | goto check_right_side; 971 | } 972 | 973 | /* 974 | * If there is no parent to return to we are done. 975 | */ 976 | parent = (avl_node_t *)((uintptr_t)(*cookie) & ~CHILDBIT); 977 | if (parent == NULL) { 978 | if (tree->avl_root != NULL) { 979 | assert(tree->avl_numnodes == 1); 980 | tree->avl_root = NULL; 981 | tree->avl_numnodes = 0; 982 | } 983 | return (NULL); 984 | } 985 | 986 | /* 987 | * Remove the child pointer we just visited from the parent and tree. 988 | */ 989 | child = (uintptr_t)(*cookie) & CHILDBIT; 990 | parent->avl_child[child] = NULL; 991 | assert(tree->avl_numnodes > 1); 992 | --tree->avl_numnodes; 993 | 994 | /* 995 | * If we just did a right child or there isn't one, go up to parent. 996 | */ 997 | if (child == 1 || parent->avl_child[1] == NULL) { 998 | node = parent; 999 | parent = AVL_XPARENT(parent); 1000 | goto done; 1001 | } 1002 | 1003 | /* 1004 | * Do parent's right child, then leftmost descendent. 1005 | */ 1006 | node = parent->avl_child[1]; 1007 | while (node->avl_child[0] != NULL) { 1008 | parent = node; 1009 | node = node->avl_child[0]; 1010 | } 1011 | 1012 | /* 1013 | * If here, we moved to a left child. It may have one 1014 | * child on the right (when balance == +1). 1015 | */ 1016 | check_right_side: 1017 | if (node->avl_child[1] != NULL) { 1018 | assert(AVL_XBALANCE(node) == 1); 1019 | parent = node; 1020 | node = node->avl_child[1]; 1021 | assert(node->avl_child[0] == NULL && 1022 | node->avl_child[1] == NULL); 1023 | } else { 1024 | assert(AVL_XBALANCE(node) <= 0); 1025 | } 1026 | 1027 | done: 1028 | if (parent == NULL) { 1029 | *cookie = (void *)CHILDBIT; 1030 | assert(node == tree->avl_root); 1031 | } else { 1032 | *cookie = (void *)((uintptr_t)parent | AVL_XCHILD(node)); 1033 | } 1034 | 1035 | return (AVL_NODE2DATA(node, off)); 1036 | } 1037 | -------------------------------------------------------------------------------- /avl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CDDL HEADER START 3 | * 4 | * The contents of this file are subject to the terms of the 5 | * Common Development and Distribution License (the "License"). 6 | * You may not use this file except in compliance with the License. 7 | * 8 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 | * or http://www.opensolaris.org/os/licensing. 10 | * See the License for the specific language governing permissions 11 | * and limitations under the License. 12 | * 13 | * When distributing Covered Code, include this CDDL HEADER in each 14 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 | * If applicable, add the following below this CDDL HEADER, with the 16 | * fields enclosed by brackets "[]" replaced with your own identifying 17 | * information: Portions Copyright [yyyy] [name of copyright owner] 18 | * 19 | * CDDL HEADER END 20 | */ 21 | /* 22 | * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 | * Use is subject to license terms. 24 | */ 25 | 26 | #ifndef _ACF_UTILS_AVL_H_ 27 | #define _ACF_UTILS_AVL_H_ 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | #include 34 | #include 35 | 36 | #define _AVL_IMPL_INCLUDED_FROM_AVL_H 37 | #include "avl_impl.h" 38 | 39 | /* 40 | * This is a generic implemenatation of AVL trees for use in the Solaris kernel. 41 | * The interfaces provide an efficient way of implementing an ordered set of 42 | * data structures. 43 | * 44 | * AVL trees provide an alternative to using an ordered linked list. Using AVL 45 | * trees will usually be faster, however they requires more storage. An ordered 46 | * linked list in general requires 2 pointers in each data structure. The 47 | * AVL tree implementation uses 3 pointers. The following chart gives the 48 | * approximate performance of operations with the different approaches: 49 | * 50 | * Operation Link List AVL tree 51 | * --------- -------- -------- 52 | * lookup O(n) O(log(n)) 53 | * 54 | * insert 1 node constant constant 55 | * 56 | * delete 1 node constant between constant and O(log(n)) 57 | * 58 | * delete all nodes O(n) O(n) 59 | * 60 | * visit the next 61 | * or prev node constant between constant and O(log(n)) 62 | * 63 | * 64 | * The data structure nodes are anchored at an "avl_tree_t" (the equivalent 65 | * of a list header) and the individual nodes will have a field of 66 | * type "avl_node_t" (corresponding to list pointers). 67 | * 68 | * The type "avl_index_t" is used to indicate a position in the list for 69 | * certain calls. 70 | * 71 | * The usage scenario is generally: 72 | * 73 | * 1. Create the list/tree with: avl_create() 74 | * 75 | * followed by any mixture of: 76 | * 77 | * 2a. Insert nodes with: avl_add(), or avl_find() and avl_insert() 78 | * 79 | * 2b. Visited elements with: 80 | * avl_first() - returns the lowest valued node 81 | * avl_last() - returns the highest valued node 82 | * AVL_NEXT() - given a node go to next higher one 83 | * AVL_PREV() - given a node go to previous lower one 84 | * 85 | * 2c. Find the node with the closest value either less than or greater 86 | * than a given value with avl_nearest(). 87 | * 88 | * 2d. Remove individual nodes from the list/tree with avl_remove(). 89 | * 90 | * and finally when the list is being destroyed 91 | * 92 | * 3. Use avl_destroy_nodes() to quickly process/free up any remaining nodes. 93 | * Note that once you use avl_destroy_nodes(), you can no longer 94 | * use any routine except avl_destroy_nodes() and avl_destoy(). 95 | * 96 | * 4. Use avl_destroy() to destroy the AVL tree itself. 97 | * 98 | * Any locking for multiple thread access is up to the user to provide, just 99 | * as is needed for any linked list implementation. 100 | */ 101 | 102 | 103 | /* 104 | * Type used for the root of the AVL tree. 105 | */ 106 | typedef struct avl_tree avl_tree_t; 107 | 108 | /* 109 | * The data nodes in the AVL tree must have a field of this type. 110 | */ 111 | typedef struct avl_node avl_node_t; 112 | 113 | /* 114 | * An opaque type used to locate a position in the tree where a node 115 | * would be inserted. 116 | */ 117 | typedef uintptr_t avl_index_t; 118 | 119 | 120 | /* 121 | * Direction constants used for avl_nearest(). 122 | */ 123 | #define AVL_BEFORE (0) 124 | #define AVL_AFTER (1) 125 | 126 | 127 | /* 128 | * Prototypes 129 | * 130 | * Where not otherwise mentioned, "void *" arguments are a pointer to the 131 | * user data structure which must contain a field of type avl_node_t. 132 | * 133 | * Also assume the user data structures looks like: 134 | * stuct my_type { 135 | * ... 136 | * avl_node_t my_link; 137 | * ... 138 | * }; 139 | */ 140 | 141 | /* 142 | * Initialize an AVL tree. Arguments are: 143 | * 144 | * tree - the tree to be initialized 145 | * compar - function to compare two nodes, it must return exactly: -1, 0, or +1 146 | * -1 for <, 0 for ==, and +1 for > 147 | * size - the value of sizeof(struct my_type) 148 | * offset - the value of OFFSETOF(struct my_type, my_link) 149 | */ 150 | extern void avl_create(avl_tree_t *tree, 151 | int (*compar) (const void *, const void *), size_t size, size_t offset); 152 | 153 | 154 | /* 155 | * Find a node with a matching value in the tree. Returns the matching node 156 | * found. If not found, it returns NULL and then if "where" is not NULL it sets 157 | * "where" for use with avl_insert() or avl_nearest(). 158 | * 159 | * node - node that has the value being looked for 160 | * where - position for use with avl_nearest() or avl_insert(), may be NULL 161 | */ 162 | extern void *avl_find(const avl_tree_t *tree, const void *node, 163 | avl_index_t *where); 164 | 165 | /* 166 | * Insert a node into the tree. 167 | * 168 | * node - the node to insert 169 | * where - position as returned from avl_find() 170 | */ 171 | extern void avl_insert(avl_tree_t *tree, void *node, 172 | avl_index_t where); 173 | 174 | /* 175 | * Insert "new_data" in "tree" in the given "direction" either after 176 | * or before the data "here". 177 | * 178 | * This might be usefull for avl clients caching recently accessed 179 | * data to avoid doing avl_find() again for insertion. 180 | * 181 | * new_data - new data to insert 182 | * here - existing node in "tree" 183 | * direction - either AVL_AFTER or AVL_BEFORE the data "here". 184 | */ 185 | extern void avl_insert_here(avl_tree_t *tree, void *new_data, 186 | void *here, int direction); 187 | 188 | 189 | /* 190 | * Return the first or last valued node in the tree. Will return NULL 191 | * if the tree is empty. 192 | * 193 | */ 194 | extern void *avl_first(const avl_tree_t *tree); 195 | extern void *avl_last(const avl_tree_t *tree); 196 | 197 | 198 | /* 199 | * Return the next or previous valued node in the tree. 200 | * AVL_NEXT() will return NULL if at the last node. 201 | * AVL_PREV() will return NULL if at the first node. 202 | * 203 | * node - the node from which the next or previous node is found 204 | */ 205 | #define AVL_NEXT(tree, node) avl_walk(tree, node, AVL_AFTER) 206 | #define AVL_PREV(tree, node) avl_walk(tree, node, AVL_BEFORE) 207 | 208 | 209 | /* 210 | * Find the node with the nearest value either greater or less than 211 | * the value from a previous avl_find(). Returns the node or NULL if 212 | * there isn't a matching one. 213 | * 214 | * where - position as returned from avl_find() 215 | * direction - either AVL_BEFORE or AVL_AFTER 216 | * 217 | * EXAMPLE get the greatest node that is less than a given value: 218 | * 219 | * avl_tree_t *tree; 220 | * struct my_data look_for_value = {....}; 221 | * struct my_data *node; 222 | * struct my_data *less; 223 | * avl_index_t where; 224 | * 225 | * node = avl_find(tree, &look_for_value, &where); 226 | * if (node != NULL) 227 | * less = AVL_PREV(tree, node); 228 | * else 229 | * less = avl_nearest(tree, where, AVL_BEFORE); 230 | */ 231 | extern void *avl_nearest(const avl_tree_t *tree, avl_index_t where, 232 | int direction); 233 | 234 | 235 | /* 236 | * Add a single node to the tree. 237 | * The node must not be in the tree, and it must not 238 | * compare equal to any other node already in the tree. 239 | * 240 | * node - the node to add 241 | */ 242 | extern void avl_add(avl_tree_t *tree, void *node); 243 | 244 | 245 | /* 246 | * Remove a single node from the tree. The node must be in the tree. 247 | * 248 | * node - the node to remove 249 | */ 250 | extern void avl_remove(avl_tree_t *tree, void *node); 251 | 252 | /* 253 | * Reinsert a node only if its order has changed relative to its nearest 254 | * neighbors. To optimize performance avl_update_lt() checks only the previous 255 | * node and avl_update_gt() checks only the next node. Use avl_update_lt() and 256 | * avl_update_gt() only if you know the direction in which the order of the 257 | * node may change. 258 | */ 259 | extern bool avl_update(avl_tree_t *, void *); 260 | extern bool avl_update_lt(avl_tree_t *, void *); 261 | extern bool avl_update_gt(avl_tree_t *, void *); 262 | 263 | /* 264 | * Return the number of nodes in the tree 265 | */ 266 | extern unsigned long avl_numnodes(const avl_tree_t *tree); 267 | 268 | /* 269 | * Return B_TRUE if there are zero nodes in the tree, B_FALSE otherwise. 270 | */ 271 | extern bool avl_is_empty(avl_tree_t *tree); 272 | 273 | /* 274 | * Used to destroy any remaining nodes in a tree. The cookie argument should 275 | * be initialized to NULL before the first call. Returns a node that has been 276 | * removed from the tree and may be free()'d. Returns NULL when the tree is 277 | * empty. 278 | * 279 | * Once you call avl_destroy_nodes(), you can only continuing calling it and 280 | * finally avl_destroy(). No other AVL routines will be valid. 281 | * 282 | * cookie - a "void *" used to save state between calls to avl_destroy_nodes() 283 | * 284 | * EXAMPLE: 285 | * avl_tree_t *tree; 286 | * struct my_data *node; 287 | * void *cookie; 288 | * 289 | * cookie = NULL; 290 | * while ((node = avl_destroy_nodes(tree, &cookie)) != NULL) 291 | * free(node); 292 | * avl_destroy(tree); 293 | */ 294 | extern void *avl_destroy_nodes(avl_tree_t *tree, void **cookie); 295 | 296 | 297 | /* 298 | * Final destroy of an AVL tree. Arguments are: 299 | * 300 | * tree - the empty tree to destroy 301 | */ 302 | extern void avl_destroy(avl_tree_t *tree); 303 | 304 | #ifdef __cplusplus 305 | } 306 | #endif 307 | 308 | #endif /* _ACF_UTILS_AVL_H_ */ 309 | -------------------------------------------------------------------------------- /avl_impl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CDDL HEADER START 3 | * 4 | * The contents of this file are subject to the terms of the 5 | * Common Development and Distribution License, Version 1.0 only 6 | * (the "License"). You may not use this file except in compliance 7 | * with the License. 8 | * 9 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | * or http://www.opensolaris.org/os/licensing. 11 | * See the License for the specific language governing permissions 12 | * and limitations under the License. 13 | * 14 | * When distributing Covered Code, include this CDDL HEADER in each 15 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | * If applicable, add the following below this CDDL HEADER, with the 17 | * fields enclosed by brackets "[]" replaced with your own identifying 18 | * information: Portions Copyright [yyyy] [name of copyright owner] 19 | * 20 | * CDDL HEADER END 21 | */ 22 | /* 23 | * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 | * Use is subject to license terms. 25 | */ 26 | 27 | #ifndef _AVL_IMPL_INCLUDED_FROM_AVL_H 28 | #error "Don't include avl_impl.h directly. Use avl.h instead." 29 | #endif 30 | 31 | #ifndef _ACF_UTILS_AVL_IMPL_H_ 32 | #define _ACF_UTILS_AVL_IMPL_H_ 33 | 34 | /* 35 | * This is a private header file. Applications should not directly include 36 | * this file. 37 | */ 38 | 39 | #include 40 | #include 41 | 42 | #ifdef __cplusplus 43 | extern "C" { 44 | #endif 45 | 46 | 47 | /* 48 | * generic AVL tree implementation for kernel use 49 | * 50 | * There are 5 pieces of information stored for each node in an AVL tree 51 | * 52 | * pointer to less than child 53 | * pointer to greater than child 54 | * a pointer to the parent of this node 55 | * an indication [0/1] of which child I am of my parent 56 | * a "balance" (-1, 0, +1) indicating which child tree is taller 57 | * 58 | * Since they only need 3 bits, the last two fields are packed into the 59 | * bottom bits of the parent pointer on 64 bit machines to save on space. 60 | */ 61 | 62 | #ifndef _LP64 63 | 64 | struct avl_node { 65 | struct avl_node *avl_child[2]; /* left/right children */ 66 | struct avl_node *avl_parent; /* this node's parent */ 67 | unsigned short avl_child_index; /* my index in parent's avl_child[] */ 68 | short avl_balance; /* balance value: -1, 0, +1 */ 69 | }; 70 | 71 | #define AVL_XPARENT(n) ((n)->avl_parent) 72 | #define AVL_SETPARENT(n, p) ((n)->avl_parent = (p)) 73 | 74 | #define AVL_XCHILD(n) ((n)->avl_child_index) 75 | #define AVL_SETCHILD(n, c) ((n)->avl_child_index = (unsigned short)(c)) 76 | 77 | #define AVL_XBALANCE(n) ((n)->avl_balance) 78 | #define AVL_SETBALANCE(n, b) ((n)->avl_balance = (short)(b)) 79 | 80 | #else /* _LP64 */ 81 | 82 | /* 83 | * for 64 bit machines, avl_pcb contains parent pointer, balance and child_index 84 | * values packed in the following manner: 85 | * 86 | * |63 3| 2 |1 0 | 87 | * |-------------------------------------|-----------------|-------------| 88 | * | avl_parent hi order bits | avl_child_index | avl_balance | 89 | * | | | + 1 | 90 | * |-------------------------------------|-----------------|-------------| 91 | * 92 | */ 93 | struct avl_node { 94 | struct avl_node *avl_child[2]; /* left/right children nodes */ 95 | uintptr_t avl_pcb; /* parent, child_index, balance */ 96 | }; 97 | 98 | /* 99 | * macros to extract/set fields in avl_pcb 100 | * 101 | * pointer to the parent of the current node is the high order bits 102 | */ 103 | #define AVL_XPARENT(n) ((struct avl_node *)((n)->avl_pcb & ~7)) 104 | #define AVL_SETPARENT(n, p) \ 105 | ((n)->avl_pcb = (((n)->avl_pcb & 7) | (uintptr_t)(p))) 106 | 107 | /* 108 | * index of this node in its parent's avl_child[]: bit #2 109 | */ 110 | #define AVL_XCHILD(n) (((n)->avl_pcb >> 2) & 1) 111 | #define AVL_SETCHILD(n, c) \ 112 | ((n)->avl_pcb = (uintptr_t)(((n)->avl_pcb & ~4) | ((c) << 2))) 113 | 114 | /* 115 | * balance indication for a node, lowest 2 bits. A valid balance is 116 | * -1, 0, or +1, and is encoded by adding 1 to the value to get the 117 | * unsigned values of 0, 1, 2. 118 | */ 119 | #define AVL_XBALANCE(n) ((int)(((n)->avl_pcb & 3) - 1)) 120 | #define AVL_SETBALANCE(n, b) \ 121 | ((n)->avl_pcb = (uintptr_t)((((n)->avl_pcb & ~3) | ((b) + 1)))) 122 | 123 | #endif /* _LP64 */ 124 | 125 | 126 | 127 | /* 128 | * switch between a node and data pointer for a given tree 129 | * the value of "o" is tree->avl_offset 130 | */ 131 | #define AVL_NODE2DATA(n, o) ((void *)((uintptr_t)(n) - (o))) 132 | #define AVL_DATA2NODE(d, o) ((struct avl_node *)((uintptr_t)(d) + (o))) 133 | 134 | 135 | 136 | /* 137 | * macros used to create/access an avl_index_t 138 | */ 139 | #define AVL_INDEX2NODE(x) ((avl_node_t *)((x) & ~1)) 140 | #define AVL_INDEX2CHILD(x) ((x) & 1) 141 | #define AVL_MKINDEX(n, c) ((avl_index_t)(n) | (c)) 142 | 143 | 144 | /* 145 | * The tree structure. The fields avl_root, avl_compar, and avl_offset come 146 | * first since they are needed for avl_find(). We want them to fit into 147 | * a single 64 byte cache line to make avl_find() as fast as possible. 148 | */ 149 | struct avl_tree { 150 | struct avl_node *avl_root; /* root node in tree */ 151 | int (*avl_compar)(const void *, const void *); 152 | size_t avl_offset; /* offsetof(type, avl_link_t field) */ 153 | unsigned long avl_numnodes; /* number of nodes in the tree */ 154 | size_t avl_size; /* sizeof user type struct */ 155 | }; 156 | 157 | 158 | /* 159 | * This will only by used via AVL_NEXT() or AVL_PREV() 160 | */ 161 | extern void *avl_walk(const struct avl_tree *, const void *, int); 162 | 163 | #ifdef __cplusplus 164 | } 165 | #endif 166 | 167 | #endif /* _ACF_UTILS_AVL_IMPL_H_ */ 168 | -------------------------------------------------------------------------------- /polar2afl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * CDDL HEADER START 3 | * 4 | * This file and its contents are supplied under the terms of the 5 | * Common Development and Distribution License ("CDDL"), version 1.0. 6 | * You may only use this file in accordance with the terms of version 7 | * 1.0 of the CDDL. 8 | * 9 | * A full copy of the text of the CDDL should have accompanied this 10 | * source. A copy of the CDDL is also available via the Internet at 11 | * http://www.illumos.org/license/CDDL. 12 | * 13 | * CDDL HEADER END 14 | */ 15 | /* 16 | * Copyright 2020 Saso Kiselkov. All rights reserved. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "avl.h" 30 | #include "safe_alloc.h" 31 | 32 | #ifndef MIN 33 | #define MIN(x, y) ((x) < (y) ? (x) : (y)) 34 | #endif 35 | #ifndef MAX 36 | #define MAX(x, y) ((x) > (y) ? (x) : (y)) 37 | #endif 38 | 39 | typedef struct { 40 | double alpha; 41 | double Cl; 42 | double Cd; 43 | double Cm; 44 | avl_node_t node; 45 | } polar_t; 46 | 47 | typedef struct { 48 | struct { 49 | double Re; 50 | double slope; 51 | double intercept; 52 | double alpha_min; 53 | double alpha_max; 54 | double lin_range; 55 | double lift_power; 56 | double Cl_max; 57 | double stall_drop; 58 | double stall_power; 59 | double stalled_drop; 60 | double Cd_min; 61 | double min_Cd_Cl; 62 | double Cd_alpha10; 63 | double Cd_power; 64 | double buck_Cl; 65 | double buck_width; 66 | double buck_depth; 67 | double buck_power; 68 | double Cm_alpha1; 69 | double Cm_alpha2; 70 | double Cm_val_alpha_neg20; 71 | double Cm_val_alpha1; 72 | double Cm_val_alpha2; 73 | double Cm_val_alpha_pos20; 74 | } params; 75 | avl_tree_t polars; 76 | avl_node_t node; 77 | } polar_diag_t; 78 | 79 | typedef struct { 80 | char preamble[1024]; 81 | avl_tree_t diags; 82 | } afl_t; 83 | 84 | static bool do_smooth_polars = true; 85 | static double edge_blend_range = 10; /* degrees */ 86 | 87 | static void afl_free(afl_t *afl); 88 | static polar_diag_t *smooth_diag(polar_diag_t *d); 89 | 90 | ssize_t 91 | my_getline(char **line_p, size_t *cap_p, FILE *fp) 92 | { 93 | char *line; 94 | size_t cap, n = 0; 95 | 96 | assert(line_p != NULL); 97 | line = *line_p; 98 | assert(cap_p != NULL); 99 | cap = *cap_p; 100 | assert(fp != NULL); 101 | 102 | do { 103 | if (n + 1 >= cap) { 104 | cap += 256; 105 | line = realloc(line, cap); 106 | } 107 | assert(n < cap); 108 | if (fgets(&line[n], cap - n, fp) == NULL) { 109 | if (n != 0) 110 | break; 111 | *line_p = line; 112 | *cap_p = cap; 113 | return (-1); 114 | } 115 | n = strlen(line); 116 | } while (n > 0 && line[n - 1] != '\n'); 117 | 118 | *line_p = line; 119 | *cap_p = cap; 120 | 121 | return (n); 122 | } 123 | 124 | static double 125 | wavg(double x, double y, double w) 126 | { 127 | assert(w >= 0.0 && w <= 1.0); 128 | return (x + (y - x) * w); 129 | } 130 | 131 | static inline double 132 | clamp(double x, double min_val, double max_val) 133 | { 134 | if (min_val < max_val) { 135 | if (x < min_val) 136 | return (min_val); 137 | if (x > max_val) 138 | return (max_val); 139 | } else { 140 | if (x > min_val) 141 | return (min_val); 142 | if (x < max_val) 143 | return (max_val); 144 | } 145 | return (x); 146 | } 147 | 148 | static inline double 149 | iter_fract(double x, double min_val, double max_val, bool clamp_output) 150 | { 151 | x = (x - min_val) / (max_val - min_val); 152 | if (clamp_output) 153 | x = clamp(x, 0, 1); 154 | return (x); 155 | } 156 | 157 | /* 158 | * Removes all leading & trailing whitespace from a line. 159 | */ 160 | void 161 | strip_space(char *line) 162 | { 163 | char *p; 164 | size_t len = strlen(line); 165 | 166 | /* strip leading whitespace */ 167 | for (p = line; *p != 0 && isspace(*p); p++) 168 | ; 169 | if (p != line) { 170 | memmove(line, p, (len + 1) - (p - line)); 171 | len -= (p - line); 172 | } 173 | 174 | for (p = line + len - 1; p >= line && isspace(*p); p--) 175 | ; 176 | p[1] = 0; 177 | } 178 | 179 | static int 180 | polar_diag_compar(const void *a, const void *b) 181 | { 182 | const polar_diag_t *pa = a, *pb = b; 183 | 184 | if (pa->params.Re < pb->params.Re) 185 | return (-1); 186 | if (pa->params.Re > pb->params.Re) 187 | return (1); 188 | return (0); 189 | } 190 | 191 | static int 192 | polar_compar(const void *a, const void *b) 193 | { 194 | const polar_t *pa = a, *pb = b; 195 | double alpha_a = round(pa->alpha * 10.0) / 10.0; 196 | double alpha_b = round(pb->alpha * 10.0) / 10.0; 197 | 198 | if (alpha_a < alpha_b) 199 | return (-1); 200 | if (alpha_a > alpha_b) 201 | return (1); 202 | return (0); 203 | } 204 | 205 | static void 206 | polar_diag_free(polar_diag_t *diag) 207 | { 208 | void *cookie = NULL; 209 | polar_t *polar; 210 | 211 | while ((polar = avl_destroy_nodes(&diag->polars, &cookie)) != NULL) 212 | free(polar); 213 | avl_destroy(&diag->polars); 214 | free(diag); 215 | } 216 | 217 | static polar_diag_t * 218 | polar_diag_alloc(void) 219 | { 220 | polar_diag_t *diag = safe_calloc(1, sizeof (*diag)); 221 | avl_create(&diag->polars, polar_compar, sizeof (polar_t), 222 | offsetof(polar_t, node)); 223 | return (diag); 224 | } 225 | 226 | static polar_diag_t * 227 | afl_parse_polar_diag(FILE *fp, const char *filename, int *line_nr) 228 | { 229 | char *line = NULL; 230 | size_t linecap = 0; 231 | int n; 232 | polar_diag_t *diag = polar_diag_alloc(); 233 | 234 | n = my_getline(&line, &linecap, fp); 235 | /* Probably an EOF, just exit */ 236 | if (n <= 0) 237 | goto errout; 238 | if (sscanf(line, "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf " 239 | "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf", 240 | &diag->params.Re, &diag->params.slope, &diag->params.intercept, 241 | &diag->params.alpha_min, &diag->params.alpha_max, 242 | &diag->params.lin_range, &diag->params.lift_power, 243 | &diag->params.Cl_max, &diag->params.stall_drop, 244 | &diag->params.stall_power, &diag->params.stalled_drop, 245 | &diag->params.Cd_min, &diag->params.min_Cd_Cl, 246 | &diag->params.Cd_alpha10, &diag->params.Cd_power, 247 | &diag->params.buck_Cl, &diag->params.buck_width, 248 | &diag->params.buck_depth, &diag->params.buck_power, 249 | &diag->params.Cm_alpha1, &diag->params.Cm_alpha2, 250 | &diag->params.Cm_val_alpha_neg20, 251 | &diag->params.Cm_val_alpha1, &diag->params.Cm_val_alpha2, 252 | &diag->params.Cm_val_alpha_pos20) != 25) { 253 | fprintf(stderr, "%s:%d: error parsing polar parameters\n", 254 | filename, *line_nr); 255 | goto errout; 256 | } 257 | diag->params.Re *= 1000000.0; 258 | (*line_nr)++; 259 | /* skip the "alpha cl cd cm:" line */ 260 | if (my_getline(&line, &linecap, fp) <= 0) { 261 | fprintf(stderr, "%s:%d: premature EOF\n", filename, *line_nr); 262 | goto errout; 263 | } 264 | (*line_nr)++; 265 | 266 | for (; my_getline(&line, &linecap, fp) > 0; (*line_nr)++) { 267 | double alpha, Cl, Cd, Cm; 268 | polar_t *polar; 269 | avl_index_t where; 270 | 271 | if (sscanf(line, "%lf %lf %lf %lf", &alpha, &Cl, &Cd, &Cm) != 272 | 4) { 273 | fprintf(stderr, "%s:%d: error parsing polar line\n", 274 | filename, *line_nr); 275 | goto errout; 276 | } 277 | polar = safe_calloc(1, sizeof (*polar)); 278 | polar->alpha = alpha; 279 | polar->Cl = Cl; 280 | polar->Cd = Cd; 281 | polar->Cm = Cm; 282 | if (avl_find(&diag->polars, polar, &where) != NULL) { 283 | fprintf(stderr, "%s:%d: duplicate alpha detected\n", 284 | filename, *line_nr); 285 | goto errout; 286 | } 287 | avl_insert(&diag->polars, polar, where); 288 | /* The 180 degree polar is the last one in a diagram */ 289 | if (alpha >= 180.0) 290 | break; 291 | } 292 | 293 | free(line); 294 | return (diag); 295 | errout: 296 | polar_diag_free(diag); 297 | free(line); 298 | return (NULL); 299 | } 300 | 301 | static afl_t * 302 | afl_alloc(void) 303 | { 304 | afl_t *afl = safe_calloc(1, sizeof (*afl)); 305 | 306 | avl_create(&afl->diags, polar_diag_compar, 307 | sizeof (polar_diag_t), offsetof(polar_diag_t, node)); 308 | return (afl); 309 | } 310 | 311 | static afl_t * 312 | afl_parse(const char *filename) 313 | { 314 | afl_t *afl; 315 | char *line = NULL; 316 | size_t linecap = 0; 317 | FILE *fp; 318 | int line_nr; 319 | polar_diag_t *diag; 320 | 321 | fp = fopen(filename, "r"); 322 | if (fp == NULL) { 323 | fprintf(stderr, "Can't open %s: %s\n", filename, 324 | strerror(errno)); 325 | return (NULL); 326 | } 327 | 328 | afl = afl_alloc(); 329 | 330 | for (line_nr = 0; my_getline(&line, &linecap, fp) > 0; line_nr++) { 331 | if (line_nr < 19) { 332 | strncat(afl->preamble, line, 333 | sizeof (afl->preamble) - 1); 334 | continue; 335 | } 336 | /* 337 | This is just the number of diags in the file, we don't 338 | * need that, we parse until we can't get any more polars 339 | */ 340 | if (line_nr == 19) { 341 | line_nr++; 342 | break; 343 | } 344 | } 345 | while ((diag = afl_parse_polar_diag(fp, filename, &line_nr)) != NULL) { 346 | avl_index_t where; 347 | 348 | if (avl_find(&afl->diags, diag, &where) != NULL) { 349 | fprintf(stderr, "%s: duplicate polar diagram for " 350 | "Re = %f\n", filename, diag->params.Re); 351 | polar_diag_free(diag); 352 | goto errout; 353 | } 354 | avl_insert(&afl->diags, diag, where); 355 | } 356 | 357 | free(line); 358 | fclose(fp); 359 | return (afl); 360 | errout: 361 | afl_free(afl); 362 | free(line); 363 | fclose(fp); 364 | return (NULL); 365 | } 366 | 367 | static void 368 | afl_write_diag(FILE *fp, const polar_diag_t *diag, double chord) 369 | { 370 | if (avl_numnodes(&diag->polars) == 0) 371 | return; 372 | 373 | fprintf(fp, "%.5f %.5f %.5f %.5f %.5f %.5f %.5f %.5f %.5f %.5f " 374 | "%.5f %.5f %.5f %.5f %.5f %.5f %.5f %.5f %.5f %.5f %.5f %.5f " 375 | "%.5f %.5f %.5f\n", 376 | round((diag->params.Re * chord) / 10000.0) / 100.0, 377 | diag->params.slope, diag->params.intercept, diag->params.alpha_min, 378 | diag->params.alpha_max, diag->params.lin_range, 379 | diag->params.lift_power, diag->params.Cl_max, 380 | diag->params.stall_drop, diag->params.stall_power, 381 | diag->params.stalled_drop, diag->params.Cd_min, 382 | diag->params.min_Cd_Cl, diag->params.Cd_alpha10, 383 | diag->params.Cd_power, diag->params.buck_Cl, 384 | diag->params.buck_width, diag->params.buck_depth, 385 | diag->params.buck_power, diag->params.Cm_alpha1, 386 | diag->params.Cm_alpha2, diag->params.Cm_val_alpha_neg20, 387 | diag->params.Cm_val_alpha1, diag->params.Cm_val_alpha2, 388 | diag->params.Cm_val_alpha_pos20); 389 | fprintf(fp, "alpha cl cd cm:\n"); 390 | 391 | for (double alpha = -180; alpha <= 180;) { 392 | double step = ((alpha < -20.0 || alpha >= 20.0) ? 1.0 : 0.1); 393 | polar_t srch = { .alpha = alpha }; 394 | polar_t *p1, *p2; 395 | avl_index_t where; 396 | double Cl, Cd, Cm; 397 | 398 | p1 = avl_find(&diag->polars, &srch, &where); 399 | if (p1 != NULL) { 400 | Cl = p1->Cl; 401 | Cd = p1->Cd; 402 | Cm = p1->Cm; 403 | } else { 404 | p1 = avl_nearest(&diag->polars, where, AVL_BEFORE); 405 | p2 = avl_nearest(&diag->polars, where, AVL_AFTER); 406 | 407 | assert(p1 != NULL || p2 != NULL); 408 | if (p1 != NULL && p2 != NULL) { 409 | double w = iter_fract(alpha, p1->alpha, 410 | p2->alpha, true); 411 | 412 | Cl = wavg(p1->Cl, p2->Cl, w); 413 | Cd = wavg(p1->Cd, p2->Cd, w); 414 | Cm = wavg(p1->Cm, p2->Cm, w); 415 | } else { 416 | if (p1 == NULL) 417 | p1 = p2; 418 | Cl = p1->Cl; 419 | Cd = p1->Cd; 420 | Cm = p1->Cm; 421 | } 422 | } 423 | 424 | fprintf(fp, "%6.1f %8.5f %8.5f %8.5f\n", alpha, Cl, Cd, Cm); 425 | 426 | alpha = round((alpha + step) * 10.0) / 10.0; 427 | } 428 | } 429 | 430 | static bool 431 | afl_write(const afl_t *afl, const char *filename, double chord) 432 | { 433 | FILE *fp = fopen(filename, "w"); 434 | 435 | if (fp == NULL) { 436 | fprintf(stderr, "Can't write airfoil %s: %s\n", filename, 437 | strerror(errno)); 438 | return (false); 439 | } 440 | 441 | fprintf(fp, "%s", afl->preamble); 442 | fprintf(fp, "%d\n", (int)avl_numnodes(&afl->diags)); 443 | 444 | for (const polar_diag_t *diag = avl_first(&afl->diags); 445 | diag != NULL; diag = AVL_NEXT(&afl->diags, diag)) { 446 | afl_write_diag(fp, diag, chord); 447 | } 448 | 449 | fclose(fp); 450 | return (true); 451 | } 452 | 453 | static void 454 | afl_free(afl_t *afl) 455 | { 456 | void *cookie = NULL; 457 | polar_diag_t *diag; 458 | 459 | while ((diag = avl_destroy_nodes(&afl->diags, &cookie)) != 460 | NULL) { 461 | polar_diag_free(diag); 462 | } 463 | avl_destroy(&afl->diags); 464 | free(afl); 465 | } 466 | 467 | static double 468 | lerp(double x, double x1, double y1, double x2, double y2) 469 | { 470 | assert(x1 != x2); 471 | return (((x - x1) / (x2 - x1)) * (y2 - y1) + y1); 472 | } 473 | 474 | static bool 475 | xfoil_polar_parse(afl_t *xfoil, const char *filename) 476 | { 477 | FILE *fp; 478 | polar_diag_t *diag; 479 | bool header = true; 480 | char *line = NULL; 481 | size_t linecap = 0; 482 | avl_index_t where; 483 | 484 | fp = fopen(filename, "r"); 485 | if (fp == NULL) { 486 | fprintf(stderr, "Error opening %s: %s\n", filename, 487 | strerror(errno)); 488 | return (false); 489 | } 490 | diag = polar_diag_alloc(); 491 | 492 | while (my_getline(&line, &linecap, fp) > 0) { 493 | strip_space(line); 494 | if (strlen(line) == 0) 495 | continue; 496 | if (header) { 497 | const char *p; 498 | double mantissa, exponent; 499 | 500 | if ((p = strstr(line, "Re =")) != NULL) { 501 | if (sscanf(&p[4], "%lf e %lf", &mantissa, 502 | &exponent) != 2) { 503 | fprintf(stderr, "%s: error parsing " 504 | "Re\n", filename); 505 | goto errout; 506 | } 507 | diag->params.Re = mantissa * pow(10, exponent); 508 | } else if ((p = strstr(line, "-------")) != NULL) { 509 | header = false; 510 | } 511 | } else { 512 | polar_t *polar = safe_calloc(1, sizeof (*polar)); 513 | avl_index_t where; 514 | double CDp; 515 | 516 | if (sscanf(line, "%lf %lf %lf %lf %lf", 517 | &polar->alpha, &polar->Cl, &polar->Cd, &CDp, 518 | &polar->Cm) != 5) { 519 | fprintf(stderr, "%s: error parsing polar\n", 520 | filename); 521 | goto errout; 522 | } 523 | if (avl_find(&diag->polars, polar, &where) != NULL) { 524 | free(polar); 525 | continue; 526 | } 527 | avl_insert(&diag->polars, polar, where); 528 | } 529 | } 530 | /* 531 | * Pass over the entire diagram and do a linear interpolation 532 | * of any missing pieces. 533 | */ 534 | for (polar_t *pt = avl_first(&diag->polars), *pt_next = NULL; 535 | pt != NULL; pt = pt_next) { 536 | pt_next = AVL_NEXT(&diag->polars, pt); 537 | if (pt_next == NULL) 538 | break; 539 | for (double alpha = pt->alpha + 0.1; 540 | alpha + 0.01 < pt_next->alpha; 541 | alpha = (round(alpha * 10.0) + 1.0) / 10.0) { 542 | polar_t *polar = safe_calloc(1, sizeof (*polar)); 543 | avl_index_t where; 544 | 545 | polar->alpha = alpha; 546 | polar->Cl = lerp(alpha, pt->alpha, pt->Cl, 547 | pt_next->alpha, pt_next->Cl); 548 | polar->Cd = lerp(alpha, pt->alpha, pt->Cd, 549 | pt_next->alpha, pt_next->Cd); 550 | polar->Cm = lerp(alpha, pt->alpha, pt->Cm, 551 | pt_next->alpha, pt_next->Cm); 552 | /* 553 | * Duplicates are possible because we round the 554 | * alpha, so deal with them gracefully. 555 | */ 556 | if (avl_find(&diag->polars, polar, &where) == NULL) 557 | avl_insert(&diag->polars, polar, where); 558 | else 559 | free(polar); 560 | } 561 | } 562 | 563 | if (do_smooth_polars) 564 | diag = smooth_diag(diag); 565 | if (avl_find(&xfoil->diags, diag, &where) != NULL) { 566 | fprintf(stderr, "%s: is a duplicate of an existing polar " 567 | "at Re = %fM\n", filename, diag->params.Re / 1000000); 568 | goto errout; 569 | } 570 | avl_insert(&xfoil->diags, diag, where); 571 | 572 | free(line); 573 | fclose(fp); 574 | return (true); 575 | errout: 576 | polar_diag_free(diag); 577 | free(line); 578 | fclose(fp); 579 | return (false); 580 | } 581 | 582 | static polar_t * 583 | find_nearest_polar(const polar_diag_t *diag, double alpha) 584 | { 585 | polar_t srch = { .alpha = alpha }; 586 | avl_index_t where; 587 | polar_t *polar = avl_find(&diag->polars, &srch, &where); 588 | 589 | if (polar == NULL) 590 | polar = avl_nearest(&diag->polars, where, AVL_BEFORE); 591 | if (polar == NULL) 592 | polar = avl_nearest(&diag->polars, where, AVL_AFTER); 593 | assert(polar != NULL); 594 | return (polar); 595 | } 596 | 597 | static void 598 | blend_polar(const polar_diag_t *diag, polar_t *tgt_polar, 599 | const polar_t *src_polar1, double d_alpha) 600 | { 601 | double blend_tgt_alpha = src_polar1->alpha + d_alpha; 602 | const polar_t *src_polar2 = find_nearest_polar(diag, blend_tgt_alpha); 603 | double interp_w, blend_w, Cl, Cd, Cm; 604 | double alpha_min, alpha_max; 605 | 606 | assert(src_polar2 != NULL); 607 | 608 | alpha_min = MIN(src_polar1->alpha, src_polar1->alpha + d_alpha); 609 | alpha_max = MAX(src_polar1->alpha, src_polar1->alpha + d_alpha); 610 | if (tgt_polar->alpha < alpha_min || tgt_polar->alpha > alpha_max) 611 | return; 612 | 613 | interp_w = iter_fract(tgt_polar->alpha, src_polar1->alpha, 614 | src_polar2->alpha, true); 615 | Cl = wavg(src_polar1->Cl, src_polar2->Cl, interp_w); 616 | Cd = wavg(src_polar1->Cd, src_polar2->Cd, interp_w); 617 | Cm = wavg(src_polar1->Cm, src_polar2->Cm, interp_w); 618 | 619 | blend_w = iter_fract(fabs(tgt_polar->alpha - blend_tgt_alpha), 620 | 0, fabs(d_alpha), true); 621 | tgt_polar->Cl = wavg(tgt_polar->Cl, Cl, blend_w); 622 | tgt_polar->Cd = wavg(tgt_polar->Cd, Cd, blend_w); 623 | tgt_polar->Cm = wavg(tgt_polar->Cm, Cm, blend_w); 624 | } 625 | 626 | static void 627 | afl_interp_diag_polar(const polar_diag_t *d1, const polar_diag_t *d2, 628 | polar_t *p, avl_index_t where) 629 | { 630 | const polar_t *p1, *p2; 631 | double w; 632 | 633 | p1 = avl_nearest(&d2->polars, where, AVL_BEFORE); 634 | p2 = avl_nearest(&d2->polars, where, AVL_AFTER); 635 | if (p1 == NULL) { 636 | assert(p2 != NULL); 637 | blend_polar(d1, p, p2, -edge_blend_range); 638 | } else if (p2 == NULL) { 639 | assert(p1 != NULL); 640 | blend_polar(d1, p, p1, edge_blend_range); 641 | } else { 642 | w = iter_fract(p->alpha, p1->alpha, p2->alpha, true); 643 | p->Cl = wavg(p1->Cl, p2->Cl, w); 644 | p->Cd = wavg(p1->Cd, p2->Cd, w); 645 | p->Cm = wavg(p1->Cm, p2->Cm, w); 646 | } 647 | } 648 | 649 | /* 650 | * Uses a Gaussian interpolator to smooth out a polar diagram. The passed 651 | * diagram is then freed and the smoothed-out version is returned. The 652 | * should substitute the original diagram with the newly smoothed-out one. 653 | */ 654 | static polar_diag_t * 655 | smooth_diag(polar_diag_t *d1) 656 | { 657 | polar_diag_t *d2; 658 | const double kernel[] = { 0.06136, 0.24477, 0.38774, 0.24477, 0.06136 }; 659 | 660 | d2 = polar_diag_alloc(); 661 | memcpy(&d2->params, &d1->params, sizeof (d1->params)); 662 | 663 | for (polar_t *p = avl_first(&d1->polars); p != NULL; 664 | p = AVL_NEXT(&d1->polars, p)) { 665 | polar_t *p_l, *p_ll, *p_r, *p_rr; 666 | polar_t *np = safe_calloc(1, sizeof (*np)); 667 | 668 | memcpy(np, p, sizeof (*np)); 669 | avl_add(&d2->polars, np); 670 | 671 | p_l = AVL_PREV(&d1->polars, p); 672 | p_r = AVL_NEXT(&d1->polars, p); 673 | if (p_l == NULL || p_r == NULL) 674 | continue; 675 | p_ll = AVL_PREV(&d1->polars, p_l); 676 | p_rr = AVL_NEXT(&d1->polars, p_r); 677 | if (p_ll == NULL || p_rr == NULL) 678 | continue; 679 | 680 | np->Cl = (p_ll->Cl * kernel[0] + p_l->Cl * kernel[1] + 681 | p->Cl * kernel[2] + p_r->Cl * kernel[3] + 682 | p_rr->Cl * kernel[4]); 683 | np->Cd = (p_ll->Cd * kernel[0] + p_l->Cd * kernel[1] + 684 | p->Cd * kernel[2] + p_r->Cd * kernel[3] + 685 | p_rr->Cd * kernel[4]); 686 | np->Cm = (p_ll->Cm * kernel[0] + p_l->Cm * kernel[1] + 687 | p->Cm * kernel[2] + p_r->Cm * kernel[3] + 688 | p_rr->Cm * kernel[4]); 689 | } 690 | 691 | polar_diag_free(d1); 692 | 693 | return (d2); 694 | } 695 | 696 | static void 697 | afl_combine_diag(polar_diag_t *d1, const polar_diag_t *d2) 698 | { 699 | double Cl_max = -INFINITY; 700 | double Cl_max_alpha = NAN; 701 | double Cl_min = INFINITY; 702 | double Cl_min_alpha = NAN; 703 | double Cd_min = INFINITY; 704 | double Cd_min_Cl = NAN; 705 | 706 | for (polar_t *p = avl_first(&d1->polars); p != NULL; 707 | p = AVL_NEXT(&d1->polars, p)) { 708 | const polar_t *p2; 709 | avl_index_t where; 710 | 711 | p2 = avl_find(&d2->polars, p, &where); 712 | if (p2 != NULL) { 713 | p->Cl = p2->Cl; 714 | p->Cd = p2->Cd; 715 | p->Cm = p2->Cm; 716 | } else { 717 | afl_interp_diag_polar(d1, d2, p, where); 718 | } 719 | 720 | if (p->Cl > Cl_max) { 721 | Cl_max = p->Cl; 722 | Cl_max_alpha = p->alpha; 723 | } 724 | if (p->Cl < Cl_min) { 725 | Cl_min = p->Cl; 726 | Cl_min_alpha = p->alpha; 727 | } 728 | if (p->Cd < Cd_min) { 729 | Cd_min = p->Cd; 730 | Cd_min_Cl = p->Cl; 731 | } 732 | } 733 | 734 | assert(isfinite(Cl_max)); 735 | assert(!isnan(Cl_max_alpha)); 736 | assert(isfinite(Cl_min)); 737 | assert(!isnan(Cl_min_alpha)); 738 | assert(isfinite(Cd_min)); 739 | assert(!isnan(Cd_min_Cl)); 740 | 741 | d1->params.Cl_max = Cl_max; 742 | d1->params.alpha_max = clamp(Cl_max_alpha, -19.999, 19.999); 743 | d1->params.alpha_min = clamp(Cl_min_alpha, -19.999, 19.999); 744 | d1->params.Cd_min = Cd_min; 745 | d1->params.min_Cd_Cl = Cd_min_Cl; 746 | } 747 | 748 | static bool 749 | afl_combine(afl_t *afl, const afl_t *xfoil) 750 | { 751 | bool result = true; 752 | 753 | for (polar_diag_t *d_xfoil = avl_first(&xfoil->diags); d_xfoil != NULL; 754 | d_xfoil = AVL_NEXT(&afl->diags, d_xfoil)) { 755 | const polar_diag_t *d_afl = 756 | avl_find(&afl->diags, d_xfoil, NULL); 757 | 758 | if (d_afl == NULL) { 759 | fprintf(stderr, "ERROR: XFoil polar Re=%.3fM has no " 760 | "matching polar in the AFL file.\n" 761 | " Open the input AFL file in Airfoil Maker and " 762 | "add the missing Re number first,\n" 763 | " then re-run polar2afl.\n", 764 | d_xfoil->params.Re / 1000000.0); 765 | result = false; 766 | } 767 | } 768 | 769 | for (polar_diag_t *d_afl = avl_first(&afl->diags); d_afl != NULL; 770 | d_afl = AVL_NEXT(&afl->diags, d_afl)) { 771 | const polar_diag_t *d_xfoil = 772 | avl_find(&xfoil->diags, d_afl, NULL); 773 | 774 | if (d_xfoil == NULL) { 775 | fprintf(stderr, "ERROR: polar Re=%.3fM in AFL file " 776 | "has no matching XFoil polar,\n" 777 | " leaving unmodified.\n", 778 | d_afl->params.Re / 1000000.0); 779 | result = false; 780 | continue; 781 | } 782 | assert(avl_numnodes(&d_xfoil->polars) != 0); 783 | 784 | afl_combine_diag(d_afl, d_xfoil); 785 | } 786 | 787 | return (result); 788 | } 789 | 790 | static void 791 | print_usage(FILE *fp, const char *progname) 792 | { 793 | fprintf(fp, "Usage: %s [-hs] [-c ] [-e ] " 794 | " [polar.txt...]\n" 795 | "\n" 796 | " Copyright 2020 Saso Kiselkov. All rights reserved.\n" 797 | "\n" 798 | " This utility takes an input .afl file produced by X-Plane's\n" 799 | " Airfoil Maker and a number of polar graphs from XFoil and\n" 800 | " splices the XFoil data into the .afl file, generating a new\n" 801 | " output .afl file in the process. If you are using XFLR5 to\n" 802 | " generate the polars, you can batch export all polars by\n" 803 | " selecting Polars -> Export all -> to text format.\n" 804 | " Please note: before attempt to splice the XFoil polars an .afl\n" 805 | " file, make sure that the file already contains all the Re\n" 806 | " numbers you want to insert. For example, if you've generated\n" 807 | " XFoil polars for Re=3.5M, 4.5M and 5.5M, make sure the\n" 808 | " corresponding Re tabs are already present in the .afl file.\n" 809 | " Then save the file in Airfoil Maker and use polar2afl.\n" 810 | " polar2afl will NOT splice in any polars for Re numbers that\n" 811 | " are not already present in the .afl file.\n" 812 | "\n" 813 | " IMPORTANT CAVEAT:\n" 814 | " X-Plane doesn't scale Reynolds numbers in airfoil files, so\n" 815 | " it's going to calculate the exact Reynolds for the wing\n" 816 | " section and simply look up a matching entry (or interpolate)\n" 817 | " from the afl file. But XFoil uses unit chords. So, to work\n" 818 | " around this, use the `-c ' parameter. This makes\n" 819 | " polar2afl multiply the Reynolds in the output AFL file by\n" 820 | " by this number, effectively pre-scaling the Reynolds numbers\n" 821 | " to the chord length of your wing.\n" 822 | "\n" 823 | "Example:\n" 824 | " Take two polars named `NACA2412_Re2.5.txt' and " 825 | "`NACA2412_Re3.5.txt'\n" 826 | " in the current directory and splice them into an airfoil file\n" 827 | " named `foo.afl', writing the result to a new file named\n" 828 | " `bar.afl' and scaling the Reynolds numbers to a 1.8m wing.\n" 829 | "\n" 830 | " $ %s -c 1.8 foo.afl bar.afl NACA2412_Re2.5.txt " 831 | "NACA2412_Re3.5.txt\n" 832 | "\n" 833 | "Options:\n" 834 | " -h: Shows this help screen and exits.\n" 835 | " -s: DON'T smooth polar diagrams. The polars produced by XFoil\n" 836 | " sometimes contain jagged jumps, especially when the solver\n" 837 | " couldn't converge on some points. To avoid such weirdness\n" 838 | " propagating into the .afl file, we do a guassian smoothing\n" 839 | " pass to get rid of such artifacts. Passing the '-s' option\n" 840 | " DISABLES this smoothing pass.\n" 841 | " -e : selects the alpha range over which we interpolate\n" 842 | " between the XFoil polars and the original input .afl file.\n" 843 | " The edges of the XFoil polars generally don't smoothly\n" 844 | " attach to the original .afl file, so over a range of alphas\n" 845 | " outside of the polar data range, we gradually interpolate\n" 846 | " between the original .afl curve and our polar data curve.\n" 847 | " The default is to interpolate the edge fit over 10 degrees\n" 848 | " of alpha range.\n" 849 | " -c : scale the output AFL file's Reynolds numbers by\n" 850 | " this factor. This can be used to translate from XFoil's\n" 851 | " unit chord data to X-Plane's scaled chord data.\n" 852 | " -i : you can optionally use the -i option to\n" 853 | " specify the input file out of order. This helps with\n" 854 | " build script construction.\n" 855 | " -o : you can optionally use the -o option to\n" 856 | " specify the output file out of order. This helps with\n" 857 | " build script construction.\n", 858 | progname, progname); 859 | } 860 | 861 | int 862 | main(int argc, char *argv[]) 863 | { 864 | afl_t *afl; 865 | afl_t *xfoil; 866 | int opt; 867 | bool result; 868 | const char *infoil = NULL, *outfoil = NULL; 869 | const char *progname = argv[0]; 870 | double chord = 1; 871 | 872 | while ((opt = getopt(argc, argv, "hse:i:o:c:")) != -1) { 873 | switch (opt) { 874 | case 'h': 875 | print_usage(stdout, argv[0]); 876 | return (0); 877 | case 's': 878 | do_smooth_polars = false; 879 | break; 880 | case 'e': 881 | edge_blend_range = atof(optarg); 882 | if (edge_blend_range < 0.1) { 883 | fprintf(stderr, "Invalid edge blend range, " 884 | "must be greater than or equal to 0.1\n"); 885 | return (1); 886 | } 887 | break; 888 | case 'i': 889 | infoil = optarg; 890 | break; 891 | case 'o': 892 | outfoil = optarg; 893 | break; 894 | case 'c': 895 | chord = atof(optarg); 896 | if (chord <= 0) { 897 | fprintf(stderr, "Invalid chord length. " 898 | "Must be a positive number.\n"); 899 | return (1); 900 | } 901 | break; 902 | default: 903 | print_usage(stderr, argv[0]); 904 | return (1); 905 | } 906 | } 907 | 908 | argc -= optind - 1; 909 | argv += optind - 1; 910 | 911 | if (infoil == NULL) { 912 | if (argc < 1) 913 | goto missing_args; 914 | infoil = argv[1]; 915 | argv++; 916 | argc--; 917 | } 918 | if (outfoil == NULL) { 919 | if (argc < 1) 920 | goto missing_args; 921 | outfoil = argv[1]; 922 | argv++; 923 | argc--; 924 | } 925 | 926 | afl = afl_parse(infoil); 927 | if (afl == NULL) 928 | return (1); 929 | 930 | xfoil = afl_alloc(); 931 | for (int i = 1; i < argc; i++) { 932 | if (!xfoil_polar_parse(xfoil, argv[i])) 933 | return (1); 934 | } 935 | result = afl_combine(afl, xfoil); 936 | afl_write(afl, outfoil, chord); 937 | 938 | afl_free(afl); 939 | afl_free(xfoil); 940 | 941 | return (result ? 0 : 1); 942 | missing_args: 943 | fprintf(stderr, "Missing arguments. Try \"%s -h\" for help.\n", 944 | progname); 945 | return (1); 946 | } 947 | -------------------------------------------------------------------------------- /safe_alloc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CDDL HEADER START 3 | * 4 | * This file and its contents are supplied under the terms of the 5 | * Common Development and Distribution License ("CDDL"), version 1.0. 6 | * You may only use this file in accordance with the terms of version 7 | * 1.0 of the CDDL. 8 | * 9 | * A full copy of the text of the CDDL should have accompanied this 10 | * source. A copy of the CDDL is also available via the Internet at 11 | * http://www.illumos.org/license/CDDL. 12 | * 13 | * CDDL HEADER END 14 | */ 15 | /* 16 | * Copyright 2019 Saso Kiselkov. All rights reserved. 17 | */ 18 | 19 | #ifndef _SAFE_ALLOC_H_ 20 | #define _SAFE_ALLOC_H_ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | static inline void * 31 | safe_malloc(size_t size) 32 | { 33 | void *p = malloc(size); 34 | if (size > 0) 35 | assert(p != NULL); 36 | return (p); 37 | } 38 | 39 | static inline void * 40 | safe_calloc(size_t nmemb, size_t size) 41 | { 42 | void *p = calloc(nmemb, size); 43 | if (nmemb > 0 && size > 0) 44 | assert(p != NULL); 45 | return (p); 46 | } 47 | 48 | static inline void * 49 | safe_realloc(void *oldptr, size_t size) 50 | { 51 | void *p = realloc(oldptr, size); 52 | if (size > 0) 53 | assert(p != NULL); 54 | return (p); 55 | } 56 | 57 | static inline char * 58 | safe_strdup(const char *str2) 59 | { 60 | char *str = strdup(str2); 61 | if (str2 != NULL) 62 | assert(str != NULL); 63 | return (str); 64 | } 65 | 66 | #ifdef __cplusplus 67 | } 68 | #endif 69 | 70 | #endif /* _SAFE_ALLOC_H_ */ 71 | --------------------------------------------------------------------------------