747 lines
27 KiB
Zig
747 lines
27 KiB
Zig
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
const testing = std.testing;
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
///
|
|
/// A comptime bitmap that uses a specific type to store the entries. No allocators needed.
|
|
///
|
|
/// Arguments:
|
|
/// IN BitmapType: type - The integer type to use to store entries.
|
|
///
|
|
/// Return: type.
|
|
/// The bitmap type created.
|
|
///
|
|
pub fn ComptimeBitmap(comptime BitmapType: type) type {
|
|
return struct {
|
|
const Self = @This();
|
|
|
|
/// The number of entries that one bitmap type can hold. Evaluates to the number of bits the type has
|
|
pub const NUM_ENTRIES: usize = std.meta.bitCount(BitmapType);
|
|
|
|
/// The value that a full bitmap will have
|
|
pub const BITMAP_FULL = std.math.maxInt(BitmapType);
|
|
|
|
/// The type of an index into a bitmap entry. The smallest integer needed to represent all bit positions in the bitmap entry type
|
|
pub const IndexType = std.meta.IntType(.unsigned, std.math.log2(std.math.ceilPowerOfTwo(u16, std.meta.bitCount(BitmapType)) catch unreachable));
|
|
|
|
bitmap: BitmapType,
|
|
num_free_entries: BitmapType,
|
|
|
|
///
|
|
/// Create an instance of this bitmap type.
|
|
///
|
|
/// Return: Self.
|
|
/// The bitmap instance.
|
|
///
|
|
pub fn init() Self {
|
|
return .{
|
|
.bitmap = 0,
|
|
.num_free_entries = NUM_ENTRIES,
|
|
};
|
|
}
|
|
|
|
///
|
|
/// Set an entry within a bitmap as occupied.
|
|
///
|
|
/// Arguments:
|
|
/// IN/OUT self: *Self - The bitmap to modify.
|
|
/// IN idx: IndexType - The index within the bitmap to set.
|
|
///
|
|
pub fn setEntry(self: *Self, idx: IndexType) void {
|
|
if (!self.isSet(idx)) {
|
|
self.bitmap |= self.indexToBit(idx);
|
|
self.num_free_entries -= 1;
|
|
}
|
|
}
|
|
|
|
///
|
|
/// Set an entry within a bitmap as unoccupied.
|
|
///
|
|
/// Arguments:
|
|
/// IN/OUT self: *Self - The bitmap to modify.
|
|
/// IN idx: IndexType - The index within the bitmap to clear.
|
|
///
|
|
pub fn clearEntry(self: *Self, idx: IndexType) void {
|
|
if (self.isSet(idx)) {
|
|
self.bitmap &= ~self.indexToBit(idx);
|
|
self.num_free_entries += 1;
|
|
}
|
|
}
|
|
|
|
///
|
|
/// Convert a global bitmap index into the bit corresponding to an entry within a single BitmapType.
|
|
///
|
|
/// Arguments:
|
|
/// IN self: *const Self - The bitmap to use.
|
|
/// IN idx: IndexType - The index into all of the bitmaps entries.
|
|
///
|
|
/// Return: BitmapType.
|
|
/// The bit corresponding to that index but within a single BitmapType.
|
|
///
|
|
fn indexToBit(self: *const Self, idx: IndexType) BitmapType {
|
|
return @as(BitmapType, 1) << idx;
|
|
}
|
|
|
|
///
|
|
/// Find a number of contiguous free entries and set them.
|
|
///
|
|
/// Arguments:
|
|
/// IN/OUT self: *Self - The bitmap to modify.
|
|
/// IN num: usize - The number of entries to set.
|
|
///
|
|
/// Return: ?IndexType
|
|
/// The first entry set or null if there weren't enough contiguous entries.
|
|
///
|
|
pub fn setContiguous(self: *Self, num: usize) ?IndexType {
|
|
if (num > self.num_free_entries) {
|
|
return null;
|
|
}
|
|
|
|
var count: usize = 0;
|
|
var start: ?IndexType = null;
|
|
|
|
var bit: IndexType = 0;
|
|
while (true) {
|
|
const entry = bit;
|
|
if (entry >= NUM_ENTRIES) {
|
|
return null;
|
|
}
|
|
if ((self.bitmap & @as(BitmapType, 1) << bit) != 0) {
|
|
// This is a one so clear the progress
|
|
count = 0;
|
|
start = null;
|
|
} else {
|
|
// It's a zero so increment the count
|
|
count += 1;
|
|
if (start == null) {
|
|
// Start of the contiguous zeroes
|
|
start = entry;
|
|
}
|
|
if (count == num) {
|
|
// Reached the desired number
|
|
break;
|
|
}
|
|
}
|
|
// Avoiding overflow by checking if bit is less than the max - 1
|
|
if (bit < NUM_ENTRIES - 1) {
|
|
bit += 1;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (count == num) {
|
|
if (start) |start_entry| {
|
|
var i: IndexType = 0;
|
|
while (i < num) : (i += 1) {
|
|
self.setEntry(start_entry + i);
|
|
}
|
|
return start_entry;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
///
|
|
/// Set the first free entry within the bitmaps as occupied.
|
|
///
|
|
/// Return: ?IndexType.
|
|
/// The index within all bitmaps that was set or null if there wasn't one free.
|
|
/// 0 .. NUM_ENTRIES - 1 if in the first bitmap, NUM_ENTRIES .. NUM_ENTRIES * 2 - 1 if in the second etc.
|
|
///
|
|
pub fn setFirstFree(self: *Self) ?IndexType {
|
|
if (self.num_free_entries == 0 or self.bitmap == BITMAP_FULL) {
|
|
std.debug.assert(self.num_free_entries == 0 and self.bitmap == BITMAP_FULL);
|
|
return null;
|
|
}
|
|
const bit = @truncate(IndexType, @ctz(BitmapType, ~self.bitmap));
|
|
self.setEntry(bit);
|
|
return bit;
|
|
}
|
|
|
|
///
|
|
/// Check if an entry is set.
|
|
///
|
|
/// Arguments:
|
|
/// IN self: *const Self - The bitmap to check.
|
|
/// IN idx: usize - The entry to check.
|
|
///
|
|
/// Return: bool.
|
|
/// True if the entry is set, else false.
|
|
///
|
|
pub fn isSet(self: *const Self, idx: IndexType) bool {
|
|
return (self.bitmap & self.indexToBit(idx)) != 0;
|
|
}
|
|
};
|
|
}
|
|
|
|
///
|
|
/// A bitmap that uses a specific type to store the entries.
|
|
///
|
|
/// Arguments:
|
|
/// IN BitmapType: type - The integer type to use to store entries.
|
|
///
|
|
/// Return: type.
|
|
/// The bitmap type created.
|
|
///
|
|
pub fn Bitmap(comptime BitmapType: type) type {
|
|
return struct {
|
|
/// The possible errors thrown by bitmap functions
|
|
pub const BitmapError = error{
|
|
/// The address given was outside the region covered by a bitmap
|
|
OutOfBounds,
|
|
};
|
|
|
|
const Self = @This();
|
|
|
|
/// The number of entries that one bitmap type can hold. Evaluates to the number of bits the type has
|
|
pub const ENTRIES_PER_BITMAP: usize = std.meta.bitCount(BitmapType);
|
|
|
|
/// The value that a full bitmap will have
|
|
pub const BITMAP_FULL = std.math.maxInt(BitmapType);
|
|
|
|
/// The type of an index into a bitmap entry. The smallest integer needed to represent all bit positions in the bitmap entry type
|
|
pub const IndexType = std.meta.IntType(.unsigned, std.math.log2(std.math.ceilPowerOfTwo(u16, std.meta.bitCount(BitmapType)) catch unreachable));
|
|
|
|
num_bitmaps: usize,
|
|
num_entries: usize,
|
|
bitmaps: []BitmapType,
|
|
num_free_entries: usize,
|
|
allocator: *std.mem.Allocator,
|
|
|
|
///
|
|
/// Create an instance of this bitmap type.
|
|
///
|
|
/// Arguments:
|
|
/// IN num_entries: usize - The number of entries that the bitmap created will have.
|
|
/// The number of BitmapType required to store this many entries will be allocated and each will be zeroed.
|
|
/// IN allocator: *std.mem.Allocator - The allocator to use when allocating the BitmapTypes required.
|
|
///
|
|
/// Return: Self.
|
|
/// The bitmap instance.
|
|
///
|
|
/// Error: std.mem.Allocator.Error
|
|
/// OutOfMemory: There isn't enough memory available to allocate the required number of BitmapType.
|
|
///
|
|
pub fn init(num_entries: usize, allocator: *std.mem.Allocator) !Self {
|
|
const num = std.mem.alignForward(num_entries, ENTRIES_PER_BITMAP) / ENTRIES_PER_BITMAP;
|
|
const self = Self{
|
|
.num_bitmaps = num,
|
|
.num_entries = num_entries,
|
|
.bitmaps = try allocator.alloc(BitmapType, num),
|
|
.num_free_entries = num_entries,
|
|
.allocator = allocator,
|
|
};
|
|
for (self.bitmaps) |*bmp| {
|
|
bmp.* = 0;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
///
|
|
/// Clone this bitmap.
|
|
///
|
|
/// Arguments:
|
|
/// IN self: *Self - The bitmap to clone.
|
|
///
|
|
/// Return: Self
|
|
/// The cloned bitmap
|
|
///
|
|
/// Error: std.mem.Allocator.Error
|
|
/// OutOfMemory: There isn't enough memory available to allocate the required number of BitmapType.
|
|
///
|
|
pub fn clone(self: *const Self) std.mem.Allocator.Error!Self {
|
|
var copy = try init(self.num_entries, self.allocator);
|
|
var i: usize = 0;
|
|
while (i < copy.num_entries) : (i += 1) {
|
|
if (self.isSet(i) catch unreachable) {
|
|
copy.setEntry(i) catch unreachable;
|
|
}
|
|
}
|
|
return copy;
|
|
}
|
|
|
|
///
|
|
/// Free the memory occupied by this bitmap's internal state. It will become unusable afterwards.
|
|
///
|
|
/// Arguments:
|
|
/// IN self: *Self - The bitmap that should be deinitialised
|
|
///
|
|
pub fn deinit(self: *Self) void {
|
|
self.allocator.free(self.bitmaps);
|
|
}
|
|
|
|
///
|
|
/// Set an entry within a bitmap as occupied.
|
|
///
|
|
/// Arguments:
|
|
/// IN/OUT self: *Self - The bitmap to modify.
|
|
/// IN idx: usize - The index within the bitmap to set.
|
|
///
|
|
/// Error: BitmapError.
|
|
/// OutOfBounds: The index given is out of bounds.
|
|
///
|
|
pub fn setEntry(self: *Self, idx: usize) BitmapError!void {
|
|
if (idx >= self.num_entries) {
|
|
return BitmapError.OutOfBounds;
|
|
}
|
|
if (!try self.isSet(idx)) {
|
|
const bit = self.indexToBit(idx);
|
|
self.bitmaps[idx / ENTRIES_PER_BITMAP] |= bit;
|
|
self.num_free_entries -= 1;
|
|
}
|
|
}
|
|
|
|
///
|
|
/// Set an entry within a bitmap as unoccupied.
|
|
///
|
|
/// Arguments:
|
|
/// IN/OUT self: *Self - The bitmap to modify.
|
|
/// IN idx: usize - The index within the bitmap to clear.
|
|
///
|
|
/// Error: BitmapError.
|
|
/// OutOfBounds: The index given is out of bounds.
|
|
///
|
|
pub fn clearEntry(self: *Self, idx: usize) BitmapError!void {
|
|
if (idx >= self.num_entries) {
|
|
return BitmapError.OutOfBounds;
|
|
}
|
|
if (try self.isSet(idx)) {
|
|
const bit = self.indexToBit(idx);
|
|
self.bitmaps[idx / ENTRIES_PER_BITMAP] &= ~bit;
|
|
self.num_free_entries += 1;
|
|
}
|
|
}
|
|
|
|
///
|
|
/// Convert a global bitmap index into the bit corresponding to an entry within a single BitmapType.
|
|
///
|
|
/// Arguments:
|
|
/// IN self: *const Self - The bitmap to use.
|
|
/// IN idx: usize - The index into all of the bitmaps entries.
|
|
///
|
|
/// Return: BitmapType.
|
|
/// The bit corresponding to that index but within a single BitmapType.
|
|
///
|
|
fn indexToBit(self: *const Self, idx: usize) BitmapType {
|
|
return @as(BitmapType, 1) << @intCast(IndexType, idx % ENTRIES_PER_BITMAP);
|
|
}
|
|
|
|
///
|
|
/// Find a number of contiguous free entries and set them.
|
|
///
|
|
/// Arguments:
|
|
/// IN/OUT self: *Self - The bitmap to modify.
|
|
/// IN num: usize - The number of entries to set.
|
|
///
|
|
/// Return: ?usize
|
|
/// The first entry set or null if there weren't enough contiguous entries.
|
|
///
|
|
pub fn setContiguous(self: *Self, num: usize) ?usize {
|
|
if (num > self.num_free_entries) {
|
|
return null;
|
|
}
|
|
|
|
var count: usize = 0;
|
|
var start: ?usize = null;
|
|
for (self.bitmaps) |bmp, i| {
|
|
var bit: IndexType = 0;
|
|
while (true) {
|
|
const entry = bit + i * ENTRIES_PER_BITMAP;
|
|
if (entry >= self.num_entries) {
|
|
return null;
|
|
}
|
|
if ((bmp & @as(BitmapType, 1) << bit) != 0) {
|
|
// This is a one so clear the progress
|
|
count = 0;
|
|
start = null;
|
|
} else {
|
|
// It's a zero so increment the count
|
|
count += 1;
|
|
if (start == null) {
|
|
// Start of the contiguous zeroes
|
|
start = entry;
|
|
}
|
|
if (count == num) {
|
|
// Reached the desired number
|
|
break;
|
|
}
|
|
}
|
|
// Avoiding overflow by checking if bit is less than the max - 1
|
|
if (bit < ENTRIES_PER_BITMAP - 1) {
|
|
bit += 1;
|
|
} else {
|
|
// Reached the end of the bitmap
|
|
break;
|
|
}
|
|
}
|
|
if (count == num) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (count == num) {
|
|
if (start) |start_entry| {
|
|
var i: usize = 0;
|
|
while (i < num) : (i += 1) {
|
|
// Can't fail as the entry was found to be free
|
|
self.setEntry(start_entry + i) catch unreachable;
|
|
}
|
|
return start_entry;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
///
|
|
/// Set the first free entry within the bitmaps as occupied.
|
|
///
|
|
/// Return: ?usize.
|
|
/// The index within all bitmaps that was set or null if there wasn't one free.
|
|
/// 0 .. ENTRIES_PER_BITMAP - 1 if in the first bitmap, ENTRIES_PER_BITMAP .. ENTRIES_PER_BITMAP * 2 - 1 if in the second etc.
|
|
///
|
|
pub fn setFirstFree(self: *Self) ?usize {
|
|
if (self.num_free_entries == 0) {
|
|
return null;
|
|
}
|
|
for (self.bitmaps) |*bmp, i| {
|
|
if (bmp.* == BITMAP_FULL) {
|
|
continue;
|
|
}
|
|
const bit = @truncate(IndexType, @ctz(BitmapType, ~bmp.*));
|
|
const idx = bit + i * ENTRIES_PER_BITMAP;
|
|
// Failing here means that the index is outside of the bitmap, so there are no free entries
|
|
self.setEntry(idx) catch return null;
|
|
return idx;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
///
|
|
/// Check if an entry is set.
|
|
///
|
|
/// Arguments:
|
|
/// IN self: *const Self - The bitmap to check.
|
|
/// IN idx: usize - The entry to check.
|
|
///
|
|
/// Return: bool.
|
|
/// True if the entry is set, else false.
|
|
///
|
|
/// Error: BitmapError.
|
|
/// OutOfBounds: The index given is out of bounds.
|
|
///
|
|
pub fn isSet(self: *const Self, idx: usize) BitmapError!bool {
|
|
if (idx >= self.num_entries) {
|
|
return BitmapError.OutOfBounds;
|
|
}
|
|
return (self.bitmaps[idx / ENTRIES_PER_BITMAP] & self.indexToBit(idx)) != 0;
|
|
}
|
|
};
|
|
}
|
|
|
|
test "Comptime setEntry" {
|
|
var bmp = ComptimeBitmap(u32).init();
|
|
testing.expectEqual(@as(u32, 32), bmp.num_free_entries);
|
|
|
|
bmp.setEntry(0);
|
|
testing.expectEqual(@as(u32, 1), bmp.bitmap);
|
|
testing.expectEqual(@as(u32, 31), bmp.num_free_entries);
|
|
|
|
bmp.setEntry(1);
|
|
testing.expectEqual(@as(u32, 3), bmp.bitmap);
|
|
testing.expectEqual(@as(u32, 30), bmp.num_free_entries);
|
|
|
|
// Repeat setting entry 1 to make sure state doesn't change
|
|
bmp.setEntry(1);
|
|
testing.expectEqual(@as(u32, 3), bmp.bitmap);
|
|
testing.expectEqual(@as(u32, 30), bmp.num_free_entries);
|
|
}
|
|
|
|
test "Comptime clearEntry" {
|
|
var bmp = ComptimeBitmap(u32).init();
|
|
testing.expectEqual(@as(u32, 32), bmp.num_free_entries);
|
|
|
|
bmp.setEntry(0);
|
|
testing.expectEqual(@as(u32, 31), bmp.num_free_entries);
|
|
bmp.setEntry(1);
|
|
testing.expectEqual(@as(u32, 30), bmp.num_free_entries);
|
|
testing.expectEqual(@as(u32, 3), bmp.bitmap);
|
|
bmp.clearEntry(0);
|
|
testing.expectEqual(@as(u32, 31), bmp.num_free_entries);
|
|
testing.expectEqual(@as(u32, 2), bmp.bitmap);
|
|
|
|
// Repeat to make sure state doesn't change
|
|
bmp.clearEntry(0);
|
|
testing.expectEqual(@as(u32, 31), bmp.num_free_entries);
|
|
testing.expectEqual(@as(u32, 2), bmp.bitmap);
|
|
|
|
// Try clearing an unset entry to make sure state doesn't change
|
|
bmp.clearEntry(2);
|
|
testing.expectEqual(@as(u32, 31), bmp.num_free_entries);
|
|
testing.expectEqual(@as(u32, 2), bmp.bitmap);
|
|
}
|
|
|
|
test "Comptime setFirstFree" {
|
|
var bmp = ComptimeBitmap(u32).init();
|
|
|
|
// Allocate the first entry
|
|
testing.expectEqual(bmp.setFirstFree() orelse unreachable, 0);
|
|
testing.expectEqual(bmp.bitmap, 1);
|
|
|
|
// Allocate the second entry
|
|
testing.expectEqual(bmp.setFirstFree() orelse unreachable, 1);
|
|
testing.expectEqual(bmp.bitmap, 3);
|
|
|
|
// Make all but the MSB occupied and try to allocate it
|
|
bmp.bitmap = ComptimeBitmap(u32).BITMAP_FULL & ~@as(u32, 1 << (ComptimeBitmap(u32).NUM_ENTRIES - 1));
|
|
bmp.num_free_entries = 1;
|
|
testing.expectEqual(bmp.setFirstFree() orelse unreachable, ComptimeBitmap(u32).NUM_ENTRIES - 1);
|
|
testing.expectEqual(bmp.bitmap, ComptimeBitmap(u32).BITMAP_FULL);
|
|
|
|
// We should no longer be able to allocate any entries
|
|
testing.expectEqual(bmp.setFirstFree(), null);
|
|
testing.expectEqual(bmp.bitmap, ComptimeBitmap(u32).BITMAP_FULL);
|
|
}
|
|
|
|
test "Comptime isSet" {
|
|
var bmp = ComptimeBitmap(u32).init();
|
|
|
|
bmp.bitmap = 1;
|
|
// Make sure that only the set entry is considered set
|
|
testing.expect(bmp.isSet(0));
|
|
var i: usize = 1;
|
|
while (i < ComptimeBitmap(u32).NUM_ENTRIES) : (i += 1) {
|
|
testing.expect(!bmp.isSet(@truncate(ComptimeBitmap(u32).IndexType, i)));
|
|
}
|
|
|
|
bmp.bitmap = 3;
|
|
testing.expect(bmp.isSet(0));
|
|
testing.expect(bmp.isSet(1));
|
|
i = 2;
|
|
while (i < ComptimeBitmap(u32).NUM_ENTRIES) : (i += 1) {
|
|
testing.expect(!bmp.isSet(@truncate(ComptimeBitmap(u32).IndexType, i)));
|
|
}
|
|
|
|
bmp.bitmap = 11;
|
|
testing.expect(bmp.isSet(0));
|
|
testing.expect(bmp.isSet(1));
|
|
testing.expect(!bmp.isSet(2));
|
|
testing.expect(bmp.isSet(3));
|
|
i = 4;
|
|
while (i < ComptimeBitmap(u32).NUM_ENTRIES) : (i += 1) {
|
|
testing.expect(!bmp.isSet(@truncate(ComptimeBitmap(u32).IndexType, i)));
|
|
}
|
|
}
|
|
|
|
test "Comptime indexToBit" {
|
|
var bmp = ComptimeBitmap(u8).init();
|
|
testing.expectEqual(bmp.indexToBit(0), 1);
|
|
testing.expectEqual(bmp.indexToBit(1), 2);
|
|
testing.expectEqual(bmp.indexToBit(2), 4);
|
|
testing.expectEqual(bmp.indexToBit(3), 8);
|
|
testing.expectEqual(bmp.indexToBit(4), 16);
|
|
testing.expectEqual(bmp.indexToBit(5), 32);
|
|
testing.expectEqual(bmp.indexToBit(6), 64);
|
|
testing.expectEqual(bmp.indexToBit(7), 128);
|
|
}
|
|
|
|
test "Comptime setContiguous" {
|
|
var bmp = ComptimeBitmap(u15).init();
|
|
// Test trying to set more entries than the bitmap has
|
|
testing.expectEqual(bmp.setContiguous(ComptimeBitmap(u15).NUM_ENTRIES + 1), null);
|
|
// All entries should still be free
|
|
testing.expectEqual(bmp.num_free_entries, ComptimeBitmap(u15).NUM_ENTRIES);
|
|
testing.expectEqual(bmp.setContiguous(3) orelse unreachable, 0);
|
|
testing.expectEqual(bmp.setContiguous(4) orelse unreachable, 3);
|
|
// 0b0000.0000.0111.1111
|
|
bmp.bitmap |= 0x200;
|
|
// 0b0000.0010.0111.1111
|
|
testing.expectEqual(bmp.setContiguous(3) orelse unreachable, 10);
|
|
// 0b0001.1110.0111.1111
|
|
testing.expectEqual(bmp.setContiguous(5), null);
|
|
testing.expectEqual(bmp.setContiguous(2), 7);
|
|
// 0b001.1111.1111.1111
|
|
// Test trying to set beyond the end of the bitmaps
|
|
testing.expectEqual(bmp.setContiguous(3), null);
|
|
testing.expectEqual(bmp.setContiguous(2), 13);
|
|
}
|
|
|
|
test "setEntry" {
|
|
var bmp = try Bitmap(u32).init(31, std.testing.allocator);
|
|
defer bmp.deinit();
|
|
testing.expectEqual(@as(u32, 31), bmp.num_free_entries);
|
|
|
|
try bmp.setEntry(0);
|
|
testing.expectEqual(@as(u32, 1), bmp.bitmaps[0]);
|
|
testing.expectEqual(@as(u32, 30), bmp.num_free_entries);
|
|
|
|
try bmp.setEntry(1);
|
|
testing.expectEqual(@as(u32, 3), bmp.bitmaps[0]);
|
|
testing.expectEqual(@as(u32, 29), bmp.num_free_entries);
|
|
|
|
// Repeat setting entry 1 to make sure state doesn't change
|
|
try bmp.setEntry(1);
|
|
testing.expectEqual(@as(u32, 3), bmp.bitmaps[0]);
|
|
testing.expectEqual(@as(u32, 29), bmp.num_free_entries);
|
|
|
|
testing.expectError(Bitmap(u32).BitmapError.OutOfBounds, bmp.setEntry(31));
|
|
testing.expectEqual(@as(u32, 29), bmp.num_free_entries);
|
|
}
|
|
|
|
test "clearEntry" {
|
|
var bmp = try Bitmap(u32).init(32, std.testing.allocator);
|
|
defer bmp.deinit();
|
|
testing.expectEqual(@as(u32, 32), bmp.num_free_entries);
|
|
|
|
try bmp.setEntry(0);
|
|
testing.expectEqual(@as(u32, 31), bmp.num_free_entries);
|
|
try bmp.setEntry(1);
|
|
testing.expectEqual(@as(u32, 30), bmp.num_free_entries);
|
|
testing.expectEqual(@as(u32, 3), bmp.bitmaps[0]);
|
|
try bmp.clearEntry(0);
|
|
testing.expectEqual(@as(u32, 31), bmp.num_free_entries);
|
|
testing.expectEqual(@as(u32, 2), bmp.bitmaps[0]);
|
|
|
|
// Repeat to make sure state doesn't change
|
|
try bmp.clearEntry(0);
|
|
testing.expectEqual(@as(u32, 31), bmp.num_free_entries);
|
|
testing.expectEqual(@as(u32, 2), bmp.bitmaps[0]);
|
|
|
|
// Try clearing an unset entry to make sure state doesn't change
|
|
try bmp.clearEntry(2);
|
|
testing.expectEqual(@as(u32, 31), bmp.num_free_entries);
|
|
testing.expectEqual(@as(u32, 2), bmp.bitmaps[0]);
|
|
|
|
testing.expectError(Bitmap(u32).BitmapError.OutOfBounds, bmp.clearEntry(32));
|
|
}
|
|
|
|
test "setFirstFree multiple bitmaps" {
|
|
var bmp = try Bitmap(u8).init(9, std.testing.allocator);
|
|
defer bmp.deinit();
|
|
|
|
// Allocate the first entry
|
|
testing.expectEqual(bmp.setFirstFree() orelse unreachable, 0);
|
|
testing.expectEqual(bmp.bitmaps[0], 1);
|
|
|
|
// Allocate the second entry
|
|
testing.expectEqual(bmp.setFirstFree() orelse unreachable, 1);
|
|
testing.expectEqual(bmp.bitmaps[0], 3);
|
|
|
|
// Allocate the entirety of the first bitmap
|
|
var entry: u32 = 2;
|
|
var expected: u8 = 7;
|
|
while (entry < Bitmap(u8).ENTRIES_PER_BITMAP) {
|
|
testing.expectEqual(bmp.setFirstFree() orelse unreachable, entry);
|
|
testing.expectEqual(bmp.bitmaps[0], expected);
|
|
if (entry + 1 < Bitmap(u8).ENTRIES_PER_BITMAP) {
|
|
entry += 1;
|
|
expected = expected * 2 + 1;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Try allocating an entry in the next bitmap
|
|
testing.expectEqual(bmp.setFirstFree() orelse unreachable, Bitmap(u8).ENTRIES_PER_BITMAP);
|
|
testing.expectEqual(bmp.bitmaps[0], Bitmap(u8).BITMAP_FULL);
|
|
testing.expectEqual(bmp.bitmaps[1], 1);
|
|
|
|
// We should no longer be able to allocate any entries
|
|
testing.expectEqual(bmp.setFirstFree(), null);
|
|
testing.expectEqual(bmp.bitmaps[0], Bitmap(u8).BITMAP_FULL);
|
|
testing.expectEqual(bmp.bitmaps[1], 1);
|
|
}
|
|
|
|
test "setFirstFree" {
|
|
var bmp = try Bitmap(u32).init(32, std.testing.allocator);
|
|
defer bmp.deinit();
|
|
|
|
// Allocate the first entry
|
|
testing.expectEqual(bmp.setFirstFree() orelse unreachable, 0);
|
|
testing.expectEqual(bmp.bitmaps[0], 1);
|
|
|
|
// Allocate the second entry
|
|
testing.expectEqual(bmp.setFirstFree() orelse unreachable, 1);
|
|
testing.expectEqual(bmp.bitmaps[0], 3);
|
|
|
|
// Make all but the MSB occupied and try to allocate it
|
|
bmp.bitmaps[0] = Bitmap(u32).BITMAP_FULL & ~@as(u32, 1 << (Bitmap(u32).ENTRIES_PER_BITMAP - 1));
|
|
testing.expectEqual(bmp.setFirstFree() orelse unreachable, Bitmap(u32).ENTRIES_PER_BITMAP - 1);
|
|
testing.expectEqual(bmp.bitmaps[0], Bitmap(u32).BITMAP_FULL);
|
|
|
|
// We should no longer be able to allocate any entries
|
|
testing.expectEqual(bmp.setFirstFree(), null);
|
|
testing.expectEqual(bmp.bitmaps[0], Bitmap(u32).BITMAP_FULL);
|
|
}
|
|
|
|
test "isSet" {
|
|
var bmp = try Bitmap(u32).init(32, std.testing.allocator);
|
|
defer bmp.deinit();
|
|
|
|
bmp.bitmaps[0] = 1;
|
|
// Make sure that only the set entry is considered set
|
|
testing.expect(try bmp.isSet(0));
|
|
var i: u32 = 1;
|
|
while (i < bmp.num_entries) : (i += 1) {
|
|
testing.expect(!try bmp.isSet(i));
|
|
}
|
|
|
|
bmp.bitmaps[0] = 3;
|
|
testing.expect(try bmp.isSet(0));
|
|
testing.expect(try bmp.isSet(1));
|
|
i = 2;
|
|
while (i < bmp.num_entries) : (i += 1) {
|
|
testing.expect(!try bmp.isSet(i));
|
|
}
|
|
|
|
bmp.bitmaps[0] = 11;
|
|
testing.expect(try bmp.isSet(0));
|
|
testing.expect(try bmp.isSet(1));
|
|
testing.expect(!try bmp.isSet(2));
|
|
testing.expect(try bmp.isSet(3));
|
|
i = 4;
|
|
while (i < bmp.num_entries) : (i += 1) {
|
|
testing.expect(!try bmp.isSet(i));
|
|
}
|
|
|
|
testing.expectError(Bitmap(u32).BitmapError.OutOfBounds, bmp.isSet(33));
|
|
}
|
|
|
|
test "indexToBit" {
|
|
var bmp = try Bitmap(u8).init(10, std.testing.allocator);
|
|
defer bmp.deinit();
|
|
testing.expectEqual(bmp.indexToBit(0), 1);
|
|
testing.expectEqual(bmp.indexToBit(1), 2);
|
|
testing.expectEqual(bmp.indexToBit(2), 4);
|
|
testing.expectEqual(bmp.indexToBit(3), 8);
|
|
testing.expectEqual(bmp.indexToBit(4), 16);
|
|
testing.expectEqual(bmp.indexToBit(5), 32);
|
|
testing.expectEqual(bmp.indexToBit(6), 64);
|
|
testing.expectEqual(bmp.indexToBit(7), 128);
|
|
testing.expectEqual(bmp.indexToBit(8), 1);
|
|
testing.expectEqual(bmp.indexToBit(9), 2);
|
|
}
|
|
|
|
test "setContiguous" {
|
|
var bmp = try Bitmap(u4).init(15, std.testing.allocator);
|
|
defer bmp.deinit();
|
|
// Test trying to set more entries than the bitmap has
|
|
testing.expectEqual(bmp.setContiguous(bmp.num_entries + 1), null);
|
|
// All entries should still be free
|
|
testing.expectEqual(bmp.num_free_entries, bmp.num_entries);
|
|
|
|
testing.expectEqual(bmp.setContiguous(3) orelse unreachable, 0);
|
|
testing.expectEqual(bmp.setContiguous(4) orelse unreachable, 3);
|
|
// 0b0000.0000.0111.1111
|
|
bmp.bitmaps[2] |= 2;
|
|
// 0b0000.0010.0111.1111
|
|
testing.expectEqual(bmp.setContiguous(3) orelse unreachable, 10);
|
|
// 0b0001.1110.0111.1111
|
|
testing.expectEqual(bmp.setContiguous(5), null);
|
|
testing.expectEqual(bmp.setContiguous(2), 7);
|
|
// 0b001.1111.1111.1111
|
|
// Test trying to set beyond the end of the bitmaps
|
|
testing.expectEqual(bmp.setContiguous(3), null);
|
|
testing.expectEqual(bmp.setContiguous(2), 13);
|
|
}
|