-
Notifications
You must be signed in to change notification settings - Fork 31
Add physical memory manager #104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,306 @@ | ||
| const std = @import("std"); | ||
| const builtin = @import("builtin"); | ||
| const testing = std.testing; | ||
|
|
||
| /// | ||
| /// A bitmap that uses a specific type to store the entries. | ||
| /// | ||
| /// Arguments: | ||
| /// IN BitmapType: type - The integer type to use to store entries. | ||
| /// | ||
| /// Return: type. | ||
| /// The bitmap type created. | ||
| /// | ||
| pub fn Bitmap(comptime BitmapType: type) type { | ||
SamTebbs33 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return struct { | ||
| /// The possible errors thrown by bitmap functions | ||
| pub const BitmapError = error{ | ||
| /// The address given was outside the region covered by a bitmap | ||
| OutOfBounds, | ||
| }; | ||
|
|
||
| const Self = @This(); | ||
|
|
||
| /// The number of entries that one bitmap type can hold. Evaluates to the number of bits the type has | ||
| pub const ENTRIES_PER_BITMAP: u32 = std.meta.bitCount(BitmapType); | ||
SamTebbs33 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /// 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 = @Type(builtin.TypeInfo{ .Int = builtin.TypeInfo.Int{ .is_signed = false, .bits = std.math.log2(std.meta.bitCount(BitmapType)) } }); | ||
|
|
||
| num_bitmaps: u32, | ||
| num_entries: u32, | ||
| bitmaps: []BitmapType, | ||
| num_free_entries: u32, | ||
|
|
||
| /// | ||
| /// Create an instance of this bitmap type. | ||
| /// | ||
| /// Arguments: | ||
| /// IN num_entries: u32 - The number of entries that the bitmap created will have. | ||
| /// The number of BitmapType required to store this many entries will be allocated and each will be zeroed. | ||
| /// IN allocator: *std.mem.Allocator - The allocator to use when allocating the BitmapTypes required. | ||
| /// | ||
| /// Return: Self. | ||
| /// The bitmap instance. | ||
| /// | ||
| /// Error: std.mem.Allocator.Error | ||
| /// OutOfMemory: There isn't enough memory available to allocate the required number of BitmapType. | ||
| /// | ||
| pub fn init(num_entries: u32, allocator: *std.mem.Allocator) !Self { | ||
| const num = @floatToInt(u32, @ceil(@intToFloat(f32, num_entries) / @intToFloat(f32, ENTRIES_PER_BITMAP))); | ||
| const self = Self{ | ||
| .num_bitmaps = num, | ||
| .num_entries = num_entries, | ||
| .bitmaps = try allocator.alloc(BitmapType, num), | ||
| .num_free_entries = num_entries, | ||
| }; | ||
| for (self.bitmaps) |*bmp| { | ||
| bmp.* = 0; | ||
| } | ||
| return self; | ||
| } | ||
|
|
||
| /// | ||
| /// Set an entry within a bitmap as occupied. | ||
| /// | ||
| /// Arguments: | ||
| /// INOUT self: *Self - The bitmap to modify. | ||
| /// IN idx: BitmapIndex - The index within the bitmap to set. | ||
| /// | ||
| /// Error: BitmapError. | ||
| /// OutOfBounds: The index given is out of bounds. | ||
| /// | ||
| pub fn setEntry(self: *Self, idx: u32) BitmapError!void { | ||
| if (idx >= self.num_entries) return BitmapError.OutOfBounds; | ||
| if (!try self.isSet(idx)) { | ||
| const bit = self.indexToBit(idx); | ||
| self.bitmaps[idx / ENTRIES_PER_BITMAP] |= bit; | ||
| self.num_free_entries -= 1; | ||
| } | ||
| } | ||
|
|
||
| /// | ||
| /// Set an entry within a bitmap as unoccupied. | ||
| /// | ||
| /// Arguments: | ||
| /// INOUT self: *Self - The bitmap to modify. | ||
| /// IN idx: BitmapIndex - The index within the bitmap to clear. | ||
| /// | ||
| /// Error: BitmapError. | ||
| /// OutOfBounds: The index given is out of bounds. | ||
| /// | ||
| pub fn clearEntry(self: *Self, idx: u32) BitmapError!void { | ||
SamTebbs33 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (idx >= self.num_entries) return BitmapError.OutOfBounds; | ||
| if (try self.isSet(idx)) { | ||
| const bit = self.indexToBit(idx); | ||
| self.bitmaps[idx / ENTRIES_PER_BITMAP] &= ~bit; | ||
| self.num_free_entries += 1; | ||
| } | ||
| } | ||
|
|
||
| /// | ||
| /// Convert a global bitmap index into the bit corresponding to an entry within a single BitmapType. | ||
| /// | ||
| /// Arguments: | ||
| /// IN self: *Self - The bitmap to use. | ||
| /// IN idx: u32 - The index into all of the bitmap's entries. | ||
| /// | ||
| /// Return: BitmapType. | ||
| /// The bit corresponding to that index but within a single BitmapType. | ||
| /// | ||
| fn indexToBit(self: *Self, idx: u32) BitmapType { | ||
| return @as(BitmapType, 1) << @intCast(IndexType, idx % ENTRIES_PER_BITMAP); | ||
| } | ||
|
|
||
| /// | ||
| /// Set the first free entry within the bitmaps as occupied. | ||
| /// | ||
| /// Return: ?u32. | ||
| /// The index within all bitmaps that was set or null if there wasn't one free. | ||
| /// 0 .. ENTRIES_PER_BITMAP - 1 if in the first bitmap, ENTRIES_PER_BITMAP .. ENTRIES_PER_BITMAP * 2 - 1 if in the second etc. | ||
| /// | ||
| pub fn setFirstFree(self: *Self) ?u32 { | ||
| if (self.num_free_entries == 0) return null; | ||
| for (self.bitmaps) |*bmp, i| { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| if (bmp.* == BITMAP_FULL) | ||
| continue; | ||
| const bit = @truncate(IndexType, @ctz(BitmapType, ~bmp.*)); | ||
| const idx = bit + @intCast(u32, i) * ENTRIES_PER_BITMAP; | ||
| // Failing here means that the index is outside of the bitmap, so there are no free entries | ||
| self.setEntry(idx) catch return null; | ||
| return idx; | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| /// | ||
| /// Check if an entry is set. | ||
| /// | ||
| /// Arguments: | ||
| /// IN self: *Bitmap - The bitmap to check. | ||
| /// IN idx: u32 - The entry to check. | ||
| /// | ||
| /// Return: bool. | ||
| /// True if the entry is set, else false. | ||
| /// | ||
| /// Error: BitmapError. | ||
| /// OutOfBounds: The index given is out of bounds. | ||
| /// | ||
| pub fn isSet(self: *Self, idx: u32) BitmapError!bool { | ||
DrDeano marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (idx >= self.num_entries) return BitmapError.OutOfBounds; | ||
| return (self.bitmaps[idx / ENTRIES_PER_BITMAP] & self.indexToBit(idx)) != 0; | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| test "setEntry" { | ||
SamTebbs33 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| var bmp = try Bitmap(u32).init(31, std.heap.direct_allocator); | ||
| testing.expectEqual(@as(u32, 31), bmp.num_free_entries); | ||
|
|
||
| try bmp.setEntry(0); | ||
| testing.expectEqual(@as(u32, 1), bmp.bitmaps[0]); | ||
| testing.expectEqual(@as(u32, 30), bmp.num_free_entries); | ||
|
|
||
| try bmp.setEntry(1); | ||
| testing.expectEqual(@as(u32, 3), bmp.bitmaps[0]); | ||
| testing.expectEqual(@as(u32, 29), bmp.num_free_entries); | ||
|
|
||
| // Repeat setting entry 1 to make sure state doesn't change | ||
| try bmp.setEntry(1); | ||
| testing.expectEqual(@as(u32, 3), bmp.bitmaps[0]); | ||
| testing.expectEqual(@as(u32, 29), bmp.num_free_entries); | ||
|
|
||
| testing.expectError(Bitmap(u32).BitmapError.OutOfBounds, bmp.setEntry(31)); | ||
| testing.expectEqual(@as(u32, 29), bmp.num_free_entries); | ||
| } | ||
|
|
||
| test "clearEntry" { | ||
SamTebbs33 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| var bmp = try Bitmap(u32).init(32, std.heap.direct_allocator); | ||
| testing.expectEqual(@as(u32, 32), bmp.num_free_entries); | ||
|
|
||
| try bmp.setEntry(0); | ||
| testing.expectEqual(@as(u32, 31), bmp.num_free_entries); | ||
| try bmp.setEntry(1); | ||
| testing.expectEqual(@as(u32, 30), bmp.num_free_entries); | ||
| testing.expectEqual(@as(u32, 3), bmp.bitmaps[0]); | ||
| try bmp.clearEntry(0); | ||
| testing.expectEqual(@as(u32, 31), bmp.num_free_entries); | ||
| testing.expectEqual(@as(u32, 2), bmp.bitmaps[0]); | ||
|
|
||
| // Repeat to make sure state doesn't change | ||
| try bmp.clearEntry(0); | ||
| testing.expectEqual(@as(u32, 31), bmp.num_free_entries); | ||
| testing.expectEqual(@as(u32, 2), bmp.bitmaps[0]); | ||
|
|
||
| // Try clearing an unset entry to make sure state doesn't change | ||
| try bmp.clearEntry(2); | ||
| testing.expectEqual(@as(u32, 31), bmp.num_free_entries); | ||
| testing.expectEqual(@as(u32, 2), bmp.bitmaps[0]); | ||
|
|
||
| testing.expectError(Bitmap(u32).BitmapError.OutOfBounds, bmp.clearEntry(32)); | ||
| } | ||
|
|
||
| test "setFirstFree multiple bitmaps" { | ||
| var bmp = try Bitmap(u8).init(9, std.heap.direct_allocator); | ||
|
|
||
| // Allocate the first entry | ||
| testing.expectEqual(bmp.setFirstFree() orelse unreachable, 0); | ||
| testing.expectEqual(bmp.bitmaps[0], 1); | ||
|
|
||
| // Allocate the second entry | ||
| testing.expectEqual(bmp.setFirstFree() orelse unreachable, 1); | ||
| testing.expectEqual(bmp.bitmaps[0], 3); | ||
|
|
||
| // Allocate the entirety of the first bitmap | ||
| var entry: u32 = 2; | ||
| var expected: u8 = 7; | ||
| while (entry < Bitmap(u8).ENTRIES_PER_BITMAP) { | ||
| testing.expectEqual(bmp.setFirstFree() orelse unreachable, entry); | ||
| testing.expectEqual(bmp.bitmaps[0], expected); | ||
| if (entry + 1 < Bitmap(u8).ENTRIES_PER_BITMAP) { | ||
| entry += 1; | ||
| expected = expected * 2 + 1; | ||
| } else { | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| // Try allocating an entry in the next bitmap | ||
| testing.expectEqual(bmp.setFirstFree() orelse unreachable, Bitmap(u8).ENTRIES_PER_BITMAP); | ||
| testing.expectEqual(bmp.bitmaps[0], Bitmap(u8).BITMAP_FULL); | ||
| testing.expectEqual(bmp.bitmaps[1], 1); | ||
|
|
||
| // We should no longer be able to allocate any entries | ||
| testing.expectEqual(bmp.setFirstFree(), null); | ||
| testing.expectEqual(bmp.bitmaps[0], Bitmap(u8).BITMAP_FULL); | ||
| testing.expectEqual(bmp.bitmaps[1], 1); | ||
| } | ||
| test "setFirstFree" { | ||
SamTebbs33 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| var bmp = try Bitmap(u32).init(32, std.heap.direct_allocator); | ||
|
|
||
| // Allocate the first entry | ||
| testing.expectEqual(bmp.setFirstFree() orelse unreachable, 0); | ||
| testing.expectEqual(bmp.bitmaps[0], 1); | ||
|
|
||
| // Allocate the second entry | ||
| testing.expectEqual(bmp.setFirstFree() orelse unreachable, 1); | ||
| testing.expectEqual(bmp.bitmaps[0], 3); | ||
|
|
||
| // Make all but the MSB occupied and try to allocate it | ||
| bmp.bitmaps[0] = Bitmap(u32).BITMAP_FULL & ~@as(u32, 1 << (Bitmap(u32).ENTRIES_PER_BITMAP - 1)); | ||
| testing.expectEqual(bmp.setFirstFree() orelse unreachable, Bitmap(u32).ENTRIES_PER_BITMAP - 1); | ||
| testing.expectEqual(bmp.bitmaps[0], Bitmap(u32).BITMAP_FULL); | ||
|
|
||
| // We should no longer be able to allocate any entries | ||
| testing.expectEqual(bmp.setFirstFree(), null); | ||
| testing.expectEqual(bmp.bitmaps[0], Bitmap(u32).BITMAP_FULL); | ||
| } | ||
|
|
||
| test "isSet" { | ||
| var bmp = try Bitmap(u32).init(32, std.heap.direct_allocator); | ||
|
|
||
| bmp.bitmaps[0] = 1; | ||
| // Make sure that only the set entry is considered set | ||
| testing.expect(try bmp.isSet(0)); | ||
| var i: u32 = 1; | ||
| while (i < bmp.num_entries) : (i += 1) { | ||
| testing.expect(!try bmp.isSet(i)); | ||
| } | ||
|
|
||
| bmp.bitmaps[0] = 3; | ||
| testing.expect(try bmp.isSet(0)); | ||
| testing.expect(try bmp.isSet(1)); | ||
| i = 2; | ||
| while (i < bmp.num_entries) : (i += 1) { | ||
| testing.expect(!try bmp.isSet(i)); | ||
| } | ||
|
|
||
| bmp.bitmaps[0] = 11; | ||
| testing.expect(try bmp.isSet(0)); | ||
| testing.expect(try bmp.isSet(1)); | ||
| testing.expect(!try bmp.isSet(2)); | ||
| testing.expect(try bmp.isSet(3)); | ||
SamTebbs33 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| i = 4; | ||
| while (i < bmp.num_entries) : (i += 1) { | ||
| testing.expect(!try bmp.isSet(i)); | ||
| } | ||
|
|
||
| testing.expectError(Bitmap(u32).BitmapError.OutOfBounds, bmp.isSet(33)); | ||
| } | ||
|
|
||
| test "indexToBit" { | ||
| var bmp = try Bitmap(u8).init(10, std.heap.direct_allocator); | ||
| testing.expectEqual(bmp.indexToBit(0), 1); | ||
| testing.expectEqual(bmp.indexToBit(1), 2); | ||
| testing.expectEqual(bmp.indexToBit(2), 4); | ||
| testing.expectEqual(bmp.indexToBit(3), 8); | ||
| testing.expectEqual(bmp.indexToBit(4), 16); | ||
| testing.expectEqual(bmp.indexToBit(5), 32); | ||
| testing.expectEqual(bmp.indexToBit(6), 64); | ||
| testing.expectEqual(bmp.indexToBit(7), 128); | ||
| testing.expectEqual(bmp.indexToBit(8), 1); | ||
| testing.expectEqual(bmp.indexToBit(9), 2); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.