diff --git a/src/AnyReader.zig b/src/AnyReader.zig new file mode 100644 index 0000000000000000000000000000000000000000..037bc91707fb0c6418de9ff1cf47ccfdf09efeef --- /dev/null +++ b/src/AnyReader.zig @@ -0,0 +1,123 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); +const assert = std.debug.assert; + +pub const AnyReader = struct { + readFn: *const fn (*anyopaque, []u8) anyerror!usize, + state: *anyopaque, + + pub fn from(reader: anytype) AnyReader { + const R = @TypeOf(reader); + const ctx = reader.context; + const Ctx = @TypeOf(ctx); + switch (@typeInfo(Ctx)) { + .Pointer => { + const S = struct { + fn foo(s: *anyopaque, buffer: []u8) anyerror!usize { + const r = R{ .context = @ptrCast(@alignCast(s)) }; + return r.read(buffer); + } + }; + return .{ + .readFn = S.foo, + .state = ctx, + }; + }, + .Struct => switch (R) { + std.fs.File.Reader => { + const S = struct { + fn foo(s: *anyopaque, buffer: []u8) anyerror!usize { + const r = R{ .context = .{ .handle = @intCast(@intFromPtr(s)) } }; + return r.read(buffer); + } + }; + return .{ + .readFn = S.foo, + .state = @ptrFromInt(@as(usize, @intCast(ctx.handle))), + }; + }, + else => @compileError(@typeName(R)), + }, + else => |v| @compileError(@typeName(R) ++ " , " ++ @tagName(v)), + } + } + + pub fn read(r: AnyReader, buffer: []u8) anyerror!usize { + return r.readFn(r.state, buffer); + } + + pub fn readByte(self: AnyReader) !u8 { + var result: [1]u8 = undefined; + const amt_read = try self.read(result[0..]); + if (amt_read < 1) return error.EndOfStream; + return result[0]; + } + + pub fn readInt(self: AnyReader, comptime T: type, endian: std.builtin.Endian) !T { + const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))); + return std.mem.readInt(T, &bytes, endian); + } + + pub fn readBytesNoEof(self: AnyReader, comptime num_bytes: usize) ![num_bytes]u8 { + var bytes: [num_bytes]u8 = undefined; + try self.readNoEof(&bytes); + return bytes; + } + + pub fn readNoEof(self: AnyReader, buf: []u8) !void { + const amt_read = try self.readAll(buf); + if (amt_read < buf.len) return error.EndOfStream; + } + + pub fn readAll(self: AnyReader, buffer: []u8) !usize { + return self.readAtLeast(buffer, buffer.len); + } + + pub fn readAtLeast(self: AnyReader, buffer: []u8, len: usize) !usize { + assert(len <= buffer.len); + var index: usize = 0; + while (index < len) { + const amt = try self.read(buffer[index..]); + if (amt == 0) break; + index += amt; + } + return index; + } + + pub fn readAllAlloc(self: AnyReader, allocator: std.mem.Allocator, max_size: usize) ![]u8 { + var array_list = std.ArrayList(u8).init(allocator); + defer array_list.deinit(); + try self.readAllArrayList(&array_list, max_size); + return try array_list.toOwnedSlice(); + } + + pub fn readAllArrayList(self: AnyReader, array_list: *std.ArrayList(u8), max_append_size: usize) !void { + return self.readAllArrayListAligned(null, array_list, max_append_size); + } + + pub fn readAllArrayListAligned(self: AnyReader, comptime alignment: ?u29, array_list: *std.ArrayListAligned(u8, alignment), max_append_size: usize) !void { + try array_list.ensureTotalCapacity(@min(max_append_size, 4096)); + const original_len = array_list.items.len; + var start_index: usize = original_len; + while (true) { + array_list.expandToCapacity(); + const dest_slice = array_list.items[start_index..]; + const bytes_read = try self.readAll(dest_slice); + start_index += bytes_read; + + if (start_index - original_len > max_append_size) { + array_list.shrinkAndFree(original_len + max_append_size); + return error.StreamTooLong; + } + + if (bytes_read != dest_slice.len) { + array_list.shrinkAndFree(start_index); + return; + } + + // This will trigger ArrayList to expand superlinearly at whatever its growth rate is. + try array_list.ensureTotalCapacity(start_index + 1); + } + } +}; diff --git a/src/BufIndexer.zig b/src/BufIndexer.zig new file mode 100644 index 0000000000000000000000000000000000000000..3c87d1a7249da5ed2cf3c0cd685b6cde7cdc73d9 --- /dev/null +++ b/src/BufIndexer.zig @@ -0,0 +1,23 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn BufIndexer(comptime T: type, comptime endian: std.builtin.Endian) type { + return struct { + bytes: [*]const u8, + max_len: usize, + + const Self = @This(); + + pub fn init(bytes: [*]const u8, max_len: usize) Self { + return .{ + .bytes = bytes, + .max_len = max_len, + }; + } + + pub fn at(self: *const Self, idx: usize) T { + return extras.indexBufferT(self.bytes, T, endian, idx, self.max_len); + } + }; +} diff --git a/src/FieldUnion.zig b/src/FieldUnion.zig new file mode 100644 index 0000000000000000000000000000000000000000..197dbf3813f6c217686eeee647962f6388465326 --- /dev/null +++ b/src/FieldUnion.zig @@ -0,0 +1,22 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn FieldUnion(comptime T: type) type { + const infos = std.meta.fields(T); + + var fields: [infos.len]std.builtin.Type.UnionField = undefined; + inline for (infos, 0..) |field, i| { + fields[i] = .{ + .name = field.name, + .type = field.type, + .alignment = field.alignment, + }; + } + return @Type(std.builtin.Type{ .Union = .{ + .layout = .Auto, + .tag_type = std.meta.FieldEnum(T), + .fields = &fields, + .decls = &.{}, + } }); +} diff --git a/src/FixedMaxBuffer.zig b/src/FixedMaxBuffer.zig new file mode 100644 index 0000000000000000000000000000000000000000..92cbe6c233fea1af7f75acc3695ca220f43e7f7a --- /dev/null +++ b/src/FixedMaxBuffer.zig @@ -0,0 +1,48 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); +const assert = std.debug.assert; + +pub fn FixedMaxBuffer(comptime max_len: usize) type { + return struct { + buf: [max_len]u8, + len: usize, + pos: usize, + + const Self = @This(); + pub const Reader = std.io.Reader(*Self, error{}, read); + + pub fn init(r: anytype, runtime_len: usize) !Self { + var fmr = Self{ + .buf = undefined, + .len = runtime_len, + .pos = 0, + }; + _ = try r.readAll(fmr.buf[0..runtime_len]); + return fmr; + } + + pub fn reader(self: *Self) Reader { + return .{ .context = self }; + } + + fn read(self: *Self, dest: []u8) error{}!usize { + const buf = self.buf[0..self.len]; + const size = @min(dest.len, buf.len - self.pos); + const end = self.pos + size; + std.mem.copy(u8, dest[0..size], buf[self.pos..end]); + self.pos = end; + return size; + } + + pub fn readLen(self: *Self, len: usize) []const u8 { + assert(self.pos + len <= self.len); + defer self.pos += len; + return self.buf[self.pos..][0..len]; + } + + pub fn atEnd(self: *const Self) bool { + return self.pos == self.len; + } + }; +} diff --git a/src/LoggingReader.zig b/src/LoggingReader.zig new file mode 100644 index 0000000000000000000000000000000000000000..5376c16c1a341c0edaa5a9713ce8895e2b52f760 --- /dev/null +++ b/src/LoggingReader.zig @@ -0,0 +1,30 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn LoggingReader(comptime T: type, comptime scope: @Type(.EnumLiteral)) type { + return struct { + child_stream: T, + + pub const Error = T.Error; + pub const Reader = std.io.Reader(Self, Error, read); + + const Self = @This(); + + pub fn init(child_stream: T) Self { + return .{ + .child_stream = child_stream, + }; + } + + pub fn reader(self: Self) Reader { + return .{ .context = self }; + } + + fn read(self: Self, dest: []u8) Error!usize { + const n = try self.child_stream.read(dest); + std.log.scoped(scope).debug("{s}", .{dest[0..n]}); + return n; + } + }; +} diff --git a/src/LoggingWriter.zig b/src/LoggingWriter.zig new file mode 100644 index 0000000000000000000000000000000000000000..2146e289f998b2bd9e3805c2811e4ed416d6bc9a --- /dev/null +++ b/src/LoggingWriter.zig @@ -0,0 +1,29 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn LoggingWriter(comptime T: type, comptime scope: @Type(.EnumLiteral)) type { + return struct { + child_stream: T, + + pub const Error = T.Error; + pub const Writer = std.io.Writer(Self, Error, write); + + const Self = @This(); + + pub fn init(child_stream: T) Self { + return .{ + .child_stream = child_stream, + }; + } + + pub fn writer(self: Self) Writer { + return .{ .context = self }; + } + + fn write(self: Self, bytes: []const u8) Error!usize { + std.log.scoped(scope).debug("{s}", .{bytes}); + return self.child_stream.write(bytes); + } + }; +} diff --git a/src/OneBiggerInt.zig b/src/OneBiggerInt.zig new file mode 100644 index 0000000000000000000000000000000000000000..da908cb9a5bf747d9d22633bd0dc73784c7a4321 --- /dev/null +++ b/src/OneBiggerInt.zig @@ -0,0 +1,9 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn OneBiggerInt(comptime T: type) type { + var info = @typeInfo(T); + info.Int.bits += 1; + return @Type(info); +} diff --git a/src/Partial.zig b/src/Partial.zig new file mode 100644 index 0000000000000000000000000000000000000000..27e5fb12ccf099747b9945b497f52c577954706e --- /dev/null +++ b/src/Partial.zig @@ -0,0 +1,24 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn Partial(comptime T: type) type { + const fields_before = std.meta.fields(T); + var fields_after: [fields_before.len]std.builtin.Type.StructField = undefined; + inline for (fields_before, 0..) |item, i| { + fields_after[i] = std.builtin.Type.StructField{ + .name = item.name, + .type = ?item.type, + .default_value = &@as(?item.type, null), + .is_comptime = false, + .alignment = @alignOf(?item.type), + }; + } + return @Type(@unionInit(std.builtin.Type, "Struct", .{ + .layout = .Auto, + .backing_integer = null, + .fields = &fields_after, + .decls = &.{}, + .is_tuple = false, + })); +} diff --git a/src/ReverseFields.zig b/src/ReverseFields.zig new file mode 100644 index 0000000000000000000000000000000000000000..c00bb28072b62a4b4f4d5973908f2f9031871566 --- /dev/null +++ b/src/ReverseFields.zig @@ -0,0 +1,14 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn ReverseFields(comptime T: type) type { + var info = @typeInfo(T).Struct; + const len = info.fields.len; + var fields: [len]std.builtin.Type.StructField = undefined; + for (0..len) |i| { + fields[i] = info.fields[len - 1 - i]; + } + info.fields = &fields; + return @Type(.{ .Struct = info }); +} diff --git a/src/RingBuffer.zig b/src/RingBuffer.zig new file mode 100644 index 0000000000000000000000000000000000000000..f7256d8e2e367b7dd8bf5daaf185f1e5d01e6a15 --- /dev/null +++ b/src/RingBuffer.zig @@ -0,0 +1,22 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn RingBuffer(comptime T: type, comptime capacity: usize) type { + return struct { + items: [capacity]T = undefined, + len: usize = 0, + comptime capacity: usize = capacity, + + const Self = @This(); + + pub fn append(self: *Self, new_item: T) void { + if (self.len == self.capacity) { + for (1..self.len) |i| self.items[i - 1] = self.items[i]; + self.len -= 1; + } + self.items[self.len] = new_item; + self.len += 1; + } + }; +} diff --git a/src/StringerJsonStringifyMixin.zig b/src/StringerJsonStringifyMixin.zig new file mode 100644 index 0000000000000000000000000000000000000000..495e87d2ee1aa32d98ef82cb9cc2da93ffb7bdcc --- /dev/null +++ b/src/StringerJsonStringifyMixin.zig @@ -0,0 +1,18 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn StringerJsonStringifyMixin(comptime S: type) type { + return struct { + pub fn jsonStringify(self: S, options: std.json.StringifyOptions, out_stream: anytype) !void { + var buf: [1024]u8 = undefined; + var fba = std.heap.FixedBufferAllocator.init(&buf); + const alloc = fba.allocator(); + var list = std.ArrayList(u8).init(alloc); + errdefer list.deinit(); + const writer = list.writer(); + try writer.writeAll(try self.toString(alloc)); + try std.json.stringify(list.toOwnedSlice(), options, out_stream); + } + }; +} diff --git a/src/TagNameJsonStringifyMixin.zig b/src/TagNameJsonStringifyMixin.zig new file mode 100644 index 0000000000000000000000000000000000000000..6903a7a73c0de6c4b62c2df70e19c3ba2b8b3f90 --- /dev/null +++ b/src/TagNameJsonStringifyMixin.zig @@ -0,0 +1,11 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn TagNameJsonStringifyMixin(comptime S: type) type { + return struct { + pub fn jsonStringify(self: S, options: std.json.StringifyOptions, out_stream: anytype) !void { + try std.json.stringify(@tagName(self), options, out_stream); + } + }; +} diff --git a/src/addSentinel.zig b/src/addSentinel.zig new file mode 100644 index 0000000000000000000000000000000000000000..be595aab5d0a04681484eb14e488385778947e63 --- /dev/null +++ b/src/addSentinel.zig @@ -0,0 +1,11 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn addSentinel(alloc: std.mem.Allocator, comptime T: type, input: []const T, comptime sentinel: T) ![:sentinel]const T { + var list = try std.ArrayList(T).initCapacity(alloc, input.len + 1); + try list.appendSlice(input); + try list.append(sentinel); + const str = list.toOwnedSlice(); + return str[0 .. str.len - 1 :sentinel]; +} diff --git a/src/asciiUpper.zig b/src/asciiUpper.zig new file mode 100644 index 0000000000000000000000000000000000000000..94d39d22a4517d049f4fdc40c050bd28d6c5c0c3 --- /dev/null +++ b/src/asciiUpper.zig @@ -0,0 +1,11 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn asciiUpper(alloc: std.mem.Allocator, input: string) ![]u8 { + var buf = try alloc.dupe(u8, input); + for (0..buf.len) |i| { + buf[i] = std.ascii.toUpper(buf[i]); + } + return buf; +} diff --git a/src/base64DecodeAlloc.zig b/src/base64DecodeAlloc.zig new file mode 100644 index 0000000000000000000000000000000000000000..4c89c2eed3f773a448521e16ec16431f9731d303 --- /dev/null +++ b/src/base64DecodeAlloc.zig @@ -0,0 +1,10 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn base64DecodeAlloc(alloc: std.mem.Allocator, input: string) !string { + const base64 = std.base64.standard.Decoder; + var buf = try alloc.alloc(u8, try base64.calcSizeForSlice(input)); + try base64.decode(buf, input); + return buf; +} diff --git a/src/base64EncodeAlloc.zig b/src/base64EncodeAlloc.zig new file mode 100644 index 0000000000000000000000000000000000000000..a3f4fbbed30a92c42fe4cca90ae4ea8781301e0d --- /dev/null +++ b/src/base64EncodeAlloc.zig @@ -0,0 +1,9 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn base64EncodeAlloc(alloc: std.mem.Allocator, input: string) !string { + const base64 = std.base64.standard.Encoder; + var buf = try alloc.alloc(u8, base64.calcSize(input.len)); + return base64.encode(buf, input); +} diff --git a/src/coalescePartial.zig b/src/coalescePartial.zig new file mode 100644 index 0000000000000000000000000000000000000000..e27e85fd37ebe49ce92277ba287b262d11031cd6 --- /dev/null +++ b/src/coalescePartial.zig @@ -0,0 +1,11 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn coalescePartial(comptime T: type, into: T, from: extras.Partial(T)) T { + var temp = into; + inline for (comptime std.meta.fieldNames(T)) |name| { + if (@field(from, name)) |val| @field(temp, name) = val; + } + return temp; +} diff --git a/src/containsAggregate.zig b/src/containsAggregate.zig new file mode 100644 index 0000000000000000000000000000000000000000..b2cae38095a05148875a569305427d5cfb4057f6 --- /dev/null +++ b/src/containsAggregate.zig @@ -0,0 +1,12 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn containsAggregate(comptime T: type, haystack: []const T, needle: T) bool { + for (haystack) |item| { + if (T.eql(item, needle)) { + return true; + } + } + return false; +} diff --git a/src/containsString.zig b/src/containsString.zig new file mode 100644 index 0000000000000000000000000000000000000000..b468d9001943e3b704a7bc8b2ae5fb63a0b3494f --- /dev/null +++ b/src/containsString.zig @@ -0,0 +1,12 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn containsString(haystack: []const string, needle: string) bool { + for (haystack) |item| { + if (std.mem.eql(u8, item, needle)) { + return true; + } + } + return false; +} diff --git a/src/countScalar.zig b/src/countScalar.zig new file mode 100644 index 0000000000000000000000000000000000000000..28087c62fb43990c554366c9b6c160ca2272744f --- /dev/null +++ b/src/countScalar.zig @@ -0,0 +1,14 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn countScalar(comptime T: type, haystack: []const T, needle: T) usize { + var found: usize = 0; + + for (haystack) |item| { + if (item == needle) { + found += 1; + } + } + return found; +} diff --git a/src/dirSize.zig b/src/dirSize.zig new file mode 100644 index 0000000000000000000000000000000000000000..c497129d1b13b73c0346dd5ef31f994de3b2bc7d --- /dev/null +++ b/src/dirSize.zig @@ -0,0 +1,15 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn dirSize(alloc: std.mem.Allocator, dir: std.fs.IterableDir) !u64 { + var res: u64 = 0; + + var walk = try dir.walk(alloc); + defer walk.deinit(); + while (try walk.next()) |entry| { + if (entry.kind != .File) continue; + res += try extras.fileSize(dir.dir, entry.path); + } + return res; +} diff --git a/src/doesFileExist.zig b/src/doesFileExist.zig new file mode 100644 index 0000000000000000000000000000000000000000..636a05d7f6dd4aedcb402fa3edba2523c3d991ab --- /dev/null +++ b/src/doesFileExist.zig @@ -0,0 +1,13 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn doesFileExist(dir: ?std.fs.Dir, fpath: []const u8) !bool { + const file = (dir orelse std.fs.cwd()).openFile(fpath, .{}) catch |e| switch (e) { + error.FileNotFound => return false, + error.IsDir => return true, + else => return e, + }; + defer file.close(); + return true; +} diff --git a/src/doesFolderExist.zig b/src/doesFolderExist.zig new file mode 100644 index 0000000000000000000000000000000000000000..ab120e14f6b14c0ae1d9bfc90f88108cfba23244 --- /dev/null +++ b/src/doesFolderExist.zig @@ -0,0 +1,17 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn doesFolderExist(dir: ?std.fs.Dir, fpath: []const u8) !bool { + const file = (dir orelse std.fs.cwd()).openFile(fpath, .{}) catch |e| switch (e) { + error.FileNotFound => return false, + error.IsDir => return true, + else => return e, + }; + defer file.close(); + const s = try file.stat(); + if (s.kind != .directory) { + return false; + } + return true; +} diff --git a/src/ensureFieldSubset.zig b/src/ensureFieldSubset.zig new file mode 100644 index 0000000000000000000000000000000000000000..b27ba8c12cd1a928f041cbec8a6b4af03424b3a7 --- /dev/null +++ b/src/ensureFieldSubset.zig @@ -0,0 +1,9 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn ensureFieldSubset(comptime L: type, comptime R: type) void { + for (std.meta.fields(L)) |item| { + if (!@hasField(R, item.name)) @compileError(std.fmt.comptimePrint("{s} is missing the {s} field from {s}", .{ R, item.name, L })); + } +} diff --git a/src/fileList.zig b/src/fileList.zig new file mode 100644 index 0000000000000000000000000000000000000000..6e80bf06a4d56ffaa4fc981fa4a7433e72cdc7e3 --- /dev/null +++ b/src/fileList.zig @@ -0,0 +1,16 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn fileList(alloc: std.mem.Allocator, dir: std.fs.IterableDir) ![]string { + var list = std.ArrayList(string).init(alloc); + defer list.deinit(); + + var walk = try dir.walk(alloc); + defer walk.deinit(); + while (try walk.next()) |entry| { + if (entry.kind != .file) continue; + try list.append(try alloc.dupe(u8, entry.path)); + } + return list.toOwnedSlice(); +} diff --git a/src/fileSize.zig b/src/fileSize.zig new file mode 100644 index 0000000000000000000000000000000000000000..1c4df45361162bf89d1514ed77d4e1e9b357408b --- /dev/null +++ b/src/fileSize.zig @@ -0,0 +1,10 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn fileSize(dir: std.fs.Dir, sub_path: string) !u64 { + const f = try dir.openFile(sub_path, .{}); + defer f.close(); + const s = try f.stat(); + return s.size; +} diff --git a/src/fmtByteCountIEC.zig b/src/fmtByteCountIEC.zig new file mode 100644 index 0000000000000000000000000000000000000000..726b996279df89785e474cf622ad3714eca27d81 --- /dev/null +++ b/src/fmtByteCountIEC.zig @@ -0,0 +1,7 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn fmtByteCountIEC(alloc: std.mem.Allocator, b: u64) !string { + return try extras.reduceNumber(alloc, b, 1024, "B", "KMGTPEZYRQ"); +} diff --git a/src/fmtReplacer.zig b/src/fmtReplacer.zig new file mode 100644 index 0000000000000000000000000000000000000000..76b074affe55eabc33fe726e8a2eb3da5cffdb82 --- /dev/null +++ b/src/fmtReplacer.zig @@ -0,0 +1,28 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn fmtReplacer(bytes: string, from: u8, to: u8) std.fmt.Formatter(formatReplacer) { + return .{ .data = .{ + .bytes = bytes, + .from = from, + .to = to, + } }; +} + +fn formatReplacer( + self: struct { + bytes: string, + from: u8, + to: u8, + }, + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = fmt; + _ = options; + for (self.bytes) |c| { + try writer.writeByte(if (c == self.from) self.to else @intCast(c)); + } +} diff --git a/src/hashBytes.zig b/src/hashBytes.zig new file mode 100644 index 0000000000000000000000000000000000000000..7780110bf76bf333554bc742eef87de5d7a3b007 --- /dev/null +++ b/src/hashBytes.zig @@ -0,0 +1,11 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn hashBytes(comptime Algo: type, bytes: []const u8) [Algo.digest_length]u8 { + var h = Algo.init(.{}); + var out: [Algo.digest_length]u8 = undefined; + h.update(bytes); + h.final(&out); + return out; +} diff --git a/src/hashFile.zig b/src/hashFile.zig new file mode 100644 index 0000000000000000000000000000000000000000..e4861ddb614f57651e94e77280fc5dc1917eb75d --- /dev/null +++ b/src/hashFile.zig @@ -0,0 +1,16 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn hashFile(dir: std.fs.Dir, sub_path: string, comptime Algo: type) ![Algo.digest_length * 2]u8 { + const file = try dir.openFile(sub_path, .{}); + defer file.close(); + var h = Algo.init(.{}); + var out: [Algo.digest_length]u8 = undefined; + try extras.pipe(file.reader(), h.writer()); + h.final(&out); + var res: [Algo.digest_length * 2]u8 = undefined; + var fbs = std.io.fixedBufferStream(&res); + try std.fmt.format(fbs.writer(), "{x}", .{std.fmt.fmtSliceHexLower(&out)}); + return res; +} diff --git a/src/indexBufferT.zig b/src/indexBufferT.zig new file mode 100644 index 0000000000000000000000000000000000000000..0cde81f51f69340ee0b295792428502ff1f1dab9 --- /dev/null +++ b/src/indexBufferT.zig @@ -0,0 +1,11 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn indexBufferT(bytes: [*]const u8, comptime T: type, endian: std.builtin.Endian, idx: usize, max_len: usize) T { + std.debug.assert(idx < max_len); + var fbs = std.io.fixedBufferStream((bytes + (idx * @sizeOf(T)))[0..@sizeOf(T)]); + return extras.readType(fbs.reader(), T, endian) catch |err| switch (err) { + error.EndOfStream => unreachable, // assert above has been violated + }; +} diff --git a/src/is.zig b/src/is.zig new file mode 100644 index 0000000000000000000000000000000000000000..e930efb0e403a0ae7cb7176c5d2dfbab122f34c4 --- /dev/null +++ b/src/is.zig @@ -0,0 +1,9 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +/// ?A == A fails +/// ?A == @as(?A, b) works +pub fn is(a: anytype, b: @TypeOf(a)) bool { + return a == b; +} diff --git a/src/isArrayOf.zig b/src/isArrayOf.zig new file mode 100644 index 0000000000000000000000000000000000000000..0cc73b85efa779c6267f788b98f8e09c9eaff5ac --- /dev/null +++ b/src/isArrayOf.zig @@ -0,0 +1,15 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn isArrayOf(comptime T: type) std.meta.trait.TraitFn { + const Closure = struct { + pub fn trait(comptime C: type) bool { + return switch (@typeInfo(C)) { + .Array => |ti| ti.child == T, + else => false, + }; + } + }; + return Closure.trait; +} diff --git a/src/joinPartial.zig b/src/joinPartial.zig new file mode 100644 index 0000000000000000000000000000000000000000..b0b7fe26d2784ed135a11479f2cd1ec544773d4b --- /dev/null +++ b/src/joinPartial.zig @@ -0,0 +1,11 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn joinPartial(comptime P: type, a: P, b: P) P { + var temp = a; + inline for (comptime std.meta.fieldNames(P)) |name| { + if (@field(b, name)) |val| @field(temp, name) = val; + } + return temp; +} diff --git a/src/lessThanSlice.zig b/src/lessThanSlice.zig new file mode 100644 index 0000000000000000000000000000000000000000..04eaf6594eac2dace72ea3433dda419418c9aab1 --- /dev/null +++ b/src/lessThanSlice.zig @@ -0,0 +1,15 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn lessThanSlice(comptime T: type) fn (void, T, T) bool { + return struct { + fn f(_: void, lhs: T, rhs: T) bool { + const result = for (0..@min(lhs.len, rhs.len)) |i| { + if (lhs[i] < rhs[i]) break true; + if (lhs[i] > rhs[i]) break false; + } else false; + return result; + } + }.f; +} diff --git a/src/lib.zig b/src/lib.zig index 8357e0b390ae04436523f24c1887fa3f29d86bd2..c50f4245830ad0f085ab63e7eb8230fd48e60c07 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -1,839 +1,81 @@ const std = @import("std"); const string = []const u8; +const extras = @import("./lib.zig"); const assert = std.debug.assert; const builtin = @import("builtin"); -pub fn fmtByteCountIEC(alloc: std.mem.Allocator, b: u64) !string { - return try reduceNumber(alloc, b, 1024, "B", "KMGTPEZYRQ"); -} - -pub fn reduceNumber(alloc: std.mem.Allocator, input: u64, comptime unit: u64, comptime base: string, comptime prefixes: string) !string { - if (input < unit) { - return std.fmt.allocPrint(alloc, "{d} {s}", .{ input, base }); - } - var div = unit; - var exp: usize = 0; - var n = input / unit; - while (n >= unit) : (n /= unit) { - div *= unit; - exp += 1; - } - return try std.fmt.allocPrint(alloc, "{d:.3} {s}{s}", .{ @as(f64, @floatFromInt(input)) / @as(f64, @floatFromInt(div)), prefixes[exp .. exp + 1], base }); -} - -pub fn addSentinel(alloc: std.mem.Allocator, comptime T: type, input: []const T, comptime sentinel: T) ![:sentinel]const T { - var list = try std.ArrayList(T).initCapacity(alloc, input.len + 1); - try list.appendSlice(input); - try list.append(sentinel); - const str = list.toOwnedSlice(); - return str[0 .. str.len - 1 :sentinel]; -} - -const alphabet = "0123456789abcdefghijklmnopqrstuvwxyz"; - -pub fn randomSlice(alloc: std.mem.Allocator, rand: std.rand.Random, comptime T: type, len: usize) ![]T { - var buf = try alloc.alloc(T, len); - var i: usize = 0; - while (i < len) : (i += 1) { - buf[i] = alphabet[rand.int(u8) % alphabet.len]; - } - return buf; -} - -pub fn trimPrefix(in: string, prefix: string) string { - if (std.mem.startsWith(u8, in, prefix)) { - return in[prefix.len..]; - } - return in; -} - -pub fn trimPrefixEnsure(in: string, prefix: string) ?string { - if (!std.mem.startsWith(u8, in, prefix)) return null; - return in[prefix.len..]; -} - -pub fn trimSuffix(in: string, suffix: string) string { - if (std.mem.endsWith(u8, in, suffix)) { - return in[0 .. in.len - suffix.len]; - } - return in; -} - -pub fn trimSuffixEnsure(in: string, suffix: string) ?string { - if (!std.mem.endsWith(u8, in, suffix)) return null; - return in[0 .. in.len - suffix.len]; -} - -pub fn base64EncodeAlloc(alloc: std.mem.Allocator, input: string) !string { - const base64 = std.base64.standard.Encoder; - var buf = try alloc.alloc(u8, base64.calcSize(input.len)); - return base64.encode(buf, input); -} - -pub fn base64DecodeAlloc(alloc: std.mem.Allocator, input: string) !string { - const base64 = std.base64.standard.Decoder; - var buf = try alloc.alloc(u8, try base64.calcSizeForSlice(input)); - try base64.decode(buf, input); - return buf; -} - -pub fn asciiUpper(alloc: std.mem.Allocator, input: string) ![]u8 { - var buf = try alloc.dupe(u8, input); - for (0..buf.len) |i| { - buf[i] = std.ascii.toUpper(buf[i]); - } - return buf; -} - -pub fn doesFolderExist(dir: ?std.fs.Dir, fpath: []const u8) !bool { - const file = (dir orelse std.fs.cwd()).openFile(fpath, .{}) catch |e| switch (e) { - error.FileNotFound => return false, - error.IsDir => return true, - else => return e, - }; - defer file.close(); - const s = try file.stat(); - if (s.kind != .directory) { - return false; - } - return true; -} - -pub fn doesFileExist(dir: ?std.fs.Dir, fpath: []const u8) !bool { - const file = (dir orelse std.fs.cwd()).openFile(fpath, .{}) catch |e| switch (e) { - error.FileNotFound => return false, - error.IsDir => return true, - else => return e, - }; - defer file.close(); - return true; -} - -pub fn sliceToInt(comptime T: type, comptime E: type, slice: []const E) !T { - const a = @typeInfo(T).Int.bits; - const b = @typeInfo(E).Int.bits; - if (a < b * slice.len) return error.Overflow; - - var n: T = 0; - for (slice, 0..) |item, i| { - const shift: std.math.Log2Int(T) = @intCast(b * (slice.len - 1 - i)); - n = n | (@as(T, item) << shift); - } - return n; -} - -pub fn fileList(alloc: std.mem.Allocator, dir: std.fs.IterableDir) ![]string { - var list = std.ArrayList(string).init(alloc); - defer list.deinit(); - - var walk = try dir.walk(alloc); - defer walk.deinit(); - while (try walk.next()) |entry| { - if (entry.kind != .file) continue; - try list.append(try alloc.dupe(u8, entry.path)); - } - return list.toOwnedSlice(); -} - -pub fn dirSize(alloc: std.mem.Allocator, dir: std.fs.IterableDir) !u64 { - var res: u64 = 0; - - var walk = try dir.walk(alloc); - defer walk.deinit(); - while (try walk.next()) |entry| { - if (entry.kind != .File) continue; - res += try fileSize(dir.dir, entry.path); - } - return res; -} - -pub fn fileSize(dir: std.fs.Dir, sub_path: string) !u64 { - const f = try dir.openFile(sub_path, .{}); - defer f.close(); - const s = try f.stat(); - return s.size; -} - -pub fn hashFile(dir: std.fs.Dir, sub_path: string, comptime Algo: type) ![Algo.digest_length * 2]u8 { - const file = try dir.openFile(sub_path, .{}); - defer file.close(); - var h = Algo.init(.{}); - var out: [Algo.digest_length]u8 = undefined; - try pipe(file.reader(), h.writer()); - h.final(&out); - var res: [Algo.digest_length * 2]u8 = undefined; - var fbs = std.io.fixedBufferStream(&res); - try std.fmt.format(fbs.writer(), "{x}", .{std.fmt.fmtSliceHexLower(&out)}); - return res; -} - -pub fn pipe(reader_from: anytype, writer_to: anytype) !void { - var buf: [std.mem.page_size]u8 = undefined; - var fifo = std.fifo.LinearFifo(u8, .Slice).init(&buf); - defer fifo.deinit(); - try fifo.pump(reader_from, writer_to); -} - -pub fn StringerJsonStringifyMixin(comptime S: type) type { - return struct { - pub fn jsonStringify(self: S, options: std.json.StringifyOptions, out_stream: anytype) !void { - var buf: [1024]u8 = undefined; - var fba = std.heap.FixedBufferAllocator.init(&buf); - const alloc = fba.allocator(); - var list = std.ArrayList(u8).init(alloc); - errdefer list.deinit(); - const writer = list.writer(); - try writer.writeAll(try self.toString(alloc)); - try std.json.stringify(list.toOwnedSlice(), options, out_stream); - } - }; -} - -pub fn TagNameJsonStringifyMixin(comptime S: type) type { - return struct { - pub fn jsonStringify(self: S, options: std.json.StringifyOptions, out_stream: anytype) !void { - try std.json.stringify(@tagName(self), options, out_stream); - } - }; -} - -pub fn countScalar(comptime T: type, haystack: []const T, needle: T) usize { - var found: usize = 0; - - for (haystack) |item| { - if (item == needle) { - found += 1; - } - } - return found; -} - -pub fn ptrCast(comptime T: type, ptr: *anyopaque) *T { - if (@alignOf(T) == 0) @compileError(@typeName(T)); - return @ptrCast(@alignCast(ptr)); -} - -pub fn ptrCastConst(comptime T: type, ptr: *const anyopaque) *const T { - if (@alignOf(T) == 0) @compileError(@typeName(T)); - return @ptrCast(@alignCast(ptr)); -} - -pub fn sortBy(comptime T: type, items: []T, comptime field: std.meta.FieldEnum(T)) void { - std.mem.sort(T, items, {}, struct { - fn f(_: void, lhs: T, rhs: T) bool { - return @field(lhs, @tagName(field)) < @field(rhs, @tagName(field)); - } - }.f); -} - -pub fn sortBySlice(comptime T: type, items: []T, comptime field: std.meta.FieldEnum(T)) void { - std.mem.sort(T, items, {}, struct { - fn f(_: void, lhs: T, rhs: T) bool { - return lessThanSlice(std.meta.FieldType(T, field))({}, @field(lhs, @tagName(field)), @field(rhs, @tagName(field))); - } - }.f); -} - -pub fn lessThanSlice(comptime T: type) fn (void, T, T) bool { - return struct { - fn f(_: void, lhs: T, rhs: T) bool { - const result = for (0..@min(lhs.len, rhs.len)) |i| { - if (lhs[i] < rhs[i]) break true; - if (lhs[i] > rhs[i]) break false; - } else false; - return result; - } - }.f; -} - -pub fn containsString(haystack: []const string, needle: string) bool { - for (haystack) |item| { - if (std.mem.eql(u8, item, needle)) { - return true; - } - } - return false; -} - -pub fn FieldsTuple(comptime T: type) type { - const fields = std.meta.fields(T); - var types: [fields.len]type = undefined; - for (fields, 0..) |item, i| { - types[i] = item.type; - } - return std.meta.Tuple(&types); -} - -pub fn positionalInit(comptime T: type, args: FieldsTuple(T)) T { - var t: T = undefined; - inline for (std.meta.fields(T), 0..) |field, i| { - @field(t, field.name) = args[i]; - } - return t; -} - -pub fn d2index(d1len: usize, d1: usize, d2: usize) usize { - return (d1len * d2) + d1; -} - -pub fn ensureFieldSubset(comptime L: type, comptime R: type) void { - for (std.meta.fields(L)) |item| { - if (!@hasField(R, item.name)) @compileError(std.fmt.comptimePrint("{s} is missing the {s} field from {s}", .{ R, item.name, L })); - } -} - -pub fn fmtReplacer(bytes: string, from: u8, to: u8) std.fmt.Formatter(formatReplacer) { - return .{ .data = .{ .bytes = bytes, .from = from, .to = to } }; -} - -const ReplacerData = struct { bytes: string, from: u8, to: u8 }; -fn formatReplacer(self: ReplacerData, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { - _ = fmt; - _ = options; - for (self.bytes) |c| { - try writer.writeByte(if (c == self.from) self.to else @intCast(c)); - } -} - -pub fn randomBytes(comptime len: usize) [len]u8 { - var bytes: [len]u8 = undefined; - std.crypto.random.bytes(&bytes); - return bytes; -} - -pub fn writeEnumBig(writer: anytype, comptime E: type, value: E) !void { - try writer.writeIntBig(@typeInfo(E).Enum.tag_type, @intFromEnum(value)); -} - -pub fn readEnumBig(reader: anytype, comptime E: type) !E { - return @enumFromInt(try reader.readIntBig(@typeInfo(E).Enum.tag_type)); -} - -pub fn readExpected(reader: anytype, expected: []const u8) !bool { - for (expected) |item| { - const actual = try reader.readByte(); - if (actual != item) { - return false; - } - } - return true; -} - -pub fn readBytes(reader: anytype, comptime len: usize) ![len]u8 { - var bytes: [len]u8 = undefined; - assert(try reader.readAll(&bytes) == len); - return bytes; -} - -pub fn FixedMaxBuffer(comptime max_len: usize) type { - return struct { - buf: [max_len]u8, - len: usize, - pos: usize, - - const Self = @This(); - pub const Reader = std.io.Reader(*Self, error{}, read); - - pub fn init(r: anytype, runtime_len: usize) !Self { - var fmr = Self{ - .buf = undefined, - .len = runtime_len, - .pos = 0, - }; - _ = try r.readAll(fmr.buf[0..runtime_len]); - return fmr; - } - - pub fn reader(self: *Self) Reader { - return .{ .context = self }; - } - - fn read(self: *Self, dest: []u8) error{}!usize { - const buf = self.buf[0..self.len]; - const size = @min(dest.len, buf.len - self.pos); - const end = self.pos + size; - std.mem.copy(u8, dest[0..size], buf[self.pos..end]); - self.pos = end; - return size; - } - - pub fn readLen(self: *Self, len: usize) []const u8 { - assert(self.pos + len <= self.len); - defer self.pos += len; - return self.buf[self.pos..][0..len]; - } - - pub fn atEnd(self: *const Self) bool { - return self.pos == self.len; - } - }; -} - -pub fn hashBytes(comptime Algo: type, bytes: []const u8) [Algo.digest_length]u8 { - var h = Algo.init(.{}); - var out: [Algo.digest_length]u8 = undefined; - h.update(bytes); - h.final(&out); - return out; -} - -pub fn readType(reader: anytype, comptime T: type, endian: std.builtin.Endian) !T { - if (T == u8) return reader.readByte(); // single bytes dont have an endianness - return switch (@typeInfo(T)) { - .Struct => |t| { - switch (t.layout) { - .Auto, .Extern => { - var s: T = undefined; - inline for (std.meta.fields(T)) |field| { - @field(s, field.name) = try readType(reader, field.type, endian); - } - return s; - }, - .Packed => return @bitCast(try readType(reader, t.backing_integer.?, endian)), - } - }, - .Array => |t| { - var s: T = undefined; - for (0..t.len) |i| { - s[i] = try readType(reader, t.child, endian); - } - return s; - }, - .Int => try reader.readInt(T, endian), - .Enum => |t| @enumFromInt(try readType(reader, t.tag_type, endian)), - else => |e| @compileError(@tagName(e)), - }; -} - -pub fn indexBufferT(bytes: [*]const u8, comptime T: type, endian: std.builtin.Endian, idx: usize, max_len: usize) T { - std.debug.assert(idx < max_len); - var fbs = std.io.fixedBufferStream((bytes + (idx * @sizeOf(T)))[0..@sizeOf(T)]); - return readType(fbs.reader(), T, endian) catch |err| switch (err) { - error.EndOfStream => unreachable, // assert above has been violated - }; -} - -pub fn BufIndexer(comptime T: type, comptime endian: std.builtin.Endian) type { - return struct { - bytes: [*]const u8, - max_len: usize, - - const Self = @This(); - - pub fn init(bytes: [*]const u8, max_len: usize) Self { - return .{ - .bytes = bytes, - .max_len = max_len, - }; - } - - pub fn at(self: *const Self, idx: usize) T { - return indexBufferT(self.bytes, T, endian, idx, self.max_len); - } - }; -} - -pub fn skipToBoundary(pos: u64, boundary: u64, reader: anytype) !void { - // const gdiff = counter.bytes_read % 4; - // for (range(if (gdiff > 0) 4 - gdiff else 0)) |_| { - const a = pos; - const b = boundary; - try reader.skipBytes(((a + (b - 1)) & ~(b - 1)) - a, .{}); -} - -/// ?A == A fails -/// ?A == @as(?A, b) works -pub fn is(a: anytype, b: @TypeOf(a)) bool { - return a == b; -} - -/// Allows u32 + i16 to work -pub fn safeAdd(a: anytype, b: anytype) @TypeOf(a) { - if (b >= 0) { - return a + @as(@TypeOf(a), @intCast(b)); - } - return a - @as(@TypeOf(a), @intCast(-@as(OneBiggerInt(@TypeOf(b)), b))); -} - -/// Allows u32 + i16 to work -pub fn safeAddWrap(a: anytype, b: anytype) @TypeOf(a) { - if (b >= 0) { - return a +% @as(@TypeOf(a), @intCast(b)); - } - return a -% @as(@TypeOf(a), @intCast(-@as(OneBiggerInt(@TypeOf(b)), b))); -} - -pub fn readBytesAlloc(reader: anytype, alloc: std.mem.Allocator, len: usize) ![]u8 { - var list = std.ArrayListUnmanaged(u8){}; - try list.ensureTotalCapacityPrecise(alloc, len); - errdefer list.deinit(alloc); - list.appendNTimesAssumeCapacity(0, len); - try reader.readNoEof(list.items[0..len]); - return list.items; -} - -pub fn readFile(dir: std.fs.Dir, sub_path: string, alloc: std.mem.Allocator) !string { - _ = dir; - _ = sub_path; - _ = alloc; - @compileError("use std.fs.Dir.readFileAlloc instead"); -} - -pub fn nullifyS(s: ?string) ?string { - if (s == null) return null; - if (s.?.len == 0) return null; - return s.?; -} - -pub fn sliceTo(comptime T: type, haystack: []const T, needle: T) []const T { - if (std.mem.indexOfScalar(T, haystack, needle)) |index| { - return haystack[0..index]; - } - return haystack; -} - -pub fn matchesAll(comptime T: type, haystack: []const T, comptime needle: fn (T) bool) bool { - for (haystack) |c| { - if (!needle(c)) { - return false; - } - } - return true; -} - -pub fn matchesAny(comptime T: type, haystack: []const T, comptime needle: fn (T) bool) bool { - for (haystack) |c| { - if (needle(c)) { - return true; - } - } - return false; -} - -pub fn opslice(slice: anytype, index: usize) ?std.meta.Child(@TypeOf(slice)) { - if (slice.len <= index) return null; - return slice[index]; -} +pub usingnamespace @import("./reduceNumber.zig"); +pub usingnamespace @import("./fmtByteCountIEC.zig"); +pub usingnamespace @import("./addSentinel.zig"); +pub usingnamespace @import("./randomSlice.zig"); +pub usingnamespace @import("./trimPrefix.zig"); +pub usingnamespace @import("./trimPrefixEnsure.zig"); +pub usingnamespace @import("./trimSuffix.zig"); +pub usingnamespace @import("./trimSuffixEnsure.zig"); +pub usingnamespace @import("./base64EncodeAlloc.zig"); +pub usingnamespace @import("./base64DecodeAlloc.zig"); +pub usingnamespace @import("./asciiUpper.zig"); +pub usingnamespace @import("./doesFileExist.zig"); +pub usingnamespace @import("./doesFolderExist.zig"); +pub usingnamespace @import("./sliceToInt.zig"); +pub usingnamespace @import("./fileList.zig"); +pub usingnamespace @import("./fileSize.zig"); +pub usingnamespace @import("./dirSize.zig"); +pub usingnamespace @import("./hashFile.zig"); +pub usingnamespace @import("./pipe.zig"); +pub usingnamespace @import("./StringerJsonStringifyMixin.zig"); +pub usingnamespace @import("./TagNameJsonStringifyMixin.zig"); +pub usingnamespace @import("./countScalar.zig"); +pub usingnamespace @import("./ptrCast.zig"); +pub usingnamespace @import("./ptrCastConst.zig"); +pub usingnamespace @import("./sortBy.zig"); +pub usingnamespace @import("./sortBySlice.zig"); +pub usingnamespace @import("./lessThanSlice.zig"); +pub usingnamespace @import("./containsString.zig"); +pub usingnamespace @import("./positionalInit.zig"); +pub usingnamespace @import("./ensureFieldSubset.zig"); +pub usingnamespace @import("./fmtReplacer.zig"); +pub usingnamespace @import("./randomBytes.zig"); +pub usingnamespace @import("./readExpected.zig"); +pub usingnamespace @import("./readBytes.zig"); +pub usingnamespace @import("./FixedMaxBuffer.zig"); +pub usingnamespace @import("./hashBytes.zig"); +pub usingnamespace @import("./readType.zig"); +pub usingnamespace @import("./indexBufferT.zig"); +pub usingnamespace @import("./BufIndexer.zig"); +pub usingnamespace @import("./skipToBoundary.zig"); +pub usingnamespace @import("./is.zig"); +pub usingnamespace @import("./safeAdd.zig"); +pub usingnamespace @import("./safeAddWrap.zig"); +pub usingnamespace @import("./readBytesAlloc.zig"); +pub usingnamespace @import("./nullifyS.zig"); +pub usingnamespace @import("./sliceTo.zig"); +pub usingnamespace @import("./matchesAll.zig"); +pub usingnamespace @import("./matchesAny.zig"); +pub usingnamespace @import("./opslice.zig"); pub fn assertLog(ok: bool, comptime message: string, args: anytype) void { if (!ok) std.log.err("assertion failure: " ++ message, args); if (!ok) unreachable; // assertion failure } -pub fn parse_json(alloc: std.mem.Allocator, input: string) !std.json.Parsed(std.json.Value) { - return std.json.parseFromSlice(std.json.Value, alloc, input, .{}); -} - -pub fn isArrayOf(comptime T: type) std.meta.trait.TraitFn { - const Closure = struct { - pub fn trait(comptime C: type) bool { - return switch (@typeInfo(C)) { - .Array => |ti| ti.child == T, - else => false, - }; - } - }; - return Closure.trait; -} - -pub fn parse_int(comptime T: type, s: ?string, b: u8, d: T) T { - if (s == null) return d; - return std.fmt.parseInt(T, s.?, b) catch d; -} - -pub fn parse_bool(s: ?string) bool { - return parse_int(u1, s, 10, 0) > 0; -} - -pub fn to_hex(array: anytype) [array.len * 2]u8 { - var res: [array.len * 2]u8 = undefined; - var fbs = std.io.fixedBufferStream(&res); - std.fmt.format(fbs.writer(), "{x}", .{std.fmt.fmtSliceHexLower(&array)}) catch unreachable; - return res; -} - -pub fn FieldUnion(comptime T: type) type { - const infos = std.meta.fields(T); - - var fields: [infos.len]std.builtin.Type.UnionField = undefined; - inline for (infos, 0..) |field, i| { - fields[i] = .{ - .name = field.name, - .type = field.type, - .alignment = field.alignment, - }; - } - return @Type(std.builtin.Type{ .Union = .{ - .layout = .Auto, - .tag_type = std.meta.FieldEnum(T), - .fields = &fields, - .decls = &.{}, - } }); -} - -pub fn LoggingReader(comptime T: type, comptime scope: @Type(.EnumLiteral)) type { - return struct { - child_stream: T, - - pub const Error = T.Error; - pub const Reader = std.io.Reader(Self, Error, read); - - const Self = @This(); - - pub fn init(child_stream: T) Self { - return .{ - .child_stream = child_stream, - }; - } - - pub fn reader(self: Self) Reader { - return .{ .context = self }; - } - - fn read(self: Self, dest: []u8) Error!usize { - const n = try self.child_stream.read(dest); - std.log.scoped(scope).debug("{s}", .{dest[0..n]}); - return n; - } - }; -} - -pub fn LoggingWriter(comptime T: type, comptime scope: @Type(.EnumLiteral)) type { - return struct { - child_stream: T, - - pub const Error = T.Error; - pub const Writer = std.io.Writer(Self, Error, write); - - const Self = @This(); - - pub fn init(child_stream: T) Self { - return .{ - .child_stream = child_stream, - }; - } - - pub fn writer(self: Self) Writer { - return .{ .context = self }; - } - - fn write(self: Self, bytes: []const u8) Error!usize { - std.log.scoped(scope).debug("{s}", .{bytes}); - return self.child_stream.write(bytes); - } - }; -} - -pub fn Partial(comptime T: type) type { - const fields_before = std.meta.fields(T); - var fields_after: [fields_before.len]std.builtin.Type.StructField = undefined; - inline for (fields_before, 0..) |item, i| { - fields_after[i] = std.builtin.Type.StructField{ - .name = item.name, - .type = ?item.type, - .default_value = &@as(?item.type, null), - .is_comptime = false, - .alignment = @alignOf(?item.type), - }; - } - return @Type(@unionInit(std.builtin.Type, "Struct", .{ - .layout = .Auto, - .backing_integer = null, - .fields = &fields_after, - .decls = &.{}, - .is_tuple = false, - })); -} - -pub fn coalescePartial(comptime T: type, into: T, from: Partial(T)) T { - var temp = into; - inline for (comptime std.meta.fieldNames(T)) |name| { - if (@field(from, name)) |val| @field(temp, name) = val; - } - return temp; -} - -pub fn joinPartial(comptime P: type, a: P, b: P) P { - var temp = a; - inline for (comptime std.meta.fieldNames(P)) |name| { - if (@field(b, name)) |val| @field(temp, name) = val; - } - return temp; -} - -pub fn OneBiggerInt(comptime T: type) type { - var info = @typeInfo(T); - info.Int.bits += 1; - return @Type(info); -} - -pub fn ReverseFields(comptime T: type) type { - var info = @typeInfo(T).Struct; - const len = info.fields.len; - var fields: [len]std.builtin.Type.StructField = undefined; - for (0..len) |i| { - fields[i] = info.fields[len - 1 - i]; - } - info.fields = &fields; - return @Type(.{ .Struct = info }); -} - -pub fn stringToEnum(comptime E: type, str: ?string) ?E { - return std.meta.stringToEnum(E, str orelse return null); -} - -pub fn containsAggregate(comptime T: type, haystack: []const T, needle: T) bool { - for (haystack) |item| { - if (T.eql(item, needle)) { - return true; - } - } - return false; -} - -pub const AnyReader = struct { - readFn: *const fn (*anyopaque, []u8) anyerror!usize, - state: *anyopaque, - - pub fn from(reader: anytype) AnyReader { - const R = @TypeOf(reader); - const ctx = reader.context; - const Ctx = @TypeOf(ctx); - switch (@typeInfo(Ctx)) { - .Pointer => { - const S = struct { - fn foo(s: *anyopaque, buffer: []u8) anyerror!usize { - const r = R{ .context = @ptrCast(@alignCast(s)) }; - return r.read(buffer); - } - }; - return .{ - .readFn = S.foo, - .state = ctx, - }; - }, - .Struct => switch (R) { - std.fs.File.Reader => { - const S = struct { - fn foo(s: *anyopaque, buffer: []u8) anyerror!usize { - const r = R{ .context = .{ .handle = @intCast(@intFromPtr(s)) } }; - return r.read(buffer); - } - }; - return .{ - .readFn = S.foo, - .state = @ptrFromInt(@as(usize, @intCast(ctx.handle))), - }; - }, - else => @compileError(@typeName(R)), - }, - else => |v| @compileError(@typeName(R) ++ " , " ++ @tagName(v)), - } - } - - pub fn read(r: AnyReader, buffer: []u8) anyerror!usize { - return r.readFn(r.state, buffer); - } - - pub fn readByte(self: AnyReader) !u8 { - var result: [1]u8 = undefined; - const amt_read = try self.read(result[0..]); - if (amt_read < 1) return error.EndOfStream; - return result[0]; - } - - pub fn readInt(self: AnyReader, comptime T: type, endian: std.builtin.Endian) !T { - const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8))); - return std.mem.readInt(T, &bytes, endian); - } - - pub fn readBytesNoEof(self: AnyReader, comptime num_bytes: usize) ![num_bytes]u8 { - var bytes: [num_bytes]u8 = undefined; - try self.readNoEof(&bytes); - return bytes; - } - - pub fn readNoEof(self: AnyReader, buf: []u8) !void { - const amt_read = try self.readAll(buf); - if (amt_read < buf.len) return error.EndOfStream; - } - - pub fn readAll(self: AnyReader, buffer: []u8) !usize { - return self.readAtLeast(buffer, buffer.len); - } - - pub fn readAtLeast(self: AnyReader, buffer: []u8, len: usize) !usize { - assert(len <= buffer.len); - var index: usize = 0; - while (index < len) { - const amt = try self.read(buffer[index..]); - if (amt == 0) break; - index += amt; - } - return index; - } - - pub fn readAllAlloc(self: AnyReader, allocator: std.mem.Allocator, max_size: usize) ![]u8 { - var array_list = std.ArrayList(u8).init(allocator); - defer array_list.deinit(); - try self.readAllArrayList(&array_list, max_size); - return try array_list.toOwnedSlice(); - } - - pub fn readAllArrayList(self: AnyReader, array_list: *std.ArrayList(u8), max_append_size: usize) !void { - return self.readAllArrayListAligned(null, array_list, max_append_size); - } - - pub fn readAllArrayListAligned(self: AnyReader, comptime alignment: ?u29, array_list: *std.ArrayListAligned(u8, alignment), max_append_size: usize) !void { - try array_list.ensureTotalCapacity(@min(max_append_size, 4096)); - const original_len = array_list.items.len; - var start_index: usize = original_len; - while (true) { - array_list.expandToCapacity(); - const dest_slice = array_list.items[start_index..]; - const bytes_read = try self.readAll(dest_slice); - start_index += bytes_read; - - if (start_index - original_len > max_append_size) { - array_list.shrinkAndFree(original_len + max_append_size); - return error.StreamTooLong; - } - - if (bytes_read != dest_slice.len) { - array_list.shrinkAndFree(start_index); - return; - } - - // This will trigger ArrayList to expand superlinearly at whatever its growth rate is. - try array_list.ensureTotalCapacity(start_index + 1); - } - } -}; - -pub fn sum(comptime T: type, slice: []const T) T { - var res: T = 0; - for (slice) |item| res += item; - return res; -} - -pub fn RingBuffer(comptime T: type, comptime capacity: usize) type { - return struct { - items: [capacity]T = undefined, - len: usize = 0, - comptime capacity: usize = capacity, - - const Self = @This(); - - pub fn append(self: *Self, new_item: T) void { - if (self.len == self.capacity) { - for (1..self.len) |i| self.items[i - 1] = self.items[i]; - self.len -= 1; - } - self.items[self.len] = new_item; - self.len += 1; - } - }; -} +pub usingnamespace @import("./parse_json.zig"); +pub usingnamespace @import("./isArrayOf.zig"); +pub usingnamespace @import("./parse_int.zig"); +pub usingnamespace @import("./parse_bool.zig"); +pub usingnamespace @import("./to_hex.zig"); +pub usingnamespace @import("./FieldUnion.zig"); +pub usingnamespace @import("./LoggingReader.zig"); +pub usingnamespace @import("./LoggingWriter.zig"); +pub usingnamespace @import("./Partial.zig"); +pub usingnamespace @import("./coalescePartial.zig"); +pub usingnamespace @import("./OneBiggerInt.zig"); +pub usingnamespace @import("./ReverseFields.zig"); +pub usingnamespace @import("./stringToEnum.zig"); +pub usingnamespace @import("./containsAggregate.zig"); +pub usingnamespace @import("./AnyReader.zig"); +pub usingnamespace @import("./sum.zig"); +pub usingnamespace @import("./RingBuffer.zig"); pub fn fd_realpath(fd: std.os.fd_t) ![std.fs.MAX_PATH_BYTES:0]u8 { switch (builtin.os.tag) { diff --git a/src/matchesAll.zig b/src/matchesAll.zig new file mode 100644 index 0000000000000000000000000000000000000000..fefa2186f9fb3e4d2015ec7ac8ef12874ddfbc6d --- /dev/null +++ b/src/matchesAll.zig @@ -0,0 +1,12 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn matchesAll(comptime T: type, haystack: []const T, comptime needle: fn (T) bool) bool { + for (haystack) |c| { + if (!needle(c)) { + return false; + } + } + return true; +} diff --git a/src/matchesAny.zig b/src/matchesAny.zig new file mode 100644 index 0000000000000000000000000000000000000000..7935a4ca3be6778059245cd331cd1e7db68aec64 --- /dev/null +++ b/src/matchesAny.zig @@ -0,0 +1,12 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn matchesAny(comptime T: type, haystack: []const T, comptime needle: fn (T) bool) bool { + for (haystack) |c| { + if (needle(c)) { + return true; + } + } + return false; +} diff --git a/src/nullifyS.zig b/src/nullifyS.zig new file mode 100644 index 0000000000000000000000000000000000000000..dac000a9cb3baeb66f053669e84cdea0f6c407f1 --- /dev/null +++ b/src/nullifyS.zig @@ -0,0 +1,9 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn nullifyS(s: ?string) ?string { + if (s == null) return null; + if (s.?.len == 0) return null; + return s.?; +} diff --git a/src/opslice.zig b/src/opslice.zig new file mode 100644 index 0000000000000000000000000000000000000000..7580099aadb878d8b19269f69667cb81697d347f --- /dev/null +++ b/src/opslice.zig @@ -0,0 +1,8 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn opslice(slice: anytype, index: usize) ?std.meta.Child(@TypeOf(slice)) { + if (slice.len <= index) return null; + return slice[index]; +} diff --git a/src/parse_bool.zig b/src/parse_bool.zig new file mode 100644 index 0000000000000000000000000000000000000000..fbba992c6dc3511116d28644e82cfc79bd3ff329 --- /dev/null +++ b/src/parse_bool.zig @@ -0,0 +1,7 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn parse_bool(s: ?string) bool { + return extras.parse_int(u1, s, 10, 0) > 0; +} diff --git a/src/parse_int.zig b/src/parse_int.zig new file mode 100644 index 0000000000000000000000000000000000000000..70f91c4c5a40915e65dd09f5123bb86748553c71 --- /dev/null +++ b/src/parse_int.zig @@ -0,0 +1,8 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn parse_int(comptime T: type, s: ?string, b: u8, d: T) T { + if (s == null) return d; + return std.fmt.parseInt(T, s.?, b) catch d; +} diff --git a/src/parse_json.zig b/src/parse_json.zig new file mode 100644 index 0000000000000000000000000000000000000000..5ece213b5278c781923c166a645a97529776906c --- /dev/null +++ b/src/parse_json.zig @@ -0,0 +1,7 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn parse_json(alloc: std.mem.Allocator, input: string) !std.json.Parsed(std.json.Value) { + return std.json.parseFromSlice(std.json.Value, alloc, input, .{}); +} diff --git a/src/pipe.zig b/src/pipe.zig new file mode 100644 index 0000000000000000000000000000000000000000..4d12079c7e409baf0aea9f9e7f0728c8f1d81d04 --- /dev/null +++ b/src/pipe.zig @@ -0,0 +1,10 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn pipe(reader_from: anytype, writer_to: anytype) !void { + var buf: [std.mem.page_size]u8 = undefined; + var fifo = std.fifo.LinearFifo(u8, .Slice).init(&buf); + defer fifo.deinit(); + try fifo.pump(reader_from, writer_to); +} diff --git a/src/positionalInit.zig b/src/positionalInit.zig new file mode 100644 index 0000000000000000000000000000000000000000..29d1226171a16415378df25d54d2a642ba6ea319 --- /dev/null +++ b/src/positionalInit.zig @@ -0,0 +1,11 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn positionalInit(comptime T: type, args: std.meta.FieldsTuple(T)) T { + var t: T = undefined; + inline for (std.meta.fields(T), 0..) |field, i| { + @field(t, field.name) = args[i]; + } + return t; +} diff --git a/src/ptrCast.zig b/src/ptrCast.zig new file mode 100644 index 0000000000000000000000000000000000000000..9437ced539e489aceead12c3be6bb6d3eaeb8021 --- /dev/null +++ b/src/ptrCast.zig @@ -0,0 +1,8 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn ptrCast(comptime T: type, ptr: *anyopaque) *T { + if (@alignOf(T) == 0) @compileError(@typeName(T)); + return @ptrCast(@alignCast(ptr)); +} diff --git a/src/ptrCastConst.zig b/src/ptrCastConst.zig new file mode 100644 index 0000000000000000000000000000000000000000..4c5c207b027794e8387b2df695ecabcfebc0b675 --- /dev/null +++ b/src/ptrCastConst.zig @@ -0,0 +1,8 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn ptrCastConst(comptime T: type, ptr: *const anyopaque) *const T { + if (@alignOf(T) == 0) @compileError(@typeName(T)); + return @ptrCast(@alignCast(ptr)); +} diff --git a/src/randomBytes.zig b/src/randomBytes.zig new file mode 100644 index 0000000000000000000000000000000000000000..5950467b3f18f0078389d9ea5b5be63576dd7857 --- /dev/null +++ b/src/randomBytes.zig @@ -0,0 +1,9 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn randomBytes(comptime len: usize) [len]u8 { + var bytes: [len]u8 = undefined; + std.crypto.random.bytes(&bytes); + return bytes; +} diff --git a/src/randomSlice.zig b/src/randomSlice.zig new file mode 100644 index 0000000000000000000000000000000000000000..65d6adfa75bf604506637e7a169e483b6a4b939e --- /dev/null +++ b/src/randomSlice.zig @@ -0,0 +1,14 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +const alphabet = "0123456789abcdefghijklmnopqrstuvwxyz"; + +pub fn randomSlice(alloc: std.mem.Allocator, rand: std.rand.Random, comptime T: type, len: usize) ![]T { + var buf = try alloc.alloc(T, len); + var i: usize = 0; + while (i < len) : (i += 1) { + buf[i] = alphabet[rand.int(u8) % alphabet.len]; + } + return buf; +} diff --git a/src/readBytes.zig b/src/readBytes.zig new file mode 100644 index 0000000000000000000000000000000000000000..b9b47923d6ac436b37689dd5304071a09f8e4eab --- /dev/null +++ b/src/readBytes.zig @@ -0,0 +1,10 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); +const assert = std.debug.assert; + +pub fn readBytes(reader: anytype, comptime len: usize) ![len]u8 { + var bytes: [len]u8 = undefined; + assert(try reader.readAll(&bytes) == len); + return bytes; +} diff --git a/src/readBytesAlloc.zig b/src/readBytesAlloc.zig new file mode 100644 index 0000000000000000000000000000000000000000..55510b48fdca5d87f1e554c3c66a76ccdc3062b6 --- /dev/null +++ b/src/readBytesAlloc.zig @@ -0,0 +1,12 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn readBytesAlloc(reader: anytype, alloc: std.mem.Allocator, len: usize) ![]u8 { + var list = std.ArrayListUnmanaged(u8){}; + try list.ensureTotalCapacityPrecise(alloc, len); + errdefer list.deinit(alloc); + list.appendNTimesAssumeCapacity(0, len); + try reader.readNoEof(list.items[0..len]); + return list.items; +} diff --git a/src/readExpected.zig b/src/readExpected.zig new file mode 100644 index 0000000000000000000000000000000000000000..422d81cf47f6a993bdd11b4a6c26ba45a49fc62e --- /dev/null +++ b/src/readExpected.zig @@ -0,0 +1,13 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn readExpected(reader: anytype, expected: []const u8) !bool { + for (expected) |item| { + const actual = try reader.readByte(); + if (actual != item) { + return false; + } + } + return true; +} diff --git a/src/readType.zig b/src/readType.zig new file mode 100644 index 0000000000000000000000000000000000000000..e728c8225d3bf5536de5206e1fa0e3382062977a --- /dev/null +++ b/src/readType.zig @@ -0,0 +1,31 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn readType(reader: anytype, comptime T: type, endian: std.builtin.Endian) !T { + if (T == u8) return reader.readByte(); // single bytes dont have an endianness + return switch (@typeInfo(T)) { + .Struct => |t| { + switch (t.layout) { + .Auto, .Extern => { + var s: T = undefined; + inline for (std.meta.fields(T)) |field| { + @field(s, field.name) = try readType(reader, field.type, endian); + } + return s; + }, + .Packed => return @bitCast(try readType(reader, t.backing_integer.?, endian)), + } + }, + .Array => |t| { + var s: T = undefined; + for (0..t.len) |i| { + s[i] = try readType(reader, t.child, endian); + } + return s; + }, + .Int => try reader.readInt(T, endian), + .Enum => |t| @enumFromInt(try readType(reader, t.tag_type, endian)), + else => |e| @compileError(@tagName(e)), + }; +} diff --git a/src/reduceNumber.zig b/src/reduceNumber.zig new file mode 100644 index 0000000000000000000000000000000000000000..071fe0e24f346ec7b2880b830a068b398df6f46d --- /dev/null +++ b/src/reduceNumber.zig @@ -0,0 +1,17 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn reduceNumber(alloc: std.mem.Allocator, input: u64, comptime unit: u64, comptime base: string, comptime prefixes: string) !string { + if (input < unit) { + return std.fmt.allocPrint(alloc, "{d} {s}", .{ input, base }); + } + var div = unit; + var exp: usize = 0; + var n = input / unit; + while (n >= unit) : (n /= unit) { + div *= unit; + exp += 1; + } + return try std.fmt.allocPrint(alloc, "{d:.3} {s}{s}", .{ @as(f64, @floatFromInt(input)) / @as(f64, @floatFromInt(div)), prefixes[exp .. exp + 1], base }); +} diff --git a/src/safeAdd.zig b/src/safeAdd.zig new file mode 100644 index 0000000000000000000000000000000000000000..0011e90b45f6aaa12f69dbcbc00a47e603023770 --- /dev/null +++ b/src/safeAdd.zig @@ -0,0 +1,11 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +/// Allows u32 + i16 to work +pub fn safeAdd(a: anytype, b: anytype) @TypeOf(a) { + if (b >= 0) { + return a + @as(@TypeOf(a), @intCast(b)); + } + return a - @as(@TypeOf(a), @intCast(-@as(extras.OneBiggerInt(@TypeOf(b)), b))); +} diff --git a/src/safeAddWrap.zig b/src/safeAddWrap.zig new file mode 100644 index 0000000000000000000000000000000000000000..6fb1a11b21145bf07aa7e830903de9a9d4ee839c --- /dev/null +++ b/src/safeAddWrap.zig @@ -0,0 +1,11 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +/// Allows u32 +% i16 to work +pub fn safeAddWrap(a: anytype, b: anytype) @TypeOf(a) { + if (b >= 0) { + return a +% @as(@TypeOf(a), @intCast(b)); + } + return a -% @as(@TypeOf(a), @intCast(-@as(extras.OneBiggerInt(@TypeOf(b)), b))); +} diff --git a/src/skipToBoundary.zig b/src/skipToBoundary.zig new file mode 100644 index 0000000000000000000000000000000000000000..7408b0a8df89632864bc5e08edcbcab19f15b85a --- /dev/null +++ b/src/skipToBoundary.zig @@ -0,0 +1,11 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn skipToBoundary(pos: u64, boundary: u64, reader: anytype) !void { + // const gdiff = counter.bytes_read % 4; + // for (range(if (gdiff > 0) 4 - gdiff else 0)) |_| { + const a = pos; + const b = boundary; + try reader.skipBytes(((a + (b - 1)) & ~(b - 1)) - a, .{}); +} diff --git a/src/sliceTo.zig b/src/sliceTo.zig new file mode 100644 index 0000000000000000000000000000000000000000..eeaec8e7879745cfbadd0e66fa570a609b029f07 --- /dev/null +++ b/src/sliceTo.zig @@ -0,0 +1,10 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn sliceTo(comptime T: type, haystack: []const T, needle: T) []const T { + if (std.mem.indexOfScalar(T, haystack, needle)) |index| { + return haystack[0..index]; + } + return haystack; +} diff --git a/src/sliceToInt.zig b/src/sliceToInt.zig new file mode 100644 index 0000000000000000000000000000000000000000..fc5ee09934626e916a01d636db29701c307d2d9d --- /dev/null +++ b/src/sliceToInt.zig @@ -0,0 +1,16 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn sliceToInt(comptime T: type, comptime E: type, slice: []const E) !T { + const a = @typeInfo(T).Int.bits; + const b = @typeInfo(E).Int.bits; + if (a < b * slice.len) return error.Overflow; + + var n: T = 0; + for (slice, 0..) |item, i| { + const shift: std.math.Log2Int(T) = @intCast(b * (slice.len - 1 - i)); + n = n | (@as(T, item) << shift); + } + return n; +} diff --git a/src/sortBy.zig b/src/sortBy.zig new file mode 100644 index 0000000000000000000000000000000000000000..a838bd7f50bdbc078fccaad2c890aa4fdcf6c0f4 --- /dev/null +++ b/src/sortBy.zig @@ -0,0 +1,11 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn sortBy(comptime T: type, items: []T, comptime field: std.meta.FieldEnum(T)) void { + std.mem.sort(T, items, {}, struct { + fn f(_: void, lhs: T, rhs: T) bool { + return @field(lhs, @tagName(field)) < @field(rhs, @tagName(field)); + } + }.f); +} diff --git a/src/sortBySlice.zig b/src/sortBySlice.zig new file mode 100644 index 0000000000000000000000000000000000000000..79e8fece047dcddc12017071ecae9ed3a7a26b0b --- /dev/null +++ b/src/sortBySlice.zig @@ -0,0 +1,11 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn sortBySlice(comptime T: type, items: []T, comptime field: std.meta.FieldEnum(T)) void { + std.mem.sort(T, items, {}, struct { + fn f(_: void, lhs: T, rhs: T) bool { + return extras.lessThanSlice(std.meta.FieldType(T, field))({}, @field(lhs, @tagName(field)), @field(rhs, @tagName(field))); + } + }.f); +} diff --git a/src/stringToEnum.zig b/src/stringToEnum.zig new file mode 100644 index 0000000000000000000000000000000000000000..c2eec2a42aebfbe2f59ee93fda26df1abb5829ef --- /dev/null +++ b/src/stringToEnum.zig @@ -0,0 +1,7 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn stringToEnum(comptime E: type, str: ?string) ?E { + return std.meta.stringToEnum(E, str orelse return null); +} diff --git a/src/sum.zig b/src/sum.zig new file mode 100644 index 0000000000000000000000000000000000000000..dff27a768ac5300cd5295fb6509188baab2cc4ef --- /dev/null +++ b/src/sum.zig @@ -0,0 +1,9 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn sum(comptime T: type, slice: []const T) T { + var res: T = 0; + for (slice) |item| res += item; + return res; +} diff --git a/src/to_hex.zig b/src/to_hex.zig new file mode 100644 index 0000000000000000000000000000000000000000..3c8cc996a381aa57812db98924d3930dab284560 --- /dev/null +++ b/src/to_hex.zig @@ -0,0 +1,10 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn to_hex(array: anytype) [array.len * 2]u8 { + var res: [array.len * 2]u8 = undefined; + var fbs = std.io.fixedBufferStream(&res); + std.fmt.format(fbs.writer(), "{x}", .{std.fmt.fmtSliceHexLower(&array)}) catch unreachable; + return res; +} diff --git a/src/trimPrefix.zig b/src/trimPrefix.zig new file mode 100644 index 0000000000000000000000000000000000000000..b19d2110e228a11f2029edbde552733a84f716a4 --- /dev/null +++ b/src/trimPrefix.zig @@ -0,0 +1,10 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn trimPrefix(in: string, prefix: string) string { + if (std.mem.startsWith(u8, in, prefix)) { + return in[prefix.len..]; + } + return in; +} diff --git a/src/trimPrefixEnsure.zig b/src/trimPrefixEnsure.zig new file mode 100644 index 0000000000000000000000000000000000000000..8fcb497764af1cb3d245cd2df4f74ec1927b533c --- /dev/null +++ b/src/trimPrefixEnsure.zig @@ -0,0 +1,8 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn trimPrefixEnsure(in: string, prefix: string) ?string { + if (!std.mem.startsWith(u8, in, prefix)) return null; + return in[prefix.len..]; +} diff --git a/src/trimSuffix.zig b/src/trimSuffix.zig new file mode 100644 index 0000000000000000000000000000000000000000..588828c7e6ad00e187c953bfd48e488350176d27 --- /dev/null +++ b/src/trimSuffix.zig @@ -0,0 +1,10 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn trimSuffix(in: string, suffix: string) string { + if (std.mem.endsWith(u8, in, suffix)) { + return in[0 .. in.len - suffix.len]; + } + return in; +} diff --git a/src/trimSuffixEnsure.zig b/src/trimSuffixEnsure.zig new file mode 100644 index 0000000000000000000000000000000000000000..2b8772bf3dd32adf51e5db87ca6581f4fdfd9bb2 --- /dev/null +++ b/src/trimSuffixEnsure.zig @@ -0,0 +1,8 @@ +const std = @import("std"); +const string = []const u8; +const extras = @import("./lib.zig"); + +pub fn trimSuffixEnsure(in: string, suffix: string) ?string { + if (!std.mem.endsWith(u8, in, suffix)) return null; + return in[0 .. in.len - suffix.len]; +}