Application Context Implementation #4
|
@ -151,8 +151,6 @@ pub const Writer = meta.Function([]const u8, usize);
|
||||||
///
|
///
|
||||||
/// Returns a sliced reference of the raw bytes in `pointer`.
|
/// Returns a sliced reference of the raw bytes in `pointer`.
|
||||||
///
|
///
|
||||||
/// **Note** that passing a slice will convert it to a byte slice.
|
|
||||||
///
|
|
||||||
pub fn bytesOf(pointer: anytype) switch (@typeInfo(@TypeOf(pointer))) {
|
pub fn bytesOf(pointer: anytype) switch (@typeInfo(@TypeOf(pointer))) {
|
||||||
kayomn marked this conversation as resolved
Outdated
|
|||||||
.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"),
|
||||||
|
@ -254,8 +252,10 @@ test "fill" {
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Searches for the first instance of an `Element` equal to `needle` in `haystack`, returning its
|
/// Linearly searches for the first instance of an `Element` equal to `needle` in `haystack`,
|
||||||
/// index or `null` if nothing was found.
|
/// returning its index or `null` if nothing was found.
|
||||||
|
///
|
||||||
kayomn marked this conversation as resolved
Outdated
kayomn
commented
Worth mentioning that it performs linear time, making it O(n) time complexity? Worth mentioning that it performs linear time, making it O(n) time complexity?
|
|||||||
|
/// **Note** that this operation has `O(n)` time complexity.
|
||||||
///
|
///
|
||||||
pub fn findFirst(comptime Element: type, haystack: []const Element,
|
pub fn findFirst(comptime Element: type, haystack: []const Element,
|
||||||
needle: Element, comptime testEquality: fn (Element, Element) bool) ?usize {
|
needle: Element, comptime testEquality: fn (Element, Element) bool) ?usize {
|
||||||
|
@ -282,6 +282,8 @@ test "findFirst" {
|
||||||
/// Searches for the first instance of an `Element` sequence equal to the contents of `needle` in
|
/// Searches for the first instance of an `Element` sequence equal to the contents of `needle` in
|
||||||
kayomn marked this conversation as resolved
Outdated
kayomn
commented
Worth mentioning that this uses linear search? Worth mentioning that this uses linear search?
|
|||||||
/// `haystack`, returning the starting index or `null` if nothing was found.
|
/// `haystack`, returning the starting index or `null` if nothing was found.
|
||||||
///
|
///
|
||||||
|
/// **Note** that this operation has `O(nm)` time complexity.
|
||||||
|
///
|
||||||
pub fn findFirstOf(comptime Element: type, haystack: []const Element,
|
pub fn findFirstOf(comptime Element: type, haystack: []const Element,
|
||||||
needle: []const Element, comptime testEquality: fn (Element, Element) bool) ?usize {
|
needle: []const Element, comptime testEquality: fn (Element, Element) bool) ?usize {
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const testing = @import("./testing.zig");
|
const testing = @import("./testing.zig");
|
||||||
|
|
||||||
|
// TODO: Remove stdlib dependency.
|
||||||
kayomn marked this conversation as resolved
Outdated
kayomn
commented
Warrants Warrants `TODO` comment mentioning that this stdlib dependency should be removed in future.
|
|||||||
pub const IntFittingRange = std.math.IntFittingRange;
|
pub const IntFittingRange = std.math.IntFittingRange;
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|
|
@ -46,22 +46,23 @@ pub fn Fixed(comptime Element: type) type {
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Attempts to push `element` into `self`, returning a [PushError] if it failed.
|
/// Attempts to push `element` into `self`, returning a [FixedPushError] if it failed.
|
||||||
///
|
///
|
||||||
pub fn push(self: *Self, element: Element) PushError!void {
|
pub fn push(self: *Self, element: Element) FixedPushError!void {
|
||||||
if (self.isFull()) return error.OutOfMemory;
|
if (self.isFull()) return error.BufferOverflow;
|
||||||
|
|
||||||
self.buffer[self.filled] = element;
|
self.buffer[self.filled] = element;
|
||||||
self.filled += 1;
|
self.filled += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Attempts to push all of `elements` into `self`, returning a [PushError] if it failed.
|
/// Attempts to push all of `elements` into `self`, returning a [FixedPushError] if it
|
||||||
|
/// failed.
|
||||||
///
|
///
|
||||||
pub fn pushAll(self: *Self, elements: []const Element) PushError!void {
|
pub fn pushAll(self: *Self, elements: []const Element) FixedPushError!void {
|
||||||
const filled = (self.filled + elements.len);
|
const filled = (self.filled + elements.len);
|
||||||
|
|
||||||
if (filled > self.buffer.len) return error.OutOfMemory;
|
if (filled > self.buffer.len) return error.BufferOverflow;
|
||||||
|
|
||||||
io.copy(Element, self.buffer[self.filled ..], elements);
|
io.copy(Element, self.buffer[self.filled ..], elements);
|
||||||
|
|
||||||
|
@ -69,13 +70,13 @@ pub fn Fixed(comptime Element: type) type {
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Attempts to push `count` instances of `element` into `self`, returning a [PushError] if
|
/// Attempts to push `count` instances of `element` into `self`, returning a
|
||||||
/// it failed.
|
/// [FixedPushError] if it failed.
|
||||||
///
|
///
|
||||||
pub fn pushMany(self: *Self, element: Element, count: usize) PushError!void {
|
pub fn pushMany(self: *Self, element: Element, count: usize) FixedPushError!void {
|
||||||
const filled = (self.filled + count);
|
const filled = (self.filled + count);
|
||||||
|
|
||||||
if (filled > self.buffer.len) return error.OutOfMemory;
|
if (filled > self.buffer.len) return error.BufferOverflow;
|
||||||
|
|
||||||
io.fill(Element, self.buffer[self.filled ..], element);
|
io.fill(Element, self.buffer[self.filled ..], element);
|
||||||
|
|
||||||
|
@ -131,9 +132,11 @@ test "Fixed([]const u8)" {
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Potential errors that may occur while trying to push one or more elements into a stack.
|
/// Potential errors that may occur while trying to push one or more elements into a [Fixed] stack.
|
||||||
///
|
///
|
||||||
kayomn marked this conversation as resolved
Outdated
kayomn
commented
Seeing as Would it make more sense to use Seeing as `FixedStack` does not depend on any kind of dynamic allocation directly, does it make sense to make `PushError` a direct alias of `io.MakeError`?
Would it make more sense to use `BufferOverflow` instead of `OutOfMemory`?
|
|||||||
pub const PushError = io.MakeError;
|
pub const FixedPushError = error {
|
||||||
|
BufferOverflow,
|
||||||
|
};
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Creates and returns a [io.Allocator] value wrapping `fixed_stack`.
|
/// Creates and returns a [io.Allocator] value wrapping `fixed_stack`.
|
||||||
|
@ -154,8 +157,10 @@ pub fn fixedAllocator(fixed_stack: *Fixed(u8)) io.Allocator {
|
||||||
if (buffer_address < stack_address or buffer_address >=
|
if (buffer_address < stack_address or buffer_address >=
|
||||||
(stack_address + stack.filled)) return buffer;
|
(stack_address + stack.filled)) return buffer;
|
||||||
kayomn marked this conversation as resolved
Outdated
kayomn
commented
Reallocation could benefit from the same kind of last-alloc check optimization as deallocation. May be worth clarifying that in the comment and code structure. Reallocation could benefit from the same kind of last-alloc check optimization as deallocation.
May be worth clarifying that in the comment and code structure.
|
|||||||
|
|
||||||
// TODO: Investigate ways of freeing if it is the last allocation.
|
// TODO: Investigate ways of actually freeing if it is the last allocation.
|
||||||
return null;
|
return null;
|
||||||
|
} else {
|
||||||
|
// TODO: Investigate ways of in-place relocating if it is the last allocation.
|
||||||
};
|
};
|
||||||
|
|
||||||
// Reallocate / allocate the memory.
|
// Reallocate / allocate the memory.
|
||||||
|
@ -232,7 +237,7 @@ pub fn fixedWriter(fixed_stack: *Fixed(u8)) io.Writer {
|
||||||
return io.Writer.fromClosure(fixed_stack, struct {
|
return io.Writer.fromClosure(fixed_stack, struct {
|
||||||
fn write(stack: *Fixed(u8), buffer: []const u8) usize {
|
fn write(stack: *Fixed(u8), buffer: []const u8) usize {
|
||||||
stack.pushAll(buffer) catch |err| switch (err) {
|
stack.pushAll(buffer) catch |err| switch (err) {
|
||||||
error.OutOfMemory => return 0,
|
error.BufferOverflow => return 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
return buffer.len;
|
return buffer.len;
|
||||||
|
|
|
@ -97,7 +97,7 @@ pub fn Hashed(comptime Key: type, comptime Value: type,
|
||||||
/// [InsertError] if it fails.
|
/// [InsertError] if it fails.
|
||||||
///
|
///
|
||||||
pub fn insert(self: *Self, key: Key, value: Value) InsertError!void {
|
pub fn insert(self: *Self, key: Key, value: Value) InsertError!void {
|
||||||
if (self.loadFactor() >= self.load_limit) {
|
if (self.isOverloaded()) {
|
||||||
const old_buckets = self.buckets;
|
const old_buckets = self.buckets;
|
||||||
|
|
||||||
defer io.free(self.allocator, old_buckets);
|
defer io.free(self.allocator, old_buckets);
|
||||||
|
@ -131,17 +131,18 @@ pub fn Hashed(comptime Key: type, comptime Value: type,
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Returns the current load factor of `self`, which is derived from the number of capacity
|
/// Returns `true` if the current load factor, derived from the number of elements filling
|
||||||
/// that has been filled.
|
/// the bucket table, is greater than the current load limit.
|
||||||
///
|
///
|
||||||
pub fn loadFactor(self: Self) f32 {
|
pub fn isOverloaded(self: Self) bool {
|
||||||
kayomn marked this conversation as resolved
Outdated
kayomn
commented
Since this function is only ever used to check if the load factor is at / beyond its maximum, would it make more sense to replace it with Since this function is only ever used to check if the load factor is at / beyond its maximum, would it make more sense to replace it with `isMaxLoad(Self) bool` or something like that which meets the requirements of it uses more precisely?
|
|||||||
return @intToFloat(f32, self.filled) / @intToFloat(f32, self.buckets.len);
|
return (@intToFloat(f32, self.filled) /
|
||||||
|
@intToFloat(f32, self.buckets.len)) >= self.load_limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Searches for a value indexed with `key` in `self`.
|
/// Searches for a value indexed with `key` in `self`.
|
||||||
///
|
///
|
||||||
kayomn marked this conversation as resolved
Outdated
kayomn
commented
Comment typo: Comment typo: `if any key` instead of `if an key`.
|
|||||||
/// The found value is returned or `null` if an key matching `key` failed to be found.
|
/// The found value is returned or `null` if any key matching `key` failed to be found.
|
||||||
///
|
///
|
||||||
pub fn lookup(self: Self, key: Key) ?Value {
|
pub fn lookup(self: Self, key: Key) ?Value {
|
||||||
var bucket = &(self.buckets[@mod(key_context.hash(key), self.buckets.len)]);
|
var bucket = &(self.buckets[@mod(key_context.hash(key), self.buckets.len)]);
|
||||||
|
|
|
@ -3,13 +3,16 @@ const std = @import("std");
|
||||||
const sys = @import("./sys.zig");
|
const sys = @import("./sys.zig");
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Starts the the game engine.
|
/// Application entry-point.
|
||||||
///
|
///
|
||||||
pub fn main() anyerror!void {
|
pub fn main() anyerror!void {
|
||||||
return nosuspend await async sys.display(anyerror, run);
|
return nosuspend await async sys.display(anyerror, runEngine);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(app: *sys.App, graphics: *sys.Graphics) anyerror!void {
|
///
|
||||||
kayomn marked this conversation as resolved
Outdated
kayomn
commented
Missing documentation comment. Missing documentation comment.
|
|||||||
|
/// Runs the game engine.
|
||||||
|
///
|
||||||
|
fn runEngine(app: *sys.App, graphics: *sys.Graphics) anyerror!void {
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
|
||||||
defer _ = gpa.deinit();
|
defer _ = gpa.deinit();
|
||||||
|
|
|
@ -6,7 +6,6 @@ const sys = @import("./sys.zig");
|
||||||
///
|
///
|
||||||
const Block = extern struct {
|
const Block = extern struct {
|
||||||
signature: [signature_magic.len]u8 = signature_magic,
|
signature: [signature_magic.len]u8 = signature_magic,
|
||||||
revision: u8 = 0,
|
|
||||||
path: sys.Path = sys.Path.empty,
|
path: sys.Path = sys.Path.empty,
|
||||||
data_size: u64 = 0,
|
data_size: u64 = 0,
|
||||||
data_head: u64 = 0,
|
data_head: u64 = 0,
|
||||||
|
@ -15,19 +14,23 @@ const Block = extern struct {
|
||||||
comptime {
|
comptime {
|
||||||
const entry_size = @sizeOf(@This());
|
const entry_size = @sizeOf(@This());
|
||||||
|
|
||||||
if (entry_size != 512) @compileError("EntryBlock is greater than 512 bytes");
|
if (entry_size != 512) @compileError("EntryBlock is not 512 bytes");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
/// Reference to a file entry in an Oar archive, denoting the starting offset from the top of head
|
||||||
|
/// of the file and its size.
|
||||||
///
|
///
|
||||||
pub const Entry = struct {
|
pub const Entry = struct {
|
||||||
kayomn marked this conversation as resolved
Outdated
kayomn
commented
Missing documentation comment. Missing documentation comment.
|
|||||||
head: u64,
|
head: u64,
|
||||||
size: u64,
|
size: u64,
|
||||||
|
|
||||||
///
|
///
|
||||||
|
/// [FindError.EntryNotFound] occurs when no entry matched the parameters of the find operation.
|
||||||
///
|
///
|
||||||
|
/// [FindError.ArchiveUnsupported] occurs if the file provided to the find operation is not a
|
||||||
kayomn marked this conversation as resolved
Outdated
kayomn
commented
Missing documentation comment. Missing documentation comment.
|
|||||||
|
/// valid archive file.
|
||||||
///
|
///
|
||||||
pub const FindError = error {
|
pub const FindError = error {
|
||||||
EntryNotFound,
|
EntryNotFound,
|
||||||
|
@ -35,7 +38,11 @@ pub const Entry = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
///
|
///
|
||||||
kayomn marked this conversation as resolved
Outdated
kayomn
commented
Missing documentation comment. Missing documentation comment.
|
|||||||
|
/// Attempts to perform a binary search on the entry blocks defined in `archive_file` for one
|
||||||
|
/// matching `entry_path`, returning an [Entry] referencing its data or a [FindError] if it
|
||||||
|
/// failed.
|
||||||
///
|
///
|
||||||
|
/// **Note** that this operation has `O(log n)` time complexity.
|
||||||
///
|
///
|
||||||
pub fn find(archive_file: *sys.ReadableFile, entry_path: sys.Path) FindError!Entry {
|
pub fn find(archive_file: *sys.ReadableFile, entry_path: sys.Path) FindError!Entry {
|
||||||
var header = Header{};
|
var header = Header{};
|
||||||
|
@ -78,7 +85,11 @@ pub const Entry = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
/// Reads the data from `entry` in `archive_file` from the byte at the entry-relative `offset`
|
||||||
|
/// into `buffer` until either the end of the entry data, end of archive file, or end of buffer
|
||||||
|
/// is reached.
|
||||||
///
|
///
|
||||||
|
/// The number of bytes read is returned or [sys.FileError] if it failed.
|
||||||
///
|
///
|
||||||
pub fn read(entry: Entry, archive_file: *sys.ReadableFile,
|
pub fn read(entry: Entry, archive_file: *sys.ReadableFile,
|
||||||
kayomn marked this conversation as resolved
Outdated
kayomn
commented
Missing documentation comment. Missing documentation comment.
|
|||||||
offset: u64, buffer: []u8) sys.FileError!usize {
|
offset: u64, buffer: []u8) sys.FileError!usize {
|
||||||
|
@ -89,7 +100,7 @@ pub const Entry = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
/// Header data that every Oar archive file starts with at byte offset `0`.
|
||||||
///
|
///
|
||||||
const Header = extern struct {
|
const Header = extern struct {
|
||||||
signature: [signature_magic.len]u8 = signature_magic,
|
signature: [signature_magic.len]u8 = signature_magic,
|
||||||
|
@ -101,16 +112,16 @@ const Header = extern struct {
|
||||||
comptime {
|
comptime {
|
||||||
const size = @sizeOf(@This());
|
const size = @sizeOf(@This());
|
||||||
|
|
||||||
if (size != 512) @compileError("Header is greater than 512 bytes");
|
if (size != 512) @compileError("Header is not 512 bytes");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
/// The magic revision number that this Oar software implementation understands.
|
||||||
///
|
///
|
||||||
const revision_magic = 0;
|
const revision_magic = 0;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Magic identifier used to validate [Entry] data.
|
/// Magic identifier used to validate [Header] and [Block] data.
|
||||||
///
|
///
|
||||||
const signature_magic = [3]u8{'o', 'a', 'r'};
|
const signature_magic = [3]u8{'o', 'a', 'r'};
|
||||||
|
|
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.