133 lines
3.6 KiB
Zig
133 lines
3.6 KiB
Zig
const debug = @import("./debug.zig");
|
|
|
|
const io = @import("./io.zig");
|
|
|
|
const math = @import("./math.zig");
|
|
|
|
pub fn Dense(comptime Element: type) type {
|
|
return struct {
|
|
allocator: io.MemoryAllocator,
|
|
capacity: usize,
|
|
values: []Element,
|
|
|
|
const Self = @This();
|
|
|
|
pub fn clear(self: *Self) void {
|
|
self.values = self.values[0 .. 0];
|
|
}
|
|
|
|
pub fn deinit(self: *Self) void {
|
|
self.allocator.deallocate(self.values.ptr);
|
|
}
|
|
|
|
pub fn drop(self: *Self, amount: usize) bool {
|
|
if (amount > self.values.len) return false;
|
|
|
|
self.values = self.values[0 .. self.values.len - amount];
|
|
|
|
return true;
|
|
}
|
|
|
|
pub fn grow(self: *Self, growth_amount: usize) GrowError!void {
|
|
const grown_capacity = self.capacity + growth_amount;
|
|
|
|
self.values = (self.allocator.reallocate(Element, self.values.ptr,
|
|
grown_capacity) orelse return error.OutOfMemory)[0 .. self.values.len];
|
|
|
|
self.capacity = grown_capacity;
|
|
}
|
|
|
|
pub fn init(allocator: io.MemoryAllocator, initial_capacity: usize) !Self {
|
|
return Self{
|
|
.values = (allocator.allocate_many(Element, initial_capacity) orelse return error.OutOfMemory)[0 .. 0],
|
|
.allocator = allocator,
|
|
.capacity = initial_capacity,
|
|
};
|
|
}
|
|
|
|
pub fn push_all(self: *Self, values: []const Element) GrowError!void {
|
|
const new_length = self.values.len + values.len;
|
|
|
|
if (new_length >= self.capacity) try self.grow(math.min(usize, new_length, self.capacity));
|
|
|
|
const offset_index = self.values.len;
|
|
|
|
self.values = self.values.ptr[0 .. new_length];
|
|
|
|
{
|
|
var index: usize = 0;
|
|
|
|
while (index < values.len) : (index += 1) self.values[offset_index + index] = values[index];
|
|
}
|
|
}
|
|
|
|
pub fn push_many(self: *Self, value: Element, amount: usize) GrowError!void {
|
|
const new_length = self.values.len + amount;
|
|
|
|
if (new_length >= self.capacity) try self.grow(math.min(usize, new_length, self.capacity));
|
|
|
|
const offset_index = self.values.len;
|
|
|
|
self.values = self.values.ptr[0 .. new_length];
|
|
|
|
{
|
|
var index: usize = 0;
|
|
|
|
while (index < amount) : (index += 1) self.values[offset_index + index] = value;
|
|
}
|
|
}
|
|
|
|
pub fn push_one(self: *Self, value: Element) GrowError!void {
|
|
if (self.values.len == self.capacity) try self.grow(math.min(usize, 1, self.capacity));
|
|
|
|
const offset_index = self.values.len;
|
|
|
|
self.values = self.values.ptr[0 .. self.values.len + 1];
|
|
|
|
self.values[offset_index] = value;
|
|
}
|
|
};
|
|
}
|
|
|
|
pub const GrowError = error {
|
|
OutOfMemory
|
|
};
|
|
|
|
pub fn as_dense_allocator(stack: *Dense(u8)) io.MemoryAllocator {
|
|
return io.MemoryAllocator.bind(stack, struct {
|
|
pub fn reallocate(writable_stack: *Dense(u8), existing_allocation: ?[*]u8, allocation_size: usize) ?[*]u8 {
|
|
if (allocation_size == 0) return null;
|
|
|
|
writable_stack.push_all(io.bytes_of(&allocation_size)) catch return null;
|
|
|
|
const usize_size = @sizeOf(usize);
|
|
|
|
errdefer debug.assert(writable_stack.drop(usize_size));
|
|
|
|
const allocation_index = writable_stack.values.len;
|
|
|
|
if (existing_allocation) |allocation| {
|
|
const existing_allocation_size = @intToPtr(*const usize, @ptrToInt(allocation) - usize_size).*;
|
|
|
|
writable_stack.push_all(allocation[0 .. existing_allocation_size]) catch return null;
|
|
} else {
|
|
writable_stack.push_many(0, allocation_size) catch return null;
|
|
}
|
|
|
|
return @ptrCast([*]u8, &writable_stack.values[allocation_index]);
|
|
}
|
|
});
|
|
}
|
|
|
|
pub fn as_dense_writer(stack: *Dense(u8)) io.Writer {
|
|
return io.Writer.bind(stack, struct {
|
|
pub fn write(writable_stack: *Dense(u8), buffer: []const u8) io.WriteError!usize {
|
|
writable_stack.push_all(buffer) catch |grow_error| switch (grow_error) {
|
|
error.OutOfMemory => return error.IoUnavailable,
|
|
};
|
|
|
|
return buffer.len;
|
|
}
|
|
});
|
|
}
|