1 .\"
   2 .\" This file and its contents are supplied under the terms of the
   3 .\" Common Development and Distribution License ("CDDL"), version 1.0.
   4 .\" You may only use this file in accordance with the terms of version
   5 .\" 1.0 of the CDDL.
   6 .\"
   7 .\" A full copy of the text of the CDDL should have accompanied this
   8 .\" source.  A copy of the CDDL is also available via the Internet at
   9 .\" http://www.illumos.org/license/CDDL.
  10 .\"
  11 .\"
  12 .\" Copyright 2015 Joyent, Inc.
  13 .\"
  14 .Dd May 07, 2015
  15 .Dt LIBAVL 3LIB
  16 .Os
  17 .Sh NAME
  18 .Nm libavl
  19 .Nd generic self-balancing binary search tree library
  20 .Sh SYNOPSIS
  21 .Lb libavl
  22 .In sys/avl.h
  23 .Sh DESCRIPTION
  24 The
  25 .Nm
  26 library provides a generic implementation of AVL trees, a form of
  27 self-balancing binary tree. The interfaces provided allow for an
  28 efficient way of implementing an ordered set of data structures and, due
  29 to its embeddable nature, allow for a single instance of a data
  30 structure to belong to multiple AVL trees.
  31 .Lp
  32 Each AVL tree contains entries of a single type of data structure.
  33 Rather than allocating memory for pointers for those data structures,
  34 the storage for the tree is embedded into the data structures by
  35 declaring a member of type
  36 .Vt avl_node_t .
  37 When an AVL tree is created, through the use of
  38 .Fn avl_create ,
  39 it encodes the size of the data structure, the offset of the data
  40 structure, and a comparator function which is used to compare two
  41 instances of a data structure. A data structure may be a member of
  42 multiple AVL trees by creating AVL trees which use different
  43 offsets (different members) into the data structure.
  44 .Lp
  45 AVL trees support both look up of an arbitrary item and ordered
  46 iteration over the contents of the entire tree. In addition, from any
  47 node, you can find the previous and next entries in the tree, if they
  48 exist. In addition, AVL trees support arbitrary insertion and deletion.
  49 .Ss Performance
  50 AVL trees are often used in place of linked lists. Compared to the
  51 standard, intrusive, doubly linked list, it has the following
  52 performance characteristics:
  53 .Bl -hang -width Ds
  54 .It Sy Lookup One Node
  55 .Bd -filled -compact
  56 Lookup of a single node in a linked list is
  57 .Sy O(n) ,
  58 whereas lookup of a single node in an AVL tree is
  59 .Sy O(log(n)) .
  60 .Ed
  61 .It Sy Insert One Node
  62 .Bd -filled -compact
  63 Inserting a single node into a linked list is
  64 .Sy O(1) .
  65 Inserting a single node into an AVL tree is
  66 .Sy O(log(n)) .
  67 .Pp
  68 Note, insertions into an AVL tree always result in an ordered tree.
  69 Insertions into a linked list do not guarantee order. If order is
  70 required, then the time to do the insertion into a linked list will
  71 depend on the time of the search algorithm being employed to find the
  72 place to insert at.
  73 .Ed
  74 .It Sy Delete One Node
  75 .Bd -filled -compact
  76 Deleting a single node from a linked list is
  77 .Sy O(1),
  78 whereas deleting a single node from an AVL tree takes
  79 .Sy O(log(n))
  80 time.
  81 .Ed
  82 .It Sy Delete All Nodes
  83 .Bd -filled -compact
  84 Deleting all nodes from a linked list is
  85 .Sy O(n) .
  86 With an AVL tree, if using the
  87 .Xr avl_delete_nodes 3AVL
  88 function then deleting all nodes
  89 is
  90 .Sy O(n) .
  91 However, if iterating over each entry in the tree and then removing it using
  92 a while loop,
  93 .Xr avl_first 3AVL
  94 and
  95 .Xr avl_remove 3AVL
  96 then the time to remove all nodes is
  97 .Sy O(n\ *\ log(n)).
  98 .Ed
  99 .It Sy Visit the Next or Previous Node
 100 .Bd -filled -compact
 101 Visiting the next or previous node in a linked list is
 102 .Sy O(1) ,
 103 whereas going from the next to the previous node in an AVL tree will
 104 take between
 105 .Sy O(1)
 106 and
 107 .Sy O(log(n)) .
 108 .Ed
 109 .El
 110 .Pp
 111 In general, AVL trees are a good alternative for linked lists when order
 112 or lookup speed is important and a reasonable number of items will be
 113 present.
 114 .Sh INTERFACES
 115 The shared object
 116 .Sy libavl.so.1
 117 provides the public interfaces defined below. See
 118 .Xr Intro(3)
 119 for additional information on shared object interfaces. Individual
 120 functions are documented in their own manual pages.
 121 .Bl -column -offset indent ".Sy avl_is_empty" ".Sy avl_destroy_nodes"
 122 .It Sy avl_add Ta Sy avl_create
 123 .It Sy avl_destroy Ta Sy avl_destroy_nodes
 124 .It Sy avl_find Ta Sy avl_first
 125 .It Sy avl_insert Ta Sy avl_insert_here
 126 .It Sy avl_is_empty Ta Sy avl_last
 127 .It Sy avl_nearest Ta Sy avl_numnodes
 128 .It Sy avl_remove Ta Sy avl_swap
 129 .El
 130 .Pp
 131 In addition, the library defines C pre-processor macros which are
 132 defined below and documented in their own manual pages.
 133 .\"
 134 .\" Use the same column widths in both cases where we describe the list
 135 .\" of interfaces, to allow the manual page to better line up when rendered.
 136 .\"
 137 .Bl -column -offset indent ".Sy avl_is_empty" ".Sy avl_destroy_nodes"
 138 .It Sy AVL_NEXT Ta Sy AVL_PREV
 139 .El
 140 .Sh TYPES
 141 The
 142 .Nm
 143 library defines the following types:
 144 .Lp
 145 .Sy avl_tree_t
 146 .Lp
 147 Type used for the root of the AVL tree. Consumers define one of these
 148 for each of the different trees that they want to have.
 149 .Lp
 150 .Sy avl_node_t
 151 .Lp
 152 Type used as the data node for an AVL tree. One of these is embedded in
 153 each data structure that is the member of an AVL tree.
 154 .Lp
 155 .Sy avl_index_t
 156 .Lp
 157 Type used to locate a position in a tree. This is used with
 158 .Xr avl_find 3AVL
 159 and
 160 .Xr avl_insert 3AVL .
 161 .Sh LOCKING
 162 The
 163 .Nm
 164 library provides no locking. Callers that are using the same AVL tree
 165 from multiple threads need to provide their own synchronization. If only
 166 one thread is ever accessing or modifying the AVL tree, then there are
 167 no synchronization concerns. If multiple AVL trees exist, then they may
 168 all be used simultaneously; however, they are subject to the same rules
 169 around simultaneous access from a single thread.
 170 .Lp
 171 All routines are both
 172 .Sy Fork-safe
 173 and
 174 .Sy Async-Signal-Safe .
 175 Callers may call functions in
 176 .Nm
 177 from a signal handler and
 178 .Nm
 179 calls are all safe in face of
 180 .Xr fork 2 ;
 181 however, if callers have their own locks, then they must make sure that
 182 they are accounted for by the use of routines such as
 183 .Xr pthread_atfork 3C .
 184 .Sh EXAMPLES
 185 The following code shows examples of exercising all of the functionality
 186 that is present in
 187 .Nm .
 188 It can be compiled by using a C compiler and linking
 189 against
 190 .Nm .
 191 For example, given a file named avl.c, with gcc, one would run:
 192 .Bd -literal
 193 $ gcc -Wall -o avl avl.c -lavl
 194 .Ed
 195 .Bd -literal
 196 /*
 197  * Example of using AVL Trees
 198  */
 199 
 200 #include <sys/avl.h>
 201 #include <stddef.h>
 202 #include <stdlib.h>
 203 #include <stdio.h>
 204 
 205 static avl_tree_t inttree;
 206 
 207 /*
 208  * The structure that we're storing in an AVL tree.
 209  */
 210 typedef struct intnode {
 211         int in_val;
 212         avl_node_t in_avl;
 213 } intnode_t;
 214 
 215 static int
 216 intnode_comparator(const void *l, const void *r)
 217 {
 218         const intnode_t *li = l;
 219         const intnode_t *ri = r;
 220 
 221         if (li->in_val > ri->in_val)
 222                 return (1);
 223         if (li->in_val < ri->in_val)
 224                 return (-1);
 225         return (0);
 226 }
 227 
 228 /*
 229  * Create an AVL Tree
 230  */
 231 static void
 232 create_avl(void)
 233 {
 234         avl_create(&inttree, intnode_comparator, sizeof (intnode_t),
 235             offsetof(intnode_t, in_avl));
 236 }
 237 
 238 /*
 239  * Add entries to the tree with the avl_add function.
 240  */
 241 static void
 242 fill_avl(void)
 243 {
 244         int i;
 245         intnode_t *inp;
 246 
 247         for (i = 0; i < 20; i++) {
 248                 inp = malloc(sizeof (intnode_t));
 249                 assert(inp != NULL);
 250                 inp->in_val = i;
 251                 avl_add(&inttree, inp);
 252         }
 253 }
 254 
 255 /*
 256  * Find entries in the AVL tree. Note, we create an intnode_t on the
 257  * stack that we use to look this up.
 258  */
 259 static void
 260 find_avl(void)
 261 {
 262         int i;
 263         intnode_t lookup, *inp;
 264 
 265         for (i = 10; i < 30; i++) {
 266                 lookup.in_val = i;
 267                 inp = avl_find(&inttree, &lookup, NULL);
 268                 if (inp == NULL) {
 269                         printf("Entry %d is not in the tree\\n", i);
 270                 } else {
 271                         printf("Entry %d is in the tree\\n",
 272                             inp->in_val);
 273                 }
 274         }
 275 }
 276 
 277 /*
 278  * Walk the tree forwards
 279  */
 280 static void
 281 walk_forwards(void)
 282 {
 283         intnode_t *inp;
 284         for (inp = avl_first(&inttree); inp != NULL;
 285             inp = AVL_NEXT(&inttree, inp)) {
 286                 printf("Found entry %d\\n", inp->in_val);
 287         }
 288 }
 289 
 290 /*
 291  * Walk the tree backwards.
 292  */
 293 static void
 294 walk_backwards(void)
 295 {
 296         intnode_t *inp;
 297         for (inp = avl_last(&inttree); inp != NULL;
 298             inp = AVL_PREV(&inttree, inp)) {
 299                 printf("Found entry %d\\n", inp->in_val);
 300         }
 301 }
 302 
 303 /*
 304  * Determine the number of nodes in the tree and if it is empty or
 305  * not.
 306  */
 307 static void
 308 inttree_inspect(void)
 309 {
 310         printf("The tree is %s, there are %ld nodes in it\\n",
 311             avl_is_empty(&inttree) == B_TRUE ? "empty" : "not empty",
 312             avl_numnodes(&inttree));
 313 }
 314 
 315 /*
 316  * Use avl_remove to remove entries from the tree.
 317  */
 318 static void
 319 remove_nodes(void)
 320 {
 321         int i;
 322         intnode_t lookup, *inp;
 323 
 324         for (i = 0; i < 20; i+= 4) {
 325                 lookup.in_val = i;
 326                 inp = avl_find(&inttree, &lookup, NULL);
 327                 if (inp != NULL)
 328                         avl_remove(&inttree, inp);
 329         }
 330 }
 331 
 332 /*
 333  * Find the nearest nodes in the tree.
 334  */
 335 static void
 336 nearest_nodes(void)
 337 {
 338         intnode_t lookup, *inp;
 339         avl_index_t where;
 340 
 341         lookup.in_val = 12;
 342         if (avl_find(&inttree, &lookup, &where) != NULL)
 343                 abort();
 344         inp = avl_nearest(&inttree, where, AVL_BEFORE);
 345         assert(inp != NULL);
 346         printf("closest node before 12 is: %d\\n", inp->in_val);
 347         inp = avl_nearest(&inttree, where, AVL_AFTER);
 348         assert(inp != NULL);
 349         printf("closest node after 12 is: %d\\n", inp->in_val);
 350 }
 351 
 352 static void
 353 insert_avl(void)
 354 {
 355         intnode_t lookup, *inp;
 356         avl_index_t where;
 357 
 358         lookup.in_val = 12;
 359         if (avl_find(&inttree, &lookup, &where) != NULL)
 360                 abort();
 361         inp = malloc(sizeof (intnode_t));
 362         assert(inp != NULL);
 363         avl_insert(&inttree, inp, where);
 364 }
 365 
 366 static void
 367 swap_avl(void)
 368 {
 369         avl_tree_t swap;
 370 
 371         avl_create(&swap, intnode_comparator, sizeof (intnode_t),
 372             offsetof(intnode_t, in_avl));
 373         avl_swap(&inttree, &swap);
 374         inttree_inspect();
 375         avl_swap(&inttree, &swap);
 376         inttree_inspect();
 377 }
 378 
 379 /*
 380  * Remove all remaining nodes in the tree. We first use
 381  * avl_destroy_nodes to empty the tree, then avl_destroy to finish.
 382  */
 383 static void
 384 cleanup(void)
 385 {
 386         intnode_t *inp;
 387         void *c = NULL;
 388 
 389         while ((inp = avl_destroy_nodes(&inttree, &c)) != NULL) {
 390                 free(inp);
 391         }
 392         avl_destroy(&inttree);
 393 }
 394 
 395 int
 396 main(void)
 397 {
 398         create_avl();
 399         inttree_inspect();
 400         fill_avl();
 401         find_avl();
 402         walk_forwards();
 403         walk_backwards();
 404         inttree_inspect();
 405         remove_nodes();
 406         inttree_inspect();
 407         nearest_nodes();
 408         insert_avl();
 409         inttree_inspect();
 410         swap_avl();
 411         cleanup();
 412         return (0);
 413 }
 414 .Ed
 415 .Sh INTERFACE STABILITY
 416 .Sy Committed
 417 .Sh MT-Level
 418 See
 419 .Sx Locking.
 420 .Sh SEE ALSO
 421 .Xr Intro 3 ,
 422 .Xr pthread_atfork 3C
 423 .Lp
 424 .Xr avl_add 3AVL ,
 425 .Xr avl_create 3AVL ,
 426 .Xr avl_destroy 3AVL ,
 427 .Xr avl_destroy_nodes 3AVL ,
 428 .Xr avl_find 3AVL ,
 429 .Xr avl_first 3AVL ,
 430 .Xr avl_insert 3AVL ,
 431 .Xr avl_insert_here 3AVL ,
 432 .Xr avl_is_empty 3AVL ,
 433 .Xr avl_last 3AVL ,
 434 .Xr avl_nearest 3AVL ,
 435 .Xr avl_numnodes 3AVL ,
 436 .Xr avl_remove 3AVL ,
 437 .Xr avl_swap 3AVL ,
 438 .Rs
 439 .%A Adel'son-Vel'skiy, G. M.
 440 .%A Landis, Ye. M.
 441 .%T "An Algorithm for the Organization of Information"
 442 .%Q Deklady Akademii Nauk
 443 .%C USSR, Moscow
 444 .%P 263-266
 445 .%V Vol. 16
 446 .%N No. 2
 447 .%D 1962
 448 .Re