204 lines
6.1 KiB
Zig
Executable File
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);
|
|
}
|