Rewrite stacking arena allocator to be correct
This commit is contained in:
parent
523509d300
commit
2f4cab3cc7
|
@ -7,35 +7,47 @@ 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),
|
||||
allocator: io.Allocator,
|
||||
min_region_size: usize,
|
||||
head_region: ?*Region,
|
||||
tail_region: ?*Region,
|
||||
|
||||
const AllocationsList = list.Stack(usize);
|
||||
const Region = struct {
|
||||
next: ?*Region,
|
||||
count: usize,
|
||||
capacity: usize,
|
||||
|
||||
const Page = struct {
|
||||
buffer: []io.Byte,
|
||||
used: usize,
|
||||
fn allocate(self: *Region, region_size: usize) []usize {
|
||||
debug.assert(self.can_allocate(region_size));
|
||||
|
||||
fn available(self: Page) usize {
|
||||
return self.buffer.len - self.used;
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
const PageList = list.Stack(Page);
|
||||
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)))));
|
||||
|
||||
fn allocate_page(self: *Stacking, page_size: usize) io.AllocationError!*Page {
|
||||
var buffer = try self.page_allocator.reallocate(null, page_size);
|
||||
region.* = .{
|
||||
.next = null,
|
||||
.count = 0,
|
||||
.capacity = region_size,
|
||||
};
|
||||
|
||||
errdefer self.page_allocator.deallocate(buffer);
|
||||
|
||||
try self.pages.push_one(.{
|
||||
.buffer = buffer,
|
||||
.used = 0,
|
||||
});
|
||||
|
||||
return (self.current_page() orelse unreachable);
|
||||
return region;
|
||||
}
|
||||
|
||||
pub fn as_allocator(self: *Stacking) io.Allocator {
|
||||
|
@ -45,67 +57,60 @@ pub const Stacking = struct {
|
|||
});
|
||||
}
|
||||
|
||||
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);
|
||||
while (self.head_region) |region| {
|
||||
const next_region = region.next;
|
||||
|
||||
self.allocator.deallocate(region.as_raw());
|
||||
|
||||
self.head_region = next_region;
|
||||
}
|
||||
|
||||
self.pages.free();
|
||||
self.allocations.free();
|
||||
self.head_region = null;
|
||||
self.tail_region = null;
|
||||
}
|
||||
|
||||
pub fn deallocate(_: *Stacking, _: []io.Byte) void {
|
||||
fn deallocate(_: *Stacking, _: []io.Byte) void {
|
||||
// TODO: Decide how to implement.
|
||||
unreachable;
|
||||
}
|
||||
|
||||
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(@max(self.min_page_size, aligned_size));
|
||||
|
||||
page.used = size;
|
||||
|
||||
return page.buffer[0 .. size];
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
var page = self.current_page() orelse unreachable;
|
||||
const region_size = (byte_size + @sizeOf(usize) - 1) / @sizeOf(usize);
|
||||
|
||||
if (page.available() <= aligned_size) {
|
||||
page = try self.allocate_page(@max(self.min_page_size, aligned_size));
|
||||
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];
|
||||
}
|
||||
|
||||
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 @as([*]io.Byte, @ptrCast(tail_region.allocate(region_size)))[0 .. byte_size];
|
||||
}
|
||||
|
||||
return reallocation;
|
||||
}
|
||||
|
||||
pub fn make(allocator: io.Allocator, min_page_size: usize) Stacking {
|
||||
pub fn make(allocator: io.Allocator, min_region_size: usize) Stacking {
|
||||
return Stacking{
|
||||
.allocations = AllocationsList.make(allocator),
|
||||
.pages = PageList.make(allocator),
|
||||
.page_allocator = allocator,
|
||||
.min_page_size = min_page_size,
|
||||
.allocator = allocator,
|
||||
.min_region_size = min_region_size,
|
||||
.head_region = null,
|
||||
.tail_region = null,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue