const io = @import("./io.zig"); const math = @import("./math.zig"); pub const ByteStack = Stack(io.Byte); pub fn Stack(comptime Value: type) type { return struct { allocator: io.Allocator, capacity: usize, values: []Value, const Self = @This(); pub fn clear(self: *Self) void { self.values = self.values[0 .. 0]; } pub fn deinit(self: *Self) void { if (self.capacity == 0) { return; } self.allocator.deallocate(self.values.ptr[0 .. self.capacity]); self.values = &.{}; } pub fn drop(self: *Self, count: usize) bool { if (math.checked_sub(self.values.len, count)) |updated_count| { self.values = self.values[0 .. updated_count]; return true; } return false; } pub fn grow(self: *Self, growth_amount: usize) io.AllocationError!void { const grown_capacity = self.capacity + growth_amount; const buffer = try self.allocator.reallocate(null, @sizeOf(Value) * grown_capacity); errdefer self.allocator.deallocate(buffer); if (self.capacity != 0) { io.copy(buffer, io.bytes_of(self.values)); self.allocator.deallocate(self.values.ptr[0 .. self.capacity]); } self.values = @as([*]Value, @ptrCast(@alignCast(buffer)))[0 .. self.values.len]; self.capacity = grown_capacity; } pub fn init(allocator: io.Allocator) Self { return .{ .allocator = allocator, .capacity = 0, .values = &.{}, }; } pub fn is_empty(self: Self) bool { return self.values.len == 0; } pub fn pack(self: *Self) io.AllocationError![]Value { const packed_size = self.values.len; const buffer = try self.allocator.reallocate(null, @sizeOf(Value) * self.values.len); io.copy(buffer, io.bytes_of(self.values)); if (self.capacity != 0) { self.allocator.deallocate(self.values.ptr[0 .. self.capacity]); } self.values = @as([*]Value, @ptrCast(@alignCast(buffer)))[0 .. packed_size]; self.capacity = packed_size; return self.values; } pub fn peek(self: Self) ?Value { if (self.values.len == 0) { return null; } return self.values[self.values.len - 1]; } pub fn pop(self: *Self) ?Value { if (self.values.len == 0) { return null; } const last_index = self.values.len - 1; defer self.values = self.values[0 .. last_index]; return self.values[last_index]; } pub fn push_all(self: *Self, values: []const Value) io.AllocationError!void { const new_length = self.values.len + values.len; if (new_length > self.capacity) { try self.grow(values.len + values.len); } const offset_index = self.values.len; self.values = self.values.ptr[0 .. new_length]; for (0 .. values.len) |index| { self.values[offset_index + index] = values[index]; } } pub fn push_one(self: *Self, value: Value) io.AllocationError!void { if (self.values.len == self.capacity) { try self.grow(@max(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 stack_as_writer(self: *ByteStack) io.Writer { return io.Writer.bind(ByteStack, self, write_stack); } fn write_stack(stack: *ByteStack, bytes: []const io.Byte) ?usize { stack.push_all(bytes) catch return null; return bytes.len; }