authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-29 17:26:09-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-29 17:26:09-07:00
logaf64fd2f424401ff66638696b35b1bf385c4b039
treebfd10169c9af10e947dfbbe593ad353604074f62
parent27e008eb292038c5a6b9a13b64c7b69d1525f690
parentd1cea16f5cd29eb143ff9b3302e7ec56731647ea

Merge remote-tracking branch 'origin/master' into stage2-zig-cc

This merges in the revert that fixes the broken Windows build of master branch.

5 files changed, 1 insertions(+), 940 deletions(-)

lib/std/bloom_filter.zig deleted-265
...@@ -1,265 +0,0 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6const builtin = @import("builtin");
7const std = @import("std.zig");
8const math = std.math;
9const debug = std.debug;
10const assert = std.debug.assert;
11const testing = std.testing;
12
13/// There is a trade off of how quickly to fill a bloom filter;
14/// the number of items is:
15/// n_items / K * ln(2)
16/// the rate of false positives is:
17/// (1-e^(-K*N/n_items))^K
18/// where N is the number of items
19pub fn BloomFilter(
20 /// Size of bloom filter in cells, must be a power of two.
21 comptime n_items: usize,
22 /// Number of cells to set per item
23 comptime K: usize,
24 /// Cell type, should be:
25 /// - `bool` for a standard bloom filter
26 /// - an unsigned integer type for a counting bloom filter
27 comptime Cell: type,
28 /// endianess of the Cell
29 comptime endian: builtin.Endian,
30 /// Hash function to use
31 comptime hash: fn (out: []u8, Ki: usize, in: []const u8) void,
32) type {
33 assert(n_items > 0);
34 assert(math.isPowerOfTwo(n_items));
35 assert(K > 0);
36 const cellEmpty = if (Cell == bool) false else @as(Cell, 0);
37 const cellMax = if (Cell == bool) true else math.maxInt(Cell);
38 const n_bytes = (n_items * comptime std.meta.bitCount(Cell)) / 8;
39 assert(n_bytes > 0);
40 const Io = std.packed_int_array.PackedIntIo(Cell, endian);
41
42 return struct {
43 const Self = @This();
44 pub const items = n_items;
45 pub const Index = math.IntFittingRange(0, n_items - 1);
46
47 data: [n_bytes]u8 = [_]u8{0} ** n_bytes,
48
49 pub fn reset(self: *Self) void {
50 std.mem.set(u8, self.data[0..], 0);
51 }
52
53 pub fn @"union"(x: Self, y: Self) Self {
54 var r = Self{ .data = undefined };
55 inline for (x.data) |v, i| {
56 r.data[i] = v | y.data[i];
57 }
58 return r;
59 }
60
61 pub fn intersection(x: Self, y: Self) Self {
62 var r = Self{ .data = undefined };
63 inline for (x.data) |v, i| {
64 r.data[i] = v & y.data[i];
65 }
66 return r;
67 }
68
69 pub fn getCell(self: Self, cell: Index) Cell {
70 return Io.get(&self.data, cell, 0);
71 }
72
73 pub fn incrementCell(self: *Self, cell: Index) void {
74 if (Cell == bool or Cell == u1) {
75 // skip the 'get' operation
76 Io.set(&self.data, cell, 0, cellMax);
77 } else {
78 const old = Io.get(&self.data, cell, 0);
79 if (old != cellMax) {
80 Io.set(&self.data, cell, 0, old + 1);
81 }
82 }
83 }
84
85 pub fn clearCell(self: *Self, cell: Index) void {
86 Io.set(&self.data, cell, 0, cellEmpty);
87 }
88
89 pub fn add(self: *Self, item: []const u8) void {
90 comptime var i = 0;
91 inline while (i < K) : (i += 1) {
92 var K_th_bit: packed struct {
93 x: Index,
94 } = undefined;
95 hash(std.mem.asBytes(&K_th_bit), i, item);
96 incrementCell(self, K_th_bit.x);
97 }
98 }
99
100 pub fn contains(self: Self, item: []const u8) bool {
101 comptime var i = 0;
102 inline while (i < K) : (i += 1) {
103 var K_th_bit: packed struct {
104 x: Index,
105 } = undefined;
106 hash(std.mem.asBytes(&K_th_bit), i, item);
107 if (getCell(self, K_th_bit.x) == cellEmpty)
108 return false;
109 }
110 return true;
111 }
112
113 pub fn resize(self: Self, comptime newsize: usize) BloomFilter(newsize, K, Cell, endian, hash) {
114 var r: BloomFilter(newsize, K, Cell, endian, hash) = undefined;
115 if (newsize < n_items) {
116 std.mem.copy(u8, r.data[0..], self.data[0..r.data.len]);
117 var copied: usize = r.data.len;
118 while (copied < self.data.len) : (copied += r.data.len) {
119 for (self.data[copied .. copied + r.data.len]) |s, i| {
120 r.data[i] |= s;
121 }
122 }
123 } else if (newsize == n_items) {
124 r = self;
125 } else if (newsize > n_items) {
126 var copied: usize = 0;
127 while (copied < r.data.len) : (copied += self.data.len) {
128 std.mem.copy(u8, r.data[copied .. copied + self.data.len], &self.data);
129 }
130 }
131 return r;
132 }
133
134 /// Returns number of non-zero cells
135 pub fn popCount(self: Self) Index {
136 var n: Index = 0;
137 if (Cell == bool or Cell == u1) {
138 for (self.data) |b, i| {
139 n += @popCount(u8, b);
140 }
141 } else {
142 var i: usize = 0;
143 while (i < n_items) : (i += 1) {
144 const cell = self.getCell(@intCast(Index, i));
145 n += if (if (Cell == bool) cell else cell > 0) @as(Index, 1) else @as(Index, 0);
146 }
147 }
148 return n;
149 }
150
151 pub fn estimateItems(self: Self) f64 {
152 const m = comptime @intToFloat(f64, n_items);
153 const k = comptime @intToFloat(f64, K);
154 const X = @intToFloat(f64, self.popCount());
155 return (comptime (-m / k)) * math.log1p(X * comptime (-1 / m));
156 }
157 };
158}
159
160fn hashFunc(out: []u8, Ki: usize, in: []const u8) void {
161 var st = std.crypto.hash.Gimli.init(.{});
162 st.update(std.mem.asBytes(&Ki));
163 st.update(in);
164 st.final(out);
165}
166
167test "std.BloomFilter" {
168 // https://github.com/ziglang/zig/issues/5127
169 if (std.Target.current.cpu.arch == .mips) return error.SkipZigTest;
170
171 inline for ([_]type{ bool, u1, u2, u3, u4 }) |Cell| {
172 const emptyCell = if (Cell == bool) false else @as(Cell, 0);
173 const BF = BloomFilter(128 * 8, 8, Cell, builtin.endian, hashFunc);
174 var bf = BF{};
175 var i: usize = undefined;
176 // confirm that it is initialised to the empty filter
177 i = 0;
178 while (i < BF.items) : (i += 1) {
179 testing.expectEqual(emptyCell, bf.getCell(@intCast(BF.Index, i)));
180 }
181 testing.expectEqual(@as(BF.Index, 0), bf.popCount());
182 testing.expectEqual(@as(f64, 0), bf.estimateItems());
183 // fill in a few items
184 bf.incrementCell(42);
185 bf.incrementCell(255);
186 bf.incrementCell(256);
187 bf.incrementCell(257);
188 // check that they were set
189 testing.expectEqual(true, bf.getCell(42) != emptyCell);
190 testing.expectEqual(true, bf.getCell(255) != emptyCell);
191 testing.expectEqual(true, bf.getCell(256) != emptyCell);
192 testing.expectEqual(true, bf.getCell(257) != emptyCell);
193 // clear just one of them; make sure the rest are still set
194 bf.clearCell(256);
195 testing.expectEqual(true, bf.getCell(42) != emptyCell);
196 testing.expectEqual(true, bf.getCell(255) != emptyCell);
197 testing.expectEqual(false, bf.getCell(256) != emptyCell);
198 testing.expectEqual(true, bf.getCell(257) != emptyCell);
199 // reset any of the ones we've set and confirm we're back to the empty filter
200 bf.clearCell(42);
201 bf.clearCell(255);
202 bf.clearCell(257);
203 i = 0;
204 while (i < BF.items) : (i += 1) {
205 testing.expectEqual(emptyCell, bf.getCell(@intCast(BF.Index, i)));
206 }
207 testing.expectEqual(@as(BF.Index, 0), bf.popCount());
208 testing.expectEqual(@as(f64, 0), bf.estimateItems());
209
210 // Lets add a string
211 bf.add("foo");
212 testing.expectEqual(true, bf.contains("foo"));
213 {
214 // try adding same string again. make sure popcount is the same
215 const old_popcount = bf.popCount();
216 testing.expect(old_popcount > 0);
217 bf.add("foo");
218 testing.expectEqual(true, bf.contains("foo"));
219 testing.expectEqual(old_popcount, bf.popCount());
220 }
221
222 // Get back to empty filter via .reset
223 bf.reset();
224 // Double check that .reset worked
225 i = 0;
226 while (i < BF.items) : (i += 1) {
227 testing.expectEqual(emptyCell, bf.getCell(@intCast(BF.Index, i)));
228 }
229 testing.expectEqual(@as(BF.Index, 0), bf.popCount());
230 testing.expectEqual(@as(f64, 0), bf.estimateItems());
231
232 comptime var teststrings = [_][]const u8{
233 "foo",
234 "bar",
235 "a longer string",
236 "some more",
237 "the quick brown fox",
238 "unique string",
239 };
240 inline for (teststrings) |str| {
241 bf.add(str);
242 }
243 inline for (teststrings) |str| {
244 testing.expectEqual(true, bf.contains(str));
245 }
246
247 { // estimate should be close for low packing
248 const est = bf.estimateItems();
249 testing.expect(est > @intToFloat(f64, teststrings.len) - 1);
250 testing.expect(est < @intToFloat(f64, teststrings.len) + 1);
251 }
252
253 const larger_bf = bf.resize(4096);
254 inline for (teststrings) |str| {
255 testing.expectEqual(true, larger_bf.contains(str));
256 }
257 testing.expectEqual(@as(u12, bf.popCount()) * (4096 / 1024), larger_bf.popCount());
258
259 const smaller_bf = bf.resize(64);
260 inline for (teststrings) |str| {
261 testing.expectEqual(true, smaller_bf.contains(str));
262 }
263 testing.expect(bf.popCount() <= @as(u10, smaller_bf.popCount()) * (1024 / 64));
264 }
265}
lib/std/fs/test.zig-23
...@@ -813,26 +813,3 @@ fn run_lock_file_test(contexts: []FileLockTestContext) !void {...@@ -813,26 +813,3 @@ fn run_lock_file_test(contexts: []FileLockTestContext) !void {
813 try threads.append(try std.Thread.spawn(ctx, FileLockTestContext.run));813 try threads.append(try std.Thread.spawn(ctx, FileLockTestContext.run));
814 }814 }
815}815}
816
817test "deleteDir" {
818 var tmp_dir = tmpDir(.{});
819 defer tmp_dir.cleanup();
820
821 // deleting a non-existent directory
822 testing.expectError(error.FileNotFound, tmp_dir.dir.deleteDir("test_dir"));
823
824 var dir = try tmp_dir.dir.makeOpenPath("test_dir", .{});
825 var file = try dir.createFile("test_file", .{});
826 file.close();
827 dir.close();
828
829 // deleting a non-empty directory
830 testing.expectError(error.DirNotEmpty, tmp_dir.dir.deleteDir("test_dir"));
831
832 dir = try tmp_dir.dir.openDir("test_dir", .{});
833 try dir.deleteFile("test_file");
834 dir.close();
835
836 // deleting an empty directory
837 try tmp_dir.dir.deleteDir("test_dir");
838}
lib/std/os/windows.zig+1-17
...@@ -765,7 +765,6 @@ pub const DeleteFileError = error{...@@ -765,7 +765,6 @@ pub const DeleteFileError = error{
765 Unexpected,765 Unexpected,
766 NotDir,766 NotDir,
767 IsDir,767 IsDir,
768 DirNotEmpty,
769};768};
770769
771pub const DeleteFileOptions = struct {770pub const DeleteFileOptions = struct {
...@@ -820,7 +819,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil...@@ -820,7 +819,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
820 0,819 0,
821 );820 );
822 switch (rc) {821 switch (rc) {
823 .SUCCESS => CloseHandle(tmp_handle),822 .SUCCESS => return CloseHandle(tmp_handle),
824 .OBJECT_NAME_INVALID => unreachable,823 .OBJECT_NAME_INVALID => unreachable,
825 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,824 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
826 .INVALID_PARAMETER => unreachable,825 .INVALID_PARAMETER => unreachable,
...@@ -829,21 +828,6 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil...@@ -829,21 +828,6 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
829 .SHARING_VIOLATION => return error.FileBusy,828 .SHARING_VIOLATION => return error.FileBusy,
830 else => return unexpectedStatus(rc),829 else => return unexpectedStatus(rc),
831 }830 }
832
833 // If a directory fails to be deleted, CloseHandle will still report success
834 // Check if the directory still exists and return error.DirNotEmpty if true
835 if (options.remove_dir) {
836 var basic_info: FILE_BASIC_INFORMATION = undefined;
837 switch (ntdll.NtQueryAttributesFile(&attr, &basic_info)) {
838 .SUCCESS => return error.DirNotEmpty,
839 .OBJECT_NAME_NOT_FOUND => return,
840 .OBJECT_PATH_NOT_FOUND => return,
841 .INVALID_PARAMETER => unreachable,
842 .ACCESS_DENIED => return error.AccessDenied,
843 .OBJECT_PATH_SYNTAX_BAD => unreachable,
844 else => |urc| return unexpectedStatus(urc),
845 }
846 }
847}831}
848832
849pub const MoveFileError = error{ FileNotFound, Unexpected };833pub const MoveFileError = error{ FileNotFound, Unexpected };
lib/std/rb.zig deleted-633
...@@ -1,633 +0,0 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6const std = @import("std");
7const assert = std.debug.assert;
8const testing = std.testing;
9const Order = std.math.Order;
10
11const Color = enum(u1) {
12 Black,
13 Red,
14};
15const Red = Color.Red;
16const Black = Color.Black;
17
18const ReplaceError = error{NotEqual};
19const SortError = error{NotUnique}; // The new comparison function results in duplicates.
20
21/// Insert this into your struct that you want to add to a red-black tree.
22/// Do not use a pointer. Turn the *rb.Node results of the functions in rb
23/// (after resolving optionals) to your structure using @fieldParentPtr(). Example:
24///
25/// const Number = struct {
26/// node: rb.Node,
27/// value: i32,
28/// };
29/// fn number(node: *rb.Node) Number {
30/// return @fieldParentPtr(Number, "node", node);
31/// }
32pub const Node = struct {
33 left: ?*Node,
34 right: ?*Node,
35
36 /// parent | color
37 parent_and_color: usize,
38
39 pub fn next(constnode: *Node) ?*Node {
40 var node = constnode;
41
42 if (node.right) |right| {
43 var n = right;
44 while (n.left) |left|
45 n = left;
46 return n;
47 }
48
49 while (true) {
50 var parent = node.getParent();
51 if (parent) |p| {
52 if (node != p.right)
53 return p;
54 node = p;
55 } else
56 return null;
57 }
58 }
59
60 pub fn prev(constnode: *Node) ?*Node {
61 var node = constnode;
62
63 if (node.left) |left| {
64 var n = left;
65 while (n.right) |right|
66 n = right;
67 return n;
68 }
69
70 while (true) {
71 var parent = node.getParent();
72 if (parent) |p| {
73 if (node != p.left)
74 return p;
75 node = p;
76 } else
77 return null;
78 }
79 }
80
81 pub fn isRoot(node: *Node) bool {
82 return node.getParent() == null;
83 }
84
85 fn isRed(node: *Node) bool {
86 return node.getColor() == Red;
87 }
88
89 fn isBlack(node: *Node) bool {
90 return node.getColor() == Black;
91 }
92
93 fn setParent(node: *Node, parent: ?*Node) void {
94 node.parent_and_color = @ptrToInt(parent) | (node.parent_and_color & 1);
95 }
96
97 fn getParent(node: *Node) ?*Node {
98 const mask: usize = 1;
99 comptime {
100 assert(@alignOf(*Node) >= 2);
101 }
102 const maybe_ptr = node.parent_and_color & ~mask;
103 return if (maybe_ptr == 0) null else @intToPtr(*Node, maybe_ptr);
104 }
105
106 fn setColor(node: *Node, color: Color) void {
107 const mask: usize = 1;
108 node.parent_and_color = (node.parent_and_color & ~mask) | @enumToInt(color);
109 }
110
111 fn getColor(node: *Node) Color {
112 return @intToEnum(Color, @intCast(u1, node.parent_and_color & 1));
113 }
114
115 fn setChild(node: *Node, child: ?*Node, is_left: bool) void {
116 if (is_left) {
117 node.left = child;
118 } else {
119 node.right = child;
120 }
121 }
122
123 fn getFirst(nodeconst: *Node) *Node {
124 var node = nodeconst;
125 while (node.left) |left| {
126 node = left;
127 }
128 return node;
129 }
130
131 fn getLast(nodeconst: *Node) *Node {
132 var node = nodeconst;
133 while (node.right) |right| {
134 node = right;
135 }
136 return node;
137 }
138};
139
140pub const Tree = struct {
141 root: ?*Node,
142 compareFn: fn (*Node, *Node, *Tree) Order,
143
144 /// Re-sorts a tree with a new compare function
145 pub fn sort(tree: *Tree, newCompareFn: fn (*Node, *Node, *Tree) Order) SortError!void {
146 var newTree = Tree.init(newCompareFn);
147 var node: *Node = undefined;
148 while (true) {
149 node = tree.first() orelse break;
150 tree.remove(node);
151 if (newTree.insert(node) != null) {
152 return error.NotUnique; // EEXISTS
153 }
154 }
155 tree.* = newTree;
156 }
157
158 /// If you have a need for a version that caches this, please file a bug.
159 pub fn first(tree: *Tree) ?*Node {
160 var node: *Node = tree.root orelse return null;
161
162 while (node.left) |left| {
163 node = left;
164 }
165
166 return node;
167 }
168
169 pub fn last(tree: *Tree) ?*Node {
170 var node: *Node = tree.root orelse return null;
171
172 while (node.right) |right| {
173 node = right;
174 }
175
176 return node;
177 }
178
179 /// Duplicate keys are not allowed. The item with the same key already in the
180 /// tree will be returned, and the item will not be inserted.
181 pub fn insert(tree: *Tree, node_const: *Node) ?*Node {
182 var node = node_const;
183 var maybe_key: ?*Node = undefined;
184 var maybe_parent: ?*Node = undefined;
185 var is_left: bool = undefined;
186
187 maybe_key = doLookup(node, tree, &maybe_parent, &is_left);
188 if (maybe_key) |key| {
189 return key;
190 }
191
192 node.left = null;
193 node.right = null;
194 node.setColor(Red);
195 node.setParent(maybe_parent);
196
197 if (maybe_parent) |parent| {
198 parent.setChild(node, is_left);
199 } else {
200 tree.root = node;
201 }
202
203 while (node.getParent()) |*parent| {
204 if (parent.*.isBlack())
205 break;
206 // the root is always black
207 var grandpa = parent.*.getParent() orelse unreachable;
208
209 if (parent.* == grandpa.left) {
210 var maybe_uncle = grandpa.right;
211
212 if (maybe_uncle) |uncle| {
213 if (uncle.isBlack())
214 break;
215
216 parent.*.setColor(Black);
217 uncle.setColor(Black);
218 grandpa.setColor(Red);
219 node = grandpa;
220 } else {
221 if (node == parent.*.right) {
222 rotateLeft(parent.*, tree);
223 node = parent.*;
224 parent.* = node.getParent().?; // Just rotated
225 }
226 parent.*.setColor(Black);
227 grandpa.setColor(Red);
228 rotateRight(grandpa, tree);
229 }
230 } else {
231 var maybe_uncle = grandpa.left;
232
233 if (maybe_uncle) |uncle| {
234 if (uncle.isBlack())
235 break;
236
237 parent.*.setColor(Black);
238 uncle.setColor(Black);
239 grandpa.setColor(Red);
240 node = grandpa;
241 } else {
242 if (node == parent.*.left) {
243 rotateRight(parent.*, tree);
244 node = parent.*;
245 parent.* = node.getParent().?; // Just rotated
246 }
247 parent.*.setColor(Black);
248 grandpa.setColor(Red);
249 rotateLeft(grandpa, tree);
250 }
251 }
252 }
253 // This was an insert, there is at least one node.
254 tree.root.?.setColor(Black);
255 return null;
256 }
257
258 /// lookup searches for the value of key, using binary search. It will
259 /// return a pointer to the node if it is there, otherwise it will return null.
260 /// Complexity guaranteed O(log n), where n is the number of nodes book-kept
261 /// by tree.
262 pub fn lookup(tree: *Tree, key: *Node) ?*Node {
263 var parent: ?*Node = undefined;
264 var is_left: bool = undefined;
265 return doLookup(key, tree, &parent, &is_left);
266 }
267
268 /// If node is not part of tree, behavior is undefined.
269 pub fn remove(tree: *Tree, nodeconst: *Node) void {
270 var node = nodeconst;
271 // as this has the same value as node, it is unsafe to access node after newnode
272 var newnode: ?*Node = nodeconst;
273 var maybe_parent: ?*Node = node.getParent();
274 var color: Color = undefined;
275 var next: *Node = undefined;
276
277 // This clause is to avoid optionals
278 if (node.left == null and node.right == null) {
279 if (maybe_parent) |parent| {
280 parent.setChild(null, parent.left == node);
281 } else
282 tree.root = null;
283 color = node.getColor();
284 newnode = null;
285 } else {
286 if (node.left == null) {
287 next = node.right.?; // Not both null as per above
288 } else if (node.right == null) {
289 next = node.left.?; // Not both null as per above
290 } else
291 next = node.right.?.getFirst(); // Just checked for null above
292
293 if (maybe_parent) |parent| {
294 parent.setChild(next, parent.left == node);
295 } else
296 tree.root = next;
297
298 if (node.left != null and node.right != null) {
299 const left = node.left.?;
300 const right = node.right.?;
301
302 color = next.getColor();
303 next.setColor(node.getColor());
304
305 next.left = left;
306 left.setParent(next);
307
308 if (next != right) {
309 var parent = next.getParent().?; // Was traversed via child node (right/left)
310 next.setParent(node.getParent());
311
312 newnode = next.right;
313 parent.left = node;
314
315 next.right = right;
316 right.setParent(next);
317 } else {
318 next.setParent(maybe_parent);
319 maybe_parent = next;
320 newnode = next.right;
321 }
322 } else {
323 color = node.getColor();
324 newnode = next;
325 }
326 }
327
328 if (newnode) |n|
329 n.setParent(maybe_parent);
330
331 if (color == Red)
332 return;
333 if (newnode) |n| {
334 n.setColor(Black);
335 return;
336 }
337
338 while (node == tree.root) {
339 // If not root, there must be parent
340 var parent = maybe_parent.?;
341 if (node == parent.left) {
342 var sibling = parent.right.?; // Same number of black nodes.
343
344 if (sibling.isRed()) {
345 sibling.setColor(Black);
346 parent.setColor(Red);
347 rotateLeft(parent, tree);
348 sibling = parent.right.?; // Just rotated
349 }
350 if ((if (sibling.left) |n| n.isBlack() else true) and
351 (if (sibling.right) |n| n.isBlack() else true))
352 {
353 sibling.setColor(Red);
354 node = parent;
355 maybe_parent = parent.getParent();
356 continue;
357 }
358 if (if (sibling.right) |n| n.isBlack() else true) {
359 sibling.left.?.setColor(Black); // Same number of black nodes.
360 sibling.setColor(Red);
361 rotateRight(sibling, tree);
362 sibling = parent.right.?; // Just rotated
363 }
364 sibling.setColor(parent.getColor());
365 parent.setColor(Black);
366 sibling.right.?.setColor(Black); // Same number of black nodes.
367 rotateLeft(parent, tree);
368 newnode = tree.root;
369 break;
370 } else {
371 var sibling = parent.left.?; // Same number of black nodes.
372
373 if (sibling.isRed()) {
374 sibling.setColor(Black);
375 parent.setColor(Red);
376 rotateRight(parent, tree);
377 sibling = parent.left.?; // Just rotated
378 }
379 if ((if (sibling.left) |n| n.isBlack() else true) and
380 (if (sibling.right) |n| n.isBlack() else true))
381 {
382 sibling.setColor(Red);
383 node = parent;
384 maybe_parent = parent.getParent();
385 continue;
386 }
387 if (if (sibling.left) |n| n.isBlack() else true) {
388 sibling.right.?.setColor(Black); // Same number of black nodes
389 sibling.setColor(Red);
390 rotateLeft(sibling, tree);
391 sibling = parent.left.?; // Just rotated
392 }
393 sibling.setColor(parent.getColor());
394 parent.setColor(Black);
395 sibling.left.?.setColor(Black); // Same number of black nodes
396 rotateRight(parent, tree);
397 newnode = tree.root;
398 break;
399 }
400
401 if (node.isRed())
402 break;
403 }
404
405 if (newnode) |n|
406 n.setColor(Black);
407 }
408
409 /// This is a shortcut to avoid removing and re-inserting an item with the same key.
410 pub fn replace(tree: *Tree, old: *Node, newconst: *Node) !void {
411 var new = newconst;
412
413 // I assume this can get optimized out if the caller already knows.
414 if (tree.compareFn(old, new, tree) != .eq) return ReplaceError.NotEqual;
415
416 if (old.getParent()) |parent| {
417 parent.setChild(new, parent.left == old);
418 } else
419 tree.root = new;
420
421 if (old.left) |left|
422 left.setParent(new);
423 if (old.right) |right|
424 right.setParent(new);
425
426 new.* = old.*;
427 }
428
429 pub fn init(f: fn (*Node, *Node, *Tree) Order) Tree {
430 return Tree{
431 .root = null,
432 .compareFn = f,
433 };
434 }
435};
436
437fn rotateLeft(node: *Node, tree: *Tree) void {
438 var p: *Node = node;
439 var q: *Node = node.right orelse unreachable;
440 var parent: *Node = undefined;
441
442 if (!p.isRoot()) {
443 parent = p.getParent().?;
444 if (parent.left == p) {
445 parent.left = q;
446 } else {
447 parent.right = q;
448 }
449 q.setParent(parent);
450 } else {
451 tree.root = q;
452 q.setParent(null);
453 }
454 p.setParent(q);
455
456 p.right = q.left;
457 if (p.right) |right| {
458 right.setParent(p);
459 }
460 q.left = p;
461}
462
463fn rotateRight(node: *Node, tree: *Tree) void {
464 var p: *Node = node;
465 var q: *Node = node.left orelse unreachable;
466 var parent: *Node = undefined;
467
468 if (!p.isRoot()) {
469 parent = p.getParent().?;
470 if (parent.left == p) {
471 parent.left = q;
472 } else {
473 parent.right = q;
474 }
475 q.setParent(parent);
476 } else {
477 tree.root = q;
478 q.setParent(null);
479 }
480 p.setParent(q);
481
482 p.left = q.right;
483 if (p.left) |left| {
484 left.setParent(p);
485 }
486 q.right = p;
487}
488
489fn doLookup(key: *Node, tree: *Tree, pparent: *?*Node, is_left: *bool) ?*Node {
490 var maybe_node: ?*Node = tree.root;
491
492 pparent.* = null;
493 is_left.* = false;
494
495 while (maybe_node) |node| {
496 const res = tree.compareFn(node, key, tree);
497 if (res == .eq) {
498 return node;
499 }
500 pparent.* = node;
501 switch (res) {
502 .gt => {
503 is_left.* = true;
504 maybe_node = node.left;
505 },
506 .lt => {
507 is_left.* = false;
508 maybe_node = node.right;
509 },
510 .eq => unreachable, // handled above
511 }
512 }
513 return null;
514}
515
516const testNumber = struct {
517 node: Node,
518 value: usize,
519};
520
521fn testGetNumber(node: *Node) *testNumber {
522 return @fieldParentPtr(testNumber, "node", node);
523}
524
525fn testCompare(l: *Node, r: *Node, contextIgnored: *Tree) Order {
526 var left = testGetNumber(l);
527 var right = testGetNumber(r);
528
529 if (left.value < right.value) {
530 return .lt;
531 } else if (left.value == right.value) {
532 return .eq;
533 } else if (left.value > right.value) {
534 return .gt;
535 }
536 unreachable;
537}
538
539fn testCompareReverse(l: *Node, r: *Node, contextIgnored: *Tree) Order {
540 return testCompare(r, l, contextIgnored);
541}
542
543test "rb" {
544 if (@import("builtin").arch == .aarch64) {
545 // TODO https://github.com/ziglang/zig/issues/3288
546 return error.SkipZigTest;
547 }
548
549 var tree = Tree.init(testCompare);
550 var ns: [10]testNumber = undefined;
551 ns[0].value = 42;
552 ns[1].value = 41;
553 ns[2].value = 40;
554 ns[3].value = 39;
555 ns[4].value = 38;
556 ns[5].value = 39;
557 ns[6].value = 3453;
558 ns[7].value = 32345;
559 ns[8].value = 392345;
560 ns[9].value = 4;
561
562 var dup: testNumber = undefined;
563 dup.value = 32345;
564
565 _ = tree.insert(&ns[1].node);
566 _ = tree.insert(&ns[2].node);
567 _ = tree.insert(&ns[3].node);
568 _ = tree.insert(&ns[4].node);
569 _ = tree.insert(&ns[5].node);
570 _ = tree.insert(&ns[6].node);
571 _ = tree.insert(&ns[7].node);
572 _ = tree.insert(&ns[8].node);
573 _ = tree.insert(&ns[9].node);
574 tree.remove(&ns[3].node);
575 testing.expect(tree.insert(&dup.node) == &ns[7].node);
576 try tree.replace(&ns[7].node, &dup.node);
577
578 var num: *testNumber = undefined;
579 num = testGetNumber(tree.first().?);
580 while (num.node.next() != null) {
581 testing.expect(testGetNumber(num.node.next().?).value > num.value);
582 num = testGetNumber(num.node.next().?);
583 }
584}
585
586test "inserting and looking up" {
587 var tree = Tree.init(testCompare);
588 var number: testNumber = undefined;
589 number.value = 1000;
590 _ = tree.insert(&number.node);
591 var dup: testNumber = undefined;
592 //Assert that tuples with identical value fields finds the same pointer
593 dup.value = 1000;
594 assert(tree.lookup(&dup.node) == &number.node);
595 //Assert that tuples with identical values do not clobber when inserted.
596 _ = tree.insert(&dup.node);
597 assert(tree.lookup(&dup.node) == &number.node);
598 assert(tree.lookup(&number.node) != &dup.node);
599 assert(testGetNumber(tree.lookup(&dup.node).?).value == testGetNumber(&dup.node).value);
600 //Assert that if looking for a non-existing value, return null.
601 var non_existing_value: testNumber = undefined;
602 non_existing_value.value = 1234;
603 assert(tree.lookup(&non_existing_value.node) == null);
604}
605
606test "multiple inserts, followed by calling first and last" {
607 if (@import("builtin").arch == .aarch64) {
608 // TODO https://github.com/ziglang/zig/issues/3288
609 return error.SkipZigTest;
610 }
611 var tree = Tree.init(testCompare);
612 var zeroth: testNumber = undefined;
613 zeroth.value = 0;
614 var first: testNumber = undefined;
615 first.value = 1;
616 var second: testNumber = undefined;
617 second.value = 2;
618 var third: testNumber = undefined;
619 third.value = 3;
620 _ = tree.insert(&zeroth.node);
621 _ = tree.insert(&first.node);
622 _ = tree.insert(&second.node);
623 _ = tree.insert(&third.node);
624 assert(testGetNumber(tree.first().?).value == 0);
625 assert(testGetNumber(tree.last().?).value == 3);
626 var lookupNode: testNumber = undefined;
627 lookupNode.value = 3;
628 assert(tree.lookup(&lookupNode.node) == &third.node);
629 tree.sort(testCompareReverse) catch unreachable;
630 assert(testGetNumber(tree.first().?).value == 3);
631 assert(testGetNumber(tree.last().?).value == 0);
632 assert(tree.lookup(&lookupNode.node) == &third.node);
633}
lib/std/std.zig-2
...@@ -14,7 +14,6 @@ pub const AutoArrayHashMap = array_hash_map.AutoArrayHashMap;...@@ -14,7 +14,6 @@ pub const AutoArrayHashMap = array_hash_map.AutoArrayHashMap;
14pub const AutoArrayHashMapUnmanaged = array_hash_map.AutoArrayHashMapUnmanaged;14pub const AutoArrayHashMapUnmanaged = array_hash_map.AutoArrayHashMapUnmanaged;
15pub const AutoHashMap = hash_map.AutoHashMap;15pub const AutoHashMap = hash_map.AutoHashMap;
16pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged;16pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged;
17pub const BloomFilter = @import("bloom_filter.zig").BloomFilter;
18pub const BufMap = @import("buf_map.zig").BufMap;17pub const BufMap = @import("buf_map.zig").BufMap;
19pub const BufSet = @import("buf_set.zig").BufSet;18pub const BufSet = @import("buf_set.zig").BufSet;
20pub const ChildProcess = @import("child_process.zig").ChildProcess;19pub const ChildProcess = @import("child_process.zig").ChildProcess;
...@@ -77,7 +76,6 @@ pub const packed_int_array = @import("packed_int_array.zig");...@@ -77,7 +76,6 @@ pub const packed_int_array = @import("packed_int_array.zig");
77pub const pdb = @import("pdb.zig");76pub const pdb = @import("pdb.zig");
78pub const process = @import("process.zig");77pub const process = @import("process.zig");
79pub const rand = @import("rand.zig");78pub const rand = @import("rand.zig");
80pub const rb = @import("rb.zig");
81pub const sort = @import("sort.zig");79pub const sort = @import("sort.zig");
82pub const ascii = @import("ascii.zig");80pub const ascii = @import("ascii.zig");
83pub const testing = @import("testing.zig");81pub const testing = @import("testing.zig");