Application Context Implementation #4
134
src/core/io.zig
134
src/core/io.zig
|
@ -7,102 +7,6 @@ const std = @import("std");
|
||||||
///
|
///
|
||||||
pub const Allocator = std.mem.Allocator;
|
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,
|
/// Closure that captures a reference to readable resources like block devices, memory buffers,
|
||||||
/// network sockets, and more.
|
/// network sockets, and more.
|
||||||
|
@ -161,7 +65,7 @@ test "Spliterating text" {
|
||||||
var index = @as(usize, 0);
|
var index = @as(usize, 0);
|
||||||
|
|
||||||
while (spliterator.next()) |split| : (index += 1) {
|
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);
|
var index = @as(usize, 0);
|
||||||
|
|
||||||
while (spliterator.next()) |split| : (index += 1) {
|
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.
|
/// **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,
|
.Pointer => |info| if (info.is_const) []const u8 else []u8,
|
||||||
else => @compileError("`pointer` must be a pointer type"),
|
else => @compileError("`pointer` must be a pointer type"),
|
||||||
} {
|
} {
|
||||||
|
@ -233,7 +137,35 @@ test "Bytes of types" {
|
||||||
|
|
||||||
var foo: u32 = 10;
|
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;
|
||||||
|
|
||||||
kayomn marked this conversation as resolved
Outdated
|
|||||||
|
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 {
|
pub fn equals(comptime Element: type, this: []const Element, that: []const Element) bool {
|
||||||
if (this.len != that.len) return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Comment needs clarify as it appears slightly misleading / ambiguous in its current wording.
On further thought, having the comment here to begin with only adds confusion.
While slices don't have as trivial of a memory layout as other pointer types, they're still language primitives and should reasonably be treated the same as other pointer kinds.