Combine ComptimeBitmap and Bitmap

This commit is contained in:
Samuel Tebbs 2022-04-23 19:46:09 +01:00 committed by Sam Tebbs
parent 9f98062eb3
commit 9cd3765334
5 changed files with 281 additions and 353 deletions

View file

@ -1,206 +1,33 @@
const std = @import("std"); const std = @import("std");
const builtin = std.builtin; const builtin = std.builtin;
const testing = std.testing; const testing = std.testing;
const expectEqual = testing.expectEqual;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
/// /// The possible errors thrown by bitmap functions
/// A comptime bitmap that uses a specific type to store the entries. No allocators needed. pub const BitmapError = error{
/// /// The address given was outside the region covered by a bitmap
/// Arguments: OutOfBounds,
/// 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.Int(.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 |= 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 &= ~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(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.
/// IN from: ?IndexType - The entry number to start allocate from or null if it can start anywhere
///
/// Return: ?IndexType
/// The first entry set or null if there weren't enough contiguous entries.
/// If `from` was not null and any entry between `from` and `from` + num is set then null is returned.
///
pub fn setContiguous(self: *Self, num: usize, from: ?IndexType) ?IndexType {
if (num > self.num_free_entries) {
return null;
}
var count: usize = 0;
var start: ?IndexType = null;
var bit = from orelse 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;
// If the caller requested the allocation to start from
// a specific entry and it failed then return null
if (from) |_| {
return 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 & indexToBit(idx)) != 0;
}
};
}
/// ///
/// A bitmap that uses a specific type to store the entries. /// A bitmap that uses a specific type to store the entries.
/// The bitmap can either be statically or dynamically allocated.
/// Statically allocated bitmaps are allocated at compile time and therefore must know the number of entries at compile time.
/// Dynamically allocated bitmaps do not need to know the number of entries until being initialised, but do need an allocator.
/// ///
/// Arguments: /// Arguments:
/// IN static: comptime bool - Whether this bitmap is statically or dynamically allocated
/// IN BitmapType: type - The integer type to use to store entries. /// IN BitmapType: type - The integer type to use to store entries.
/// IN num_entries: usize or ?usize - The number of entries if static, else ignored (can be null)
/// ///
/// Return: type. /// Return: type.
/// The bitmap type created. /// The bitmap type created.
/// ///
pub fn Bitmap(comptime BitmapType: type) type { pub fn Bitmap(comptime num_entries: ?usize, comptime BitmapType: type) type {
return struct { 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(); const Self = @This();
const static = num_entries != null;
/// The number of entries that one bitmap type can hold. Evaluates to the number of bits the type has /// 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); pub const ENTRIES_PER_BITMAP: usize = std.meta.bitCount(BitmapType);
@ -213,37 +40,48 @@ pub fn Bitmap(comptime BitmapType: type) type {
num_bitmaps: usize, num_bitmaps: usize,
num_entries: usize, num_entries: usize,
bitmaps: []BitmapType, bitmaps: if (static) [std.mem.alignForward(num_entries.?, ENTRIES_PER_BITMAP) / ENTRIES_PER_BITMAP]BitmapType else []BitmapType,
num_free_entries: usize, num_free_entries: usize,
allocator: Allocator, allocator: if (static) ?Allocator else Allocator,
/// ///
/// Create an instance of this bitmap type. /// Create an instance of this bitmap type.
/// ///
/// Arguments: /// Arguments:
/// IN num_entries: usize - The number of entries that the bitmap created will have. /// IN num: ?usize or usize - The number of entries that the bitmap created will have if dynamically allocated, else ignored (can be null)
/// The number of BitmapType required to store this many entries will be allocated and each will be zeroed. /// The number of BitmapType required to store this many entries will be allocated (dynamically or statically) and each will be zeroed.
/// IN allocator: Allocator - The allocator to use when allocating the BitmapTypes required. /// IN allocator: ?Allocator or Allocator - The allocator to use when allocating the BitmapTypes required. Ignored if statically allocated.
/// ///
/// Return: Self. /// Return: Self.
/// The bitmap instance. /// The bitmap instance.
/// ///
/// Error: Allocator.Error /// Error: Allocator.Error
/// OutOfMemory: There isn't enough memory available to allocate the required number of BitmapType. /// OutOfMemory: There isn't enough memory available to allocate the required number of BitmapType. A statically allocated bitmap will not throw this error.
/// ///
pub fn init(num_entries: usize, allocator: Allocator) !Self { pub fn init(num: if (static) ?usize else usize, allocator: if (static) ?Allocator else Allocator) !Self {
const num = std.mem.alignForward(num_entries, ENTRIES_PER_BITMAP) / ENTRIES_PER_BITMAP; if (static) {
const self = Self{ const n = std.mem.alignForward(num_entries.?, ENTRIES_PER_BITMAP) / ENTRIES_PER_BITMAP;
.num_bitmaps = num, return Self{
.num_entries = num_entries, .num_bitmaps = n,
.bitmaps = try allocator.alloc(BitmapType, num), .bitmaps = [_]BitmapType{0} ** (std.mem.alignForward(num_entries.?, ENTRIES_PER_BITMAP) / ENTRIES_PER_BITMAP),
.num_free_entries = num_entries, .num_entries = num_entries.?,
.allocator = allocator, .num_free_entries = num_entries.?,
}; .allocator = null,
for (self.bitmaps) |*bmp| { };
bmp.* = 0; } else {
const n = std.mem.alignForward(num, ENTRIES_PER_BITMAP) / ENTRIES_PER_BITMAP;
const self = Self{
.num_bitmaps = n,
.num_entries = num,
.bitmaps = try allocator.alloc(BitmapType, n),
.num_free_entries = num,
.allocator = allocator,
};
for (self.bitmaps) |*bmp| {
bmp.* = 0;
}
return self;
} }
return self;
} }
/// ///
@ -271,12 +109,13 @@ pub fn Bitmap(comptime BitmapType: type) type {
/// ///
/// Free the memory occupied by this bitmap's internal state. It will become unusable afterwards. /// Free the memory occupied by this bitmap's internal state. It will become unusable afterwards.
/// Does nothing if the bitmap was statically allocated.
/// ///
/// Arguments: /// Arguments:
/// IN self: *Self - The bitmap that should be deinitialised /// IN self: *Self - The bitmap that should be deinitialised
/// ///
pub fn deinit(self: *Self) void { pub fn deinit(self: *Self) void {
self.allocator.free(self.bitmaps); if (!static) self.allocator.free(self.bitmaps);
} }
/// ///
@ -459,102 +298,117 @@ pub fn Bitmap(comptime BitmapType: type) type {
}; };
} }
test "Comptime setEntry" { test "static setEntry" {
var bmp = ComptimeBitmap(u32).init(); const BmpTy = Bitmap(32, u32);
var bmp = try BmpTy.init(null, null);
try testing.expectEqual(@as(u32, 1), bmp.bitmaps.len);
try testing.expectEqual(@as(u32, 32), bmp.num_free_entries); try testing.expectEqual(@as(u32, 32), bmp.num_free_entries);
bmp.setEntry(0); try bmp.setEntry(0);
try testing.expectEqual(@as(u32, 1), bmp.bitmap); try testing.expectEqual(@as(u32, 1), bmp.bitmaps[0]);
try testing.expectEqual(@as(u32, 31), bmp.num_free_entries); try testing.expectEqual(@as(u32, 31), bmp.num_free_entries);
bmp.setEntry(1); try bmp.setEntry(1);
try testing.expectEqual(@as(u32, 3), bmp.bitmap); try testing.expectEqual(@as(u32, 3), bmp.bitmaps[0]);
try testing.expectEqual(@as(u32, 30), bmp.num_free_entries); try testing.expectEqual(@as(u32, 30), bmp.num_free_entries);
// Repeat setting entry 1 to make sure state doesn't change // Repeat setting entry 1 to make sure state doesn't change
bmp.setEntry(1); try bmp.setEntry(1);
try testing.expectEqual(@as(u32, 3), bmp.bitmap); try testing.expectEqual(@as(u32, 3), bmp.bitmaps[0]);
try testing.expectEqual(@as(u32, 30), bmp.num_free_entries); try testing.expectEqual(@as(u32, 30), bmp.num_free_entries);
} }
test "Comptime clearEntry" { test "static clearEntry" {
var bmp = ComptimeBitmap(u32).init(); const BmpTy = Bitmap(32, u32);
var bmp = try BmpTy.init(null, null);
try testing.expectEqual(@as(u32, 1), bmp.bitmaps.len);
try testing.expectEqual(@as(u32, 32), bmp.num_free_entries); try testing.expectEqual(@as(u32, 32), bmp.num_free_entries);
bmp.setEntry(0); try bmp.setEntry(0);
try testing.expectEqual(@as(u32, 31), bmp.num_free_entries); try testing.expectEqual(@as(u32, 31), bmp.num_free_entries);
bmp.setEntry(1); try bmp.setEntry(1);
try testing.expectEqual(@as(u32, 30), bmp.num_free_entries); try testing.expectEqual(@as(u32, 30), bmp.num_free_entries);
try testing.expectEqual(@as(u32, 3), bmp.bitmap); try testing.expectEqual(@as(u32, 3), bmp.bitmaps[0]);
bmp.clearEntry(0); try bmp.clearEntry(0);
try testing.expectEqual(@as(u32, 31), bmp.num_free_entries); try testing.expectEqual(@as(u32, 31), bmp.num_free_entries);
try testing.expectEqual(@as(u32, 2), bmp.bitmap); try testing.expectEqual(@as(u32, 2), bmp.bitmaps[0]);
// Repeat to make sure state doesn't change // Repeat to make sure state doesn't change
bmp.clearEntry(0); try bmp.clearEntry(0);
try testing.expectEqual(@as(u32, 31), bmp.num_free_entries); try testing.expectEqual(@as(u32, 31), bmp.num_free_entries);
try testing.expectEqual(@as(u32, 2), bmp.bitmap); try testing.expectEqual(@as(u32, 2), bmp.bitmaps[0]);
// Try clearing an unset entry to make sure state doesn't change // Try clearing an unset entry to make sure state doesn't change
bmp.clearEntry(2); try bmp.clearEntry(2);
try testing.expectEqual(@as(u32, 31), bmp.num_free_entries); try testing.expectEqual(@as(u32, 31), bmp.num_free_entries);
try testing.expectEqual(@as(u32, 2), bmp.bitmap); try testing.expectEqual(@as(u32, 2), bmp.bitmaps[0]);
} }
test "Comptime setFirstFree" { test "static setFirstFree" {
var bmp = ComptimeBitmap(u32).init(); const BmpTy = Bitmap(32, u32);
var bmp = try BmpTy.init(null, null);
try testing.expectEqual(@as(u32, 1), bmp.bitmaps.len);
// Allocate the first entry // Allocate the first entry
try testing.expectEqual(bmp.setFirstFree() orelse unreachable, 0); try testing.expectEqual(bmp.setFirstFree() orelse unreachable, 0);
try testing.expectEqual(bmp.bitmap, 1); try testing.expectEqual(bmp.bitmaps[0], 1);
// Allocate the second entry // Allocate the second entry
try testing.expectEqual(bmp.setFirstFree() orelse unreachable, 1); try testing.expectEqual(bmp.setFirstFree() orelse unreachable, 1);
try testing.expectEqual(bmp.bitmap, 3); try testing.expectEqual(bmp.bitmaps[0], 3);
// Make all but the MSB occupied and try to allocate it // 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)); for (bmp.bitmaps) |*b, i| {
b.* = BmpTy.BITMAP_FULL;
if (i <= bmp.num_bitmaps - 1) b.* &= ~(@as(usize, 1) << BmpTy.ENTRIES_PER_BITMAP - 1);
}
bmp.num_free_entries = 1; bmp.num_free_entries = 1;
try testing.expectEqual(bmp.setFirstFree() orelse unreachable, ComptimeBitmap(u32).NUM_ENTRIES - 1); try testing.expectEqual(bmp.setFirstFree() orelse unreachable, bmp.num_entries - 1);
try testing.expectEqual(bmp.bitmap, ComptimeBitmap(u32).BITMAP_FULL); for (bmp.bitmaps) |b| {
try testing.expectEqual(b, BmpTy.BITMAP_FULL);
}
// We should no longer be able to allocate any entries // We should no longer be able to allocate any entries
try testing.expectEqual(bmp.setFirstFree(), null); try testing.expectEqual(bmp.setFirstFree(), null);
try testing.expectEqual(bmp.bitmap, ComptimeBitmap(u32).BITMAP_FULL); for (bmp.bitmaps) |b| {
try testing.expectEqual(b, BmpTy.BITMAP_FULL);
}
} }
test "Comptime isSet" { test "static isSet" {
var bmp = ComptimeBitmap(u32).init(); const BmpTy = Bitmap(32, u32);
var bmp = try BmpTy.init(null, null);
try testing.expectEqual(@as(u32, 1), bmp.bitmaps.len);
bmp.bitmap = 1; bmp.bitmaps[0] = 1;
// Make sure that only the set entry is considered set // Make sure that only the set entry is considered set
try testing.expect(bmp.isSet(0)); try testing.expect(try bmp.isSet(0));
var i: usize = 1; var i: usize = 1;
while (i < ComptimeBitmap(u32).NUM_ENTRIES) : (i += 1) { while (i < bmp.num_entries) : (i += 1) {
try testing.expect(!bmp.isSet(@truncate(ComptimeBitmap(u32).IndexType, i))); try testing.expect(!(try bmp.isSet(@truncate(BmpTy.IndexType, i))));
} }
bmp.bitmap = 3; bmp.bitmaps[0] = 3;
try testing.expect(bmp.isSet(0)); try testing.expect(try bmp.isSet(0));
try testing.expect(bmp.isSet(1)); try testing.expect(try bmp.isSet(1));
i = 2; i = 2;
while (i < ComptimeBitmap(u32).NUM_ENTRIES) : (i += 1) { while (i < bmp.num_entries) : (i += 1) {
try testing.expect(!bmp.isSet(@truncate(ComptimeBitmap(u32).IndexType, i))); try testing.expect(!(try bmp.isSet(@truncate(BmpTy.IndexType, i))));
} }
bmp.bitmap = 11; bmp.bitmaps[0] = 11;
try testing.expect(bmp.isSet(0)); try testing.expect(try bmp.isSet(0));
try testing.expect(bmp.isSet(1)); try testing.expect(try bmp.isSet(1));
try testing.expect(!bmp.isSet(2)); try testing.expect(!(try bmp.isSet(2)));
try testing.expect(bmp.isSet(3)); try testing.expect(try bmp.isSet(3));
i = 4; i = 4;
while (i < ComptimeBitmap(u32).NUM_ENTRIES) : (i += 1) { while (i < bmp.num_entries) : (i += 1) {
try testing.expect(!bmp.isSet(@truncate(ComptimeBitmap(u32).IndexType, i))); try testing.expect(!(try bmp.isSet(@truncate(BmpTy.IndexType, i))));
} }
} }
test "Comptime indexToBit" { test "static indexToBit" {
const Type = ComptimeBitmap(u8); const Type = Bitmap(8, u8);
try testing.expectEqual(Type.indexToBit(0), 1); try testing.expectEqual(Type.indexToBit(0), 1);
try testing.expectEqual(Type.indexToBit(1), 2); try testing.expectEqual(Type.indexToBit(1), 2);
try testing.expectEqual(Type.indexToBit(2), 4); try testing.expectEqual(Type.indexToBit(2), 4);
@ -565,45 +419,73 @@ test "Comptime indexToBit" {
try testing.expectEqual(Type.indexToBit(7), 128); try testing.expectEqual(Type.indexToBit(7), 128);
} }
test "Comptime setContiguous" { test "static setContiguous" {
var bmp = ComptimeBitmap(u16).init(); var bmp = try Bitmap(16, u16).init(null, null);
try testing.expectEqual(@as(u32, 1), bmp.bitmaps.len);
// Test trying to set more entries than the bitmap has // Test trying to set more entries than the bitmap has
try testing.expectEqual(bmp.setContiguous(bmp.num_free_entries + 1, null), null); try testing.expectEqual(bmp.setContiguous(bmp.num_free_entries + 1, null), null);
try testing.expectEqual(bmp.setContiguous(bmp.num_free_entries + 1, 1), null); try testing.expectEqual(bmp.setContiguous(bmp.num_free_entries + 1, 1), null);
// All entries should still be free // All entries should still be free
try testing.expectEqual(bmp.num_free_entries, 16); try testing.expectEqual(bmp.num_free_entries, 16);
try testing.expectEqual(bmp.bitmap, 0b0000000000000000); for (bmp.bitmaps) |b| {
try expectEqual(b, 0);
}
try testing.expectEqual(bmp.setContiguous(3, 0) orelse unreachable, 0); try testing.expectEqual(bmp.setContiguous(3, 0) orelse unreachable, 0);
try testing.expectEqual(bmp.bitmap, 0b0000000000000111); try expectEqual(bmp.bitmaps[0], 0b0000000000000111);
for (bmp.bitmaps) |b, i| {
if (i > 0) try expectEqual(b, 0);
}
// Test setting from top // Test setting from top
try testing.expectEqual(bmp.setContiguous(2, 14) orelse unreachable, 14); try testing.expectEqual(bmp.setContiguous(2, 14) orelse unreachable, 14);
try testing.expectEqual(bmp.bitmap, 0b1100000000000111); try expectEqual(bmp.bitmaps[0], 0b1100000000000111);
for (bmp.bitmaps) |b, i| {
if (i > 0) try expectEqual(b, 0);
}
try testing.expectEqual(bmp.setContiguous(3, 12), null); try testing.expectEqual(bmp.setContiguous(3, 12), null);
try testing.expectEqual(bmp.bitmap, 0b1100000000000111); try expectEqual(bmp.bitmaps[0], 0b1100000000000111);
for (bmp.bitmaps) |b, i| {
if (i > 0) try expectEqual(b, 0);
}
try testing.expectEqual(bmp.setContiguous(3, null) orelse unreachable, 3); try testing.expectEqual(bmp.setContiguous(3, null) orelse unreachable, 3);
try testing.expectEqual(bmp.bitmap, 0b1100000000111111); try expectEqual(bmp.bitmaps[0], 0b1100000000111111);
for (bmp.bitmaps) |b, i| {
if (i > 0) try expectEqual(b, 0);
}
// Test setting beyond the what is available // Test setting beyond the what is available
try testing.expectEqual(bmp.setContiguous(9, null), null); try testing.expectEqual(bmp.setContiguous(9, null), null);
try testing.expectEqual(bmp.bitmap, 0b1100000000111111); try expectEqual(bmp.bitmaps[0], 0b1100000000111111);
for (bmp.bitmaps) |b, i| {
if (i > 0) try expectEqual(b, 0);
}
try testing.expectEqual(bmp.setContiguous(8, null) orelse unreachable, 6); try testing.expectEqual(bmp.setContiguous(8, null) orelse unreachable, 6);
try testing.expectEqual(bmp.bitmap, 0b1111111111111111); try expectEqual(bmp.bitmaps[0], 0b1111111111111111);
for (bmp.bitmaps) |b, i| {
if (i > 0) try expectEqual(b, 0);
}
// No more are possible // No more are possible
try testing.expectEqual(bmp.setContiguous(1, null), null); try testing.expectEqual(bmp.setContiguous(1, null), null);
try testing.expectEqual(bmp.bitmap, 0b1111111111111111); try expectEqual(bmp.bitmaps[0], 0b1111111111111111);
for (bmp.bitmaps) |b, i| {
if (i > 0) try expectEqual(b, 0);
}
try testing.expectEqual(bmp.setContiguous(1, 0), null); try testing.expectEqual(bmp.setContiguous(1, 0), null);
try testing.expectEqual(bmp.bitmap, 0b1111111111111111); try expectEqual(bmp.bitmaps[0], 0b1111111111111111);
for (bmp.bitmaps) |b, i| {
if (i > 0) try expectEqual(b, 0);
}
} }
test "setEntry" { test "setEntry" {
var bmp = try Bitmap(u32).init(31, std.testing.allocator); var bmp = try Bitmap(null, u32).init(31, std.testing.allocator);
try testing.expectEqual(@as(u32, 1), bmp.bitmaps.len);
defer bmp.deinit(); defer bmp.deinit();
try testing.expectEqual(@as(u32, 31), bmp.num_free_entries); try testing.expectEqual(@as(u32, 31), bmp.num_free_entries);
@ -620,12 +502,13 @@ test "setEntry" {
try testing.expectEqual(@as(u32, 3), bmp.bitmaps[0]); try testing.expectEqual(@as(u32, 3), bmp.bitmaps[0]);
try testing.expectEqual(@as(u32, 29), bmp.num_free_entries); try testing.expectEqual(@as(u32, 29), bmp.num_free_entries);
try testing.expectError(Bitmap(u32).BitmapError.OutOfBounds, bmp.setEntry(31)); try testing.expectError(BitmapError.OutOfBounds, bmp.setEntry(31));
try testing.expectEqual(@as(u32, 29), bmp.num_free_entries); try testing.expectEqual(@as(u32, 29), bmp.num_free_entries);
} }
test "clearEntry" { test "clearEntry" {
var bmp = try Bitmap(u32).init(32, std.testing.allocator); var bmp = try Bitmap(null, u32).init(32, std.testing.allocator);
try testing.expectEqual(@as(u32, 1), bmp.bitmaps.len);
defer bmp.deinit(); defer bmp.deinit();
try testing.expectEqual(@as(u32, 32), bmp.num_free_entries); try testing.expectEqual(@as(u32, 32), bmp.num_free_entries);
@ -648,11 +531,13 @@ test "clearEntry" {
try testing.expectEqual(@as(u32, 31), bmp.num_free_entries); try testing.expectEqual(@as(u32, 31), bmp.num_free_entries);
try testing.expectEqual(@as(u32, 2), bmp.bitmaps[0]); try testing.expectEqual(@as(u32, 2), bmp.bitmaps[0]);
try testing.expectError(Bitmap(u32).BitmapError.OutOfBounds, bmp.clearEntry(32)); try testing.expectError(BitmapError.OutOfBounds, bmp.clearEntry(32));
} }
test "setFirstFree multiple bitmaps" { test "setFirstFree multiple bitmaps" {
var bmp = try Bitmap(u8).init(9, std.testing.allocator); const BmpTy = Bitmap(null, u8);
var bmp = try BmpTy.init(9, std.testing.allocator);
try testing.expectEqual(@as(u32, 2), bmp.bitmaps.len);
defer bmp.deinit(); defer bmp.deinit();
// Allocate the first entry // Allocate the first entry
@ -666,10 +551,10 @@ test "setFirstFree multiple bitmaps" {
// Allocate the entirety of the first bitmap // Allocate the entirety of the first bitmap
var entry: u32 = 2; var entry: u32 = 2;
var expected: u8 = 7; var expected: u8 = 7;
while (entry < Bitmap(u8).ENTRIES_PER_BITMAP) { while (entry < BmpTy.ENTRIES_PER_BITMAP) {
try testing.expectEqual(bmp.setFirstFree() orelse unreachable, entry); try testing.expectEqual(bmp.setFirstFree() orelse unreachable, entry);
try testing.expectEqual(bmp.bitmaps[0], expected); try testing.expectEqual(bmp.bitmaps[0], expected);
if (entry + 1 < Bitmap(u8).ENTRIES_PER_BITMAP) { if (entry + 1 < BmpTy.ENTRIES_PER_BITMAP) {
entry += 1; entry += 1;
expected = expected * 2 + 1; expected = expected * 2 + 1;
} else { } else {
@ -678,18 +563,20 @@ test "setFirstFree multiple bitmaps" {
} }
// Try allocating an entry in the next bitmap // Try allocating an entry in the next bitmap
try testing.expectEqual(bmp.setFirstFree() orelse unreachable, Bitmap(u8).ENTRIES_PER_BITMAP); try testing.expectEqual(bmp.setFirstFree() orelse unreachable, BmpTy.ENTRIES_PER_BITMAP);
try testing.expectEqual(bmp.bitmaps[0], Bitmap(u8).BITMAP_FULL); try testing.expectEqual(bmp.bitmaps[0], BmpTy.BITMAP_FULL);
try testing.expectEqual(bmp.bitmaps[1], 1); try testing.expectEqual(bmp.bitmaps[1], 1);
// We should no longer be able to allocate any entries // We should no longer be able to allocate any entries
try testing.expectEqual(bmp.setFirstFree(), null); try testing.expectEqual(bmp.setFirstFree(), null);
try testing.expectEqual(bmp.bitmaps[0], Bitmap(u8).BITMAP_FULL); try testing.expectEqual(bmp.bitmaps[0], BmpTy.BITMAP_FULL);
try testing.expectEqual(bmp.bitmaps[1], 1); try testing.expectEqual(bmp.bitmaps[1], 1);
} }
test "setFirstFree" { test "setFirstFree" {
var bmp = try Bitmap(u32).init(32, std.testing.allocator); const BmpTy = Bitmap(null, u32);
var bmp = try BmpTy.init(32, std.testing.allocator);
try testing.expectEqual(@as(u32, 1), bmp.bitmaps.len);
defer bmp.deinit(); defer bmp.deinit();
// Allocate the first entry // Allocate the first entry
@ -701,17 +588,18 @@ test "setFirstFree" {
try testing.expectEqual(bmp.bitmaps[0], 3); try testing.expectEqual(bmp.bitmaps[0], 3);
// Make all but the MSB occupied and try to allocate it // 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)); bmp.bitmaps[0] = BmpTy.BITMAP_FULL & ~@as(u32, 1 << (BmpTy.ENTRIES_PER_BITMAP - 1));
try testing.expectEqual(bmp.setFirstFree() orelse unreachable, Bitmap(u32).ENTRIES_PER_BITMAP - 1); try testing.expectEqual(bmp.setFirstFree() orelse unreachable, BmpTy.ENTRIES_PER_BITMAP - 1);
try testing.expectEqual(bmp.bitmaps[0], Bitmap(u32).BITMAP_FULL); try testing.expectEqual(bmp.bitmaps[0], BmpTy.BITMAP_FULL);
// We should no longer be able to allocate any entries // We should no longer be able to allocate any entries
try testing.expectEqual(bmp.setFirstFree(), null); try testing.expectEqual(bmp.setFirstFree(), null);
try testing.expectEqual(bmp.bitmaps[0], Bitmap(u32).BITMAP_FULL); try testing.expectEqual(bmp.bitmaps[0], BmpTy.BITMAP_FULL);
} }
test "isSet" { test "isSet" {
var bmp = try Bitmap(u32).init(32, std.testing.allocator); var bmp = try Bitmap(null, u32).init(32, std.testing.allocator);
try testing.expectEqual(@as(u32, 1), bmp.bitmaps.len);
defer bmp.deinit(); defer bmp.deinit();
bmp.bitmaps[0] = 1; bmp.bitmaps[0] = 1;
@ -740,12 +628,13 @@ test "isSet" {
try testing.expect(!try bmp.isSet(i)); try testing.expect(!try bmp.isSet(i));
} }
try testing.expectError(Bitmap(u32).BitmapError.OutOfBounds, bmp.isSet(33)); try testing.expectError(BitmapError.OutOfBounds, bmp.isSet(33));
} }
test "indexToBit" { test "indexToBit" {
const Type = Bitmap(u8); const Type = Bitmap(null, u8);
var bmp = try Type.init(10, std.testing.allocator); var bmp = try Type.init(10, std.testing.allocator);
try testing.expectEqual(@as(u32, 2), bmp.bitmaps.len);
defer bmp.deinit(); defer bmp.deinit();
try testing.expectEqual(Type.indexToBit(0), 1); try testing.expectEqual(Type.indexToBit(0), 1);
try testing.expectEqual(Type.indexToBit(1), 2); try testing.expectEqual(Type.indexToBit(1), 2);
@ -759,7 +648,7 @@ test "indexToBit" {
try testing.expectEqual(Type.indexToBit(9), 2); try testing.expectEqual(Type.indexToBit(9), 2);
} }
fn testCheckBitmaps(bmp: Bitmap(u4), b1: u4, b2: u4, b3: u4, b4: u4) !void { fn testCheckBitmaps(bmp: Bitmap(null, u4), b1: u4, b2: u4, b3: u4, b4: u4) !void {
try testing.expectEqual(@as(u4, b1), bmp.bitmaps[0]); try testing.expectEqual(@as(u4, b1), bmp.bitmaps[0]);
try testing.expectEqual(@as(u4, b2), bmp.bitmaps[1]); try testing.expectEqual(@as(u4, b2), bmp.bitmaps[1]);
try testing.expectEqual(@as(u4, b3), bmp.bitmaps[2]); try testing.expectEqual(@as(u4, b3), bmp.bitmaps[2]);
@ -767,7 +656,8 @@ fn testCheckBitmaps(bmp: Bitmap(u4), b1: u4, b2: u4, b3: u4, b4: u4) !void {
} }
test "setContiguous" { test "setContiguous" {
var bmp = try Bitmap(u4).init(16, std.testing.allocator); var bmp = try Bitmap(null, u4).init(16, std.testing.allocator);
try testing.expectEqual(@as(u32, 4), bmp.bitmaps.len);
defer bmp.deinit(); defer bmp.deinit();
// Test trying to set more entries than the bitmap has // Test trying to set more entries than the bitmap has
try testing.expectEqual(bmp.setContiguous(bmp.num_entries + 1, null), null); try testing.expectEqual(bmp.setContiguous(bmp.num_entries + 1, null), null);

View file

@ -6,10 +6,11 @@ const arch = @import("arch.zig").internals;
const MemProfile = @import("mem.zig").MemProfile; const MemProfile = @import("mem.zig").MemProfile;
const testing = std.testing; const testing = std.testing;
const panic = @import("panic.zig").panic; const panic = @import("panic.zig").panic;
const Bitmap = @import("bitmap.zig").Bitmap; const bitmap = @import("bitmap.zig");
const Bitmap = bitmap.Bitmap;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const PmmBitmap = Bitmap(u32); const PmmBitmap = Bitmap(null, u32);
/// The possible errors thrown by bitmap functions /// The possible errors thrown by bitmap functions
const PmmError = error{ const PmmError = error{
@ -20,7 +21,7 @@ const PmmError = error{
/// The size of memory associated with each bitmap entry /// The size of memory associated with each bitmap entry
pub const BLOCK_SIZE: usize = arch.MEMORY_BLOCK_SIZE; pub const BLOCK_SIZE: usize = arch.MEMORY_BLOCK_SIZE;
var bitmap: PmmBitmap = undefined; var the_bitmap: PmmBitmap = undefined;
/// ///
/// Set the bitmap entry for an address as occupied /// Set the bitmap entry for an address as occupied
@ -31,8 +32,8 @@ var bitmap: PmmBitmap = undefined;
/// Error: PmmBitmap.BitmapError. /// Error: PmmBitmap.BitmapError.
/// *: See PmmBitmap.setEntry. Could occur if the address is out of bounds. /// *: See PmmBitmap.setEntry. Could occur if the address is out of bounds.
/// ///
pub fn setAddr(addr: usize) PmmBitmap.BitmapError!void { pub fn setAddr(addr: usize) bitmap.BitmapError!void {
try bitmap.setEntry(@intCast(u32, addr / BLOCK_SIZE)); try the_bitmap.setEntry(@intCast(u32, addr / BLOCK_SIZE));
} }
/// ///
@ -46,8 +47,8 @@ pub fn setAddr(addr: usize) PmmBitmap.BitmapError!void {
/// Error: PmmBitmap.BitmapError. /// Error: PmmBitmap.BitmapError.
/// *: See PmmBitmap.setEntry. Could occur if the address is out of bounds. /// *: See PmmBitmap.setEntry. Could occur if the address is out of bounds.
/// ///
pub fn isSet(addr: usize) PmmBitmap.BitmapError!bool { pub fn isSet(addr: usize) bitmap.BitmapError!bool {
return bitmap.isSet(@intCast(u32, addr / BLOCK_SIZE)); return the_bitmap.isSet(@intCast(u32, addr / BLOCK_SIZE));
} }
/// ///
@ -56,7 +57,7 @@ pub fn isSet(addr: usize) PmmBitmap.BitmapError!bool {
/// Return: The address that was allocated. /// Return: The address that was allocated.
/// ///
pub fn alloc() ?usize { pub fn alloc() ?usize {
if (bitmap.setFirstFree()) |entry| { if (the_bitmap.setFirstFree()) |entry| {
return entry * BLOCK_SIZE; return entry * BLOCK_SIZE;
} }
return null; return null;
@ -72,10 +73,10 @@ pub fn alloc() ?usize {
/// PmmError.NotAllocated: The address wasn't allocated. /// PmmError.NotAllocated: The address wasn't allocated.
/// PmmBitmap.BitmapError.OutOfBounds: The address given was out of bounds. /// PmmBitmap.BitmapError.OutOfBounds: The address given was out of bounds.
/// ///
pub fn free(addr: usize) (PmmBitmap.BitmapError || PmmError)!void { pub fn free(addr: usize) (bitmap.BitmapError || PmmError)!void {
const idx = @intCast(u32, addr / BLOCK_SIZE); const idx = @intCast(u32, addr / BLOCK_SIZE);
if (try bitmap.isSet(idx)) { if (try the_bitmap.isSet(idx)) {
try bitmap.clearEntry(idx); try the_bitmap.clearEntry(idx);
} else { } else {
return PmmError.NotAllocated; return PmmError.NotAllocated;
} }
@ -88,7 +89,7 @@ pub fn free(addr: usize) (PmmBitmap.BitmapError || PmmError)!void {
/// The number of unallocated blocks of memory /// The number of unallocated blocks of memory
/// ///
pub fn blocksFree() usize { pub fn blocksFree() usize {
return bitmap.num_free_entries; return the_bitmap.num_free_entries;
} }
/// Intiialise the physical memory manager and set all unavailable regions as occupied (those from the memory map and those from the linker symbols). /// Intiialise the physical memory manager and set all unavailable regions as occupied (those from the memory map and those from the linker symbols).
@ -101,7 +102,7 @@ pub fn init(mem_profile: *const MemProfile, allocator: Allocator) void {
log.info("Init\n", .{}); log.info("Init\n", .{});
defer log.info("Done\n", .{}); defer log.info("Done\n", .{});
bitmap = PmmBitmap.init(mem_profile.mem_kb * 1024 / BLOCK_SIZE, allocator) catch |e| { the_bitmap = PmmBitmap.init(mem_profile.mem_kb * 1024 / BLOCK_SIZE, allocator) catch |e| {
panic(@errorReturnTrace(), "Bitmap allocation failed: {}\n", .{e}); panic(@errorReturnTrace(), "Bitmap allocation failed: {}\n", .{e});
}; };
@ -116,7 +117,7 @@ pub fn init(mem_profile: *const MemProfile, allocator: Allocator) void {
while (addr < end) : (addr += BLOCK_SIZE) { while (addr < end) : (addr += BLOCK_SIZE) {
setAddr(addr) catch |e| switch (e) { setAddr(addr) catch |e| switch (e) {
// We can ignore out of bounds errors as the memory won't be available anyway // We can ignore out of bounds errors as the memory won't be available anyway
PmmBitmap.BitmapError.OutOfBounds => break, bitmap.BitmapError.OutOfBounds => break,
else => panic(@errorReturnTrace(), "Failed setting address 0x{x} from memory map as occupied: {}", .{ addr, e }), else => panic(@errorReturnTrace(), "Failed setting address 0x{x} from memory map as occupied: {}", .{ addr, e }),
}; };
} }
@ -132,12 +133,12 @@ pub fn init(mem_profile: *const MemProfile, allocator: Allocator) void {
/// Free the internal state of the PMM. Is unusable aftwards unless re-initialised /// Free the internal state of the PMM. Is unusable aftwards unless re-initialised
/// ///
pub fn deinit() void { pub fn deinit() void {
bitmap.deinit(); the_bitmap.deinit();
} }
test "alloc" { test "alloc" {
bitmap = try Bitmap(u32).init(32, testing.allocator); the_bitmap = try Bitmap(null, u32).init(32, testing.allocator);
defer bitmap.deinit(); defer the_bitmap.deinit();
comptime var addr = 0; comptime var addr = 0;
comptime var i = 0; comptime var i = 0;
// Allocate all entries, making sure they succeed and return the correct addresses // Allocate all entries, making sure they succeed and return the correct addresses
@ -155,8 +156,8 @@ test "alloc" {
} }
test "free" { test "free" {
bitmap = try Bitmap(u32).init(32, testing.allocator); the_bitmap = try Bitmap(null, u32).init(32, testing.allocator);
defer bitmap.deinit(); defer the_bitmap.deinit();
comptime var i = 0; comptime var i = 0;
// Allocate and free all entries // Allocate and free all entries
inline while (i < 32) : (i += 1) { inline while (i < 32) : (i += 1) {
@ -173,8 +174,8 @@ test "free" {
test "setAddr and isSet" { test "setAddr and isSet" {
const num_entries: u32 = 32; const num_entries: u32 = 32;
bitmap = try Bitmap(u32).init(num_entries, testing.allocator); the_bitmap = try Bitmap(null, u32).init(num_entries, testing.allocator);
defer bitmap.deinit(); defer the_bitmap.deinit();
var addr: u32 = 0; var addr: u32 = 0;
var i: u32 = 0; var i: u32 = 0;
while (i < num_entries) : ({ while (i < num_entries) : ({

View file

@ -195,8 +195,11 @@ test "pickNextTask" {
tasks = TailQueue(*Task){}; tasks = TailQueue(*Task){};
// Set up a current task // Set up a current task
current_task = try allocator.create(Task); var first = try allocator.create(Task);
defer allocator.destroy(current_task); // We use an intermediary variable to avoid a double-free.
// Deferring freeing current_task will free whatever current_task points to at the end
defer allocator.destroy(first);
current_task = first;
current_task.pid = 0; current_task.pid = 0;
current_task.kernel_stack = @intToPtr([*]u32, @ptrToInt(&KERNEL_STACK_START))[0..4096]; current_task.kernel_stack = @intToPtr([*]u32, @ptrToInt(&KERNEL_STACK_START))[0..4096];
current_task.stack_pointer = @ptrToInt(&KERNEL_STACK_START); current_task.stack_pointer = @ptrToInt(&KERNEL_STACK_START);
@ -232,7 +235,7 @@ test "pickNextTask" {
// The stack pointer of the re-added task should point to the context // The stack pointer of the re-added task should point to the context
try expectEqual(tasks.first.?.data.stack_pointer, @ptrToInt(&ctx)); try expectEqual(tasks.first.?.data.stack_pointer, @ptrToInt(&ctx));
// Should be back tot he beginning // Should be back to the beginning
try expectEqual(current_task.pid, 0); try expectEqual(current_task.pid, 0);
// Reset the test pid // Reset the test pid

View file

@ -11,7 +11,6 @@ const pmm = @import("pmm.zig");
const mem = @import("mem.zig"); const mem = @import("mem.zig");
const elf = @import("elf.zig"); const elf = @import("elf.zig");
const bitmap = @import("bitmap.zig"); const bitmap = @import("bitmap.zig");
const ComptimeBitmap = bitmap.ComptimeBitmap;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const log = std.log.scoped(.task); const log = std.log.scoped(.task);
@ -23,14 +22,14 @@ extern var KERNEL_STACK_START: *u32;
pub const EntryPoint = usize; pub const EntryPoint = usize;
/// The bitmap type for the PIDs /// The bitmap type for the PIDs
const PidBitmap = if (is_test) ComptimeBitmap(u128) else ComptimeBitmap(u1024); const PidBitmap = bitmap.Bitmap(1024, usize);
/// The list of PIDs that have been allocated. /// The list of PIDs that have been allocated.
var all_pids: PidBitmap = brk: { var all_pids: PidBitmap = init: {
var pids = PidBitmap.init(); var pids = PidBitmap.init(1024, null) catch unreachable;
// Set the first PID as this is for the current task running, init 0 // Reserve PID 0 for the init task
_ = pids.setFirstFree() orelse unreachable; _ = pids.setFirstFree() orelse unreachable;
break :brk pids; break :init pids;
}; };
/// The default stack size of a task. Currently this is set to a page size. /// The default stack size of a task. Currently this is set to a page size.
@ -41,7 +40,7 @@ pub const Task = struct {
const Self = @This(); const Self = @This();
/// The unique task identifier /// The unique task identifier
pid: PidBitmap.IndexType, pid: usize,
/// Pointer to the kernel stack for the task. This will be allocated on initialisation. /// Pointer to the kernel stack for the task. This will be allocated on initialisation.
kernel_stack: []usize, kernel_stack: []usize,
@ -81,7 +80,7 @@ pub const Task = struct {
errdefer allocator.destroy(task); errdefer allocator.destroy(task);
const pid = allocatePid(); const pid = allocatePid();
errdefer freePid(pid); errdefer freePid(pid) catch |e| panic(@errorReturnTrace(), "Failed to free task PID in errdefer ({}): {}\n", .{ pid, e });
var k_stack = try allocator.alloc(usize, STACK_SIZE); var k_stack = try allocator.alloc(usize, STACK_SIZE);
errdefer allocator.free(k_stack); errdefer allocator.free(k_stack);
@ -103,7 +102,7 @@ pub const Task = struct {
return task; return task;
} }
pub fn createFromElf(program_elf: elf.Elf, kernel: bool, task_vmm: *vmm.VirtualMemoryManager(arch.VmmPayload), allocator: Allocator) (bitmap.Bitmap(usize).BitmapError || vmm.VmmError || Allocator.Error)!*Task { pub fn createFromElf(program_elf: elf.Elf, kernel: bool, task_vmm: *vmm.VirtualMemoryManager(arch.VmmPayload), allocator: Allocator) (bitmap.BitmapError || vmm.VmmError || Allocator.Error)!*Task {
const task = try create(program_elf.header.entry_address, kernel, task_vmm, allocator); const task = try create(program_elf.header.entry_address, kernel, task_vmm, allocator);
errdefer task.destroy(allocator); errdefer task.destroy(allocator);
@ -143,7 +142,7 @@ pub const Task = struct {
/// IN allocator: Allocator - The allocator used to create the task. /// IN allocator: Allocator - The allocator used to create the task.
/// ///
pub fn destroy(self: *Self, allocator: Allocator) void { pub fn destroy(self: *Self, allocator: Allocator) void {
freePid(self.pid); freePid(self.pid) catch |e| panic(@errorReturnTrace(), "Failed to free task's PID ({}): {}\n", .{ self.pid, e });
// We need to check that the the stack has been allocated as task 0 (init) won't have a // We need to check that the the stack has been allocated as task 0 (init) won't have a
// stack allocated as this in the linker script // stack allocated as this in the linker script
if (@ptrToInt(self.kernel_stack.ptr) != @ptrToInt(&KERNEL_STACK_START)) { if (@ptrToInt(self.kernel_stack.ptr) != @ptrToInt(&KERNEL_STACK_START)) {
@ -163,7 +162,7 @@ pub const Task = struct {
/// Return: u32 /// Return: u32
/// A new PID. /// A new PID.
/// ///
fn allocatePid() PidBitmap.IndexType { fn allocatePid() usize {
return all_pids.setFirstFree() orelse panic(@errorReturnTrace(), "Out of PIDs\n", .{}); return all_pids.setFirstFree() orelse panic(@errorReturnTrace(), "Out of PIDs\n", .{});
} }
@ -171,13 +170,16 @@ fn allocatePid() PidBitmap.IndexType {
/// Free an allocated PID. One must be allocated to be freed. If one wasn't allocated will panic. /// Free an allocated PID. One must be allocated to be freed. If one wasn't allocated will panic.
/// ///
/// Arguments: /// Arguments:
/// IN pid: u32 - The PID to free. /// IN pid: usize - The PID to free.
/// ///
fn freePid(pid: PidBitmap.IndexType) void { /// Error: BitmapError.
if (!all_pids.isSet(pid)) { /// OutOfBounds: The index given is out of bounds.
///
fn freePid(pid: usize) bitmap.BitmapError!void {
if (!(try all_pids.isSet(pid))) {
panic(@errorReturnTrace(), "PID {} not allocated\n", .{pid}); panic(@errorReturnTrace(), "PID {} not allocated\n", .{pid});
} }
all_pids.clearEntry(pid); try all_pids.clearEntry(pid);
} }
// For testing the errdefer // For testing the errdefer
@ -197,7 +199,9 @@ test "create out of memory for task" {
try expectEqual(fa.allocated_bytes, fa.freed_bytes); try expectEqual(fa.allocated_bytes, fa.freed_bytes);
// Make sure no PIDs were allocated // Make sure no PIDs were allocated
try expectEqual(all_pids.bitmap, 0); for (all_pids.bitmaps) |bmp| {
try expectEqual(bmp, 0);
}
} }
test "create out of memory for stack" { test "create out of memory for stack" {
@ -211,7 +215,9 @@ test "create out of memory for stack" {
try expectEqual(fa.allocated_bytes, fa.freed_bytes); try expectEqual(fa.allocated_bytes, fa.freed_bytes);
// Make sure no PIDs were allocated // Make sure no PIDs were allocated
try expectEqual(all_pids.bitmap, 0); for (all_pids.bitmaps) |bmp| {
try expectEqual(bmp, 0);
}
} }
test "create expected setup" { test "create expected setup" {
@ -242,7 +248,9 @@ test "destroy cleans up" {
user_task.destroy(allocator); user_task.destroy(allocator);
// All PIDs were freed // All PIDs were freed
try expectEqual(all_pids.bitmap, 0); for (all_pids.bitmaps) |bmp| {
try expectEqual(bmp, 0);
}
} }
test "Multiple create" { test "Multiple create" {
@ -251,16 +259,25 @@ test "Multiple create" {
try expectEqual(task1.pid, 0); try expectEqual(task1.pid, 0);
try expectEqual(task2.pid, 1); try expectEqual(task2.pid, 1);
try expectEqual(all_pids.bitmap, 3); try expectEqual(all_pids.bitmaps[0], 3);
for (all_pids.bitmaps) |bmp, i| {
if (i > 0) try expectEqual(bmp, 0);
}
task1.destroy(std.testing.allocator); task1.destroy(std.testing.allocator);
try expectEqual(all_pids.bitmap, 2); try expectEqual(all_pids.bitmaps[0], 2);
for (all_pids.bitmaps) |bmp, i| {
if (i > 0) try expectEqual(bmp, 0);
}
var task3 = try Task.create(@ptrToInt(test_fn1), true, undefined, std.testing.allocator); var task3 = try Task.create(@ptrToInt(test_fn1), true, undefined, std.testing.allocator);
try expectEqual(task3.pid, 0); try expectEqual(task3.pid, 0);
try expectEqual(all_pids.bitmap, 3); try expectEqual(all_pids.bitmaps[0], 3);
for (all_pids.bitmaps) |bmp, i| {
if (i > 0) try expectEqual(bmp, 0);
}
task2.destroy(std.testing.allocator); task2.destroy(std.testing.allocator);
task3.destroy(std.testing.allocator); task3.destroy(std.testing.allocator);
@ -268,28 +285,39 @@ test "Multiple create" {
var user_task = try Task.create(@ptrToInt(test_fn1), false, undefined, std.testing.allocator); var user_task = try Task.create(@ptrToInt(test_fn1), false, undefined, std.testing.allocator);
try expectEqual(user_task.pid, 0); try expectEqual(user_task.pid, 0);
try expectEqual(all_pids.bitmap, 1); try expectEqual(all_pids.bitmaps[0], 1);
for (all_pids.bitmaps) |bmp, i| {
if (i > 0) try expectEqual(bmp, 0);
}
user_task.destroy(std.testing.allocator); user_task.destroy(std.testing.allocator);
try expectEqual(all_pids.bitmap, 0); for (all_pids.bitmaps) |bmp| {
try expectEqual(bmp, 0);
}
} }
test "allocatePid and freePid" { test "allocatePid and freePid" {
try expectEqual(all_pids.bitmap, 0); for (all_pids.bitmaps) |bmp| {
try expectEqual(bmp, 0);
}
var i: usize = 0; var i: usize = 0;
while (i < PidBitmap.NUM_ENTRIES) : (i += 1) { while (i < all_pids.num_entries) : (i += 1) {
try expectEqual(i, allocatePid()); try expectEqual(i, allocatePid());
} }
try expectEqual(all_pids.bitmap, PidBitmap.BITMAP_FULL); for (all_pids.bitmaps) |bmp| {
try expectEqual(bmp, PidBitmap.BITMAP_FULL);
i = 0;
while (i < PidBitmap.NUM_ENTRIES) : (i += 1) {
freePid(@truncate(PidBitmap.IndexType, i));
} }
try expectEqual(all_pids.bitmap, 0); i = 0;
while (i < all_pids.num_entries) : (i += 1) {
try freePid(i);
}
for (all_pids.bitmaps) |bmp| {
try expectEqual(bmp, 0);
}
} }
test "createFromElf" { test "createFromElf" {
@ -333,7 +361,7 @@ test "createFromElf clean-up" {
// Test OutOfMemory // Test OutOfMemory
var allocator2 = std.testing.FailingAllocator.init(allocator, 0).allocator(); var allocator2 = std.testing.FailingAllocator.init(allocator, 0).allocator();
try std.testing.expectError(std.mem.Allocator.Error.OutOfMemory, Task.createFromElf(the_elf, true, &the_vmm, allocator2)); try std.testing.expectError(std.mem.Allocator.Error.OutOfMemory, Task.createFromElf(the_elf, true, &the_vmm, allocator2));
try std.testing.expectEqual(all_pids.num_free_entries, PidBitmap.NUM_ENTRIES - 1); try std.testing.expectEqual(all_pids.num_free_entries, all_pids.num_entries - 1);
// Test AlreadyAllocated // Test AlreadyAllocated
try std.testing.expectError(error.AlreadyAllocated, Task.createFromElf(the_elf, true, &the_vmm, allocator)); try std.testing.expectError(error.AlreadyAllocated, Task.createFromElf(the_elf, true, &the_vmm, allocator));
// Test OutOfBounds // Test OutOfBounds

View file

@ -133,7 +133,7 @@ pub var kernel_vmm: VirtualMemoryManager(arch.VmmPayload) = undefined;
pub fn VirtualMemoryManager(comptime Payload: type) type { pub fn VirtualMemoryManager(comptime Payload: type) type {
return struct { return struct {
/// The bitmap that keeps track of allocated and free regions /// The bitmap that keeps track of allocated and free regions
bmp: bitmap.Bitmap(usize), bmp: bitmap.Bitmap(null, usize),
/// The start of the memory to be tracked /// The start of the memory to be tracked
start: usize, start: usize,
@ -173,7 +173,7 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
/// ///
pub fn init(start: usize, end: usize, allocator: Allocator, mapper: Mapper(Payload), payload: Payload) Allocator.Error!Self { pub fn init(start: usize, end: usize, allocator: Allocator, mapper: Mapper(Payload), payload: Payload) Allocator.Error!Self {
const size = end - start; const size = end - start;
var bmp = try bitmap.Bitmap(usize).init(std.mem.alignForward(size, pmm.BLOCK_SIZE) / pmm.BLOCK_SIZE, allocator); var bmp = try bitmap.Bitmap(null, usize).init(std.mem.alignForward(size, pmm.BLOCK_SIZE) / pmm.BLOCK_SIZE, allocator);
return Self{ return Self{
.bmp = bmp, .bmp = bmp,
.start = start, .start = start,
@ -305,7 +305,10 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
/// Error: pmm.PmmError /// Error: pmm.PmmError
/// Bitmap(u32).Error.OutOfBounds - The address given is outside of the memory managed /// Bitmap(u32).Error.OutOfBounds - The address given is outside of the memory managed
/// ///
pub fn isSet(self: *const Self, virt: usize) bitmap.Bitmap(u32).BitmapError!bool { pub fn isSet(self: *const Self, virt: usize) bitmap.BitmapError!bool {
if (virt < self.start) {
return bitmap.BitmapError.OutOfBounds;
}
return self.bmp.isSet((virt - self.start) / BLOCK_SIZE); return self.bmp.isSet((virt - self.start) / BLOCK_SIZE);
} }
@ -328,7 +331,7 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
/// Allocator.Error.OutOfMemory - Allocating the required memory failed /// Allocator.Error.OutOfMemory - Allocating the required memory failed
/// MapperError.* - The causes depend on the mapper used /// MapperError.* - The causes depend on the mapper used
/// ///
pub fn set(self: *Self, virtual: mem.Range, physical: ?mem.Range, attrs: Attributes) (VmmError || bitmap.Bitmap(u32).BitmapError || Allocator.Error || MapperError)!void { pub fn set(self: *Self, virtual: mem.Range, physical: ?mem.Range, attrs: Attributes) (VmmError || bitmap.BitmapError || Allocator.Error || MapperError)!void {
var virt = virtual.start; var virt = virtual.start;
while (virt < virtual.end) : (virt += BLOCK_SIZE) { while (virt < virtual.end) : (virt += BLOCK_SIZE) {
if (try self.isSet(virt)) { if (try self.isSet(virt)) {
@ -440,14 +443,15 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
/// Bitmap(u32).Error.OutOfBounds - The address given is outside of the memory managed /// Bitmap(u32).Error.OutOfBounds - The address given is outside of the memory managed
/// Allocator.Error.OutOfMemory - There wasn't enough memory available to fulfill the request /// Allocator.Error.OutOfMemory - There wasn't enough memory available to fulfill the request
/// ///
pub fn copyData(self: *Self, other: *const Self, comptime from: bool, data: if (from) []const u8 else []u8, address: usize) (bitmap.Bitmap(usize).BitmapError || VmmError || Allocator.Error)!void { pub fn copyData(self: *Self, other: *const Self, comptime from: bool, data: if (from) []const u8 else []u8, address: usize) (bitmap.BitmapError || VmmError || Allocator.Error)!void {
if (data.len == 0) { if (data.len == 0) {
return; return;
} }
const start_addr = std.mem.alignBackward(address, BLOCK_SIZE); const start_addr = std.mem.alignBackward(address, BLOCK_SIZE);
const end_addr = std.mem.alignForward(address + data.len, BLOCK_SIZE); const end_addr = std.mem.alignForward(address + data.len, BLOCK_SIZE);
if (end_addr >= other.end or start_addr < other.start) if (end_addr >= other.end or start_addr < other.start)
return bitmap.Bitmap(usize).BitmapError.OutOfBounds; return bitmap.BitmapError.OutOfBounds;
// Find physical blocks for the address // Find physical blocks for the address
var blocks = std.ArrayList(usize).init(self.allocator); var blocks = std.ArrayList(usize).init(self.allocator);
defer blocks.deinit(); defer blocks.deinit();
@ -511,7 +515,7 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
/// VmmError.NotAllocated - This address hasn't been allocated yet /// VmmError.NotAllocated - This address hasn't been allocated yet
/// Bitmap.BitmapError.OutOfBounds - The address is out of the manager's bounds /// Bitmap.BitmapError.OutOfBounds - The address is out of the manager's bounds
/// ///
pub fn free(self: *Self, vaddr: usize) (bitmap.Bitmap(u32).BitmapError || VmmError)!void { pub fn free(self: *Self, vaddr: usize) (bitmap.BitmapError || VmmError)!void {
const entry = (vaddr - self.start) / BLOCK_SIZE; const entry = (vaddr - self.start) / BLOCK_SIZE;
if (try self.bmp.isSet(entry)) { if (try self.bmp.isSet(entry)) {
// There will be an allocation associated with this virtual address // There will be an allocation associated with this virtual address
@ -666,8 +670,8 @@ test "alloc and free" {
// Allocation failed as there weren't enough free entries // Allocation failed as there weren't enough free entries
if (vaddr >= num_entries * BLOCK_SIZE) { if (vaddr >= num_entries * BLOCK_SIZE) {
// If this address is beyond the VMM's end address, it should be out of bounds // If this address is beyond the VMM's end address, it should be out of bounds
try std.testing.expectError(bitmap.Bitmap(u32).BitmapError.OutOfBounds, vmm.isSet(vaddr)); try std.testing.expectError(bitmap.BitmapError.OutOfBounds, vmm.isSet(vaddr));
try std.testing.expectError(bitmap.Bitmap(u64).BitmapError.OutOfBounds, allocations.isSet(vaddr / BLOCK_SIZE)); try std.testing.expectError(bitmap.BitmapError.OutOfBounds, allocations.isSet(vaddr / BLOCK_SIZE));
} else { } else {
// Else it should not be set // Else it should not be set
try std.testing.expect(!(try vmm.isSet(vaddr))); try std.testing.expect(!(try vmm.isSet(vaddr)));
@ -685,6 +689,8 @@ test "alloc and free" {
try std.testing.expect(!(try pmm.isSet(later_entry * BLOCK_SIZE))); try std.testing.expect(!(try pmm.isSet(later_entry * BLOCK_SIZE)));
} }
} }
// Out of bounds entries should cause an error
try std.testing.expectError(bitmap.BitmapError.OutOfBounds, vmm.isSet((entry + 1) * BLOCK_SIZE));
// Try freeing all allocations // Try freeing all allocations
for (virtual_allocations.items) |alloc| { for (virtual_allocations.items) |alloc| {
@ -832,8 +838,8 @@ test "copyData from" {
try std.testing.expectEqual(vmm2_free_entries - 1, vmm2.bmp.num_free_entries); try std.testing.expectEqual(vmm2_free_entries - 1, vmm2.bmp.num_free_entries);
// Test Bitmap.Error.OutOfBounds // Test Bitmap.Error.OutOfBounds
try std.testing.expectError(bitmap.Bitmap(usize).BitmapError.OutOfBounds, vmm2.copyData(&vmm, true, buff[0..buff.len], vmm.end)); try std.testing.expectError(bitmap.BitmapError.OutOfBounds, vmm2.copyData(&vmm, true, buff[0..buff.len], vmm.end));
try std.testing.expectError(bitmap.Bitmap(usize).BitmapError.OutOfBounds, vmm.copyData(&vmm2, true, buff[0..buff.len], vmm2.end)); try std.testing.expectError(bitmap.BitmapError.OutOfBounds, vmm.copyData(&vmm2, true, buff[0..buff.len], vmm2.end));
try std.testing.expectEqual(vmm_free_entries, vmm.bmp.num_free_entries); try std.testing.expectEqual(vmm_free_entries, vmm.bmp.num_free_entries);
try std.testing.expectEqual(vmm2_free_entries - 1, vmm2.bmp.num_free_entries); try std.testing.expectEqual(vmm2_free_entries - 1, vmm2.bmp.num_free_entries);
} }
@ -857,7 +863,7 @@ test "copyDaya to" {
try std.testing.expectEqual(vmm2_free_entries - 1, vmm2.bmp.num_free_entries); try std.testing.expectEqual(vmm2_free_entries - 1, vmm2.bmp.num_free_entries);
} }
var test_allocations: ?*bitmap.Bitmap(u64) = null; var test_allocations: ?*bitmap.Bitmap(null, u64) = null;
var test_mapper = Mapper(arch.VmmPayload){ .mapFn = testMap, .unmapFn = testUnmap }; var test_mapper = Mapper(arch.VmmPayload){ .mapFn = testMap, .unmapFn = testUnmap };
/// ///
@ -874,8 +880,8 @@ var test_mapper = Mapper(arch.VmmPayload){ .mapFn = testMap, .unmapFn = testUnma
/// ///
pub fn testInit(num_entries: u32) Allocator.Error!VirtualMemoryManager(arch.VmmPayload) { pub fn testInit(num_entries: u32) Allocator.Error!VirtualMemoryManager(arch.VmmPayload) {
if (test_allocations == null) { if (test_allocations == null) {
test_allocations = try std.testing.allocator.create(bitmap.Bitmap(u64)); test_allocations = try std.testing.allocator.create(bitmap.Bitmap(null, u64));
test_allocations.?.* = try bitmap.Bitmap(u64).init(num_entries, std.testing.allocator); test_allocations.?.* = try bitmap.Bitmap(null, u64).init(num_entries, std.testing.allocator);
} else |allocations| { } else |allocations| {
var entry: u32 = 0; var entry: u32 = 0;
while (entry < allocations.num_entries) : (entry += 1) { while (entry < allocations.num_entries) : (entry += 1) {