mirror of
https://github.com/rdmsr/zag.git
synced 2026-10-27 13:58:20 +00:00
feat(rtl): add bst and tagged pointer
This commit is contained in:
+101
@@ -0,0 +1,101 @@
|
||||
const rtl = @import("rtl");
|
||||
const std = @import("std");
|
||||
|
||||
pub const Node = struct {
|
||||
left: *Node,
|
||||
right: *Node,
|
||||
parent: rtl.TaggedPtr(Node),
|
||||
};
|
||||
|
||||
/// 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,
|
||||
|
||||
/// 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 = &self.nil;
|
||||
self.root = &self.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;
|
||||
}
|
||||
|
||||
/// Search for a node in the BST that matches `elem`, and return a pointer to it if found.
|
||||
pub fn search(self: *Self, elem: *Node) ?*Node {
|
||||
var node = self.root;
|
||||
while (!self.is_nil(node)) {
|
||||
const order = cmp(elem, node);
|
||||
if (order == .eq) {
|
||||
return node;
|
||||
} else if (order == .lt) {
|
||||
node = node.left;
|
||||
} else {
|
||||
node = node.right;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Return a pointer to the minimum node in the subtree rooted at `node`.
|
||||
pub fn minimum(self: *Self, node: *Node) *Node {
|
||||
var current = node;
|
||||
while (!self.is_nil(current.left)) {
|
||||
current = current.left;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
/// Return a pointer to the maximum node in the subtree rooted at `node`.
|
||||
pub fn maximum(self: *Self, node: *Node) *Node {
|
||||
var current = node;
|
||||
while (!self.is_nil(current.right)) {
|
||||
current = current.right;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
/// Return a pointer to the successor of `node`, or nil if `node` is the maximum element in the tree.
|
||||
pub fn successor(self: *Self, node: *Node) *Node {
|
||||
if (!self.is_nil(node.right)) {
|
||||
// Minimum of right subtree
|
||||
return self.minimum(node.right);
|
||||
}
|
||||
|
||||
// Go up and find it.
|
||||
var current = node;
|
||||
var parent = current.parent.get_ptr();
|
||||
while (!self.is_nil(parent) and current == parent.right) {
|
||||
current = parent;
|
||||
parent = parent.parent.get_ptr();
|
||||
}
|
||||
|
||||
if (parent == self.root) {
|
||||
return &self.nil;
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
/// Transplant the subtree rooted at `u` with the subtree rooted at `v`. This is used as a helper function.
|
||||
pub fn transplant(self: *Self, u: *Node, v: *Node) void {
|
||||
const u_parent = u.parent.get_ptr();
|
||||
if (self.is_nil(u_parent)) {
|
||||
self.root = v;
|
||||
} else if (u == u_parent.left) {
|
||||
u_parent.left = v;
|
||||
} else {
|
||||
u_parent.right = v;
|
||||
}
|
||||
v.parent.set_ptr(u_parent);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -6,6 +6,8 @@ pub const barrier = @import("barrier.zig");
|
||||
pub const cmdline = @import("cmdline.zig");
|
||||
pub const pairing_heap = @import("pairing_heap.zig");
|
||||
pub const PairingHeap = pairing_heap.PairingHeap;
|
||||
pub const TaggedPtr = @import("tagged_ptr.zig").TaggedPtr;
|
||||
pub const bst = @import("bst.zig");
|
||||
|
||||
test {
|
||||
std.testing.refAllDecls(@This());
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/// Struct used to wrap a pointer with a 2-bit tag in the lower bits.
|
||||
/// This assumes that the pointer is at least 4-byte aligned.
|
||||
pub fn TaggedPtr(comptime T: type) type {
|
||||
return struct {
|
||||
value: usize,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
const mask: usize = 0x3;
|
||||
|
||||
pub fn init(ptr: *T, tag: u2) Self {
|
||||
return Self{ .value = (@intFromPtr(ptr) & ~mask) | (@as(usize, tag) & mask) };
|
||||
}
|
||||
|
||||
/// Return the pointer with the tag bits masked out.
|
||||
pub fn get_ptr(self: *Self) *T {
|
||||
return @ptrFromInt(self.value & ~mask);
|
||||
}
|
||||
|
||||
/// Set the tag bits to `tag`, while preserving the pointer.
|
||||
pub fn set_tag(self: *Self, tag: u2) void {
|
||||
self.value = (self.value & ~mask) | (@as(usize, tag) & mask);
|
||||
}
|
||||
|
||||
/// Return the tag bits.
|
||||
pub fn get_tag(self: *Self) u2 {
|
||||
return @as(u2, @truncate(self.value & mask));
|
||||
}
|
||||
|
||||
/// Set the pointer to `ptr`, while preserving the tag bits.
|
||||
pub fn set_ptr(self: *Self, ptr: *T) void {
|
||||
self.value = (@intFromPtr(ptr) & ~mask) | (self.value & mask);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
test TaggedPtr {
|
||||
var x: u32 = 0;
|
||||
var tagged = TaggedPtr(u32).init(&x, 2);
|
||||
|
||||
try std.testing.expectEqual(&x, tagged.get_ptr());
|
||||
try std.testing.expectEqual(2, tagged.get_tag());
|
||||
|
||||
tagged.set_tag(1);
|
||||
try std.testing.expectEqual(1, tagged.get_tag());
|
||||
|
||||
var y: u32 = 0;
|
||||
tagged.set_ptr(&y);
|
||||
try std.testing.expectEqual(&y, tagged.get_ptr());
|
||||
}
|
||||
Reference in New Issue
Block a user