103 lines
2.5 KiB
Zig
Executable File
103 lines
2.5 KiB
Zig
Executable File
const debug = @import("./debug.zig");
|
|
|
|
const io = @import("./io.zig");
|
|
|
|
const list = @import("./list.zig");
|
|
|
|
const math = @import("./math.zig");
|
|
|
|
pub const Stacking = struct {
|
|
base_allocator: io.Allocator,
|
|
min_page_size: usize,
|
|
allocations: list.Stack(usize) = .{},
|
|
pages: list.Stack(Page) = .{},
|
|
|
|
const Page = struct {
|
|
buffer: []u8,
|
|
used: usize,
|
|
|
|
const Self = @This();
|
|
|
|
fn available(self: Self) usize {
|
|
return self.buffer.len - self.used;
|
|
}
|
|
};
|
|
|
|
pub fn allocate(self: *Stacking, allocation_size: usize) io.AllocationError![]u8 {
|
|
const alignment = @as(usize, 4);
|
|
const aligned_allocation_size = (allocation_size + alignment - 1) & ~(alignment - 1);
|
|
|
|
if (self.pages.values.len == 0) {
|
|
const page = try self.allocate_page(math.max(self.min_page_size, aligned_allocation_size));
|
|
|
|
page.used = allocation_size;
|
|
|
|
return page.buffer[0 .. allocation_size];
|
|
}
|
|
|
|
var page = self.current_page() orelse unreachable;
|
|
|
|
if (page.available() <= aligned_allocation_size) {
|
|
page = try self.allocate_page(math.max(self.min_page_size, aligned_allocation_size));
|
|
}
|
|
|
|
debug.assert(page.available() >= allocation_size);
|
|
|
|
defer page.used += aligned_allocation_size;
|
|
|
|
return page.buffer[page.used .. (page.used + allocation_size)];
|
|
}
|
|
|
|
fn allocate_page(self: *Stacking, page_size: usize) io.AllocationError!*Page {
|
|
var buffer = try io.allocate_many(self.base_allocator, page_size, u8);
|
|
|
|
errdefer io.deallocate(self.base_allocator, buffer);
|
|
|
|
try self.pages.push_one(self.base_allocator, .{
|
|
.buffer = buffer,
|
|
.used = 0,
|
|
});
|
|
|
|
return (self.current_page() orelse unreachable);
|
|
}
|
|
|
|
pub fn as_allocator(self: *Stacking) io.Allocator {
|
|
return io.Allocator.bind(Stacking, self, struct {
|
|
fn reallocate(stacking: *Stacking, options: io.AllocationOptions) ?[]u8 {
|
|
const allocation = options.allocation orelse {
|
|
return stacking.allocate(options.size) catch null;
|
|
};
|
|
|
|
if (allocation.len == 0) {
|
|
return null;
|
|
}
|
|
|
|
const reallocation = stacking.allocate(allocation.len) catch {
|
|
return null;
|
|
};
|
|
|
|
io.copy(reallocation, allocation);
|
|
|
|
return reallocation;
|
|
}
|
|
}.reallocate);
|
|
}
|
|
|
|
pub fn clear_allocations(self: *Stacking) void {
|
|
for (self.pages.values) |page| {
|
|
io.deallocate(self.base_allocator, page.buffer);
|
|
}
|
|
|
|
self.pages.deinit(self.base_allocator);
|
|
self.allocations.deinit(self.base_allocator);
|
|
}
|
|
|
|
fn current_page(self: Stacking) ?*Page {
|
|
if (self.pages.values.len == 0) {
|
|
return null;
|
|
}
|
|
|
|
return &self.pages.values[self.pages.values.len - 1];
|
|
}
|
|
};
|