Compare commits

..

No commits in common. "4bb86c41bc5d0190b56807bc202f3300e4d0a300" and "14b39210015012db0709e9e9fcfcf03b11affbfa" have entirely different histories.

8 changed files with 59 additions and 229 deletions

2
.vscode/launch.json vendored
View File

@ -9,7 +9,6 @@
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"valuesFormatting": "parseText", "valuesFormatting": "parseText",
"preLaunchTask": "Build Debug", "preLaunchTask": "Build Debug",
"internalConsoleOptions": "openOnSessionStart",
}, },
{ {
@ -21,7 +20,6 @@
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"valuesFormatting": "parseText", "valuesFormatting": "parseText",
"preLaunchTask": "Build Debug", "preLaunchTask": "Build Debug",
"internalConsoleOptions": "openOnSessionStart",
}, },
] ]
} }

View File

@ -13,7 +13,7 @@ pub const Allocation = struct {
}; };
/// ///
/// Closure for dynamic memory allocation through the referenced allocator state machine capture. /// Dynamic memory allocation interface.
/// ///
pub const Allocator = meta.Function(Allocation, ?[*]u8); pub const Allocator = meta.Function(Allocation, ?[*]u8);
@ -78,7 +78,7 @@ pub fn Spliterator(comptime Element: type) type {
}; };
} }
test "Spliterator(u8)" { test "Spliterator of string literals" {
// Empty source. // Empty source.
{ {
var spliterator = Spliterator(u8){ var spliterator = Spliterator(u8){
@ -172,7 +172,7 @@ pub fn bytesOf(pointer: anytype) switch (@typeInfo(@TypeOf(pointer))) {
} }
} }
test "bytesOf" { test "Bytes of types" {
var foo: u32 = 10; var foo: u32 = 10;
try testing.expect(bytesOf(&foo)[0] == 0x0a); try testing.expect(bytesOf(&foo)[0] == 0x0a);
@ -195,7 +195,7 @@ pub fn compareBytes(this: []const u8, that: []const u8) isize {
return (@intCast(isize, this.len) - @intCast(isize, that.len)); return (@intCast(isize, this.len) - @intCast(isize, that.len));
} }
test "compareBytes" { test "Compare bytes" {
try testing.expect(compareBytes(&.{69, 42, 0}, &.{69, 42, 0}) == 0); try testing.expect(compareBytes(&.{69, 42, 0}, &.{69, 42, 0}) == 0);
try testing.expect(compareBytes(&.{69, 42, 11}, &.{69, 42}) == 1); try testing.expect(compareBytes(&.{69, 42, 11}, &.{69, 42}) == 1);
try testing.expect(compareBytes(&.{69, 42}, &.{69, 42, 11}) == -1); try testing.expect(compareBytes(&.{69, 42}, &.{69, 42, 11}) == -1);
@ -208,7 +208,7 @@ pub fn copy(comptime Element: type, target: []Element, source: []const Element)
for (source) |element, index| target[index] = element; for (source) |element, index| target[index] = element;
} }
test "copy" { test "Copy data" {
var buffer = [_]u32{0} ** 20; var buffer = [_]u32{0} ** 20;
const data = [_]u32{3, 20, 8000}; const data = [_]u32{3, 20, 8000};
@ -231,7 +231,7 @@ pub fn equals(comptime Element: type, this: []const Element, that: []const Eleme
return true; return true;
} }
test "equals" { test "Check memory is equal" {
const bytes_sequence = &.{69, 42, 0}; const bytes_sequence = &.{69, 42, 0};
try testing.expect(equals(u8, bytes_sequence, bytes_sequence)); try testing.expect(equals(u8, bytes_sequence, bytes_sequence));
@ -245,7 +245,7 @@ pub fn fill(comptime Element: type, target: []Element, source: Element) void {
for (target) |_, index| target[index] = source; for (target) |_, index| target[index] = source;
} }
test "fill" { test "Fill data" {
var buffer = [_]u32{0} ** 8; var buffer = [_]u32{0} ** 8;
fill(u32, &buffer, 1); fill(u32, &buffer, 1);
@ -265,7 +265,7 @@ pub fn findFirst(comptime Element: type, haystack: []const Element,
return null; return null;
} }
test "findFirst" { test "Find first of element" {
const haystack = &.{"", "", "foo"}; const haystack = &.{"", "", "foo"};
const testEquality = struct { const testEquality = struct {
@ -298,7 +298,7 @@ pub fn findFirstOf(comptime Element: type, haystack: []const Element,
return null; return null;
} }
test "findFirstOf" { test "Find first of sequence" {
const haystack = &.{"foo", "bar", "baz"}; const haystack = &.{"foo", "bar", "baz"};
const testEquality = struct { const testEquality = struct {
@ -333,16 +333,6 @@ pub fn free(allocator: Allocator, allocated_memory: anytype) void {
}) != null) unreachable; }) != null) unreachable;
} }
test "free" {
var buffer = [_]u8{0} ** 4096;
var memory = stack.Fixed(u8){.buffer = &buffer};
const fixed_allocator = stack.fixedAllocator(&memory);
const block_size = 8;
const allocated_block = (try makeMany(u8, fixed_allocator, block_size))[0 .. block_size];
defer free(fixed_allocator, allocated_block);
}
/// ///
/// Returns a deterministic hash code compiled from each byte in `bytes`. /// Returns a deterministic hash code compiled from each byte in `bytes`.
/// ///
@ -356,7 +346,7 @@ pub fn hashBytes(bytes: []const u8) usize {
return hash; return hash;
} }
test "hashBytes" { test "Hashing bytes" {
const bytes_sequence = &.{69, 42, 0}; const bytes_sequence = &.{69, 42, 0};
try testing.expect(hashBytes(bytes_sequence) == hashBytes(bytes_sequence)); try testing.expect(hashBytes(bytes_sequence) == hashBytes(bytes_sequence));
@ -370,42 +360,15 @@ test "hashBytes" {
pub fn makeMany(comptime Element: type, allocator: Allocator, size: usize) MakeError![*]Element { pub fn makeMany(comptime Element: type, allocator: Allocator, size: usize) MakeError![*]Element {
const alignment = @alignOf(Element); const alignment = @alignOf(Element);
return @ptrCast([*]Element, @alignCast(alignment, allocator.call(.{ if (allocator.call(.{
.existing = null, .existing = null,
.size = @sizeOf(Element) * size, .size = @sizeOf(Element) * size,
.alignment = alignment, .alignment = alignment,
}) orelse return error.OutOfMemory)); })) |buffer| {
} return @ptrCast([*]Element, @alignCast(alignment, buffer));
}
test "makeMany" { return error.OutOfMemory;
var buffer = [_]u8{0} ** 4096;
var memory = stack.Fixed(u8){.buffer = &buffer};
const block_size = 8;
// Don't care about the actual allocation - just assertions about it.
_ = (try makeMany(u8, stack.fixedAllocator(&memory), block_size))[0 .. block_size];
}
///
/// Attempts to allocate a buffer of `1` `Element` using `allocator`, returning it or a [MakeError]
/// if it failed.
///
pub fn makeOne(comptime Element: type, allocator: Allocator) MakeError!*Element {
const alignment = @alignOf(Element);
return @ptrCast(*Element, @alignCast(alignment, allocator.call(.{
.existing = null,
.size = @sizeOf(Element),
.alignment = alignment,
}) orelse return error.OutOfMemory));
}
test "makeOne" {
var buffer = [_]u8{0} ** 4096;
var memory = stack.Fixed(u8){.buffer = &buffer};
// Don't care about the actual allocation - just assertions about it.
_ = try makeOne(u8, stack.fixedAllocator(&memory));
} }
/// ///
@ -417,7 +380,7 @@ pub fn swap(comptime Data: type, this: *Data, that: *Data) void {
that.* = temp; that.* = temp;
} }
test "swap" { test "Data swapping" {
var a: u64 = 0; var a: u64 = 0;
var b: u64 = 1; var b: u64 = 1;
@ -440,7 +403,7 @@ pub const null_writer = Writer.from(struct {
} }
}.write); }.write);
test "null_writer" { test "Null writing" {
const sequence = "foo"; const sequence = "foo";
try testing.expect(null_writer.call(sequence) == sequence.len); try testing.expect(null_writer.call(sequence) == sequence.len);

View File

@ -1,12 +1,10 @@
const std = @import("std");
const testing = @import("./testing.zig");
pub const IntFittingRange = std.math.IntFittingRange; pub const IntFittingRange = @import("std").math.IntFittingRange;
/// ///
/// Returns the highest integer value representable by `Integer`. /// Returns the maximum value of `Integer`.
/// ///
pub fn maxIntValue(comptime Integer: type) comptime_int { pub fn maxInt(comptime Integer: type) comptime_int {
return switch (@typeInfo(Integer)) { return switch (@typeInfo(Integer)) {
.Int => |info| if (info.bits == 0) 0 else .Int => |info| if (info.bits == 0) 0 else
((1 << (info.bits - @boolToInt(info.signedness == .signed))) - 1), ((1 << (info.bits - @boolToInt(info.signedness == .signed))) - 1),
@ -15,32 +13,6 @@ pub fn maxIntValue(comptime Integer: type) comptime_int {
}; };
} }
test "maxIntValue" {
try testing.expect(maxIntValue(u8) == 255);
try testing.expect(maxIntValue(i8) == 127);
try testing.expect(maxIntValue(u16) == 65535);
try testing.expect(maxIntValue(i16) == 32767);
}
///
/// Returns the highest `Number` value between `this` and `that`.
///
pub fn max(comptime Number: type, this: Number, that: Number) Number {
return switch (@typeInfo(Number)) {
.Int, .Float, .ComptimeInt, .ComptimeFloat => if (this > that) this else that,
else => @compileError("`" ++ @typeName(Number) ++
"` must be an int, float, comptime_int, or comptime_float"),
};
}
test "max" {
try testing.expect(max(f32, 0.1, 1.0) == 1.0);
try testing.expect(max(f64, 1.0, 1.01) == 1.01);
try testing.expect(max(u32, 35615, 2873) == 35615);
}
/// ///
/// Returns the lowest `Number` value between `this` and `that`. /// Returns the lowest `Number` value between `this` and `that`.
/// ///
@ -52,9 +24,3 @@ pub fn min(comptime Number: type, this: Number, that: Number) Number {
"` must be an int, float, comptime_int, or comptime_float"), "` must be an int, float, comptime_int, or comptime_float"),
}; };
} }
test "min" {
try testing.expect(min(f32, 0.1, 1.0) == 0.1);
try testing.expect(min(f64, 1.0, 1.01) == 1.0);
try testing.expect(min(u32, 35615, 2873) == 2873);
}

View File

@ -22,13 +22,6 @@ pub fn Fixed(comptime Element: type) type {
self.filled = 0; self.filled = 0;
} }
///
/// Returns `true` if `self` has filled its buffer to maximum capacity, otherwise `false`.
///
pub fn isFull(self: Self) bool {
return (self.filled == self.buffer.len);
}
/// ///
/// If `self` is filled with at least `1` value, it is decremented by `1`, otherwise leaving /// If `self` is filled with at least `1` value, it is decremented by `1`, otherwise leaving
/// the actual memory contents of the buffer untouched until it is later overwritten by /// the actual memory contents of the buffer untouched until it is later overwritten by
@ -49,7 +42,7 @@ 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 [PushError] if it failed.
/// ///
pub fn push(self: *Self, element: Element) PushError!void { pub fn push(self: *Self, element: Element) PushError!void {
if (self.isFull()) return error.OutOfMemory; if (self.filled == self.buffer.len) return error.OutOfMemory;
self.buffer[self.filled] = element; self.buffer[self.filled] = element;
self.filled += 1; self.filled += 1;
@ -84,7 +77,7 @@ pub fn Fixed(comptime Element: type) type {
}; };
} }
test "Fixed([]const u8)" { test "Fixed stack of string literals" {
const default_value = ""; const default_value = "";
var buffer = [_][]const u8{default_value} ** 4; var buffer = [_][]const u8{default_value} ** 4;
var shopping_list = Fixed([]const u8){.buffer = &buffer}; var shopping_list = Fixed([]const u8){.buffer = &buffer};
@ -147,12 +140,12 @@ pub fn fixedAllocator(fixed_stack: *Fixed(u8)) io.Allocator {
if (allocation.existing) |buffer| if (allocation.size == 0) { if (allocation.existing) |buffer| if (allocation.size == 0) {
// Deallocate the memory. // Deallocate the memory.
const buffer_address = @ptrToInt(buffer); const buffer_address = @ptrToInt(buffer);
const stack_address = @ptrToInt(stack.buffer.ptr); const stack_buffer_address = @ptrToInt(stack.buffer.ptr);
// Check the buffer is within the address space of the stack buffer. If not, it // Check the buffer is within the address space of the stack buffer. If not, it
// should just be returned to let the caller know it cannot be freed. // should just be returned to let the caller know it cannot be freed.
if (buffer_address < stack_address or buffer_address >= if ((buffer_address < stack_buffer_address) or
(stack_address + stack.filled)) return buffer; (buffer_address >= (stack_buffer_address + stack.filled))) return buffer;
// TODO: Investigate ways of freeing if it is the last allocation. // TODO: Investigate ways of freeing if it is the last allocation.
return null; return null;
@ -174,54 +167,6 @@ pub fn fixedAllocator(fixed_stack: *Fixed(u8)) io.Allocator {
}.alloc); }.alloc);
} }
test "fixedAllocator" {
var buffer = [_]u8{0} ** 32;
var stack = Fixed(u8){.buffer = &buffer};
const allocator = fixedAllocator(&stack);
// Allocation
var block_memory = allocator.call(.{
.existing = null,
.alignment = @alignOf(u64),
.size = @sizeOf(u64),
});
try testing.expect(block_memory != null);
const buffer_address_head = @ptrToInt(&buffer);
const buffer_address_tail = @ptrToInt(&buffer) + buffer.len;
{
const block_memory_address = @ptrToInt(block_memory);
try testing.expect(block_memory_address >= buffer_address_head and
block_memory_address < buffer_address_tail);
}
// Reallocation.
block_memory = allocator.call(.{
.existing = block_memory,
.alignment = @alignOf(u64),
.size = @sizeOf(u64),
});
try testing.expect(block_memory != null);
{
const block_memory_address = @ptrToInt(block_memory);
try testing.expect(block_memory_address >= buffer_address_head and
block_memory_address < buffer_address_tail);
}
// Deallocation.
try testing.expect(allocator.call(.{
.existing = block_memory,
.alignment = 0,
.size = 0,
}) == null);
}
/// ///
/// Returns an [io.Writer] wrapping `fixed_stack`. /// Returns an [io.Writer] wrapping `fixed_stack`.
/// ///
@ -240,7 +185,7 @@ pub fn fixedWriter(fixed_stack: *Fixed(u8)) io.Writer {
}.write); }.write);
} }
test "fixedWriter" { test "Fixed writer" {
var buffer = [_]u8{0} ** 4; var buffer = [_]u8{0} ** 4;
var sequence_stack = Fixed(u8){.buffer = &buffer}; var sequence_stack = Fixed(u8){.buffer = &buffer};
const sequence_data = [_]u8{8, 16, 32, 64}; const sequence_data = [_]u8{8, 16, 32, 64};

View File

@ -197,12 +197,12 @@ pub const string_literal_context = KeyContext([]const u8){
}.stringsEqual, }.stringsEqual,
}; };
test "Hashed([]const u8, u32, string_literal_context)" { test "Hash table manipulation with string literal context" {
var buffer = [_]u8{0} ** 4096; var buffer = [_]u8{0} ** 4096;
var memory = stack.Fixed(u8){.buffer = &buffer}; var fixed_stack = stack.Fixed(u8){.buffer = &buffer};
var table = try Hashed([]const u8, u32, string_literal_context). var table = try Hashed([]const u8, u32, string_literal_context).
init(stack.fixedAllocator(&memory)); init(stack.fixedAllocator(&fixed_stack));
defer table.deinit(); defer table.deinit();

View File

@ -13,10 +13,6 @@ pub fn expect(ok: bool) TestError!void {
if (!ok) return error.UnexpectedResult; if (!ok) return error.UnexpectedResult;
} }
test "expect" { // TODO: Implement tests.
try expect(true);
expect(false) catch {};
}
pub const expectError = @import("std").testing.expectError; pub const expectError = @import("std").testing.expectError;

View File

@ -1,7 +1,5 @@
const io = @import("./io.zig"); const io = @import("./io.zig");
const math = @import("./math.zig"); const math = @import("./math.zig");
const stack = @import("./stack.zig");
const testing = @import("./testing.zig");
/// ///
/// [PrintError.WriteFailure] occurs when the underlying [io.Writer] implementation failed to write /// [PrintError.WriteFailure] occurs when the underlying [io.Writer] implementation failed to write
@ -12,7 +10,7 @@ pub const PrintError = error {
}; };
/// ///
/// Named identifiers for number formats used in printing functions. /// Number formatting modes supported by [printInt].
/// ///
pub const Radix = enum { pub const Radix = enum {
binary, binary,
@ -30,29 +28,6 @@ pub const Radix = enum {
tetradecimal, tetradecimal,
pentadecimal, pentadecimal,
hexadecimal, hexadecimal,
///
/// Returns the base number of `radix`.
///
pub fn base(radix: Radix) u8 {
return switch (radix) {
.binary => 2,
.tinary => 3,
.quaternary => 4,
.quinary => 5,
.senary => 6,
.septenary => 7,
.octal => 8,
.nonary => 9,
.decimal => 10,
.undecimal => 11,
.duodecimal => 12,
.tridecimal => 13,
.tetradecimal => 14,
.pentadecimal => 15,
.hexadecimal => 16,
};
}
}; };
/// ///
@ -65,39 +40,35 @@ pub fn printInt(writer: io.Writer, radix: Radix, value: anytype) PrintError!void
const Int = @TypeOf(value); const Int = @TypeOf(value);
switch (@typeInfo(Int)) { switch (@typeInfo(Int)) {
.Int => |info| { .Int => |int_info| {
if (value == 0) { if (value == 0) return writer.apply("0");
const zero = "0";
if (writer.call(zero) != zero.len) return error.WriteFailure; const base = @enumToInt(radix);
} else { const is_signed = (int_info.signedness == .signed);
// Big enough to hold the hexadecimal representation of the integer type, which is
// the largest number format accomodated for in [Radix].
var buffer = [_]u8{0} ** (@sizeOf(Int) * (@bitSizeOf(u8) / 4));
var buffer_count: usize = 0;
var n1 = value;
if (info.signedness == .signed and value < 0) { var buffer = [_]u8{0} ** (math.ceil(math.log(math.
// Negative value. maxInt(Int), base)) + @boolToInt(is_signed));
n1 = -value;
buffer[0] = '-';
buffer_count += 1;
}
while (n1 != 0) { var buffer_count: usize = 0;
const base = radix.base(); var n1 = value;
buffer[buffer_count] = @intCast(u8, (n1 % base) + '0'); if (is_signed and (value < 0)) {
n1 = (n1 / base); // Negative value.
buffer_count += 1; n1 = -value;
} buffer[0] = '-';
buffer_count += 1;
for (buffer[0 .. (buffer_count / 2)]) |_, i|
io.swap(u8, &buffer[i], &buffer[buffer_count - i - 1]);
if (writer.call(buffer[0 .. buffer_count]) != buffer_count)
return error.WriteFailure;
} }
while (n1 != 0) {
buffer[buffer_count] = @intCast(u8, (n1 % base) + '0');
n1 = (n1 / base);
buffer_count += 1;
}
for (buffer[0 .. (buffer_count / 2)]) |_, i|
io.swap(u8, &buffer[i], &buffer[buffer_count - i - 1]);
if (writer.call(buffer[0 .. buffer_count]) != buffer_count) return error.WriteFailure;
}, },
// Cast comptime int into known-size integer and try again. // Cast comptime int into known-size integer and try again.
@ -108,12 +79,6 @@ pub fn printInt(writer: io.Writer, radix: Radix, value: anytype) PrintError!void
} }
} }
test "printInt" { test "Print 64-bit signed integer" {
// Max digits to represent a decimal u8 is 3 (i.e. 127 / 255). // TODO: implement.
var decimal_buffer = [_]u8{0} ** 3;
var decimal_stack = stack.Fixed(u8){.buffer = &decimal_buffer};
var decimal_writer = stack.fixedWriter(&decimal_stack);
try printInt(decimal_writer, .decimal, 365);
try testing.expect(decimal_stack.isFull());
} }

View File

@ -114,10 +114,7 @@ pub const ReadableFile = opaque {
{ {
ext.SDL_ClearError(); ext.SDL_ClearError();
const math = core.math; var sought = core.math.min(u64, offset, core.math.maxInt(i64));
const min = math.min;
const maxIntValue = math.maxIntValue;
var sought = min(u64, offset, maxIntValue(i64));
if (ext.SDL_RWseek(rw_ops, @intCast(i64, sought), ext.RW_SEEK_SET) < 0) if (ext.SDL_RWseek(rw_ops, @intCast(i64, sought), ext.RW_SEEK_SET) < 0)
return error.FileInaccessible; return error.FileInaccessible;
@ -125,7 +122,7 @@ pub const ReadableFile = opaque {
var to_seek = offset - sought; var to_seek = offset - sought;
while (to_seek != 0) { while (to_seek != 0) {
sought = min(u64, to_seek, maxIntValue(i64)); sought = core.math.min(u64, to_seek, core.math.maxInt(i64));
ext.SDL_ClearError(); ext.SDL_ClearError();