235 lines
6.4 KiB
Zig
Executable File
235 lines
6.4 KiB
Zig
Executable File
const debug = @import("./debug.zig");
|
|
|
|
const math = @import("./math.zig");
|
|
|
|
pub const AllocationError = error {
|
|
OutOfMemory,
|
|
};
|
|
|
|
pub const AllocationOptions = struct {
|
|
allocation: ?[]u8 = null,
|
|
size: usize,
|
|
};
|
|
|
|
pub const Allocator = Functor(?[]u8, AllocationOptions);
|
|
|
|
pub fn Functor(comptime Output: type, comptime Input: type) type {
|
|
return struct {
|
|
context: *anyopaque,
|
|
invoker: *const fn (capture: *anyopaque, input: Input) Output,
|
|
|
|
const Self = @This();
|
|
|
|
pub fn bind(state: anytype, comptime invoker: fn (capture: @TypeOf(state), input: Input) Output) Self {
|
|
const State = @TypeOf(state);
|
|
const state_info = @typeInfo(State);
|
|
|
|
if (state_info != .Pointer) {
|
|
@compileError("`@typeOf(state)` must be a pointer type");
|
|
}
|
|
|
|
return .{
|
|
.context = state,
|
|
|
|
.invoker = struct {
|
|
fn invoke_opaque(context: *anyopaque, input: Input) Output {
|
|
return invoker(@ptrCast(State, @alignCast(@alignOf(state_info.Pointer.child), context)), input);
|
|
}
|
|
}.invoke_opaque,
|
|
};
|
|
}
|
|
|
|
pub fn invoke(self: Self, input: Input) Output {
|
|
return self.invoker(self.context, input);
|
|
}
|
|
};
|
|
}
|
|
|
|
pub const ReadError = error {
|
|
IoUnavailable,
|
|
};
|
|
|
|
pub const Reader = Functor(ReadError!usize, []u8);
|
|
|
|
pub fn Tag(comptime Element: type) type {
|
|
return switch (@typeInfo(Element)) {
|
|
.Enum => |info| info.tag_type,
|
|
.Union => |info| info.tag_type orelse @compileError(@typeName(Element) ++ " has no tag type"),
|
|
else => @compileError("expected enum or union type, found '" ++ @typeName(Element) ++ "'"),
|
|
};
|
|
}
|
|
|
|
pub const WriteError = error{
|
|
IoUnavailable,
|
|
};
|
|
|
|
pub const Writer = Functor(WriteError!usize, []const u8);
|
|
|
|
pub fn allocate_many(comptime Type: type, amount: usize, allocator: Allocator) AllocationError![]Type {
|
|
if (@sizeOf(Type) == 0) {
|
|
@compileError("Cannot allocate memory for 0-byte type " ++ @typeName(Type));
|
|
}
|
|
|
|
if (amount == 0) {
|
|
return &.{};
|
|
}
|
|
|
|
return @ptrCast([*]Type, @alignCast(@alignOf(Type), allocator.invoke(.{.size = @sizeOf(Type) * amount}) orelse {
|
|
return error.OutOfMemory;
|
|
}))[0 .. amount];
|
|
}
|
|
|
|
pub fn allocate_one(comptime Type: type, allocator: Allocator) AllocationError!*Type {
|
|
if (@sizeOf(Type) == 0) {
|
|
@compileError("Cannot allocate memory for 0-byte type " ++ @typeName(Type));
|
|
}
|
|
|
|
return @ptrCast(*Type, @alignCast(@alignOf(Type), allocator.invoke(.{.size = @sizeOf(Type)}) orelse {
|
|
return error.OutOfMemory;
|
|
}));
|
|
}
|
|
|
|
pub fn bytes_of(value: anytype) []const u8 {
|
|
const pointer_info = @typeInfo(@TypeOf(value)).Pointer;
|
|
|
|
debug.assert(pointer_info.size == .One);
|
|
|
|
return @ptrCast([*]const u8, value)[0 .. @sizeOf(pointer_info.child)];
|
|
}
|
|
|
|
pub fn compare(this: []const u8, that: []const u8) isize {
|
|
const range = math.min(usize, this.len, that.len);
|
|
var index: usize = 0;
|
|
|
|
while (index < range) : (index += 1) {
|
|
const difference = @intCast(isize, this[index]) - @intCast(isize, that[index]);
|
|
|
|
if (difference != 0) return difference;
|
|
}
|
|
|
|
return @intCast(isize, this.len) - @intCast(isize, that.len);
|
|
}
|
|
|
|
pub fn deallocate(allocator: Allocator, allocation: anytype) void {
|
|
const Element = @TypeOf(allocation);
|
|
|
|
switch (@typeInfo(Element).Pointer.size) {
|
|
.One => {
|
|
debug.assert(allocator.invoke(.{
|
|
.allocation = @ptrCast([*]u8, allocation)[0 .. @sizeOf(Element)],
|
|
.size = 0
|
|
}) == null);
|
|
},
|
|
|
|
.Slice => {
|
|
debug.assert(allocator.invoke(.{
|
|
.allocation = @ptrCast([*]u8, allocation.ptr)[0 .. (@sizeOf(Element) * allocation.len)],
|
|
.size = 0
|
|
}) == null);
|
|
},
|
|
|
|
.Many, .C => @compileError("length of allocation must be known to deallocate"),
|
|
}
|
|
}
|
|
|
|
pub fn copy(target: []u8, source: []const u8) void {
|
|
var index: usize = 0;
|
|
|
|
while (index < source.len) : (index += 1) target[index] = source[index];
|
|
}
|
|
|
|
pub fn ends_with(target: []const u8, match: []const u8) bool {
|
|
if (target.len < match.len) return false;
|
|
|
|
var index = @as(usize, 0);
|
|
|
|
while (index < match.len) : (index += 1) {
|
|
if (target[target.len - (1 + index)] != match[match.len - (1 + index)]) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
pub fn equals(this: []const u8, that: []const u8) bool {
|
|
if (this.len != that.len) return false;
|
|
|
|
{
|
|
var index: usize = 0;
|
|
|
|
while (index < this.len) : (index += 1) if (this[index] != that[index]) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
var null_context = @as(usize, 0);
|
|
|
|
pub const null_writer = Writer.bind(&null_context, struct {
|
|
pub fn write(context: *usize, buffer: []const u8) usize {
|
|
debug.assert(context.* == 0);
|
|
|
|
return buffer.len;
|
|
}
|
|
});
|
|
|
|
pub fn overlaps(pointer: [*]u8, memory_range: []u8) bool {
|
|
return (pointer >= memory_range.ptr) and (pointer < (memory_range.ptr + memory_range.len));
|
|
}
|
|
|
|
pub fn bytes_to(comptime Type: type, source_bytes: []const u8) ?Type {
|
|
const type_size = @sizeOf(Type);
|
|
|
|
if (source_bytes.len != type_size) return null;
|
|
|
|
var target_bytes = @as([type_size]u8, undefined);
|
|
|
|
copy(&target_bytes, source_bytes);
|
|
|
|
return @bitCast(Type, target_bytes);
|
|
}
|
|
|
|
pub fn reallocate(allocator: Allocator, allocation: anytype, amount: usize) AllocationError![]@typeInfo(@TypeOf(allocation)).Pointer.child {
|
|
const pointer_info = @typeInfo(@TypeOf(allocation)).Pointer;
|
|
const Element = pointer_info.child;
|
|
|
|
return @ptrCast([*]Element, @alignCast(@alignOf(Element), (allocator.invoke(switch (pointer_info.size) {
|
|
.Slice => .{
|
|
.allocation = @ptrCast([*]u8, allocation.ptr)[0 .. (@sizeOf(Element) * allocation.len)],
|
|
.size = @sizeOf(Element) * amount,
|
|
},
|
|
|
|
.Many, .C, .One => @compileError("allocation must be a slice to reallocate"),
|
|
}) orelse return error.OutOfMemory).ptr))[0 .. amount];
|
|
}
|
|
|
|
pub fn slice_sentineled(comptime element: type, comptime sentinel: element, sequence: [*:sentinel]const element) []const element {
|
|
var length: usize = 0;
|
|
|
|
while (sequence[length] != sentinel) : (length += 1) {}
|
|
|
|
return sequence[0..length];
|
|
}
|
|
|
|
pub fn stream(output: Writer, input: Reader, buffer: []u8) (ReadError || WriteError)!u64 {
|
|
var total_written: u64 = 0;
|
|
var read = try input.invoke(buffer);
|
|
|
|
while (read != 0) {
|
|
total_written += try output.invoke(buffer[0..read]);
|
|
read = try input.invoke(buffer);
|
|
}
|
|
|
|
return total_written;
|
|
}
|
|
|
|
pub fn swap(comptime Element: type, this: *Element, that: *Element) void {
|
|
const temp = this.*;
|
|
|
|
this.* = that.*;
|
|
that.* = temp;
|
|
}
|
|
|
|
pub fn zero(target: []u8) void {
|
|
for (target) |*t| t.* = 0;
|
|
}
|