VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/table/avl_Base.cpp.h

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 17.3 KB
Line 
1/* $Id: avl_Base.cpp.h 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * kAVLBase - basic routines for all AVL trees.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37#ifndef _kAVLBase_h_
38#define _kAVLBase_h_
39
40
41/** @page pg_rt_kAVL kAVL Template configuration.
42 * @internal
43 *
44 * This is a template made to implement multiple AVL trees. The differences
45 * among the implementations are related to the key used.
46 *
47 * \#define KAVL_FN
48 * Use this to alter the names of the AVL functions.
49 * Must be defined.
50 *
51 * \#define KAVL_EQUAL_ALLOWED
52 * Define this to tell us that equal keys are allowed.
53 * Then Equal keys will be put in a list pointed to by pList in the KAVLNODECORE.
54 * This is by default not defined.
55 *
56 * \#define KAVL_CHECK_FOR_EQUAL_INSERT
57 * Define this to enable insert check for equal nodes.
58 * This is by default not defined.
59 *
60 * \#define KAVL_MAX_STACK
61 * Use this to specify the number of stack entries the stack will use when inserting
62 * and removing nodes from the tree. I think the size should be about
63 * log2(<max nodes>) + 3
64 * Must be defined.
65 *
66 */
67
68/*******************************************************************************
69* Defined Constants And Macros *
70*******************************************************************************/
71#define AVL_HEIGHTOF(pNode) ((unsigned char)((pNode) != NULL ? pNode->uchHeight : 0))
72
73/** @def KAVL_GET_POINTER
74 * Reads a 'pointer' value.
75 *
76 * @returns The native pointer.
77 * @param pp Pointer to the pointer to read.
78 */
79
80/** @def KAVL_GET_POINTER_NULL
81 * Reads a 'pointer' value which can be KAVL_NULL.
82 *
83 * @returns The native pointer.
84 * @returns NULL pointer if KAVL_NULL.
85 * @param pp Pointer to the pointer to read.
86 */
87
88/** @def KAVL_SET_POINTER
89 * Writes a 'pointer' value.
90 * For offset-based schemes offset relative to pp is calculated and assigned to *pp.
91 *
92 * @returns stored pointer.
93 * @param pp Pointer to where to store the pointer.
94 * @param p Native pointer to assign to *pp.
95 */
96
97/** @def KAVL_SET_POINTER_NULL
98 * Writes a 'pointer' value which can be KAVL_NULL.
99 *
100 * For offset-based schemes offset relative to pp is calculated and assigned to *pp,
101 * if p is not KAVL_NULL of course.
102 *
103 * @returns stored pointer.
104 * @param pp Pointer to where to store the pointer.
105 * @param pp2 Pointer to where to pointer to assign to pp. This can be KAVL_NULL
106 */
107
108#ifndef KAVL_GET_POINTER
109# ifdef KAVL_OFFSET
110# define KAVL_GET_POINTER(pp) ( (PKAVLNODECORE)((intptr_t)(pp) + *(pp)) )
111# define KAVL_GET_POINTER_NULL(pp) ( *(pp) != KAVL_NULL ? KAVL_GET_POINTER(pp) : NULL )
112# define KAVL_SET_POINTER(pp, p) ( (*(pp)) = ((intptr_t)(p) - (intptr_t)(pp)) )
113# define KAVL_SET_POINTER_NULL(pp, pp2) ( (*(pp)) = *(pp2) != KAVL_NULL ? (intptr_t)KAVL_GET_POINTER(pp2) - (intptr_t)(pp) : KAVL_NULL )
114# else
115# define KAVL_GET_POINTER(pp) ( *(pp) )
116# define KAVL_GET_POINTER_NULL(pp) ( *(pp) )
117# define KAVL_SET_POINTER(pp, p) ( (*(pp)) = (p) )
118# define KAVL_SET_POINTER_NULL(pp, pp2) ( (*(pp)) = *(pp2) )
119# endif
120#endif
121
122
123/** @def KAVL_NULL
124 * The NULL 'pointer' equivalent.
125 */
126#ifndef KAVL_NULL
127# ifdef KAVL_OFFSET
128# define KAVL_NULL 0
129# else
130# define KAVL_NULL NULL
131# endif
132#endif
133
134#ifndef KAVL_RANGE
135# define KAVL_R_IS_INTERSECTING(key1B, key2B, key1E, key2E) KAVL_E(key1B, key2B)
136# define KAVL_R_IS_IDENTICAL(key1B, key2B, key1E, key2E) KAVL_E(key1B, key2B)
137#endif
138
139/** @def KAVL_DECL
140 * Function declation macro in the RTDECL tradition.
141 * @param a_Type The function return type. */
142#ifndef KAVL_DECL
143# define KAVL_DECL(a_Type) RTDECL(a_Type)
144#endif
145
146
147/*******************************************************************************
148* Structures and Typedefs *
149*******************************************************************************/
150/*
151 * A stack used to avoid recursive calls...
152 */
153typedef struct _kAvlStack
154{
155 unsigned cEntries;
156 PPKAVLNODECORE aEntries[KAVL_MAX_STACK];
157} KAVLSTACK, *PKAVLSTACK;
158
159typedef struct _kAvlStack2
160{
161 unsigned cEntries;
162 PKAVLNODECORE aEntries[KAVL_MAX_STACK];
163 char achFlags[KAVL_MAX_STACK];
164} KAVLSTACK2, *PLAVLSTACK2;
165
166
167
168/**
169 * Rewinds a stack of pointers to pointers to nodes, rebalancing the tree.
170 * @param pStack Pointer to stack to rewind.
171 * @sketch LOOP thru all stack entries
172 * BEGIN
173 * Get pointer to pointer to node (and pointer to node) from the stack.
174 * IF 2 higher left subtree than in right subtree THEN
175 * BEGIN
176 * IF higher (or equal) left-sub-subtree than right-sub-subtree THEN
177 * * n+2|n+3
178 * / \ / \
179 * n+2 n ==> n+1 n+1|n+2
180 * / \ / \
181 * n+1 n|n+1 n|n+1 n
182 *
183 * Or with keys:
184 *
185 * 4 2
186 * / \ / \
187 * 2 5 ==> 1 4
188 * / \ / \
189 * 1 3 3 5
190 *
191 * ELSE
192 * * n+2
193 * / \ / \
194 * n+2 n n+1 n+1
195 * / \ ==> / \ / \
196 * n n+1 n L R n
197 * / \
198 * L R
199 *
200 * Or with keys:
201 * 6 4
202 * / \ / \
203 * 2 7 ==> 2 6
204 * / \ / \ / \
205 * 1 4 1 3 5 7
206 * / \
207 * 3 5
208 * END
209 * ELSE IF 2 higher in right subtree than in left subtree THEN
210 * BEGIN
211 * Same as above but left <==> right. (invert the picture)
212 * ELSE
213 * IF correct height THEN break
214 * ELSE correct height.
215 * END
216 */
217DECLINLINE(void) KAVL_FN(Rebalance)(PKAVLSTACK pStack)
218{
219 while (pStack->cEntries > 0)
220 {
221 /** @todo Perhaps some of these KAVL_SET_POINTER_NULL() cases could be optimized away.. */
222 PPKAVLNODECORE ppNode = pStack->aEntries[--pStack->cEntries];
223 PKAVLNODECORE pNode = KAVL_GET_POINTER(ppNode);
224 PKAVLNODECORE pLeftNode = KAVL_GET_POINTER_NULL(&pNode->pLeft);
225 unsigned char uchLeftHeight = AVL_HEIGHTOF(pLeftNode);
226 PKAVLNODECORE pRightNode = KAVL_GET_POINTER_NULL(&pNode->pRight);
227 unsigned char uchRightHeight = AVL_HEIGHTOF(pRightNode);
228
229 if (uchRightHeight + 1 < uchLeftHeight)
230 {
231 PKAVLNODECORE pLeftLeftNode = KAVL_GET_POINTER_NULL(&pLeftNode->pLeft);
232 PKAVLNODECORE pLeftRightNode = KAVL_GET_POINTER_NULL(&pLeftNode->pRight);
233 unsigned char uchLeftRightHeight = AVL_HEIGHTOF(pLeftRightNode);
234
235 if (AVL_HEIGHTOF(pLeftLeftNode) >= uchLeftRightHeight)
236 {
237 KAVL_SET_POINTER_NULL(&pNode->pLeft, &pLeftNode->pRight);
238 KAVL_SET_POINTER(&pLeftNode->pRight, pNode);
239 pLeftNode->uchHeight = (unsigned char)(1 + (pNode->uchHeight = (unsigned char)(1 + uchLeftRightHeight)));
240 KAVL_SET_POINTER(ppNode, pLeftNode);
241 }
242 else
243 {
244 KAVL_SET_POINTER_NULL(&pLeftNode->pRight, &pLeftRightNode->pLeft);
245 KAVL_SET_POINTER_NULL(&pNode->pLeft, &pLeftRightNode->pRight);
246 KAVL_SET_POINTER(&pLeftRightNode->pLeft, pLeftNode);
247 KAVL_SET_POINTER(&pLeftRightNode->pRight, pNode);
248 pLeftNode->uchHeight = pNode->uchHeight = uchLeftRightHeight;
249 pLeftRightNode->uchHeight = uchLeftHeight;
250 KAVL_SET_POINTER(ppNode, pLeftRightNode);
251 }
252 }
253 else if (uchLeftHeight + 1 < uchRightHeight)
254 {
255 PKAVLNODECORE pRightLeftNode = KAVL_GET_POINTER_NULL(&pRightNode->pLeft);
256 unsigned char uchRightLeftHeight = AVL_HEIGHTOF(pRightLeftNode);
257 PKAVLNODECORE pRightRightNode = KAVL_GET_POINTER_NULL(&pRightNode->pRight);
258
259 if (AVL_HEIGHTOF(pRightRightNode) >= uchRightLeftHeight)
260 {
261 KAVL_SET_POINTER_NULL(&pNode->pRight, &pRightNode->pLeft);
262 KAVL_SET_POINTER(&pRightNode->pLeft, pNode);
263 pRightNode->uchHeight = (unsigned char)(1 + (pNode->uchHeight = (unsigned char)(1 + uchRightLeftHeight)));
264 KAVL_SET_POINTER(ppNode, pRightNode);
265 }
266 else
267 {
268 KAVL_SET_POINTER_NULL(&pRightNode->pLeft, &pRightLeftNode->pRight);
269 KAVL_SET_POINTER_NULL(&pNode->pRight, &pRightLeftNode->pLeft);
270 KAVL_SET_POINTER(&pRightLeftNode->pRight, pRightNode);
271 KAVL_SET_POINTER(&pRightLeftNode->pLeft, pNode);
272 pRightNode->uchHeight = pNode->uchHeight = uchRightLeftHeight;
273 pRightLeftNode->uchHeight = uchRightHeight;
274 KAVL_SET_POINTER(ppNode, pRightLeftNode);
275 }
276 }
277 else
278 {
279 unsigned char uchHeight = (unsigned char)(KMAX(uchLeftHeight, uchRightHeight) + 1);
280 if (uchHeight == pNode->uchHeight)
281 break;
282 pNode->uchHeight = uchHeight;
283 }
284 }
285
286}
287
288
289
290
291/**
292 * Inserts a node into the AVL-tree.
293 * @returns TRUE if inserted.
294 * FALSE if node exists in tree.
295 * @param ppTree Pointer to the AVL-tree root node pointer.
296 * @param pNode Pointer to the node which is to be added.
297 * @sketch Find the location of the node (using binary tree algorithm.):
298 * LOOP until KAVL_NULL leaf pointer
299 * BEGIN
300 * Add node pointer pointer to the AVL-stack.
301 * IF new-node-key < node key THEN
302 * left
303 * ELSE
304 * right
305 * END
306 * Fill in leaf node and insert it.
307 * Rebalance the tree.
308 */
309KAVL_DECL(bool) KAVL_FN(Insert)(PPKAVLNODECORE ppTree, PKAVLNODECORE pNode)
310{
311 KAVLSTACK AVLStack;
312 PPKAVLNODECORE ppCurNode = ppTree;
313 PKAVLNODECORE pCurNode;
314 KAVLKEY Key = pNode->Key; NOREF(Key);
315#ifdef KAVL_RANGE
316 KAVLKEY KeyLast = pNode->KeyLast; NOREF(KeyLast);
317#endif
318
319 AVLStack.cEntries = 0;
320
321#ifdef KAVL_RANGE
322 if (Key > KeyLast)
323 return false;
324#endif
325
326 for (;;)
327 {
328 if (*ppCurNode != KAVL_NULL)
329 pCurNode = KAVL_GET_POINTER(ppCurNode);
330 else
331 break;
332 Assert(pCurNode->uchHeight == RT_MAX(AVL_HEIGHTOF(KAVL_GET_POINTER_NULL(&pCurNode->pLeft)),
333 AVL_HEIGHTOF(KAVL_GET_POINTER_NULL(&pCurNode->pRight))) + 1);
334
335 kASSERT(AVLStack.cEntries < KAVL_MAX_STACK);
336 AVLStack.aEntries[AVLStack.cEntries++] = ppCurNode;
337#ifdef KAVL_EQUAL_ALLOWED
338 if (KAVL_R_IS_IDENTICAL(pCurNode->Key, Key, pCurNode->KeyLast, KeyLast))
339 {
340 /*
341 * If equal then we'll use a list of equal nodes.
342 */
343 pNode->pLeft = pNode->pRight = KAVL_NULL;
344 pNode->uchHeight = 0;
345 KAVL_SET_POINTER_NULL(&pNode->pList, &pCurNode->pList);
346 KAVL_SET_POINTER(&pCurNode->pList, pNode);
347 return true;
348 }
349#endif
350#ifdef KAVL_CHECK_FOR_EQUAL_INSERT
351 if (KAVL_R_IS_INTERSECTING(pCurNode->Key, Key, pCurNode->KeyLast, KeyLast))
352 return false;
353#endif
354 if (KAVL_G(pCurNode->Key, Key))
355 ppCurNode = &pCurNode->pLeft;
356 else
357 ppCurNode = &pCurNode->pRight;
358 }
359
360 pNode->pLeft = pNode->pRight = KAVL_NULL;
361#ifdef KAVL_EQUAL_ALLOWED
362 pNode->pList = KAVL_NULL;
363#endif
364 pNode->uchHeight = 1;
365 KAVL_SET_POINTER(ppCurNode, pNode);
366
367 KAVL_FN(Rebalance)(SSToDS(&AVLStack));
368 return true;
369}
370
371
372/**
373 * Removes a node from the AVL-tree.
374 * @returns Pointer to the node.
375 * @param ppTree Pointer to the AVL-tree root node pointer.
376 * @param Key Key value of the node which is to be removed.
377 * @sketch Find the node which is to be removed:
378 * LOOP until not found
379 * BEGIN
380 * Add node pointer pointer to the AVL-stack.
381 * IF the keys matches THEN break!
382 * IF remove key < node key THEN
383 * left
384 * ELSE
385 * right
386 * END
387 * IF found THEN
388 * BEGIN
389 * IF left node not empty THEN
390 * BEGIN
391 * Find the right most node in the left tree while adding the pointer to the pointer to it's parent to the stack:
392 * Start at left node.
393 * LOOP until right node is empty
394 * BEGIN
395 * Add to stack.
396 * go right.
397 * END
398 * Link out the found node.
399 * Replace the node which is to be removed with the found node.
400 * Correct the stack entry for the pointer to the left tree.
401 * END
402 * ELSE
403 * BEGIN
404 * Move up right node.
405 * Remove last stack entry.
406 * END
407 * Balance tree using stack.
408 * END
409 * return pointer to the removed node (if found).
410 */
411KAVL_DECL(PKAVLNODECORE) KAVL_FN(Remove)(PPKAVLNODECORE ppTree, KAVLKEY Key)
412{
413 KAVLSTACK AVLStack;
414 PPKAVLNODECORE ppDeleteNode = ppTree;
415 PKAVLNODECORE pDeleteNode;
416
417 AVLStack.cEntries = 0;
418
419 for (;;)
420 {
421 if (*ppDeleteNode != KAVL_NULL)
422 pDeleteNode = KAVL_GET_POINTER(ppDeleteNode);
423 else
424 return NULL;
425 Assert(pDeleteNode->uchHeight == RT_MAX(AVL_HEIGHTOF(KAVL_GET_POINTER_NULL(&pDeleteNode->pLeft)),
426 AVL_HEIGHTOF(KAVL_GET_POINTER_NULL(&pDeleteNode->pRight))) + 1);
427
428 kASSERT(AVLStack.cEntries < KAVL_MAX_STACK);
429 AVLStack.aEntries[AVLStack.cEntries++] = ppDeleteNode;
430 if (KAVL_E(pDeleteNode->Key, Key))
431 break;
432
433 if (KAVL_G(pDeleteNode->Key, Key))
434 ppDeleteNode = &pDeleteNode->pLeft;
435 else
436 ppDeleteNode = &pDeleteNode->pRight;
437 }
438
439 if (pDeleteNode->pLeft != KAVL_NULL)
440 {
441 /* find the rightmost node in the left tree. */
442 const unsigned iStackEntry = AVLStack.cEntries;
443 PPKAVLNODECORE ppLeftLeast = &pDeleteNode->pLeft;
444 PKAVLNODECORE pLeftLeast = KAVL_GET_POINTER(ppLeftLeast);
445
446 while (pLeftLeast->pRight != KAVL_NULL)
447 {
448 kASSERT(AVLStack.cEntries < KAVL_MAX_STACK);
449 AVLStack.aEntries[AVLStack.cEntries++] = ppLeftLeast;
450 ppLeftLeast = &pLeftLeast->pRight;
451 pLeftLeast = KAVL_GET_POINTER(ppLeftLeast);
452 }
453
454 /* link out pLeftLeast */
455 KAVL_SET_POINTER_NULL(ppLeftLeast, &pLeftLeast->pLeft);
456
457 /* link it in place of the delete node. */
458 KAVL_SET_POINTER_NULL(&pLeftLeast->pLeft, &pDeleteNode->pLeft);
459 KAVL_SET_POINTER_NULL(&pLeftLeast->pRight, &pDeleteNode->pRight);
460 pLeftLeast->uchHeight = pDeleteNode->uchHeight;
461 KAVL_SET_POINTER(ppDeleteNode, pLeftLeast);
462 AVLStack.aEntries[iStackEntry] = &pLeftLeast->pLeft;
463 }
464 else
465 {
466 KAVL_SET_POINTER_NULL(ppDeleteNode, &pDeleteNode->pRight);
467 AVLStack.cEntries--;
468 }
469
470 KAVL_FN(Rebalance)(SSToDS(&AVLStack));
471 return pDeleteNode;
472}
473
474#endif
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use