ona/src/stack.zig

204 lines
6.1 KiB
Zig
Executable File

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);
}