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