1 module d.gc.rbtree; 2 3 struct Node(N, string NodeName = "node") { 4 private: 5 alias Link = .Link!(N, NodeName); 6 Link[2] childs; 7 8 @property 9 ref Link left() { 10 return childs[0]; 11 } 12 13 @property 14 ref Link right() { 15 return childs[1]; 16 } 17 } 18 19 // TODO: when supported add default to use opCmp for compare. 20 struct RBTree(N, alias compare, string NodeName = "node") { 21 private: 22 N* root; 23 24 alias Link = .Link!(N, NodeName); 25 alias Node = .Node!(N, NodeName); 26 alias Path = .Path!(N, NodeName); 27 28 static ref Node nodeData(N* n) { 29 return .nodeData!NodeName(n); 30 } 31 32 public: 33 @property 34 bool empty() { 35 return root is null; 36 } 37 38 N* find(N* test) { 39 auto n = root; 40 41 while (n !is null) { 42 auto cmp = compare(test, n); 43 // We have a perfect match. 44 if (cmp == 0) { 45 return n; 46 } 47 48 n = nodeData(n).childs[cmp > 0].node; 49 } 50 51 return null; 52 } 53 54 /** 55 * Find the smallest item that is larger than the test. 56 */ 57 N* bestfit(N* test) { 58 auto n = root; 59 60 N* bf = null; 61 while (n !is null) { 62 auto cmp = compare(test, n); 63 // We have a perfect match. 64 if (cmp == 0) { 65 return n; 66 } 67 68 if (cmp < 0) { 69 bf = n; 70 } 71 72 n = nodeData(n).childs[cmp > 0].node; 73 } 74 75 return bf; 76 } 77 78 void insert(N* n) { 79 // rbtree's depth is ln(n) which is at most 8 * size_t.sizeof. 80 // Each tree node that N.sizeof size, so we can remove ln(N.sizeof). 81 // But a branch can be at most 2* longer than the shortest one. 82 import d.gc.util; 83 Path[16 * size_t.sizeof - lg2floor(N.sizeof)] path = void; 84 auto stackp = &path[0]; // TODO: use .ptr when available. 85 86 // Let's make sure this is a child node. 87 nodeData(n).left = Link(null, Color.Black); 88 nodeData(n).right = Link(null, Color.Black); 89 90 // Root is always black. 91 auto link = Link(root, Color.Black); 92 while (!link.isLeaf()) { 93 auto diff = compare(n, link.node); 94 assert(diff != 0); 95 96 auto cmp = diff > 0; 97 *stackp = Path(link, cmp); 98 99 stackp++; 100 link = link.childs[cmp]; 101 } 102 103 // The tree only has a root. 104 if (stackp is &path[0]) { 105 root = n; 106 return; 107 } 108 109 // Inserted node is always red. 110 *stackp = Path(Link(n, Color.Red), false); 111 assert(stackp.isRed()); 112 113 // Now we found an insertion point, let's fix the tree. 114 for (stackp--; stackp !is (&path[0] - 1); stackp--) { 115 link = stackp.link; 116 auto cmp = stackp.cmp; 117 118 auto child = link.childs[cmp] = stackp[1].link; 119 if (child.isBlack()) { 120 break; 121 } 122 123 if (link.isRed()) { 124 continue; 125 } 126 127 auto sibling = link.childs[!cmp]; 128 if (sibling.isRed()) { 129 assert(link.isBlack()); 130 assert(link.left.isRed() && link.right.isRed()); 131 132 /** 133 * B Br 134 * / \ => / \ 135 * R R Rb Rb 136 */ 137 link.left = link.left.getAs(Color.Black); 138 link.right = link.right.getAs(Color.Black); 139 *stackp = stackp.getWithLink(link.getAs(Color.Red)); 140 continue; 141 } 142 143 auto line = child.childs[cmp]; 144 if (line.isBlack()) { 145 if (child.childs[!cmp].isBlack()) { 146 // Our red child has 2 black child, we are good. 147 break; 148 } 149 150 /** 151 * We transform The zigzag case into the line case. 152 * 153 * B 154 * B / \ 155 * / \ B R 156 * B R => \ 157 * / \ R 158 * R B \ 159 * B 160 */ 161 assert(child.childs[!cmp].isRed()); 162 child = child.rotate(cmp); 163 } 164 165 /** 166 * B Rb 167 * / \ / \ 168 * B R => Br R 169 * / \ / \ 170 * B R B B 171 */ 172 link.childs[cmp] = child.getAs(Color.Black); 173 link = link.getAs(Color.Red); 174 *stackp = stackp.getWithLink(link.rotate(!cmp)); 175 } 176 177 root = path[0].node; 178 } 179 180 void remove(N* n) { 181 assert(n !is null); 182 auto removed = extract(n); 183 assert(n is removed); 184 } 185 186 N* extractAny() { 187 return extract(root); 188 } 189 190 N* extract(N* n) { 191 // rbtree's depth is ln(n) which is at most 8 * size_t.sizeof. 192 // Each tree node that N.sizeof size, so we can remove ln(N.sizeof). 193 // But a branch can be at most 2* longer than the shortest one. 194 import d.gc.util; 195 Path[16 * size_t.sizeof - lg2floor(N.sizeof)] path = void; 196 auto stackp = &path[0]; // TODO: use .ptr when available. 197 198 // Root is always black. 199 auto link = Link(root, Color.Black); 200 auto rn = root; 201 while (rn !is null) { 202 auto diff = compare(n, rn); 203 204 // We found our node ! 205 if (diff == 0) { 206 break; 207 } 208 209 auto cmp = diff > 0; 210 *stackp = Path(link, cmp); 211 212 stackp++; 213 link = link.childs[cmp]; 214 rn = link.node; 215 } 216 217 if (rn is null) { 218 return null; 219 } 220 221 // Now we look for a succesor. 222 *stackp = Path(link, true); 223 auto removep = stackp; 224 auto removed = link; 225 226 /** 227 * We find a replacing node by going one to the right 228 * and then as far as possible to the left. That way 229 * we get the next node in the tree and its ordering 230 * will be valid. 231 */ 232 link = removed.right; 233 while (!link.isLeaf()) { 234 stackp++; 235 *stackp = Path(link, false); 236 link = link.left; 237 } 238 239 link = stackp.link; 240 241 if (stackp is removep) { 242 // The node we remove has no successor. 243 *stackp = stackp.getWithLink(link.left); 244 } else { 245 /** 246 * Swap node to be deleted with its successor 247 * but not the color, so we keep tree color 248 * constraint in place. 249 */ 250 auto rcolor = removed.color; 251 252 removed = removed.getAs(link.color); 253 *stackp = stackp.getWithLink(link.right); 254 255 link = link.getAs(rcolor); 256 link.left = removed.left; 257 258 /** 259 * If the successor is the right child of the 260 * node we want to delete, this is incorrect. 261 * However, it doesn't matter, as it is going 262 * to be fixed during pruning. 263 */ 264 link.right = removed.right; 265 266 // NB: We don't clean the node to be removed. 267 // We simply splice it out. 268 *removep = removep.getWithLink(link); 269 } 270 271 // If we are not at the root, fix the parent. 272 if (removep !is &path[0]) { 273 removep[-1].childs[removep[-1].cmp] = removep.link; 274 } 275 276 // Removing red node require no fixup. 277 if (removed.isRed()) { 278 stackp[-1].childs[stackp[-1].cmp] = Link(null, Color.Black); 279 280 // Update root and exit 281 root = path[0].node; 282 return rn; 283 } 284 285 for (stackp--; stackp !is (&path[0] - 1); stackp--) { 286 link = stackp.link; 287 auto cmp = stackp.cmp; 288 289 auto child = stackp[1].link; 290 if (child.isRed()) { 291 // If the double black is on a red node, recolor. 292 link.childs[cmp] = child.getAs(Color.Black); 293 break; 294 } 295 296 link.childs[cmp] = child; 297 298 /** 299 * b = changed to black 300 * r = changed to red 301 * // = double black path 302 * 303 * We rotate and recolor to find ourselves in a case 304 * where sibling is black one level below. Because the 305 * new root will be red, zigzag case will bubble up 306 * with a red node, which is going to terminate. 307 * 308 * Rb 309 * B \ 310 * / \\ => Br <- new link 311 * R B \\ 312 * B 313 */ 314 auto sibling = link.childs[!cmp]; 315 if (sibling.isRed()) { 316 assert(link.isBlack()); 317 318 link = link.getAs(Color.Red); 319 auto parent = link.rotate(cmp); 320 *stackp = stackp.getWithLink(parent.getAs(Color.Black)); 321 322 // As we are going down one level, make sure we fix the parent. 323 if (stackp !is &path[0]) { 324 stackp[-1].childs[stackp[-1].cmp] = stackp.link; 325 } 326 327 stackp++; 328 329 // Fake landing one level below. 330 // NB: We don't need to fake cmp. 331 *stackp = stackp.getWithLink(link); 332 sibling = link.childs[!cmp]; 333 } 334 335 auto line = sibling.childs[!cmp]; 336 if (line.isRed()) { 337 goto Line; 338 } 339 340 if (sibling.childs[cmp].isBlack()) { 341 /** 342 * b = changed to black 343 * r = changed to red 344 * // = double black path 345 * 346 * We recolor the sibling to push the double 347 * black one level up. 348 * 349 * X (X) 350 * / \\ / \ 351 * B B => Br B 352 * / \ / \ 353 * B B B B 354 */ 355 link.childs[!cmp] = sibling.getAs(Color.Red); 356 continue; 357 } 358 359 /** 360 * b = changed to black 361 * r = changed to red 362 * // = double black path 363 * 364 * We rotate the zigzag to be in the line case. 365 * 366 * X 367 * X / \\ 368 * / \\ Rb B 369 * B B => / 370 * / \ Br 371 * B R / 372 * B 373 */ 374 line = sibling.getAs(Color.Red); 375 sibling = line.rotate(!cmp); 376 sibling = sibling.getAs(Color.Black); 377 link.childs[!cmp] = sibling; 378 379 Line: 380 /** 381 * b = changed to black 382 * x = changed to x's original color 383 * // = double black path 384 * 385 * X Bx 386 * / \\ / \ 387 * B B => Rb Xb 388 * / \ / \ 389 * R Y Y B 390 */ 391 auto l = link.getAs(Color.Black); 392 l = l.rotate(cmp); 393 l.childs[!cmp] = line.getAs(Color.Black); 394 395 // If we are the root, we are done. 396 if (stackp is &path[0]) { 397 root = l.node; 398 return rn; 399 } 400 401 stackp[-1].childs[stackp[-1].cmp] = l.getAs(link.color); 402 break; 403 } 404 405 // Update root and exit 406 root = path[0].node; 407 return rn; 408 } 409 410 void dump() { 411 rb_print_tree!NodeName(root); 412 } 413 } 414 415 private: 416 //+ 417 void rb_print_tree(string NodeName, N)(N* root) { 418 Debug!(N, NodeName).print_tree(Link!(N, NodeName)(root, Color.Black), 0); 419 } 420 421 // +/ 422 423 private: 424 425 ref Node!(N, NodeName) nodeData(string NodeName, N)(N* n) { 426 mixin("return n." ~ NodeName ~ ";"); 427 } 428 429 struct Link(N, string NodeName) { 430 alias Node = .Node!(N, NodeName); 431 432 // This is effectively a tagged pointer, don't use as this. 433 N* _child; 434 435 this(N* n, Color c) { 436 assert(c == Color.Black || n !is null); 437 _child = cast(N*) ((cast(size_t) n) | c); 438 } 439 440 auto getAs(Color c) { 441 assert(c == Color.Black || node !is null); 442 return Link(node, c); 443 } 444 445 @property 446 N* node() { 447 return cast(N*) ((cast(size_t) _child) & ~0x01); 448 } 449 450 @property 451 ref Node nodeData() { 452 return .nodeData!NodeName(node); 453 } 454 455 @property 456 ref Link left() { 457 return nodeData.left; 458 } 459 460 @property 461 ref Link right() { 462 return nodeData.right; 463 } 464 465 @property 466 ref Link[2] childs() { 467 return nodeData.childs; 468 } 469 470 @property 471 Color color() const { 472 return cast(Color) ((cast(size_t) _child) & 0x01); 473 } 474 475 bool isRed() const { 476 return color == Color.Red; 477 } 478 479 bool isBlack() const { 480 return color == Color.Black; 481 } 482 483 bool isLeaf() const { 484 return _child is null; 485 } 486 487 // Rotate the tree and return the new root. 488 // The tree turn clockwize if cmp is true, 489 // counterclockwize if it is false. 490 auto rotate(bool cmp) { 491 auto x = childs[!cmp]; 492 childs[!cmp] = x.childs[cmp]; 493 x.childs[cmp] = this; 494 return x; 495 } 496 497 // Rotate the tree and return the new root. 498 auto rotateLeft() { 499 auto r = right; 500 right = r.left; 501 r.left = this; 502 return r; 503 } 504 505 // Rotate the tree and return the new root. 506 auto rotateRight() { 507 auto l = left; 508 left = l.right; 509 l.right = this; 510 return l; 511 } 512 } 513 514 enum Color : bool { 515 Black = false, 516 Red = true, 517 } 518 519 struct Path(N, string NodeName) { 520 alias Link = .Link!(N, NodeName); 521 alias Node = .Node!(N, NodeName); 522 523 // This is effectively a tagged pointer, don't use as this. 524 N* _child; 525 526 this(Link l, bool c) { 527 _child = cast(N*) ((cast(size_t) l._child) | (c << 1)); 528 } 529 530 auto getWithLink(Link l) { 531 return Path(l, cmp); 532 } 533 534 auto getWithCmp(bool c) { 535 return Path(link, c); 536 } 537 538 @property 539 Link link() { 540 return Link(node, color); 541 } 542 543 @property 544 bool cmp() const { 545 return !!((cast(size_t) _child) & 0x02); 546 } 547 548 @property 549 N* node() { 550 return cast(N*) ((cast(size_t) _child) & ~0x03); 551 } 552 553 @property 554 ref Node nodeData() { 555 return .nodeData!NodeName(node); 556 } 557 558 @property 559 ref Link left() { 560 return nodeData.left; 561 } 562 563 @property 564 ref Link right() { 565 return nodeData.right; 566 } 567 568 @property 569 ref Link[2] childs() { 570 return nodeData.childs; 571 } 572 573 @property 574 Color color() const { 575 return cast(Color) ((cast(size_t) _child) & 0x01); 576 } 577 578 bool isRed() const { 579 return color == Color.Red; 580 } 581 582 bool isBlack() const { 583 return color == Color.Black; 584 } 585 } 586 587 //+ 588 template Debug(N, string NodeName) { 589 void print_tree(Link!(N, NodeName) root, uint depth) { 590 foreach (i; 0 .. depth) { 591 import core.stdc.stdio; 592 printf("\t".ptr); 593 } 594 595 if (root.isBlack()) { 596 import core.stdc.stdio; 597 printf("B %p\n".ptr, root.node); 598 } else { 599 assert(root.isRed()); 600 601 import core.stdc.stdio; 602 printf("R %p\n".ptr, root.node); 603 } 604 605 if (!root.isLeaf()) { 606 print_tree(root.right, depth + 1); 607 print_tree(root.left, depth + 1); 608 } 609 } 610 } 611 612 // +/ 613 614 unittest rbtree { 615 struct Stuff { 616 Node!Stuff node; 617 ulong value; 618 } 619 620 static ptrdiff_t stuffCmp(Stuff* lhs, Stuff* rhs) { 621 return (lhs.value == rhs.value) 622 ? (cast(ptrdiff_t) lhs) - (cast(ptrdiff_t) rhs) 623 : (lhs.value - rhs.value); 624 } 625 626 static ulong prand_next(ulong prev) { 627 return (prev * 31415821 + 1) % 100_000_000; 628 } 629 630 enum Items = 174762; 631 Stuff[32][Items]* nodes; 632 633 Stuff* get_node(ulong tree, ulong node) { 634 assert(node < 174762 && tree < 32); 635 return &nodes[0][node][tree]; 636 } 637 638 // 128 Mb to ramble through. 639 nodes = cast(Stuff[32][Items]*) __sd_gc_tl_malloc(128 * 1024 * 1024); 640 ulong prand = 365307287; 641 642 foreach (i; 0 .. Items) { 643 foreach (t; 0 .. 32) { 644 prand = prand_next(prand); 645 get_node(t, i).value = prand; 646 } 647 } 648 649 RBTree!(Stuff, stuffCmp)[32] trees; 650 651 foreach (i; 0 .. Items) { 652 foreach (t; 0 .. 32) { 653 trees[t].insert(get_node(t, i)); 654 // rb_print_tree(trees[t]); 655 } 656 } 657 658 foreach (i; 0 .. Items) { 659 foreach (t; 0 .. 32) { 660 trees[t].remove(get_node(t, i)); 661 // rb_print_tree(trees[t]); 662 } 663 } 664 }