const debug = @import("./debug.zig"); const io = @import("./io.zig"); const math = @import("./math.zig"); pub fn Dense(comptime Element: type) type { return struct { capacity: usize = 0, values: []Element = &.{}, const Self = @This(); pub fn clear(self: *Self) void { self.values = self.values[0 .. 0]; } pub fn deinit(self: *Self, allocator: io.Allocator) void { io.deallocate(allocator, self.values); } 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, allocator: io.Allocator, growth_amount: usize) io.AllocationError!void { const grown_capacity = self.capacity + growth_amount; self.values = (try io.reallocate(allocator, self.values, grown_capacity))[0 .. self.values.len]; self.capacity = grown_capacity; } pub fn push_all(self: *Self, allocator: io.Allocator, values: []const Element) io.AllocationError!void { const new_length = self.values.len + values.len; if (new_length >= self.capacity) { try self.grow(allocator, 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, allocator: io.Allocator, value: Element, amount: usize) io.AllocationError!void { const new_length = self.values.len + amount; if (new_length >= self.capacity) { try self.grow(allocator, math.max(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, allocator: io.MemoryAllocator, value: Element) io.AllocationError!void { if (self.values.len == self.capacity) { try self.grow(allocator, math.max(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 fn as_dense_allocator(stack: *Dense(u8)) io.Allocator { return io.Allocator.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; } }); }