authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2024-01-28 00:39:00 -08:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2024-01-28 00:39:00 -08:00
log3b1ee55372343807515c6a776f211471323804b7
tree8ab4b9216a76be7b5f659e406e5b6d03fd50be6d
parente8f81eb1d126e37dc5b57e796fd8ee5e10123312

add more tests


23 files changed, 387 insertions(+), 14 deletions(-)

src/BufIndexer.zig+39-1
...@@ -1,6 +1,8 @@...@@ -1,6 +1,8 @@
1const std = @import("std");1const std = @import("std");
2const string = []const u8;2const string = []const u8;
3const extras = @import("./lib.zig");3const extras = @import("./lib.zig");
4const indexBufferT = extras.indexBufferT;
5const rawInt = extras.rawInt;
46
5pub fn BufIndexer(comptime T: type, comptime endian: std.builtin.Endian) type {7pub 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 }
1820
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
28test {
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
34test {
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
41test {
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
50test {
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
50test {
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");
44
5pub fn TagNameJsonStringifyMixin(comptime S: type) type {5pub 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
13test {
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
14test {
15 try std.testing.expect(containsString(&.{ "a", "b", "c", "d" }, "b"));
16}
17test {
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
16test {
17 try std.testing.expect(countScalar(u8, "The lazy brown fox jumped over the lazy dog.", ' ') == 8);
18}
19test {
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");
2const string = []const u8;2const string = []const u8;
3const extras = @import("./lib.zig");3const extras = @import("./lib.zig");
44
5/// Asserts at comptime that L ⊆ R.
5pub fn ensureFieldSubset(comptime L: type, comptime R: type) void {6pub 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
14test {
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}
1212
13const FormatData = struct {
14 bytes: string,
15 from: u8,
16 to: u8,
17};
18
13fn formatReplacer(19fn 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
32test {
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 @@
1const std = @import("std");1const std = @import("std");
2const string = []const u8;2const string = []const u8;
3const extras = @import("./lib.zig");3const extras = @import("./lib.zig");
4const rawInt = extras.rawInt;
45
5pub fn hashBytes(comptime Algo: type, bytes: []const u8) [Algo.digest_length]u8 {6pub 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
14test {
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
19test {
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
25test {
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 @@
1const std = @import("std");1const std = @import("std");
2const string = []const u8;2const string = []const u8;
3const extras = @import("./lib.zig");3const extras = @import("./lib.zig");
4const readType = extras.readType;
5const rawInt = extras.rawInt;
46
5pub fn indexBufferT(bytes: [*]const u8, comptime T: type, endian: std.builtin.Endian, idx: usize, max_len: usize) T {7pub 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 violated11 error.EndOfStream => unreachable, // assert above has been violated
10 };12 };
11}13}
14
15test {
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}
20test {
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");
44
5/// ?A == A fails5/// ?A == A fails
6/// ?A == @as(?A, b) works6/// ?A == @as(?A, b) works
7/// Sourced from https://github.com/ziglang/zig/issues/12609
7pub fn is(a: anytype, b: @TypeOf(a)) bool {8pub fn is(a: anytype, b: @TypeOf(a)) bool {
8 return a == b;9 return a == b;
9}10}
11
12test {
13 try std.testing.expect(is(@as(u8, 5), 5));
14}
15test {
16 try std.testing.expect(is(@as(?u8, 5), 5));
17}
18test {
19 try std.testing.expect(!is(@as(u8, 5), 8));
20}
21test {
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;
3const extras = @import("./lib.zig");3const extras = @import("./lib.zig");
44
5pub fn lessThanSlice(comptime T: type) fn (void, T, T) bool {5pub 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
18test {
19 try std.testing.expect(lessThanSlice(string)({}, "mouse", "possum"));
20}
21test {
22 try std.testing.expect(!lessThanSlice(string)({}, "mouse", "kangaroo"));
23}
24test {
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
10test {
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
10test {
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
11test {
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
12test {
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;
3const extras = @import("./lib.zig");3const extras = @import("./lib.zig");
44
5pub fn readBytesAlloc(reader: anytype, alloc: std.mem.Allocator, len: usize) ![]u8 {5pub 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
14test {
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
25test {
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
15test {
16 std.testing.refAllDecls(@This());
17}
src/readType.zig+64
...@@ -1,6 +1,9 @@...@@ -1,6 +1,9 @@
1const std = @import("std");1const std = @import("std");
2const string = []const u8;2const string = []const u8;
3const extras = @import("./lib.zig");3const extras = @import("./lib.zig");
4const builtin = @import("builtin");
5const native_endian = builtin.target.cpu.arch.endian();
6const rawInt = extras.rawInt;
47
5pub fn readType(reader: anytype, comptime T: type, endian: std.builtin.Endian) !T {8pub fn readType(reader: anytype, comptime T: type, endian: std.builtin.Endian) !T {
6 if (T == u8) return reader.readByte(); // single bytes dont have an endianness9 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
36test {
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
44test {
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
58test {
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
72test {
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
81test {
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 @@
1const std = @import("std");1const std = @import("std");
2const string = []const u8;2const string = []const u8;
3const extras = @import("./lib.zig");3const extras = @import("./lib.zig");
4const OneBiggerInt = extras.OneBiggerInt;
45
5/// Allows u32 + i16 to work6/// Allows u32 + i16 to work
6pub fn safeAdd(a: anytype, b: anytype) @TypeOf(a) {7pub 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
14test {
15 try std.testing.expect(safeAdd(@as(u16, 10), @as(i8, -2)) == 8);
16}
17test {
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 @@
1const std = @import("std");1const std = @import("std");
2const string = []const u8;2const string = []const u8;
3const extras = @import("./lib.zig");3const extras = @import("./lib.zig");
4const OneBiggerInt = extras.OneBiggerInt;
45
5/// Allows u32 +% i16 to work6/// Allows u32 +% i16 to work
6pub fn safeAddWrap(a: anytype, b: anytype) @TypeOf(a) {7pub 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
14test {
15 try std.testing.expect(safeAddWrap(@as(u16, 0), @as(i8, -2)) == 65534);
16}
17test {
18 try std.testing.expect(safeAddWrap(@as(u16, 10), @as(i8, -2)) == 8);
19}
20test {
21 try std.testing.expect(safeAddWrap(@as(u16, 65535), @as(i8, 1)) == 0);
22}
23test {
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
13test {
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
23test {
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
33test {
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
43test {
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
53test {
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
13test {
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 @@
1const std = @import("std");1const std = @import("std");
2const string = []const u8;2const string = []const u8;
3const extras = @import("./lib.zig");3const extras = @import("./lib.zig");
4const lessThanSlice = extras.lessThanSlice;
45
5pub fn sortBySlice(comptime T: type, items: []T, comptime field: std.meta.FieldEnum(T)) void {6pub 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
14test {
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}