178 lines
5.6 KiB
Zig
178 lines
5.6 KiB
Zig
//! Represents some kind of signed range with an inclusive lower bound and an exclusive upper bound.
|
|
//! An empty Range can be represented by `start == end`.
|
|
|
|
const std = @import("std");
|
|
|
|
const assert = std.debug.assert;
|
|
|
|
const Range = @This();
|
|
/// Inclusive lower bound of the range.
|
|
start: i64,
|
|
/// Exclusive upper bound of the range.
|
|
end: i64,
|
|
|
|
pub fn size(range: Range) u64 {
|
|
assert(range.end >= range.start);
|
|
// return @intCast(@as(i128, range.end) - range.start); // prevent overflow
|
|
return @intCast(range.end - range.start);
|
|
}
|
|
|
|
pub fn alignTo(range: Range, alignment: u64) Range {
|
|
assert(range.end >= range.start);
|
|
assert(std.math.isPowerOfTwo(alignment));
|
|
assert(alignment <= std.math.maxInt(i64));
|
|
const lower = std.mem.alignBackward(i64, range.start, @intCast(alignment));
|
|
const upper = std.mem.alignForward(i64, range.end, @intCast(alignment));
|
|
assert(upper >= lower);
|
|
return .{ .start = lower, .end = upper };
|
|
}
|
|
|
|
pub fn overlaps(range: Range, other: Range) bool {
|
|
assert(range.end >= range.start);
|
|
assert(other.end >= other.start);
|
|
return range.start < other.end and other.start < range.end;
|
|
}
|
|
|
|
pub fn equals(range: Range, other: Range) bool {
|
|
assert(range.end >= range.start);
|
|
assert(other.end >= other.start);
|
|
return range.start == other.start and range.end == other.end;
|
|
}
|
|
|
|
pub fn contains(range: Range, other: Range) bool {
|
|
assert(range.end >= range.start);
|
|
assert(other.end >= other.start);
|
|
return range.start <= other.start and range.end >= other.end;
|
|
}
|
|
|
|
pub fn touches(range: Range, other: Range) bool {
|
|
assert(range.end >= range.start);
|
|
assert(other.end >= other.start);
|
|
return range.start <= other.end and other.start <= range.end;
|
|
}
|
|
|
|
/// Ranges are considered equal if they touch.
|
|
pub fn compare(lhs: Range, rhs: Range) std.math.Order {
|
|
assert(lhs.end >= lhs.start);
|
|
assert(rhs.end >= rhs.start);
|
|
return if (lhs.start > rhs.end) .gt else if (lhs.end < rhs.start) .lt else .eq;
|
|
}
|
|
|
|
pub fn getStart(range: Range, T: type) T {
|
|
return @intCast(range.start);
|
|
}
|
|
|
|
pub fn getEnd(range: Range, T: type) T {
|
|
return @intCast(range.end);
|
|
}
|
|
|
|
pub fn format(
|
|
self: @This(),
|
|
writer: *std.Io.Writer,
|
|
) std.Io.Writer.Error!void {
|
|
try writer.print(".{{ .start = 0x{x}, .end = 0x{x} }}", .{ self.start, self.end });
|
|
}
|
|
|
|
test "AddressRange size" {
|
|
const range = Range{ .start = 100, .end = 250 };
|
|
try std.testing.expectEqual(@as(u64, 150), range.size());
|
|
}
|
|
|
|
test "AddressRange alignTo unaligned" {
|
|
const range = Range{ .start = 101, .end = 199 };
|
|
const aligned = range.alignTo(16);
|
|
try std.testing.expectEqual(@as(i64, 96), aligned.start);
|
|
try std.testing.expectEqual(@as(i64, 208), aligned.end);
|
|
}
|
|
|
|
test "AddressRange alignTo already aligned" {
|
|
const range = Range{ .start = 64, .end = 128 };
|
|
const aligned = range.alignTo(64);
|
|
try std.testing.expectEqual(@as(i64, 64), aligned.start);
|
|
try std.testing.expectEqual(@as(i64, 128), aligned.end);
|
|
}
|
|
|
|
test "AddressRange no overlap before" {
|
|
const base = Range{ .start = 100, .end = 200 };
|
|
const other = Range{ .start = 0, .end = 100 };
|
|
try std.testing.expect(!base.overlaps(other));
|
|
}
|
|
|
|
test "AddressRange no overlap after" {
|
|
const base = Range{ .start = 100, .end = 200 };
|
|
const other = Range{ .start = 200, .end = 300 };
|
|
try std.testing.expect(!base.overlaps(other));
|
|
}
|
|
|
|
test "AddressRange overlap at start" {
|
|
const base = Range{ .start = 100, .end = 200 };
|
|
const other = Range{ .start = 50, .end = 150 };
|
|
try std.testing.expect(base.overlaps(other));
|
|
}
|
|
|
|
test "AddressRange overlap at end" {
|
|
const base = Range{ .start = 100, .end = 200 };
|
|
const other = Range{ .start = 150, .end = 250 };
|
|
try std.testing.expect(base.overlaps(other));
|
|
}
|
|
|
|
test "AddressRange overlap contained" {
|
|
const base = Range{ .start = 100, .end = 200 };
|
|
const other = Range{ .start = 120, .end = 180 };
|
|
try std.testing.expect(base.overlaps(other));
|
|
}
|
|
|
|
test "AddressRange overlap containing" {
|
|
const base = Range{ .start = 100, .end = 200 };
|
|
const other = Range{ .start = 50, .end = 250 };
|
|
try std.testing.expect(base.overlaps(other));
|
|
}
|
|
|
|
test "AddressRange overlap identical" {
|
|
const base = Range{ .start = 100, .end = 200 };
|
|
const other = Range{ .start = 100, .end = 200 };
|
|
try std.testing.expect(base.overlaps(other));
|
|
}
|
|
|
|
test "AddressRange touches before" {
|
|
const base = Range{ .start = 100, .end = 200 };
|
|
const other = Range{ .start = 0, .end = 100 };
|
|
try std.testing.expect(base.touches(other));
|
|
}
|
|
|
|
test "AddressRange touches after" {
|
|
const base = Range{ .start = 100, .end = 200 };
|
|
const other = Range{ .start = 200, .end = 300 };
|
|
try std.testing.expect(base.touches(other));
|
|
}
|
|
|
|
test "AddressRange touches at start" {
|
|
const base = Range{ .start = 100, .end = 200 };
|
|
const other = Range{ .start = 50, .end = 150 };
|
|
try std.testing.expect(base.touches(other));
|
|
}
|
|
|
|
test "AddressRange touches at end" {
|
|
const base = Range{ .start = 100, .end = 200 };
|
|
const other = Range{ .start = 150, .end = 250 };
|
|
try std.testing.expect(base.touches(other));
|
|
}
|
|
|
|
test "AddressRange touches contained" {
|
|
const base = Range{ .start = 100, .end = 200 };
|
|
const other = Range{ .start = 120, .end = 180 };
|
|
try std.testing.expect(base.touches(other));
|
|
}
|
|
|
|
test "AddressRange touches containing" {
|
|
const base = Range{ .start = 100, .end = 200 };
|
|
const other = Range{ .start = 50, .end = 250 };
|
|
try std.testing.expect(base.touches(other));
|
|
}
|
|
|
|
test "AddressRange touches identical" {
|
|
const base = Range{ .start = 100, .end = 200 };
|
|
const other = Range{ .start = 100, .end = 200 };
|
|
try std.testing.expect(base.touches(other));
|
|
}
|