const std = @import("std"); const mem = std.mem; var gpa_impl = std.heap.GeneralPurposeAllocator(.{}){}; /// General purpose allocator. pub const gpa = gpa_impl.allocator(); pub const ParseIntsOptions = struct { base: u8 = 10, sep: u8 = ' ', }; /// Parses separated integers. Sequential separators are treated as one. Caller owns memory. pub fn parseIntsScalar(comptime T: type, alloc: std.mem.Allocator, input: []const u8, opts: ParseIntsOptions) ![]T { var items = std.ArrayList(T).init(alloc); errdefer items.deinit(); var it = split(input, opts.sep); while (it.next()) |i| { if (i.len == 0) continue; try items.append(try std.fmt.parseInt(T, i, opts.base)); } return try items.toOwnedSlice(); } /// Shortcut to splitScalar with a byte buffer. pub fn split(buffer: []const u8, sep: u8) mem.SplitIterator(u8, .scalar) { return mem.splitScalar(u8, buffer, sep); } pub fn splitLines(buffer: []const u8) mem.SplitIterator(u8, .scalar) { return split(buffer, '\n'); } pub fn splitSpace(buffer: []const u8) mem.SplitIterator(u8, .scalar) { return split(buffer, ' '); } pub fn allocGrid(comptime T: type, alloc: mem.Allocator, w: usize, h: usize, init: T) ![][]T { var grid = try alloc.alloc([]T, h); for (0..h) |i| { grid[i] = try alloc.alloc(T, w); @memset(grid[i], init); } return grid; } pub fn toGridOwned(alloc: mem.Allocator, buffer: []const u8, sep: u8) ![][]u8 { if (buffer.len == 0) return error.Empty; const w = mem.indexOfScalar(u8, buffer, sep) orelse unreachable; var h = mem.count(u8, buffer, &[_]u8{sep}); if (buffer[buffer.len - 1] != sep) h += 1; var grid = try alloc.alloc([]u8, h); var it = split(buffer, sep); var i: usize = 0; while (it.next()) |line| { grid[i] = try alloc.alloc(u8, w); @memcpy(grid[i], line); i += 1; } return grid; }