const debug = @import("./debug.zig"); const io = @import("./io.zig"); const list = @import("./list.zig"); const math = @import("./math.zig"); pub const Stacking = struct { allocator: io.Allocator, min_region_size: usize, head_region: ?*Region, tail_region: ?*Region, const Region = struct { next: ?*Region, count: usize, capacity: usize, fn allocate(self: *Region, region_size: usize) []usize { debug.assert(self.can_allocate(region_size)); const offset = (@sizeOf(Region) / @alignOf(usize)) + self.count; const allocation = @as([*]usize, @ptrCast(self))[offset .. (offset + region_size)]; self.count += region_size; return allocation; } fn as_raw(self: *Region) []usize { return @as([*]usize, @ptrCast(self))[0 .. ((@sizeOf(Region) / @alignOf(usize)) + self.capacity)]; } fn can_allocate(self: Region, region_size: usize) bool { return (self.count + region_size) <= self.capacity; } }; fn allocate_region(self: *Stacking, requested_region_size: usize) io.AllocationError!*Region { const region_size = @max(requested_region_size, self.min_region_size); const region = @as(*Region, @ptrCast(@alignCast(try self.allocator.reallocate(null, @alignOf(Region) + (@alignOf(usize) * region_size))))); region.* = .{ .next = null, .count = 0, .capacity = region_size, }; return region; } pub fn as_allocator(self: *Stacking) io.Allocator { return io.Allocator.bind(Stacking, self, .{ .deallocate = deallocate, .reallocate = reallocate, }); } pub fn deinit(self: *Stacking) void { while (self.head_region) |region| { const next_region = region.next; self.allocator.deallocate(region.as_raw()); self.head_region = next_region; } self.head_region = null; self.tail_region = null; } fn deallocate(_: *Stacking, _: []io.Byte) void { // TODO: Decide how to implement. unreachable; } pub fn init(allocator: io.Allocator, min_region_size: usize) Stacking { return Stacking{ .allocator = allocator, .min_region_size = min_region_size, .head_region = null, .tail_region = null, }; } fn reallocate(self: *Stacking, _: usize, allocation: ?[]io.Byte, byte_size: usize) io.AllocationError![]io.Byte { if (allocation) |buffer| { if (byte_size < buffer.len) { return buffer[0 .. byte_size]; } } const region_size = (byte_size + @sizeOf(usize) - 1) / @sizeOf(usize); const tail_region = self.tail_region orelse { const region = try self.allocate_region(region_size); self.tail_region = region; self.head_region = self.tail_region; return @as([*]io.Byte, @ptrCast(region.allocate(region_size)))[0 .. byte_size]; }; if (!tail_region.can_allocate(region_size)) { const region = try self.allocate_region(region_size); tail_region.next = region; self.tail_region = region; return @as([*]io.Byte, @ptrCast(region.allocate(region_size)))[0 .. byte_size]; } return @as([*]io.Byte, @ptrCast(tail_region.allocate(region_size)))[0 .. byte_size]; } };