const io = @import("./io.zig"); const std = @import("std"); /// /// Potential errors that may occur while trying to push one or more elements into a stack of a /// known maximum size. /// /// [FinitePushError.Overflow] is returned if the stack does not have sufficient capacity to hold a /// given set of elements. /// pub const FinitePushError = error { Overflow, }; /// /// Returns a fixed-size stack collection capable of holding a maximum of `capacity` elements of /// type `Element`. /// pub fn Fixed(comptime Element: type, comptime capacity: usize) type { return struct { filled: usize, buffer: [capacity]Element, const Self = @This(); /// /// Wraps `self` and returns it in a [io.Writer] value. /// /// Note that this will raise a compilation error if [Element] is not `u8`. /// pub fn asWriter(self: *Self) io.Writer { if (Element != u8) @compileError("Cannot coerce fixed stack of type " ++ @typeName(Element) ++ " into a Writer"); return io.Writer.wrap(Self, self, struct { fn write(stack: *Self, buffer: []const u8) usize { stack.pushAll(buffer) catch |err| switch (err) { error.Overflow => return 0, }; return buffer.len; } }.write); } /// /// Clears all elements from `self`. /// pub fn clear(self: *Self) void { self.filled = 0; } /// /// Counts and returns the number of pushed elements in `self`. /// pub fn count(self: Self) usize { return self.filled; } /// /// Attempts to pop the tail-end of `self`, returning the element value or `null` if the /// stack is empty. /// pub fn pop(self: *Self) ?Element { if (self.filled == 0) return null; self.filled -= 1; return self.buffer[self.filled]; } /// /// Attempts to push `element` into `self`, returning [FinitePushError.Overflow] if the /// stack is full. /// pub fn push(self: *Self, element: Element) FinitePushError!void { if (self.filled == capacity) return error.Overflow; self.buffer[self.filled] = element; self.filled += 1; } /// /// Attempts to push all of `elements` into `self`, returning [FinitePushError.Overflow] if /// the stack does not have sufficient capacity to hold the new elements. /// pub fn pushAll(self: *Self, elements: []const u8) FinitePushError!void { const filled = (self.filled + elements.len); if (filled > capacity) return error.Overflow; std.mem.copy(u8, self.buffer[self.filled ..], elements); self.filled = filled; } }; } pub fn Unmanaged(comptime Element: type) type { return struct { filled: usize, buffer: []Element, const Self = @This(); /// /// Wraps `self` and returns it in a [io.Writer] value. /// /// Note that this will raise a compilation error if [Element] is not `u8`. /// pub fn asWriter(self: *Self) io.Writer { if (Element != u8) @compileError("Cannot coerce fixed stack of type " ++ @typeName(Element) ++ " into a Writer"); return io.Writer.wrap(Self, self, struct { fn write(stack: *Self, buffer: []const u8) usize { stack.pushAll(buffer) catch |err| switch (err) { error.Overflow => return 0, }; return buffer.len; } }.write); } /// /// Clears all elements from `self`. /// pub fn clear(self: *Self) void { self.filled = 0; } /// /// Counts and returns the number of pushed elements in `self`. /// pub fn count(self: Self) usize { return self.filled; } /// /// Attempts to pop the tail-end of `self`, returning the element value or `null` if the /// stack is empty. /// pub fn pop(self: *Self) ?Element { if (self.filled == 0) return null; self.filled -= 1; return self.buffer[self.filled]; } /// /// Attempts to push `element` into `self`, returning [FinitePushError.Overflow] if the /// stack is full. /// pub fn push(self: *Self, element: Element) FinitePushError!void { if (self.filled == self.buffer.len) return error.Overflow; self.buffer[self.filled] = element; self.filled += 1; } /// /// Attempts to push all of `elements` into `self`, returning [FinitePushError.Overflow] if /// the stack does not have sufficient capacity to hold the new elements. /// pub fn pushAll(self: *Self, elements: []const u8) FinitePushError!void { const filled = (self.filled + elements.len); if (filled > self.buffer.len) return error.Overflow; std.mem.copy(u8, self.buffer[self.filled ..], elements); self.filled = filled; } }; } test "fixed stack" { const testing = @import("std").testing; const expectError = testing.expectError; const expectEqual = testing.expectEqual; var stack = Fixed(u8, 4){}; try expectEqual(stack.count(), 0); try expectEqual(stack.pop(), null); try stack.push(69); try expectEqual(stack.count(), 1); try expectEqual(stack.pop(), 69); try stack.pushAll(&.{42, 10, 95, 0}); try expectEqual(stack.count(), 4); try expectError(FinitePushError.Overflow, stack.push(1)); try expectError(FinitePushError.Overflow, stack.pushAll(&.{1, 11, 11})); stack.clear(); try expectEqual(stack.count(), 0); const writer = stack.asWriter(); try expectEqual(writer.write(&.{0, 0, 0, 0}), 4); try expectEqual(writer.writeByte(0), false); }