const std = @import("std"); const Io = std.Io; var stdout_buffer: [1024]u8 = undefined; var stdout_file_writer: Io.File.Writer = undefined; var process: std.process.Init = undefined; pub fn print(comptime fmt: []const u8, args: anytype) void { writer().print(fmt ++ "\n", args) catch unreachable; writer().flush() catch unreachable; } pub fn writer() *Io.Writer { return &stdout_file_writer.interface; } pub fn sharedInit(init: std.process.Init) !void { process = init; const io = init.io; stdout_file_writer = .init(.stdout(), io, &stdout_buffer); } pub fn loadBytesFile(allocator: std.mem.Allocator, path: []const u8) []u8 { const bytes = std.Io.Dir.cwd().readFileAlloc(process.io, path, allocator, .unlimited) catch { std.debug.print("unable to load file {s}\n", .{path}); unreachable; }; return bytes; } pub fn loadBytesArg(allocator: std.mem.Allocator) []const u8 { // Accessing command line arguments: const args = process.minimal.args.toSlice(allocator) catch unreachable; if (args.len < 2) { print("invalid number of arguments", .{}); unreachable; } return trim(normalizeEndings(allocator, loadBytesFile(allocator, args[1]))); } pub fn normalizeEndings(allocator: std.mem.Allocator, bytes: []const u8) []u8 { var s: std.ArrayList(u8) = .empty; for (bytes, 0..) |c, i| { if (c != '\r' or c != '\n') s.append(allocator, c) catch unreachable; if (c == '\r') if (bytes[i + 1] == '\n') s.append(allocator, '\n') catch unreachable; } return s.toOwnedSlice(allocator) catch unreachable; } pub fn isWhiteSpace(s: []const u8, off: usize) bool { const c = s[off]; return c == ' ' or c == '\n' or c == '\r' or c == '\t'; } pub fn trim(bytes: []const u8) []const u8 { if (bytes.len == 0) return bytes; var start: usize = 0; while (isWhiteSpace(bytes, start)) start += 1; var end: usize = bytes.len - 1; while (end - 1 > 0 and isWhiteSpace(bytes, end)) end -= 1; return bytes[start .. end + 1]; } pub const LineIter = struct { line: usize = 0, s: usize = 0, e: usize = 0, pub fn toSlice(self: *@This(), bytes: []const u8) []const u8 { return trim(bytes[self.s..self.e]); } pub fn nextLine(self: *@This(), bytes: []const u8) ?[]const u8 { if (self.e == bytes.len) { return null; } self.s = self.e; if (isWhiteSpace(bytes, self.e)) self.s += 1; self.e = self.s; self.line += 1; while (self.e != bytes.len and bytes[self.e] != '\n') { self.e += 1; } if (self.e != bytes.len and bytes[self.e] == '\n') self.e += 1; return self.toSlice(bytes); } };