const debug = @import("./debug.zig"); const math = @import("./math.zig"); pub const MemoryArena = struct { pub fn as_allocator(self: *MemoryArena) MemoryAllocator { return MemoryAllocator.bind(self, MemoryArena); } }; pub const MemoryAllocator = struct { context: *anyopaque, call: *const fn (capture: *anyopaque, maybe_allocation: ?[*]u8, size: usize) ?[*]u8, const Capture = [@sizeOf(usize)]u8; pub fn bind(state: anytype, comptime Actions: type) MemoryAllocator { const State = @TypeOf(state); const state_info = @typeInfo(State); if (state_info != .Pointer) @compileError("`@typeOf(state)` must be a pointer type"); return .{ .context = state, .call = struct { fn reallocate(context: *anyopaque, maybe_allocation: ?[*]u8, size: usize) ?[*]u8 { return Actions.reallocate(@ptrCast(State, @alignCast(@alignOf(state_info.Pointer.child), context)), maybe_allocation, size); } }.reallocate, }; } pub fn allocate_many(self: MemoryAllocator, comptime Type: type, amount: usize) ?[*]Type { return @ptrCast(?[*]Type, @alignCast(@alignOf(Type), self.call(self.context, null, @sizeOf(Type) * amount))); } pub fn allocate_one(self: MemoryAllocator, comptime Type: type) ?*Type { return @ptrCast(?*Type, @alignCast(@alignOf(Type), self.call(self.context, null, @sizeOf(Type)))); } pub fn deallocate(self: MemoryAllocator, maybe_allocation: anytype) void { if (@typeInfo(@TypeOf(maybe_allocation)) != .Pointer) @compileError("`maybe_allocation` must be a pointer type"); debug.assert(self.call(self.context, @ptrCast(?[*]u8, maybe_allocation), 0) == null); } pub fn reallocate(self: MemoryAllocator, comptime Type: type, maybe_allocation: ?[*]Type, amount: usize) ?[*]Type { return @ptrCast(?[*]Type, @alignCast(@alignOf(Type), self.call(self.context, @ptrCast(?[*]u8, maybe_allocation), @sizeOf(Type) * amount))); } }; pub const ReadError = error{ IoUnavailable, }; pub const Reader = struct { context: *anyopaque, call: *const fn (context: *anyopaque, buffer: []u8) ReadError!usize, pub fn bind(state: anytype, comptime Actions: type) Reader { const State = @TypeOf(state); const state_info = @typeInfo(State); if (@typeInfo(State) != .Pointer) @compileError("`@typeOf(state)` must be a pointer type"); return .{ .context = @ptrCast(*anyopaque, state), .call = struct { fn read(context: *anyopaque, buffer: []u8) ReadError!usize { return Actions.read(@ptrCast(State, @alignCast(@alignOf(state_info.Pointer.child), context)), buffer); } }.read, }; } pub fn read(self: Reader, buffer: []u8) ReadError!usize { return self.call(self.context, buffer); } }; pub const WriteError = error{ IoUnavailable, }; pub const Writer = struct { context: *anyopaque, call: *const fn (context: *anyopaque, buffer: []const u8) WriteError!usize, pub fn bind(state: anytype, comptime Actions: type) Writer { const State = @TypeOf(state); const state_info = @typeInfo(State); if (state_info != .Pointer) @compileError("`@typeOf(state)` must be a pointer type"); return .{ .context = @ptrCast(*anyopaque, state), .call = struct { fn write(context: *anyopaque, buffer: []const u8) ReadError!usize { return Actions.write(@ptrCast(State, @alignCast(@alignOf(state_info.Pointer.child), context)), buffer); } }.write, }; } pub fn write(self: Writer, buffer: []const u8) WriteError!usize { return self.call(self.context, buffer); } }; 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 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 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.read(buffer); while (read != 0) { total_written += try output.write(buffer[0..read]); read = try input.read(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; }