feat(rtl): use one sentinel nil node globally

This commit is contained in:
Loïc Grégoire
2026-04-05 22:55:12 -04:00
parent 5341e057d2
commit 6a8fcaeebc
4 changed files with 66 additions and 52 deletions
+2
View File
@@ -1,4 +1,5 @@
const std = @import("std"); const std = @import("std");
const rtl = @import("rtl");
const b = @import("base"); const b = @import("base");
const pl = b.pl; const pl = b.pl;
const ke = b.ke; const ke = b.ke;
@@ -10,6 +11,7 @@ var thread0: ke.Thread = undefined;
var stack: [8192]u8 align(16) = undefined; var stack: [8192]u8 align(16) = undefined;
pub fn init(boot_info: *pl.BootInfo) linksection(b.init) void { pub fn init(boot_info: *pl.BootInfo) linksection(b.init) void {
rtl.init();
std.log.info("hello, world", .{}); std.log.info("hello, world", .{});
ki.impl.early_init(); ki.impl.early_init();
pl.early_init(boot_info); pl.early_init(boot_info);
+30 -17
View File
@@ -7,26 +7,38 @@ pub const Node = struct {
parent: TaggedPtr(Node), 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. /// 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. /// 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 { pub fn BST(comptime cmp: fn (*Node, *Node) std.math.Order) type {
return struct { return struct {
const Self = @This(); const Self = @This();
root: *Node = undefined, root: *Node = &nil,
nil: Node = undefined,
/// Initialize the BST. This must be called before using the tree. pub fn init() Self {
pub fn init(self: *Self) void { return Self{
self.nil.left = &self.nil; .root = &nil,
self.nil.right = &self.nil; };
self.nil.parent = .init(&self.nil, 0);
self.root = &self.nil;
} }
/// Return whether or not the given node is the sentinel nil node. /// Return whether or not the given node is the sentinel nil node.
pub fn is_nil(self: *Self, node: *Node) bool { 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. /// 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) { if (parent == self.root) {
return &self.nil; return &nil;
} }
return parent; return parent;
@@ -105,9 +117,9 @@ pub fn BST(comptime cmp: fn (*Node, *Node) std.math.Order) type {
if (self.is_nil(current)) { if (self.is_nil(current)) {
self.root = elem; self.root = elem;
elem.left = &self.nil; elem.left = &nil;
elem.right = &self.nil; elem.right = &nil;
elem.parent.set_ptr(&self.nil); elem.parent.set_ptr(&nil);
return; return;
} }
@@ -132,8 +144,8 @@ pub fn BST(comptime cmp: fn (*Node, *Node) std.math.Order) type {
} }
} }
elem.left = &self.nil; elem.left = &nil;
elem.right = &self.nil; elem.right = &nil;
elem.parent.set_ptr(current); elem.parent.set_ptr(current);
} }
@@ -158,8 +170,9 @@ fn node_cmp(a: *Node, b: *Node) std.math.Order {
} }
test BST { test BST {
var tree: BST(node_cmp) = undefined; init_nil();
tree.init();
var tree: BST(node_cmp) = .init();
var nodes = [_]MyNode{ var nodes = [_]MyNode{
.{ .value = 5, .node = undefined }, .{ .value = 5, .node = undefined },
+30 -35
View File
@@ -68,8 +68,10 @@ pub fn RBTree(comptime cmp: fn (*bst.Node, *bst.Node) std.math.Order) type {
node.parent.set_ptr(y); node.parent.set_ptr(y);
} }
pub fn init(self: *Self) void { pub fn init() Self {
self.tree.init(); return Self{
.tree = bst.BST(cmp).init(),
};
} }
/// Insert the node `elem` into the tree. /// 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. /// Delete the node `elem` from the tree.
pub fn delete(self: *Self, elem: *bst.Node) void { pub fn delete(self: *Self, elem: *bst.Node) void {
var node = elem; var node = elem;
var child = &self.tree.nil; var child = &bst.nil;
var orig_color = get_color(node); var orig_color = get_color(node);
if (self.tree.is_nil(node.left)) { 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); _ = try get_black_height(tree, tree.tree.root);
} }
test "invariants hold after each insertion" { fn make_tree() RBTree(cmp_test_node) {
var tree: RBTree(cmp_test_node) = undefined; bst.init_nil();
tree.init(); 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{ var nodes = [_]TestNode{
.{ .value = 10, .node = undefined }, .{ .value = 10, .node = undefined },
.{ .value = 5, .node = undefined }, .{ .value = 5, .node = undefined },
@@ -319,7 +328,6 @@ test "invariants hold after each insertion" {
.{ .value = 3, .node = undefined }, .{ .value = 3, .node = undefined },
.{ .value = 6, .node = undefined }, .{ .value = 6, .node = undefined },
}; };
for (&nodes) |*n| { for (&nodes) |*n| {
try tree.insert(&n.node); try tree.insert(&n.node);
try check_invariants(&tree); try check_invariants(&tree);
@@ -327,33 +335,28 @@ test "invariants hold after each insertion" {
} }
test "ascending insertion stays balanced" { test "ascending insertion stays balanced" {
var tree: RBTree(cmp_test_node) = undefined; var tree = make_tree();
tree.init();
var nodes: [10]TestNode = undefined; var nodes: [10]TestNode = undefined;
for (&nodes, 0..) |*n, i| { for (&nodes, 0..) |*n, i| {
n.value = @intCast(i + 1); n.* = .{ .value = @intCast(i + 1), .node = undefined };
n.node = undefined;
try tree.insert(&n.node); try tree.insert(&n.node);
try check_invariants(&tree); try check_invariants(&tree);
} }
} }
test "descending insertion stays balanced" { test "descending insertion stays balanced" {
var tree: RBTree(cmp_test_node) = undefined; var tree = make_tree();
tree.init();
var nodes: [10]TestNode = undefined; var nodes: [10]TestNode = undefined;
for (&nodes, 0..) |*n, i| { for (&nodes, 0..) |*n, i| {
n.value = @intCast(10 - i); n.* = .{ .value = @intCast(10 - i), .node = undefined };
n.node = undefined;
try tree.insert(&n.node); try tree.insert(&n.node);
try check_invariants(&tree); try check_invariants(&tree);
} }
} }
test "single insert and delete" { test "single insert and delete" {
var tree: RBTree(cmp_test_node) = undefined; var tree = make_tree();
tree.init(); var n = TestNode{ .value = 42, .node = undefined };
var n = TestNode{ .value = 69, .node = undefined };
try tree.insert(&n.node); try tree.insert(&n.node);
try check_invariants(&tree); try check_invariants(&tree);
tree.delete(&n.node); tree.delete(&n.node);
@@ -361,37 +364,32 @@ test "single insert and delete" {
} }
test "delete root" { test "delete root" {
var tree: RBTree(cmp_test_node) = undefined; var tree = make_tree();
tree.init();
var nodes = [_]TestNode{ var nodes = [_]TestNode{
.{ .value = 5, .node = undefined }, .{ .value = 5, .node = undefined },
.{ .value = 3, .node = undefined }, .{ .value = 3, .node = undefined },
.{ .value = 7, .node = undefined }, .{ .value = 7, .node = undefined },
}; };
for (&nodes) |*n| try tree.insert(&n.node); try insert_all(&tree, &nodes);
tree.delete(&nodes[0].node); tree.delete(&nodes[0].node);
try check_invariants(&tree); try check_invariants(&tree);
try std.testing.expect(!tree.tree.is_empty()); try std.testing.expect(!tree.tree.is_empty());
} }
test "delete leaf" { test "delete leaf" {
var tree: RBTree(cmp_test_node) = undefined; var tree = make_tree();
tree.init();
var nodes = [_]TestNode{ var nodes = [_]TestNode{
.{ .value = 5, .node = undefined }, .{ .value = 5, .node = undefined },
.{ .value = 3, .node = undefined }, .{ .value = 3, .node = undefined },
.{ .value = 7, .node = undefined }, .{ .value = 7, .node = undefined },
}; };
for (&nodes) |*n| try tree.insert(&n.node); try insert_all(&tree, &nodes);
tree.delete(&nodes[1].node); tree.delete(&nodes[1].node);
try check_invariants(&tree); try check_invariants(&tree);
} }
test "delete node with two children" { test "delete node with two children" {
var tree: RBTree(cmp_test_node) = undefined; var tree = make_tree();
tree.init();
var nodes = [_]TestNode{ var nodes = [_]TestNode{
.{ .value = 10, .node = undefined }, .{ .value = 10, .node = undefined },
.{ .value = 5, .node = undefined }, .{ .value = 5, .node = undefined },
@@ -399,15 +397,13 @@ test "delete node with two children" {
.{ .value = 3, .node = undefined }, .{ .value = 3, .node = undefined },
.{ .value = 7, .node = undefined }, .{ .value = 7, .node = undefined },
}; };
for (&nodes) |*n| try tree.insert(&n.node); try insert_all(&tree, &nodes);
tree.delete(&nodes[1].node); tree.delete(&nodes[1].node);
try check_invariants(&tree); try check_invariants(&tree);
} }
test "invariants hold after each deletion" { test "invariants hold after each deletion" {
var tree: RBTree(cmp_test_node) = undefined; var tree = make_tree();
tree.init();
var nodes = [_]TestNode{ var nodes = [_]TestNode{
.{ .value = 5, .node = undefined }, .{ .value = 5, .node = undefined },
.{ .value = 3, .node = undefined }, .{ .value = 3, .node = undefined },
@@ -419,7 +415,7 @@ test "invariants hold after each deletion" {
.{ .value = 1, .node = undefined }, .{ .value = 1, .node = undefined },
.{ .value = 9, .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 }; const delete_order = [_]usize{ 3, 0, 7, 1, 5, 2, 6, 4, 8 };
for (delete_order) |i| { for (delete_order) |i| {
@@ -430,8 +426,7 @@ test "invariants hold after each deletion" {
} }
test "duplicate insert returns error" { test "duplicate insert returns error" {
var tree: RBTree(cmp_test_node) = undefined; var tree = make_tree();
tree.init();
var a = TestNode{ .value = 5, .node = undefined }; var a = TestNode{ .value = 5, .node = undefined };
var b = TestNode{ .value = 5, .node = undefined }; var b = TestNode{ .value = 5, .node = undefined };
try tree.insert(&a.node); try tree.insert(&a.node);
+4
View File
@@ -11,6 +11,10 @@ pub const bst = @import("bst.zig");
pub const BST = bst.BST; pub const BST = bst.BST;
pub const RBTree = @import("rbtree.zig").RBTree; pub const RBTree = @import("rbtree.zig").RBTree;
pub fn init() void {
bst.init_nil();
}
test { test {
std.testing.refAllDecls(@This()); std.testing.refAllDecls(@This());
} }