| author | |
| committer | |
| log | 3b1ee55372343807515c6a776f211471323804b7 |
| tree | 8ab4b9216a76be7b5f659e406e5b6d03fd50be6d |
| parent | e8f81eb1d126e37dc5b57e796fd8ee5e10123312 |
23 files changed, 387 insertions(+), 14 deletions(-)
src/BufIndexer.zig+39-1| ... | @@ -1,6 +1,8 @@ | ... | @@ -1,6 +1,8 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const string = []const u8; | 2 | const string = []const u8; |
| 3 | const extras = @import("./lib.zig"); | 3 | const extras = @import("./lib.zig"); |
| 4 | const indexBufferT = extras.indexBufferT; | ||
| 5 | const rawInt = extras.rawInt; | ||
| 4 | 6 | ||
| 5 | pub fn BufIndexer(comptime T: type, comptime endian: std.builtin.Endian) type { | 7 | pub fn BufIndexer(comptime T: type, comptime endian: std.builtin.Endian) type { |
| 6 | return struct { | 8 | return struct { |
| ... | @@ -16,8 +18,44 @@ pub fn BufIndexer(comptime T: type, comptime endian: std.builtin.Endian) type { | ... | @@ -16,8 +18,44 @@ pub fn BufIndexer(comptime T: type, comptime endian: std.builtin.Endian) type { |
| 16 | }; | 18 | }; |
| 17 | } | 19 | } |
| 18 | 20 | ||
| 21 | /// asserts 'idx' to be in bounds | ||
| 19 | pub fn at(self: *const Self, idx: usize) T { | 22 | pub fn at(self: *const Self, idx: usize) T { |
| 20 | return extras.indexBufferT(self.bytes, T, endian, idx, self.max_len); | 23 | return indexBufferT(self.bytes, T, endian, idx, self.max_len); |
| 21 | } | 24 | } |
| 22 | }; | 25 | }; |
| 23 | } | 26 | } |
| 27 | |||
| 28 | test { | ||
| 29 | const bytes = std.mem.toBytes(rawInt(u64, 0x4e5a7da9f3f1d132)); | ||
| 30 | const bindex = BufIndexer(u64, .Big).init(&bytes, 1); | ||
| 31 | try std.testing.expect(bindex.at(0) == 0x4e5a7da9f3f1d132); | ||
| 32 | } | ||
| 33 | |||
| 34 | test { | ||
| 35 | const bytes = std.mem.toBytes(rawInt(u64, 0x4e5a7da9f3f1d132)); | ||
| 36 | const bindex = BufIndexer(u32, .Big).init(&bytes, 2); | ||
| 37 | try std.testing.expect(bindex.at(0) == 0x4e5a7da9); | ||
| 38 | try std.testing.expect(bindex.at(1) == 0xf3f1d132); | ||
| 39 | } | ||
| 40 | |||
| 41 | test { | ||
| 42 | const bytes = std.mem.toBytes(rawInt(u64, 0x4e5a7da9f3f1d132)); | ||
| 43 | const bindex = BufIndexer(u16, .Big).init(&bytes, 4); | ||
| 44 | try std.testing.expect(bindex.at(0) == 0x4e5a); | ||
| 45 | try std.testing.expect(bindex.at(1) == 0x7da9); | ||
| 46 | try std.testing.expect(bindex.at(2) == 0xf3f1); | ||
| 47 | try std.testing.expect(bindex.at(3) == 0xd132); | ||
| 48 | } | ||
| 49 | |||
| 50 | test { | ||
| 51 | const bytes = std.mem.toBytes(rawInt(u64, 0x4e5a7da9f3f1d132)); | ||
| 52 | const bindex = BufIndexer(u8, .Big).init(&bytes, 8); | ||
| 53 | try std.testing.expect(bindex.at(0) == 0x4e); | ||
| 54 | try std.testing.expect(bindex.at(1) == 0x5a); | ||
| 55 | try std.testing.expect(bindex.at(2) == 0x7d); | ||
| 56 | try std.testing.expect(bindex.at(3) == 0xa9); | ||
| 57 | try std.testing.expect(bindex.at(4) == 0xf3); | ||
| 58 | try std.testing.expect(bindex.at(5) == 0xf1); | ||
| 59 | try std.testing.expect(bindex.at(6) == 0xd1); | ||
| 60 | try std.testing.expect(bindex.at(7) == 0x32); | ||
| 61 | } |
src/FixedMaxBuffer.zig+4| ... | @@ -46,3 +46,7 @@ pub fn FixedMaxBuffer(comptime max_len: usize) type { | ... | @@ -46,3 +46,7 @@ pub fn FixedMaxBuffer(comptime max_len: usize) type { |
| 46 | } | 46 | } |
| 47 | }; | 47 | }; |
| 48 | } | 48 | } |
| 49 | |||
| 50 | test { | ||
| 51 | std.testing.refAllDecls(FixedMaxBuffer(16)); | ||
| 52 | } |
src/TagNameJsonStringifyMixin.zig+18-2| ... | @@ -4,8 +4,24 @@ const extras = @import("./lib.zig"); | ... | @@ -4,8 +4,24 @@ const extras = @import("./lib.zig"); |
| 4 | 4 | ||
| 5 | pub fn TagNameJsonStringifyMixin(comptime S: type) type { | 5 | pub fn TagNameJsonStringifyMixin(comptime S: type) type { |
| 6 | return struct { | 6 | return struct { |
| 7 | pub fn jsonStringify(self: S, options: std.json.StringifyOptions, out_stream: anytype) !void { | 7 | pub fn jsonStringify(self: S, json_stream: anytype) !void { |
| 8 | try std.json.stringify(@tagName(self), options, out_stream); | 8 | try json_stream.write(@tagName(self)); |
| 9 | } | 9 | } |
| 10 | }; | 10 | }; |
| 11 | } | 11 | } |
| 12 | |||
| 13 | test { | ||
| 14 | const E = enum { | ||
| 15 | windows, | ||
| 16 | linux, | ||
| 17 | macos, | ||
| 18 | freebsd, | ||
| 19 | |||
| 20 | pub usingnamespace TagNameJsonStringifyMixin(@This()); | ||
| 21 | }; | ||
| 22 | const allocator = std.testing.allocator; | ||
| 23 | var str = std.ArrayList(u8).init(allocator); | ||
| 24 | defer str.deinit(); | ||
| 25 | try std.json.stringify(@as(E, .linux), .{}, str.writer()); | ||
| 26 | try std.testing.expect(std.mem.eql(u8, str.items, "\"linux\"")); | ||
| 27 | } |
src/containsString.zig+7| ... | @@ -10,3 +10,10 @@ pub fn containsString(haystack: []const string, needle: string) bool { | ... | @@ -10,3 +10,10 @@ pub fn containsString(haystack: []const string, needle: string) bool { |
| 10 | } | 10 | } |
| 11 | return false; | 11 | return false; |
| 12 | } | 12 | } |
| 13 | |||
| 14 | test { | ||
| 15 | try std.testing.expect(containsString(&.{ "a", "b", "c", "d" }, "b")); | ||
| 16 | } | ||
| 17 | test { | ||
| 18 | try std.testing.expect(!containsString(&.{}, "b")); | ||
| 19 | } |
src/countScalar.zig+7| ... | @@ -12,3 +12,10 @@ pub fn countScalar(comptime T: type, haystack: []const T, needle: T) usize { | ... | @@ -12,3 +12,10 @@ pub fn countScalar(comptime T: type, haystack: []const T, needle: T) usize { |
| 12 | } | 12 | } |
| 13 | return found; | 13 | return found; |
| 14 | } | 14 | } |
| 15 | |||
| 16 | test { | ||
| 17 | try std.testing.expect(countScalar(u8, "The lazy brown fox jumped over the lazy dog.", ' ') == 8); | ||
| 18 | } | ||
| 19 | test { | ||
| 20 | try std.testing.expect(countScalar(u8, "kljsklksldajvaskidjklvadv", 'z') == 0); | ||
| 21 | } |
src/ensureFieldSubset.zig+15-1| ... | @@ -2,8 +2,22 @@ const std = @import("std"); | ... | @@ -2,8 +2,22 @@ const std = @import("std"); |
| 2 | const string = []const u8; | 2 | const string = []const u8; |
| 3 | const extras = @import("./lib.zig"); | 3 | const extras = @import("./lib.zig"); |
| 4 | 4 | ||
| 5 | /// Asserts at comptime that L ⊆ R. | ||
| 5 | pub fn ensureFieldSubset(comptime L: type, comptime R: type) void { | 6 | pub fn ensureFieldSubset(comptime L: type, comptime R: type) void { |
| 6 | for (std.meta.fields(L)) |item| { | 7 | for (std.meta.fields(L)) |item| { |
| 7 | if (!@hasField(R, item.name)) @compileError(std.fmt.comptimePrint("{s} is missing the {s} field from {s}", .{ R, item.name, L })); | 8 | if (!@hasField(R, item.name)) { |
| 9 | @compileError(std.fmt.comptimePrint("'{s}' field in {s} is not present in {s}", .{ item.name, @typeName(L), @typeName(R) })); | ||
| 10 | } | ||
| 11 | } | ||
| 12 | } | ||
| 13 | |||
| 14 | test { | ||
| 15 | comptime { | ||
| 16 | ensureFieldSubset(enum { | ||
| 17 | linux, | ||
| 18 | macos, | ||
| 19 | windows, | ||
| 20 | freebsd, | ||
| 21 | }, std.Target.Os.Tag); | ||
| 8 | } | 22 | } |
| 9 | } | 23 | } |
src/fmtReplacer.zig+13-5| ... | @@ -10,12 +10,14 @@ pub fn fmtReplacer(bytes: string, from: u8, to: u8) std.fmt.Formatter(formatRepl | ... | @@ -10,12 +10,14 @@ pub fn fmtReplacer(bytes: string, from: u8, to: u8) std.fmt.Formatter(formatRepl |
| 10 | } }; | 10 | } }; |
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | const FormatData = struct { | ||
| 14 | bytes: string, | ||
| 15 | from: u8, | ||
| 16 | to: u8, | ||
| 17 | }; | ||
| 18 | |||
| 13 | fn formatReplacer( | 19 | fn formatReplacer( |
| 14 | self: struct { | 20 | self: FormatData, |
| 15 | bytes: string, | ||
| 16 | from: u8, | ||
| 17 | to: u8, | ||
| 18 | }, | ||
| 19 | comptime fmt: []const u8, | 21 | comptime fmt: []const u8, |
| 20 | options: std.fmt.FormatOptions, | 22 | options: std.fmt.FormatOptions, |
| 21 | writer: anytype, | 23 | writer: anytype, |
| ... | @@ -26,3 +28,9 @@ fn formatReplacer( | ... | @@ -26,3 +28,9 @@ fn formatReplacer( |
| 26 | try writer.writeByte(if (c == self.from) self.to else @intCast(c)); | 28 | try writer.writeByte(if (c == self.from) self.to else @intCast(c)); |
| 27 | } | 29 | } |
| 28 | } | 30 | } |
| 31 | |||
| 32 | test { | ||
| 33 | const in = "C:\\Program Files\\Custom Utilities\\StringFinder.exe"; | ||
| 34 | const out = "C:/Program Files/Custom Utilities/StringFinder.exe"; | ||
| 35 | try std.testing.expectFmt(out, "{}", .{fmtReplacer(in, '\\', '/')}); | ||
| 36 | } |
src/hashBytes.zig+17| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const string = []const u8; | 2 | const string = []const u8; |
| 3 | const extras = @import("./lib.zig"); | 3 | const extras = @import("./lib.zig"); |
| 4 | const rawInt = extras.rawInt; | ||
| 4 | 5 | ||
| 5 | pub fn hashBytes(comptime Algo: type, bytes: []const u8) [Algo.digest_length]u8 { | 6 | pub fn hashBytes(comptime Algo: type, bytes: []const u8) [Algo.digest_length]u8 { |
| 6 | var h = Algo.init(.{}); | 7 | var h = Algo.init(.{}); |
| ... | @@ -9,3 +10,19 @@ pub fn hashBytes(comptime Algo: type, bytes: []const u8) [Algo.digest_length]u8 | ... | @@ -9,3 +10,19 @@ pub fn hashBytes(comptime Algo: type, bytes: []const u8) [Algo.digest_length]u8 |
| 9 | h.final(&out); | 10 | h.final(&out); |
| 10 | return out; | 11 | return out; |
| 11 | } | 12 | } |
| 13 | |||
| 14 | test { | ||
| 15 | const A = std.crypto.hash.Md5; | ||
| 16 | try std.testing.expect(std.mem.eql(u8, &hashBytes(A, "hello"), &std.mem.toBytes(rawInt(u128, 0x5d41402abc4b2a76b9719d911017c592)))); | ||
| 17 | } | ||
| 18 | |||
| 19 | test { | ||
| 20 | const A = std.crypto.hash.Sha1; | ||
| 21 | // by default the array len is 24 for u160 | ||
| 22 | try std.testing.expect(std.mem.eql(u8, &hashBytes(A, "hello"), std.mem.toBytes(rawInt(u160, 0xaaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d))[0..20])); | ||
| 23 | } | ||
| 24 | |||
| 25 | test { | ||
| 26 | const A = std.crypto.hash.sha2.Sha256; | ||
| 27 | try std.testing.expect(std.mem.eql(u8, &hashBytes(A, "hello"), &std.mem.toBytes(rawInt(u256, 0x2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824)))); | ||
| 28 | } |
src/indexBufferT.zig+14-1| ... | @@ -1,11 +1,24 @@ | ... | @@ -1,11 +1,24 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const string = []const u8; | 2 | const string = []const u8; |
| 3 | const extras = @import("./lib.zig"); | 3 | const extras = @import("./lib.zig"); |
| 4 | const readType = extras.readType; | ||
| 5 | const rawInt = extras.rawInt; | ||
| 4 | 6 | ||
| 5 | pub fn indexBufferT(bytes: [*]const u8, comptime T: type, endian: std.builtin.Endian, idx: usize, max_len: usize) T { | 7 | pub fn indexBufferT(bytes: [*]const u8, comptime T: type, endian: std.builtin.Endian, idx: usize, max_len: usize) T { |
| 6 | std.debug.assert(idx < max_len); | 8 | std.debug.assert(idx < max_len); |
| 7 | var fbs = std.io.fixedBufferStream((bytes + (idx * @sizeOf(T)))[0..@sizeOf(T)]); | 9 | var fbs = std.io.fixedBufferStream((bytes + (idx * @sizeOf(T)))[0..@sizeOf(T)]); |
| 8 | return extras.readType(fbs.reader(), T, endian) catch |err| switch (err) { | 10 | return readType(fbs.reader(), T, endian) catch |err| switch (err) { |
| 9 | error.EndOfStream => unreachable, // assert above has been violated | 11 | error.EndOfStream => unreachable, // assert above has been violated |
| 10 | }; | 12 | }; |
| 11 | } | 13 | } |
| 14 | |||
| 15 | test { | ||
| 16 | const bytes = std.mem.toBytes(rawInt(u32, 0x4e5a7da9)); | ||
| 17 | try std.testing.expect(indexBufferT(&bytes, u16, .Big, 0, 2) == 0x4e5a); | ||
| 18 | try std.testing.expect(indexBufferT(&bytes, u16, .Big, 1, 2) == 0x7da9); | ||
| 19 | } | ||
| 20 | test { | ||
| 21 | const bytes = std.mem.toBytes(rawInt(u32, 0x4e5a7da9)); | ||
| 22 | try std.testing.expect(indexBufferT(&bytes, u16, .Little, 0, 2) == 0x5a4e); | ||
| 23 | try std.testing.expect(indexBufferT(&bytes, u16, .Little, 1, 2) == 0xa97d); | ||
| 24 | } |
src/is.zig+14| ... | @@ -4,6 +4,20 @@ const extras = @import("./lib.zig"); | ... | @@ -4,6 +4,20 @@ const extras = @import("./lib.zig"); |
| 4 | 4 | ||
| 5 | /// ?A == A fails | 5 | /// ?A == A fails |
| 6 | /// ?A == @as(?A, b) works | 6 | /// ?A == @as(?A, b) works |
| 7 | /// Sourced from https://github.com/ziglang/zig/issues/12609 | ||
| 7 | pub fn is(a: anytype, b: @TypeOf(a)) bool { | 8 | pub fn is(a: anytype, b: @TypeOf(a)) bool { |
| 8 | return a == b; | 9 | return a == b; |
| 9 | } | 10 | } |
| 11 | |||
| 12 | test { | ||
| 13 | try std.testing.expect(is(@as(u8, 5), 5)); | ||
| 14 | } | ||
| 15 | test { | ||
| 16 | try std.testing.expect(is(@as(?u8, 5), 5)); | ||
| 17 | } | ||
| 18 | test { | ||
| 19 | try std.testing.expect(!is(@as(u8, 5), 8)); | ||
| 20 | } | ||
| 21 | test { | ||
| 22 | try std.testing.expect(!is(@as(?u8, 5), 8)); | ||
| 23 | } |
src/lessThanSlice.zig+11| ... | @@ -3,6 +3,7 @@ const string = []const u8; | ... | @@ -3,6 +3,7 @@ const string = []const u8; |
| 3 | const extras = @import("./lib.zig"); | 3 | const extras = @import("./lib.zig"); |
| 4 | 4 | ||
| 5 | pub fn lessThanSlice(comptime T: type) fn (void, T, T) bool { | 5 | pub fn lessThanSlice(comptime T: type) fn (void, T, T) bool { |
| 6 | comptime std.debug.assert(std.meta.trait.isSlice(T)); | ||
| 6 | return struct { | 7 | return struct { |
| 7 | fn f(_: void, lhs: T, rhs: T) bool { | 8 | fn f(_: void, lhs: T, rhs: T) bool { |
| 8 | const result = for (0..@min(lhs.len, rhs.len)) |i| { | 9 | const result = for (0..@min(lhs.len, rhs.len)) |i| { |
| ... | @@ -13,3 +14,13 @@ pub fn lessThanSlice(comptime T: type) fn (void, T, T) bool { | ... | @@ -13,3 +14,13 @@ pub fn lessThanSlice(comptime T: type) fn (void, T, T) bool { |
| 13 | } | 14 | } |
| 14 | }.f; | 15 | }.f; |
| 15 | } | 16 | } |
| 17 | |||
| 18 | test { | ||
| 19 | try std.testing.expect(lessThanSlice(string)({}, "mouse", "possum")); | ||
| 20 | } | ||
| 21 | test { | ||
| 22 | try std.testing.expect(!lessThanSlice(string)({}, "mouse", "kangaroo")); | ||
| 23 | } | ||
| 24 | test { | ||
| 25 | try std.testing.expect(!lessThanSlice(string)({}, "mouse", "mouse")); | ||
| 26 | } |
src/ptrCast.zig+7| ... | @@ -6,3 +6,10 @@ pub fn ptrCast(comptime T: type, ptr: *anyopaque) *T { | ... | @@ -6,3 +6,10 @@ pub fn ptrCast(comptime T: type, ptr: *anyopaque) *T { |
| 6 | if (@alignOf(T) == 0) @compileError(@typeName(T)); | 6 | if (@alignOf(T) == 0) @compileError(@typeName(T)); |
| 7 | return @ptrCast(@alignCast(ptr)); | 7 | return @ptrCast(@alignCast(ptr)); |
| 8 | } | 8 | } |
| 9 | |||
| 10 | test { | ||
| 11 | var a: u16 = 42; | ||
| 12 | const b: *anyopaque = &a; | ||
| 13 | const c: *u16 = ptrCast(u16, b); | ||
| 14 | try std.testing.expect(a == c.*); | ||
| 15 | } |
src/ptrCastConst.zig+7| ... | @@ -6,3 +6,10 @@ pub fn ptrCastConst(comptime T: type, ptr: *const anyopaque) *const T { | ... | @@ -6,3 +6,10 @@ pub fn ptrCastConst(comptime T: type, ptr: *const anyopaque) *const T { |
| 6 | if (@alignOf(T) == 0) @compileError(@typeName(T)); | 6 | if (@alignOf(T) == 0) @compileError(@typeName(T)); |
| 7 | return @ptrCast(@alignCast(ptr)); | 7 | return @ptrCast(@alignCast(ptr)); |
| 8 | } | 8 | } |
| 9 | |||
| 10 | test { | ||
| 11 | const a: u16 = 42; | ||
| 12 | const b: *const anyopaque = &a; | ||
| 13 | const c: *const u16 = ptrCastConst(u16, b); | ||
| 14 | try std.testing.expect(a == c.*); | ||
| 15 | } |
src/randomBytes.zig+4| ... | @@ -7,3 +7,7 @@ pub fn randomBytes(comptime len: usize) [len]u8 { | ... | @@ -7,3 +7,7 @@ pub fn randomBytes(comptime len: usize) [len]u8 { |
| 7 | std.crypto.random.bytes(&bytes); | 7 | std.crypto.random.bytes(&bytes); |
| 8 | return bytes; | 8 | return bytes; |
| 9 | } | 9 | } |
| 10 | |||
| 11 | test { | ||
| 12 | std.testing.refAllDecls(@This()); | ||
| 13 | } |
src/readBytes.zig+4| ... | @@ -8,3 +8,7 @@ pub fn readBytes(reader: anytype, comptime len: usize) ![len]u8 { | ... | @@ -8,3 +8,7 @@ pub fn readBytes(reader: anytype, comptime len: usize) ![len]u8 { |
| 8 | assert(try reader.readAll(&bytes) == len); | 8 | assert(try reader.readAll(&bytes) == len); |
| 9 | return bytes; | 9 | return bytes; |
| 10 | } | 10 | } |
| 11 | |||
| 12 | test { | ||
| 13 | std.testing.refAllDecls(@This()); | ||
| 14 | } |
src/readBytesAlloc.zig+20-1| ... | @@ -3,10 +3,29 @@ const string = []const u8; | ... | @@ -3,10 +3,29 @@ const string = []const u8; |
| 3 | const extras = @import("./lib.zig"); | 3 | const extras = @import("./lib.zig"); |
| 4 | 4 | ||
| 5 | pub fn readBytesAlloc(reader: anytype, alloc: std.mem.Allocator, len: usize) ![]u8 { | 5 | pub fn readBytesAlloc(reader: anytype, alloc: std.mem.Allocator, len: usize) ![]u8 { |
| 6 | var list = std.ArrayListUnmanaged(u8){}; | 6 | var list = try std.ArrayListUnmanaged(u8).initCapacity(alloc, len); |
| 7 | try list.ensureTotalCapacityPrecise(alloc, len); | 7 | try list.ensureTotalCapacityPrecise(alloc, len); |
| 8 | errdefer list.deinit(alloc); | 8 | errdefer list.deinit(alloc); |
| 9 | list.appendNTimesAssumeCapacity(0, len); | 9 | list.appendNTimesAssumeCapacity(0, len); |
| 10 | try reader.readNoEof(list.items[0..len]); | 10 | try reader.readNoEof(list.items[0..len]); |
| 11 | return list.items; | 11 | return list.items; |
| 12 | } | 12 | } |
| 13 | |||
| 14 | test { | ||
| 15 | const array = [_]u8{ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; | ||
| 16 | var fba = std.io.fixedBufferStream(&array); | ||
| 17 | const reader = fba.reader(); | ||
| 18 | const alloc = std.testing.allocator; | ||
| 19 | const res = try readBytesAlloc(reader, alloc, 2); | ||
| 20 | defer alloc.free(res); | ||
| 21 | try std.testing.expect(res.len == 2); | ||
| 22 | try std.testing.expect(std.mem.eql(u8, res, &.{ 9, 8 })); | ||
| 23 | } | ||
| 24 | |||
| 25 | test { | ||
| 26 | const array = [_]u8{ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; | ||
| 27 | var fba = std.io.fixedBufferStream(&array); | ||
| 28 | const reader = fba.reader(); | ||
| 29 | const alloc = std.testing.allocator; | ||
| 30 | try std.testing.expect(readBytesAlloc(reader, alloc, 11) == error.EndOfStream); | ||
| 31 | } |
src/readExpected.zig+4| ... | @@ -11,3 +11,7 @@ pub fn readExpected(reader: anytype, expected: []const u8) !bool { | ... | @@ -11,3 +11,7 @@ pub fn readExpected(reader: anytype, expected: []const u8) !bool { |
| 11 | } | 11 | } |
| 12 | return true; | 12 | return true; |
| 13 | } | 13 | } |
| 14 | |||
| 15 | test { | ||
| 16 | std.testing.refAllDecls(@This()); | ||
| 17 | } |
src/readType.zig+64| ... | @@ -1,6 +1,9 @@ | ... | @@ -1,6 +1,9 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const string = []const u8; | 2 | const string = []const u8; |
| 3 | const extras = @import("./lib.zig"); | 3 | const extras = @import("./lib.zig"); |
| 4 | const builtin = @import("builtin"); | ||
| 5 | const native_endian = builtin.target.cpu.arch.endian(); | ||
| 6 | const rawInt = extras.rawInt; | ||
| 4 | 7 | ||
| 5 | pub fn readType(reader: anytype, comptime T: type, endian: std.builtin.Endian) !T { | 8 | pub fn readType(reader: anytype, comptime T: type, endian: std.builtin.Endian) !T { |
| 6 | if (T == u8) return reader.readByte(); // single bytes dont have an endianness | 9 | if (T == u8) return reader.readByte(); // single bytes dont have an endianness |
| ... | @@ -29,3 +32,64 @@ pub fn readType(reader: anytype, comptime T: type, endian: std.builtin.Endian) ! | ... | @@ -29,3 +32,64 @@ pub fn readType(reader: anytype, comptime T: type, endian: std.builtin.Endian) ! |
| 29 | else => |e| @compileError(@tagName(e)), | 32 | else => |e| @compileError(@tagName(e)), |
| 30 | }; | 33 | }; |
| 31 | } | 34 | } |
| 35 | |||
| 36 | test { | ||
| 37 | const bytes = std.mem.toBytes(@as(u32, 0x4e5a7da9)); | ||
| 38 | var fba = std.io.fixedBufferStream(&bytes); | ||
| 39 | const I = u32; | ||
| 40 | const i = try readType(fba.reader(), I, native_endian); | ||
| 41 | try std.testing.expect(i == 1314553257); | ||
| 42 | } | ||
| 43 | |||
| 44 | test { | ||
| 45 | const bytes = std.mem.toBytes(@as(u32, 0x4e5a7da9)); | ||
| 46 | var fba = std.io.fixedBufferStream(&bytes); | ||
| 47 | const E = enum(u32) { | ||
| 48 | a, | ||
| 49 | b, | ||
| 50 | c, | ||
| 51 | d = 0x4e5a7da9, | ||
| 52 | e, | ||
| 53 | }; | ||
| 54 | const e = try readType(fba.reader(), E, native_endian); | ||
| 55 | try std.testing.expect(e == .d); | ||
| 56 | } | ||
| 57 | |||
| 58 | test { | ||
| 59 | const bytes = std.mem.toBytes(rawInt(u32, 0x4e5a7da9)); | ||
| 60 | var fba = std.io.fixedBufferStream(&bytes); | ||
| 61 | const S = struct { | ||
| 62 | a: u16, | ||
| 63 | b: u8, | ||
| 64 | c: u8, | ||
| 65 | }; | ||
| 66 | const s = try readType(fba.reader(), S, .Big); | ||
| 67 | try std.testing.expect(s.a == 0x4e5a); | ||
| 68 | try std.testing.expect(s.b == 0x7d); | ||
| 69 | try std.testing.expect(s.c == 0xa9); | ||
| 70 | } | ||
| 71 | |||
| 72 | test { | ||
| 73 | const bytes = std.mem.toBytes(rawInt(u32, 0x4e5a7da9)); | ||
| 74 | var fba = std.io.fixedBufferStream(&bytes); | ||
| 75 | const A = [2]u16; | ||
| 76 | const a = try readType(fba.reader(), A, .Big); | ||
| 77 | try std.testing.expect(a[0] == 0x4e5a); | ||
| 78 | try std.testing.expect(a[1] == 0x7da9); | ||
| 79 | } | ||
| 80 | |||
| 81 | test { | ||
| 82 | const bytes = std.mem.toBytes(rawInt(u32, 0x4e5a7da9)); | ||
| 83 | var fba = std.io.fixedBufferStream(&bytes); | ||
| 84 | const S = packed struct { | ||
| 85 | a: u16, | ||
| 86 | b: u4, | ||
| 87 | c: u4, | ||
| 88 | d: u8, | ||
| 89 | }; | ||
| 90 | const s = try readType(fba.reader(), S, .Big); | ||
| 91 | try std.testing.expect(s.a == 0x7da9); | ||
| 92 | try std.testing.expect(s.b == 0xa); | ||
| 93 | try std.testing.expect(s.c == 0x5); | ||
| 94 | try std.testing.expect(s.d == 0x4e); | ||
| 95 | } |
src/safeAdd.zig+9-1| ... | @@ -1,11 +1,19 @@ | ... | @@ -1,11 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const string = []const u8; | 2 | const string = []const u8; |
| 3 | const extras = @import("./lib.zig"); | 3 | const extras = @import("./lib.zig"); |
| 4 | const OneBiggerInt = extras.OneBiggerInt; | ||
| 4 | 5 | ||
| 5 | /// Allows u32 + i16 to work | 6 | /// Allows u32 + i16 to work |
| 6 | pub fn safeAdd(a: anytype, b: anytype) @TypeOf(a) { | 7 | pub fn safeAdd(a: anytype, b: anytype) @TypeOf(a) { |
| 7 | if (b >= 0) { | 8 | if (b >= 0) { |
| 8 | return a + @as(@TypeOf(a), @intCast(b)); | 9 | return a + @as(@TypeOf(a), @intCast(b)); |
| 9 | } | 10 | } |
| 10 | return a - @as(@TypeOf(a), @intCast(-@as(extras.OneBiggerInt(@TypeOf(b)), b))); | 11 | return a - @as(@TypeOf(a), @intCast(-@as(OneBiggerInt(@TypeOf(b)), b))); |
| 12 | } | ||
| 13 | |||
| 14 | test { | ||
| 15 | try std.testing.expect(safeAdd(@as(u16, 10), @as(i8, -2)) == 8); | ||
| 16 | } | ||
| 17 | test { | ||
| 18 | try std.testing.expect(safeAdd(@as(u16, 256), @as(i8, -128)) == 128); | ||
| 11 | } | 19 | } |
src/safeAddWrap.zig+15-1| ... | @@ -1,11 +1,25 @@ | ... | @@ -1,11 +1,25 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const string = []const u8; | 2 | const string = []const u8; |
| 3 | const extras = @import("./lib.zig"); | 3 | const extras = @import("./lib.zig"); |
| 4 | const OneBiggerInt = extras.OneBiggerInt; | ||
| 4 | 5 | ||
| 5 | /// Allows u32 +% i16 to work | 6 | /// Allows u32 +% i16 to work |
| 6 | pub fn safeAddWrap(a: anytype, b: anytype) @TypeOf(a) { | 7 | pub fn safeAddWrap(a: anytype, b: anytype) @TypeOf(a) { |
| 7 | if (b >= 0) { | 8 | if (b >= 0) { |
| 8 | return a +% @as(@TypeOf(a), @intCast(b)); | 9 | return a +% @as(@TypeOf(a), @intCast(b)); |
| 9 | } | 10 | } |
| 10 | return a -% @as(@TypeOf(a), @intCast(-@as(extras.OneBiggerInt(@TypeOf(b)), b))); | 11 | return a -% @as(@TypeOf(a), @intCast(-@as(OneBiggerInt(@TypeOf(b)), b))); |
| 12 | } | ||
| 13 | |||
| 14 | test { | ||
| 15 | try std.testing.expect(safeAddWrap(@as(u16, 0), @as(i8, -2)) == 65534); | ||
| 16 | } | ||
| 17 | test { | ||
| 18 | try std.testing.expect(safeAddWrap(@as(u16, 10), @as(i8, -2)) == 8); | ||
| 19 | } | ||
| 20 | test { | ||
| 21 | try std.testing.expect(safeAddWrap(@as(u16, 65535), @as(i8, 1)) == 0); | ||
| 22 | } | ||
| 23 | test { | ||
| 24 | try std.testing.expect(safeAddWrap(@as(u16, 256), @as(i8, -128)) == 128); | ||
| 11 | } | 25 | } |
src/skipToBoundary.zig+50| ... | @@ -9,3 +9,53 @@ pub fn skipToBoundary(pos: u64, boundary: u64, reader: anytype) !void { | ... | @@ -9,3 +9,53 @@ pub fn skipToBoundary(pos: u64, boundary: u64, reader: anytype) !void { |
| 9 | const b = boundary; | 9 | const b = boundary; |
| 10 | try reader.skipBytes(((a + (b - 1)) & ~(b - 1)) - a, .{}); | 10 | try reader.skipBytes(((a + (b - 1)) & ~(b - 1)) - a, .{}); |
| 11 | } | 11 | } |
| 12 | |||
| 13 | test { | ||
| 14 | const array = [_]u8{ 5, 4, 3, 2, 1, 0 }; | ||
| 15 | var fba = std.io.fixedBufferStream(&array); | ||
| 16 | var counter = std.io.countingReader(fba.reader()); | ||
| 17 | const reader = counter.reader(); | ||
| 18 | try reader.skipBytes(0, .{}); | ||
| 19 | try skipToBoundary(counter.bytes_read, 4, reader); | ||
| 20 | try std.testing.expect(counter.bytes_read == 0); | ||
| 21 | } | ||
| 22 | |||
| 23 | test { | ||
| 24 | const array = [_]u8{ 5, 4, 3, 2, 1, 0 }; | ||
| 25 | var fba = std.io.fixedBufferStream(&array); | ||
| 26 | var counter = std.io.countingReader(fba.reader()); | ||
| 27 | const reader = counter.reader(); | ||
| 28 | try reader.skipBytes(1, .{}); | ||
| 29 | try skipToBoundary(counter.bytes_read, 4, reader); | ||
| 30 | try std.testing.expect(counter.bytes_read == 4); | ||
| 31 | } | ||
| 32 | |||
| 33 | test { | ||
| 34 | const array = [_]u8{ 5, 4, 3, 2, 1, 0 }; | ||
| 35 | var fba = std.io.fixedBufferStream(&array); | ||
| 36 | var counter = std.io.countingReader(fba.reader()); | ||
| 37 | const reader = counter.reader(); | ||
| 38 | try reader.skipBytes(2, .{}); | ||
| 39 | try skipToBoundary(counter.bytes_read, 4, reader); | ||
| 40 | try std.testing.expect(counter.bytes_read == 4); | ||
| 41 | } | ||
| 42 | |||
| 43 | test { | ||
| 44 | const array = [_]u8{ 5, 4, 3, 2, 1, 0 }; | ||
| 45 | var fba = std.io.fixedBufferStream(&array); | ||
| 46 | var counter = std.io.countingReader(fba.reader()); | ||
| 47 | const reader = counter.reader(); | ||
| 48 | try reader.skipBytes(3, .{}); | ||
| 49 | try skipToBoundary(counter.bytes_read, 4, reader); | ||
| 50 | try std.testing.expect(counter.bytes_read == 4); | ||
| 51 | } | ||
| 52 | |||
| 53 | test { | ||
| 54 | const array = [_]u8{ 5, 4, 3, 2, 1, 0 }; | ||
| 55 | var fba = std.io.fixedBufferStream(&array); | ||
| 56 | var counter = std.io.countingReader(fba.reader()); | ||
| 57 | const reader = counter.reader(); | ||
| 58 | try reader.skipBytes(4, .{}); | ||
| 59 | try skipToBoundary(counter.bytes_read, 4, reader); | ||
| 60 | try std.testing.expect(counter.bytes_read == 4); | ||
| 61 | } |
src/sortBy.zig+21| ... | @@ -9,3 +9,24 @@ pub fn sortBy(comptime T: type, items: []T, comptime field: std.meta.FieldEnum(T | ... | @@ -9,3 +9,24 @@ pub fn sortBy(comptime T: type, items: []T, comptime field: std.meta.FieldEnum(T |
| 9 | } | 9 | } |
| 10 | }.f); | 10 | }.f); |
| 11 | } | 11 | } |
| 12 | |||
| 13 | test { | ||
| 14 | const Block = struct { | ||
| 15 | from: u21, | ||
| 16 | to: u21, | ||
| 17 | name: []const u8, | ||
| 18 | }; | ||
| 19 | var data = [_]Block{ | ||
| 20 | .{ .from = 0x0000, .to = 0x007F, .name = "Basic Latin" }, | ||
| 21 | .{ .from = 0x0250, .to = 0x02AF, .name = "IPA Extensions" }, | ||
| 22 | .{ .from = 0x0100, .to = 0x017F, .name = "Latin Extended-A" }, | ||
| 23 | .{ .from = 0x0180, .to = 0x024F, .name = "Latin Extended-B" }, | ||
| 24 | .{ .from = 0x0080, .to = 0x00FF, .name = "Latin-1 Supplement" }, | ||
| 25 | }; | ||
| 26 | sortBy(Block, &data, .from); | ||
| 27 | try std.testing.expect(std.mem.eql(u8, data[0].name, "Basic Latin")); | ||
| 28 | try std.testing.expect(std.mem.eql(u8, data[1].name, "Latin-1 Supplement")); | ||
| 29 | try std.testing.expect(std.mem.eql(u8, data[2].name, "Latin Extended-A")); | ||
| 30 | try std.testing.expect(std.mem.eql(u8, data[3].name, "Latin Extended-B")); | ||
| 31 | try std.testing.expect(std.mem.eql(u8, data[4].name, "IPA Extensions")); | ||
| 32 | } |
src/sortBySlice.zig+23-1| ... | @@ -1,11 +1,33 @@ | ... | @@ -1,11 +1,33 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const string = []const u8; | 2 | const string = []const u8; |
| 3 | const extras = @import("./lib.zig"); | 3 | const extras = @import("./lib.zig"); |
| 4 | const lessThanSlice = extras.lessThanSlice; | ||
| 4 | 5 | ||
| 5 | pub fn sortBySlice(comptime T: type, items: []T, comptime field: std.meta.FieldEnum(T)) void { | 6 | pub fn sortBySlice(comptime T: type, items: []T, comptime field: std.meta.FieldEnum(T)) void { |
| 6 | std.mem.sort(T, items, {}, struct { | 7 | std.mem.sort(T, items, {}, struct { |
| 7 | fn f(_: void, lhs: T, rhs: T) bool { | 8 | fn f(_: void, lhs: T, rhs: T) bool { |
| 8 | return extras.lessThanSlice(std.meta.FieldType(T, field))({}, @field(lhs, @tagName(field)), @field(rhs, @tagName(field))); | 9 | return lessThanSlice(std.meta.FieldType(T, field))({}, @field(lhs, @tagName(field)), @field(rhs, @tagName(field))); |
| 9 | } | 10 | } |
| 10 | }.f); | 11 | }.f); |
| 11 | } | 12 | } |
| 13 | |||
| 14 | test { | ||
| 15 | const Block = struct { | ||
| 16 | from: u21, | ||
| 17 | to: u21, | ||
| 18 | name: []const u8, | ||
| 19 | }; | ||
| 20 | var data = [_]Block{ | ||
| 21 | .{ .from = 0x0000, .to = 0x007F, .name = "Basic Latin" }, | ||
| 22 | .{ .from = 0x0080, .to = 0x00FF, .name = "Latin-1 Supplement" }, | ||
| 23 | .{ .from = 0x0180, .to = 0x024F, .name = "Latin Extended-B" }, | ||
| 24 | .{ .from = 0x0100, .to = 0x017F, .name = "Latin Extended-A" }, | ||
| 25 | .{ .from = 0x0250, .to = 0x02AF, .name = "IPA Extensions" }, | ||
| 26 | }; | ||
| 27 | sortBySlice(Block, &data, .name); | ||
| 28 | try std.testing.expect(std.mem.eql(u8, data[0].name, "Basic Latin")); | ||
| 29 | try std.testing.expect(std.mem.eql(u8, data[1].name, "IPA Extensions")); | ||
| 30 | try std.testing.expect(std.mem.eql(u8, data[2].name, "Latin Extended-A")); | ||
| 31 | try std.testing.expect(std.mem.eql(u8, data[3].name, "Latin Extended-B")); | ||
| 32 | try std.testing.expect(std.mem.eql(u8, data[4].name, "Latin-1 Supplement")); | ||
| 33 | } |