authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-01-30 09:45:35-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-01-30 09:45:35-05:00
loge3a383a661aaf9b43016358e2951ff9d792f240e
treea0d4005ef78ac8531d95fb05b776348d2fc2c63b
parent0303e7bd8e4fb245b4dfae51c7c1e34a66e0855c
parentad15a73240324fce93a659fbee9dab7be24dfbb0
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #4239 from shawnl/rb

rb: **breaking** change API to be thread-safe

1 files changed, 37 insertions(+), 14 deletions(-)

lib/std/rb.zig+37-14
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1const std = @import("std.zig");1const std = @import("std");
2const assert = std.debug.assert;2const assert = std.debug.assert;
3const testing = std.testing;3const testing = std.testing;
4const Order = std.math.Order;4const Order = std.math.Order;
...@@ -11,6 +11,7 @@ const Red = Color.Red;...@@ -11,6 +11,7 @@ const Red = Color.Red;
11const Black = Color.Black;11const Black = Color.Black;
1212
13const ReplaceError = error{NotEqual};13const ReplaceError = error{NotEqual};
14const SortError = error{NotUnique}; // The new comparison function results in duplicates.
1415
15/// Insert this into your struct that you want to add to a red-black tree.16/// Insert this into your struct that you want to add to a red-black tree.
16/// Do not use a pointer. Turn the *rb.Node results of the functions in rb17/// Do not use a pointer. Turn the *rb.Node results of the functions in rb
...@@ -132,7 +133,21 @@ pub const Node = struct {...@@ -132,7 +133,21 @@ pub const Node = struct {
132133
133pub const Tree = struct {134pub const Tree = struct {
134 root: ?*Node,135 root: ?*Node,
135 compareFn: fn (*Node, *Node) Order,136 compareFn: fn (*Node, *Node, *Tree) Order,
137
138 /// Re-sorts a tree with a new compare function
139 pub fn sort(tree: *Tree, newCompareFn: fn (*Node, *Node, *Tree) Order) SortError!void {
140 var newTree = Tree.init(newCompareFn);
141 var node: *Node = undefined;
142 while (true) {
143 node = tree.first() orelse break;
144 tree.remove(node);
145 if (newTree.insert(node) != null) {
146 return error.NotUnique; // EEXISTS
147 }
148 }
149 tree.* = newTree;
150 }
136151
137 /// If you have a need for a version that caches this, please file a bug.152 /// If you have a need for a version that caches this, please file a bug.
138 pub fn first(tree: *Tree) ?*Node {153 pub fn first(tree: *Tree) ?*Node {
...@@ -244,6 +259,7 @@ pub const Tree = struct {...@@ -244,6 +259,7 @@ pub const Tree = struct {
244 return doLookup(key, tree, &parent, &is_left);259 return doLookup(key, tree, &parent, &is_left);
245 }260 }
246261
262 /// If node is not part of tree, behavior is undefined.
247 pub fn remove(tree: *Tree, nodeconst: *Node) void {263 pub fn remove(tree: *Tree, nodeconst: *Node) void {
248 var node = nodeconst;264 var node = nodeconst;
249 // as this has the same value as node, it is unsafe to access node after newnode265 // as this has the same value as node, it is unsafe to access node after newnode
...@@ -389,7 +405,7 @@ pub const Tree = struct {...@@ -389,7 +405,7 @@ pub const Tree = struct {
389 var new = newconst;405 var new = newconst;
390406
391 // I assume this can get optimized out if the caller already knows.407 // I assume this can get optimized out if the caller already knows.
392 if (tree.compareFn(old, new) != .eq) return ReplaceError.NotEqual;408 if (tree.compareFn(old, new, tree) != .eq) return ReplaceError.NotEqual;
393409
394 if (old.getParent()) |parent| {410 if (old.getParent()) |parent| {
395 parent.setChild(new, parent.left == old);411 parent.setChild(new, parent.left == old);
...@@ -404,9 +420,11 @@ pub const Tree = struct {...@@ -404,9 +420,11 @@ pub const Tree = struct {
404 new.* = old.*;420 new.* = old.*;
405 }421 }
406422
407 pub fn init(tree: *Tree, f: fn (*Node, *Node) Order) void {423 pub fn init(f: fn (*Node, *Node, *Tree) Order) Tree {
408 tree.root = null;424 return Tree{
409 tree.compareFn = f;425 .root = null,
426 .compareFn = f,
427 };
410 }428 }
411};429};
412430
...@@ -469,7 +487,7 @@ fn doLookup(key: *Node, tree: *Tree, pparent: *?*Node, is_left: *bool) ?*Node {...@@ -469,7 +487,7 @@ fn doLookup(key: *Node, tree: *Tree, pparent: *?*Node, is_left: *bool) ?*Node {
469 is_left.* = false;487 is_left.* = false;
470488
471 while (maybe_node) |node| {489 while (maybe_node) |node| {
472 const res = tree.compareFn(node, key);490 const res = tree.compareFn(node, key, tree);
473 if (res == .eq) {491 if (res == .eq) {
474 return node;492 return node;
475 }493 }
...@@ -498,7 +516,7 @@ fn testGetNumber(node: *Node) *testNumber {...@@ -498,7 +516,7 @@ fn testGetNumber(node: *Node) *testNumber {
498 return @fieldParentPtr(testNumber, "node", node);516 return @fieldParentPtr(testNumber, "node", node);
499}517}
500518
501fn testCompare(l: *Node, r: *Node) Order {519fn testCompare(l: *Node, r: *Node, contextIgnored: *Tree) Order {
502 var left = testGetNumber(l);520 var left = testGetNumber(l);
503 var right = testGetNumber(r);521 var right = testGetNumber(r);
504522
...@@ -512,13 +530,17 @@ fn testCompare(l: *Node, r: *Node) Order {...@@ -512,13 +530,17 @@ fn testCompare(l: *Node, r: *Node) Order {
512 unreachable;530 unreachable;
513}531}
514532
533fn testCompareReverse(l: *Node, r: *Node, contextIgnored: *Tree) Order {
534 return testCompare(r, l, contextIgnored);
535}
536
515test "rb" {537test "rb" {
516 if (@import("builtin").arch == .aarch64) {538 if (@import("builtin").arch == .aarch64) {
517 // TODO https://github.com/ziglang/zig/issues/3288539 // TODO https://github.com/ziglang/zig/issues/3288
518 return error.SkipZigTest;540 return error.SkipZigTest;
519 }541 }
520542
521 var tree: Tree = undefined;543 var tree = Tree.init(testCompare);
522 var ns: [10]testNumber = undefined;544 var ns: [10]testNumber = undefined;
523 ns[0].value = 42;545 ns[0].value = 42;
524 ns[1].value = 41;546 ns[1].value = 41;
...@@ -534,7 +556,6 @@ test "rb" {...@@ -534,7 +556,6 @@ test "rb" {
534 var dup: testNumber = undefined;556 var dup: testNumber = undefined;
535 dup.value = 32345;557 dup.value = 32345;
536558
537 tree.init(testCompare);
538 _ = tree.insert(&ns[1].node);559 _ = tree.insert(&ns[1].node);
539 _ = tree.insert(&ns[2].node);560 _ = tree.insert(&ns[2].node);
540 _ = tree.insert(&ns[3].node);561 _ = tree.insert(&ns[3].node);
...@@ -557,8 +578,7 @@ test "rb" {...@@ -557,8 +578,7 @@ test "rb" {
557}578}
558579
559test "inserting and looking up" {580test "inserting and looking up" {
560 var tree: Tree = undefined;581 var tree = Tree.init(testCompare);
561 tree.init(testCompare);
562 var number: testNumber = undefined;582 var number: testNumber = undefined;
563 number.value = 1000;583 number.value = 1000;
564 _ = tree.insert(&number.node);584 _ = tree.insert(&number.node);
...@@ -582,8 +602,7 @@ test "multiple inserts, followed by calling first and last" {...@@ -582,8 +602,7 @@ test "multiple inserts, followed by calling first and last" {
582 // TODO https://github.com/ziglang/zig/issues/3288602 // TODO https://github.com/ziglang/zig/issues/3288
583 return error.SkipZigTest;603 return error.SkipZigTest;
584 }604 }
585 var tree: Tree = undefined;605 var tree = Tree.init(testCompare);
586 tree.init(testCompare);
587 var zeroth: testNumber = undefined;606 var zeroth: testNumber = undefined;
588 zeroth.value = 0;607 zeroth.value = 0;
589 var first: testNumber = undefined;608 var first: testNumber = undefined;
...@@ -601,4 +620,8 @@ test "multiple inserts, followed by calling first and last" {...@@ -601,4 +620,8 @@ test "multiple inserts, followed by calling first and last" {
601 var lookupNode: testNumber = undefined;620 var lookupNode: testNumber = undefined;
602 lookupNode.value = 3;621 lookupNode.value = 3;
603 assert(tree.lookup(&lookupNode.node) == &third.node);622 assert(tree.lookup(&lookupNode.node) == &third.node);
623 tree.sort(testCompareReverse) catch unreachable;
624 assert(testGetNumber(tree.first().?).value == 3);
625 assert(testGetNumber(tree.last().?).value == 0);
626 assert(tree.lookup(&lookupNode.node) == &third.node);
604}627}