diff --git a/src/core/io.zig b/src/core/io.zig index 48dee34..11c3fa1 100644 --- a/src/core/io.zig +++ b/src/core/io.zig @@ -7,102 +7,6 @@ const std = @import("std"); /// pub const Allocator = std.mem.Allocator; -/// -/// File-system agnostic abstraction for manipulating a file. -/// -pub const FileAccess = struct { - context: *anyopaque, - implementation: *const Implementation, - - /// - /// Provides a set of implementation-specific behaviors to a [FileAccess] instance. - /// - pub const Implementation = struct { - close: fn (*anyopaque) void, - queryCursor: fn (*anyopaque) Error!u64, - queryLength: fn (*anyopaque) Error!u64, - read: fn (*anyopaque, []u8) Error!usize, - seek: fn (*anyopaque, u64) Error!void, - seekToEnd: fn (*anyopaque) Error!void, - skip: fn (*anyopaque, i64) Error!void, - }; - - /// - /// [Error.FileInaccessible] is a generic catch-all for a [FileAccess] reference no longer - /// pointing to a file or the file becomming invalid for whatever reason. - /// - pub const Error = error { - FileInaccessible, - }; - - /// - /// Close the file referenced by `file_access` on the main thread, invalidating the reference to - /// it and releasing any associated resources. - /// - /// Freeing an invalid `file_access` has no effect on the file and logs a warning over the - /// wasted effort. - /// - pub fn close(file_access: FileAccess) void { - return file_access.implementation.close(file_access.context); - } - - /// - /// Attempts to query the current cursor position for the file referenced by `file_access`. - /// - /// Returns the number of bytes into the file that the cursor is relative to its beginning or a - /// [Error] on failure. - /// - pub fn queryCursor(file_access: FileAccess) Error!u64 { - return file_access.implementation.queryCursor(file_access.context); - } - - /// - /// Attempts to query the current length for the file referenced by `file_access`. - /// - /// Returns the current length of the file at the time of the operation or a [Error] if the file - /// failed to be queried. - /// - pub fn queryLength(file_access: FileAccess) Error!u64 { - return file_access.implementation.queryLength(file_access.context); - } - - /// - /// Attempts to read `file_access` from the its current position into `buffer`. - /// - /// Returns the number of bytes that were available to be read, otherwise an [Error] on failure. - /// - pub fn read(file_access: FileAccess, buffer: []u8) Error!usize { - return file_access.implementation.read(file_access.context, buffer); - } - - /// - /// Attempts to seek `file_access` from the beginning of the file to `cursor` bytes. - /// - /// Returns [Error] on failure. - /// - pub fn seek(file_access: FileAccess, cursor: u64) Error!void { - return file_access.implementation.seek(file_access.context, cursor); - } - - /// - /// Attempts to seek `file_access` to the end of the file. - /// - /// Returns [Error] on failure. - /// - pub fn seekToEnd(file_access: FileAccess) Error!void { - return file_access.implementation.seekToEnd(file_access.context); - } - - /// - /// Attempts to seek `file_access` by `offset` from the current file position. - /// - /// Returns [Error] on failure; - /// - pub fn skip(file_access: FileAccess, offset: i64) Error!void { - return file_access.implementation.skip(file_access.context, offset); - } -}; - /// /// Closure that captures a reference to readable resources like block devices, memory buffers, /// network sockets, and more. @@ -161,7 +65,7 @@ test "Spliterating text" { var index = @as(usize, 0); while (spliterator.next()) |split| : (index += 1) { - try testing.expect(std.mem.eql(u8, split, components[index])); + try testing.expect(equals(u8, split, components[index])); } } @@ -176,7 +80,7 @@ test "Spliterating text" { var index = @as(usize, 0); while (spliterator.next()) |split| : (index += 1) { - try testing.expect(std.mem.eql(u8, split, components[index])); + try testing.expect(equals(u8, split, components[index])); } } } @@ -209,7 +113,7 @@ test "Check memory begins with" { /// /// **Note** that passing a slice will convert it to a byte slice. /// -pub fn bytes(pointer: anytype) switch (@typeInfo(@TypeOf(pointer))) { +pub fn bytesOf(pointer: anytype) switch (@typeInfo(@TypeOf(pointer))) { .Pointer => |info| if (info.is_const) []const u8 else []u8, else => @compileError("`pointer` must be a pointer type"), } { @@ -233,7 +137,35 @@ test "Bytes of types" { var foo: u32 = 10; - testing.expectEqual(bytes(&foo), 0x0a); + testing.expectEqual(bytesOf(&foo), 0x0a); +} + +/// +/// Compares `this` to `that`, returning the difference between the first byte deviation in the two +/// sequences, otherwise `0` if they are identical. +/// +pub fn compareBytes(this: []const u8, that: []const u8) isize { + var cursor: usize = 0; + + while (cursor != this.len) : (cursor += 1) { + const this_byte = this[cursor]; + + if (cursor != that.len) return this_byte; + + const that_byte = that[cursor]; + + if (this_byte != that_byte) return (this_byte - that_byte); + } + + return 0; +} + +test "Compare bytes" { + const testing = std.testing; + + try testing.expectEquals(compareBytes(&.{69, 42, 0}, &.{69, 42, 0}), 0); + try testing.expectEquals(compareBytes(&.{69, 42, 0}, &.{69, 42}), 42); + try testing.expectEquals(compareBytes(&.{69, 42}, &.{69, 42, 0}), -42); } /// @@ -243,11 +175,9 @@ test "Bytes of types" { pub fn equals(comptime Element: type, this: []const Element, that: []const Element) bool { if (this.len != that.len) return false; - { - var i = std.mem.zeroes(usize); + var i = std.mem.zeroes(usize); - while (i < this.len) : (i += 1) if (this[i] != that[i]) return false; - } + while (i < this.len) : (i += 1) if (this[i] != that[i]) return false; return true; }