CookMem 1.4.0
A C++ memory context / allocator
Loading...
Searching...
No Matches
avltree.h
1/*
2 * Copyright (c) 2018-2026 Heng Yuan
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#ifndef COOK_PTR_AVLTREE_H
17#define COOK_PTR_AVLTREE_H
18
19#include <cstddef>
20#include <cstdint>
21#include <cstdio>
22
23#include "exception.h"
24
25namespace cookmem
26{
27
33{
34private:
38 struct Node
39 {
41 std::size_t reserved;
43 std::size_t size;
45 Node* left;
47 Node* right;
49 Node* next;
51 std::int16_t height;
52 };
53
59 struct RemoveStruct
60 {
62 std::size_t size;
64 Node* node;
65
66 RemoveStruct (std::size_t s)
67 : size(s), node(nullptr)
68 {
69 }
70 };
71
72public:
77 : m_root(nullptr)
78 {
79 }
80
85
96 void
97 add (void* ptr, std::size_t size)
98 {
99 Node* node = reinterpret_cast<Node*>(ptr);
100 node->size = size;
101
102 if (m_root == nullptr)
103 {
104 node->left = nullptr;
105 node->right = nullptr;
106 node->next = nullptr;
107 node->height = 1;
108 m_root = node;
109
110 return;
111 }
112
113 Node* root;
114 Node* stack[sizeof(Node*) * 8 + 1];
115 std::int16_t depth;
116
117 root = stack[0] = m_root;
118
119 for (depth = 0; ; ++depth)
120 {
121 if (root == nullptr)
122 {
123 setParent (node, stack[depth - 1]);
124 node->left = nullptr;
125 node->right = nullptr;
126 node->next = nullptr;
127 node->height = 1;
128
129 root = balance (stack, depth);
130 if (root != nullptr)
131 {
132 m_root = root;
133 }
134 return;
135 }
136
137 if (size < root->size)
138 {
139 root = stack[depth + 1] = root->left;
140 continue;
141 }
142 else if (size > root->size)
143 {
144 root = stack[depth + 1] = root->right;
145 continue;
146 }
147 else
148 {
149 // The current root matches the node's size. In this case we
150 // just add node to the next pointer of the root.
151 //
152 Node* next = root->next;
153 root->next = node;
154 node->next = next;
155 node->left = root;
156 node->height = -1; // indicating that this node is in a DLL
157 if (next)
158 {
159 next->left = node;
160 }
161
162 // We can simply return since the tree structure is not
163 // changed.
164 return;
165 }
166 }
167 throw Exception (MEM_ERROR_GENERAL, "Invalid PtrAVLTree.", __FILE__, __LINE__);
168 }
169
183 void*
184 remove (std::size_t& size, std::size_t limit = ~static_cast<std::size_t> (0))
185 {
186 Node* stack[sizeof(Node*) * 8 + 1];
187 Node* root = m_root;
188 Node* candidate = nullptr;
189 std::int16_t depth = 0;
190 std::int16_t candidateDepth = 0;
191
192 while (root != nullptr)
193 {
194 stack[depth] = root;
195
196 if (size <= root->size)
197 {
198 candidate = root;
200 root = root->left;
201 }
202 else
203 {
204 root = root->right;
205 }
206 ++depth;
207 }
208
209 if (candidate == nullptr || candidate->size >= limit)
210 {
211 return nullptr;
212 }
213
214 // The candidate is the smallest node whose size is at least the key.
215 if (candidate->next)
216 {
217 Node* next = candidate->next;
218 candidate->next = next->next;
219 if (candidate->next != nullptr)
220 {
221 candidate->next->left = candidate;
222 }
223
224 size = next->size;
225 return next;
226 }
227
228 size = candidate->size;
229 removeNode (candidate, stack, candidateDepth);
230 return candidate;
231 }
232
239 void
240 remove (void* ptr)
241 {
242 Node* node = reinterpret_cast<Node*>(ptr);
243
244 if (node->height == -1)
245 {
246 // this is a simple case that node is in a DLL
247 Node* prev = node->left;
248 Node* next = node->next;
249 prev->next = next;
250 if (next)
251 {
252 next->left = prev;
253 }
254 return;
255 }
256
257 // The node is not in a DLL. So this is a more complicate case that
258 // need to do the removal from the root (since there is a potential
259 // need to balance the tree from root.
260
261 Node* root;
262 Node* stack[sizeof(Node*) * 8 + 1];
263 std::int16_t depth;
264
265 std::size_t size = node->size;
266
267 COOKMEM_ASSERT(m_root != nullptr);
268
269 root = stack[0] = m_root;
270
271 for (depth = 0; ; ++depth)
272 {
273 if (root == nullptr)
274 {
275 throw Exception (MEM_ERROR_GENERAL, "pointer not found.", __FILE__, __LINE__);
276 }
277
278 if (size < root->size)
279 {
280 root = stack[depth + 1] = root->left;
281 continue;
282 }
283 else if (size > root->size)
284 {
285 root = stack[depth + 1] = root->right;
286 continue;
287 }
288
289 break;
290 }
291
292 COOKMEM_ASSERT(root == node);
293
294 if (root->next)
295 {
296 // Just use the next node to replace the current node.
297
298 Node* next = root->next;
299 next->left = root->left;
300 next->right = root->right;
301 next->height = root->height;
302
303 if (depth == 0)
304 {
305 m_root = next;
306 }
307 else
308 {
309 setParent (next, stack[depth - 1]);
310 }
311 return;
312 }
313
314 removeNode (root, stack, depth);
315 }
316
323 bool
324 contains (void* ptr)
325 {
326 if (m_root == nullptr)
327 {
328 return false;
329 }
330
331 Node* node = reinterpret_cast<Node*>(ptr);
332 const std::size_t size = node->size;
333
334 Node* root = m_root;
335 while (root != nullptr)
336 {
337 if (size < root->size)
338 {
339 root = root->left;
340 }
341 else if (size > root->size)
342 {
343 root = root->right;
344 }
345 else
346 {
347 do
348 {
349 if (root == node)
350 {
351 return true;
352 }
353 root = root->next;
354 }
355 while (root != nullptr);
356 }
357 }
358 return false;
359 }
360
372 bool
373 canGrowInPlace (void* ptr, std::size_t newSize)
374 {
375 Node* node = reinterpret_cast<Node*>(ptr);
376
377 // All nodes in a DLL must have the same size.
378 if (node->height == -1 || node->next != nullptr)
379 {
380 return false;
381 }
382
383 // Find the next larger node. There are no parent pointers, so
384 // search from the root and remember the last node where the search
385 // went left.
386 std::size_t size = node->size;
387 Node* nextLarger = nullptr;
388 Node* root = m_root;
389 while (root != node)
390 {
391 if (root == nullptr)
392 {
393 return false;
394 }
395 if (size < root->size)
396 {
398 root = root->left;
399 }
400 else
401 {
402 root = root->right;
403 }
404 }
405
406 if (node->right != nullptr)
407 {
408 nextLarger = node->right;
409 while (nextLarger->left != nullptr)
410 {
411 nextLarger = nextLarger->left;
412 }
413 }
414
415 return nextLarger == nullptr || newSize < nextLarger->size;
416 }
417
423 bool
425 {
426 return m_root == nullptr;
427 }
428
432 void
434 {
435 printf ("graph G {\n");
436 printNode (m_root);
437 printf ("}\n");
438 }
439private:
440 inline static std::int16_t
441 getMax (std::int16_t a, std::int16_t b)
442 {
443 return (a < b) ? b : a;
444 }
445
446 inline static std::int16_t
447 getHeight (const Node* node)
448 {
449 return node ? node->height : 0;
450 }
451
452 inline static void
453 updateHeight (Node* node)
454 {
455 node->height = getMax (getHeight (node->left), getHeight (node->right)) + 1;
456 }
457
458 inline static void
459 setParent (Node* node, Node* parent)
460 {
461 if (node->size < parent->size)
462 {
463 parent->left = node;
464 }
465 else
466 {
467 parent->right = node;
468 }
469 }
470
471 inline static void
472 setLeftChild (Node* root, Node* left)
473 {
474 root->left = left;
475 }
476
477 inline static void
478 setRightChild (Node* root, Node* right)
479 {
480 root->right = right;
481 }
482
483 inline static Node*
484 balance (Node** stack, std::int16_t depth)
485 {
486 for (;;)
487 {
488 --depth;
489 Node* root = stack[depth];
490 Node* left = root->left;
491 Node* right = root->right;
492
493 std::int16_t oldHeight = root->height;
494 int16_t diff = getHeight (left) - getHeight (right);
495 if (diff > 1)
496 {
497 if (getHeight (left->left) >= getHeight (left->right))
498 {
499 root = rotateWithLeftChild (root);
500 }
501 else
502 {
503 root = doubleWithLeftChild (root);
504 }
505 // update the parent
506 if (depth == 0)
507 {
508 return root;
509 }
510 setParent (root, stack[depth - 1]);
511 continue;
512 }
513 else if (diff < -1)
514 {
515 if (getHeight (right->right) >= getHeight (right->left))
516 {
517 root = rotateWithRightChild (root);
518 }
519 else
520 {
521 root = doubleWithRightChild (root);
522 }
523 // update the parent
524 if (depth == 0)
525 {
526 return root;
527 }
528 setParent (root, stack[depth - 1]);
529 continue;
530 }
531 else
532 {
533 updateHeight (root);
534 }
535
536 if (depth == 0)
537 {
538 return root;
539 }
540
541 if (oldHeight == root->height)
542 break;
543 }
544 return nullptr;
545 }
546
547 inline static Node*
548 rotateWithLeftChild (Node* root)
549 {
550 Node* left = root->left;
551 setLeftChild (root, left->right);
552 setRightChild (left, root);
553 updateHeight (root);
554 updateHeight (left);
555 return left;
556 }
557
558 inline static Node*
559 rotateWithRightChild (Node* root)
560 {
561 Node* right = root->right;
562 setRightChild (root, right->left);
563 setLeftChild (right, root);
564 updateHeight (root);
565 updateHeight (right);
566 return right;
567 }
568
569 inline static Node*
570 doubleWithLeftChild (Node* root)
571 {
572 setLeftChild (root, rotateWithRightChild (root->left));
573 return rotateWithLeftChild (root);
574 }
575
576 inline static Node*
577 doubleWithRightChild (Node* root)
578 {
579 setRightChild (root, rotateWithLeftChild (root->right));
580 return rotateWithRightChild (root);
581 }
582
583 static void
584 printNode (Node* root)
585 {
586 if (!root)
587 return;
588 printf ("%016zx -- {", (std::size_t)root);
589 if (root->left)
590 {
591 printf (" %016zx", (std::size_t)root->left);
592 }
593 if (root->right)
594 {
595 printf (" %016zx", (std::size_t)root->right);
596 }
597 printf (" }\n");
598 printf ("%016zx [ label = %zd ]\n",
599 (std::size_t)root,
600 root->size);
601 printNode (root->left);
602 printNode (root->right);
603 }
604
605 void
606 removeNode (Node* root, Node** stack, int depth)
607 {
608 Node* left = root->left;
609 Node* right = root->right;
610
611 Node* newRoot;
612 if (left == nullptr)
613 {
614 newRoot = right;
615 }
616 else if (right == nullptr)
617 {
618 newRoot = left;
619 }
620 else
621 {
622 // this is a more complicated case.
623 // the solution is the move the smallest of the right branch
624 // here.
625 std::int16_t newDepth;
626 Node* newStack[sizeof (Node*) * 8 + 1];
627 newStack[0] = right;
628 for (newDepth = 0; newStack[newDepth]->left != nullptr; ++newDepth)
629 {
630 newStack[newDepth + 1] = newStack[newDepth]->left;
631 }
632 Node* min = newStack[newDepth];
633 if (min == right)
634 {
635 newRoot = right;
636 setLeftChild (newRoot, left);
637 updateHeight (newRoot);
638 }
639 else
640 {
641 // first detach min from the parent
642 Node* parent = newStack[newDepth - 1];
643 parent->left = min->right;
644
645 // rebalance the right
646 Node* newRight = balance (newStack, newDepth);
647 if (newRight != nullptr)
648 {
649 right = newRight;
650 }
651
652 setLeftChild (min, left);
653 setRightChild (min, right);
654 updateHeight (min);
655
656 newRoot = min;
657 }
658 }
659
660 if (depth == 0)
661 {
662 m_root = newRoot;
663 if (newRoot != nullptr)
664 {
665 stack[0] = newRoot;
666 newRoot = balance (stack, 1);
667 if (newRoot != nullptr)
668 {
669 m_root = newRoot;
670 }
671 }
672 return;
673 }
674 if (newRoot == nullptr)
675 {
676 // so root is the leaf.
677 if (root->size < stack[depth - 1]->size)
678 {
679 stack[depth - 1]->left = nullptr;
680 }
681 else
682 {
683 stack[depth - 1]->right = nullptr;
684 }
685 newRoot = balance (stack, depth);
686 if (newRoot != nullptr)
687 {
688 m_root = newRoot;
689 }
690 return;
691 }
692
693 setParent (newRoot, stack[depth - 1]);
694
695 stack[depth] = newRoot;
696 newRoot = balance (stack, depth + 1);
697
698 if (newRoot != nullptr)
699 {
700 m_root = newRoot;
701 }
702 }
703
704private:
705 // node entries
706 Node* m_root;
707};
708
709} // namespace cookmem
710
711#endif // COOK_PTR_AVLTREE_H
Definition exception.h:39
Definition allocator.h:39
Definition avltree.h:33
void printGraph()
Definition avltree.h:433
bool isEmpty()
Definition avltree.h:424
void add(void *ptr, std::size_t size)
Definition avltree.h:97
PtrAVLTree()
Definition avltree.h:76
bool canGrowInPlace(void *ptr, std::size_t newSize)
Definition avltree.h:373
void remove(void *ptr)
Definition avltree.h:240
bool contains(void *ptr)
Definition avltree.h:324
~PtrAVLTree()
Definition avltree.h:84
void * remove(std::size_t &size, std::size_t limit=~static_cast< std::size_t >(0))
Definition avltree.h:184
Definition allocator.h:29
@ MEM_ERROR_GENERAL
general error
Definition exception.h:29