Merge pull request #318 from ZystemOS/f/combine-bitmaps
Combine ComptimeBitmap and Bitmap
This commit is contained in:
commit
71497a9284
5 changed files with 281 additions and 353 deletions
|
@ -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);
|
||||||
|
|
|
@ -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) : ({
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in a new issue