| author | |
| committer | |
| log | 8ac9d9061a70f1b0e234daa918685cdf49bd5c4a |
| tree | 30c36d9af37131b82868715a42216f076de2d8b7 |
| parent | 14f3f971a731b78c854ac2dfdadbdd4f81ffe796 |
68 files changed, 1082 insertions(+), 825 deletions(-)
src/AnyReader.zig created+123| ... | ... | @@ -0,0 +1,123 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | const assert = std.debug.assert; | |
| 5 | ||
| 6 | pub const AnyReader = struct { | |
| 7 | readFn: *const fn (*anyopaque, []u8) anyerror!usize, | |
| 8 | state: *anyopaque, | |
| 9 | ||
| 10 | pub fn from(reader: anytype) AnyReader { | |
| 11 | const R = @TypeOf(reader); | |
| 12 | const ctx = reader.context; | |
| 13 | const Ctx = @TypeOf(ctx); | |
| 14 | switch (@typeInfo(Ctx)) { | |
| 15 | .Pointer => { | |
| 16 | const S = struct { | |
| 17 | fn foo(s: *anyopaque, buffer: []u8) anyerror!usize { | |
| 18 | const r = R{ .context = @ptrCast(@alignCast(s)) }; | |
| 19 | return r.read(buffer); | |
| 20 | } | |
| 21 | }; | |
| 22 | return .{ | |
| 23 | .readFn = S.foo, | |
| 24 | .state = ctx, | |
| 25 | }; | |
| 26 | }, | |
| 27 | .Struct => switch (R) { | |
| 28 | std.fs.File.Reader => { | |
| 29 | const S = struct { | |
| 30 | fn foo(s: *anyopaque, buffer: []u8) anyerror!usize { | |
| 31 | const r = R{ .context = .{ .handle = @intCast(@intFromPtr(s)) } }; | |
| 32 | return r.read(buffer); | |
| 33 | } | |
| 34 | }; | |
| 35 | return .{ | |
| 36 | .readFn = S.foo, | |
| 37 | .state = @ptrFromInt(@as(usize, @intCast(ctx.handle))), | |
| 38 | }; | |
| 39 | }, | |
| 40 | else => @compileError(@typeName(R)), | |
| 41 | }, | |
| 42 | else => |v| @compileError(@typeName(R) ++ " , " ++ @tagName(v)), | |
| 43 | } | |
| 44 | } | |
| 45 | ||
| 46 | pub fn read(r: AnyReader, buffer: []u8) anyerror!usize { | |
| 47 | return r.readFn(r.state, buffer); | |
| 48 | } | |
| 49 | ||
| 50 | pub fn readByte(self: AnyReader) !u8 { | |
| 51 | var result: [1]u8 = undefined; | |
| 52 | const amt_read = try self.read(result[0..]); | |
| 53 | if (amt_read < 1) return error.EndOfStream; | |
| 54 | return result[0]; | |
| 55 | } | |
| 56 | ||
| 57 | pub fn readInt(self: AnyReader, comptime T: type, endian: std.builtin.Endian) !T { | |
| 58 | const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))); | |
| 59 | return std.mem.readInt(T, &bytes, endian); | |
| 60 | } | |
| 61 | ||
| 62 | pub fn readBytesNoEof(self: AnyReader, comptime num_bytes: usize) ![num_bytes]u8 { | |
| 63 | var bytes: [num_bytes]u8 = undefined; | |
| 64 | try self.readNoEof(&bytes); | |
| 65 | return bytes; | |
| 66 | } | |
| 67 | ||
| 68 | pub fn readNoEof(self: AnyReader, buf: []u8) !void { | |
| 69 | const amt_read = try self.readAll(buf); | |
| 70 | if (amt_read < buf.len) return error.EndOfStream; | |
| 71 | } | |
| 72 | ||
| 73 | pub fn readAll(self: AnyReader, buffer: []u8) !usize { | |
| 74 | return self.readAtLeast(buffer, buffer.len); | |
| 75 | } | |
| 76 | ||
| 77 | pub fn readAtLeast(self: AnyReader, buffer: []u8, len: usize) !usize { | |
| 78 | assert(len <= buffer.len); | |
| 79 | var index: usize = 0; | |
| 80 | while (index < len) { | |
| 81 | const amt = try self.read(buffer[index..]); | |
| 82 | if (amt == 0) break; | |
| 83 | index += amt; | |
| 84 | } | |
| 85 | return index; | |
| 86 | } | |
| 87 | ||
| 88 | pub fn readAllAlloc(self: AnyReader, allocator: std.mem.Allocator, max_size: usize) ![]u8 { | |
| 89 | var array_list = std.ArrayList(u8).init(allocator); | |
| 90 | defer array_list.deinit(); | |
| 91 | try self.readAllArrayList(&array_list, max_size); | |
| 92 | return try array_list.toOwnedSlice(); | |
| 93 | } | |
| 94 | ||
| 95 | pub fn readAllArrayList(self: AnyReader, array_list: *std.ArrayList(u8), max_append_size: usize) !void { | |
| 96 | return self.readAllArrayListAligned(null, array_list, max_append_size); | |
| 97 | } | |
| 98 | ||
| 99 | pub fn readAllArrayListAligned(self: AnyReader, comptime alignment: ?u29, array_list: *std.ArrayListAligned(u8, alignment), max_append_size: usize) !void { | |
| 100 | try array_list.ensureTotalCapacity(@min(max_append_size, 4096)); | |
| 101 | const original_len = array_list.items.len; | |
| 102 | var start_index: usize = original_len; | |
| 103 | while (true) { | |
| 104 | array_list.expandToCapacity(); | |
| 105 | const dest_slice = array_list.items[start_index..]; | |
| 106 | const bytes_read = try self.readAll(dest_slice); | |
| 107 | start_index += bytes_read; | |
| 108 | ||
| 109 | if (start_index - original_len > max_append_size) { | |
| 110 | array_list.shrinkAndFree(original_len + max_append_size); | |
| 111 | return error.StreamTooLong; | |
| 112 | } | |
| 113 | ||
| 114 | if (bytes_read != dest_slice.len) { | |
| 115 | array_list.shrinkAndFree(start_index); | |
| 116 | return; | |
| 117 | } | |
| 118 | ||
| 119 | // This will trigger ArrayList to expand superlinearly at whatever its growth rate is. | |
| 120 | try array_list.ensureTotalCapacity(start_index + 1); | |
| 121 | } | |
| 122 | } | |
| 123 | }; |
src/BufIndexer.zig created+23| ... | ... | @@ -0,0 +1,23 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn BufIndexer(comptime T: type, comptime endian: std.builtin.Endian) type { | |
| 6 | return struct { | |
| 7 | bytes: [*]const u8, | |
| 8 | max_len: usize, | |
| 9 | ||
| 10 | const Self = @This(); | |
| 11 | ||
| 12 | pub fn init(bytes: [*]const u8, max_len: usize) Self { | |
| 13 | return .{ | |
| 14 | .bytes = bytes, | |
| 15 | .max_len = max_len, | |
| 16 | }; | |
| 17 | } | |
| 18 | ||
| 19 | pub fn at(self: *const Self, idx: usize) T { | |
| 20 | return extras.indexBufferT(self.bytes, T, endian, idx, self.max_len); | |
| 21 | } | |
| 22 | }; | |
| 23 | } |
src/FieldUnion.zig created+22| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn FieldUnion(comptime T: type) type { | |
| 6 | const infos = std.meta.fields(T); | |
| 7 | ||
| 8 | var fields: [infos.len]std.builtin.Type.UnionField = undefined; | |
| 9 | inline for (infos, 0..) |field, i| { | |
| 10 | fields[i] = .{ | |
| 11 | .name = field.name, | |
| 12 | .type = field.type, | |
| 13 | .alignment = field.alignment, | |
| 14 | }; | |
| 15 | } | |
| 16 | return @Type(std.builtin.Type{ .Union = .{ | |
| 17 | .layout = .Auto, | |
| 18 | .tag_type = std.meta.FieldEnum(T), | |
| 19 | .fields = &fields, | |
| 20 | .decls = &.{}, | |
| 21 | } }); | |
| 22 | } |
src/FixedMaxBuffer.zig created+48| ... | ... | @@ -0,0 +1,48 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | const assert = std.debug.assert; | |
| 5 | ||
| 6 | pub fn FixedMaxBuffer(comptime max_len: usize) type { | |
| 7 | return struct { | |
| 8 | buf: [max_len]u8, | |
| 9 | len: usize, | |
| 10 | pos: usize, | |
| 11 | ||
| 12 | const Self = @This(); | |
| 13 | pub const Reader = std.io.Reader(*Self, error{}, read); | |
| 14 | ||
| 15 | pub fn init(r: anytype, runtime_len: usize) !Self { | |
| 16 | var fmr = Self{ | |
| 17 | .buf = undefined, | |
| 18 | .len = runtime_len, | |
| 19 | .pos = 0, | |
| 20 | }; | |
| 21 | _ = try r.readAll(fmr.buf[0..runtime_len]); | |
| 22 | return fmr; | |
| 23 | } | |
| 24 | ||
| 25 | pub fn reader(self: *Self) Reader { | |
| 26 | return .{ .context = self }; | |
| 27 | } | |
| 28 | ||
| 29 | fn read(self: *Self, dest: []u8) error{}!usize { | |
| 30 | const buf = self.buf[0..self.len]; | |
| 31 | const size = @min(dest.len, buf.len - self.pos); | |
| 32 | const end = self.pos + size; | |
| 33 | std.mem.copy(u8, dest[0..size], buf[self.pos..end]); | |
| 34 | self.pos = end; | |
| 35 | return size; | |
| 36 | } | |
| 37 | ||
| 38 | pub fn readLen(self: *Self, len: usize) []const u8 { | |
| 39 | assert(self.pos + len <= self.len); | |
| 40 | defer self.pos += len; | |
| 41 | return self.buf[self.pos..][0..len]; | |
| 42 | } | |
| 43 | ||
| 44 | pub fn atEnd(self: *const Self) bool { | |
| 45 | return self.pos == self.len; | |
| 46 | } | |
| 47 | }; | |
| 48 | } |
src/LoggingReader.zig created+30| ... | ... | @@ -0,0 +1,30 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn LoggingReader(comptime T: type, comptime scope: @Type(.EnumLiteral)) type { | |
| 6 | return struct { | |
| 7 | child_stream: T, | |
| 8 | ||
| 9 | pub const Error = T.Error; | |
| 10 | pub const Reader = std.io.Reader(Self, Error, read); | |
| 11 | ||
| 12 | const Self = @This(); | |
| 13 | ||
| 14 | pub fn init(child_stream: T) Self { | |
| 15 | return .{ | |
| 16 | .child_stream = child_stream, | |
| 17 | }; | |
| 18 | } | |
| 19 | ||
| 20 | pub fn reader(self: Self) Reader { | |
| 21 | return .{ .context = self }; | |
| 22 | } | |
| 23 | ||
| 24 | fn read(self: Self, dest: []u8) Error!usize { | |
| 25 | const n = try self.child_stream.read(dest); | |
| 26 | std.log.scoped(scope).debug("{s}", .{dest[0..n]}); | |
| 27 | return n; | |
| 28 | } | |
| 29 | }; | |
| 30 | } |
src/LoggingWriter.zig created+29| ... | ... | @@ -0,0 +1,29 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn LoggingWriter(comptime T: type, comptime scope: @Type(.EnumLiteral)) type { | |
| 6 | return struct { | |
| 7 | child_stream: T, | |
| 8 | ||
| 9 | pub const Error = T.Error; | |
| 10 | pub const Writer = std.io.Writer(Self, Error, write); | |
| 11 | ||
| 12 | const Self = @This(); | |
| 13 | ||
| 14 | pub fn init(child_stream: T) Self { | |
| 15 | return .{ | |
| 16 | .child_stream = child_stream, | |
| 17 | }; | |
| 18 | } | |
| 19 | ||
| 20 | pub fn writer(self: Self) Writer { | |
| 21 | return .{ .context = self }; | |
| 22 | } | |
| 23 | ||
| 24 | fn write(self: Self, bytes: []const u8) Error!usize { | |
| 25 | std.log.scoped(scope).debug("{s}", .{bytes}); | |
| 26 | return self.child_stream.write(bytes); | |
| 27 | } | |
| 28 | }; | |
| 29 | } |
src/OneBiggerInt.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn OneBiggerInt(comptime T: type) type { | |
| 6 | var info = @typeInfo(T); | |
| 7 | info.Int.bits += 1; | |
| 8 | return @Type(info); | |
| 9 | } |
src/Partial.zig created+24| ... | ... | @@ -0,0 +1,24 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn Partial(comptime T: type) type { | |
| 6 | const fields_before = std.meta.fields(T); | |
| 7 | var fields_after: [fields_before.len]std.builtin.Type.StructField = undefined; | |
| 8 | inline for (fields_before, 0..) |item, i| { | |
| 9 | fields_after[i] = std.builtin.Type.StructField{ | |
| 10 | .name = item.name, | |
| 11 | .type = ?item.type, | |
| 12 | .default_value = &@as(?item.type, null), | |
| 13 | .is_comptime = false, | |
| 14 | .alignment = @alignOf(?item.type), | |
| 15 | }; | |
| 16 | } | |
| 17 | return @Type(@unionInit(std.builtin.Type, "Struct", .{ | |
| 18 | .layout = .Auto, | |
| 19 | .backing_integer = null, | |
| 20 | .fields = &fields_after, | |
| 21 | .decls = &.{}, | |
| 22 | .is_tuple = false, | |
| 23 | })); | |
| 24 | } |
src/ReverseFields.zig created+14| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn ReverseFields(comptime T: type) type { | |
| 6 | var info = @typeInfo(T).Struct; | |
| 7 | const len = info.fields.len; | |
| 8 | var fields: [len]std.builtin.Type.StructField = undefined; | |
| 9 | for (0..len) |i| { | |
| 10 | fields[i] = info.fields[len - 1 - i]; | |
| 11 | } | |
| 12 | info.fields = &fields; | |
| 13 | return @Type(.{ .Struct = info }); | |
| 14 | } |
src/RingBuffer.zig created+22| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn RingBuffer(comptime T: type, comptime capacity: usize) type { | |
| 6 | return struct { | |
| 7 | items: [capacity]T = undefined, | |
| 8 | len: usize = 0, | |
| 9 | comptime capacity: usize = capacity, | |
| 10 | ||
| 11 | const Self = @This(); | |
| 12 | ||
| 13 | pub fn append(self: *Self, new_item: T) void { | |
| 14 | if (self.len == self.capacity) { | |
| 15 | for (1..self.len) |i| self.items[i - 1] = self.items[i]; | |
| 16 | self.len -= 1; | |
| 17 | } | |
| 18 | self.items[self.len] = new_item; | |
| 19 | self.len += 1; | |
| 20 | } | |
| 21 | }; | |
| 22 | } |
src/StringerJsonStringifyMixin.zig created+18| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn StringerJsonStringifyMixin(comptime S: type) type { | |
| 6 | return struct { | |
| 7 | pub fn jsonStringify(self: S, options: std.json.StringifyOptions, out_stream: anytype) !void { | |
| 8 | var buf: [1024]u8 = undefined; | |
| 9 | var fba = std.heap.FixedBufferAllocator.init(&buf); | |
| 10 | const alloc = fba.allocator(); | |
| 11 | var list = std.ArrayList(u8).init(alloc); | |
| 12 | errdefer list.deinit(); | |
| 13 | const writer = list.writer(); | |
| 14 | try writer.writeAll(try self.toString(alloc)); | |
| 15 | try std.json.stringify(list.toOwnedSlice(), options, out_stream); | |
| 16 | } | |
| 17 | }; | |
| 18 | } |
src/TagNameJsonStringifyMixin.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn TagNameJsonStringifyMixin(comptime S: type) type { | |
| 6 | return struct { | |
| 7 | pub fn jsonStringify(self: S, options: std.json.StringifyOptions, out_stream: anytype) !void { | |
| 8 | try std.json.stringify(@tagName(self), options, out_stream); | |
| 9 | } | |
| 10 | }; | |
| 11 | } |
src/addSentinel.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn addSentinel(alloc: std.mem.Allocator, comptime T: type, input: []const T, comptime sentinel: T) ![:sentinel]const T { | |
| 6 | var list = try std.ArrayList(T).initCapacity(alloc, input.len + 1); | |
| 7 | try list.appendSlice(input); | |
| 8 | try list.append(sentinel); | |
| 9 | const str = list.toOwnedSlice(); | |
| 10 | return str[0 .. str.len - 1 :sentinel]; | |
| 11 | } |
src/asciiUpper.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn asciiUpper(alloc: std.mem.Allocator, input: string) ![]u8 { | |
| 6 | var buf = try alloc.dupe(u8, input); | |
| 7 | for (0..buf.len) |i| { | |
| 8 | buf[i] = std.ascii.toUpper(buf[i]); | |
| 9 | } | |
| 10 | return buf; | |
| 11 | } |
src/base64DecodeAlloc.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn base64DecodeAlloc(alloc: std.mem.Allocator, input: string) !string { | |
| 6 | const base64 = std.base64.standard.Decoder; | |
| 7 | var buf = try alloc.alloc(u8, try base64.calcSizeForSlice(input)); | |
| 8 | try base64.decode(buf, input); | |
| 9 | return buf; | |
| 10 | } |
src/base64EncodeAlloc.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn base64EncodeAlloc(alloc: std.mem.Allocator, input: string) !string { | |
| 6 | const base64 = std.base64.standard.Encoder; | |
| 7 | var buf = try alloc.alloc(u8, base64.calcSize(input.len)); | |
| 8 | return base64.encode(buf, input); | |
| 9 | } |
src/coalescePartial.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn coalescePartial(comptime T: type, into: T, from: extras.Partial(T)) T { | |
| 6 | var temp = into; | |
| 7 | inline for (comptime std.meta.fieldNames(T)) |name| { | |
| 8 | if (@field(from, name)) |val| @field(temp, name) = val; | |
| 9 | } | |
| 10 | return temp; | |
| 11 | } |
src/containsAggregate.zig created+12| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn containsAggregate(comptime T: type, haystack: []const T, needle: T) bool { | |
| 6 | for (haystack) |item| { | |
| 7 | if (T.eql(item, needle)) { | |
| 8 | return true; | |
| 9 | } | |
| 10 | } | |
| 11 | return false; | |
| 12 | } |
src/containsString.zig created+12| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn containsString(haystack: []const string, needle: string) bool { | |
| 6 | for (haystack) |item| { | |
| 7 | if (std.mem.eql(u8, item, needle)) { | |
| 8 | return true; | |
| 9 | } | |
| 10 | } | |
| 11 | return false; | |
| 12 | } |
src/countScalar.zig created+14| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn countScalar(comptime T: type, haystack: []const T, needle: T) usize { | |
| 6 | var found: usize = 0; | |
| 7 | ||
| 8 | for (haystack) |item| { | |
| 9 | if (item == needle) { | |
| 10 | found += 1; | |
| 11 | } | |
| 12 | } | |
| 13 | return found; | |
| 14 | } |
src/dirSize.zig created+15| ... | ... | @@ -0,0 +1,15 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn dirSize(alloc: std.mem.Allocator, dir: std.fs.IterableDir) !u64 { | |
| 6 | var res: u64 = 0; | |
| 7 | ||
| 8 | var walk = try dir.walk(alloc); | |
| 9 | defer walk.deinit(); | |
| 10 | while (try walk.next()) |entry| { | |
| 11 | if (entry.kind != .File) continue; | |
| 12 | res += try extras.fileSize(dir.dir, entry.path); | |
| 13 | } | |
| 14 | return res; | |
| 15 | } |
src/doesFileExist.zig created+13| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn doesFileExist(dir: ?std.fs.Dir, fpath: []const u8) !bool { | |
| 6 | const file = (dir orelse std.fs.cwd()).openFile(fpath, .{}) catch |e| switch (e) { | |
| 7 | error.FileNotFound => return false, | |
| 8 | error.IsDir => return true, | |
| 9 | else => return e, | |
| 10 | }; | |
| 11 | defer file.close(); | |
| 12 | return true; | |
| 13 | } |
src/doesFolderExist.zig created+17| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn doesFolderExist(dir: ?std.fs.Dir, fpath: []const u8) !bool { | |
| 6 | const file = (dir orelse std.fs.cwd()).openFile(fpath, .{}) catch |e| switch (e) { | |
| 7 | error.FileNotFound => return false, | |
| 8 | error.IsDir => return true, | |
| 9 | else => return e, | |
| 10 | }; | |
| 11 | defer file.close(); | |
| 12 | const s = try file.stat(); | |
| 13 | if (s.kind != .directory) { | |
| 14 | return false; | |
| 15 | } | |
| 16 | return true; | |
| 17 | } |
src/ensureFieldSubset.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn ensureFieldSubset(comptime L: type, comptime R: type) void { | |
| 6 | 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 | } | |
| 9 | } |
src/fileList.zig created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn fileList(alloc: std.mem.Allocator, dir: std.fs.IterableDir) ![]string { | |
| 6 | var list = std.ArrayList(string).init(alloc); | |
| 7 | defer list.deinit(); | |
| 8 | ||
| 9 | var walk = try dir.walk(alloc); | |
| 10 | defer walk.deinit(); | |
| 11 | while (try walk.next()) |entry| { | |
| 12 | if (entry.kind != .file) continue; | |
| 13 | try list.append(try alloc.dupe(u8, entry.path)); | |
| 14 | } | |
| 15 | return list.toOwnedSlice(); | |
| 16 | } |
src/fileSize.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn fileSize(dir: std.fs.Dir, sub_path: string) !u64 { | |
| 6 | const f = try dir.openFile(sub_path, .{}); | |
| 7 | defer f.close(); | |
| 8 | const s = try f.stat(); | |
| 9 | return s.size; | |
| 10 | } |
src/fmtByteCountIEC.zig created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn fmtByteCountIEC(alloc: std.mem.Allocator, b: u64) !string { | |
| 6 | return try extras.reduceNumber(alloc, b, 1024, "B", "KMGTPEZYRQ"); | |
| 7 | } |
src/fmtReplacer.zig created+28| ... | ... | @@ -0,0 +1,28 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn fmtReplacer(bytes: string, from: u8, to: u8) std.fmt.Formatter(formatReplacer) { | |
| 6 | return .{ .data = .{ | |
| 7 | .bytes = bytes, | |
| 8 | .from = from, | |
| 9 | .to = to, | |
| 10 | } }; | |
| 11 | } | |
| 12 | ||
| 13 | fn formatReplacer( | |
| 14 | self: struct { | |
| 15 | bytes: string, | |
| 16 | from: u8, | |
| 17 | to: u8, | |
| 18 | }, | |
| 19 | comptime fmt: []const u8, | |
| 20 | options: std.fmt.FormatOptions, | |
| 21 | writer: anytype, | |
| 22 | ) !void { | |
| 23 | _ = fmt; | |
| 24 | _ = options; | |
| 25 | for (self.bytes) |c| { | |
| 26 | try writer.writeByte(if (c == self.from) self.to else @intCast(c)); | |
| 27 | } | |
| 28 | } |
src/hashBytes.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn hashBytes(comptime Algo: type, bytes: []const u8) [Algo.digest_length]u8 { | |
| 6 | var h = Algo.init(.{}); | |
| 7 | var out: [Algo.digest_length]u8 = undefined; | |
| 8 | h.update(bytes); | |
| 9 | h.final(&out); | |
| 10 | return out; | |
| 11 | } |
src/hashFile.zig created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn hashFile(dir: std.fs.Dir, sub_path: string, comptime Algo: type) ![Algo.digest_length * 2]u8 { | |
| 6 | const file = try dir.openFile(sub_path, .{}); | |
| 7 | defer file.close(); | |
| 8 | var h = Algo.init(.{}); | |
| 9 | var out: [Algo.digest_length]u8 = undefined; | |
| 10 | try extras.pipe(file.reader(), h.writer()); | |
| 11 | h.final(&out); | |
| 12 | var res: [Algo.digest_length * 2]u8 = undefined; | |
| 13 | var fbs = std.io.fixedBufferStream(&res); | |
| 14 | try std.fmt.format(fbs.writer(), "{x}", .{std.fmt.fmtSliceHexLower(&out)}); | |
| 15 | return res; | |
| 16 | } |
src/indexBufferT.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | 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); | |
| 7 | var fbs = std.io.fixedBufferStream((bytes + (idx * @sizeOf(T)))[0..@sizeOf(T)]); | |
| 8 | return extras.readType(fbs.reader(), T, endian) catch |err| switch (err) { | |
| 9 | error.EndOfStream => unreachable, // assert above has been violated | |
| 10 | }; | |
| 11 | } |
src/is.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | /// ?A == A fails | |
| 6 | /// ?A == @as(?A, b) works | |
| 7 | pub fn is(a: anytype, b: @TypeOf(a)) bool { | |
| 8 | return a == b; | |
| 9 | } |
src/isArrayOf.zig created+15| ... | ... | @@ -0,0 +1,15 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn isArrayOf(comptime T: type) std.meta.trait.TraitFn { | |
| 6 | const Closure = struct { | |
| 7 | pub fn trait(comptime C: type) bool { | |
| 8 | return switch (@typeInfo(C)) { | |
| 9 | .Array => |ti| ti.child == T, | |
| 10 | else => false, | |
| 11 | }; | |
| 12 | } | |
| 13 | }; | |
| 14 | return Closure.trait; | |
| 15 | } |
src/joinPartial.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn joinPartial(comptime P: type, a: P, b: P) P { | |
| 6 | var temp = a; | |
| 7 | inline for (comptime std.meta.fieldNames(P)) |name| { | |
| 8 | if (@field(b, name)) |val| @field(temp, name) = val; | |
| 9 | } | |
| 10 | return temp; | |
| 11 | } |
src/lessThanSlice.zig created+15| ... | ... | @@ -0,0 +1,15 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn lessThanSlice(comptime T: type) fn (void, T, T) bool { | |
| 6 | return struct { | |
| 7 | fn f(_: void, lhs: T, rhs: T) bool { | |
| 8 | const result = for (0..@min(lhs.len, rhs.len)) |i| { | |
| 9 | if (lhs[i] < rhs[i]) break true; | |
| 10 | if (lhs[i] > rhs[i]) break false; | |
| 11 | } else false; | |
| 12 | return result; | |
| 13 | } | |
| 14 | }.f; | |
| 15 | } |
src/lib.zig+67-825| ... | ... | @@ -1,839 +1,81 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const string = []const u8; |
| 3 | const extras = @import("./lib.zig"); | |
| 3 | 4 | const assert = std.debug.assert; |
| 4 | 5 | const builtin = @import("builtin"); |
| 5 | 6 | |
| 6 | pub fn fmtByteCountIEC(alloc: std.mem.Allocator, b: u64) !string { | |
| 7 | return try reduceNumber(alloc, b, 1024, "B", "KMGTPEZYRQ"); | |
| 8 | } | |
| 9 | ||
| 10 | pub fn reduceNumber(alloc: std.mem.Allocator, input: u64, comptime unit: u64, comptime base: string, comptime prefixes: string) !string { | |
| 11 | if (input < unit) { | |
| 12 | return std.fmt.allocPrint(alloc, "{d} {s}", .{ input, base }); | |
| 13 | } | |
| 14 | var div = unit; | |
| 15 | var exp: usize = 0; | |
| 16 | var n = input / unit; | |
| 17 | while (n >= unit) : (n /= unit) { | |
| 18 | div *= unit; | |
| 19 | exp += 1; | |
| 20 | } | |
| 21 | return try std.fmt.allocPrint(alloc, "{d:.3} {s}{s}", .{ @as(f64, @floatFromInt(input)) / @as(f64, @floatFromInt(div)), prefixes[exp .. exp + 1], base }); | |
| 22 | } | |
| 23 | ||
| 24 | pub fn addSentinel(alloc: std.mem.Allocator, comptime T: type, input: []const T, comptime sentinel: T) ![:sentinel]const T { | |
| 25 | var list = try std.ArrayList(T).initCapacity(alloc, input.len + 1); | |
| 26 | try list.appendSlice(input); | |
| 27 | try list.append(sentinel); | |
| 28 | const str = list.toOwnedSlice(); | |
| 29 | return str[0 .. str.len - 1 :sentinel]; | |
| 30 | } | |
| 31 | ||
| 32 | const alphabet = "0123456789abcdefghijklmnopqrstuvwxyz"; | |
| 33 | ||
| 34 | pub fn randomSlice(alloc: std.mem.Allocator, rand: std.rand.Random, comptime T: type, len: usize) ![]T { | |
| 35 | var buf = try alloc.alloc(T, len); | |
| 36 | var i: usize = 0; | |
| 37 | while (i < len) : (i += 1) { | |
| 38 | buf[i] = alphabet[rand.int(u8) % alphabet.len]; | |
| 39 | } | |
| 40 | return buf; | |
| 41 | } | |
| 42 | ||
| 43 | pub fn trimPrefix(in: string, prefix: string) string { | |
| 44 | if (std.mem.startsWith(u8, in, prefix)) { | |
| 45 | return in[prefix.len..]; | |
| 46 | } | |
| 47 | return in; | |
| 48 | } | |
| 49 | ||
| 50 | pub fn trimPrefixEnsure(in: string, prefix: string) ?string { | |
| 51 | if (!std.mem.startsWith(u8, in, prefix)) return null; | |
| 52 | return in[prefix.len..]; | |
| 53 | } | |
| 54 | ||
| 55 | pub fn trimSuffix(in: string, suffix: string) string { | |
| 56 | if (std.mem.endsWith(u8, in, suffix)) { | |
| 57 | return in[0 .. in.len - suffix.len]; | |
| 58 | } | |
| 59 | return in; | |
| 60 | } | |
| 61 | ||
| 62 | pub fn trimSuffixEnsure(in: string, suffix: string) ?string { | |
| 63 | if (!std.mem.endsWith(u8, in, suffix)) return null; | |
| 64 | return in[0 .. in.len - suffix.len]; | |
| 65 | } | |
| 66 | ||
| 67 | pub fn base64EncodeAlloc(alloc: std.mem.Allocator, input: string) !string { | |
| 68 | const base64 = std.base64.standard.Encoder; | |
| 69 | var buf = try alloc.alloc(u8, base64.calcSize(input.len)); | |
| 70 | return base64.encode(buf, input); | |
| 71 | } | |
| 72 | ||
| 73 | pub fn base64DecodeAlloc(alloc: std.mem.Allocator, input: string) !string { | |
| 74 | const base64 = std.base64.standard.Decoder; | |
| 75 | var buf = try alloc.alloc(u8, try base64.calcSizeForSlice(input)); | |
| 76 | try base64.decode(buf, input); | |
| 77 | return buf; | |
| 78 | } | |
| 79 | ||
| 80 | pub fn asciiUpper(alloc: std.mem.Allocator, input: string) ![]u8 { | |
| 81 | var buf = try alloc.dupe(u8, input); | |
| 82 | for (0..buf.len) |i| { | |
| 83 | buf[i] = std.ascii.toUpper(buf[i]); | |
| 84 | } | |
| 85 | return buf; | |
| 86 | } | |
| 87 | ||
| 88 | pub fn doesFolderExist(dir: ?std.fs.Dir, fpath: []const u8) !bool { | |
| 89 | const file = (dir orelse std.fs.cwd()).openFile(fpath, .{}) catch |e| switch (e) { | |
| 90 | error.FileNotFound => return false, | |
| 91 | error.IsDir => return true, | |
| 92 | else => return e, | |
| 93 | }; | |
| 94 | defer file.close(); | |
| 95 | const s = try file.stat(); | |
| 96 | if (s.kind != .directory) { | |
| 97 | return false; | |
| 98 | } | |
| 99 | return true; | |
| 100 | } | |
| 101 | ||
| 102 | pub fn doesFileExist(dir: ?std.fs.Dir, fpath: []const u8) !bool { | |
| 103 | const file = (dir orelse std.fs.cwd()).openFile(fpath, .{}) catch |e| switch (e) { | |
| 104 | error.FileNotFound => return false, | |
| 105 | error.IsDir => return true, | |
| 106 | else => return e, | |
| 107 | }; | |
| 108 | defer file.close(); | |
| 109 | return true; | |
| 110 | } | |
| 111 | ||
| 112 | pub fn sliceToInt(comptime T: type, comptime E: type, slice: []const E) !T { | |
| 113 | const a = @typeInfo(T).Int.bits; | |
| 114 | const b = @typeInfo(E).Int.bits; | |
| 115 | if (a < b * slice.len) return error.Overflow; | |
| 116 | ||
| 117 | var n: T = 0; | |
| 118 | for (slice, 0..) |item, i| { | |
| 119 | const shift: std.math.Log2Int(T) = @intCast(b * (slice.len - 1 - i)); | |
| 120 | n = n | (@as(T, item) << shift); | |
| 121 | } | |
| 122 | return n; | |
| 123 | } | |
| 124 | ||
| 125 | pub fn fileList(alloc: std.mem.Allocator, dir: std.fs.IterableDir) ![]string { | |
| 126 | var list = std.ArrayList(string).init(alloc); | |
| 127 | defer list.deinit(); | |
| 128 | ||
| 129 | var walk = try dir.walk(alloc); | |
| 130 | defer walk.deinit(); | |
| 131 | while (try walk.next()) |entry| { | |
| 132 | if (entry.kind != .file) continue; | |
| 133 | try list.append(try alloc.dupe(u8, entry.path)); | |
| 134 | } | |
| 135 | return list.toOwnedSlice(); | |
| 136 | } | |
| 137 | ||
| 138 | pub fn dirSize(alloc: std.mem.Allocator, dir: std.fs.IterableDir) !u64 { | |
| 139 | var res: u64 = 0; | |
| 140 | ||
| 141 | var walk = try dir.walk(alloc); | |
| 142 | defer walk.deinit(); | |
| 143 | while (try walk.next()) |entry| { | |
| 144 | if (entry.kind != .File) continue; | |
| 145 | res += try fileSize(dir.dir, entry.path); | |
| 146 | } | |
| 147 | return res; | |
| 148 | } | |
| 149 | ||
| 150 | pub fn fileSize(dir: std.fs.Dir, sub_path: string) !u64 { | |
| 151 | const f = try dir.openFile(sub_path, .{}); | |
| 152 | defer f.close(); | |
| 153 | const s = try f.stat(); | |
| 154 | return s.size; | |
| 155 | } | |
| 156 | ||
| 157 | pub fn hashFile(dir: std.fs.Dir, sub_path: string, comptime Algo: type) ![Algo.digest_length * 2]u8 { | |
| 158 | const file = try dir.openFile(sub_path, .{}); | |
| 159 | defer file.close(); | |
| 160 | var h = Algo.init(.{}); | |
| 161 | var out: [Algo.digest_length]u8 = undefined; | |
| 162 | try pipe(file.reader(), h.writer()); | |
| 163 | h.final(&out); | |
| 164 | var res: [Algo.digest_length * 2]u8 = undefined; | |
| 165 | var fbs = std.io.fixedBufferStream(&res); | |
| 166 | try std.fmt.format(fbs.writer(), "{x}", .{std.fmt.fmtSliceHexLower(&out)}); | |
| 167 | return res; | |
| 168 | } | |
| 169 | ||
| 170 | pub fn pipe(reader_from: anytype, writer_to: anytype) !void { | |
| 171 | var buf: [std.mem.page_size]u8 = undefined; | |
| 172 | var fifo = std.fifo.LinearFifo(u8, .Slice).init(&buf); | |
| 173 | defer fifo.deinit(); | |
| 174 | try fifo.pump(reader_from, writer_to); | |
| 175 | } | |
| 176 | ||
| 177 | pub fn StringerJsonStringifyMixin(comptime S: type) type { | |
| 178 | return struct { | |
| 179 | pub fn jsonStringify(self: S, options: std.json.StringifyOptions, out_stream: anytype) !void { | |
| 180 | var buf: [1024]u8 = undefined; | |
| 181 | var fba = std.heap.FixedBufferAllocator.init(&buf); | |
| 182 | const alloc = fba.allocator(); | |
| 183 | var list = std.ArrayList(u8).init(alloc); | |
| 184 | errdefer list.deinit(); | |
| 185 | const writer = list.writer(); | |
| 186 | try writer.writeAll(try self.toString(alloc)); | |
| 187 | try std.json.stringify(list.toOwnedSlice(), options, out_stream); | |
| 188 | } | |
| 189 | }; | |
| 190 | } | |
| 191 | ||
| 192 | pub fn TagNameJsonStringifyMixin(comptime S: type) type { | |
| 193 | return struct { | |
| 194 | pub fn jsonStringify(self: S, options: std.json.StringifyOptions, out_stream: anytype) !void { | |
| 195 | try std.json.stringify(@tagName(self), options, out_stream); | |
| 196 | } | |
| 197 | }; | |
| 198 | } | |
| 199 | ||
| 200 | pub fn countScalar(comptime T: type, haystack: []const T, needle: T) usize { | |
| 201 | var found: usize = 0; | |
| 202 | ||
| 203 | for (haystack) |item| { | |
| 204 | if (item == needle) { | |
| 205 | found += 1; | |
| 206 | } | |
| 207 | } | |
| 208 | return found; | |
| 209 | } | |
| 210 | ||
| 211 | pub fn ptrCast(comptime T: type, ptr: *anyopaque) *T { | |
| 212 | if (@alignOf(T) == 0) @compileError(@typeName(T)); | |
| 213 | return @ptrCast(@alignCast(ptr)); | |
| 214 | } | |
| 215 | ||
| 216 | pub fn ptrCastConst(comptime T: type, ptr: *const anyopaque) *const T { | |
| 217 | if (@alignOf(T) == 0) @compileError(@typeName(T)); | |
| 218 | return @ptrCast(@alignCast(ptr)); | |
| 219 | } | |
| 220 | ||
| 221 | pub fn sortBy(comptime T: type, items: []T, comptime field: std.meta.FieldEnum(T)) void { | |
| 222 | std.mem.sort(T, items, {}, struct { | |
| 223 | fn f(_: void, lhs: T, rhs: T) bool { | |
| 224 | return @field(lhs, @tagName(field)) < @field(rhs, @tagName(field)); | |
| 225 | } | |
| 226 | }.f); | |
| 227 | } | |
| 228 | ||
| 229 | pub fn sortBySlice(comptime T: type, items: []T, comptime field: std.meta.FieldEnum(T)) void { | |
| 230 | std.mem.sort(T, items, {}, struct { | |
| 231 | fn f(_: void, lhs: T, rhs: T) bool { | |
| 232 | return lessThanSlice(std.meta.FieldType(T, field))({}, @field(lhs, @tagName(field)), @field(rhs, @tagName(field))); | |
| 233 | } | |
| 234 | }.f); | |
| 235 | } | |
| 236 | ||
| 237 | pub fn lessThanSlice(comptime T: type) fn (void, T, T) bool { | |
| 238 | return struct { | |
| 239 | fn f(_: void, lhs: T, rhs: T) bool { | |
| 240 | const result = for (0..@min(lhs.len, rhs.len)) |i| { | |
| 241 | if (lhs[i] < rhs[i]) break true; | |
| 242 | if (lhs[i] > rhs[i]) break false; | |
| 243 | } else false; | |
| 244 | return result; | |
| 245 | } | |
| 246 | }.f; | |
| 247 | } | |
| 248 | ||
| 249 | pub fn containsString(haystack: []const string, needle: string) bool { | |
| 250 | for (haystack) |item| { | |
| 251 | if (std.mem.eql(u8, item, needle)) { | |
| 252 | return true; | |
| 253 | } | |
| 254 | } | |
| 255 | return false; | |
| 256 | } | |
| 257 | ||
| 258 | pub fn FieldsTuple(comptime T: type) type { | |
| 259 | const fields = std.meta.fields(T); | |
| 260 | var types: [fields.len]type = undefined; | |
| 261 | for (fields, 0..) |item, i| { | |
| 262 | types[i] = item.type; | |
| 263 | } | |
| 264 | return std.meta.Tuple(&types); | |
| 265 | } | |
| 266 | ||
| 267 | pub fn positionalInit(comptime T: type, args: FieldsTuple(T)) T { | |
| 268 | var t: T = undefined; | |
| 269 | inline for (std.meta.fields(T), 0..) |field, i| { | |
| 270 | @field(t, field.name) = args[i]; | |
| 271 | } | |
| 272 | return t; | |
| 273 | } | |
| 274 | ||
| 275 | pub fn d2index(d1len: usize, d1: usize, d2: usize) usize { | |
| 276 | return (d1len * d2) + d1; | |
| 277 | } | |
| 278 | ||
| 279 | pub fn ensureFieldSubset(comptime L: type, comptime R: type) void { | |
| 280 | for (std.meta.fields(L)) |item| { | |
| 281 | if (!@hasField(R, item.name)) @compileError(std.fmt.comptimePrint("{s} is missing the {s} field from {s}", .{ R, item.name, L })); | |
| 282 | } | |
| 283 | } | |
| 284 | ||
| 285 | pub fn fmtReplacer(bytes: string, from: u8, to: u8) std.fmt.Formatter(formatReplacer) { | |
| 286 | return .{ .data = .{ .bytes = bytes, .from = from, .to = to } }; | |
| 287 | } | |
| 288 | ||
| 289 | const ReplacerData = struct { bytes: string, from: u8, to: u8 }; | |
| 290 | fn formatReplacer(self: ReplacerData, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { | |
| 291 | _ = fmt; | |
| 292 | _ = options; | |
| 293 | for (self.bytes) |c| { | |
| 294 | try writer.writeByte(if (c == self.from) self.to else @intCast(c)); | |
| 295 | } | |
| 296 | } | |
| 297 | ||
| 298 | pub fn randomBytes(comptime len: usize) [len]u8 { | |
| 299 | var bytes: [len]u8 = undefined; | |
| 300 | std.crypto.random.bytes(&bytes); | |
| 301 | return bytes; | |
| 302 | } | |
| 303 | ||
| 304 | pub fn writeEnumBig(writer: anytype, comptime E: type, value: E) !void { | |
| 305 | try writer.writeIntBig(@typeInfo(E).Enum.tag_type, @intFromEnum(value)); | |
| 306 | } | |
| 307 | ||
| 308 | pub fn readEnumBig(reader: anytype, comptime E: type) !E { | |
| 309 | return @enumFromInt(try reader.readIntBig(@typeInfo(E).Enum.tag_type)); | |
| 310 | } | |
| 311 | ||
| 312 | pub fn readExpected(reader: anytype, expected: []const u8) !bool { | |
| 313 | for (expected) |item| { | |
| 314 | const actual = try reader.readByte(); | |
| 315 | if (actual != item) { | |
| 316 | return false; | |
| 317 | } | |
| 318 | } | |
| 319 | return true; | |
| 320 | } | |
| 321 | ||
| 322 | pub fn readBytes(reader: anytype, comptime len: usize) ![len]u8 { | |
| 323 | var bytes: [len]u8 = undefined; | |
| 324 | assert(try reader.readAll(&bytes) == len); | |
| 325 | return bytes; | |
| 326 | } | |
| 327 | ||
| 328 | pub fn FixedMaxBuffer(comptime max_len: usize) type { | |
| 329 | return struct { | |
| 330 | buf: [max_len]u8, | |
| 331 | len: usize, | |
| 332 | pos: usize, | |
| 333 | ||
| 334 | const Self = @This(); | |
| 335 | pub const Reader = std.io.Reader(*Self, error{}, read); | |
| 336 | ||
| 337 | pub fn init(r: anytype, runtime_len: usize) !Self { | |
| 338 | var fmr = Self{ | |
| 339 | .buf = undefined, | |
| 340 | .len = runtime_len, | |
| 341 | .pos = 0, | |
| 342 | }; | |
| 343 | _ = try r.readAll(fmr.buf[0..runtime_len]); | |
| 344 | return fmr; | |
| 345 | } | |
| 346 | ||
| 347 | pub fn reader(self: *Self) Reader { | |
| 348 | return .{ .context = self }; | |
| 349 | } | |
| 350 | ||
| 351 | fn read(self: *Self, dest: []u8) error{}!usize { | |
| 352 | const buf = self.buf[0..self.len]; | |
| 353 | const size = @min(dest.len, buf.len - self.pos); | |
| 354 | const end = self.pos + size; | |
| 355 | std.mem.copy(u8, dest[0..size], buf[self.pos..end]); | |
| 356 | self.pos = end; | |
| 357 | return size; | |
| 358 | } | |
| 359 | ||
| 360 | pub fn readLen(self: *Self, len: usize) []const u8 { | |
| 361 | assert(self.pos + len <= self.len); | |
| 362 | defer self.pos += len; | |
| 363 | return self.buf[self.pos..][0..len]; | |
| 364 | } | |
| 365 | ||
| 366 | pub fn atEnd(self: *const Self) bool { | |
| 367 | return self.pos == self.len; | |
| 368 | } | |
| 369 | }; | |
| 370 | } | |
| 371 | ||
| 372 | pub fn hashBytes(comptime Algo: type, bytes: []const u8) [Algo.digest_length]u8 { | |
| 373 | var h = Algo.init(.{}); | |
| 374 | var out: [Algo.digest_length]u8 = undefined; | |
| 375 | h.update(bytes); | |
| 376 | h.final(&out); | |
| 377 | return out; | |
| 378 | } | |
| 379 | ||
| 380 | pub fn readType(reader: anytype, comptime T: type, endian: std.builtin.Endian) !T { | |
| 381 | if (T == u8) return reader.readByte(); // single bytes dont have an endianness | |
| 382 | return switch (@typeInfo(T)) { | |
| 383 | .Struct => |t| { | |
| 384 | switch (t.layout) { | |
| 385 | .Auto, .Extern => { | |
| 386 | var s: T = undefined; | |
| 387 | inline for (std.meta.fields(T)) |field| { | |
| 388 | @field(s, field.name) = try readType(reader, field.type, endian); | |
| 389 | } | |
| 390 | return s; | |
| 391 | }, | |
| 392 | .Packed => return @bitCast(try readType(reader, t.backing_integer.?, endian)), | |
| 393 | } | |
| 394 | }, | |
| 395 | .Array => |t| { | |
| 396 | var s: T = undefined; | |
| 397 | for (0..t.len) |i| { | |
| 398 | s[i] = try readType(reader, t.child, endian); | |
| 399 | } | |
| 400 | return s; | |
| 401 | }, | |
| 402 | .Int => try reader.readInt(T, endian), | |
| 403 | .Enum => |t| @enumFromInt(try readType(reader, t.tag_type, endian)), | |
| 404 | else => |e| @compileError(@tagName(e)), | |
| 405 | }; | |
| 406 | } | |
| 407 | ||
| 408 | pub fn indexBufferT(bytes: [*]const u8, comptime T: type, endian: std.builtin.Endian, idx: usize, max_len: usize) T { | |
| 409 | std.debug.assert(idx < max_len); | |
| 410 | var fbs = std.io.fixedBufferStream((bytes + (idx * @sizeOf(T)))[0..@sizeOf(T)]); | |
| 411 | return readType(fbs.reader(), T, endian) catch |err| switch (err) { | |
| 412 | error.EndOfStream => unreachable, // assert above has been violated | |
| 413 | }; | |
| 414 | } | |
| 415 | ||
| 416 | pub fn BufIndexer(comptime T: type, comptime endian: std.builtin.Endian) type { | |
| 417 | return struct { | |
| 418 | bytes: [*]const u8, | |
| 419 | max_len: usize, | |
| 420 | ||
| 421 | const Self = @This(); | |
| 422 | ||
| 423 | pub fn init(bytes: [*]const u8, max_len: usize) Self { | |
| 424 | return .{ | |
| 425 | .bytes = bytes, | |
| 426 | .max_len = max_len, | |
| 427 | }; | |
| 428 | } | |
| 429 | ||
| 430 | pub fn at(self: *const Self, idx: usize) T { | |
| 431 | return indexBufferT(self.bytes, T, endian, idx, self.max_len); | |
| 432 | } | |
| 433 | }; | |
| 434 | } | |
| 435 | ||
| 436 | pub fn skipToBoundary(pos: u64, boundary: u64, reader: anytype) !void { | |
| 437 | // const gdiff = counter.bytes_read % 4; | |
| 438 | // for (range(if (gdiff > 0) 4 - gdiff else 0)) |_| { | |
| 439 | const a = pos; | |
| 440 | const b = boundary; | |
| 441 | try reader.skipBytes(((a + (b - 1)) & ~(b - 1)) - a, .{}); | |
| 442 | } | |
| 443 | ||
| 444 | /// ?A == A fails | |
| 445 | /// ?A == @as(?A, b) works | |
| 446 | pub fn is(a: anytype, b: @TypeOf(a)) bool { | |
| 447 | return a == b; | |
| 448 | } | |
| 449 | ||
| 450 | /// Allows u32 + i16 to work | |
| 451 | pub fn safeAdd(a: anytype, b: anytype) @TypeOf(a) { | |
| 452 | if (b >= 0) { | |
| 453 | return a + @as(@TypeOf(a), @intCast(b)); | |
| 454 | } | |
| 455 | return a - @as(@TypeOf(a), @intCast(-@as(OneBiggerInt(@TypeOf(b)), b))); | |
| 456 | } | |
| 457 | ||
| 458 | /// Allows u32 + i16 to work | |
| 459 | pub fn safeAddWrap(a: anytype, b: anytype) @TypeOf(a) { | |
| 460 | if (b >= 0) { | |
| 461 | return a +% @as(@TypeOf(a), @intCast(b)); | |
| 462 | } | |
| 463 | return a -% @as(@TypeOf(a), @intCast(-@as(OneBiggerInt(@TypeOf(b)), b))); | |
| 464 | } | |
| 465 | ||
| 466 | pub fn readBytesAlloc(reader: anytype, alloc: std.mem.Allocator, len: usize) ![]u8 { | |
| 467 | var list = std.ArrayListUnmanaged(u8){}; | |
| 468 | try list.ensureTotalCapacityPrecise(alloc, len); | |
| 469 | errdefer list.deinit(alloc); | |
| 470 | list.appendNTimesAssumeCapacity(0, len); | |
| 471 | try reader.readNoEof(list.items[0..len]); | |
| 472 | return list.items; | |
| 473 | } | |
| 474 | ||
| 475 | pub fn readFile(dir: std.fs.Dir, sub_path: string, alloc: std.mem.Allocator) !string { | |
| 476 | _ = dir; | |
| 477 | _ = sub_path; | |
| 478 | _ = alloc; | |
| 479 | @compileError("use std.fs.Dir.readFileAlloc instead"); | |
| 480 | } | |
| 481 | ||
| 482 | pub fn nullifyS(s: ?string) ?string { | |
| 483 | if (s == null) return null; | |
| 484 | if (s.?.len == 0) return null; | |
| 485 | return s.?; | |
| 486 | } | |
| 487 | ||
| 488 | pub fn sliceTo(comptime T: type, haystack: []const T, needle: T) []const T { | |
| 489 | if (std.mem.indexOfScalar(T, haystack, needle)) |index| { | |
| 490 | return haystack[0..index]; | |
| 491 | } | |
| 492 | return haystack; | |
| 493 | } | |
| 494 | ||
| 495 | pub fn matchesAll(comptime T: type, haystack: []const T, comptime needle: fn (T) bool) bool { | |
| 496 | for (haystack) |c| { | |
| 497 | if (!needle(c)) { | |
| 498 | return false; | |
| 499 | } | |
| 500 | } | |
| 501 | return true; | |
| 502 | } | |
| 503 | ||
| 504 | pub fn matchesAny(comptime T: type, haystack: []const T, comptime needle: fn (T) bool) bool { | |
| 505 | for (haystack) |c| { | |
| 506 | if (needle(c)) { | |
| 507 | return true; | |
| 508 | } | |
| 509 | } | |
| 510 | return false; | |
| 511 | } | |
| 512 | ||
| 513 | pub fn opslice(slice: anytype, index: usize) ?std.meta.Child(@TypeOf(slice)) { | |
| 514 | if (slice.len <= index) return null; | |
| 515 | return slice[index]; | |
| 516 | } | |
| 7 | pub usingnamespace @import("./reduceNumber.zig"); | |
| 8 | pub usingnamespace @import("./fmtByteCountIEC.zig"); | |
| 9 | pub usingnamespace @import("./addSentinel.zig"); | |
| 10 | pub usingnamespace @import("./randomSlice.zig"); | |
| 11 | pub usingnamespace @import("./trimPrefix.zig"); | |
| 12 | pub usingnamespace @import("./trimPrefixEnsure.zig"); | |
| 13 | pub usingnamespace @import("./trimSuffix.zig"); | |
| 14 | pub usingnamespace @import("./trimSuffixEnsure.zig"); | |
| 15 | pub usingnamespace @import("./base64EncodeAlloc.zig"); | |
| 16 | pub usingnamespace @import("./base64DecodeAlloc.zig"); | |
| 17 | pub usingnamespace @import("./asciiUpper.zig"); | |
| 18 | pub usingnamespace @import("./doesFileExist.zig"); | |
| 19 | pub usingnamespace @import("./doesFolderExist.zig"); | |
| 20 | pub usingnamespace @import("./sliceToInt.zig"); | |
| 21 | pub usingnamespace @import("./fileList.zig"); | |
| 22 | pub usingnamespace @import("./fileSize.zig"); | |
| 23 | pub usingnamespace @import("./dirSize.zig"); | |
| 24 | pub usingnamespace @import("./hashFile.zig"); | |
| 25 | pub usingnamespace @import("./pipe.zig"); | |
| 26 | pub usingnamespace @import("./StringerJsonStringifyMixin.zig"); | |
| 27 | pub usingnamespace @import("./TagNameJsonStringifyMixin.zig"); | |
| 28 | pub usingnamespace @import("./countScalar.zig"); | |
| 29 | pub usingnamespace @import("./ptrCast.zig"); | |
| 30 | pub usingnamespace @import("./ptrCastConst.zig"); | |
| 31 | pub usingnamespace @import("./sortBy.zig"); | |
| 32 | pub usingnamespace @import("./sortBySlice.zig"); | |
| 33 | pub usingnamespace @import("./lessThanSlice.zig"); | |
| 34 | pub usingnamespace @import("./containsString.zig"); | |
| 35 | pub usingnamespace @import("./positionalInit.zig"); | |
| 36 | pub usingnamespace @import("./ensureFieldSubset.zig"); | |
| 37 | pub usingnamespace @import("./fmtReplacer.zig"); | |
| 38 | pub usingnamespace @import("./randomBytes.zig"); | |
| 39 | pub usingnamespace @import("./readExpected.zig"); | |
| 40 | pub usingnamespace @import("./readBytes.zig"); | |
| 41 | pub usingnamespace @import("./FixedMaxBuffer.zig"); | |
| 42 | pub usingnamespace @import("./hashBytes.zig"); | |
| 43 | pub usingnamespace @import("./readType.zig"); | |
| 44 | pub usingnamespace @import("./indexBufferT.zig"); | |
| 45 | pub usingnamespace @import("./BufIndexer.zig"); | |
| 46 | pub usingnamespace @import("./skipToBoundary.zig"); | |
| 47 | pub usingnamespace @import("./is.zig"); | |
| 48 | pub usingnamespace @import("./safeAdd.zig"); | |
| 49 | pub usingnamespace @import("./safeAddWrap.zig"); | |
| 50 | pub usingnamespace @import("./readBytesAlloc.zig"); | |
| 51 | pub usingnamespace @import("./nullifyS.zig"); | |
| 52 | pub usingnamespace @import("./sliceTo.zig"); | |
| 53 | pub usingnamespace @import("./matchesAll.zig"); | |
| 54 | pub usingnamespace @import("./matchesAny.zig"); | |
| 55 | pub usingnamespace @import("./opslice.zig"); | |
| 517 | 56 | |
| 518 | 57 | pub fn assertLog(ok: bool, comptime message: string, args: anytype) void { |
| 519 | 58 | if (!ok) std.log.err("assertion failure: " ++ message, args); |
| 520 | 59 | if (!ok) unreachable; // assertion failure |
| 521 | 60 | } |
| 522 | 61 | |
| 523 | pub fn parse_json(alloc: std.mem.Allocator, input: string) !std.json.Parsed(std.json.Value) { | |
| 524 | return std.json.parseFromSlice(std.json.Value, alloc, input, .{}); | |
| 525 | } | |
| 526 | ||
| 527 | pub fn isArrayOf(comptime T: type) std.meta.trait.TraitFn { | |
| 528 | const Closure = struct { | |
| 529 | pub fn trait(comptime C: type) bool { | |
| 530 | return switch (@typeInfo(C)) { | |
| 531 | .Array => |ti| ti.child == T, | |
| 532 | else => false, | |
| 533 | }; | |
| 534 | } | |
| 535 | }; | |
| 536 | return Closure.trait; | |
| 537 | } | |
| 538 | ||
| 539 | pub fn parse_int(comptime T: type, s: ?string, b: u8, d: T) T { | |
| 540 | if (s == null) return d; | |
| 541 | return std.fmt.parseInt(T, s.?, b) catch d; | |
| 542 | } | |
| 543 | ||
| 544 | pub fn parse_bool(s: ?string) bool { | |
| 545 | return parse_int(u1, s, 10, 0) > 0; | |
| 546 | } | |
| 547 | ||
| 548 | pub fn to_hex(array: anytype) [array.len * 2]u8 { | |
| 549 | var res: [array.len * 2]u8 = undefined; | |
| 550 | var fbs = std.io.fixedBufferStream(&res); | |
| 551 | std.fmt.format(fbs.writer(), "{x}", .{std.fmt.fmtSliceHexLower(&array)}) catch unreachable; | |
| 552 | return res; | |
| 553 | } | |
| 554 | ||
| 555 | pub fn FieldUnion(comptime T: type) type { | |
| 556 | const infos = std.meta.fields(T); | |
| 557 | ||
| 558 | var fields: [infos.len]std.builtin.Type.UnionField = undefined; | |
| 559 | inline for (infos, 0..) |field, i| { | |
| 560 | fields[i] = .{ | |
| 561 | .name = field.name, | |
| 562 | .type = field.type, | |
| 563 | .alignment = field.alignment, | |
| 564 | }; | |
| 565 | } | |
| 566 | return @Type(std.builtin.Type{ .Union = .{ | |
| 567 | .layout = .Auto, | |
| 568 | .tag_type = std.meta.FieldEnum(T), | |
| 569 | .fields = &fields, | |
| 570 | .decls = &.{}, | |
| 571 | } }); | |
| 572 | } | |
| 573 | ||
| 574 | pub fn LoggingReader(comptime T: type, comptime scope: @Type(.EnumLiteral)) type { | |
| 575 | return struct { | |
| 576 | child_stream: T, | |
| 577 | ||
| 578 | pub const Error = T.Error; | |
| 579 | pub const Reader = std.io.Reader(Self, Error, read); | |
| 580 | ||
| 581 | const Self = @This(); | |
| 582 | ||
| 583 | pub fn init(child_stream: T) Self { | |
| 584 | return .{ | |
| 585 | .child_stream = child_stream, | |
| 586 | }; | |
| 587 | } | |
| 588 | ||
| 589 | pub fn reader(self: Self) Reader { | |
| 590 | return .{ .context = self }; | |
| 591 | } | |
| 592 | ||
| 593 | fn read(self: Self, dest: []u8) Error!usize { | |
| 594 | const n = try self.child_stream.read(dest); | |
| 595 | std.log.scoped(scope).debug("{s}", .{dest[0..n]}); | |
| 596 | return n; | |
| 597 | } | |
| 598 | }; | |
| 599 | } | |
| 600 | ||
| 601 | pub fn LoggingWriter(comptime T: type, comptime scope: @Type(.EnumLiteral)) type { | |
| 602 | return struct { | |
| 603 | child_stream: T, | |
| 604 | ||
| 605 | pub const Error = T.Error; | |
| 606 | pub const Writer = std.io.Writer(Self, Error, write); | |
| 607 | ||
| 608 | const Self = @This(); | |
| 609 | ||
| 610 | pub fn init(child_stream: T) Self { | |
| 611 | return .{ | |
| 612 | .child_stream = child_stream, | |
| 613 | }; | |
| 614 | } | |
| 615 | ||
| 616 | pub fn writer(self: Self) Writer { | |
| 617 | return .{ .context = self }; | |
| 618 | } | |
| 619 | ||
| 620 | fn write(self: Self, bytes: []const u8) Error!usize { | |
| 621 | std.log.scoped(scope).debug("{s}", .{bytes}); | |
| 622 | return self.child_stream.write(bytes); | |
| 623 | } | |
| 624 | }; | |
| 625 | } | |
| 626 | ||
| 627 | pub fn Partial(comptime T: type) type { | |
| 628 | const fields_before = std.meta.fields(T); | |
| 629 | var fields_after: [fields_before.len]std.builtin.Type.StructField = undefined; | |
| 630 | inline for (fields_before, 0..) |item, i| { | |
| 631 | fields_after[i] = std.builtin.Type.StructField{ | |
| 632 | .name = item.name, | |
| 633 | .type = ?item.type, | |
| 634 | .default_value = &@as(?item.type, null), | |
| 635 | .is_comptime = false, | |
| 636 | .alignment = @alignOf(?item.type), | |
| 637 | }; | |
| 638 | } | |
| 639 | return @Type(@unionInit(std.builtin.Type, "Struct", .{ | |
| 640 | .layout = .Auto, | |
| 641 | .backing_integer = null, | |
| 642 | .fields = &fields_after, | |
| 643 | .decls = &.{}, | |
| 644 | .is_tuple = false, | |
| 645 | })); | |
| 646 | } | |
| 647 | ||
| 648 | pub fn coalescePartial(comptime T: type, into: T, from: Partial(T)) T { | |
| 649 | var temp = into; | |
| 650 | inline for (comptime std.meta.fieldNames(T)) |name| { | |
| 651 | if (@field(from, name)) |val| @field(temp, name) = val; | |
| 652 | } | |
| 653 | return temp; | |
| 654 | } | |
| 655 | ||
| 656 | pub fn joinPartial(comptime P: type, a: P, b: P) P { | |
| 657 | var temp = a; | |
| 658 | inline for (comptime std.meta.fieldNames(P)) |name| { | |
| 659 | if (@field(b, name)) |val| @field(temp, name) = val; | |
| 660 | } | |
| 661 | return temp; | |
| 662 | } | |
| 663 | ||
| 664 | pub fn OneBiggerInt(comptime T: type) type { | |
| 665 | var info = @typeInfo(T); | |
| 666 | info.Int.bits += 1; | |
| 667 | return @Type(info); | |
| 668 | } | |
| 669 | ||
| 670 | pub fn ReverseFields(comptime T: type) type { | |
| 671 | var info = @typeInfo(T).Struct; | |
| 672 | const len = info.fields.len; | |
| 673 | var fields: [len]std.builtin.Type.StructField = undefined; | |
| 674 | for (0..len) |i| { | |
| 675 | fields[i] = info.fields[len - 1 - i]; | |
| 676 | } | |
| 677 | info.fields = &fields; | |
| 678 | return @Type(.{ .Struct = info }); | |
| 679 | } | |
| 680 | ||
| 681 | pub fn stringToEnum(comptime E: type, str: ?string) ?E { | |
| 682 | return std.meta.stringToEnum(E, str orelse return null); | |
| 683 | } | |
| 684 | ||
| 685 | pub fn containsAggregate(comptime T: type, haystack: []const T, needle: T) bool { | |
| 686 | for (haystack) |item| { | |
| 687 | if (T.eql(item, needle)) { | |
| 688 | return true; | |
| 689 | } | |
| 690 | } | |
| 691 | return false; | |
| 692 | } | |
| 693 | ||
| 694 | pub const AnyReader = struct { | |
| 695 | readFn: *const fn (*anyopaque, []u8) anyerror!usize, | |
| 696 | state: *anyopaque, | |
| 697 | ||
| 698 | pub fn from(reader: anytype) AnyReader { | |
| 699 | const R = @TypeOf(reader); | |
| 700 | const ctx = reader.context; | |
| 701 | const Ctx = @TypeOf(ctx); | |
| 702 | switch (@typeInfo(Ctx)) { | |
| 703 | .Pointer => { | |
| 704 | const S = struct { | |
| 705 | fn foo(s: *anyopaque, buffer: []u8) anyerror!usize { | |
| 706 | const r = R{ .context = @ptrCast(@alignCast(s)) }; | |
| 707 | return r.read(buffer); | |
| 708 | } | |
| 709 | }; | |
| 710 | return .{ | |
| 711 | .readFn = S.foo, | |
| 712 | .state = ctx, | |
| 713 | }; | |
| 714 | }, | |
| 715 | .Struct => switch (R) { | |
| 716 | std.fs.File.Reader => { | |
| 717 | const S = struct { | |
| 718 | fn foo(s: *anyopaque, buffer: []u8) anyerror!usize { | |
| 719 | const r = R{ .context = .{ .handle = @intCast(@intFromPtr(s)) } }; | |
| 720 | return r.read(buffer); | |
| 721 | } | |
| 722 | }; | |
| 723 | return .{ | |
| 724 | .readFn = S.foo, | |
| 725 | .state = @ptrFromInt(@as(usize, @intCast(ctx.handle))), | |
| 726 | }; | |
| 727 | }, | |
| 728 | else => @compileError(@typeName(R)), | |
| 729 | }, | |
| 730 | else => |v| @compileError(@typeName(R) ++ " , " ++ @tagName(v)), | |
| 731 | } | |
| 732 | } | |
| 733 | ||
| 734 | pub fn read(r: AnyReader, buffer: []u8) anyerror!usize { | |
| 735 | return r.readFn(r.state, buffer); | |
| 736 | } | |
| 737 | ||
| 738 | pub fn readByte(self: AnyReader) !u8 { | |
| 739 | var result: [1]u8 = undefined; | |
| 740 | const amt_read = try self.read(result[0..]); | |
| 741 | if (amt_read < 1) return error.EndOfStream; | |
| 742 | return result[0]; | |
| 743 | } | |
| 744 | ||
| 745 | pub fn readInt(self: AnyReader, comptime T: type, endian: std.builtin.Endian) !T { | |
| 746 | const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))); | |
| 747 | return std.mem.readInt(T, &bytes, endian); | |
| 748 | } | |
| 749 | ||
| 750 | pub fn readBytesNoEof(self: AnyReader, comptime num_bytes: usize) ![num_bytes]u8 { | |
| 751 | var bytes: [num_bytes]u8 = undefined; | |
| 752 | try self.readNoEof(&bytes); | |
| 753 | return bytes; | |
| 754 | } | |
| 755 | ||
| 756 | pub fn readNoEof(self: AnyReader, buf: []u8) !void { | |
| 757 | const amt_read = try self.readAll(buf); | |
| 758 | if (amt_read < buf.len) return error.EndOfStream; | |
| 759 | } | |
| 760 | ||
| 761 | pub fn readAll(self: AnyReader, buffer: []u8) !usize { | |
| 762 | return self.readAtLeast(buffer, buffer.len); | |
| 763 | } | |
| 764 | ||
| 765 | pub fn readAtLeast(self: AnyReader, buffer: []u8, len: usize) !usize { | |
| 766 | assert(len <= buffer.len); | |
| 767 | var index: usize = 0; | |
| 768 | while (index < len) { | |
| 769 | const amt = try self.read(buffer[index..]); | |
| 770 | if (amt == 0) break; | |
| 771 | index += amt; | |
| 772 | } | |
| 773 | return index; | |
| 774 | } | |
| 775 | ||
| 776 | pub fn readAllAlloc(self: AnyReader, allocator: std.mem.Allocator, max_size: usize) ![]u8 { | |
| 777 | var array_list = std.ArrayList(u8).init(allocator); | |
| 778 | defer array_list.deinit(); | |
| 779 | try self.readAllArrayList(&array_list, max_size); | |
| 780 | return try array_list.toOwnedSlice(); | |
| 781 | } | |
| 782 | ||
| 783 | pub fn readAllArrayList(self: AnyReader, array_list: *std.ArrayList(u8), max_append_size: usize) !void { | |
| 784 | return self.readAllArrayListAligned(null, array_list, max_append_size); | |
| 785 | } | |
| 786 | ||
| 787 | pub fn readAllArrayListAligned(self: AnyReader, comptime alignment: ?u29, array_list: *std.ArrayListAligned(u8, alignment), max_append_size: usize) !void { | |
| 788 | try array_list.ensureTotalCapacity(@min(max_append_size, 4096)); | |
| 789 | const original_len = array_list.items.len; | |
| 790 | var start_index: usize = original_len; | |
| 791 | while (true) { | |
| 792 | array_list.expandToCapacity(); | |
| 793 | const dest_slice = array_list.items[start_index..]; | |
| 794 | const bytes_read = try self.readAll(dest_slice); | |
| 795 | start_index += bytes_read; | |
| 796 | ||
| 797 | if (start_index - original_len > max_append_size) { | |
| 798 | array_list.shrinkAndFree(original_len + max_append_size); | |
| 799 | return error.StreamTooLong; | |
| 800 | } | |
| 801 | ||
| 802 | if (bytes_read != dest_slice.len) { | |
| 803 | array_list.shrinkAndFree(start_index); | |
| 804 | return; | |
| 805 | } | |
| 806 | ||
| 807 | // This will trigger ArrayList to expand superlinearly at whatever its growth rate is. | |
| 808 | try array_list.ensureTotalCapacity(start_index + 1); | |
| 809 | } | |
| 810 | } | |
| 811 | }; | |
| 812 | ||
| 813 | pub fn sum(comptime T: type, slice: []const T) T { | |
| 814 | var res: T = 0; | |
| 815 | for (slice) |item| res += item; | |
| 816 | return res; | |
| 817 | } | |
| 818 | ||
| 819 | pub fn RingBuffer(comptime T: type, comptime capacity: usize) type { | |
| 820 | return struct { | |
| 821 | items: [capacity]T = undefined, | |
| 822 | len: usize = 0, | |
| 823 | comptime capacity: usize = capacity, | |
| 824 | ||
| 825 | const Self = @This(); | |
| 826 | ||
| 827 | pub fn append(self: *Self, new_item: T) void { | |
| 828 | if (self.len == self.capacity) { | |
| 829 | for (1..self.len) |i| self.items[i - 1] = self.items[i]; | |
| 830 | self.len -= 1; | |
| 831 | } | |
| 832 | self.items[self.len] = new_item; | |
| 833 | self.len += 1; | |
| 834 | } | |
| 835 | }; | |
| 836 | } | |
| 62 | pub usingnamespace @import("./parse_json.zig"); | |
| 63 | pub usingnamespace @import("./isArrayOf.zig"); | |
| 64 | pub usingnamespace @import("./parse_int.zig"); | |
| 65 | pub usingnamespace @import("./parse_bool.zig"); | |
| 66 | pub usingnamespace @import("./to_hex.zig"); | |
| 67 | pub usingnamespace @import("./FieldUnion.zig"); | |
| 68 | pub usingnamespace @import("./LoggingReader.zig"); | |
| 69 | pub usingnamespace @import("./LoggingWriter.zig"); | |
| 70 | pub usingnamespace @import("./Partial.zig"); | |
| 71 | pub usingnamespace @import("./coalescePartial.zig"); | |
| 72 | pub usingnamespace @import("./OneBiggerInt.zig"); | |
| 73 | pub usingnamespace @import("./ReverseFields.zig"); | |
| 74 | pub usingnamespace @import("./stringToEnum.zig"); | |
| 75 | pub usingnamespace @import("./containsAggregate.zig"); | |
| 76 | pub usingnamespace @import("./AnyReader.zig"); | |
| 77 | pub usingnamespace @import("./sum.zig"); | |
| 78 | pub usingnamespace @import("./RingBuffer.zig"); | |
| 837 | 79 | |
| 838 | 80 | pub fn fd_realpath(fd: std.os.fd_t) ![std.fs.MAX_PATH_BYTES:0]u8 { |
| 839 | 81 | switch (builtin.os.tag) { |
src/matchesAll.zig created+12| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn matchesAll(comptime T: type, haystack: []const T, comptime needle: fn (T) bool) bool { | |
| 6 | for (haystack) |c| { | |
| 7 | if (!needle(c)) { | |
| 8 | return false; | |
| 9 | } | |
| 10 | } | |
| 11 | return true; | |
| 12 | } |
src/matchesAny.zig created+12| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn matchesAny(comptime T: type, haystack: []const T, comptime needle: fn (T) bool) bool { | |
| 6 | for (haystack) |c| { | |
| 7 | if (needle(c)) { | |
| 8 | return true; | |
| 9 | } | |
| 10 | } | |
| 11 | return false; | |
| 12 | } |
src/nullifyS.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn nullifyS(s: ?string) ?string { | |
| 6 | if (s == null) return null; | |
| 7 | if (s.?.len == 0) return null; | |
| 8 | return s.?; | |
| 9 | } |
src/opslice.zig created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn opslice(slice: anytype, index: usize) ?std.meta.Child(@TypeOf(slice)) { | |
| 6 | if (slice.len <= index) return null; | |
| 7 | return slice[index]; | |
| 8 | } |
src/parse_bool.zig created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn parse_bool(s: ?string) bool { | |
| 6 | return extras.parse_int(u1, s, 10, 0) > 0; | |
| 7 | } |
src/parse_int.zig created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn parse_int(comptime T: type, s: ?string, b: u8, d: T) T { | |
| 6 | if (s == null) return d; | |
| 7 | return std.fmt.parseInt(T, s.?, b) catch d; | |
| 8 | } |
src/parse_json.zig created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn parse_json(alloc: std.mem.Allocator, input: string) !std.json.Parsed(std.json.Value) { | |
| 6 | return std.json.parseFromSlice(std.json.Value, alloc, input, .{}); | |
| 7 | } |
src/pipe.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn pipe(reader_from: anytype, writer_to: anytype) !void { | |
| 6 | var buf: [std.mem.page_size]u8 = undefined; | |
| 7 | var fifo = std.fifo.LinearFifo(u8, .Slice).init(&buf); | |
| 8 | defer fifo.deinit(); | |
| 9 | try fifo.pump(reader_from, writer_to); | |
| 10 | } |
src/positionalInit.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn positionalInit(comptime T: type, args: std.meta.FieldsTuple(T)) T { | |
| 6 | var t: T = undefined; | |
| 7 | inline for (std.meta.fields(T), 0..) |field, i| { | |
| 8 | @field(t, field.name) = args[i]; | |
| 9 | } | |
| 10 | return t; | |
| 11 | } |
src/ptrCast.zig created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn ptrCast(comptime T: type, ptr: *anyopaque) *T { | |
| 6 | if (@alignOf(T) == 0) @compileError(@typeName(T)); | |
| 7 | return @ptrCast(@alignCast(ptr)); | |
| 8 | } |
src/ptrCastConst.zig created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn ptrCastConst(comptime T: type, ptr: *const anyopaque) *const T { | |
| 6 | if (@alignOf(T) == 0) @compileError(@typeName(T)); | |
| 7 | return @ptrCast(@alignCast(ptr)); | |
| 8 | } |
src/randomBytes.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn randomBytes(comptime len: usize) [len]u8 { | |
| 6 | var bytes: [len]u8 = undefined; | |
| 7 | std.crypto.random.bytes(&bytes); | |
| 8 | return bytes; | |
| 9 | } |
src/randomSlice.zig created+14| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | const alphabet = "0123456789abcdefghijklmnopqrstuvwxyz"; | |
| 6 | ||
| 7 | pub fn randomSlice(alloc: std.mem.Allocator, rand: std.rand.Random, comptime T: type, len: usize) ![]T { | |
| 8 | var buf = try alloc.alloc(T, len); | |
| 9 | var i: usize = 0; | |
| 10 | while (i < len) : (i += 1) { | |
| 11 | buf[i] = alphabet[rand.int(u8) % alphabet.len]; | |
| 12 | } | |
| 13 | return buf; | |
| 14 | } |
src/readBytes.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | const assert = std.debug.assert; | |
| 5 | ||
| 6 | pub fn readBytes(reader: anytype, comptime len: usize) ![len]u8 { | |
| 7 | var bytes: [len]u8 = undefined; | |
| 8 | assert(try reader.readAll(&bytes) == len); | |
| 9 | return bytes; | |
| 10 | } |
src/readBytesAlloc.zig created+12| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn readBytesAlloc(reader: anytype, alloc: std.mem.Allocator, len: usize) ![]u8 { | |
| 6 | var list = std.ArrayListUnmanaged(u8){}; | |
| 7 | try list.ensureTotalCapacityPrecise(alloc, len); | |
| 8 | errdefer list.deinit(alloc); | |
| 9 | list.appendNTimesAssumeCapacity(0, len); | |
| 10 | try reader.readNoEof(list.items[0..len]); | |
| 11 | return list.items; | |
| 12 | } |
src/readExpected.zig created+13| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn readExpected(reader: anytype, expected: []const u8) !bool { | |
| 6 | for (expected) |item| { | |
| 7 | const actual = try reader.readByte(); | |
| 8 | if (actual != item) { | |
| 9 | return false; | |
| 10 | } | |
| 11 | } | |
| 12 | return true; | |
| 13 | } |
src/readType.zig created+31| ... | ... | @@ -0,0 +1,31 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | 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 | |
| 7 | return switch (@typeInfo(T)) { | |
| 8 | .Struct => |t| { | |
| 9 | switch (t.layout) { | |
| 10 | .Auto, .Extern => { | |
| 11 | var s: T = undefined; | |
| 12 | inline for (std.meta.fields(T)) |field| { | |
| 13 | @field(s, field.name) = try readType(reader, field.type, endian); | |
| 14 | } | |
| 15 | return s; | |
| 16 | }, | |
| 17 | .Packed => return @bitCast(try readType(reader, t.backing_integer.?, endian)), | |
| 18 | } | |
| 19 | }, | |
| 20 | .Array => |t| { | |
| 21 | var s: T = undefined; | |
| 22 | for (0..t.len) |i| { | |
| 23 | s[i] = try readType(reader, t.child, endian); | |
| 24 | } | |
| 25 | return s; | |
| 26 | }, | |
| 27 | .Int => try reader.readInt(T, endian), | |
| 28 | .Enum => |t| @enumFromInt(try readType(reader, t.tag_type, endian)), | |
| 29 | else => |e| @compileError(@tagName(e)), | |
| 30 | }; | |
| 31 | } |
src/reduceNumber.zig created+17| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn reduceNumber(alloc: std.mem.Allocator, input: u64, comptime unit: u64, comptime base: string, comptime prefixes: string) !string { | |
| 6 | if (input < unit) { | |
| 7 | return std.fmt.allocPrint(alloc, "{d} {s}", .{ input, base }); | |
| 8 | } | |
| 9 | var div = unit; | |
| 10 | var exp: usize = 0; | |
| 11 | var n = input / unit; | |
| 12 | while (n >= unit) : (n /= unit) { | |
| 13 | div *= unit; | |
| 14 | exp += 1; | |
| 15 | } | |
| 16 | return try std.fmt.allocPrint(alloc, "{d:.3} {s}{s}", .{ @as(f64, @floatFromInt(input)) / @as(f64, @floatFromInt(div)), prefixes[exp .. exp + 1], base }); | |
| 17 | } |
src/safeAdd.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | /// Allows u32 + i16 to work | |
| 6 | pub fn safeAdd(a: anytype, b: anytype) @TypeOf(a) { | |
| 7 | if (b >= 0) { | |
| 8 | return a + @as(@TypeOf(a), @intCast(b)); | |
| 9 | } | |
| 10 | return a - @as(@TypeOf(a), @intCast(-@as(extras.OneBiggerInt(@TypeOf(b)), b))); | |
| 11 | } |
src/safeAddWrap.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | /// Allows u32 +% i16 to work | |
| 6 | pub fn safeAddWrap(a: anytype, b: anytype) @TypeOf(a) { | |
| 7 | if (b >= 0) { | |
| 8 | return a +% @as(@TypeOf(a), @intCast(b)); | |
| 9 | } | |
| 10 | return a -% @as(@TypeOf(a), @intCast(-@as(extras.OneBiggerInt(@TypeOf(b)), b))); | |
| 11 | } |
src/skipToBoundary.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn skipToBoundary(pos: u64, boundary: u64, reader: anytype) !void { | |
| 6 | // const gdiff = counter.bytes_read % 4; | |
| 7 | // for (range(if (gdiff > 0) 4 - gdiff else 0)) |_| { | |
| 8 | const a = pos; | |
| 9 | const b = boundary; | |
| 10 | try reader.skipBytes(((a + (b - 1)) & ~(b - 1)) - a, .{}); | |
| 11 | } |
src/sliceTo.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn sliceTo(comptime T: type, haystack: []const T, needle: T) []const T { | |
| 6 | if (std.mem.indexOfScalar(T, haystack, needle)) |index| { | |
| 7 | return haystack[0..index]; | |
| 8 | } | |
| 9 | return haystack; | |
| 10 | } |
src/sliceToInt.zig created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn sliceToInt(comptime T: type, comptime E: type, slice: []const E) !T { | |
| 6 | const a = @typeInfo(T).Int.bits; | |
| 7 | const b = @typeInfo(E).Int.bits; | |
| 8 | if (a < b * slice.len) return error.Overflow; | |
| 9 | ||
| 10 | var n: T = 0; | |
| 11 | for (slice, 0..) |item, i| { | |
| 12 | const shift: std.math.Log2Int(T) = @intCast(b * (slice.len - 1 - i)); | |
| 13 | n = n | (@as(T, item) << shift); | |
| 14 | } | |
| 15 | return n; | |
| 16 | } |
src/sortBy.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn sortBy(comptime T: type, items: []T, comptime field: std.meta.FieldEnum(T)) void { | |
| 6 | std.mem.sort(T, items, {}, struct { | |
| 7 | fn f(_: void, lhs: T, rhs: T) bool { | |
| 8 | return @field(lhs, @tagName(field)) < @field(rhs, @tagName(field)); | |
| 9 | } | |
| 10 | }.f); | |
| 11 | } |
src/sortBySlice.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn sortBySlice(comptime T: type, items: []T, comptime field: std.meta.FieldEnum(T)) void { | |
| 6 | std.mem.sort(T, items, {}, struct { | |
| 7 | 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 | } | |
| 10 | }.f); | |
| 11 | } |
src/stringToEnum.zig created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn stringToEnum(comptime E: type, str: ?string) ?E { | |
| 6 | return std.meta.stringToEnum(E, str orelse return null); | |
| 7 | } |
src/sum.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn sum(comptime T: type, slice: []const T) T { | |
| 6 | var res: T = 0; | |
| 7 | for (slice) |item| res += item; | |
| 8 | return res; | |
| 9 | } |
src/to_hex.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn to_hex(array: anytype) [array.len * 2]u8 { | |
| 6 | var res: [array.len * 2]u8 = undefined; | |
| 7 | var fbs = std.io.fixedBufferStream(&res); | |
| 8 | std.fmt.format(fbs.writer(), "{x}", .{std.fmt.fmtSliceHexLower(&array)}) catch unreachable; | |
| 9 | return res; | |
| 10 | } |
src/trimPrefix.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn trimPrefix(in: string, prefix: string) string { | |
| 6 | if (std.mem.startsWith(u8, in, prefix)) { | |
| 7 | return in[prefix.len..]; | |
| 8 | } | |
| 9 | return in; | |
| 10 | } |
src/trimPrefixEnsure.zig created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn trimPrefixEnsure(in: string, prefix: string) ?string { | |
| 6 | if (!std.mem.startsWith(u8, in, prefix)) return null; | |
| 7 | return in[prefix.len..]; | |
| 8 | } |
src/trimSuffix.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn trimSuffix(in: string, suffix: string) string { | |
| 6 | if (std.mem.endsWith(u8, in, suffix)) { | |
| 7 | return in[0 .. in.len - suffix.len]; | |
| 8 | } | |
| 9 | return in; | |
| 10 | } |
src/trimSuffixEnsure.zig created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | const std = @import("std"); | |
| 2 | const string = []const u8; | |
| 3 | const extras = @import("./lib.zig"); | |
| 4 | ||
| 5 | pub fn trimSuffixEnsure(in: string, suffix: string) ?string { | |
| 6 | if (!std.mem.endsWith(u8, in, suffix)) return null; | |
| 7 | return in[0 .. in.len - suffix.len]; | |
| 8 | } |