diff --git a/src/kern/init.zig b/src/kern/init.zig index 7106692..7ec5a4c 100644 --- a/src/kern/init.zig +++ b/src/kern/init.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const rtl = @import("rtl"); const b = @import("base"); const pl = b.pl; const ke = b.ke; @@ -10,6 +11,7 @@ var thread0: ke.Thread = undefined; var stack: [8192]u8 align(16) = undefined; pub fn init(boot_info: *pl.BootInfo) linksection(b.init) void { + rtl.init(); std.log.info("hello, world", .{}); ki.impl.early_init(); pl.early_init(boot_info); diff --git a/src/rtl/bst.zig b/src/rtl/bst.zig index fc20480..68cb5f6 100644 --- a/src/rtl/bst.zig +++ b/src/rtl/bst.zig @@ -7,26 +7,38 @@ pub const Node = struct { parent: TaggedPtr(Node), }; +pub var nil = Node{ + .left = undefined, + .right = undefined, + .parent = undefined, +}; + +/// Initialize the sentinel nil node. This should be called before using any BSTs. +pub fn init_nil() void { + nil.left = &nil; + nil.right = &nil; + nil.parent.set_ptr(&nil); + nil.parent.set_tag(0); +} + /// Base struct for binary search trees. /// This should be used as a field in other types that are based on binary search trees, such as red-black trees or AVL trees. pub fn BST(comptime cmp: fn (*Node, *Node) std.math.Order) type { return struct { const Self = @This(); - root: *Node = undefined, - nil: Node = undefined, + root: *Node = &nil, - /// Initialize the BST. This must be called before using the tree. - pub fn init(self: *Self) void { - self.nil.left = &self.nil; - self.nil.right = &self.nil; - self.nil.parent = .init(&self.nil, 0); - self.root = &self.nil; + pub fn init() Self { + return Self{ + .root = &nil, + }; } /// Return whether or not the given node is the sentinel nil node. pub fn is_nil(self: *Self, node: *Node) bool { - return node == &self.nil; + _ = self; + return node == &nil; } /// Search for a node in the BST that matches `elem`, and return a pointer to it if found. @@ -79,7 +91,7 @@ pub fn BST(comptime cmp: fn (*Node, *Node) std.math.Order) type { } if (parent == self.root) { - return &self.nil; + return &nil; } return parent; @@ -105,9 +117,9 @@ pub fn BST(comptime cmp: fn (*Node, *Node) std.math.Order) type { if (self.is_nil(current)) { self.root = elem; - elem.left = &self.nil; - elem.right = &self.nil; - elem.parent.set_ptr(&self.nil); + elem.left = &nil; + elem.right = &nil; + elem.parent.set_ptr(&nil); return; } @@ -132,8 +144,8 @@ pub fn BST(comptime cmp: fn (*Node, *Node) std.math.Order) type { } } - elem.left = &self.nil; - elem.right = &self.nil; + elem.left = &nil; + elem.right = &nil; elem.parent.set_ptr(current); } @@ -158,8 +170,9 @@ fn node_cmp(a: *Node, b: *Node) std.math.Order { } test BST { - var tree: BST(node_cmp) = undefined; - tree.init(); + init_nil(); + + var tree: BST(node_cmp) = .init(); var nodes = [_]MyNode{ .{ .value = 5, .node = undefined }, diff --git a/src/rtl/rbtree.zig b/src/rtl/rbtree.zig index fcbbc43..76d6552 100644 --- a/src/rtl/rbtree.zig +++ b/src/rtl/rbtree.zig @@ -68,8 +68,10 @@ pub fn RBTree(comptime cmp: fn (*bst.Node, *bst.Node) std.math.Order) type { node.parent.set_ptr(y); } - pub fn init(self: *Self) void { - self.tree.init(); + pub fn init() Self { + return Self{ + .tree = bst.BST(cmp).init(), + }; } /// Insert the node `elem` into the tree. @@ -157,7 +159,7 @@ pub fn RBTree(comptime cmp: fn (*bst.Node, *bst.Node) std.math.Order) type { /// Delete the node `elem` from the tree. pub fn delete(self: *Self, elem: *bst.Node) void { var node = elem; - var child = &self.tree.nil; + var child = &bst.nil; var orig_color = get_color(node); if (self.tree.is_nil(node.left)) { @@ -304,10 +306,17 @@ fn check_invariants(tree: *RBTree(cmp_test_node)) !void { _ = try get_black_height(tree, tree.tree.root); } -test "invariants hold after each insertion" { - var tree: RBTree(cmp_test_node) = undefined; - tree.init(); +fn make_tree() RBTree(cmp_test_node) { + bst.init_nil(); + return .init(); +} +fn insert_all(tree: *RBTree(cmp_test_node), nodes: []TestNode) !void { + for (nodes) |*n| try tree.insert(&n.node); +} + +test "invariants hold after each insertion" { + var tree = make_tree(); var nodes = [_]TestNode{ .{ .value = 10, .node = undefined }, .{ .value = 5, .node = undefined }, @@ -319,7 +328,6 @@ test "invariants hold after each insertion" { .{ .value = 3, .node = undefined }, .{ .value = 6, .node = undefined }, }; - for (&nodes) |*n| { try tree.insert(&n.node); try check_invariants(&tree); @@ -327,33 +335,28 @@ test "invariants hold after each insertion" { } test "ascending insertion stays balanced" { - var tree: RBTree(cmp_test_node) = undefined; - tree.init(); + var tree = make_tree(); var nodes: [10]TestNode = undefined; for (&nodes, 0..) |*n, i| { - n.value = @intCast(i + 1); - n.node = undefined; + n.* = .{ .value = @intCast(i + 1), .node = undefined }; try tree.insert(&n.node); try check_invariants(&tree); } } test "descending insertion stays balanced" { - var tree: RBTree(cmp_test_node) = undefined; - tree.init(); + var tree = make_tree(); var nodes: [10]TestNode = undefined; for (&nodes, 0..) |*n, i| { - n.value = @intCast(10 - i); - n.node = undefined; + n.* = .{ .value = @intCast(10 - i), .node = undefined }; try tree.insert(&n.node); try check_invariants(&tree); } } test "single insert and delete" { - var tree: RBTree(cmp_test_node) = undefined; - tree.init(); - var n = TestNode{ .value = 69, .node = undefined }; + var tree = make_tree(); + var n = TestNode{ .value = 42, .node = undefined }; try tree.insert(&n.node); try check_invariants(&tree); tree.delete(&n.node); @@ -361,37 +364,32 @@ test "single insert and delete" { } test "delete root" { - var tree: RBTree(cmp_test_node) = undefined; - tree.init(); - + var tree = make_tree(); var nodes = [_]TestNode{ .{ .value = 5, .node = undefined }, .{ .value = 3, .node = undefined }, .{ .value = 7, .node = undefined }, }; - for (&nodes) |*n| try tree.insert(&n.node); + try insert_all(&tree, &nodes); tree.delete(&nodes[0].node); try check_invariants(&tree); try std.testing.expect(!tree.tree.is_empty()); } test "delete leaf" { - var tree: RBTree(cmp_test_node) = undefined; - tree.init(); + var tree = make_tree(); var nodes = [_]TestNode{ .{ .value = 5, .node = undefined }, .{ .value = 3, .node = undefined }, .{ .value = 7, .node = undefined }, }; - for (&nodes) |*n| try tree.insert(&n.node); + try insert_all(&tree, &nodes); tree.delete(&nodes[1].node); try check_invariants(&tree); } test "delete node with two children" { - var tree: RBTree(cmp_test_node) = undefined; - tree.init(); - + var tree = make_tree(); var nodes = [_]TestNode{ .{ .value = 10, .node = undefined }, .{ .value = 5, .node = undefined }, @@ -399,15 +397,13 @@ test "delete node with two children" { .{ .value = 3, .node = undefined }, .{ .value = 7, .node = undefined }, }; - for (&nodes) |*n| try tree.insert(&n.node); + try insert_all(&tree, &nodes); tree.delete(&nodes[1].node); try check_invariants(&tree); } test "invariants hold after each deletion" { - var tree: RBTree(cmp_test_node) = undefined; - tree.init(); - + var tree = make_tree(); var nodes = [_]TestNode{ .{ .value = 5, .node = undefined }, .{ .value = 3, .node = undefined }, @@ -419,7 +415,7 @@ test "invariants hold after each deletion" { .{ .value = 1, .node = undefined }, .{ .value = 9, .node = undefined }, }; - for (&nodes) |*n| try tree.insert(&n.node); + try insert_all(&tree, &nodes); const delete_order = [_]usize{ 3, 0, 7, 1, 5, 2, 6, 4, 8 }; for (delete_order) |i| { @@ -430,8 +426,7 @@ test "invariants hold after each deletion" { } test "duplicate insert returns error" { - var tree: RBTree(cmp_test_node) = undefined; - tree.init(); + var tree = make_tree(); var a = TestNode{ .value = 5, .node = undefined }; var b = TestNode{ .value = 5, .node = undefined }; try tree.insert(&a.node); diff --git a/src/rtl/root.zig b/src/rtl/root.zig index 0a2b0b7..bd430f7 100644 --- a/src/rtl/root.zig +++ b/src/rtl/root.zig @@ -11,6 +11,10 @@ pub const bst = @import("bst.zig"); pub const BST = bst.BST; pub const RBTree = @import("rbtree.zig").RBTree; +pub fn init() void { + bst.init_nil(); +} + test { std.testing.refAllDecls(@This()); }