const debug = @import("./debug.zig"); const io = @import("./io.zig"); const list = @import("./list.zig"); const math = @import("./math.zig"); pub const Stacking = struct { page_allocator: io.Allocator, min_page_size: usize, allocations: list.Stack(usize), pages: list.Stack(Page), const AllocationsList = list.Stack(usize); const Page = struct { buffer: []io.Byte, used: usize, fn available(self: Page) usize { return self.buffer.len - self.used; } }; const PageList = list.Stack(Page); fn allocate_page(self: *Stacking, page_size: usize) io.AllocationError!*Page { var buffer = try self.page_allocator.reallocate(null, page_size); errdefer self.page_allocator.deallocate(buffer); try self.pages.push_one(.{ .buffer = buffer, .used = 0, }); return (self.current_page() orelse unreachable); } pub fn as_allocator(self: *Stacking) io.Allocator { return io.Allocator.bind(Stacking, self, .{ .deallocate = deallocate, .reallocate = reallocate, }); } fn current_page(self: Stacking) ?*Page { if (self.pages.values.len == 0) { return null; } return &self.pages.values[self.pages.values.len - 1]; } pub fn free(self: *Stacking) void { for (self.pages.values) |page| { self.page_allocator.deallocate(page.buffer); } self.pages.free(); self.allocations.free(); } pub fn deallocate(_: *Stacking, _: []io.Byte) void { // TODO: Decide how to implement. } pub fn reallocate(self: *Stacking, return_address: usize, existing_allocation: ?[]io.Byte, size: usize) io.AllocationError![]io.Byte { // TODO: Safety-check existing allocation is from allocator or null. _ = return_address; const alignment = @as(usize, 4); const aligned_size = (size + alignment - 1) & ~(alignment - 1); if (self.pages.values.len == 0) { const page = try self.allocate_page(math.max(self.min_page_size, aligned_size)); page.used = size; return page.buffer[0 .. size]; } var page = self.current_page() orelse unreachable; if (page.available() <= aligned_size) { page = try self.allocate_page(math.max(self.min_page_size, aligned_size)); } debug.assert(page.available() >= size); defer page.used += aligned_size; const reallocation = page.buffer[page.used .. (page.used + size)]; if (existing_allocation) |allocation| { io.copy(reallocation, allocation); } return reallocation; } pub fn make(allocator: io.Allocator, min_page_size: usize) Stacking { return Stacking{ .allocations = AllocationsList.make(allocator), .pages = PageList.make(allocator), .page_allocator = allocator, .min_page_size = min_page_size, }; } };