rangemap: implement free_range

This commit is contained in:
Eugene Rossokha
2025-03-24 21:58:46 +02:00
parent 33d7adf63c
commit e72bbfff3f
3 changed files with 120 additions and 59 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ pub fn BTree(comptime N: type, comptime compare_fn: CompareFn(N), comptime deini
pub const Iterator = struct {
current: ?*Node,
pub fn next(self: *Iterator) ?*const Node {
pub fn next(self: *Iterator) ?*Node {
while (self.current) |n| {
const v = n;
+23 -4
View File
@@ -16,13 +16,32 @@ pub fn Range(comptime T: type) type {
/// Range length.
len: T,
const Self = @This();
/// Returns `start + len` of the range.
pub fn end(self: *const @This()) T {
pub fn end(self: Self) T {
return self.start + self.len;
}
/// Returns a range representing a subtraction of `other` from `self`
pub fn subtract(self: Self, other: Self) ?Self {
// No overlap
if (self.end() <= other.start or other.end() <= self.start)
return self;
// Self is fully in other
if (other.start <= self.start and other.end() >= self.end())
return null;
// Partial overlap
return if (self.start < other.start)
.{ .start = self.start, .len = other.start - self.start }
else
.{ .start = other.end(), .len = self.end() - other.end() };
}
/// Returns a range representing an intersections of `self` with an`other` range.
pub fn intersect(self: *const @This(), other: *const @This()) ?Range(T) {
pub fn intersect(self: Self, other: Self) ?Self {
if (self.start < other.start) {
const p = other.start - self.start;
if (p < self.len) {
@@ -38,11 +57,11 @@ pub fn Range(comptime T: type) type {
return null;
}
pub fn contains(self: *const @This(), scalar: T) bool {
pub fn contains(self: Self, scalar: T) bool {
return scalar >= self.start and scalar - self.start < self.len;
}
pub fn compare_disjoint(a: *const @This(), b: *const @This()) std.math.Order {
pub fn compare_disjoint(a: Self, b: Self) std.math.Order {
if (a.start >= b.end()) {
return .gt;
} else if (b.start >= a.end()) {
+96 -54
View File
@@ -11,11 +11,44 @@ pub fn RangeMap(
comptime K: type,
comptime V: type,
comptime ops: struct {
deinit_fn: ?fn(*V) void = null,
deinit_fn: ?fn (*V) void = null,
merge_fn: ?fn (*const V, *const V) bool = null,
},
) type {
return struct {
btree: Tree,
const Self = @This();
pub const Error = error{
ScalarOutOfRange,
RangeOutOfBounds,
} || Tree.Error;
pub const Tree = BTree(Node, compare_fn, deinit_node_fn);
pub const WalkFn = fn (*const Node) void;
pub const Iterator = struct {
inner: Tree.Iterator,
pub fn next(self: *Iterator) ?*Node {
if (self.inner.next()) |n| {
return &n.key;
} else {
return null;
}
}
};
pub fn init(gpa: Allocator) @This() {
return .{ .btree = Tree.init(gpa) };
}
pub fn deinit(self: *@This()) void {
self.btree.deinit();
}
pub const Node = struct {
key: Range(K),
value: V,
@@ -25,30 +58,8 @@ pub fn RangeMap(
}
};
pub const Error = error{
ScalarOutOfRange,
RangeOutOfBounds,
} || Tree.Error;
pub const WalkFn = fn (*const Node) void;
pub const Iterator = struct {
inner: Tree.Iterator,
pub fn next(self: *Iterator) ?*const Node {
if (self.inner.next()) |n| {
return &n.key;
} else {
return null;
}
}
};
pub const Tree = BTree(Node, compare_fn, deinit_node_fn);
fn compare_fn(a: *const Node, b: *const Node) Order {
return Range(K).compare_disjoint(&a.key, &b.key);
return Range(K).compare_disjoint(a.key, b.key);
}
fn deinit_node_fn(n: *Node) void {
@@ -57,16 +68,6 @@ pub fn RangeMap(
}
}
btree: Tree,
pub fn init(gpa: Allocator) @This() {
return .{ .btree = Tree.init(gpa) };
}
pub fn deinit(self: *@This()) void {
self.btree.deinit();
}
/// Returns the value at a given scalar point, along with the full range it belongs to.
pub fn get_scalar(self: *const @This(), scalar: K) ?*Node {
return if (self.get_scalar_node(scalar)) |n| &n.key else null;
@@ -87,6 +88,27 @@ pub fn RangeMap(
}.call);
}
/// Frees a range of the range map.
/// If there are any partially freed ranges, they are resized.
pub fn free_range(self: *Self, range: Range(K)) !void {
var it = self.iterator();
while (it.next()) |node| {
// If the node is fully in range, we can remove it
// If the node partially overlaps, we remove it and reinsert with the new range
// TODO: make the iterator actually work with it
if (range.intersect(node.key)) |intersection| {
if (node.key.subtract(intersection)) |range_resized| {
const value = node.value;
try self.btree.remove(node.*);
_ = try self.btree.insert(.{ .key = range_resized, .value = value });
} else {
try self.btree.remove(node.*);
}
it = self.iterator();
}
}
}
/// Splits a given node at a scalar point inside its interval.
///
/// The part of the interval before `at` is considered a "left" half, the remaining
@@ -245,11 +267,11 @@ test "Range map merging insertion" {
{
var it = map.iterator();
const n0 = it.next().?;
try std.testing.expectEqual(Range(u32) { .start = 0, .len = 10 }, n0.key);
try std.testing.expectEqual(Range(u32){ .start = 0, .len = 10 }, n0.key);
try std.testing.expectEqual(true, n0.value);
const n1 = it.next().?;
try std.testing.expectEqual(Range(u32) { .start = 10, .len = 30 }, n1.key);
try std.testing.expectEqual(Range(u32){ .start = 10, .len = 30 }, n1.key);
try std.testing.expectEqual(false, n1.value);
try std.testing.expectEqual(null, it.next());
@@ -262,19 +284,19 @@ test "Range map merging insertion" {
{
var it = map.iterator();
const n0 = it.next().?;
try std.testing.expectEqual(Range(u32) { .start = 0, .len = 10 }, n0.key);
try std.testing.expectEqual(Range(u32){ .start = 0, .len = 10 }, n0.key);
try std.testing.expectEqual(true, n0.value);
const n1 = it.next().?;
try std.testing.expectEqual(Range(u32) { .start = 10, .len = 30 }, n1.key);
try std.testing.expectEqual(Range(u32){ .start = 10, .len = 30 }, n1.key);
try std.testing.expectEqual(false, n1.value);
const n2 = it.next().?;
try std.testing.expectEqual(Range(u32) { .start = 40, .len = 10 }, n2.key);
try std.testing.expectEqual(Range(u32){ .start = 40, .len = 10 }, n2.key);
try std.testing.expectEqual(true, n2.value);
const n3 = it.next().?;
try std.testing.expectEqual(Range(u32) { .start = 50, .len = 10 }, n3.key);
try std.testing.expectEqual(Range(u32){ .start = 50, .len = 10 }, n3.key);
try std.testing.expectEqual(false, n3.value);
try std.testing.expectEqual(null, it.next());
@@ -287,23 +309,23 @@ test "Range map merging insertion" {
{
var it = map.iterator();
const n0 = it.next().?;
try std.testing.expectEqual(Range(u32) { .start = 0, .len = 10 }, n0.key);
try std.testing.expectEqual(Range(u32){ .start = 0, .len = 10 }, n0.key);
try std.testing.expectEqual(true, n0.value);
const n1 = it.next().?;
try std.testing.expectEqual(Range(u32) { .start = 10, .len = 30 }, n1.key);
try std.testing.expectEqual(Range(u32){ .start = 10, .len = 30 }, n1.key);
try std.testing.expectEqual(false, n1.value);
const n2 = it.next().?;
try std.testing.expectEqual(Range(u32) { .start = 40, .len = 10 }, n2.key);
try std.testing.expectEqual(Range(u32){ .start = 40, .len = 10 }, n2.key);
try std.testing.expectEqual(true, n2.value);
const n3 = it.next().?;
try std.testing.expectEqual(Range(u32) { .start = 50, .len = 20 }, n3.key);
try std.testing.expectEqual(Range(u32){ .start = 50, .len = 20 }, n3.key);
try std.testing.expectEqual(false, n3.value);
const n4 = it.next().?;
try std.testing.expectEqual(Range(u32) { .start = 71, .len = 9 }, n4.key);
try std.testing.expectEqual(Range(u32){ .start = 71, .len = 9 }, n4.key);
try std.testing.expectEqual(false, n4.value);
try std.testing.expectEqual(null, it.next());
@@ -315,19 +337,19 @@ test "Range map merging insertion" {
{
var it = map.iterator();
const n0 = it.next().?;
try std.testing.expectEqual(Range(u32) { .start = 0, .len = 10 }, n0.key);
try std.testing.expectEqual(Range(u32){ .start = 0, .len = 10 }, n0.key);
try std.testing.expectEqual(true, n0.value);
const n1 = it.next().?;
try std.testing.expectEqual(Range(u32) { .start = 10, .len = 30 }, n1.key);
try std.testing.expectEqual(Range(u32){ .start = 10, .len = 30 }, n1.key);
try std.testing.expectEqual(false, n1.value);
const n2 = it.next().?;
try std.testing.expectEqual(Range(u32) { .start = 40, .len = 10 }, n2.key);
try std.testing.expectEqual(Range(u32){ .start = 40, .len = 10 }, n2.key);
try std.testing.expectEqual(true, n2.value);
const n3 = it.next().?;
try std.testing.expectEqual(Range(u32) { .start = 50, .len = 30 }, n3.key);
try std.testing.expectEqual(Range(u32){ .start = 50, .len = 30 }, n3.key);
try std.testing.expectEqual(false, n3.value);
try std.testing.expectEqual(null, it.next());
@@ -340,23 +362,23 @@ test "Range map merging insertion" {
{
var it = map.iterator();
const n0 = it.next().?;
try std.testing.expectEqual(Range(u32) { .start = 0, .len = 10 }, n0.key);
try std.testing.expectEqual(Range(u32){ .start = 0, .len = 10 }, n0.key);
try std.testing.expectEqual(true, n0.value);
const n1 = it.next().?;
try std.testing.expectEqual(Range(u32) { .start = 10, .len = 30 }, n1.key);
try std.testing.expectEqual(Range(u32){ .start = 10, .len = 30 }, n1.key);
try std.testing.expectEqual(false, n1.value);
const n2 = it.next().?;
try std.testing.expectEqual(Range(u32) { .start = 40, .len = 10 }, n2.key);
try std.testing.expectEqual(Range(u32){ .start = 40, .len = 10 }, n2.key);
try std.testing.expectEqual(true, n2.value);
const n3 = it.next().?;
try std.testing.expectEqual(Range(u32) { .start = 50, .len = 30 }, n3.key);
try std.testing.expectEqual(Range(u32){ .start = 50, .len = 30 }, n3.key);
try std.testing.expectEqual(false, n3.value);
const n4 = it.next().?;
try std.testing.expectEqual(Range(u32) { .start = 100, .len = 20 }, n4.key);
try std.testing.expectEqual(Range(u32){ .start = 100, .len = 20 }, n4.key);
try std.testing.expectEqual(false, n4.value);
try std.testing.expectEqual(null, it.next());
@@ -449,3 +471,23 @@ test "Range map should disallow overflowing ranges" {
try std.testing.expectError(error.RangeOutOfBounds, map.insert(0xF0000000, 0x20000000, false));
}
test "Range map free" {
const Map = RangeMap(u32, bool, .{});
var map = Map.init(std.testing.allocator);
defer map.deinit();
_ = try map.insert(0x1000, 0x1000, true);
_ = try map.insert(0x3000, 0x1000, true);
_ = try map.insert(0x5000, 0x1000, true);
_ = try map.insert(0x7000, 0x1000, true);
try map.free_range(.{ .start = 0, .len = 0x3800 });
try std.testing.expectEqual(null, map.get_scalar(0x1000));
try std.testing.expectEqual(null, map.get_scalar(0x1800));
try std.testing.expectEqual(null, map.get_scalar(0x2000));
try std.testing.expectEqual(null, map.get_scalar(0x3000));
try std.testing.expectEqual(Range(u32){ .start = 0x3800, .len = 0x800 }, map.get_scalar(0x3800).?.key);
}