From 0e37ff0d591dd75ceec9208196bec29efaec607a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Jun 2025 20:05:22 -0700 Subject: std.fmt: breaking API changes added adapter to AnyWriter and GenericWriter to help bridge the gap between old and new API make std.testing.expectFmt work at compile-time std.fmt no longer has a dependency on std.unicode. Formatted printing was never properly unicode-aware. Now it no longer pretends to be. Breakage/deprecations: * std.fs.File.reader -> std.fs.File.deprecatedReader * std.fs.File.writer -> std.fs.File.deprecatedWriter * std.io.GenericReader -> std.io.Reader * std.io.GenericWriter -> std.io.Writer * std.io.AnyReader -> std.io.Reader * std.io.AnyWriter -> std.io.Writer * std.fmt.format -> std.fmt.deprecatedFormat * std.fmt.fmtSliceEscapeLower -> std.ascii.hexEscape * std.fmt.fmtSliceEscapeUpper -> std.ascii.hexEscape * std.fmt.fmtSliceHexLower -> {x} * std.fmt.fmtSliceHexUpper -> {X} * std.fmt.fmtIntSizeDec -> {B} * std.fmt.fmtIntSizeBin -> {Bi} * std.fmt.fmtDuration -> {D} * std.fmt.fmtDurationSigned -> {D} * {} -> {f} when there is a format method * format method signature - anytype -> *std.io.Writer - inferred error set -> error{WriteFailed} - options -> (deleted) * std.fmt.Formatted - now takes context type explicitly - no fmt string --- lib/std/Build/Step/CheckObject.zig | 1242 ++++++++++++++++-------------------- 1 file changed, 562 insertions(+), 680 deletions(-) (limited to 'lib/std/Build/Step/CheckObject.zig') diff --git a/lib/std/Build/Step/CheckObject.zig b/lib/std/Build/Step/CheckObject.zig index 3b78258d1c..96fa443b1e 100644 --- a/lib/std/Build/Step/CheckObject.zig +++ b/lib/std/Build/Step/CheckObject.zig @@ -6,6 +6,7 @@ const macho = std.macho; const math = std.math; const mem = std.mem; const testing = std.testing; +const Writer = std.io.Writer; const CheckObject = @This(); @@ -28,14 +29,14 @@ pub fn create( const gpa = owner.allocator; const check_object = gpa.create(CheckObject) catch @panic("OOM"); check_object.* = .{ - .step = Step.init(.{ + .step = .init(.{ .id = base_id, .name = "CheckObject", .owner = owner, .makeFn = make, }), .source = source.dupe(owner), - .checks = std.ArrayList(Check).init(gpa), + .checks = .init(gpa), .obj_format = obj_format, }; check_object.source.addStepDependencies(&check_object.step); @@ -74,13 +75,13 @@ const Action = struct { b: *std.Build, step: *Step, haystack: []const u8, - global_vars: anytype, + global_vars: *std.StringHashMap(u64), ) !bool { assert(act.tag == .extract); const hay = mem.trim(u8, haystack, " "); const phrase = mem.trim(u8, act.phrase.resolve(b, step), " "); - var candidate_vars = std.ArrayList(struct { name: []const u8, value: u64 }).init(b.allocator); + var candidate_vars: std.ArrayList(struct { name: []const u8, value: u64 }) = .init(b.allocator); var hay_it = mem.tokenizeScalar(u8, hay, ' '); var needle_it = mem.tokenizeScalar(u8, phrase, ' '); @@ -153,11 +154,11 @@ const Action = struct { /// Will return true if the `phrase` is correctly parsed into an RPN program and /// its reduced, computed value compares using `op` with the expected value, either /// a literal or another extracted variable. - fn computeCmp(act: Action, b: *std.Build, step: *Step, global_vars: anytype) !bool { + fn computeCmp(act: Action, b: *std.Build, step: *Step, global_vars: std.StringHashMap(u64)) !bool { const gpa = step.owner.allocator; const phrase = act.phrase.resolve(b, step); - var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa); - var values = std.ArrayList(u64).init(gpa); + var op_stack: std.ArrayList(enum { add, sub, mod, mul }) = .init(gpa); + var values: std.ArrayList(u64) = .init(gpa); var it = mem.tokenizeScalar(u8, phrase, ' '); while (it.next()) |next| { @@ -230,17 +231,15 @@ const ComputeCompareExpected = struct { }, pub fn format( - value: @This(), + value: ComputeCompareExpected, + bw: *Writer, comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, ) !void { if (fmt.len != 0) std.fmt.invalidFmtError(fmt, value); - _ = options; - try writer.print("{s} ", .{@tagName(value.op)}); + try bw.print("{s} ", .{@tagName(value.op)}); switch (value.value) { - .variable => |name| try writer.writeAll(name), - .literal => |x| try writer.print("{x}", .{x}), + .variable => |name| try bw.writeAll(name), + .literal => |x| try bw.print("{x}", .{x}), } } }; @@ -248,56 +247,63 @@ const ComputeCompareExpected = struct { const Check = struct { kind: Kind, payload: Payload, - data: std.ArrayList(u8), - actions: std.ArrayList(Action), + allocator: Allocator, + data: std.ArrayListUnmanaged(u8), + actions: std.ArrayListUnmanaged(Action), fn create(allocator: Allocator, kind: Kind) Check { return .{ .kind = kind, .payload = .{ .none = {} }, - .data = std.ArrayList(u8).init(allocator), - .actions = std.ArrayList(Action).init(allocator), + .allocator = allocator, + .data = .empty, + .actions = .empty, }; } - fn dumpSection(allocator: Allocator, name: [:0]const u8) Check { - var check = Check.create(allocator, .dump_section); + fn dumpSection(gpa: Allocator, name: [:0]const u8) Check { + var check = Check.create(gpa, .dump_section); const off: u32 = @intCast(check.data.items.len); - check.data.writer().print("{s}\x00", .{name}) catch @panic("OOM"); + check.data.print(gpa, "{s}\x00", .{name}) catch @panic("OOM"); check.payload = .{ .dump_section = off }; return check; } fn extract(check: *Check, phrase: SearchPhrase) void { - check.actions.append(.{ + const gpa = check.allocator; + check.actions.append(gpa, .{ .tag = .extract, .phrase = phrase, }) catch @panic("OOM"); } fn exact(check: *Check, phrase: SearchPhrase) void { - check.actions.append(.{ + const gpa = check.allocator; + check.actions.append(gpa, .{ .tag = .exact, .phrase = phrase, }) catch @panic("OOM"); } fn contains(check: *Check, phrase: SearchPhrase) void { - check.actions.append(.{ + const gpa = check.allocator; + check.actions.append(gpa, .{ .tag = .contains, .phrase = phrase, }) catch @panic("OOM"); } fn notPresent(check: *Check, phrase: SearchPhrase) void { - check.actions.append(.{ + const gpa = check.allocator; + check.actions.append(gpa, .{ .tag = .not_present, .phrase = phrase, }) catch @panic("OOM"); } fn computeCmp(check: *Check, phrase: SearchPhrase, expected: ComputeCompareExpected) void { - check.actions.append(.{ + const gpa = check.allocator; + check.actions.append(gpa, .{ .tag = .compute_cmp, .phrase = phrase, .expected = expected, @@ -565,9 +571,9 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void { null, .of(u64), null, - ) catch |err| return step.fail("unable to read '{'}': {s}", .{ src_path, @errorName(err) }); + ) catch |err| return step.fail("unable to read '{f'}': {s}", .{ src_path, @errorName(err) }); - var vars = std.StringHashMap(u64).init(gpa); + var vars: std.StringHashMap(u64) = .init(gpa); for (check_object.checks.items) |chk| { if (chk.kind == .compute_compare) { assert(chk.actions.items.len == 1); @@ -581,7 +587,7 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void { return step.fail( \\ \\========= comparison failed for action: =========== - \\{s} {} + \\{s} {f} \\=================================================== , .{ act.phrase.resolve(b, step), act.expected.? }); } @@ -600,7 +606,7 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void { // we either format message string with escaped codes, or not to aid debugging // the failed test. const fmtMessageString = struct { - fn fmtMessageString(kind: Check.Kind, msg: []const u8) std.fmt.Formatter(formatMessageString) { + fn fmtMessageString(kind: Check.Kind, msg: []const u8) std.fmt.Formatter(Ctx, formatMessageString) { return .{ .data = .{ .kind = kind, .msg = msg, @@ -612,17 +618,10 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void { msg: []const u8, }; - fn formatMessageString( - ctx: Ctx, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = unused_fmt_string; - _ = options; + fn formatMessageString(ctx: Ctx, w: *Writer) !void { switch (ctx.kind) { - .dump_section => try writer.print("{s}", .{std.fmt.fmtSliceEscapeLower(ctx.msg)}), - else => try writer.writeAll(ctx.msg), + .dump_section => try w.print("{f}", .{std.ascii.hexEscape(ctx.msg, .lower)}), + else => try w.writeAll(ctx.msg), } } }.fmtMessageString; @@ -637,11 +636,11 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void { return step.fail( \\ \\========= expected to find: ========================== - \\{s} + \\{f} \\========= but parsed file does not contain it: ======= - \\{s} + \\{f} \\========= file path: ================================= - \\{} + \\{f} , .{ fmtMessageString(chk.kind, act.phrase.resolve(b, step)), fmtMessageString(chk.kind, output), @@ -657,11 +656,11 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void { return step.fail( \\ \\========= expected to find: ========================== - \\*{s}* + \\*{f}* \\========= but parsed file does not contain it: ======= - \\{s} + \\{f} \\========= file path: ================================= - \\{} + \\{f} , .{ fmtMessageString(chk.kind, act.phrase.resolve(b, step)), fmtMessageString(chk.kind, output), @@ -676,11 +675,11 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void { return step.fail( \\ \\========= expected not to find: =================== - \\{s} + \\{f} \\========= but parsed file does contain it: ======== - \\{s} + \\{f} \\========= file path: ============================== - \\{} + \\{f} , .{ fmtMessageString(chk.kind, act.phrase.resolve(b, step)), fmtMessageString(chk.kind, output), @@ -696,13 +695,13 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void { return step.fail( \\ \\========= expected to find and extract: ============== - \\{s} + \\{f} \\========= but parsed file does not contain it: ======= - \\{s} + \\{f} \\========= file path: ============================== - \\{} + \\{f} , .{ - act.phrase.resolve(b, step), + fmtMessageString(chk.kind, act.phrase.resolve(b, step)), fmtMessageString(chk.kind, output), src_path, }); @@ -755,14 +754,14 @@ const MachODumper = struct { }, .SYMTAB => { const lc = cmd.cast(macho.symtab_command).?; - const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(ctx.data.ptr + lc.symoff))[0..lc.nsyms]; + const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(ctx.data[lc.symoff..].ptr))[0..lc.nsyms]; const strtab = ctx.data[lc.stroff..][0..lc.strsize]; try ctx.symtab.appendUnalignedSlice(ctx.gpa, symtab); try ctx.strtab.appendSlice(ctx.gpa, strtab); }, .DYSYMTAB => { const lc = cmd.cast(macho.dysymtab_command).?; - const indexes = @as([*]align(1) const u32, @ptrCast(ctx.data.ptr + lc.indirectsymoff))[0..lc.nindirectsyms]; + const indexes = @as([*]align(1) const u32, @ptrCast(ctx.data[lc.indirectsymoff..].ptr))[0..lc.nindirectsyms]; try ctx.indsymtab.appendUnalignedSlice(ctx.gpa, indexes); }, .LOAD_DYLIB, @@ -780,7 +779,7 @@ const MachODumper = struct { fn getString(ctx: ObjectContext, off: u32) [:0]const u8 { assert(off < ctx.strtab.items.len); - return mem.sliceTo(@as([*:0]const u8, @ptrCast(ctx.strtab.items.ptr + off)), 0); + return mem.sliceTo(@as([*:0]const u8, @ptrCast(ctx.strtab.items[off..].ptr)), 0); } fn getLoadCommandIterator(ctx: ObjectContext) macho.LoadCommandIterator { @@ -810,7 +809,7 @@ const MachODumper = struct { return null; } - fn dumpHeader(hdr: macho.mach_header_64, writer: anytype) !void { + fn dumpHeader(hdr: macho.mach_header_64, bw: *Writer) !void { const cputype = switch (hdr.cputype) { macho.CPU_TYPE_ARM64 => "ARM64", macho.CPU_TYPE_X86_64 => "X86_64", @@ -831,7 +830,7 @@ const MachODumper = struct { else => "Unknown", }; - try writer.print( + try bw.print( \\header \\cputype {s} \\filetype {s} @@ -846,41 +845,41 @@ const MachODumper = struct { }); if (hdr.flags > 0) { - if (hdr.flags & macho.MH_NOUNDEFS != 0) try writer.writeAll(" NOUNDEFS"); - if (hdr.flags & macho.MH_INCRLINK != 0) try writer.writeAll(" INCRLINK"); - if (hdr.flags & macho.MH_DYLDLINK != 0) try writer.writeAll(" DYLDLINK"); - if (hdr.flags & macho.MH_BINDATLOAD != 0) try writer.writeAll(" BINDATLOAD"); - if (hdr.flags & macho.MH_PREBOUND != 0) try writer.writeAll(" PREBOUND"); - if (hdr.flags & macho.MH_SPLIT_SEGS != 0) try writer.writeAll(" SPLIT_SEGS"); - if (hdr.flags & macho.MH_LAZY_INIT != 0) try writer.writeAll(" LAZY_INIT"); - if (hdr.flags & macho.MH_TWOLEVEL != 0) try writer.writeAll(" TWOLEVEL"); - if (hdr.flags & macho.MH_FORCE_FLAT != 0) try writer.writeAll(" FORCE_FLAT"); - if (hdr.flags & macho.MH_NOMULTIDEFS != 0) try writer.writeAll(" NOMULTIDEFS"); - if (hdr.flags & macho.MH_NOFIXPREBINDING != 0) try writer.writeAll(" NOFIXPREBINDING"); - if (hdr.flags & macho.MH_PREBINDABLE != 0) try writer.writeAll(" PREBINDABLE"); - if (hdr.flags & macho.MH_ALLMODSBOUND != 0) try writer.writeAll(" ALLMODSBOUND"); - if (hdr.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) try writer.writeAll(" SUBSECTIONS_VIA_SYMBOLS"); - if (hdr.flags & macho.MH_CANONICAL != 0) try writer.writeAll(" CANONICAL"); - if (hdr.flags & macho.MH_WEAK_DEFINES != 0) try writer.writeAll(" WEAK_DEFINES"); - if (hdr.flags & macho.MH_BINDS_TO_WEAK != 0) try writer.writeAll(" BINDS_TO_WEAK"); - if (hdr.flags & macho.MH_ALLOW_STACK_EXECUTION != 0) try writer.writeAll(" ALLOW_STACK_EXECUTION"); - if (hdr.flags & macho.MH_ROOT_SAFE != 0) try writer.writeAll(" ROOT_SAFE"); - if (hdr.flags & macho.MH_SETUID_SAFE != 0) try writer.writeAll(" SETUID_SAFE"); - if (hdr.flags & macho.MH_NO_REEXPORTED_DYLIBS != 0) try writer.writeAll(" NO_REEXPORTED_DYLIBS"); - if (hdr.flags & macho.MH_PIE != 0) try writer.writeAll(" PIE"); - if (hdr.flags & macho.MH_DEAD_STRIPPABLE_DYLIB != 0) try writer.writeAll(" DEAD_STRIPPABLE_DYLIB"); - if (hdr.flags & macho.MH_HAS_TLV_DESCRIPTORS != 0) try writer.writeAll(" HAS_TLV_DESCRIPTORS"); - if (hdr.flags & macho.MH_NO_HEAP_EXECUTION != 0) try writer.writeAll(" NO_HEAP_EXECUTION"); - if (hdr.flags & macho.MH_APP_EXTENSION_SAFE != 0) try writer.writeAll(" APP_EXTENSION_SAFE"); - if (hdr.flags & macho.MH_NLIST_OUTOFSYNC_WITH_DYLDINFO != 0) try writer.writeAll(" NLIST_OUTOFSYNC_WITH_DYLDINFO"); + if (hdr.flags & macho.MH_NOUNDEFS != 0) try bw.writeAll(" NOUNDEFS"); + if (hdr.flags & macho.MH_INCRLINK != 0) try bw.writeAll(" INCRLINK"); + if (hdr.flags & macho.MH_DYLDLINK != 0) try bw.writeAll(" DYLDLINK"); + if (hdr.flags & macho.MH_BINDATLOAD != 0) try bw.writeAll(" BINDATLOAD"); + if (hdr.flags & macho.MH_PREBOUND != 0) try bw.writeAll(" PREBOUND"); + if (hdr.flags & macho.MH_SPLIT_SEGS != 0) try bw.writeAll(" SPLIT_SEGS"); + if (hdr.flags & macho.MH_LAZY_INIT != 0) try bw.writeAll(" LAZY_INIT"); + if (hdr.flags & macho.MH_TWOLEVEL != 0) try bw.writeAll(" TWOLEVEL"); + if (hdr.flags & macho.MH_FORCE_FLAT != 0) try bw.writeAll(" FORCE_FLAT"); + if (hdr.flags & macho.MH_NOMULTIDEFS != 0) try bw.writeAll(" NOMULTIDEFS"); + if (hdr.flags & macho.MH_NOFIXPREBINDING != 0) try bw.writeAll(" NOFIXPREBINDING"); + if (hdr.flags & macho.MH_PREBINDABLE != 0) try bw.writeAll(" PREBINDABLE"); + if (hdr.flags & macho.MH_ALLMODSBOUND != 0) try bw.writeAll(" ALLMODSBOUND"); + if (hdr.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) try bw.writeAll(" SUBSECTIONS_VIA_SYMBOLS"); + if (hdr.flags & macho.MH_CANONICAL != 0) try bw.writeAll(" CANONICAL"); + if (hdr.flags & macho.MH_WEAK_DEFINES != 0) try bw.writeAll(" WEAK_DEFINES"); + if (hdr.flags & macho.MH_BINDS_TO_WEAK != 0) try bw.writeAll(" BINDS_TO_WEAK"); + if (hdr.flags & macho.MH_ALLOW_STACK_EXECUTION != 0) try bw.writeAll(" ALLOW_STACK_EXECUTION"); + if (hdr.flags & macho.MH_ROOT_SAFE != 0) try bw.writeAll(" ROOT_SAFE"); + if (hdr.flags & macho.MH_SETUID_SAFE != 0) try bw.writeAll(" SETUID_SAFE"); + if (hdr.flags & macho.MH_NO_REEXPORTED_DYLIBS != 0) try bw.writeAll(" NO_REEXPORTED_DYLIBS"); + if (hdr.flags & macho.MH_PIE != 0) try bw.writeAll(" PIE"); + if (hdr.flags & macho.MH_DEAD_STRIPPABLE_DYLIB != 0) try bw.writeAll(" DEAD_STRIPPABLE_DYLIB"); + if (hdr.flags & macho.MH_HAS_TLV_DESCRIPTORS != 0) try bw.writeAll(" HAS_TLV_DESCRIPTORS"); + if (hdr.flags & macho.MH_NO_HEAP_EXECUTION != 0) try bw.writeAll(" NO_HEAP_EXECUTION"); + if (hdr.flags & macho.MH_APP_EXTENSION_SAFE != 0) try bw.writeAll(" APP_EXTENSION_SAFE"); + if (hdr.flags & macho.MH_NLIST_OUTOFSYNC_WITH_DYLDINFO != 0) try bw.writeAll(" NLIST_OUTOFSYNC_WITH_DYLDINFO"); } - try writer.writeByte('\n'); + try bw.writeByte('\n'); } - fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void { + fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, bw: *Writer) !void { // print header first - try writer.print( + try bw.print( \\LC {d} \\cmd {s} \\cmdsize {d} @@ -889,8 +888,8 @@ const MachODumper = struct { switch (lc.cmd()) { .SEGMENT_64 => { const seg = lc.cast(macho.segment_command_64).?; - try writer.writeByte('\n'); - try writer.print( + try bw.writeByte('\n'); + try bw.print( \\segname {s} \\vmaddr {x} \\vmsize {x} @@ -905,8 +904,8 @@ const MachODumper = struct { }); for (lc.getSections()) |sect| { - try writer.writeByte('\n'); - try writer.print( + try bw.writeByte('\n'); + try bw.print( \\sectname {s} \\addr {x} \\size {x} @@ -928,8 +927,8 @@ const MachODumper = struct { .REEXPORT_DYLIB, => { const dylib = lc.cast(macho.dylib_command).?; - try writer.writeByte('\n'); - try writer.print( + try bw.writeByte('\n'); + try bw.print( \\name {s} \\timestamp {d} \\current version {x} @@ -944,16 +943,16 @@ const MachODumper = struct { .MAIN => { const main = lc.cast(macho.entry_point_command).?; - try writer.writeByte('\n'); - try writer.print( + try bw.writeByte('\n'); + try bw.print( \\entryoff {x} \\stacksize {x} , .{ main.entryoff, main.stacksize }); }, .RPATH => { - try writer.writeByte('\n'); - try writer.print( + try bw.writeByte('\n'); + try bw.print( \\path {s} , .{ lc.getRpathPathName(), @@ -962,8 +961,8 @@ const MachODumper = struct { .UUID => { const uuid = lc.cast(macho.uuid_command).?; - try writer.writeByte('\n'); - try writer.print("uuid {x}", .{std.fmt.fmtSliceHexLower(&uuid.uuid)}); + try bw.writeByte('\n'); + try bw.print("uuid {x}", .{&uuid.uuid}); }, .DATA_IN_CODE, @@ -971,8 +970,8 @@ const MachODumper = struct { .CODE_SIGNATURE, => { const llc = lc.cast(macho.linkedit_data_command).?; - try writer.writeByte('\n'); - try writer.print( + try bw.writeByte('\n'); + try bw.print( \\dataoff {x} \\datasize {x} , .{ llc.dataoff, llc.datasize }); @@ -980,8 +979,8 @@ const MachODumper = struct { .DYLD_INFO_ONLY => { const dlc = lc.cast(macho.dyld_info_command).?; - try writer.writeByte('\n'); - try writer.print( + try bw.writeByte('\n'); + try bw.print( \\rebaseoff {x} \\rebasesize {x} \\bindoff {x} @@ -1008,8 +1007,8 @@ const MachODumper = struct { .SYMTAB => { const slc = lc.cast(macho.symtab_command).?; - try writer.writeByte('\n'); - try writer.print( + try bw.writeByte('\n'); + try bw.print( \\symoff {x} \\nsyms {x} \\stroff {x} @@ -1024,8 +1023,8 @@ const MachODumper = struct { .DYSYMTAB => { const dlc = lc.cast(macho.dysymtab_command).?; - try writer.writeByte('\n'); - try writer.print( + try bw.writeByte('\n'); + try bw.print( \\ilocalsym {x} \\nlocalsym {x} \\iextdefsym {x} @@ -1048,8 +1047,8 @@ const MachODumper = struct { .BUILD_VERSION => { const blc = lc.cast(macho.build_version_command).?; - try writer.writeByte('\n'); - try writer.print( + try bw.writeByte('\n'); + try bw.print( \\platform {s} \\minos {d}.{d}.{d} \\sdk {d}.{d}.{d} @@ -1065,12 +1064,12 @@ const MachODumper = struct { blc.ntools, }); for (lc.getBuildVersionTools()) |tool| { - try writer.writeByte('\n'); + try bw.writeByte('\n'); switch (tool.tool) { - .CLANG, .SWIFT, .LD, .LLD, .ZIG => try writer.print("tool {s}\n", .{@tagName(tool.tool)}), - else => |x| try writer.print("tool {d}\n", .{@intFromEnum(x)}), + .CLANG, .SWIFT, .LD, .LLD, .ZIG => try bw.print("tool {s}\n", .{@tagName(tool.tool)}), + else => |x| try bw.print("tool {d}\n", .{@intFromEnum(x)}), } - try writer.print( + try bw.print( \\version {d}.{d}.{d} , .{ tool.version >> 16, @@ -1086,8 +1085,8 @@ const MachODumper = struct { .VERSION_MIN_TVOS, => { const vlc = lc.cast(macho.version_min_command).?; - try writer.writeByte('\n'); - try writer.print( + try bw.writeByte('\n'); + try bw.print( \\version {d}.{d}.{d} \\sdk {d}.{d}.{d} , .{ @@ -1104,8 +1103,8 @@ const MachODumper = struct { } } - fn dumpSymtab(ctx: ObjectContext, writer: anytype) !void { - try writer.writeAll(symtab_label ++ "\n"); + fn dumpSymtab(ctx: ObjectContext, bw: *Writer) !void { + try bw.writeAll(symtab_label ++ "\n"); for (ctx.symtab.items) |sym| { const sym_name = ctx.getString(sym.n_strx); @@ -1120,32 +1119,32 @@ const MachODumper = struct { macho.N_STSYM => "STSYM", else => "UNKNOWN STAB", }; - try writer.print("{x}", .{sym.n_value}); + try bw.print("{x}", .{sym.n_value}); if (sym.n_sect > 0) { const sect = ctx.sections.items[sym.n_sect - 1]; - try writer.print(" ({s},{s})", .{ sect.segName(), sect.sectName() }); + try bw.print(" ({s},{s})", .{ sect.segName(), sect.sectName() }); } - try writer.print(" {s} (stab) {s}\n", .{ tt, sym_name }); + try bw.print(" {s} (stab) {s}\n", .{ tt, sym_name }); } else if (sym.sect()) { const sect = ctx.sections.items[sym.n_sect - 1]; - try writer.print("{x} ({s},{s})", .{ + try bw.print("{x} ({s},{s})", .{ sym.n_value, sect.segName(), sect.sectName(), }); - if (sym.n_desc & macho.REFERENCED_DYNAMICALLY != 0) try writer.writeAll(" [referenced dynamically]"); - if (sym.weakDef()) try writer.writeAll(" weak"); - if (sym.weakRef()) try writer.writeAll(" weakref"); + if (sym.n_desc & macho.REFERENCED_DYNAMICALLY != 0) try bw.writeAll(" [referenced dynamically]"); + if (sym.weakDef()) try bw.writeAll(" weak"); + if (sym.weakRef()) try bw.writeAll(" weakref"); if (sym.ext()) { - if (sym.pext()) try writer.writeAll(" private"); - try writer.writeAll(" external"); - } else if (sym.pext()) try writer.writeAll(" (was private external)"); - try writer.print(" {s}\n", .{sym_name}); + if (sym.pext()) try bw.writeAll(" private"); + try bw.writeAll(" external"); + } else if (sym.pext()) try bw.writeAll(" (was private external)"); + try bw.print(" {s}\n", .{sym_name}); } else if (sym.tentative()) { const alignment = (sym.n_desc >> 8) & 0x0F; - try writer.print(" 0x{x:0>16} (common) (alignment 2^{d})", .{ sym.n_value, alignment }); - if (sym.ext()) try writer.writeAll(" external"); - try writer.print(" {s}\n", .{sym_name}); + try bw.print(" 0x{x:0>16} (common) (alignment 2^{d})", .{ sym.n_value, alignment }); + if (sym.ext()) try bw.writeAll(" external"); + try bw.print(" {s}\n", .{sym_name}); } else if (sym.undf()) { const ordinal = @divFloor(@as(i16, @bitCast(sym.n_desc)), macho.N_SYMBOL_RESOLVER); const import_name = blk: { @@ -1164,10 +1163,10 @@ const MachODumper = struct { const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len; break :blk basename[0..ext]; }; - try writer.writeAll("(undefined)"); - if (sym.weakRef()) try writer.writeAll(" weakref"); - if (sym.ext()) try writer.writeAll(" external"); - try writer.print(" {s} (from {s})\n", .{ + try bw.writeAll("(undefined)"); + if (sym.weakRef()) try bw.writeAll(" weakref"); + if (sym.ext()) try bw.writeAll(" external"); + try bw.print(" {s} (from {s})\n", .{ sym_name, import_name, }); @@ -1175,8 +1174,8 @@ const MachODumper = struct { } } - fn dumpIndirectSymtab(ctx: ObjectContext, writer: anytype) !void { - try writer.writeAll(indirect_symtab_label ++ "\n"); + fn dumpIndirectSymtab(ctx: ObjectContext, bw: *Writer) !void { + try bw.writeAll(indirect_symtab_label ++ "\n"); var sects_buffer: [3]macho.section_64 = undefined; const sects = blk: { @@ -1214,35 +1213,33 @@ const MachODumper = struct { break :blk @sizeOf(u64); }; - try writer.print("{s},{s}\n", .{ sect.segName(), sect.sectName() }); - try writer.print("nentries {d}\n", .{end - start}); + try bw.print("{s},{s}\n", .{ sect.segName(), sect.sectName() }); + try bw.print("nentries {d}\n", .{end - start}); for (ctx.indsymtab.items[start..end], 0..) |index, j| { const sym = ctx.symtab.items[index]; const addr = sect.addr + entry_size * j; - try writer.print("0x{x} {d} {s}\n", .{ addr, index, ctx.getString(sym.n_strx) }); + try bw.print("0x{x} {d} {s}\n", .{ addr, index, ctx.getString(sym.n_strx) }); } } } - fn dumpRebaseInfo(ctx: ObjectContext, data: []const u8, writer: anytype) !void { - var rebases = std.ArrayList(u64).init(ctx.gpa); + fn dumpRebaseInfo(ctx: ObjectContext, data: []const u8, bw: *Writer) !void { + var rebases: std.ArrayList(u64) = .init(ctx.gpa); defer rebases.deinit(); try ctx.parseRebaseInfo(data, &rebases); mem.sort(u64, rebases.items, {}, std.sort.asc(u64)); for (rebases.items) |addr| { - try writer.print("0x{x}\n", .{addr}); + try bw.print("0x{x}\n", .{addr}); } } fn parseRebaseInfo(ctx: ObjectContext, data: []const u8, rebases: *std.ArrayList(u64)) !void { - var stream = std.io.fixedBufferStream(data); - var creader = std.io.countingReader(stream.reader()); - const reader = creader.reader(); + var br: std.io.Reader = .fixed(data); var seg_id: ?u8 = null; var offset: u64 = 0; while (true) { - const byte = reader.readByte() catch break; + const byte = br.takeByte() catch break; const opc = byte & macho.REBASE_OPCODE_MASK; const imm = byte & macho.REBASE_IMMEDIATE_MASK; switch (opc) { @@ -1250,17 +1247,17 @@ const MachODumper = struct { macho.REBASE_OPCODE_SET_TYPE_IMM => {}, macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => { seg_id = imm; - offset = try std.leb.readUleb128(u64, reader); + offset = try br.takeLeb128(u64); }, macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED => { offset += imm * @sizeOf(u64); }, macho.REBASE_OPCODE_ADD_ADDR_ULEB => { - const addend = try std.leb.readUleb128(u64, reader); + const addend = try br.takeLeb128(u64); offset += addend; }, macho.REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB => { - const addend = try std.leb.readUleb128(u64, reader); + const addend = try br.takeLeb128(u64); const seg = ctx.segments.items[seg_id.?]; const addr = seg.vmaddr + offset; try rebases.append(addr); @@ -1277,11 +1274,11 @@ const MachODumper = struct { ntimes = imm; }, macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES => { - ntimes = try std.leb.readUleb128(u64, reader); + ntimes = try br.takeLeb128(u64); }, macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB => { - ntimes = try std.leb.readUleb128(u64, reader); - skip = try std.leb.readUleb128(u64, reader); + ntimes = try br.takeLeb128(u64); + skip = try br.takeLeb128(u64); }, else => unreachable, } @@ -1323,8 +1320,8 @@ const MachODumper = struct { }; }; - fn dumpBindInfo(ctx: ObjectContext, data: []const u8, writer: anytype) !void { - var bindings = std.ArrayList(Binding).init(ctx.gpa); + fn dumpBindInfo(ctx: ObjectContext, data: []const u8, bw: *Writer) !void { + var bindings: std.ArrayList(Binding) = .init(ctx.gpa); defer { for (bindings.items) |*b| { b.deinit(ctx.gpa); @@ -1334,22 +1331,20 @@ const MachODumper = struct { try ctx.parseBindInfo(data, &bindings); mem.sort(Binding, bindings.items, {}, Binding.lessThan); for (bindings.items) |binding| { - try writer.print("0x{x} [addend: {d}]", .{ binding.address, binding.addend }); - try writer.writeAll(" ("); + try bw.print("0x{x} [addend: {d}]", .{ binding.address, binding.addend }); + try bw.writeAll(" ("); switch (binding.tag) { - .self => try writer.writeAll("self"), - .exe => try writer.writeAll("main executable"), - .flat => try writer.writeAll("flat lookup"), - .ord => try writer.writeAll(std.fs.path.basename(ctx.imports.items[binding.ordinal - 1])), + .self => try bw.writeAll("self"), + .exe => try bw.writeAll("main executable"), + .flat => try bw.writeAll("flat lookup"), + .ord => try bw.writeAll(std.fs.path.basename(ctx.imports.items[binding.ordinal - 1])), } - try writer.print(") {s}\n", .{binding.name}); + try bw.print(") {s}\n", .{binding.name}); } } fn parseBindInfo(ctx: ObjectContext, data: []const u8, bindings: *std.ArrayList(Binding)) !void { - var stream = std.io.fixedBufferStream(data); - var creader = std.io.countingReader(stream.reader()); - const reader = creader.reader(); + var br: std.io.Reader = .fixed(data); var seg_id: ?u8 = null; var tag: Binding.Tag = .self; @@ -1357,11 +1352,10 @@ const MachODumper = struct { var offset: u64 = 0; var addend: i64 = 0; - var name_buf = std.ArrayList(u8).init(ctx.gpa); + var name_buf: std.ArrayList(u8) = .init(ctx.gpa); defer name_buf.deinit(); - while (true) { - const byte = reader.readByte() catch break; + while (br.takeByte()) |byte| { const opc = byte & macho.BIND_OPCODE_MASK; const imm = byte & macho.BIND_IMMEDIATE_MASK; switch (opc) { @@ -1382,18 +1376,19 @@ const MachODumper = struct { }, macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => { seg_id = imm; - offset = try std.leb.readUleb128(u64, reader); + offset = try br.takeLeb128(u64); }, macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => { name_buf.clearRetainingCapacity(); - try reader.readUntilDelimiterArrayList(&name_buf, 0, std.math.maxInt(u32)); + if (true) @panic("TODO fix this"); + //try reader.readUntilDelimiterArrayList(&name_buf, 0, std.math.maxInt(u32)); try name_buf.append(0); }, macho.BIND_OPCODE_SET_ADDEND_SLEB => { - addend = try std.leb.readIleb128(i64, reader); + addend = try br.takeLeb128(i64); }, macho.BIND_OPCODE_ADD_ADDR_ULEB => { - const x = try std.leb.readUleb128(u64, reader); + const x = try br.takeLeb128(u64); offset = @intCast(@as(i64, @intCast(offset)) + @as(i64, @bitCast(x))); }, macho.BIND_OPCODE_DO_BIND, @@ -1408,14 +1403,14 @@ const MachODumper = struct { switch (opc) { macho.BIND_OPCODE_DO_BIND => {}, macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB => { - add_addr = try std.leb.readUleb128(u64, reader); + add_addr = try br.takeLeb128(u64); }, macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED => { add_addr = imm * @sizeOf(u64); }, macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB => { - count = try std.leb.readUleb128(u64, reader); - skip = try std.leb.readUleb128(u64, reader); + count = try br.takeLeb128(u64); + skip = try br.takeLeb128(u64); }, else => unreachable, } @@ -1436,18 +1431,18 @@ const MachODumper = struct { }, else => break, } - } + } else |_| {} } - fn dumpExportsTrie(ctx: ObjectContext, data: []const u8, writer: anytype) !void { + fn dumpExportsTrie(ctx: ObjectContext, data: []const u8, bw: *Writer) !void { const seg = ctx.getSegmentByName("__TEXT") orelse return; var arena = std.heap.ArenaAllocator.init(ctx.gpa); defer arena.deinit(); - var exports = std.ArrayList(Export).init(arena.allocator()); - var it = TrieIterator{ .data = data }; - try parseTrieNode(arena.allocator(), &it, "", &exports); + var exports: std.ArrayList(Export) = .init(arena.allocator()); + var br: std.io.Reader = .fixed(data); + try parseTrieNode(arena.allocator(), &br, "", &exports); mem.sort(Export, exports.items, {}, Export.lessThan); @@ -1456,66 +1451,26 @@ const MachODumper = struct { .@"export" => { const info = exp.data.@"export"; if (info.kind != .regular or info.weak) { - try writer.writeByte('['); + try bw.writeByte('['); } switch (info.kind) { .regular => {}, - .absolute => try writer.writeAll("ABS, "), - .tlv => try writer.writeAll("THREAD_LOCAL, "), + .absolute => try bw.writeAll("ABS, "), + .tlv => try bw.writeAll("THREAD_LOCAL, "), } - if (info.weak) try writer.writeAll("WEAK"); + if (info.weak) try bw.writeAll("WEAK"); if (info.kind != .regular or info.weak) { - try writer.writeAll("] "); + try bw.writeAll("] "); } - try writer.print("{x} ", .{seg.vmaddr + info.vmoffset}); + try bw.print("{x} ", .{seg.vmaddr + info.vmoffset}); }, else => {}, } - try writer.print("{s}\n", .{exp.name}); + try bw.print("{s}\n", .{exp.name}); } } - const TrieIterator = struct { - data: []const u8, - pos: usize = 0, - - fn getStream(it: *TrieIterator) std.io.FixedBufferStream([]const u8) { - return std.io.fixedBufferStream(it.data[it.pos..]); - } - - fn readUleb128(it: *TrieIterator) !u64 { - var stream = it.getStream(); - var creader = std.io.countingReader(stream.reader()); - const reader = creader.reader(); - const value = try std.leb.readUleb128(u64, reader); - it.pos += creader.bytes_read; - return value; - } - - fn readString(it: *TrieIterator) ![:0]const u8 { - var stream = it.getStream(); - const reader = stream.reader(); - - var count: usize = 0; - while (true) : (count += 1) { - const byte = try reader.readByte(); - if (byte == 0) break; - } - - const str = @as([*:0]const u8, @ptrCast(it.data.ptr + it.pos))[0..count :0]; - it.pos += count + 1; - return str; - } - - fn readByte(it: *TrieIterator) !u8 { - var stream = it.getStream(); - const value = try stream.reader().readByte(); - it.pos += 1; - return value; - } - }; - const Export = struct { name: []const u8, tag: enum { @"export", reexport, stub_resolver }, @@ -1555,17 +1510,17 @@ const MachODumper = struct { fn parseTrieNode( arena: Allocator, - it: *TrieIterator, + br: *std.io.Reader, prefix: []const u8, exports: *std.ArrayList(Export), ) !void { - const size = try it.readUleb128(); + const size = try br.takeLeb128(u64); if (size > 0) { - const flags = try it.readUleb128(); + const flags = try br.takeLeb128(u8); switch (flags) { macho.EXPORT_SYMBOL_FLAGS_REEXPORT => { - const ord = try it.readUleb128(); - const name = try arena.dupe(u8, try it.readString()); + const ord = try br.takeLeb128(u64); + const name = try br.takeSentinel(0); try exports.append(.{ .name = if (name.len > 0) name else prefix, .tag = .reexport, @@ -1573,8 +1528,8 @@ const MachODumper = struct { }); }, macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER => { - const stub_offset = try it.readUleb128(); - const resolver_offset = try it.readUleb128(); + const stub_offset = try br.takeLeb128(u64); + const resolver_offset = try br.takeLeb128(u64); try exports.append(.{ .name = prefix, .tag = .stub_resolver, @@ -1585,7 +1540,7 @@ const MachODumper = struct { }); }, else => { - const vmoff = try it.readUleb128(); + const vmoff = try br.takeLeb128(u64); try exports.append(.{ .name = prefix, .tag = .@"export", @@ -1604,21 +1559,21 @@ const MachODumper = struct { } } - const nedges = try it.readByte(); + const nedges = try br.takeByte(); for (0..nedges) |_| { - const label = try it.readString(); - const off = try it.readUleb128(); + const label = try br.takeSentinel(0); + const off = try br.takeLeb128(usize); const prefix_label = try std.fmt.allocPrint(arena, "{s}{s}", .{ prefix, label }); - const curr = it.pos; - it.pos = off; - try parseTrieNode(arena, it, prefix_label, exports); - it.pos = curr; + const seek = br.seek; + br.seek = off; + try parseTrieNode(arena, br, prefix_label, exports); + br.seek = seek; } } - fn dumpSection(ctx: ObjectContext, sect: macho.section_64, writer: anytype) !void { + fn dumpSection(ctx: ObjectContext, sect: macho.section_64, bw: *Writer) !void { const data = ctx.data[sect.offset..][0..sect.size]; - try writer.print("{s}", .{data}); + try bw.print("{s}", .{data}); } }; @@ -1632,29 +1587,30 @@ const MachODumper = struct { var ctx = ObjectContext{ .gpa = gpa, .data = bytes, .header = hdr }; try ctx.parse(); - var output = std.ArrayList(u8).init(gpa); - const writer = output.writer(); + var aw: std.io.Writer.Allocating = .init(gpa); + defer aw.deinit(); + const bw = &aw.interface; switch (check.kind) { .headers => { - try ObjectContext.dumpHeader(ctx.header, writer); + try ObjectContext.dumpHeader(ctx.header, bw); var it = ctx.getLoadCommandIterator(); var i: usize = 0; while (it.next()) |cmd| { - try ObjectContext.dumpLoadCommand(cmd, i, writer); - try writer.writeByte('\n'); + try ObjectContext.dumpLoadCommand(cmd, i, bw); + try bw.writeByte('\n'); i += 1; } }, .symtab => if (ctx.symtab.items.len > 0) { - try ctx.dumpSymtab(writer); + try ctx.dumpSymtab(bw); } else return step.fail("no symbol table found", .{}), .indirect_symtab => if (ctx.symtab.items.len > 0 and ctx.indsymtab.items.len > 0) { - try ctx.dumpIndirectSymtab(writer); + try ctx.dumpIndirectSymtab(bw); } else return step.fail("no indirect symbol table found", .{}), .dyld_rebase, @@ -1669,26 +1625,26 @@ const MachODumper = struct { switch (check.kind) { .dyld_rebase => if (lc.rebase_size > 0) { const data = ctx.data[lc.rebase_off..][0..lc.rebase_size]; - try writer.writeAll(dyld_rebase_label ++ "\n"); - try ctx.dumpRebaseInfo(data, writer); + try bw.writeAll(dyld_rebase_label ++ "\n"); + try ctx.dumpRebaseInfo(data, bw); } else return step.fail("no rebase data found", .{}), .dyld_bind => if (lc.bind_size > 0) { const data = ctx.data[lc.bind_off..][0..lc.bind_size]; - try writer.writeAll(dyld_bind_label ++ "\n"); - try ctx.dumpBindInfo(data, writer); + try bw.writeAll(dyld_bind_label ++ "\n"); + try ctx.dumpBindInfo(data, bw); } else return step.fail("no bind data found", .{}), .dyld_weak_bind => if (lc.weak_bind_size > 0) { const data = ctx.data[lc.weak_bind_off..][0..lc.weak_bind_size]; - try writer.writeAll(dyld_weak_bind_label ++ "\n"); - try ctx.dumpBindInfo(data, writer); + try bw.writeAll(dyld_weak_bind_label ++ "\n"); + try ctx.dumpBindInfo(data, bw); } else return step.fail("no weak bind data found", .{}), .dyld_lazy_bind => if (lc.lazy_bind_size > 0) { const data = ctx.data[lc.lazy_bind_off..][0..lc.lazy_bind_size]; - try writer.writeAll(dyld_lazy_bind_label ++ "\n"); - try ctx.dumpBindInfo(data, writer); + try bw.writeAll(dyld_lazy_bind_label ++ "\n"); + try ctx.dumpBindInfo(data, bw); } else return step.fail("no lazy bind data found", .{}), else => unreachable, @@ -1700,8 +1656,8 @@ const MachODumper = struct { const lc = cmd.cast(macho.dyld_info_command).?; if (lc.export_size > 0) { const data = ctx.data[lc.export_off..][0..lc.export_size]; - try writer.writeAll(exports_label ++ "\n"); - try ctx.dumpExportsTrie(data, writer); + try bw.writeAll(exports_label ++ "\n"); + try ctx.dumpExportsTrie(data, bw); break :blk; } } @@ -1709,20 +1665,20 @@ const MachODumper = struct { }, .dump_section => { - const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(check.data.items.ptr + check.payload.dump_section)), 0); + const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(check.data.items[check.payload.dump_section..].ptr)), 0); const sep_index = mem.indexOfScalar(u8, name, ',') orelse return step.fail("invalid section name: {s}", .{name}); const segname = name[0..sep_index]; const sectname = name[sep_index + 1 ..]; const sect = ctx.getSectionByName(segname, sectname) orelse return step.fail("section '{s}' not found", .{name}); - try ctx.dumpSection(sect, writer); + try ctx.dumpSection(sect, bw); }, else => return step.fail("invalid check kind for MachO file format: {s}", .{@tagName(check.kind)}), } - return output.toOwnedSlice(); + return aw.toOwnedSlice(); } }; @@ -1741,161 +1697,138 @@ const ElfDumper = struct { fn parseAndDumpArchive(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; - var stream = std.io.fixedBufferStream(bytes); - const reader = stream.reader(); + var br: std.io.Reader = .fixed(bytes); - const magic = try reader.readBytesNoEof(elf.ARMAG.len); - if (!mem.eql(u8, &magic, elf.ARMAG)) { - return error.InvalidArchiveMagicNumber; - } + if (!mem.eql(u8, try br.takeArray(elf.ARMAG.len), elf.ARMAG)) return error.InvalidArchiveMagicNumber; - var ctx = ArchiveContext{ + var ctx: ArchiveContext = .{ .gpa = gpa, .data = bytes, - .strtab = &[0]u8{}, + .symtab = &.{}, + .strtab = &.{}, + .objects = .empty, }; - defer { - for (ctx.objects.items) |*object| { - gpa.free(object.name); - } - ctx.objects.deinit(gpa); - } + defer ctx.deinit(); - while (true) { - if (stream.pos >= ctx.data.len) break; - if (!mem.isAligned(stream.pos, 2)) stream.pos += 1; - - const hdr = try reader.readStruct(elf.ar_hdr); + while (br.seek < bytes.len) { + const hdr_seek = std.mem.alignForward(usize, br.seek, 2); + br.seek = hdr_seek; + const hdr = try br.takeStruct(elf.ar_hdr); if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) return error.InvalidArchiveHeaderMagicNumber; - const size = try hdr.size(); - defer { - _ = stream.seekBy(size) catch {}; - } + const data = try br.take(try hdr.size()); if (hdr.isSymtab()) { - try ctx.parseSymtab(ctx.data[stream.pos..][0..size], .p32); + try ctx.parseSymtab(data, .p32); continue; } if (hdr.isSymtab64()) { - try ctx.parseSymtab(ctx.data[stream.pos..][0..size], .p64); + try ctx.parseSymtab(data, .p64); continue; } if (hdr.isStrtab()) { - ctx.strtab = ctx.data[stream.pos..][0..size]; + ctx.strtab = data; continue; } if (hdr.isSymdef() or hdr.isSymdefSorted()) continue; - const name = if (hdr.name()) |name| - try gpa.dupe(u8, name) - else if (try hdr.nameOffset()) |off| - try gpa.dupe(u8, ctx.getString(off)) - else - unreachable; - - try ctx.objects.append(gpa, .{ .name = name, .off = stream.pos, .len = size }); + const name = hdr.name() orelse ctx.getString((try hdr.nameOffset()).?); + try ctx.objects.putNoClobber(gpa, hdr_seek, .{ + .name = name, + .data = data, + }); } - var output = std.ArrayList(u8).init(gpa); - const writer = output.writer(); + var aw: std.io.Writer.Allocating = .init(gpa); + defer aw.deinit(); + const bw = &aw.interface; switch (check.kind) { - .archive_symtab => if (ctx.symtab.items.len > 0) { - try ctx.dumpSymtab(writer); + .archive_symtab => if (ctx.symtab.len > 0) { + try ctx.dumpSymtab(bw); } else return step.fail("no archive symbol table found", .{}), - else => if (ctx.objects.items.len > 0) { - try ctx.dumpObjects(step, check, writer); + else => if (ctx.objects.count() > 0) { + try ctx.dumpObjects(step, check, bw); } else return step.fail("empty archive", .{}), } - return output.toOwnedSlice(); + return aw.toOwnedSlice(); } const ArchiveContext = struct { gpa: Allocator, data: []const u8, - symtab: std.ArrayListUnmanaged(ArSymtabEntry) = .empty, + symtab: []ArSymtabEntry, strtab: []const u8, - objects: std.ArrayListUnmanaged(struct { name: []const u8, off: usize, len: usize }) = .empty, + objects: std.AutoArrayHashMapUnmanaged(usize, struct { name: []const u8, data: []const u8 }), - fn parseSymtab(ctx: *ArchiveContext, raw: []const u8, ptr_width: enum { p32, p64 }) !void { - var stream = std.io.fixedBufferStream(raw); - const reader = stream.reader(); + fn deinit(ctx: *ArchiveContext) void { + ctx.gpa.free(ctx.symtab); + ctx.objects.deinit(ctx.gpa); + } + + fn parseSymtab(ctx: *ArchiveContext, data: []const u8, ptr_width: enum { p32, p64 }) !void { + var br: std.io.Reader = .fixed(data); const num = switch (ptr_width) { - .p32 => try reader.readInt(u32, .big), - .p64 => try reader.readInt(u64, .big), + .p32 => try br.takeInt(u32, .big), + .p64 => try br.takeInt(u64, .big), }; const ptr_size: usize = switch (ptr_width) { .p32 => @sizeOf(u32), .p64 => @sizeOf(u64), }; - const strtab_off = (num + 1) * ptr_size; - const strtab_len = raw.len - strtab_off; - const strtab = raw[strtab_off..][0..strtab_len]; + _ = try br.discard(.limited(num * ptr_size)); + const strtab = br.buffered(); - try ctx.symtab.ensureTotalCapacityPrecise(ctx.gpa, num); + assert(ctx.symtab.len == 0); + ctx.symtab = try ctx.gpa.alloc(ArSymtabEntry, num); var stroff: usize = 0; - for (0..num) |_| { + for (ctx.symtab) |*entry| { const off = switch (ptr_width) { - .p32 => try reader.readInt(u32, .big), - .p64 => try reader.readInt(u64, .big), + .p32 => try br.takeInt(u32, .big), + .p64 => try br.takeInt(u64, .big), }; - const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(strtab.ptr + stroff)), 0); + const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(strtab[stroff..].ptr)), 0); stroff += name.len + 1; - ctx.symtab.appendAssumeCapacity(.{ .off = off, .name = name }); + entry.* = .{ .off = off, .name = name }; } } - fn dumpSymtab(ctx: ArchiveContext, writer: anytype) !void { - var files = std.AutoHashMap(usize, []const u8).init(ctx.gpa); - defer files.deinit(); - try files.ensureUnusedCapacity(@intCast(ctx.objects.items.len)); - - for (ctx.objects.items) |object| { - files.putAssumeCapacityNoClobber(object.off - @sizeOf(elf.ar_hdr), object.name); - } - - var symbols = std.AutoArrayHashMap(usize, std.ArrayList([]const u8)).init(ctx.gpa); + fn dumpSymtab(ctx: ArchiveContext, bw: *Writer) !void { + var symbols: std.AutoArrayHashMap(usize, std.ArrayList([]const u8)) = .init(ctx.gpa); defer { - for (symbols.values()) |*value| { - value.deinit(); - } + for (symbols.values()) |*value| value.deinit(); symbols.deinit(); } - for (ctx.symtab.items) |entry| { + for (ctx.symtab) |entry| { const gop = try symbols.getOrPut(@intCast(entry.off)); - if (!gop.found_existing) { - gop.value_ptr.* = std.ArrayList([]const u8).init(ctx.gpa); - } + if (!gop.found_existing) gop.value_ptr.* = .init(ctx.gpa); try gop.value_ptr.append(entry.name); } - try writer.print("{s}\n", .{archive_symtab_label}); + try bw.print("{s}\n", .{archive_symtab_label}); for (symbols.keys(), symbols.values()) |off, values| { - try writer.print("in object {s}\n", .{files.get(off).?}); - for (values.items) |value| { - try writer.print("{s}\n", .{value}); - } + try bw.print("in object {s}\n", .{ctx.objects.get(off).?.name}); + for (values.items) |value| try bw.print("{s}\n", .{value}); } } - fn dumpObjects(ctx: ArchiveContext, step: *Step, check: Check, writer: anytype) !void { - for (ctx.objects.items) |object| { - try writer.print("object {s}\n", .{object.name}); - const output = try parseAndDumpObject(step, check, ctx.data[object.off..][0..object.len]); + fn dumpObjects(ctx: ArchiveContext, step: *Step, check: Check, bw: *Writer) !void { + for (ctx.objects.values()) |object| { + try bw.print("object {s}\n", .{object.name}); + const output = try parseAndDumpObject(step, check, object.data); defer ctx.gpa.free(output); - try writer.print("{s}\n", .{output}); + try bw.print("{s}\n", .{output}); } } fn getString(ctx: ArchiveContext, off: u32) []const u8 { assert(off < ctx.strtab.len); - const name = mem.sliceTo(@as([*:'\n']const u8, @ptrCast(ctx.strtab.ptr + off)), 0); + const name = mem.sliceTo(@as([*:'\n']const u8, @ptrCast(ctx.strtab[off..].ptr)), 0); return name[0 .. name.len - 1]; } @@ -1907,24 +1840,23 @@ const ElfDumper = struct { fn parseAndDumpObject(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; - var stream = std.io.fixedBufferStream(bytes); - const reader = stream.reader(); + var br: std.io.Reader = .fixed(bytes); - const hdr = try reader.readStruct(elf.Elf64_Ehdr); - if (!mem.eql(u8, hdr.e_ident[0..4], "\x7fELF")) { - return error.InvalidMagicNumber; - } + const hdr = try br.takeStruct(elf.Elf64_Ehdr); + if (!mem.eql(u8, hdr.e_ident[0..4], "\x7fELF")) return error.InvalidMagicNumber; - const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(bytes.ptr + hdr.e_shoff))[0..hdr.e_shnum]; - const phdrs = @as([*]align(1) const elf.Elf64_Phdr, @ptrCast(bytes.ptr + hdr.e_phoff))[0..hdr.e_phnum]; + const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(bytes[hdr.e_shoff..].ptr))[0..hdr.e_shnum]; + const phdrs = @as([*]align(1) const elf.Elf64_Phdr, @ptrCast(bytes[hdr.e_phoff..].ptr))[0..hdr.e_phnum]; - var ctx = ObjectContext{ + var ctx: ObjectContext = .{ .gpa = gpa, .data = bytes, .hdr = hdr, .shdrs = shdrs, .phdrs = phdrs, .shstrtab = undefined, + .symtab = .{}, + .dysymtab = .{}, }; ctx.shstrtab = ctx.getSectionContents(ctx.hdr.e_shstrndx); @@ -1955,120 +1887,121 @@ const ElfDumper = struct { else => {}, }; - var output = std.ArrayList(u8).init(gpa); - const writer = output.writer(); + var aw: std.io.Writer.Allocating = .init(gpa); + defer aw.deinit(); + const bw = &aw.interface; switch (check.kind) { .headers => { - try ctx.dumpHeader(writer); - try ctx.dumpShdrs(writer); - try ctx.dumpPhdrs(writer); + try ctx.dumpHeader(bw); + try ctx.dumpShdrs(bw); + try ctx.dumpPhdrs(bw); }, .symtab => if (ctx.symtab.symbols.len > 0) { - try ctx.dumpSymtab(.symtab, writer); + try ctx.dumpSymtab(.symtab, bw); } else return step.fail("no symbol table found", .{}), .dynamic_symtab => if (ctx.dysymtab.symbols.len > 0) { - try ctx.dumpSymtab(.dysymtab, writer); + try ctx.dumpSymtab(.dysymtab, bw); } else return step.fail("no dynamic symbol table found", .{}), .dynamic_section => if (ctx.getSectionByName(".dynamic")) |shndx| { - try ctx.dumpDynamicSection(shndx, writer); + try ctx.dumpDynamicSection(shndx, bw); } else return step.fail("no .dynamic section found", .{}), .dump_section => { - const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(check.data.items.ptr + check.payload.dump_section)), 0); + const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(check.data.items[check.payload.dump_section..].ptr)), 0); const shndx = ctx.getSectionByName(name) orelse return step.fail("no '{s}' section found", .{name}); - try ctx.dumpSection(shndx, writer); + try ctx.dumpSection(shndx, bw); }, else => return step.fail("invalid check kind for ELF file format: {s}", .{@tagName(check.kind)}), } - return output.toOwnedSlice(); + return aw.toOwnedSlice(); } const ObjectContext = struct { gpa: Allocator, data: []const u8, - hdr: elf.Elf64_Ehdr, + hdr: *align(1) const elf.Elf64_Ehdr, shdrs: []align(1) const elf.Elf64_Shdr, phdrs: []align(1) const elf.Elf64_Phdr, shstrtab: []const u8, - symtab: Symtab = .{}, - dysymtab: Symtab = .{}, + symtab: Symtab, + dysymtab: Symtab, - fn dumpHeader(ctx: ObjectContext, writer: anytype) !void { - try writer.writeAll("header\n"); - try writer.print("type {s}\n", .{@tagName(ctx.hdr.e_type)}); - try writer.print("entry {x}\n", .{ctx.hdr.e_entry}); + fn dumpHeader(ctx: ObjectContext, bw: *Writer) !void { + try bw.writeAll("header\n"); + try bw.print("type {s}\n", .{@tagName(ctx.hdr.e_type)}); + try bw.print("entry {x}\n", .{ctx.hdr.e_entry}); } - fn dumpPhdrs(ctx: ObjectContext, writer: anytype) !void { + fn dumpPhdrs(ctx: ObjectContext, bw: *Writer) !void { if (ctx.phdrs.len == 0) return; - try writer.writeAll("program headers\n"); + try bw.writeAll("program headers\n"); for (ctx.phdrs, 0..) |phdr, phndx| { - try writer.print("phdr {d}\n", .{phndx}); - try writer.print("type {s}\n", .{fmtPhType(phdr.p_type)}); - try writer.print("vaddr {x}\n", .{phdr.p_vaddr}); - try writer.print("paddr {x}\n", .{phdr.p_paddr}); - try writer.print("offset {x}\n", .{phdr.p_offset}); - try writer.print("memsz {x}\n", .{phdr.p_memsz}); - try writer.print("filesz {x}\n", .{phdr.p_filesz}); - try writer.print("align {x}\n", .{phdr.p_align}); + try bw.print("phdr {d}\n", .{phndx}); + try bw.print("type {f}\n", .{fmtPhType(phdr.p_type)}); + try bw.print("vaddr {x}\n", .{phdr.p_vaddr}); + try bw.print("paddr {x}\n", .{phdr.p_paddr}); + try bw.print("offset {x}\n", .{phdr.p_offset}); + try bw.print("memsz {x}\n", .{phdr.p_memsz}); + try bw.print("filesz {x}\n", .{phdr.p_filesz}); + try bw.print("align {x}\n", .{phdr.p_align}); { const flags = phdr.p_flags; - try writer.writeAll("flags"); - if (flags > 0) try writer.writeByte(' '); + try bw.writeAll("flags"); + if (flags > 0) try bw.writeByte(' '); if (flags & elf.PF_R != 0) { - try writer.writeByte('R'); + try bw.writeByte('R'); } if (flags & elf.PF_W != 0) { - try writer.writeByte('W'); + try bw.writeByte('W'); } if (flags & elf.PF_X != 0) { - try writer.writeByte('E'); + try bw.writeByte('E'); } if (flags & elf.PF_MASKOS != 0) { - try writer.writeAll("OS"); + try bw.writeAll("OS"); } if (flags & elf.PF_MASKPROC != 0) { - try writer.writeAll("PROC"); + try bw.writeAll("PROC"); } - try writer.writeByte('\n'); + try bw.writeByte('\n'); } } } - fn dumpShdrs(ctx: ObjectContext, writer: anytype) !void { + fn dumpShdrs(ctx: ObjectContext, bw: *Writer) !void { if (ctx.shdrs.len == 0) return; - try writer.writeAll("section headers\n"); + try bw.writeAll("section headers\n"); for (ctx.shdrs, 0..) |shdr, shndx| { - try writer.print("shdr {d}\n", .{shndx}); - try writer.print("name {s}\n", .{ctx.getSectionName(shndx)}); - try writer.print("type {s}\n", .{fmtShType(shdr.sh_type)}); - try writer.print("addr {x}\n", .{shdr.sh_addr}); - try writer.print("offset {x}\n", .{shdr.sh_offset}); - try writer.print("size {x}\n", .{shdr.sh_size}); - try writer.print("addralign {x}\n", .{shdr.sh_addralign}); + try bw.print("shdr {d}\n", .{shndx}); + try bw.print("name {s}\n", .{ctx.getSectionName(shndx)}); + try bw.print("type {f}\n", .{fmtShType(shdr.sh_type)}); + try bw.print("addr {x}\n", .{shdr.sh_addr}); + try bw.print("offset {x}\n", .{shdr.sh_offset}); + try bw.print("size {x}\n", .{shdr.sh_size}); + try bw.print("addralign {x}\n", .{shdr.sh_addralign}); // TODO dump formatted sh_flags } } - fn dumpDynamicSection(ctx: ObjectContext, shndx: usize, writer: anytype) !void { + fn dumpDynamicSection(ctx: ObjectContext, shndx: usize, bw: *Writer) !void { const shdr = ctx.shdrs[shndx]; const strtab = ctx.getSectionContents(shdr.sh_link); const data = ctx.getSectionContents(shndx); const nentries = @divExact(data.len, @sizeOf(elf.Elf64_Dyn)); const entries = @as([*]align(1) const elf.Elf64_Dyn, @ptrCast(data.ptr))[0..nentries]; - try writer.writeAll(ElfDumper.dynamic_section_label ++ "\n"); + try bw.writeAll(ElfDumper.dynamic_section_label ++ "\n"); for (entries) |entry| { const key = @as(u64, @bitCast(entry.d_tag)); @@ -2109,7 +2042,7 @@ const ElfDumper = struct { elf.DT_NULL => "NULL", else => "UNKNOWN", }; - try writer.print("{s}", .{key_str}); + try bw.print("{s}", .{key_str}); switch (key) { elf.DT_NEEDED, @@ -2118,7 +2051,7 @@ const ElfDumper = struct { elf.DT_RUNPATH, => { const name = getString(strtab, @intCast(value)); - try writer.print(" {s}", .{name}); + try bw.print(" {s}", .{name}); }, elf.DT_INIT_ARRAY, @@ -2136,7 +2069,7 @@ const ElfDumper = struct { elf.DT_INIT, elf.DT_FINI, elf.DT_NULL, - => try writer.print(" {x}", .{value}), + => try bw.print(" {x}", .{value}), elf.DT_INIT_ARRAYSZ, elf.DT_FINI_ARRAYSZ, @@ -2146,77 +2079,77 @@ const ElfDumper = struct { elf.DT_RELASZ, elf.DT_RELAENT, elf.DT_RELACOUNT, - => try writer.print(" {d}", .{value}), + => try bw.print(" {d}", .{value}), - elf.DT_PLTREL => try writer.writeAll(switch (value) { + elf.DT_PLTREL => try bw.writeAll(switch (value) { elf.DT_REL => " REL", elf.DT_RELA => " RELA", else => " UNKNOWN", }), elf.DT_FLAGS => if (value > 0) { - if (value & elf.DF_ORIGIN != 0) try writer.writeAll(" ORIGIN"); - if (value & elf.DF_SYMBOLIC != 0) try writer.writeAll(" SYMBOLIC"); - if (value & elf.DF_TEXTREL != 0) try writer.writeAll(" TEXTREL"); - if (value & elf.DF_BIND_NOW != 0) try writer.writeAll(" BIND_NOW"); - if (value & elf.DF_STATIC_TLS != 0) try writer.writeAll(" STATIC_TLS"); + if (value & elf.DF_ORIGIN != 0) try bw.writeAll(" ORIGIN"); + if (value & elf.DF_SYMBOLIC != 0) try bw.writeAll(" SYMBOLIC"); + if (value & elf.DF_TEXTREL != 0) try bw.writeAll(" TEXTREL"); + if (value & elf.DF_BIND_NOW != 0) try bw.writeAll(" BIND_NOW"); + if (value & elf.DF_STATIC_TLS != 0) try bw.writeAll(" STATIC_TLS"); }, elf.DT_FLAGS_1 => if (value > 0) { - if (value & elf.DF_1_NOW != 0) try writer.writeAll(" NOW"); - if (value & elf.DF_1_GLOBAL != 0) try writer.writeAll(" GLOBAL"); - if (value & elf.DF_1_GROUP != 0) try writer.writeAll(" GROUP"); - if (value & elf.DF_1_NODELETE != 0) try writer.writeAll(" NODELETE"); - if (value & elf.DF_1_LOADFLTR != 0) try writer.writeAll(" LOADFLTR"); - if (value & elf.DF_1_INITFIRST != 0) try writer.writeAll(" INITFIRST"); - if (value & elf.DF_1_NOOPEN != 0) try writer.writeAll(" NOOPEN"); - if (value & elf.DF_1_ORIGIN != 0) try writer.writeAll(" ORIGIN"); - if (value & elf.DF_1_DIRECT != 0) try writer.writeAll(" DIRECT"); - if (value & elf.DF_1_TRANS != 0) try writer.writeAll(" TRANS"); - if (value & elf.DF_1_INTERPOSE != 0) try writer.writeAll(" INTERPOSE"); - if (value & elf.DF_1_NODEFLIB != 0) try writer.writeAll(" NODEFLIB"); - if (value & elf.DF_1_NODUMP != 0) try writer.writeAll(" NODUMP"); - if (value & elf.DF_1_CONFALT != 0) try writer.writeAll(" CONFALT"); - if (value & elf.DF_1_ENDFILTEE != 0) try writer.writeAll(" ENDFILTEE"); - if (value & elf.DF_1_DISPRELDNE != 0) try writer.writeAll(" DISPRELDNE"); - if (value & elf.DF_1_DISPRELPND != 0) try writer.writeAll(" DISPRELPND"); - if (value & elf.DF_1_NODIRECT != 0) try writer.writeAll(" NODIRECT"); - if (value & elf.DF_1_IGNMULDEF != 0) try writer.writeAll(" IGNMULDEF"); - if (value & elf.DF_1_NOKSYMS != 0) try writer.writeAll(" NOKSYMS"); - if (value & elf.DF_1_NOHDR != 0) try writer.writeAll(" NOHDR"); - if (value & elf.DF_1_EDITED != 0) try writer.writeAll(" EDITED"); - if (value & elf.DF_1_NORELOC != 0) try writer.writeAll(" NORELOC"); - if (value & elf.DF_1_SYMINTPOSE != 0) try writer.writeAll(" SYMINTPOSE"); - if (value & elf.DF_1_GLOBAUDIT != 0) try writer.writeAll(" GLOBAUDIT"); - if (value & elf.DF_1_SINGLETON != 0) try writer.writeAll(" SINGLETON"); - if (value & elf.DF_1_STUB != 0) try writer.writeAll(" STUB"); - if (value & elf.DF_1_PIE != 0) try writer.writeAll(" PIE"); + if (value & elf.DF_1_NOW != 0) try bw.writeAll(" NOW"); + if (value & elf.DF_1_GLOBAL != 0) try bw.writeAll(" GLOBAL"); + if (value & elf.DF_1_GROUP != 0) try bw.writeAll(" GROUP"); + if (value & elf.DF_1_NODELETE != 0) try bw.writeAll(" NODELETE"); + if (value & elf.DF_1_LOADFLTR != 0) try bw.writeAll(" LOADFLTR"); + if (value & elf.DF_1_INITFIRST != 0) try bw.writeAll(" INITFIRST"); + if (value & elf.DF_1_NOOPEN != 0) try bw.writeAll(" NOOPEN"); + if (value & elf.DF_1_ORIGIN != 0) try bw.writeAll(" ORIGIN"); + if (value & elf.DF_1_DIRECT != 0) try bw.writeAll(" DIRECT"); + if (value & elf.DF_1_TRANS != 0) try bw.writeAll(" TRANS"); + if (value & elf.DF_1_INTERPOSE != 0) try bw.writeAll(" INTERPOSE"); + if (value & elf.DF_1_NODEFLIB != 0) try bw.writeAll(" NODEFLIB"); + if (value & elf.DF_1_NODUMP != 0) try bw.writeAll(" NODUMP"); + if (value & elf.DF_1_CONFALT != 0) try bw.writeAll(" CONFALT"); + if (value & elf.DF_1_ENDFILTEE != 0) try bw.writeAll(" ENDFILTEE"); + if (value & elf.DF_1_DISPRELDNE != 0) try bw.writeAll(" DISPRELDNE"); + if (value & elf.DF_1_DISPRELPND != 0) try bw.writeAll(" DISPRELPND"); + if (value & elf.DF_1_NODIRECT != 0) try bw.writeAll(" NODIRECT"); + if (value & elf.DF_1_IGNMULDEF != 0) try bw.writeAll(" IGNMULDEF"); + if (value & elf.DF_1_NOKSYMS != 0) try bw.writeAll(" NOKSYMS"); + if (value & elf.DF_1_NOHDR != 0) try bw.writeAll(" NOHDR"); + if (value & elf.DF_1_EDITED != 0) try bw.writeAll(" EDITED"); + if (value & elf.DF_1_NORELOC != 0) try bw.writeAll(" NORELOC"); + if (value & elf.DF_1_SYMINTPOSE != 0) try bw.writeAll(" SYMINTPOSE"); + if (value & elf.DF_1_GLOBAUDIT != 0) try bw.writeAll(" GLOBAUDIT"); + if (value & elf.DF_1_SINGLETON != 0) try bw.writeAll(" SINGLETON"); + if (value & elf.DF_1_STUB != 0) try bw.writeAll(" STUB"); + if (value & elf.DF_1_PIE != 0) try bw.writeAll(" PIE"); }, - else => try writer.print(" {x}", .{value}), + else => try bw.print(" {x}", .{value}), } - try writer.writeByte('\n'); + try bw.writeByte('\n'); } } - fn dumpSymtab(ctx: ObjectContext, comptime @"type": enum { symtab, dysymtab }, writer: anytype) !void { + fn dumpSymtab(ctx: ObjectContext, comptime @"type": enum { symtab, dysymtab }, bw: *Writer) !void { const symtab = switch (@"type") { .symtab => ctx.symtab, .dysymtab => ctx.dysymtab, }; - try writer.writeAll(switch (@"type") { + try bw.writeAll(switch (@"type") { .symtab => symtab_label, .dysymtab => dynamic_symtab_label, } ++ "\n"); for (symtab.symbols, 0..) |sym, index| { - try writer.print("{x} {x}", .{ sym.st_value, sym.st_size }); + try bw.print("{x} {x}", .{ sym.st_value, sym.st_size }); { if (elf.SHN_LORESERVE <= sym.st_shndx and sym.st_shndx < elf.SHN_HIRESERVE) { if (elf.SHN_LOPROC <= sym.st_shndx and sym.st_shndx < elf.SHN_HIPROC) { - try writer.print(" LO+{d}", .{sym.st_shndx - elf.SHN_LOPROC}); + try bw.print(" LO+{d}", .{sym.st_shndx - elf.SHN_LOPROC}); } else { const sym_ndx = switch (sym.st_shndx) { elf.SHN_ABS => "ABS", @@ -2224,12 +2157,12 @@ const ElfDumper = struct { elf.SHN_LIVEPATCH => "LIV", else => "UNK", }; - try writer.print(" {s}", .{sym_ndx}); + try bw.print(" {s}", .{sym_ndx}); } } else if (sym.st_shndx == elf.SHN_UNDEF) { - try writer.writeAll(" UND"); + try bw.writeAll(" UND"); } else { - try writer.print(" {x}", .{sym.st_shndx}); + try bw.print(" {x}", .{sym.st_shndx}); } } @@ -2246,12 +2179,12 @@ const ElfDumper = struct { elf.STT_NUM => "NUM", elf.STT_GNU_IFUNC => "IFUNC", else => if (elf.STT_LOPROC <= tt and tt < elf.STT_HIPROC) { - break :blk try writer.print(" LOPROC+{d}", .{tt - elf.STT_LOPROC}); + break :blk try bw.print(" LOPROC+{d}", .{tt - elf.STT_LOPROC}); } else if (elf.STT_LOOS <= tt and tt < elf.STT_HIOS) { - break :blk try writer.print(" LOOS+{d}", .{tt - elf.STT_LOOS}); + break :blk try bw.print(" LOOS+{d}", .{tt - elf.STT_LOOS}); } else "UNK", }; - try writer.print(" {s}", .{sym_type}); + try bw.print(" {s}", .{sym_type}); } blk: { @@ -2262,28 +2195,28 @@ const ElfDumper = struct { elf.STB_WEAK => "WEAK", elf.STB_NUM => "NUM", else => if (elf.STB_LOPROC <= bind and bind < elf.STB_HIPROC) { - break :blk try writer.print(" LOPROC+{d}", .{bind - elf.STB_LOPROC}); + break :blk try bw.print(" LOPROC+{d}", .{bind - elf.STB_LOPROC}); } else if (elf.STB_LOOS <= bind and bind < elf.STB_HIOS) { - break :blk try writer.print(" LOOS+{d}", .{bind - elf.STB_LOOS}); + break :blk try bw.print(" LOOS+{d}", .{bind - elf.STB_LOOS}); } else "UNKNOWN", }; - try writer.print(" {s}", .{sym_bind}); + try bw.print(" {s}", .{sym_bind}); } const sym_vis = @as(elf.STV, @enumFromInt(@as(u2, @truncate(sym.st_other)))); - try writer.print(" {s}", .{@tagName(sym_vis)}); + try bw.print(" {s}", .{@tagName(sym_vis)}); const sym_name = switch (sym.st_type()) { elf.STT_SECTION => ctx.getSectionName(sym.st_shndx), else => symtab.getName(index).?, }; - try writer.print(" {s}\n", .{sym_name}); + try bw.print(" {s}\n", .{sym_name}); } } - fn dumpSection(ctx: ObjectContext, shndx: usize, writer: anytype) !void { + fn dumpSection(ctx: ObjectContext, shndx: usize, bw: *Writer) !void { const data = ctx.getSectionContents(shndx); - try writer.print("{s}", .{data}); + try bw.print("{s}", .{data}); } inline fn getSectionName(ctx: ObjectContext, shndx: usize) []const u8 { @@ -2321,22 +2254,15 @@ const ElfDumper = struct { }; fn getString(strtab: []const u8, off: u32) []const u8 { - assert(off < strtab.len); - return mem.sliceTo(@as([*:0]const u8, @ptrCast(strtab.ptr + off)), 0); + const str = strtab[off..]; + return str[0..std.mem.indexOfScalar(u8, str, 0).?]; } - fn fmtShType(sh_type: u32) std.fmt.Formatter(formatShType) { + fn fmtShType(sh_type: u32) std.fmt.Formatter(u32, formatShType) { return .{ .data = sh_type }; } - fn formatShType( - sh_type: u32, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = unused_fmt_string; - _ = options; + fn formatShType(sh_type: u32, w: *Writer) Writer.Error!void { const name = switch (sh_type) { elf.SHT_NULL => "NULL", elf.SHT_PROGBITS => "PROGBITS", @@ -2362,28 +2288,21 @@ const ElfDumper = struct { elf.SHT_GNU_VERNEED => "VERNEED", elf.SHT_GNU_VERSYM => "VERSYM", else => if (elf.SHT_LOOS <= sh_type and sh_type < elf.SHT_HIOS) { - return try writer.print("LOOS+0x{x}", .{sh_type - elf.SHT_LOOS}); + return try w.print("LOOS+0x{x}", .{sh_type - elf.SHT_LOOS}); } else if (elf.SHT_LOPROC <= sh_type and sh_type < elf.SHT_HIPROC) { - return try writer.print("LOPROC+0x{x}", .{sh_type - elf.SHT_LOPROC}); + return try w.print("LOPROC+0x{x}", .{sh_type - elf.SHT_LOPROC}); } else if (elf.SHT_LOUSER <= sh_type and sh_type < elf.SHT_HIUSER) { - return try writer.print("LOUSER+0x{x}", .{sh_type - elf.SHT_LOUSER}); + return try w.print("LOUSER+0x{x}", .{sh_type - elf.SHT_LOUSER}); } else "UNKNOWN", }; - try writer.writeAll(name); + try w.writeAll(name); } - fn fmtPhType(ph_type: u32) std.fmt.Formatter(formatPhType) { + fn fmtPhType(ph_type: u32) std.fmt.Formatter(u32, formatPhType) { return .{ .data = ph_type }; } - fn formatPhType( - ph_type: u32, - comptime unused_fmt_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = unused_fmt_string; - _ = options; + fn formatPhType(ph_type: u32, w: *Writer) Writer.Error!void { const p_type = switch (ph_type) { elf.PT_NULL => "NULL", elf.PT_LOAD => "LOAD", @@ -2398,12 +2317,12 @@ const ElfDumper = struct { elf.PT_GNU_STACK => "GNU_STACK", elf.PT_GNU_RELRO => "GNU_RELRO", else => if (elf.PT_LOOS <= ph_type and ph_type < elf.PT_HIOS) { - return try writer.print("LOOS+0x{x}", .{ph_type - elf.PT_LOOS}); + return try w.print("LOOS+0x{x}", .{ph_type - elf.PT_LOOS}); } else if (elf.PT_LOPROC <= ph_type and ph_type < elf.PT_HIPROC) { - return try writer.print("LOPROC+0x{x}", .{ph_type - elf.PT_LOPROC}); + return try w.print("LOPROC+0x{x}", .{ph_type - elf.PT_LOPROC}); } else "UNKNOWN", }; - try writer.writeAll(p_type); + try w.writeAll(p_type); } }; @@ -2412,49 +2331,39 @@ const WasmDumper = struct { fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; - var fbs = std.io.fixedBufferStream(bytes); - const reader = fbs.reader(); + var br: std.io.Reader = .fixed(bytes); - const buf = try reader.readBytesNoEof(8); - if (!mem.eql(u8, buf[0..4], &std.wasm.magic)) { - return error.InvalidMagicByte; - } - if (!mem.eql(u8, buf[4..], &std.wasm.version)) { - return error.UnsupportedWasmVersion; - } + const buf = try br.takeArray(8); + if (!mem.eql(u8, buf[0..4], &std.wasm.magic)) return error.InvalidMagicByte; + if (!mem.eql(u8, buf[4..8], &std.wasm.version)) return error.UnsupportedWasmVersion; + + var aw: std.io.Writer.Allocating = .init(gpa); + defer aw.deinit(); + const bw = &aw.interface; - var output = std.ArrayList(u8).init(gpa); - defer output.deinit(); - parseAndDumpInner(step, check, bytes, &fbs, &output) catch |err| switch (err) { - error.EndOfStream => try output.appendSlice("\n"), + parseAndDumpInner(step, check, &br, bw) catch |err| switch (err) { + error.EndOfStream => try bw.writeAll("\n"), else => |e| return e, }; - return output.toOwnedSlice(); + return aw.toOwnedSlice(); } fn parseAndDumpInner( step: *Step, check: Check, - bytes: []const u8, - fbs: *std.io.FixedBufferStream([]const u8), - output: *std.ArrayList(u8), + br: *std.io.Reader, + bw: *Writer, ) !void { - const reader = fbs.reader(); - const writer = output.writer(); - + var section_br: std.io.Reader = undefined; switch (check.kind) { - .headers => { - while (reader.readByte()) |current_byte| { - const section = std.enums.fromInt(std.wasm.Section, current_byte) orelse { - return step.fail("Found invalid section id '{d}'", .{current_byte}); - }; - - const section_length = try std.leb.readUleb128(u32, reader); - try parseAndDumpSection(step, section, bytes[fbs.pos..][0..section_length], writer); - fbs.pos += section_length; - } else |_| {} // reached end of stream + .headers => while (br.takeEnum(std.wasm.Section, .little)) |section| { + section_br = .fixed(try br.take(try br.takeLeb128(u32))); + try parseAndDumpSection(step, section, §ion_br, bw); + } else |err| switch (err) { + error.InvalidEnumTag => return step.fail("invalid section id", .{}), + error.EndOfStream => {}, + else => |e| return e, }, - else => return step.fail("invalid check kind for Wasm file format: {s}", .{@tagName(check.kind)}), } } @@ -2462,16 +2371,13 @@ const WasmDumper = struct { fn parseAndDumpSection( step: *Step, section: std.wasm.Section, - data: []const u8, - writer: anytype, + br: *std.io.Reader, + bw: *Writer, ) !void { - var fbs = std.io.fixedBufferStream(data); - const reader = fbs.reader(); - - try writer.print( + try bw.print( \\Section {s} \\size {d} - , .{ @tagName(section), data.len }); + , .{ @tagName(section), br.buffer.len }); switch (section) { .type, @@ -2485,96 +2391,83 @@ const WasmDumper = struct { .code, .data, => { - const entries = try std.leb.readUleb128(u32, reader); - try writer.print("\nentries {d}\n", .{entries}); - try parseSection(step, section, data[fbs.pos..], entries, writer); + const entries = try br.takeLeb128(u32); + try bw.print("\nentries {d}\n", .{entries}); + try parseSection(step, section, br, entries, bw); }, .custom => { - const name_length = try std.leb.readUleb128(u32, reader); - const name = data[fbs.pos..][0..name_length]; - fbs.pos += name_length; - try writer.print("\nname {s}\n", .{name}); + const name = try br.take(try br.takeLeb128(u32)); + try bw.print("\nname {s}\n", .{name}); if (mem.eql(u8, name, "name")) { - try parseDumpNames(step, reader, writer, data); + try parseDumpNames(step, br, bw); } else if (mem.eql(u8, name, "producers")) { - try parseDumpProducers(reader, writer, data); + try parseDumpProducers(br, bw); } else if (mem.eql(u8, name, "target_features")) { - try parseDumpFeatures(reader, writer, data); + try parseDumpFeatures(br, bw); } // TODO: Implement parsing and dumping other custom sections (such as relocations) }, .start => { - const start = try std.leb.readUleb128(u32, reader); - try writer.print("\nstart {d}\n", .{start}); + const start = try br.takeLeb128(u32); + try bw.print("\nstart {d}\n", .{start}); }, .data_count => { - const count = try std.leb.readUleb128(u32, reader); - try writer.print("\ncount {d}\n", .{count}); + const count = try br.takeLeb128(u32); + try bw.print("\ncount {d}\n", .{count}); }, else => {}, // skip unknown sections } } - fn parseSection(step: *Step, section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { - var fbs = std.io.fixedBufferStream(data); - const reader = fbs.reader(); - + fn parseSection(step: *Step, section: std.wasm.Section, br: *std.io.Reader, entries: u32, bw: *Writer) !void { switch (section) { .type => { var i: u32 = 0; while (i < entries) : (i += 1) { - const func_type = try reader.readByte(); + const func_type = try br.takeByte(); if (func_type != std.wasm.function_type) { return step.fail("expected function type, found byte '{d}'", .{func_type}); } - const params = try std.leb.readUleb128(u32, reader); - try writer.print("params {d}\n", .{params}); + const params = try br.takeLeb128(u32); + try bw.print("params {d}\n", .{params}); var index: u32 = 0; while (index < params) : (index += 1) { - _ = try parseDumpType(step, std.wasm.Valtype, reader, writer); + _ = try parseDumpType(step, std.wasm.Valtype, br, bw); } else index = 0; - const returns = try std.leb.readUleb128(u32, reader); - try writer.print("returns {d}\n", .{returns}); + const returns = try br.takeLeb128(u32); + try bw.print("returns {d}\n", .{returns}); while (index < returns) : (index += 1) { - _ = try parseDumpType(step, std.wasm.Valtype, reader, writer); + _ = try parseDumpType(step, std.wasm.Valtype, br, bw); } } }, .import => { var i: u32 = 0; while (i < entries) : (i += 1) { - const module_name_len = try std.leb.readUleb128(u32, reader); - const module_name = data[fbs.pos..][0..module_name_len]; - fbs.pos += module_name_len; - const name_len = try std.leb.readUleb128(u32, reader); - const name = data[fbs.pos..][0..name_len]; - fbs.pos += name_len; - - const kind = std.enums.fromInt(std.wasm.ExternalKind, try reader.readByte()) orelse { - return step.fail("invalid import kind", .{}); + const module_name = try br.take(try br.takeLeb128(u32)); + const name = try br.take(try br.takeLeb128(u32)); + const kind = br.takeEnum(std.wasm.ExternalKind, .little) catch |err| switch (err) { + error.InvalidEnumTag => return step.fail("invalid import kind", .{}), + else => |e| return e, }; - try writer.print( + try bw.print( \\module {s} \\name {s} \\kind {s} , .{ module_name, name, @tagName(kind) }); - try writer.writeByte('\n'); + try bw.writeByte('\n'); switch (kind) { - .function => { - try writer.print("index {d}\n", .{try std.leb.readUleb128(u32, reader)}); - }, - .memory => { - try parseDumpLimits(reader, writer); - }, + .function => try bw.print("index {d}\n", .{try br.takeLeb128(u32)}), + .memory => try parseDumpLimits(br, bw), .global => { - _ = try parseDumpType(step, std.wasm.Valtype, reader, writer); - try writer.print("mutable {}\n", .{0x01 == try std.leb.readUleb128(u32, reader)}); + _ = try parseDumpType(step, std.wasm.Valtype, br, bw); + try bw.print("mutable {}\n", .{0x01 == try br.takeLeb128(u32)}); }, .table => { - _ = try parseDumpType(step, std.wasm.RefType, reader, writer); - try parseDumpLimits(reader, writer); + _ = try parseDumpType(step, std.wasm.RefType, br, bw); + try parseDumpLimits(br, bw); }, } } @@ -2582,60 +2475,58 @@ const WasmDumper = struct { .function => { var i: u32 = 0; while (i < entries) : (i += 1) { - try writer.print("index {d}\n", .{try std.leb.readUleb128(u32, reader)}); + try bw.print("index {d}\n", .{try br.takeLeb128(u32)}); } }, .table => { var i: u32 = 0; while (i < entries) : (i += 1) { - _ = try parseDumpType(step, std.wasm.RefType, reader, writer); - try parseDumpLimits(reader, writer); + _ = try parseDumpType(step, std.wasm.RefType, br, bw); + try parseDumpLimits(br, bw); } }, .memory => { var i: u32 = 0; while (i < entries) : (i += 1) { - try parseDumpLimits(reader, writer); + try parseDumpLimits(br, bw); } }, .global => { var i: u32 = 0; while (i < entries) : (i += 1) { - _ = try parseDumpType(step, std.wasm.Valtype, reader, writer); - try writer.print("mutable {}\n", .{0x01 == try std.leb.readUleb128(u1, reader)}); - try parseDumpInit(step, reader, writer); + _ = try parseDumpType(step, std.wasm.Valtype, br, bw); + try bw.print("mutable {}\n", .{0x01 == try br.takeLeb128(u1)}); + try parseDumpInit(step, br, bw); } }, .@"export" => { var i: u32 = 0; while (i < entries) : (i += 1) { - const name_len = try std.leb.readUleb128(u32, reader); - const name = data[fbs.pos..][0..name_len]; - fbs.pos += name_len; - const kind_byte = try std.leb.readUleb128(u8, reader); - const kind = std.enums.fromInt(std.wasm.ExternalKind, kind_byte) orelse { - return step.fail("invalid export kind value '{d}'", .{kind_byte}); + const name = try br.take(try br.takeLeb128(u32)); + const kind = br.takeEnum(std.wasm.ExternalKind, .little) catch |err| switch (err) { + error.InvalidEnumTag => return step.fail("invalid export kind value", .{}), + else => |e| return e, }; - const index = try std.leb.readUleb128(u32, reader); - try writer.print( + const index = try br.takeLeb128(u32); + try bw.print( \\name {s} \\kind {s} \\index {d} , .{ name, @tagName(kind), index }); - try writer.writeByte('\n'); + try bw.writeByte('\n'); } }, .element => { var i: u32 = 0; while (i < entries) : (i += 1) { - try writer.print("table index {d}\n", .{try std.leb.readUleb128(u32, reader)}); - try parseDumpInit(step, reader, writer); + try bw.print("table index {d}\n", .{try br.takeLeb128(u32)}); + try parseDumpInit(step, br, bw); - const function_indexes = try std.leb.readUleb128(u32, reader); + const function_indexes = try br.takeLeb128(u32); var function_index: u32 = 0; - try writer.print("indexes {d}\n", .{function_indexes}); + try bw.print("indexes {d}\n", .{function_indexes}); while (function_index < function_indexes) : (function_index += 1) { - try writer.print("index {d}\n", .{try std.leb.readUleb128(u32, reader)}); + try bw.print("index {d}\n", .{try br.takeLeb128(u32)}); } } }, @@ -2643,101 +2534,95 @@ const WasmDumper = struct { .data => { var i: u32 = 0; while (i < entries) : (i += 1) { - const flags = try std.leb.readUleb128(u32, reader); - const index = if (flags & 0x02 != 0) - try std.leb.readUleb128(u32, reader) - else - 0; - try writer.print("memory index 0x{x}\n", .{index}); - if (flags == 0) { - try parseDumpInit(step, reader, writer); - } - - const size = try std.leb.readUleb128(u32, reader); - try writer.print("size {d}\n", .{size}); - try reader.skipBytes(size, .{}); // we do not care about the content of the segments + const flags: packed struct(u32) { + passive: bool, + memidx: bool, + unused: u30, + } = @bitCast(try br.takeLeb128(u32)); + const index = if (flags.memidx) try br.takeLeb128(u32) else 0; + try bw.print("memory index 0x{x}\n", .{index}); + if (!flags.passive) try parseDumpInit(step, br, bw); + const size = try br.takeLeb128(u32); + try bw.print("size {d}\n", .{size}); + _ = try br.discard(.limited(size)); // we do not care about the content of the segments } }, else => unreachable, } } - fn parseDumpType(step: *Step, comptime E: type, reader: anytype, writer: anytype) !E { - const byte = try reader.readByte(); - const tag = std.enums.fromInt(E, byte) orelse { - return step.fail("invalid wasm type value '{d}'", .{byte}); + fn parseDumpType(step: *Step, comptime E: type, br: *std.io.Reader, bw: *Writer) !E { + const tag = br.takeEnum(E, .little) catch |err| switch (err) { + error.InvalidEnumTag => return step.fail("invalid wasm type value", .{}), + else => |e| return e, }; - try writer.print("type {s}\n", .{@tagName(tag)}); + try bw.print("type {s}\n", .{@tagName(tag)}); return tag; } - fn parseDumpLimits(reader: anytype, writer: anytype) !void { - const flags = try std.leb.readUleb128(u8, reader); - const min = try std.leb.readUleb128(u32, reader); + fn parseDumpLimits(br: *std.io.Reader, bw: *Writer) !void { + const flags = try br.takeLeb128(u8); + const min = try br.takeLeb128(u32); - try writer.print("min {x}\n", .{min}); - if (flags != 0) { - try writer.print("max {x}\n", .{try std.leb.readUleb128(u32, reader)}); - } + try bw.print("min {x}\n", .{min}); + if (flags != 0) try bw.print("max {x}\n", .{try br.takeLeb128(u32)}); } - fn parseDumpInit(step: *Step, reader: anytype, writer: anytype) !void { - const byte = try reader.readByte(); - const opcode = std.enums.fromInt(std.wasm.Opcode, byte) orelse { - return step.fail("invalid wasm opcode '{d}'", .{byte}); + fn parseDumpInit(step: *Step, br: *std.io.Reader, bw: *Writer) !void { + const opcode = br.takeEnum(std.wasm.Opcode, .little) catch |err| switch (err) { + error.InvalidEnumTag => return step.fail("invalid wasm opcode", .{}), + else => |e| return e, }; switch (opcode) { - .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readIleb128(i32, reader)}), - .i64_const => try writer.print("i64.const {x}\n", .{try std.leb.readIleb128(i64, reader)}), - .f32_const => try writer.print("f32.const {x}\n", .{@as(f32, @bitCast(try reader.readInt(u32, .little)))}), - .f64_const => try writer.print("f64.const {x}\n", .{@as(f64, @bitCast(try reader.readInt(u64, .little)))}), - .global_get => try writer.print("global.get {x}\n", .{try std.leb.readUleb128(u32, reader)}), + .i32_const => try bw.print("i32.const {x}\n", .{try br.takeLeb128(i32)}), + .i64_const => try bw.print("i64.const {x}\n", .{try br.takeLeb128(i64)}), + .f32_const => try bw.print("f32.const {x}\n", .{@as(f32, @bitCast(try br.takeInt(u32, .little)))}), + .f64_const => try bw.print("f64.const {x}\n", .{@as(f64, @bitCast(try br.takeInt(u64, .little)))}), + .global_get => try bw.print("global.get {x}\n", .{try br.takeLeb128(u32)}), else => unreachable, } - const end_opcode = try std.leb.readUleb128(u8, reader); + const end_opcode = try br.takeLeb128(u8); if (end_opcode != @intFromEnum(std.wasm.Opcode.end)) { return step.fail("expected 'end' opcode in init expression", .{}); } } /// https://webassembly.github.io/spec/core/appendix/custom.html - fn parseDumpNames(step: *Step, reader: anytype, writer: anytype, data: []const u8) !void { - while (reader.context.pos < data.len) { - switch (try parseDumpType(step, std.wasm.NameSubsection, reader, writer)) { + fn parseDumpNames(step: *Step, br: *std.io.Reader, bw: *Writer) !void { + var subsection_br: std.io.Reader = undefined; + while (br.seek < br.buffer.len) { + switch (try parseDumpType(step, std.wasm.NameSubsection, br, bw)) { // The module name subsection ... consists of a single name // that is assigned to the module itself. .module => { - const size = try std.leb.readUleb128(u32, reader); - const name_len = try std.leb.readUleb128(u32, reader); - if (size != name_len + 1) return error.BadSubsectionSize; - if (reader.context.pos + name_len > data.len) return error.UnexpectedEndOfStream; - try writer.print("name {s}\n", .{data[reader.context.pos..][0..name_len]}); - reader.context.pos += name_len; + subsection_br = .fixed(try br.take(try br.takeLeb128(u32))); + const name = try subsection_br.take(try subsection_br.takeLeb128(u32)); + try bw.print( + \\name {s} + \\ + , .{name}); + if (subsection_br.seek != subsection_br.buffer.len) return error.BadSubsectionSize; }, // The function name subsection ... consists of a name map // assigning function names to function indices. .function, .global, .data_segment => { - const size = try std.leb.readUleb128(u32, reader); - const entries = try std.leb.readUleb128(u32, reader); - try writer.print( - \\size {d} + subsection_br = .fixed(try br.take(try br.takeLeb128(u32))); + const entries = try br.takeLeb128(u32); + try bw.print( \\names {d} \\ - , .{ size, entries }); + , .{entries}); for (0..entries) |_| { - const index = try std.leb.readUleb128(u32, reader); - const name_len = try std.leb.readUleb128(u32, reader); - if (reader.context.pos + name_len > data.len) return error.UnexpectedEndOfStream; - const name = data[reader.context.pos..][0..name_len]; - reader.context.pos += name.len; - - try writer.print( + const index = try br.takeLeb128(u32); + const name = try br.take(try br.takeLeb128(u32)); + try bw.print( \\index {d} \\name {s} \\ , .{ index, name }); } + if (subsection_br.seek != subsection_br.buffer.len) return error.BadSubsectionSize; }, // The local name subsection ... consists of an indirect name @@ -2752,52 +2637,49 @@ const WasmDumper = struct { } } - fn parseDumpProducers(reader: anytype, writer: anytype, data: []const u8) !void { - const field_count = try std.leb.readUleb128(u32, reader); - try writer.print("fields {d}\n", .{field_count}); + fn parseDumpProducers(br: *std.io.Reader, bw: *Writer) !void { + const field_count = try br.takeLeb128(u32); + try bw.print( + \\fields {d} + \\ + , .{field_count}); var current_field: u32 = 0; while (current_field < field_count) : (current_field += 1) { - const field_name_length = try std.leb.readUleb128(u32, reader); - const field_name = data[reader.context.pos..][0..field_name_length]; - reader.context.pos += field_name_length; - - const value_count = try std.leb.readUleb128(u32, reader); - try writer.print( + const field_name = try br.take(try br.takeLeb128(u32)); + const value_count = try br.takeLeb128(u32); + try bw.print( \\field_name {s} \\values {d} + \\ , .{ field_name, value_count }); - try writer.writeByte('\n'); var current_value: u32 = 0; while (current_value < value_count) : (current_value += 1) { - const value_length = try std.leb.readUleb128(u32, reader); - const value = data[reader.context.pos..][0..value_length]; - reader.context.pos += value_length; - - const version_length = try std.leb.readUleb128(u32, reader); - const version = data[reader.context.pos..][0..version_length]; - reader.context.pos += version_length; - - try writer.print( + const value = try br.take(try br.takeLeb128(u32)); + const version = try br.take(try br.takeLeb128(u32)); + try bw.print( \\value_name {s} \\version {s} + \\ , .{ value, version }); - try writer.writeByte('\n'); } } } - fn parseDumpFeatures(reader: anytype, writer: anytype, data: []const u8) !void { - const feature_count = try std.leb.readUleb128(u32, reader); - try writer.print("features {d}\n", .{feature_count}); + fn parseDumpFeatures(br: *std.io.Reader, bw: *Writer) !void { + const feature_count = try br.takeLeb128(u32); + try bw.print( + \\features {d} + \\ + , .{feature_count}); var index: u32 = 0; while (index < feature_count) : (index += 1) { - const prefix_byte = try std.leb.readUleb128(u8, reader); - const name_length = try std.leb.readUleb128(u32, reader); - const feature_name = data[reader.context.pos..][0..name_length]; - reader.context.pos += name_length; - - try writer.print("{c} {s}\n", .{ prefix_byte, feature_name }); + const prefix_byte = try br.takeLeb128(u8); + const feature_name = try br.take(try br.takeLeb128(u32)); + try bw.print( + \\{c} {s} + \\ + , .{ prefix_byte, feature_name }); } } }; -- cgit v1.2.3 From d6ac04c47811f162ca662f43d3c93a5dc0dcce86 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 1 Jul 2025 08:44:26 -0700 Subject: std.Build.Step.CheckObject: fix the TODO --- lib/std/Build/Step/CheckObject.zig | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'lib/std/Build/Step/CheckObject.zig') diff --git a/lib/std/Build/Step/CheckObject.zig b/lib/std/Build/Step/CheckObject.zig index 96fa443b1e..cc576e6410 100644 --- a/lib/std/Build/Step/CheckObject.zig +++ b/lib/std/Build/Step/CheckObject.zig @@ -1352,7 +1352,7 @@ const MachODumper = struct { var offset: u64 = 0; var addend: i64 = 0; - var name_buf: std.ArrayList(u8) = .init(ctx.gpa); + var name_buf: std.io.Writer.Allocating = .init(ctx.gpa); defer name_buf.deinit(); while (br.takeByte()) |byte| { @@ -1380,9 +1380,8 @@ const MachODumper = struct { }, macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => { name_buf.clearRetainingCapacity(); - if (true) @panic("TODO fix this"); - //try reader.readUntilDelimiterArrayList(&name_buf, 0, std.math.maxInt(u32)); - try name_buf.append(0); + try br.streamDelimiterLimit(&name_buf, 0, .limited(std.math.maxInt(u32))); + try name_buf.writeByte(0); }, macho.BIND_OPCODE_SET_ADDEND_SLEB => { addend = try br.takeLeb128(i64); @@ -1424,7 +1423,7 @@ const MachODumper = struct { .addend = addend, .tag = tag, .ordinal = ordinal, - .name = try ctx.gpa.dupe(u8, name_buf.items), + .name = try ctx.gpa.dupe(u8, name_buf.getWritten()), }); offset += skip + @sizeOf(u64) + add_addr; } -- cgit v1.2.3 From fac5fe57beb3ee34d5687da4258be22f0ed2f2f3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 1 Jul 2025 09:41:41 -0700 Subject: std.io.Writer.Allocating: rename interface to writer --- lib/std/Build/Step/CheckObject.zig | 12 ++++++------ lib/std/Build/Step/ConfigHeader.zig | 12 ++++++------ lib/std/array_list.zig | 2 +- lib/std/debug.zig | 4 ++-- lib/std/fmt.zig | 4 ++-- lib/std/io/Writer.zig | 32 ++++++++++++++++---------------- lib/std/zig/llvm/Builder.zig | 2 +- src/Sema.zig | 6 +++--- 8 files changed, 37 insertions(+), 37 deletions(-) (limited to 'lib/std/Build/Step/CheckObject.zig') diff --git a/lib/std/Build/Step/CheckObject.zig b/lib/std/Build/Step/CheckObject.zig index cc576e6410..ba51632c67 100644 --- a/lib/std/Build/Step/CheckObject.zig +++ b/lib/std/Build/Step/CheckObject.zig @@ -1380,8 +1380,8 @@ const MachODumper = struct { }, macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => { name_buf.clearRetainingCapacity(); - try br.streamDelimiterLimit(&name_buf, 0, .limited(std.math.maxInt(u32))); - try name_buf.writeByte(0); + _ = try br.streamDelimiterLimit(&name_buf.writer, 0, .limited(std.math.maxInt(u32))); + try name_buf.writer.writeByte(0); }, macho.BIND_OPCODE_SET_ADDEND_SLEB => { addend = try br.takeLeb128(i64); @@ -1588,7 +1588,7 @@ const MachODumper = struct { var aw: std.io.Writer.Allocating = .init(gpa); defer aw.deinit(); - const bw = &aw.interface; + const bw = &aw.writer; switch (check.kind) { .headers => { @@ -1741,7 +1741,7 @@ const ElfDumper = struct { var aw: std.io.Writer.Allocating = .init(gpa); defer aw.deinit(); - const bw = &aw.interface; + const bw = &aw.writer; switch (check.kind) { .archive_symtab => if (ctx.symtab.len > 0) { @@ -1888,7 +1888,7 @@ const ElfDumper = struct { var aw: std.io.Writer.Allocating = .init(gpa); defer aw.deinit(); - const bw = &aw.interface; + const bw = &aw.writer; switch (check.kind) { .headers => { @@ -2338,7 +2338,7 @@ const WasmDumper = struct { var aw: std.io.Writer.Allocating = .init(gpa); defer aw.deinit(); - const bw = &aw.interface; + const bw = &aw.writer; parseAndDumpInner(step, check, &br, bw) catch |err| switch (err) { error.EndOfStream => try bw.writeAll("\n"), diff --git a/lib/std/Build/Step/ConfigHeader.zig b/lib/std/Build/Step/ConfigHeader.zig index d8a7a08b33..ea7da12ff6 100644 --- a/lib/std/Build/Step/ConfigHeader.zig +++ b/lib/std/Build/Step/ConfigHeader.zig @@ -198,7 +198,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { var aw: std.io.Writer.Allocating = .init(gpa); defer aw.deinit(); - const bw = &aw.interface; + const bw = &aw.writer; const header_text = "This file was generated by ConfigHeader using the Zig Build System."; const c_generated_line = "/* " ++ header_text ++ " */\n"; @@ -335,7 +335,7 @@ fn render_autoconf_at( ) !void { const build = step.owner; const allocator = build.allocator; - const bw = &aw.interface; + const bw = &aw.writer; const used = allocator.alloc(bool, values.count()) catch @panic("OOM"); for (used) |*u| u.* = false; @@ -753,17 +753,17 @@ fn testReplaceVariablesAutoconfAt( expected: []const u8, values: std.StringArrayHashMap(Value), ) !void { - var output: std.io.Writer.Allocating = .init(allocator); - defer output.deinit(); + var aw: std.io.Writer.Allocating = .init(allocator); + defer aw.deinit(); const used = try allocator.alloc(bool, values.count()); for (used) |*u| u.* = false; defer allocator.free(used); - try expand_variables_autoconf_at(&output.interface, contents, values, used); + try expand_variables_autoconf_at(&aw.writer, contents, values, used); for (used) |u| if (!u) return error.UnusedValue; - try std.testing.expectEqualStrings(expected, output.getWritten()); + try std.testing.expectEqualStrings(expected, aw.getWritten()); } fn testReplaceVariablesCMake( diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig index f1a6ec5041..c3fade794f 100644 --- a/lib/std/array_list.zig +++ b/lib/std/array_list.zig @@ -941,7 +941,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig try self.ensureUnusedCapacity(gpa, fmt.len); var aw: std.io.Writer.Allocating = .fromArrayList(gpa, self); defer self.* = aw.toArrayList(); - return aw.interface.print(fmt, args) catch |err| switch (err) { + return aw.writer.print(fmt, args) catch |err| switch (err) { error.WriteFailed => return error.OutOfMemory, }; } diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 21e985d98b..4e1bbfcbd1 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -307,7 +307,7 @@ test dumpHexFallible { var aw: std.io.Writer.Allocating = .init(std.testing.allocator); defer aw.deinit(); - try dumpHexFallible(&aw.interface, .no_color, bytes); + try dumpHexFallible(&aw.writer, .no_color, bytes); const expected = try std.fmt.allocPrint(std.testing.allocator, \\{x:0>[2]} 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF .."3DUfw........ \\{x:0>[2]} 01 12 13 ... @@ -1230,7 +1230,7 @@ fn printLineFromFileAnyOs(writer: *Writer, source_location: SourceLocation) !voi test printLineFromFileAnyOs { var aw: Writer.Allocating = .init(std.testing.allocator); defer aw.deinit(); - const output_stream = &aw.interface; + const output_stream = &aw.writer; const allocator = std.testing.allocator; const join = std.fs.path.join; diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index b4e7dff271..b17c13d938 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -759,7 +759,7 @@ pub fn count(comptime fmt: []const u8, args: anytype) usize { pub fn allocPrint(gpa: Allocator, comptime fmt: []const u8, args: anytype) Allocator.Error![]u8 { var aw = try Writer.Allocating.initCapacity(gpa, fmt.len); defer aw.deinit(); - aw.interface.print(fmt, args) catch |err| switch (err) { + aw.writer.print(fmt, args) catch |err| switch (err) { error.WriteFailed => return error.OutOfMemory, }; return aw.toOwnedSlice(); @@ -773,7 +773,7 @@ pub fn allocPrintSentinel( ) Allocator.Error![:sentinel]u8 { var aw = try Writer.Allocating.initCapacity(gpa, fmt.len); defer aw.deinit(); - aw.interface.print(fmt, args) catch |err| switch (err) { + aw.writer.print(fmt, args) catch |err| switch (err) { error.WriteFailed => return error.OutOfMemory, }; return aw.toOwnedSliceSentinel(sentinel); diff --git a/lib/std/io/Writer.zig b/lib/std/io/Writer.zig index 1a18b46236..01832325e9 100644 --- a/lib/std/io/Writer.zig +++ b/lib/std/io/Writer.zig @@ -2056,12 +2056,12 @@ pub fn Hashed(comptime Hasher: type) type { /// When using this API, it is not necessary to call `flush`. pub const Allocating = struct { allocator: Allocator, - interface: Writer, + writer: Writer, pub fn init(allocator: Allocator) Allocating { return .{ .allocator = allocator, - .interface = .{ + .writer = .{ .buffer = &.{}, .vtable = &vtable, }, @@ -2071,7 +2071,7 @@ pub const Allocating = struct { pub fn initCapacity(allocator: Allocator, capacity: usize) error{OutOfMemory}!Allocating { return .{ .allocator = allocator, - .interface = .{ + .writer = .{ .buffer = try allocator.alloc(u8, capacity), .vtable = &vtable, }, @@ -2081,7 +2081,7 @@ pub const Allocating = struct { pub fn initOwnedSlice(allocator: Allocator, slice: []u8) Allocating { return .{ .allocator = allocator, - .interface = .{ + .writer = .{ .buffer = slice, .vtable = &vtable, }, @@ -2093,7 +2093,7 @@ pub const Allocating = struct { defer array_list.* = .empty; return .{ .allocator = allocator, - .interface = .{ + .writer = .{ .vtable = &vtable, .buffer = array_list.allocatedSlice(), .end = array_list.items.len, @@ -2108,14 +2108,14 @@ pub const Allocating = struct { }; pub fn deinit(a: *Allocating) void { - a.allocator.free(a.interface.buffer); + a.allocator.free(a.writer.buffer); a.* = undefined; } /// Returns an array list that takes ownership of the allocated memory. /// Resets the `Allocating` to an empty state. pub fn toArrayList(a: *Allocating) std.ArrayListUnmanaged(u8) { - const w = &a.interface; + const w = &a.writer; const result: std.ArrayListUnmanaged(u8) = .{ .items = w.buffer[0..w.end], .capacity = w.buffer.len, @@ -2137,13 +2137,13 @@ pub const Allocating = struct { } pub fn getWritten(a: *Allocating) []u8 { - return a.interface.buffered(); + return a.writer.buffered(); } pub fn shrinkRetainingCapacity(a: *Allocating, new_len: usize) void { - const shrink_by = a.interface.end - new_len; - a.interface.end = new_len; - a.interface.count -= shrink_by; + const shrink_by = a.writer.end - new_len; + a.writer.end = new_len; + a.writer.count -= shrink_by; } pub fn clearRetainingCapacity(a: *Allocating) void { @@ -2151,7 +2151,7 @@ pub const Allocating = struct { } fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize { - const a: *Allocating = @fieldParentPtr("interface", w); + const a: *Allocating = @fieldParentPtr("writer", w); const gpa = a.allocator; const pattern = data[data.len - 1]; const splat_len = pattern.len * splat; @@ -2177,7 +2177,7 @@ pub const Allocating = struct { fn sendFile(w: *Writer, file_reader: *File.Reader, limit: std.io.Limit) FileError!usize { if (File.Handle == void) return error.Unimplemented; - const a: *Allocating = @fieldParentPtr("interface", w); + const a: *Allocating = @fieldParentPtr("writer", w); const gpa = a.allocator; var list = a.toArrayList(); defer setArrayList(a, list); @@ -2194,14 +2194,14 @@ pub const Allocating = struct { } fn setArrayList(a: *Allocating, list: std.ArrayListUnmanaged(u8)) void { - a.interface.buffer = list.allocatedSlice(); - a.interface.end = list.items.len; + a.writer.buffer = list.allocatedSlice(); + a.writer.end = list.items.len; } test Allocating { var a: Allocating = .init(std.testing.allocator); defer a.deinit(); - const w = &a.interface; + const w = &a.writer; const x: i32 = 42; const y: i32 = 1234; diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig index 9443e7f92b..90ae323026 100644 --- a/lib/std/zig/llvm/Builder.zig +++ b/lib/std/zig/llvm/Builder.zig @@ -8962,7 +8962,7 @@ pub fn getIntrinsic( const name = name: { { var aw: Writer.Allocating = .fromArrayList(self.gpa, &self.strtab_string_bytes); - const w = &aw.interface; + const w = &aw.writer; defer self.strtab_string_bytes = aw.toArrayList(); w.print("llvm.{s}", .{@tagName(id)}) catch return error.OutOfMemory; for (overload) |ty| w.print(".{fm}", .{ty.fmt(self)}) catch return error.OutOfMemory; diff --git a/src/Sema.zig b/src/Sema.zig index d0f50a01ab..5ba17dccb8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3026,7 +3026,7 @@ pub fn createTypeName( var aw: std.io.Writer.Allocating = .init(gpa); defer aw.deinit(); - const bw = &aw.interface; + const bw = &aw.writer; bw.print("{f}(", .{block.type_name_ctx.fmt(ip)}) catch return error.OutOfMemory; var arg_i: usize = 0; @@ -5908,7 +5908,7 @@ fn zirCompileLog( var aw: std.io.Writer.Allocating = .init(gpa); defer aw.deinit(); - const bw = &aw.interface; + const bw = &aw.writer; const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); const src_node = extra.data.src_node; @@ -37224,7 +37224,7 @@ fn notePathToComptimeAllocPtr(sema: *Sema, msg: *Zcu.ErrorMsg, src: LazySrcLoc, const inter_name = try std.fmt.allocPrint(arena, "v{d}", .{intermediate_value_count}); const deriv_start = @import("print_value.zig").printPtrDerivation( derivation, - &second_path_aw.interface, + &second_path_aw.writer, pt, .lvalue, .{ .str = inter_name }, -- cgit v1.2.3 From 7e2a26c0c441a902968726442114e3590820433b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 6 Jul 2025 15:51:44 -0700 Subject: std.io.Writer.printValue: rework logic Alignment and fill options only apply to numbers. Rework the implementation to mainly branch on the format string rather than the type information. This is more straightforward to maintain and more straightforward for comptime evaluation. Enums support being printed as decimal, hexadecimal, octal, and binary. `formatInteger` is another possible format method that is unconditionally called when the value type is struct and one of the integer-printing format specifiers are used. --- lib/compiler/aro/aro/Diagnostics.zig | 4 +- lib/compiler/aro/aro/Preprocessor.zig | 2 +- lib/compiler/aro/aro/Value.zig | 3 +- lib/std/Build/Step/CheckObject.zig | 11 +- lib/std/Target.zig | 25 +- lib/std/Uri.zig | 14 + lib/std/fmt.zig | 102 +++---- lib/std/io/Writer.zig | 491 ++++++++++++++++++++-------------- lib/std/json/dynamic_test.zig | 4 +- lib/std/json/stringify.zig | 1 - lib/std/json/stringify_test.zig | 12 +- lib/std/math/big/int.zig | 32 +-- lib/std/math/big/int_test.zig | 8 +- lib/std/zig.zig | 4 +- lib/std/zig/llvm/Builder.zig | 230 +++++++++++----- lib/std/zon/stringify.zig | 2 +- src/Builtin.zig | 4 +- src/Package/Fetch.zig | 9 +- src/Package/Fetch/git.zig | 32 ++- src/Sema/LowerZon.zig | 2 +- src/arch/riscv64/CodeGen.zig | 4 +- src/arch/riscv64/bits.zig | 6 + src/arch/x86_64/CodeGen.zig | 54 ++-- src/arch/x86_64/bits.zig | 6 + src/arch/x86_64/encoder.zig | 2 +- src/link.zig | 14 +- src/link/C.zig | 4 +- src/main.zig | 4 +- src/print_value.zig | 2 +- 29 files changed, 636 insertions(+), 452 deletions(-) (limited to 'lib/std/Build/Step/CheckObject.zig') diff --git a/lib/compiler/aro/aro/Diagnostics.zig b/lib/compiler/aro/aro/Diagnostics.zig index 5e92d555cd..dbf6e29af5 100644 --- a/lib/compiler/aro/aro/Diagnostics.zig +++ b/lib/compiler/aro/aro/Diagnostics.zig @@ -461,10 +461,10 @@ pub fn renderMessage(comp: *Compilation, m: anytype, msg: Message) void { try writer.writeByte(@intCast(codepoint)); } else if (codepoint < 0xFFFF) { try writer.writeAll("\\u"); - try writer.printIntOptions(codepoint, 16, .upper, .{ .fill = '0', .width = 4 }); + try writer.printInt(codepoint, 16, .upper, .{ .fill = '0', .width = 4 }); } else { try writer.writeAll("\\U"); - try writer.printIntOptions(codepoint, 16, .upper, .{ .fill = '0', .width = 8 }); + try writer.printInt(codepoint, 16, .upper, .{ .fill = '0', .width = 8 }); } } } diff --git a/lib/compiler/aro/aro/Preprocessor.zig b/lib/compiler/aro/aro/Preprocessor.zig index d71bf4f857..c8695edd6b 100644 --- a/lib/compiler/aro/aro/Preprocessor.zig +++ b/lib/compiler/aro/aro/Preprocessor.zig @@ -3262,7 +3262,7 @@ fn printLinemarker( // containing the same bytes as the input regardless of encoding. else => { try w.writeAll("\\x"); - // TODO try w.printIntOptions(byte, 16, .lower, .{ .width = 2, .fill = '0' }); + // TODO try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }); try w.print("{x:0>2}", .{byte}); }, }; diff --git a/lib/compiler/aro/aro/Value.zig b/lib/compiler/aro/aro/Value.zig index 670068203e..183c557976 100644 --- a/lib/compiler/aro/aro/Value.zig +++ b/lib/compiler/aro/aro/Value.zig @@ -961,8 +961,7 @@ pub fn print(v: Value, ty: Type, comp: *const Compilation, w: anytype) @TypeOf(w switch (key) { .null => return w.writeAll("nullptr_t"), .int => |repr| switch (repr) { - inline .u64, .i64 => |x| return w.print("{d}", .{x}), - .big_int => |x| return w.print("{fd}", .{x}), + inline .u64, .i64, .big_int => |x| return w.print("{d}", .{x}), }, .float => |repr| switch (repr) { .f16 => |x| return w.print("{d}", .{@round(@as(f64, @floatCast(x)) * 1000) / 1000}), diff --git a/lib/std/Build/Step/CheckObject.zig b/lib/std/Build/Step/CheckObject.zig index ba51632c67..870170a02e 100644 --- a/lib/std/Build/Step/CheckObject.zig +++ b/lib/std/Build/Step/CheckObject.zig @@ -230,12 +230,7 @@ const ComputeCompareExpected = struct { literal: u64, }, - pub fn format( - value: ComputeCompareExpected, - bw: *Writer, - comptime fmt: []const u8, - ) !void { - if (fmt.len != 0) std.fmt.invalidFmtError(fmt, value); + pub fn format(value: ComputeCompareExpected, bw: *Writer) Writer.Error!void { try bw.print("{s} ", .{@tagName(value.op)}); switch (value.value) { .variable => |name| try bw.writeAll(name), @@ -571,7 +566,9 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void { null, .of(u64), null, - ) catch |err| return step.fail("unable to read '{f'}': {s}", .{ src_path, @errorName(err) }); + ) catch |err| return step.fail("unable to read '{f}': {s}", .{ + std.fmt.alt(src_path, .formatEscapeChar), @errorName(err), + }); var vars: std.StringHashMap(u64) = .init(gpa); for (check_object.checks.items) |chk| { diff --git a/lib/std/Target.zig b/lib/std/Target.zig index 91eed5fee9..91deb9ddc1 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -301,24 +301,13 @@ pub const Os = struct { /// This function is defined to serialize a Zig source code representation of this /// type, that, when parsed, will deserialize into the same data. - pub fn format(ver: WindowsVersion, w: *std.io.Writer, comptime f: []const u8) std.io.Writer.Error!void { - const maybe_name = std.enums.tagName(WindowsVersion, ver); - if (comptime std.mem.eql(u8, f, "s")) { - if (maybe_name) |name| - try w.print(".{s}", .{name}) - else - try w.print(".{d}", .{@intFromEnum(ver)}); - } else if (comptime std.mem.eql(u8, f, "c")) { - if (maybe_name) |name| - try w.print(".{s}", .{name}) - else - try w.print("@enumFromInt(0x{X:0>8})", .{@intFromEnum(ver)}); - } else if (f.len == 0) { - if (maybe_name) |name| - try w.print("WindowsVersion.{s}", .{name}) - else - try w.print("WindowsVersion(0x{X:0>8})", .{@intFromEnum(ver)}); - } else std.fmt.invalidFmtError(f, ver); + pub fn format(wv: WindowsVersion, w: *std.io.Writer) std.io.Writer.Error!void { + if (std.enums.tagName(WindowsVersion, wv)) |name| { + var vecs: [2][]const u8 = .{ ".", name }; + return w.writeVecAll(&vecs); + } else { + return w.print("@enumFromInt(0x{X:0>8})", .{wv}); + } } }; diff --git a/lib/std/Uri.zig b/lib/std/Uri.zig index 611d6581c3..19af1512c2 100644 --- a/lib/std/Uri.zig +++ b/lib/std/Uri.zig @@ -240,6 +240,10 @@ pub fn parseAfterScheme(scheme: []const u8, text: []const u8) ParseError!Uri { return uri; } +pub fn format(uri: *const Uri, writer: *std.io.Writer) std.io.Writer.Error!void { + return writeToStream(uri, writer, .all); +} + pub fn writeToStream(uri: *const Uri, writer: *std.io.Writer, flags: Format.Flags) std.io.Writer.Error!void { if (flags.scheme) { try writer.print("{s}:", .{uri.scheme}); @@ -302,6 +306,16 @@ pub const Format = struct { fragment: bool = false, /// When true, include the port part of the URI. Ignored when `port` is null. port: bool = true, + + pub const all: Flags = .{ + .scheme = true, + .authentication = true, + .authority = true, + .path = true, + .query = true, + .fragment = true, + .port = true, + }; }; pub fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index b5938fcd8f..8588c82139 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -53,16 +53,20 @@ pub const Options = struct { /// - when using a field name, you are required to enclose the field name (an identifier) in square /// brackets, e.g. {[score]...} as opposed to the numeric index form which can be written e.g. {2...} /// - *specifier* is a type-dependent formatting option that determines how a type should formatted (see below) -/// - *fill* is a single byte which is used to pad the formatted text -/// - *alignment* is one of the three bytes '<', '^', or '>' to make the text left-, center-, or right-aligned, respectively -/// - *width* is the total width of the field in bytes. This is generally only -/// useful for ASCII text, such as numbers. -/// - *precision* specifies how many decimals a formatted number should have +/// - *fill* is a single byte which is used to pad formatted numbers. +/// - *alignment* is one of the three bytes '<', '^', or '>' to make numbers +/// left, center, or right-aligned, respectively. +/// - *width* is the total width of the field in bytes. This only applies to number formatting. +/// - *precision* specifies how many decimals a formatted number should have. /// -/// Note that most of the parameters are optional and may be omitted. Also you can leave out separators like `:` and `.` when -/// all parameters after the separator are omitted. -/// Only exception is the *fill* parameter. If a non-zero *fill* character is required at the same time as *width* is specified, -/// one has to specify *alignment* as well, as otherwise the digit following `:` is interpreted as *width*, not *fill*. +/// Note that most of the parameters are optional and may be omitted. Also you +/// can leave out separators like `:` and `.` when all parameters after the +/// separator are omitted. +/// +/// Only exception is the *fill* parameter. If a non-zero *fill* character is +/// required at the same time as *width* is specified, one has to specify +/// *alignment* as well, as otherwise the digit following `:` is interpreted as +/// *width*, not *fill*. /// /// The *specifier* has several options for types: /// - `x` and `X`: output numeric value in hexadecimal notation, or string in hexadecimal bytes @@ -405,9 +409,9 @@ pub const ArgState = struct { /// Asserts the rendered integer value fits in `buffer`. /// Returns the end index within `buffer`. pub fn printInt(buffer: []u8, value: anytype, base: u8, case: Case, options: Options) usize { - var bw: Writer = .fixed(buffer); - bw.printIntOptions(value, base, case, options) catch unreachable; - return bw.end; + var w: Writer = .fixed(buffer); + w.printInt(value, base, case, options) catch unreachable; + return w.end; } /// Converts values in the range [0, 100) to a base 10 string. @@ -956,35 +960,23 @@ fn expectArrayFmt(expected: []const u8, comptime template: []const u8, comptime } test "array" { - { - const value: [3]u8 = "abc".*; - try expectArrayFmt("array: abc\n", "array: {s}\n", value); - try expectArrayFmt("array: { 97, 98, 99 }\n", "array: {d}\n", value); - try expectArrayFmt("array: 616263\n", "array: {x}\n", value); - try expectArrayFmt("array: { 97, 98, 99 }\n", "array: {any}\n", value); - - var buf: [100]u8 = undefined; - try expectFmt( - try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@intFromPtr(&value)}), - "array: {*}\n", - .{&value}, - ); - } + const value: [3]u8 = "abc".*; + try expectArrayFmt("array: abc\n", "array: {s}\n", value); + try expectArrayFmt("array: 616263\n", "array: {x}\n", value); + try expectArrayFmt("array: { 97, 98, 99 }\n", "array: {any}\n", value); - { - const value = [2][3]u8{ "abc".*, "def".* }; - - try expectArrayFmt("array: { abc, def }\n", "array: {s}\n", value); - try expectArrayFmt("array: { { 97, 98, 99 }, { 100, 101, 102 } }\n", "array: {d}\n", value); - try expectArrayFmt("array: { 616263, 646566 }\n", "array: {x}\n", value); - } + var buf: [100]u8 = undefined; + try expectFmt( + try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@intFromPtr(&value)}), + "array: {*}\n", + .{&value}, + ); } test "slice" { { const value: []const u8 = "abc"; try expectFmt("slice: abc\n", "slice: {s}\n", .{value}); - try expectFmt("slice: { 97, 98, 99 }\n", "slice: {d}\n", .{value}); try expectFmt("slice: 616263\n", "slice: {x}\n", .{value}); try expectFmt("slice: { 97, 98, 99 }\n", "slice: {any}\n", .{value}); } @@ -999,17 +991,12 @@ test "slice" { try expectFmt("buf: \x00hello\x00\n", "buf: {s}\n", .{null_term_slice}); } - try expectFmt("buf: Test\n", "buf: {s:5}\n", .{"Test"}); try expectFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"}); { var int_slice = [_]u32{ 1, 4096, 391891, 1111111111 }; - var runtime_zero: usize = 0; - _ = &runtime_zero; - try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {any}", .{int_slice[runtime_zero..]}); - try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {d}", .{int_slice[runtime_zero..]}); - try expectFmt("int: { 1, 1000, 5fad3, 423a35c7 }", "int: {x}", .{int_slice[runtime_zero..]}); - try expectFmt("int: { 00001, 01000, 5fad3, 423a35c7 }", "int: {x:0>5}", .{int_slice[runtime_zero..]}); + const input: []const u32 = &int_slice; + try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {any}", .{input}); } { const S1 = struct { @@ -1054,11 +1041,6 @@ test "cstr" { "cstr: {s}\n", .{@as([*c]const u8, @ptrCast("Test C"))}, ); - try expectFmt( - "cstr: Test C\n", - "cstr: {s:10}\n", - .{@as([*c]const u8, @ptrCast("Test C"))}, - ); } test "struct" { @@ -1428,16 +1410,12 @@ test "enum-literal" { test "padding" { try expectFmt("Simple", "{s}", .{"Simple"}); - try expectFmt(" true", "{:10}", .{true}); - try expectFmt(" true", "{:>10}", .{true}); - try expectFmt("======true", "{:=>10}", .{true}); - try expectFmt("true======", "{:=<10}", .{true}); - try expectFmt(" true ", "{:^10}", .{true}); - try expectFmt("===true===", "{:=^10}", .{true}); - try expectFmt(" Minimum width", "{s:18} width", .{"Minimum"}); - try expectFmt("==================Filled", "{s:=>24}", .{"Filled"}); - try expectFmt(" Centered ", "{s:^24}", .{"Centered"}); - try expectFmt("-", "{s:-^1}", .{""}); + try expectFmt(" 1234", "{:10}", .{1234}); + try expectFmt(" 1234", "{:>10}", .{1234}); + try expectFmt("======1234", "{:=>10}", .{1234}); + try expectFmt("1234======", "{:=<10}", .{1234}); + try expectFmt(" 1234 ", "{:^10}", .{1234}); + try expectFmt("===1234===", "{:=^10}", .{1234}); try expectFmt("====a", "{c:=>5}", .{'a'}); try expectFmt("==a==", "{c:=^5}", .{'a'}); try expectFmt("a====", "{c:=<5}", .{'a'}); @@ -1485,17 +1463,17 @@ test "named arguments" { test "runtime width specifier" { const width: usize = 9; - try expectFmt("~~hello~~", "{s:~^[1]}", .{ "hello", width }); - try expectFmt("~~hello~~", "{s:~^[width]}", .{ .string = "hello", .width = width }); - try expectFmt(" hello", "{s:[1]}", .{ "hello", width }); - try expectFmt("42 hello", "{d} {s:[2]}", .{ 42, "hello", width }); + try expectFmt("~~12345~~", "{d:~^[1]}", .{ 12345, width }); + try expectFmt("~~12345~~", "{d:~^[width]}", .{ .string = 12345, .width = width }); + try expectFmt(" 12345", "{d:[1]}", .{ 12345, width }); + try expectFmt("42 12345", "{d} {d:[2]}", .{ 42, 12345, width }); } test "runtime precision specifier" { const number: f32 = 3.1415; const precision: usize = 2; - try expectFmt("3.14e0", "{:1.[1]}", .{ number, precision }); - try expectFmt("3.14e0", "{:1.[precision]}", .{ .number = number, .precision = precision }); + try expectFmt("3.14e0", "{e:1.[1]}", .{ number, precision }); + try expectFmt("3.14e0", "{e:1.[precision]}", .{ .number = number, .precision = precision }); } test "recursive format function" { diff --git a/lib/std/io/Writer.zig b/lib/std/io/Writer.zig index 29376db2e5..a5cb0aded1 100644 --- a/lib/std/io/Writer.zig +++ b/lib/std/io/Writer.zig @@ -777,15 +777,15 @@ pub fn printAddress(w: *Writer, value: anytype) Error!void { .pointer => |info| { try w.writeAll(@typeName(info.child) ++ "@"); if (info.size == .slice) - try w.printIntOptions(@intFromPtr(value.ptr), 16, .lower, .{}) + try w.printInt(@intFromPtr(value.ptr), 16, .lower, .{}) else - try w.printIntOptions(@intFromPtr(value), 16, .lower, .{}); + try w.printInt(@intFromPtr(value), 16, .lower, .{}); return; }, .optional => |info| { if (@typeInfo(info.child) == .pointer) { try w.writeAll(@typeName(info.child) ++ "@"); - try w.printIntOptions(@intFromPtr(value), 16, .lower, .{}); + try w.printInt(@intFromPtr(value), 16, .lower, .{}); return; } }, @@ -804,11 +804,147 @@ pub fn printValue( ) Error!void { const T = @TypeOf(value); - if (fmt.len == 1) switch (fmt[0]) { - '*' => return w.printAddress(value), - 'f' => return value.format(w), + switch (fmt.len) { + 1 => switch (fmt[0]) { + '*' => return w.printAddress(value), + 'f' => return value.format(w), + 'd' => switch (@typeInfo(T)) { + .float, .comptime_float => return printFloat(w, value, .decimal, options), + .int, .comptime_int => return printInt(w, value, 10, .lower, options), + .@"struct" => return value.formatInteger(w, 10, .lower), + .@"enum" => return printInt(w, @intFromEnum(value), 10, .lower, options), + .vector => return printVector(w, fmt, options, value, max_depth), + else => invalidFmtError(fmt, value), + }, + 'c' => return w.printAsciiChar(value, options), + 'u' => return w.printUnicodeCodepoint(value), + 'b' => switch (@typeInfo(T)) { + .int, .comptime_int => return printInt(w, value, 2, .lower, options), + .@"enum" => return printInt(w, @intFromEnum(value), 2, .lower, options), + .@"struct" => return value.formatInteger(w, 2, .lower), + .vector => return printVector(w, fmt, options, value, max_depth), + else => invalidFmtError(fmt, value), + }, + 'o' => switch (@typeInfo(T)) { + .int, .comptime_int => return printInt(w, value, 8, .lower, options), + .@"enum" => return printInt(w, @intFromEnum(value), 8, .lower, options), + .@"struct" => return value.formatInteger(w, 8, .lower), + .vector => return printVector(w, fmt, options, value, max_depth), + else => invalidFmtError(fmt, value), + }, + 'x' => switch (@typeInfo(T)) { + .float, .comptime_float => return printFloatHexOptions(w, value, .lower, options), + .int, .comptime_int => return printInt(w, value, 16, .lower, options), + .@"enum" => return printInt(w, @intFromEnum(value), 16, .lower, options), + .@"struct" => return value.formatInteger(w, 16, .lower), + .pointer => |info| switch (info.size) { + .one, .slice => { + const slice: []const u8 = value; + return printHex(w, slice, .lower); + }, + .many, .c => { + const slice: [:0]const u8 = std.mem.span(value); + return printHex(w, slice, .lower); + }, + }, + .array => { + const slice: []const u8 = &value; + return printHex(w, slice, .lower); + }, + .vector => return printVector(w, fmt, options, value, max_depth), + else => invalidFmtError(fmt, value), + }, + 'X' => switch (@typeInfo(T)) { + .float, .comptime_float => return printFloatHexOptions(w, value, .lower, options), + .int, .comptime_int => return printInt(w, value, 16, .upper, options), + .@"enum" => return printInt(w, @intFromEnum(value), 16, .upper, options), + .@"struct" => return value.formatInteger(w, 16, .upper), + .pointer => |info| switch (info.size) { + .one, .slice => { + const slice: []const u8 = value; + return printHex(w, slice, .upper); + }, + .many, .c => { + const slice: [:0]const u8 = std.mem.span(value); + return printHex(w, slice, .upper); + }, + }, + .array => { + const slice: []const u8 = &value; + return printHex(w, slice, .upper); + }, + .vector => return printVector(w, fmt, options, value, max_depth), + else => invalidFmtError(fmt, value), + }, + 's' => switch (@typeInfo(T)) { + .pointer => |info| switch (info.size) { + .one, .slice => { + const slice: []const u8 = value; + return w.writeAll(slice); + }, + .many, .c => { + const slice: [:0]const u8 = std.mem.span(value); + return w.writeAll(slice); + }, + }, + .array => { + const slice: []const u8 = &value; + return w.writeAll(slice); + }, + else => invalidFmtError(fmt, value), + }, + 'B' => switch (@typeInfo(T)) { + .int, .comptime_int => return w.printByteSize(value, .decimal, options), + .@"struct" => return value.formatByteSize(w, .decimal), + else => invalidFmtError(fmt, value), + }, + 'D' => switch (@typeInfo(T)) { + .int, .comptime_int => return w.printDuration(value, options), + .@"struct" => return value.formatDuration(w), + else => invalidFmtError(fmt, value), + }, + 'e' => switch (@typeInfo(T)) { + .float, .comptime_float => return printFloat(w, value, .scientific, options), + .@"struct" => return value.formatFloat(w, .scientific), + else => invalidFmtError(fmt, value), + }, + 't' => switch (@typeInfo(T)) { + .error_set => return w.writeAll(@errorName(value)), + .@"enum", .@"union" => return w.writeAll(@tagName(value)), + else => invalidFmtError(fmt, value), + }, + else => {}, + }, + 2 => switch (fmt[0]) { + 'B' => switch (fmt[1]) { + 'i' => switch (@typeInfo(T)) { + .int, .comptime_int => return w.printByteSize(value, .binary, options), + .@"struct" => return value.formatByteSize(w, .binary), + else => invalidFmtError(fmt, value), + }, + else => {}, + }, + else => {}, + }, + 3 => if (fmt[0] == 'b' and fmt[1] == '6' and fmt[2] == '4') switch (@typeInfo(T)) { + .pointer => |info| switch (info.size) { + .one, .slice => { + const slice: []const u8 = value; + return w.printBase64(slice); + }, + .many, .c => { + const slice: [:0]const u8 = std.mem.span(value); + return w.printBase64(slice); + }, + }, + .array => { + const slice: []const u8 = &value; + return w.printBase64(slice); + }, + else => invalidFmtError(fmt, value), + }, else => {}, - }; + } const is_any = comptime std.mem.eql(u8, fmt, ANY); if (!is_any and std.meta.hasMethod(T, "format") and fmt.len == 0) { @@ -817,15 +953,21 @@ pub fn printValue( } switch (@typeInfo(T)) { - .float, .comptime_float => return w.printFloat(if (is_any) "d" else fmt, options, value), - .int, .comptime_int => return w.printInt(if (is_any) "d" else fmt, options, value), + .float, .comptime_float => { + if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); + return printFloat(w, value, .decimal, options); + }, + .int, .comptime_int => { + if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); + return printInt(w, value, 10, .lower, options); + }, .bool => { if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); - return w.alignBufferOptions(if (value) "true" else "false", options); + return w.writeAll(if (value) "true" else "false"); }, .void => { if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); - return w.alignBufferOptions("void", options); + return w.writeAll("void"); }, .optional => { const remaining_fmt = comptime if (fmt.len > 0 and fmt[0] == '?') @@ -854,40 +996,18 @@ pub fn printValue( } }, .error_set => { - if (fmt.len == 1 and fmt[0] == 't') return w.writeAll(@errorName(value)); if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); - try printErrorSet(w, value); + return printErrorSet(w, value); }, - .@"enum" => { - if (fmt.len == 1 and fmt[0] == 't') { - try w.writeAll(@tagName(value)); - return; - } - if (!is_any) { - if (fmt.len != 0) return printValue(w, fmt, options, @intFromEnum(value), max_depth); - return printValue(w, ANY, options, value, max_depth); - } - const enum_info = @typeInfo(T).@"enum"; - if (enum_info.is_exhaustive) { - var vecs: [2][]const u8 = .{ ".", @tagName(value) }; - try w.writeVecAll(&vecs); - return; - } - if (std.enums.tagName(T, value)) |tag_name| { - var vecs: [2][]const u8 = .{ ".", tag_name }; - try w.writeVecAll(&vecs); - return; + .@"enum" => |info| { + if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); + if (info.is_exhaustive) { + return printEnumExhaustive(w, value); + } else { + return printEnumNonexhaustive(w, value); } - try w.writeAll("@enumFromInt("); - try w.printValue(ANY, options, @intFromEnum(value), max_depth); - try w.writeByte(')'); - return; }, .@"union" => |info| { - if (fmt.len == 1 and fmt[0] == 't') { - try w.writeAll(@tagName(value)); - return; - } if (!is_any) { if (fmt.len != 0) invalidFmtError(fmt, value); return printValue(w, ANY, options, value, max_depth); @@ -971,38 +1091,18 @@ pub fn printValue( else => { var buffers: [2][]const u8 = .{ @typeName(ptr_info.child), "@" }; try w.writeVecAll(&buffers); - try w.printIntOptions(@intFromPtr(value), 16, .lower, options); + try w.printInt(@intFromPtr(value), 16, .lower, options); return; }, }, .many, .c => { - if (ptr_info.sentinel() != null) - return w.printValue(fmt, options, std.mem.span(value), max_depth); - if (fmt.len == 1 and fmt[0] == 's' and ptr_info.child == u8) - return w.alignBufferOptions(std.mem.span(value), options); - if (!is_any and fmt.len == 0) - @compileError("cannot format pointer without a specifier (i.e. {s} or {*})"); - if (!is_any and fmt.len != 0) - invalidFmtError(fmt, value); + if (!is_any) @compileError("cannot format pointer without a specifier (i.e. {s} or {*})"); try w.printAddress(value); }, .slice => { - if (!is_any and fmt.len == 0) + if (!is_any) @compileError("cannot format slice without a specifier (i.e. {s}, {x}, {b64}, or {any})"); - if (max_depth == 0) - return w.writeAll("{ ... }"); - if (ptr_info.child == u8) switch (fmt.len) { - 1 => switch (fmt[0]) { - 's' => return w.alignBufferOptions(value, options), - 'x' => return w.printHex(value, .lower), - 'X' => return w.printHex(value, .upper), - else => {}, - }, - 3 => if (fmt[0] == 'b' and fmt[1] == '6' and fmt[2] == '4') { - return w.printBase64(value); - }, - else => {}, - }; + if (max_depth == 0) return w.writeAll("{ ... }"); try w.writeAll("{ "); for (value, 0..) |elem, i| { try w.printValue(fmt, options, elem, max_depth - 1); @@ -1013,21 +1113,9 @@ pub fn printValue( try w.writeAll(" }"); }, }, - .array => |info| { - if (fmt.len == 0) - @compileError("cannot format array without a specifier (i.e. {s} or {any})"); - if (max_depth == 0) { - return w.writeAll("{ ... }"); - } - if (info.child == u8) { - if (fmt[0] == 's') { - return w.alignBufferOptions(&value, options); - } else if (fmt[0] == 'x') { - return w.printHex(&value, .lower); - } else if (fmt[0] == 'X') { - return w.printHex(&value, .upper); - } - } + .array => { + if (!is_any) @compileError("cannot format array without a specifier (i.e. {s} or {any})"); + if (max_depth == 0) return w.writeAll("{ ... }"); try w.writeAll("{ "); for (value, 0..) |elem, i| { try w.printValue(fmt, options, elem, max_depth - 1); @@ -1037,33 +1125,23 @@ pub fn printValue( } try w.writeAll(" }"); }, - .vector => |info| { - if (max_depth == 0) { - return w.writeAll("{ ... }"); - } - try w.writeAll("{ "); - var i: usize = 0; - while (i < info.len) : (i += 1) { - try w.printValue(fmt, options, value[i], max_depth - 1); - if (i < info.len - 1) { - try w.writeAll(", "); - } - } - try w.writeAll(" }"); + .vector => { + if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); + return printVector(w, fmt, options, value, max_depth); }, .@"fn" => @compileError("unable to format function body type, use '*const " ++ @typeName(T) ++ "' for a function pointer type"), .type => { if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); - return w.alignBufferOptions(@typeName(value), options); + return w.writeAll(@typeName(value)); }, .enum_literal => { if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); - const buffer = [_]u8{'.'} ++ @tagName(value); - return w.alignBufferOptions(buffer, options); + var vecs: [2][]const u8 = .{ ".", @tagName(value) }; + return w.writeVecAll(&vecs); }, .null => { if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); - return w.alignBufferOptions("null", options); + return w.writeAll("null"); }, else => @compileError("unable to format type '" ++ @typeName(T) ++ "'"), } @@ -1074,75 +1152,68 @@ fn printErrorSet(w: *Writer, error_set: anyerror) Error!void { try w.writeVecAll(&vecs); } -pub fn printInt( +fn printEnumExhaustive(w: *Writer, value: anytype) Error!void { + var vecs: [2][]const u8 = .{ ".", @tagName(value) }; + try w.writeVecAll(&vecs); +} + +fn printEnumNonexhaustive(w: *Writer, value: anytype) Error!void { + if (std.enums.tagName(@TypeOf(value), value)) |tag_name| { + var vecs: [2][]const u8 = .{ ".", tag_name }; + try w.writeVecAll(&vecs); + return; + } + try w.writeAll("@enumFromInt("); + try w.printInt(@intFromEnum(value), 10, .lower, .{}); + try w.writeByte(')'); +} + +pub fn printVector( w: *Writer, comptime fmt: []const u8, options: std.fmt.Options, value: anytype, + max_depth: usize, ) Error!void { - const int_value = if (@TypeOf(value) == comptime_int) blk: { - const Int = std.math.IntFittingRange(value, value); - break :blk @as(Int, value); - } else value; - - switch (fmt.len) { - 0 => return w.printIntOptions(int_value, 10, .lower, options), - 1 => switch (fmt[0]) { - 'd' => return w.printIntOptions(int_value, 10, .lower, options), - 'c' => { - if (@typeInfo(@TypeOf(int_value)).int.bits <= 8) { - return w.printAsciiChar(@as(u8, int_value), options); - } else { - @compileError("cannot print integer that is larger than 8 bits as an ASCII character"); - } - }, - 'u' => { - if (@typeInfo(@TypeOf(int_value)).int.bits <= 21) { - return w.printUnicodeCodepoint(@as(u21, int_value), options); - } else { - @compileError("cannot print integer that is larger than 21 bits as an UTF-8 sequence"); - } - }, - 'b' => return w.printIntOptions(int_value, 2, .lower, options), - 'x' => return w.printIntOptions(int_value, 16, .lower, options), - 'X' => return w.printIntOptions(int_value, 16, .upper, options), - 'o' => return w.printIntOptions(int_value, 8, .lower, options), - 'B' => return w.printByteSize(int_value, .decimal, options), - 'D' => return w.printDuration(int_value, options), - else => invalidFmtError(fmt, value), - }, - 2 => { - if (fmt[0] == 'B' and fmt[1] == 'i') { - return w.printByteSize(int_value, .binary, options); - } else { - invalidFmtError(fmt, value); - } - }, - else => invalidFmtError(fmt, value), + const len = @typeInfo(@TypeOf(value)).vector.len; + if (max_depth == 0) return w.writeAll("{ ... }"); + try w.writeAll("{ "); + inline for (0..len) |i| { + try w.printValue(fmt, options, value[i], max_depth - 1); + if (i < len - 1) try w.writeAll(", "); } - comptime unreachable; -} - -pub fn printAsciiChar(w: *Writer, c: u8, options: std.fmt.Options) Error!void { - return w.alignBufferOptions(@as(*const [1]u8, &c), options); + try w.writeAll(" }"); } -pub fn printAscii(w: *Writer, bytes: []const u8, options: std.fmt.Options) Error!void { - return w.alignBufferOptions(bytes, options); -} - -pub fn printUnicodeCodepoint(w: *Writer, c: u21, options: std.fmt.Options) Error!void { - var buf: [4]u8 = undefined; - const len = std.unicode.utf8Encode(c, &buf) catch |err| switch (err) { - error.Utf8CannotEncodeSurrogateHalf, error.CodepointTooLarge => l: { - buf[0..3].* = std.unicode.replacement_character_utf8; - break :l 3; +// A wrapper around `printIntAny` to avoid the generic explosion of this +// function by funneling smaller integer types through `isize` and `usize`. +pub inline fn printInt( + w: *Writer, + value: anytype, + base: u8, + case: std.fmt.Case, + options: std.fmt.Options, +) Error!void { + switch (@TypeOf(value)) { + isize, usize => {}, + comptime_int => { + if (comptime std.math.cast(usize, value)) |x| return printIntAny(w, x, base, case, options); + if (comptime std.math.cast(isize, value)) |x| return printIntAny(w, x, base, case, options); + const Int = std.math.IntFittingRange(value, value); + return printIntAny(w, @as(Int, value), base, case, options); }, - }; - return w.alignBufferOptions(buf[0..len], options); + else => switch (@typeInfo(@TypeOf(value)).int.signedness) { + .signed => if (std.math.cast(isize, value)) |x| return printIntAny(w, x, base, case, options), + .unsigned => if (std.math.cast(usize, value)) |x| return printIntAny(w, x, base, case, options), + }, + } + return printIntAny(w, value, base, case, options); } -pub fn printIntOptions( +/// In general, prefer `printInt` to avoid generic explosion. However this +/// function may be used when optimal codegen for a particular integer type is +/// desired. +pub fn printIntAny( w: *Writer, value: anytype, base: u8, @@ -1150,20 +1221,14 @@ pub fn printIntOptions( options: std.fmt.Options, ) Error!void { assert(base >= 2); - - const int_value = if (@TypeOf(value) == comptime_int) blk: { - const Int = std.math.IntFittingRange(value, value); - break :blk @as(Int, value); - } else value; - - const value_info = @typeInfo(@TypeOf(int_value)).int; + const value_info = @typeInfo(@TypeOf(value)).int; // The type must have the same size as `base` or be wider in order for the // division to work const min_int_bits = comptime @max(value_info.bits, 8); const MinInt = std.meta.Int(.unsigned, min_int_bits); - const abs_value = @abs(int_value); + const abs_value = @abs(value); // The worst case in terms of space needed is base 2, plus 1 for the sign var buf: [1 + @max(@as(comptime_int, value_info.bits), 1)]u8 = undefined; @@ -1210,38 +1275,49 @@ pub fn printIntOptions( return w.alignBufferOptions(buf[index..], options); } +pub fn printAsciiChar(w: *Writer, c: u8, options: std.fmt.Options) Error!void { + return w.alignBufferOptions(@as(*const [1]u8, &c), options); +} + +pub fn printAscii(w: *Writer, bytes: []const u8, options: std.fmt.Options) Error!void { + return w.alignBufferOptions(bytes, options); +} + +pub fn printUnicodeCodepoint(w: *Writer, c: u21) Error!void { + var buf: [4]u8 = undefined; + const len = std.unicode.utf8Encode(c, &buf) catch |err| switch (err) { + error.Utf8CannotEncodeSurrogateHalf, error.CodepointTooLarge => l: { + buf[0..3].* = std.unicode.replacement_character_utf8; + break :l 3; + }, + }; + return w.writeAll(buf[0..len]); +} + pub fn printFloat( w: *Writer, - comptime fmt: []const u8, - options: std.fmt.Options, value: anytype, + mode: std.fmt.float.Mode, + options: std.fmt.Options, ) Error!void { var buf: [std.fmt.float.bufferSize(.decimal, f64)]u8 = undefined; + const s = std.fmt.float.render(&buf, value, .{ + .mode = mode, + .precision = options.precision, + }) catch |err| switch (err) { + error.BufferTooSmall => "(float)", + }; + return w.alignBufferOptions(s, options); +} - if (fmt.len > 1) invalidFmtError(fmt, value); - switch (if (fmt.len == 0) 'e' else fmt[0]) { - 'e' => { - const s = std.fmt.float.render(&buf, value, .{ .mode = .scientific, .precision = options.precision }) catch |err| switch (err) { - error.BufferTooSmall => "(float)", - }; - return w.alignBufferOptions(s, options); - }, - 'd' => { - const s = std.fmt.float.render(&buf, value, .{ .mode = .decimal, .precision = options.precision }) catch |err| switch (err) { - error.BufferTooSmall => "(float)", - }; - return w.alignBufferOptions(s, options); - }, - 'x' => { - var sub_bw: Writer = .fixed(&buf); - sub_bw.printFloatHexadecimal(value, options.precision) catch unreachable; - return w.alignBufferOptions(sub_bw.buffered(), options); - }, - else => invalidFmtError(fmt, value), - } +pub fn printFloatHexOptions(w: *Writer, value: anytype, case: std.fmt.Case, options: std.fmt.Options) Error!void { + var buf: [50]u8 = undefined; // for aligning + var sub_writer: Writer = .fixed(&buf); + printFloatHex(&sub_writer, value, case, options.precision) catch unreachable; // buf is large enough + return w.alignBufferOptions(sub_writer.buffered(), options); } -pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize) Error!void { +pub fn printFloatHex(w: *Writer, value: anytype, case: std.fmt.Case, opt_precision: ?usize) Error!void { if (std.math.signbit(value)) try w.writeByte('-'); if (std.math.isNan(value)) return w.writeAll("nan"); if (std.math.isInf(value)) return w.writeAll("inf"); @@ -1320,7 +1396,7 @@ pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize) // +1 for the decimal part. var buf: [1 + mantissa_digits]u8 = undefined; - assert(std.fmt.printInt(&buf, mantissa, 16, .lower, .{ .fill = '0', .width = 1 + mantissa_digits }) == buf.len); + assert(std.fmt.printInt(&buf, mantissa, 16, case, .{ .fill = '0', .width = 1 + mantissa_digits }) == buf.len); try w.writeAll("0x"); try w.writeByte(buf[0]); @@ -1337,7 +1413,7 @@ pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize) try w.splatByteAll('0', precision - trimmed.len); }; try w.writeAll("p"); - try w.printIntOptions(exponent - exponent_bias, 10, .lower, .{}); + try w.printInt(exponent - exponent_bias, 10, case, .{}); } pub const ByteSizeUnits = enum { @@ -1433,7 +1509,7 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void { }) |unit| { if (ns_remaining >= unit.ns) { const units = ns_remaining / unit.ns; - try w.printIntOptions(units, 10, .lower, .{}); + try w.printInt(units, 10, .lower, .{}); try w.writeByte(unit.sep); ns_remaining -= units * unit.ns; if (ns_remaining == 0) return; @@ -1447,13 +1523,13 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void { }) |unit| { const kunits = ns_remaining * 1000 / unit.ns; if (kunits >= 1000) { - try w.printIntOptions(kunits / 1000, 10, .lower, .{}); + try w.printInt(kunits / 1000, 10, .lower, .{}); const frac = kunits % 1000; if (frac > 0) { // Write up to 3 decimal places var decimal_buf = [_]u8{ '.', 0, 0, 0 }; var inner: Writer = .fixed(decimal_buf[1..]); - inner.printIntOptions(frac, 10, .lower, .{ .fill = '0', .width = 3 }) catch unreachable; + inner.printInt(frac, 10, .lower, .{ .fill = '0', .width = 3 }) catch unreachable; var end: usize = 4; while (end > 1) : (end -= 1) { if (decimal_buf[end - 1] != '0') break; @@ -1464,7 +1540,7 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void { } } - try w.printIntOptions(ns_remaining, 10, .lower, .{}); + try w.printInt(ns_remaining, 10, .lower, .{}); try w.writeAll("ns"); } @@ -1474,12 +1550,18 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void { pub fn printDuration(w: *Writer, nanoseconds: anytype, options: std.fmt.Options) Error!void { // worst case: "-XXXyXXwXXdXXhXXmXX.XXXs".len = 24 var buf: [24]u8 = undefined; - var sub_bw: Writer = .fixed(&buf); - switch (@typeInfo(@TypeOf(nanoseconds)).int.signedness) { - .signed => sub_bw.printDurationSigned(nanoseconds) catch unreachable, - .unsigned => sub_bw.printDurationUnsigned(nanoseconds) catch unreachable, + var sub_writer: Writer = .fixed(&buf); + if (@TypeOf(nanoseconds) == comptime_int) { + if (nanoseconds >= 0) { + sub_writer.printDurationUnsigned(nanoseconds) catch unreachable; + } else { + sub_writer.printDurationSigned(nanoseconds) catch unreachable; + } + } else switch (@typeInfo(@TypeOf(nanoseconds)).int.signedness) { + .signed => sub_writer.printDurationSigned(nanoseconds) catch unreachable, + .unsigned => sub_writer.printDurationUnsigned(nanoseconds) catch unreachable, } - return w.alignBufferOptions(sub_bw.buffered(), options); + return w.alignBufferOptions(sub_writer.buffered(), options); } pub fn printHex(w: *Writer, bytes: []const u8, case: std.fmt.Case) Error!void { @@ -1749,7 +1831,7 @@ fn testDurationCaseSigned(expected: []const u8, input: i64) !void { try testing.expectEqualStrings(expected, w.buffered()); } -test printIntOptions { +test printInt { try testPrintIntCase("-1", @as(i1, -1), 10, .lower, .{}); try testPrintIntCase("-101111000110000101001110", @as(i32, -12345678), 2, .lower, .{}); @@ -1765,27 +1847,22 @@ test printIntOptions { try testPrintIntCase("+42", @as(i32, 42), 10, .lower, .{ .width = 3 }); try testPrintIntCase("-42", @as(i32, -42), 10, .lower, .{ .width = 3 }); -} -test "printInt with comptime_int" { - var buf: [20]u8 = undefined; - var w: Writer = .fixed(&buf); - try w.printInt("", .{}, @as(comptime_int, 123456789123456789)); - try std.testing.expectEqualStrings("123456789123456789", w.buffered()); + try testPrintIntCase("123456789123456789", @as(comptime_int, 123456789123456789), 10, .lower, .{}); } test "printFloat with comptime_float" { var buf: [20]u8 = undefined; var w: Writer = .fixed(&buf); - try w.printFloat("", .{}, @as(comptime_float, 1.0)); + try w.printFloat(@as(comptime_float, 1.0), .scientific, .{}); try std.testing.expectEqualStrings(w.buffered(), "1e0"); - try std.testing.expectFmt("1e0", "{}", .{1.0}); + try std.testing.expectFmt("1", "{}", .{1.0}); } fn testPrintIntCase(expected: []const u8, value: anytype, base: u8, case: std.fmt.Case, options: std.fmt.Options) !void { var buffer: [100]u8 = undefined; var w: Writer = .fixed(&buffer); - try w.printIntOptions(value, base, case, options); + try w.printInt(value, base, case, options); try testing.expectEqualStrings(expected, w.buffered()); } diff --git a/lib/std/json/dynamic_test.zig b/lib/std/json/dynamic_test.zig index 45cdc0d0c7..1362e3cfad 100644 --- a/lib/std/json/dynamic_test.zig +++ b/lib/std/json/dynamic_test.zig @@ -254,7 +254,7 @@ test "Value.jsonStringify" { \\ true, \\ 42, \\ 43, - \\ 4.2e1, + \\ 42, \\ "weeee", \\ [ \\ 1, @@ -266,7 +266,7 @@ test "Value.jsonStringify" { \\ } \\] ; - try testing.expectEqualSlices(u8, expected, fbs.getWritten()); + try testing.expectEqualStrings(expected, fbs.getWritten()); } test "parseFromValue(std.json.Value,...)" { diff --git a/lib/std/json/stringify.zig b/lib/std/json/stringify.zig index 0b575fc587..b3c208756c 100644 --- a/lib/std/json/stringify.zig +++ b/lib/std/json/stringify.zig @@ -469,7 +469,6 @@ pub fn WriteStream( /// * When option `emit_nonportable_numbers_as_strings` is true, if the value is outside the range `+-1<<53` (the precise integer range of f64), it is rendered as a JSON string in base 10. Otherwise, it is rendered as JSON number. /// * Zig floats -> JSON number or string. /// * If the value cannot be precisely represented by an f64, it is rendered as a JSON string. Otherwise, it is rendered as JSON number. - /// * TODO: Float rendering will likely change in the future, e.g. to remove the unnecessary "e+00". /// * Zig `[]const u8`, `[]u8`, `*[N]u8`, `@Vector(N, u8)`, and similar -> JSON string. /// * See `StringifyOptions.emit_strings_as_arrays`. /// * If the content is not valid UTF-8, rendered as an array of numbers instead. diff --git a/lib/std/json/stringify_test.zig b/lib/std/json/stringify_test.zig index 122616b596..22dd504285 100644 --- a/lib/std/json/stringify_test.zig +++ b/lib/std/json/stringify_test.zig @@ -74,16 +74,16 @@ fn testBasicWriteStream(w: anytype, slice_stream: anytype) !void { \\{ \\ "object": { \\ "one": 1, - \\ "two": 2e0 + \\ "two": 2 \\ }, \\ "string": "This is a string", \\ "array": [ \\ "Another string", \\ 1, - \\ 3.5e0 + \\ 3.5 \\ ], \\ "int": 10, - \\ "float": 3.5e0 + \\ "float": 3.5 \\} ; try std.testing.expectEqualStrings(expected, result); @@ -123,12 +123,12 @@ test "stringify basic types" { try testStringify("null", @as(?u8, null), .{}); try testStringify("null", @as(?*u32, null), .{}); try testStringify("42", 42, .{}); - try testStringify("4.2e1", 42.0, .{}); + try testStringify("42", 42.0, .{}); try testStringify("42", @as(u8, 42), .{}); try testStringify("42", @as(u128, 42), .{}); try testStringify("9999999999999999", 9999999999999999, .{}); - try testStringify("4.2e1", @as(f32, 42), .{}); - try testStringify("4.2e1", @as(f64, 42), .{}); + try testStringify("42", @as(f32, 42), .{}); + try testStringify("42", @as(f64, 42), .{}); try testStringify("\"ItBroke\"", @as(anyerror, error.ItBroke), .{}); try testStringify("\"ItBroke\"", error.ItBroke, .{}); } diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 000b578dc0..c657aeeed3 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -2028,6 +2028,14 @@ pub const Mutable = struct { pub fn normalize(r: *Mutable, length: usize) void { r.len = llnormalize(r.limbs[0..length]); } + + pub fn format(self: Mutable, w: *std.io.Writer) std.io.Writer.Error!void { + return formatInteger(self, w, 10, .lower); + } + + pub fn formatInteger(self: Const, w: *std.io.Writer, base: u8, case: std.fmt.Case) std.io.Writer.Error!void { + return self.toConst().formatInteger(w, base, case); + } }; /// A arbitrary-precision big integer, with a fixed set of immutable limbs. @@ -2321,7 +2329,7 @@ pub const Const = struct { /// this function will fail to print the string, printing "(BigInt)" instead of a number. /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory. /// See `toString` and `toStringAlloc` for a way to print big integers without failure. - pub fn print(self: Const, w: *std.io.Writer, base: u8, case: std.fmt.Case) std.io.Writer.Error!void { + pub fn formatInteger(self: Const, w: *std.io.Writer, base: u8, case: std.fmt.Case) std.io.Writer.Error!void { const available_len = 64; if (self.limbs.len > available_len) return w.writeAll("(BigInt)"); @@ -2337,20 +2345,6 @@ pub const Const = struct { return w.writeAll(buf[0..len]); } - const Format = struct { - int: Const, - base: u8, - case: std.fmt.Case, - - pub fn default(f: Format, w: *std.io.Writer) std.io.Writer.Error!void { - return print(f.int, w, f.base, f.case); - } - }; - - pub fn fmt(self: Const, base: u8, case: std.fmt.Case) std.fmt.Formatter(Format, Format.default) { - return .{ .data = .{ .int = self, .base = base, .case = case } }; - } - /// Converts self to a string in the requested base. /// Caller owns returned memory. /// Asserts that `base` is in the range [2, 36]. @@ -2918,16 +2912,16 @@ pub const Managed = struct { } /// To allow `std.fmt.format` to work with `Managed`. - pub fn format(self: Managed, w: *std.io.Writer, comptime f: []const u8) std.io.Writer.Error!void { - return self.toConst().format(w, f); + pub fn format(self: Managed, w: *std.io.Writer) std.io.Writer.Error!void { + return formatInteger(self, w, 10, .lower); } /// If the absolute value of integer is greater than or equal to `pow(2, 64 * @sizeOf(usize) * 8)`, /// this function will fail to print the string, printing "(BigInt)" instead of a number. /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory. /// See `toString` and `toStringAlloc` for a way to print big integers without failure. - pub fn fmt(self: Managed, base: u8, case: std.fmt.Case) std.fmt.Formatter(Const.Format, Const.Format.default) { - return .{ .data = .{ .int = self.toConst(), .base = base, .case = case } }; + pub fn formatInteger(self: Managed, w: *std.io.Writer, base: u8, case: std.fmt.Case) std.io.Writer.Error!void { + return self.toConst().formatInteger(w, base, case); } /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| == diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig index 58f5a01a05..f09797352c 100644 --- a/lib/std/math/big/int_test.zig +++ b/lib/std/math/big/int_test.zig @@ -3813,8 +3813,8 @@ test "(BigInt) positive" { try a.pow(&a, 64 * @sizeOf(Limb) * 8); try b.sub(&a, &c); - try testing.expectFmt("(BigInt)", "{f}", .{a.fmt(10, .lower)}); - try testing.expectFmt("1044388881413152506691752710716624382579964249047383780384233483283953907971557456848826811934997558340890106714439262837987573438185793607263236087851365277945956976543709998340361590134383718314428070011855946226376318839397712745672334684344586617496807908705803704071284048740118609114467977783598029006686938976881787785946905630190260940599579453432823469303026696443059025015972399867714215541693835559885291486318237914434496734087811872639496475100189041349008417061675093668333850551032972088269550769983616369411933015213796825837188091833656751221318492846368125550225998300412344784862595674492194617023806505913245610825731835380087608622102834270197698202313169017678006675195485079921636419370285375124784014907159135459982790513399611551794271106831134090584272884279791554849782954323534517065223269061394905987693002122963395687782878948440616007412945674919823050571642377154816321380631045902916136926708342856440730447899971901781465763473223850267253059899795996090799469201774624817718449867455659250178329070473119433165550807568221846571746373296884912819520317457002440926616910874148385078411929804522981857338977648103126085903001302413467189726673216491511131602920781738033436090243804708340403154190335", "{f}", .{b.fmt(10, .lower)}); + try testing.expectFmt("(BigInt)", "{d}", .{a}); + try testing.expectFmt("1044388881413152506691752710716624382579964249047383780384233483283953907971557456848826811934997558340890106714439262837987573438185793607263236087851365277945956976543709998340361590134383718314428070011855946226376318839397712745672334684344586617496807908705803704071284048740118609114467977783598029006686938976881787785946905630190260940599579453432823469303026696443059025015972399867714215541693835559885291486318237914434496734087811872639496475100189041349008417061675093668333850551032972088269550769983616369411933015213796825837188091833656751221318492846368125550225998300412344784862595674492194617023806505913245610825731835380087608622102834270197698202313169017678006675195485079921636419370285375124784014907159135459982790513399611551794271106831134090584272884279791554849782954323534517065223269061394905987693002122963395687782878948440616007412945674919823050571642377154816321380631045902916136926708342856440730447899971901781465763473223850267253059899795996090799469201774624817718449867455659250178329070473119433165550807568221846571746373296884912819520317457002440926616910874148385078411929804522981857338977648103126085903001302413467189726673216491511131602920781738033436090243804708340403154190335", "{d}", .{b}); } test "(BigInt) negative" { @@ -3832,10 +3832,10 @@ test "(BigInt) negative" { a.negate(); try b.add(&a, &c); - const a_fmt = try std.fmt.allocPrint(testing.allocator, "{f}", .{a.fmt(10, .lower)}); + const a_fmt = try std.fmt.allocPrint(testing.allocator, "{d}", .{a}); defer testing.allocator.free(a_fmt); - const b_fmt = try std.fmt.allocPrint(testing.allocator, "{f}", .{b.fmt(10, .lower)}); + const b_fmt = try std.fmt.allocPrint(testing.allocator, "{d}", .{b}); defer testing.allocator.free(b_fmt); try testing.expect(mem.eql(u8, a_fmt, "(BigInt)")); diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 5d7f16a2a0..be3c982d09 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -475,7 +475,7 @@ pub fn stringEscape(bytes: []const u8, w: *std.io.Writer) std.io.Writer.Error!vo ' ', '!', '#'...'&', '('...'[', ']'...'~' => try w.writeByte(byte), else => { try w.writeAll("\\x"); - try w.printIntOptions(byte, 16, .lower, .{ .width = 2, .fill = '0' }); + try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }); }, }; } @@ -492,7 +492,7 @@ pub fn charEscape(bytes: []const u8, w: *std.io.Writer) std.io.Writer.Error!void ' ', '!', '#'...'&', '('...'[', ']'...'~' => try w.writeByte(byte), else => { try w.writeAll("\\x"); - try w.printIntOptions(byte, 16, .lower, .{ .width = 2, .fill = '0' }); + try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }); }, }; } diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig index 4691b96933..b3e0311cdd 100644 --- a/lib/std/zig/llvm/Builder.zig +++ b/lib/std/zig/llvm/Builder.zig @@ -246,7 +246,7 @@ pub const Type = enum(u32) { _, pub const ptr_amdgpu_constant = - @field(Type, std.fmt.comptimePrint("ptr{f }", .{AddrSpace.amdgpu.constant})); + @field(Type, std.fmt.comptimePrint("ptr{f}", .{AddrSpace.amdgpu.constant.fmt(" ")})); pub const Tag = enum(u4) { simple, @@ -779,7 +779,7 @@ pub const Type = enum(u32) { } }, .integer => try w.print("i{d}", .{item.data}), - .pointer => try w.print("ptr{f }", .{@as(AddrSpace, @enumFromInt(item.data))}), + .pointer => try w.print("ptr{f}", .{@as(AddrSpace, @enumFromInt(item.data)).fmt(" ")}), .target => { var extra = data.builder.typeExtraDataTrail(Type.Target, item.data); const types = extra.trail.next(extra.data.types_len, Type, data.builder); @@ -1242,7 +1242,7 @@ pub const Attribute = union(Kind) { .sret, .elementtype, => |ty| try w.print(" {s}({f})", .{ @tagName(attribute), ty.fmt(data.builder, .percent) }), - .@"align" => |alignment| try w.print("{f }", .{alignment}), + .@"align" => |alignment| try w.print("{f}", .{alignment.fmt(" ")}), .dereferenceable, .dereferenceable_or_null, => |size| try w.print(" {s}({d})", .{ @tagName(attribute), size }), @@ -1853,10 +1853,31 @@ pub const ThreadLocal = enum(u3) { initialexec = 3, localexec = 4, - pub fn format(self: ThreadLocal, w: *Writer, comptime prefix: []const u8) Writer.Error!void { - if (self == .default) return; - try w.print("{s}thread_local", .{prefix}); - if (self != .generaldynamic) try w.print("({s})", .{@tagName(self)}); + pub fn format(tl: ThreadLocal, w: *Writer) Writer.Error!void { + return Prefixed.format(.{ .thread_local = tl, .prefix = "" }, w); + } + + pub const Prefixed = struct { + thread_local: ThreadLocal, + prefix: []const u8, + + pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { + switch (p.thread_local) { + .default => return, + .generaldynamic => { + var vecs: [2][]const u8 = .{ p.prefix, "thread_local" }; + return w.writeVecAll(&vecs); + }, + else => { + var vecs: [4][]const u8 = .{ p.prefix, "thread_local(", @tagName(p.thread_local), ")" }; + return w.writeVecAll(&vecs); + }, + } + } + }; + + pub fn fmt(tl: ThreadLocal, prefix: []const u8) Prefixed { + return .{ .thread_local = tl, .prefix = prefix }; } }; @@ -1961,8 +1982,24 @@ pub const AddrSpace = enum(u24) { pub const funcref: AddrSpace = @enumFromInt(20); }; - pub fn format(self: AddrSpace, w: *Writer, comptime prefix: []const u8) Writer.Error!void { - if (self != .default) try w.print("{s}addrspace({d})", .{ prefix, @intFromEnum(self) }); + pub fn format(addr_space: AddrSpace, w: *Writer) Writer.Error!void { + return Prefixed.format(.{ .addr_space = addr_space, .prefix = "" }, w); + } + + pub const Prefixed = struct { + addr_space: AddrSpace, + prefix: []const u8, + + pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { + switch (p.addr_space) { + .default => return, + else => return w.print("{s}addrspace({d})", .{ p.prefix, p.addr_space }), + } + } + }; + + pub fn fmt(addr_space: AddrSpace, prefix: []const u8) Prefixed { + return .{ .addr_space = addr_space, .prefix = prefix }; } }; @@ -1994,8 +2031,18 @@ pub const Alignment = enum(u6) { return if (self == .default) 0 else (@intFromEnum(self) + 1); } - pub fn format(self: Alignment, w: *Writer, comptime prefix: []const u8) Writer.Error!void { - try w.print("{s}align {d}", .{ prefix, self.toByteUnits() orelse return }); + pub const Prefixed = struct { + alignment: Alignment, + prefix: []const u8, + + pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { + const byte_units = p.alignment.toByteUnits() orelse return; + return w.print("{s}align ({d})", .{ p.prefix, byte_units }); + } + }; + + pub fn fmt(alignment: Alignment, prefix: []const u8) Prefixed { + return .{ .alignment = alignment, .prefix = prefix }; } }; @@ -6978,8 +7025,27 @@ pub const MemoryAccessKind = enum(u1) { normal, @"volatile", - pub fn format(self: MemoryAccessKind, w: *Writer, comptime prefix: []const u8) Writer.Error!void { - if (self != .normal) try w.print("{s}{s}", .{ prefix, @tagName(self) }); + pub fn format(memory_access_kind: MemoryAccessKind, w: *Writer) Writer.Error!void { + return Prefixed.format(.{ .memory_access_kind = memory_access_kind, .prefix = "" }, w); + } + + pub const Prefixed = struct { + memory_access_kind: MemoryAccessKind, + prefix: []const u8, + + pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { + switch (p.memory_access_kind) { + .normal => return, + .@"volatile" => { + var vecs: [2][]const u8 = .{ p.prefix, "volatile" }; + return w.writeVecAll(&vecs); + }, + } + } + }; + + pub fn fmt(memory_access_kind: MemoryAccessKind, prefix: []const u8) Prefixed { + return .{ .memory_access_kind = memory_access_kind, .prefix = prefix }; } }; @@ -6987,10 +7053,27 @@ pub const SyncScope = enum(u1) { singlethread, system, - pub fn format(self: SyncScope, w: *Writer, comptime prefix: []const u8) Writer.Error!void { - if (self != .system) try w.print( - \\{s}syncscope("{s}") - , .{ prefix, @tagName(self) }); + pub fn format(sync_scope: SyncScope, w: *Writer) Writer.Error!void { + return Prefixed.format(.{ .sync_scope = sync_scope, .prefix = "" }, w); + } + + pub const Prefixed = struct { + sync_scope: SyncScope, + prefix: []const u8, + + pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { + switch (p.sync_scope) { + .system => return, + .singlethread => { + var vecs: [2][]const u8 = .{ p.prefix, "syncscope(\"singlethread\")" }; + return w.writeVecAll(&vecs); + }, + } + } + }; + + pub fn fmt(sync_scope: SyncScope, prefix: []const u8) Prefixed { + return .{ .sync_scope = sync_scope, .prefix = prefix }; } }; @@ -7003,8 +7086,27 @@ pub const AtomicOrdering = enum(u3) { acq_rel = 5, seq_cst = 6, - pub fn format(self: AtomicOrdering, w: *Writer, comptime prefix: []const u8) Writer.Error!void { - if (self != .none) try w.print("{s}{s}", .{ prefix, @tagName(self) }); + pub fn format(atomic_ordering: AtomicOrdering, w: *Writer) Writer.Error!void { + return Prefixed.format(.{ .atomic_ordering = atomic_ordering, .prefix = "" }, w); + } + + pub const Prefixed = struct { + atomic_ordering: AtomicOrdering, + prefix: []const u8, + + pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { + switch (p.atomic_ordering) { + .none => return, + else => { + var vecs: [2][]const u8 = .{ p.prefix, @tagName(p.atomic_ordering) }; + return w.writeVecAll(&vecs); + }, + } + } + }; + + pub fn fmt(atomic_ordering: AtomicOrdering, prefix: []const u8) Prefixed { + return .{ .atomic_ordering = atomic_ordering, .prefix = prefix }; } }; @@ -8550,7 +8652,7 @@ pub fn init(options: Options) Allocator.Error!Builder { inline for (.{ 0, 4 }) |addr_space_index| { const addr_space: AddrSpace = @enumFromInt(addr_space_index); assert(self.ptrTypeAssumeCapacity(addr_space) == - @field(Type, std.fmt.comptimePrint("ptr{f }", .{addr_space}))); + @field(Type, std.fmt.comptimePrint("ptr{f}", .{addr_space.fmt(" ")}))); } } @@ -9469,7 +9571,7 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void metadata_formatter.need_comma = true; defer metadata_formatter.need_comma = undefined; try w.print( - \\{f} ={f}{f}{f}{f}{f }{f}{f }{f} {s} {f}{f}{f, }{f} + \\{f} ={f}{f}{f}{f}{f}{f}{f}{f} {s} {f}{f}{f}{f} \\ , .{ variable.global.fmt(self), @@ -9479,14 +9581,14 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void global.preemption, global.visibility, global.dll_storage_class, - variable.thread_local, + variable.thread_local.fmt(" "), global.unnamed_addr, - global.addr_space, + global.addr_space.fmt(" "), global.externally_initialized, @tagName(variable.mutability), global.type.fmt(self, .percent), variable.init.fmt(self, .{ .space = true }), - variable.alignment, + variable.alignment.fmt(", "), try metadata_formatter.fmt("!dbg ", global.dbg, null), }); } @@ -9500,7 +9602,7 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void metadata_formatter.need_comma = true; defer metadata_formatter.need_comma = undefined; try w.print( - \\{f} ={f}{f}{f}{f}{f }{f} alias {f}, {f}{f} + \\{f} ={f}{f}{f}{f}{f}{f} alias {f}, {f}{f} \\ , .{ alias.global.fmt(self), @@ -9508,7 +9610,7 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void global.preemption, global.visibility, global.dll_storage_class, - alias.thread_local, + alias.thread_local.fmt(" "), global.unnamed_addr, global.type.fmt(self, .percent), alias.aliasee.fmt(self, .{ .percent = true }), @@ -9564,15 +9666,15 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void try w.writeAll("..."); }, } - try w.print("){f}{f }", .{ global.unnamed_addr, global.addr_space }); + try w.print("){f}{f}", .{ global.unnamed_addr, global.addr_space.fmt(" ") }); if (function_attributes != .none) try w.print(" #{d}", .{ (try attribute_groups.getOrPutValue(self.gpa, function_attributes, {})).index, }); { metadata_formatter.need_comma = false; defer metadata_formatter.need_comma = undefined; - try w.print("{f }{f}", .{ - function.alignment, + try w.print("{f}{f}", .{ + function.alignment.fmt(" "), try metadata_formatter.fmt(" !dbg ", global.dbg, null), }); } @@ -9709,7 +9811,7 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void .@"alloca inalloca", => |tag| { const extra = function.extraData(Function.Instruction.Alloca, instruction.data); - try w.print(" %{f} = {s} {f}{f}{f, }{f, }", .{ + try w.print(" %{f} = {s} {f}{f}{f}{f}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), extra.type.fmt(self, .percent), @@ -9720,24 +9822,24 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void .comma = true, .percent = true, }), - extra.info.alignment, - extra.info.addr_space, + extra.info.alignment.fmt(", "), + extra.info.addr_space.fmt(", "), }); }, .arg => unreachable, .atomicrmw => |tag| { const extra = function.extraData(Function.Instruction.AtomicRmw, instruction.data); - try w.print(" %{f} = {s}{f } {s} {f}, {f}{f }{f }{f, }", .{ + try w.print(" %{f} = {t}{f} {t} {f}, {f}{f}{f}{f}", .{ instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.info.access_kind, - @tagName(extra.info.atomic_rmw_operation), + tag, + extra.info.access_kind.fmt(" "), + extra.info.atomic_rmw_operation, extra.ptr.fmt(function_index, self, .{ .percent = true }), extra.val.fmt(function_index, self, .{ .percent = true }), - extra.info.sync_scope, - extra.info.success_ordering, - extra.info.alignment, + extra.info.sync_scope.fmt(" "), + extra.info.success_ordering.fmt(" "), + extra.info.alignment.fmt(", "), }); }, .block => { @@ -9792,8 +9894,8 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void }), .none => unreachable, } - try w.print("{s}{f}{f}{f} {f} {f}(", .{ - @tagName(tag), + try w.print("{t}{f}{f}{f} {f} {f}(", .{ + tag, extra.data.info.call_conv, extra.data.attributes.ret(self).fmt(self, .{}), extra.data.callee.typeOf(function_index, self).pointerAddrSpace(self), @@ -9831,17 +9933,17 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void => |tag| { const extra = function.extraData(Function.Instruction.CmpXchg, instruction.data); - try w.print(" %{f} = {s}{f } {f}, {f}, {f}{f }{f }{f }{f, }", .{ + try w.print(" %{f} = {t}{f} {f}, {f}, {f}{f}{f}{f}{f}", .{ instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.info.access_kind, + tag, + extra.info.access_kind.fmt(" "), extra.ptr.fmt(function_index, self, .{ .percent = true }), extra.cmp.fmt(function_index, self, .{ .percent = true }), extra.new.fmt(function_index, self, .{ .percent = true }), - extra.info.sync_scope, - extra.info.success_ordering, - extra.info.failure_ordering, - extra.info.alignment, + extra.info.sync_scope.fmt(" "), + extra.info.success_ordering.fmt(" "), + extra.info.failure_ordering.fmt(" "), + extra.info.alignment.fmt(", "), }); }, .extractelement => |tag| { @@ -9869,10 +9971,10 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void }, .fence => |tag| { const info: MemoryAccessInfo = @bitCast(instruction.data); - try w.print(" {s}{f }{f }", .{ - @tagName(tag), - info.sync_scope, - info.success_ordering, + try w.print(" {t}{f}{f}", .{ + tag, + info.sync_scope.fmt(" "), + info.success_ordering.fmt(" "), }); }, .fneg, @@ -9947,15 +10049,15 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void .@"load atomic", => |tag| { const extra = function.extraData(Function.Instruction.Load, instruction.data); - try w.print(" %{f} = {s}{f } {f}, {f}{f }{f }{f, }", .{ + try w.print(" %{f} = {t}{f} {f}, {f}{f}{f}{f}", .{ instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.info.access_kind, + tag, + extra.info.access_kind.fmt(" "), extra.type.fmt(self, .percent), extra.ptr.fmt(function_index, self, .{ .percent = true }), - extra.info.sync_scope, - extra.info.success_ordering, - extra.info.alignment, + extra.info.sync_scope.fmt(" "), + extra.info.success_ordering.fmt(" "), + extra.info.alignment.fmt(", "), }); }, .phi, @@ -10015,14 +10117,14 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void .@"store atomic", => |tag| { const extra = function.extraData(Function.Instruction.Store, instruction.data); - try w.print(" {s}{f } {f}, {f}{f }{f }{f, }", .{ - @tagName(tag), - extra.info.access_kind, + try w.print(" {t}{f} {f}, {f}{f}{f}{f}", .{ + tag, + extra.info.access_kind.fmt(" "), extra.val.fmt(function_index, self, .{ .percent = true }), extra.ptr.fmt(function_index, self, .{ .percent = true }), - extra.info.sync_scope, - extra.info.success_ordering, - extra.info.alignment, + extra.info.sync_scope.fmt(" "), + extra.info.success_ordering.fmt(" "), + extra.info.alignment.fmt(", "), }); }, .@"switch" => |tag| { diff --git a/lib/std/zon/stringify.zig b/lib/std/zon/stringify.zig index c25bf733e3..2b4fc642df 100644 --- a/lib/std/zon/stringify.zig +++ b/lib/std/zon/stringify.zig @@ -615,7 +615,7 @@ pub fn Serializer(Writer: type) type { /// Serialize an integer. pub fn int(self: *Self, val: anytype) Writer.Error!void { - //try self.writer.printIntOptions(val, 10, .lower, .{}); + //try self.writer.printInt(val, 10, .lower, .{}); try std.fmt.deprecatedFormat(self.writer, "{d}", .{val}); } diff --git a/src/Builtin.zig b/src/Builtin.zig index ef674cf889..b2cb603f53 100644 --- a/src/Builtin.zig +++ b/src/Builtin.zig @@ -200,8 +200,8 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void { }), .windows => |windows| try buffer.print( \\ .windows = .{{ - \\ .min = {fc}, - \\ .max = {fc}, + \\ .min = {f}, + \\ .max = {f}, \\ }}}}, \\ , .{ windows.min, windows.max }), diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig index e7ba4ec2b5..b9a1d4376a 100644 --- a/src/Package/Fetch.zig +++ b/src/Package/Fetch.zig @@ -227,9 +227,9 @@ pub const JobQueue = struct { } try buf.writer().print( - \\ pub const build_root = "{fq}"; + \\ pub const build_root = "{f}"; \\ - , .{fetch.package_root}); + , .{std.fmt.alt(fetch.package_root, .formatEscapeString)}); if (fetch.has_build_zig) { try buf.writer().print( @@ -1079,7 +1079,10 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re }); const notes_start = try eb.reserveNotes(notes_len); eb.extra.items[notes_start] = @intFromEnum(try eb.addErrorMessage(.{ - .msg = try eb.printString("try .url = \"{f;+/}#{f}\",", .{ uri, want_oid }), + .msg = try eb.printString("try .url = \"{f}#{f}\",", .{ + uri.fmt(.{ .scheme = true, .authority = true, .path = true }), + want_oid, + }), })); return error.FetchFailed; } diff --git a/src/Package/Fetch/git.zig b/src/Package/Fetch/git.zig index 8dfcbb2453..4d2dae904f 100644 --- a/src/Package/Fetch/git.zig +++ b/src/Package/Fetch/git.zig @@ -662,13 +662,21 @@ pub const Session = struct { fn init(allocator: Allocator, uri: std.Uri) !Location { const scheme = try allocator.dupe(u8, uri.scheme); errdefer allocator.free(scheme); - const user = if (uri.user) |user| try std.fmt.allocPrint(allocator, "{fuser}", .{user}) else null; + const user = if (uri.user) |user| try std.fmt.allocPrint(allocator, "{f}", .{ + std.fmt.alt(user, .formatUser), + }) else null; errdefer if (user) |s| allocator.free(s); - const password = if (uri.password) |password| try std.fmt.allocPrint(allocator, "{fpassword}", .{password}) else null; + const password = if (uri.password) |password| try std.fmt.allocPrint(allocator, "{f}", .{ + std.fmt.alt(password, .formatPassword), + }) else null; errdefer if (password) |s| allocator.free(s); - const host = if (uri.host) |host| try std.fmt.allocPrint(allocator, "{fhost}", .{host}) else null; + const host = if (uri.host) |host| try std.fmt.allocPrint(allocator, "{f}", .{ + std.fmt.alt(host, .formatHost), + }) else null; errdefer if (host) |s| allocator.free(s); - const path = try std.fmt.allocPrint(allocator, "{fpath}", .{uri.path}); + const path = try std.fmt.allocPrint(allocator, "{f}", .{ + std.fmt.alt(uri.path, .formatPath), + }); errdefer allocator.free(path); // The query and fragment are not used as part of the base server URI. return .{ @@ -699,7 +707,9 @@ pub const Session = struct { fn getCapabilities(session: *Session, http_headers_buffer: []u8) !CapabilityIterator { var info_refs_uri = session.location.uri; { - const session_uri_path = try std.fmt.allocPrint(session.allocator, "{fpath}", .{session.location.uri.path}); + const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{ + std.fmt.alt(session.location.uri.path, .formatPath), + }); defer session.allocator.free(session_uri_path); info_refs_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "info/refs" }) }; } @@ -723,7 +733,9 @@ pub const Session = struct { if (request.response.status != .ok) return error.ProtocolError; const any_redirects_occurred = request.redirect_behavior.remaining() < max_redirects; if (any_redirects_occurred) { - const request_uri_path = try std.fmt.allocPrint(session.allocator, "{fpath}", .{request.uri.path}); + const request_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{ + std.fmt.alt(request.uri.path, .formatPath), + }); defer session.allocator.free(request_uri_path); if (!mem.endsWith(u8, request_uri_path, "/info/refs")) return error.UnparseableRedirect; var new_uri = request.uri; @@ -810,7 +822,9 @@ pub const Session = struct { pub fn listRefs(session: Session, options: ListRefsOptions) !RefIterator { var upload_pack_uri = session.location.uri; { - const session_uri_path = try std.fmt.allocPrint(session.allocator, "{fpath}", .{session.location.uri.path}); + const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{ + std.fmt.alt(session.location.uri.path, .formatPath), + }); defer session.allocator.free(session_uri_path); upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "git-upload-pack" }) }; } @@ -925,7 +939,9 @@ pub const Session = struct { ) !FetchStream { var upload_pack_uri = session.location.uri; { - const session_uri_path = try std.fmt.allocPrint(session.allocator, "{fpath}", .{session.location.uri.path}); + const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{ + std.fmt.alt(session.location.uri.path, .formatPath), + }); defer session.allocator.free(session_uri_path); upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "git-upload-pack" }) }; } diff --git a/src/Sema/LowerZon.zig b/src/Sema/LowerZon.zig index b55bab127b..8c70a5b784 100644 --- a/src/Sema/LowerZon.zig +++ b/src/Sema/LowerZon.zig @@ -492,7 +492,7 @@ fn lowerInt( if (!val.fitsInTwosComp(int_info.signedness, int_info.bits)) { return self.fail( node, - "type '{f}' cannot represent integer value '{f}'", + "type '{f}' cannot represent integer value '{d}'", .{ res_ty.fmt(self.sema.pt), val }, ); } diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index f0fdc3a98a..49baf7acad 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1151,7 +1151,7 @@ fn gen(func: *Func) !void { func.ret_mcv.long.address().offset(-func.ret_mcv.short.indirect.off), ); func.ret_mcv.long = .{ .load_frame = .{ .index = frame_index } }; - tracking_log.debug("spill {} to {f}", .{ func.ret_mcv.long, frame_index }); + tracking_log.debug("spill {} to {}", .{ func.ret_mcv.long, frame_index }); }, else => unreachable, } @@ -1987,7 +1987,7 @@ fn allocFrameIndex(func: *Func, alloc: FrameAlloc) !FrameIndex { } const frame_index: FrameIndex = @enumFromInt(func.frame_allocs.len); try func.frame_allocs.append(func.gpa, alloc); - log.debug("allocated frame {f}", .{frame_index}); + log.debug("allocated frame {}", .{frame_index}); return frame_index; } diff --git a/src/arch/riscv64/bits.zig b/src/arch/riscv64/bits.zig index 2f79da7116..94c64dfc98 100644 --- a/src/arch/riscv64/bits.zig +++ b/src/arch/riscv64/bits.zig @@ -249,6 +249,12 @@ pub const FrameIndex = enum(u32) { spill_frame, /// Other indices are used for local variable stack slots _, + + pub const named_count = @typeInfo(FrameIndex).@"enum".fields.len; + + pub fn isNamed(fi: FrameIndex) bool { + return @intFromEnum(fi) < named_count; + } }; /// A linker symbol not yet allocated in VM. diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 177d6b7b49..486497a365 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -525,47 +525,47 @@ pub const MCValue = union(enum) { }; } - pub fn format(mcv: MCValue, bw: *Writer) Writer.Error!void { + pub fn format(mcv: MCValue, w: *Writer) Writer.Error!void { switch (mcv) { - .none, .unreach, .dead, .undef => try bw.print("({s})", .{@tagName(mcv)}), - .immediate => |pl| try bw.print("0x{x}", .{pl}), - .memory => |pl| try bw.print("[ds:0x{x}]", .{pl}), - inline .eflags, .register => |pl| try bw.print("{s}", .{@tagName(pl)}), - .register_pair => |pl| try bw.print("{s}:{s}", .{ @tagName(pl[1]), @tagName(pl[0]) }), - .register_triple => |pl| try bw.print("{s}:{s}:{s}", .{ + .none, .unreach, .dead, .undef => try w.print("({s})", .{@tagName(mcv)}), + .immediate => |pl| try w.print("0x{x}", .{pl}), + .memory => |pl| try w.print("[ds:0x{x}]", .{pl}), + inline .eflags, .register => |pl| try w.print("{s}", .{@tagName(pl)}), + .register_pair => |pl| try w.print("{s}:{s}", .{ @tagName(pl[1]), @tagName(pl[0]) }), + .register_triple => |pl| try w.print("{s}:{s}:{s}", .{ @tagName(pl[2]), @tagName(pl[1]), @tagName(pl[0]), }), - .register_quadruple => |pl| try bw.print("{s}:{s}:{s}:{s}", .{ + .register_quadruple => |pl| try w.print("{s}:{s}:{s}:{s}", .{ @tagName(pl[3]), @tagName(pl[2]), @tagName(pl[1]), @tagName(pl[0]), }), - .register_offset => |pl| try bw.print("{s} + 0x{x}", .{ @tagName(pl.reg), pl.off }), - .register_overflow => |pl| try bw.print("{s}:{s}", .{ + .register_offset => |pl| try w.print("{s} + 0x{x}", .{ @tagName(pl.reg), pl.off }), + .register_overflow => |pl| try w.print("{s}:{s}", .{ @tagName(pl.eflags), @tagName(pl.reg), }), - .register_mask => |pl| try bw.print("mask({s},{f}):{c}{s}", .{ + .register_mask => |pl| try w.print("mask({s},{f}):{c}{s}", .{ @tagName(pl.info.kind), pl.info.scalar, @as(u8, if (pl.info.inverted) '!' else ' '), @tagName(pl.reg), }), - .indirect => |pl| try bw.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }), - .indirect_load_frame => |pl| try bw.print("[[{f} + 0x{x}]]", .{ pl.index, pl.off }), - .load_frame => |pl| try bw.print("[{f} + 0x{x}]", .{ pl.index, pl.off }), - .lea_frame => |pl| try bw.print("{f} + 0x{x}", .{ pl.index, pl.off }), - .load_nav => |pl| try bw.print("[nav:{d}]", .{@intFromEnum(pl)}), - .lea_nav => |pl| try bw.print("nav:{d}", .{@intFromEnum(pl)}), - .load_uav => |pl| try bw.print("[uav:{d}]", .{@intFromEnum(pl.val)}), - .lea_uav => |pl| try bw.print("uav:{d}", .{@intFromEnum(pl.val)}), - .load_lazy_sym => |pl| try bw.print("[lazy:{s}:{d}]", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }), - .lea_lazy_sym => |pl| try bw.print("lazy:{s}:{d}", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }), - .load_extern_func => |pl| try bw.print("[extern:{d}]", .{@intFromEnum(pl)}), - .lea_extern_func => |pl| try bw.print("extern:{d}", .{@intFromEnum(pl)}), - .elementwise_args => |pl| try bw.print("elementwise:{d}:[{f} + 0x{x}]", .{ + .indirect => |pl| try w.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }), + .indirect_load_frame => |pl| try w.print("[[{} + 0x{x}]]", .{ pl.index, pl.off }), + .load_frame => |pl| try w.print("[{} + 0x{x}]", .{ pl.index, pl.off }), + .lea_frame => |pl| try w.print("{} + 0x{x}", .{ pl.index, pl.off }), + .load_nav => |pl| try w.print("[nav:{d}]", .{@intFromEnum(pl)}), + .lea_nav => |pl| try w.print("nav:{d}", .{@intFromEnum(pl)}), + .load_uav => |pl| try w.print("[uav:{d}]", .{@intFromEnum(pl.val)}), + .lea_uav => |pl| try w.print("uav:{d}", .{@intFromEnum(pl.val)}), + .load_lazy_sym => |pl| try w.print("[lazy:{s}:{d}]", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }), + .lea_lazy_sym => |pl| try w.print("lazy:{s}:{d}", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }), + .load_extern_func => |pl| try w.print("[extern:{d}]", .{@intFromEnum(pl)}), + .lea_extern_func => |pl| try w.print("extern:{d}", .{@intFromEnum(pl)}), + .elementwise_args => |pl| try w.print("elementwise:{d}:[{} + 0x{x}]", .{ pl.regs, pl.frame_index, pl.frame_off, }), - .reserved_frame => |pl| try bw.print("(dead:{f})", .{pl}), - .air_ref => |pl| try bw.print("(air:0x{x})", .{@intFromEnum(pl)}), + .reserved_frame => |pl| try w.print("(dead:{})", .{pl}), + .air_ref => |pl| try w.print("(air:0x{x})", .{@intFromEnum(pl)}), } } }; @@ -2026,7 +2026,7 @@ fn gen( .{}, ); self.ret_mcv.long = .{ .load_frame = .{ .index = frame_index } }; - tracking_log.debug("spill {f} to {f}", .{ self.ret_mcv.long, frame_index }); + tracking_log.debug("spill {f} to {}", .{ self.ret_mcv.long, frame_index }); }, else => unreachable, } diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index 2e9b1f7f21..16361acb4d 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -721,6 +721,12 @@ pub const FrameIndex = enum(u32) { call_frame, // Other indices are used for local variable stack slots _, + + pub const named_count = @typeInfo(FrameIndex).@"enum".fields.len; + + pub fn isNamed(fi: FrameIndex) bool { + return @intFromEnum(fi) < named_count; + } }; pub const FrameAddr = struct { index: FrameIndex, off: i32 = 0 }; diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index 59938990f6..43d23af5fc 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -259,7 +259,7 @@ pub const Instruction = struct { switch (sib.base) { .none => any = false, .reg => |reg| try w.print("{s}", .{@tagName(reg)}), - .frame => |frame_index| try w.print("{f}", .{frame_index}), + .frame => |frame_index| try w.print("{}", .{frame_index}), .table => try w.print("Table", .{}), .rip_inst => |inst_index| try w.print("RipInst({d})", .{inst_index}), .nav => |nav| try w.print("Nav({d})", .{@intFromEnum(nav)}), diff --git a/src/link.zig b/src/link.zig index 41b184d792..0fbd4b28cf 100644 --- a/src/link.zig +++ b/src/link.zig @@ -838,8 +838,10 @@ pub const File = struct { const cached_pp_file_path = the_key.status.success.object_path; cached_pp_file_path.root_dir.handle.copyFile(cached_pp_file_path.sub_path, emit.root_dir.handle, emit.sub_path, .{}) catch |err| { const diags = &base.comp.link_diags; - return diags.fail("failed to copy '{f'}' to '{f'}': {s}", .{ - @as(Path, cached_pp_file_path), @as(Path, emit), @errorName(err), + return diags.fail("failed to copy '{f}' to '{f}': {s}", .{ + std.fmt.alt(@as(Path, cached_pp_file_path), .formatEscapeChar), + std.fmt.alt(@as(Path, emit), .formatEscapeChar), + @errorName(err), }); }; return; @@ -2086,14 +2088,14 @@ fn resolvePathInputLib( }) { var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) { error.FileNotFound => return .no_match, - else => |e| fatal("unable to search for {s} library '{f'}': {s}", .{ - @tagName(link_mode), test_path, @errorName(e), + else => |e| fatal("unable to search for {s} library '{f}': {s}", .{ + @tagName(link_mode), std.fmt.alt(test_path, .formatEscapeChar), @errorName(e), }), }; errdefer file.close(); try ld_script_bytes.resize(gpa, @max(std.elf.MAGIC.len, std.elf.ARMAG.len)); - const n = file.preadAll(ld_script_bytes.items, 0) catch |err| fatal("failed to read '{f'}': {s}", .{ - test_path, @errorName(err), + const n = file.preadAll(ld_script_bytes.items, 0) catch |err| fatal("failed to read '{f}': {s}", .{ + std.fmt.alt(test_path, .formatEscapeChar), @errorName(err), }); const buf = ld_script_bytes.items[0..n]; if (mem.startsWith(u8, buf, std.elf.MAGIC) or mem.startsWith(u8, buf, std.elf.ARMAG)) { diff --git a/src/link/C.zig b/src/link/C.zig index 6f32857c1e..5ffb0fa986 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -503,8 +503,8 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P var fw = file.writer(&.{}); var w = &fw.interface; w.writeVecAll(f.all_buffers.items) catch |err| switch (err) { - error.WriteFailed => return diags.fail("failed to write to '{f'}': {s}", .{ - self.base.emit, @errorName(fw.err.?), + error.WriteFailed => return diags.fail("failed to write to '{f}': {s}", .{ + std.fmt.alt(self.base.emit, .formatEscapeChar), @errorName(fw.err.?), }), }; } diff --git a/src/main.zig b/src/main.zig index 99cdf887f4..f639630604 100644 --- a/src/main.zig +++ b/src/main.zig @@ -6964,7 +6964,9 @@ fn cmdFetch( std.log.info("resolved ref '{s}' to commit {s}", .{ target_ref, latest_commit_hex }); // include the original refspec in a query parameter, could be used to check for updates - uri.query = .{ .percent_encoded = try std.fmt.allocPrint(arena, "ref={f%}", .{fragment}) }; + uri.query = .{ .percent_encoded = try std.fmt.allocPrint(arena, "ref={f}", .{ + std.fmt.alt(fragment, .formatEscaped), + }) }; } else { std.log.info("resolved to commit {s}", .{latest_commit_hex}); } diff --git a/src/print_value.zig b/src/print_value.zig index ae9ceb1a16..e0a489ee40 100644 --- a/src/print_value.zig +++ b/src/print_value.zig @@ -77,7 +77,7 @@ pub fn print( .func => |func| try writer.print("(function '{f}')", .{ip.getNav(func.owner_nav).name.fmt(ip)}), .int => |int| switch (int.storage) { inline .u64, .i64 => |x| try writer.print("{d}", .{x}), - .big_int => |x| try writer.print("{fd}", .{x}), + .big_int => |x| try writer.print("{d}", .{x}), .lazy_align => |ty| if (opt_sema != null) { const a = try Type.fromInterned(ty).abiAlignmentSema(pt); try writer.print("{d}", .{a.toByteUnits() orelse 0}); -- cgit v1.2.3 From f2ad3bcc1c7ad18ed8e2d9c919be45885e224bfc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 6 Jul 2025 23:49:30 -0700 Subject: fix 32-bit compilation --- lib/std/Build/Step/CheckObject.zig | 267 ++++++++++++++++++------------------- lib/std/fs/File.zig | 10 +- lib/std/io.zig | 10 ++ lib/std/io/Writer.zig | 4 +- lib/std/zig/system/linux.zig | 2 +- src/codegen/c.zig | 4 +- 6 files changed, 153 insertions(+), 144 deletions(-) (limited to 'lib/std/Build/Step/CheckObject.zig') diff --git a/lib/std/Build/Step/CheckObject.zig b/lib/std/Build/Step/CheckObject.zig index 870170a02e..3d140d9969 100644 --- a/lib/std/Build/Step/CheckObject.zig +++ b/lib/std/Build/Step/CheckObject.zig @@ -1231,12 +1231,12 @@ const MachODumper = struct { } fn parseRebaseInfo(ctx: ObjectContext, data: []const u8, rebases: *std.ArrayList(u64)) !void { - var br: std.io.Reader = .fixed(data); + var r: std.io.Reader = .fixed(data); var seg_id: ?u8 = null; var offset: u64 = 0; while (true) { - const byte = br.takeByte() catch break; + const byte = r.takeByte() catch break; const opc = byte & macho.REBASE_OPCODE_MASK; const imm = byte & macho.REBASE_IMMEDIATE_MASK; switch (opc) { @@ -1244,17 +1244,17 @@ const MachODumper = struct { macho.REBASE_OPCODE_SET_TYPE_IMM => {}, macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => { seg_id = imm; - offset = try br.takeLeb128(u64); + offset = try r.takeLeb128(u64); }, macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED => { offset += imm * @sizeOf(u64); }, macho.REBASE_OPCODE_ADD_ADDR_ULEB => { - const addend = try br.takeLeb128(u64); + const addend = try r.takeLeb128(u64); offset += addend; }, macho.REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB => { - const addend = try br.takeLeb128(u64); + const addend = try r.takeLeb128(u64); const seg = ctx.segments.items[seg_id.?]; const addr = seg.vmaddr + offset; try rebases.append(addr); @@ -1271,11 +1271,11 @@ const MachODumper = struct { ntimes = imm; }, macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES => { - ntimes = try br.takeLeb128(u64); + ntimes = try r.takeLeb128(u64); }, macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB => { - ntimes = try br.takeLeb128(u64); - skip = try br.takeLeb128(u64); + ntimes = try r.takeLeb128(u64); + skip = try r.takeLeb128(u64); }, else => unreachable, } @@ -1341,7 +1341,7 @@ const MachODumper = struct { } fn parseBindInfo(ctx: ObjectContext, data: []const u8, bindings: *std.ArrayList(Binding)) !void { - var br: std.io.Reader = .fixed(data); + var r: std.io.Reader = .fixed(data); var seg_id: ?u8 = null; var tag: Binding.Tag = .self; @@ -1352,7 +1352,7 @@ const MachODumper = struct { var name_buf: std.io.Writer.Allocating = .init(ctx.gpa); defer name_buf.deinit(); - while (br.takeByte()) |byte| { + while (r.takeByte()) |byte| { const opc = byte & macho.BIND_OPCODE_MASK; const imm = byte & macho.BIND_IMMEDIATE_MASK; switch (opc) { @@ -1373,18 +1373,18 @@ const MachODumper = struct { }, macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => { seg_id = imm; - offset = try br.takeLeb128(u64); + offset = try r.takeLeb128(u64); }, macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => { name_buf.clearRetainingCapacity(); - _ = try br.streamDelimiterLimit(&name_buf.writer, 0, .limited(std.math.maxInt(u32))); + _ = try r.streamDelimiterLimit(&name_buf.writer, 0, .limited(std.math.maxInt(u32))); try name_buf.writer.writeByte(0); }, macho.BIND_OPCODE_SET_ADDEND_SLEB => { - addend = try br.takeLeb128(i64); + addend = try r.takeLeb128(i64); }, macho.BIND_OPCODE_ADD_ADDR_ULEB => { - const x = try br.takeLeb128(u64); + const x = try r.takeLeb128(u64); offset = @intCast(@as(i64, @intCast(offset)) + @as(i64, @bitCast(x))); }, macho.BIND_OPCODE_DO_BIND, @@ -1399,14 +1399,14 @@ const MachODumper = struct { switch (opc) { macho.BIND_OPCODE_DO_BIND => {}, macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB => { - add_addr = try br.takeLeb128(u64); + add_addr = try r.takeLeb128(u64); }, macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED => { add_addr = imm * @sizeOf(u64); }, macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB => { - count = try br.takeLeb128(u64); - skip = try br.takeLeb128(u64); + count = try r.takeLeb128(u64); + skip = try r.takeLeb128(u64); }, else => unreachable, } @@ -1437,8 +1437,8 @@ const MachODumper = struct { defer arena.deinit(); var exports: std.ArrayList(Export) = .init(arena.allocator()); - var br: std.io.Reader = .fixed(data); - try parseTrieNode(arena.allocator(), &br, "", &exports); + var r: std.io.Reader = .fixed(data); + try parseTrieNode(arena.allocator(), &r, "", &exports); mem.sort(Export, exports.items, {}, Export.lessThan); @@ -1506,17 +1506,17 @@ const MachODumper = struct { fn parseTrieNode( arena: Allocator, - br: *std.io.Reader, + r: *std.io.Reader, prefix: []const u8, exports: *std.ArrayList(Export), ) !void { - const size = try br.takeLeb128(u64); + const size = try r.takeLeb128(u64); if (size > 0) { - const flags = try br.takeLeb128(u8); + const flags = try r.takeLeb128(u8); switch (flags) { macho.EXPORT_SYMBOL_FLAGS_REEXPORT => { - const ord = try br.takeLeb128(u64); - const name = try br.takeSentinel(0); + const ord = try r.takeLeb128(u64); + const name = try r.takeSentinel(0); try exports.append(.{ .name = if (name.len > 0) name else prefix, .tag = .reexport, @@ -1524,8 +1524,8 @@ const MachODumper = struct { }); }, macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER => { - const stub_offset = try br.takeLeb128(u64); - const resolver_offset = try br.takeLeb128(u64); + const stub_offset = try r.takeLeb128(u64); + const resolver_offset = try r.takeLeb128(u64); try exports.append(.{ .name = prefix, .tag = .stub_resolver, @@ -1536,7 +1536,7 @@ const MachODumper = struct { }); }, else => { - const vmoff = try br.takeLeb128(u64); + const vmoff = try r.takeLeb128(u64); try exports.append(.{ .name = prefix, .tag = .@"export", @@ -1555,15 +1555,15 @@ const MachODumper = struct { } } - const nedges = try br.takeByte(); + const nedges = try r.takeByte(); for (0..nedges) |_| { - const label = try br.takeSentinel(0); - const off = try br.takeLeb128(usize); + const label = try r.takeSentinel(0); + const off = try r.takeLeb128(usize); const prefix_label = try std.fmt.allocPrint(arena, "{s}{s}", .{ prefix, label }); - const seek = br.seek; - br.seek = off; - try parseTrieNode(arena, br, prefix_label, exports); - br.seek = seek; + const seek = r.seek; + r.seek = off; + try parseTrieNode(arena, r, prefix_label, exports); + r.seek = seek; } } @@ -1693,9 +1693,9 @@ const ElfDumper = struct { fn parseAndDumpArchive(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; - var br: std.io.Reader = .fixed(bytes); + var r: std.io.Reader = .fixed(bytes); - if (!mem.eql(u8, try br.takeArray(elf.ARMAG.len), elf.ARMAG)) return error.InvalidArchiveMagicNumber; + if (!mem.eql(u8, try r.takeArray(elf.ARMAG.len), elf.ARMAG)) return error.InvalidArchiveMagicNumber; var ctx: ArchiveContext = .{ .gpa = gpa, @@ -1706,14 +1706,14 @@ const ElfDumper = struct { }; defer ctx.deinit(); - while (br.seek < bytes.len) { - const hdr_seek = std.mem.alignForward(usize, br.seek, 2); - br.seek = hdr_seek; - const hdr = try br.takeStruct(elf.ar_hdr); + while (r.seek < bytes.len) { + const hdr_seek = std.mem.alignForward(usize, r.seek, 2); + r.seek = hdr_seek; + const hdr = try r.takeStruct(elf.ar_hdr); if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) return error.InvalidArchiveHeaderMagicNumber; - const data = try br.take(try hdr.size()); + const data = try r.take(try hdr.size()); if (hdr.isSymtab()) { try ctx.parseSymtab(data, .p32); @@ -1766,17 +1766,17 @@ const ElfDumper = struct { } fn parseSymtab(ctx: *ArchiveContext, data: []const u8, ptr_width: enum { p32, p64 }) !void { - var br: std.io.Reader = .fixed(data); + var r: std.io.Reader = .fixed(data); const num = switch (ptr_width) { - .p32 => try br.takeInt(u32, .big), - .p64 => try br.takeInt(u64, .big), + .p32 => try r.takeInt(u32, .big), + .p64 => try r.takeInt(u64, .big), }; const ptr_size: usize = switch (ptr_width) { .p32 => @sizeOf(u32), .p64 => @sizeOf(u64), }; - _ = try br.discard(.limited(num * ptr_size)); - const strtab = br.buffered(); + _ = try r.discard(.limited(num * ptr_size)); + const strtab = r.buffered(); assert(ctx.symtab.len == 0); ctx.symtab = try ctx.gpa.alloc(ArSymtabEntry, num); @@ -1784,8 +1784,8 @@ const ElfDumper = struct { var stroff: usize = 0; for (ctx.symtab) |*entry| { const off = switch (ptr_width) { - .p32 => try br.takeInt(u32, .big), - .p64 => try br.takeInt(u64, .big), + .p32 => try r.takeInt(u32, .big), + .p64 => try r.takeInt(u64, .big), }; const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(strtab[stroff..].ptr)), 0); stroff += name.len + 1; @@ -1836,9 +1836,9 @@ const ElfDumper = struct { fn parseAndDumpObject(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; - var br: std.io.Reader = .fixed(bytes); + var r: std.io.Reader = .fixed(bytes); - const hdr = try br.takeStruct(elf.Elf64_Ehdr); + const hdr = try r.takeStruct(elf.Elf64_Ehdr); if (!mem.eql(u8, hdr.e_ident[0..4], "\x7fELF")) return error.InvalidMagicNumber; const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(bytes[hdr.e_shoff..].ptr))[0..hdr.e_shnum]; @@ -2327,9 +2327,9 @@ const WasmDumper = struct { fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; - var br: std.io.Reader = .fixed(bytes); + var r: std.io.Reader = .fixed(bytes); - const buf = try br.takeArray(8); + const buf = try r.takeArray(8); if (!mem.eql(u8, buf[0..4], &std.wasm.magic)) return error.InvalidMagicByte; if (!mem.eql(u8, buf[4..8], &std.wasm.version)) return error.UnsupportedWasmVersion; @@ -2337,7 +2337,7 @@ const WasmDumper = struct { defer aw.deinit(); const bw = &aw.writer; - parseAndDumpInner(step, check, &br, bw) catch |err| switch (err) { + parseAndDumpInner(step, check, &r, bw) catch |err| switch (err) { error.EndOfStream => try bw.writeAll("\n"), else => |e| return e, }; @@ -2347,14 +2347,13 @@ const WasmDumper = struct { fn parseAndDumpInner( step: *Step, check: Check, - br: *std.io.Reader, + r: *std.io.Reader, bw: *Writer, ) !void { - var section_br: std.io.Reader = undefined; switch (check.kind) { - .headers => while (br.takeEnum(std.wasm.Section, .little)) |section| { - section_br = .fixed(try br.take(try br.takeLeb128(u32))); - try parseAndDumpSection(step, section, §ion_br, bw); + .headers => while (r.takeEnum(std.wasm.Section, .little)) |section| { + var section_reader: std.io.Reader = .fixed(try r.take(try r.takeLeb128(u32))); + try parseAndDumpSection(step, section, §ion_reader, bw); } else |err| switch (err) { error.InvalidEnumTag => return step.fail("invalid section id", .{}), error.EndOfStream => {}, @@ -2367,13 +2366,13 @@ const WasmDumper = struct { fn parseAndDumpSection( step: *Step, section: std.wasm.Section, - br: *std.io.Reader, + r: *std.io.Reader, bw: *Writer, ) !void { try bw.print( \\Section {s} \\size {d} - , .{ @tagName(section), br.buffer.len }); + , .{ @tagName(section), r.buffer.len }); switch (section) { .type, @@ -2387,63 +2386,63 @@ const WasmDumper = struct { .code, .data, => { - const entries = try br.takeLeb128(u32); + const entries = try r.takeLeb128(u32); try bw.print("\nentries {d}\n", .{entries}); - try parseSection(step, section, br, entries, bw); + try parseSection(step, section, r, entries, bw); }, .custom => { - const name = try br.take(try br.takeLeb128(u32)); + const name = try r.take(try r.takeLeb128(u32)); try bw.print("\nname {s}\n", .{name}); if (mem.eql(u8, name, "name")) { - try parseDumpNames(step, br, bw); + try parseDumpNames(step, r, bw); } else if (mem.eql(u8, name, "producers")) { - try parseDumpProducers(br, bw); + try parseDumpProducers(r, bw); } else if (mem.eql(u8, name, "target_features")) { - try parseDumpFeatures(br, bw); + try parseDumpFeatures(r, bw); } // TODO: Implement parsing and dumping other custom sections (such as relocations) }, .start => { - const start = try br.takeLeb128(u32); + const start = try r.takeLeb128(u32); try bw.print("\nstart {d}\n", .{start}); }, .data_count => { - const count = try br.takeLeb128(u32); + const count = try r.takeLeb128(u32); try bw.print("\ncount {d}\n", .{count}); }, else => {}, // skip unknown sections } } - fn parseSection(step: *Step, section: std.wasm.Section, br: *std.io.Reader, entries: u32, bw: *Writer) !void { + fn parseSection(step: *Step, section: std.wasm.Section, r: *std.io.Reader, entries: u32, bw: *Writer) !void { switch (section) { .type => { var i: u32 = 0; while (i < entries) : (i += 1) { - const func_type = try br.takeByte(); + const func_type = try r.takeByte(); if (func_type != std.wasm.function_type) { return step.fail("expected function type, found byte '{d}'", .{func_type}); } - const params = try br.takeLeb128(u32); + const params = try r.takeLeb128(u32); try bw.print("params {d}\n", .{params}); var index: u32 = 0; while (index < params) : (index += 1) { - _ = try parseDumpType(step, std.wasm.Valtype, br, bw); + _ = try parseDumpType(step, std.wasm.Valtype, r, bw); } else index = 0; - const returns = try br.takeLeb128(u32); + const returns = try r.takeLeb128(u32); try bw.print("returns {d}\n", .{returns}); while (index < returns) : (index += 1) { - _ = try parseDumpType(step, std.wasm.Valtype, br, bw); + _ = try parseDumpType(step, std.wasm.Valtype, r, bw); } } }, .import => { var i: u32 = 0; while (i < entries) : (i += 1) { - const module_name = try br.take(try br.takeLeb128(u32)); - const name = try br.take(try br.takeLeb128(u32)); - const kind = br.takeEnum(std.wasm.ExternalKind, .little) catch |err| switch (err) { + const module_name = try r.take(try r.takeLeb128(u32)); + const name = try r.take(try r.takeLeb128(u32)); + const kind = r.takeEnum(std.wasm.ExternalKind, .little) catch |err| switch (err) { error.InvalidEnumTag => return step.fail("invalid import kind", .{}), else => |e| return e, }; @@ -2455,15 +2454,15 @@ const WasmDumper = struct { , .{ module_name, name, @tagName(kind) }); try bw.writeByte('\n'); switch (kind) { - .function => try bw.print("index {d}\n", .{try br.takeLeb128(u32)}), - .memory => try parseDumpLimits(br, bw), + .function => try bw.print("index {d}\n", .{try r.takeLeb128(u32)}), + .memory => try parseDumpLimits(r, bw), .global => { - _ = try parseDumpType(step, std.wasm.Valtype, br, bw); - try bw.print("mutable {}\n", .{0x01 == try br.takeLeb128(u32)}); + _ = try parseDumpType(step, std.wasm.Valtype, r, bw); + try bw.print("mutable {}\n", .{0x01 == try r.takeLeb128(u32)}); }, .table => { - _ = try parseDumpType(step, std.wasm.RefType, br, bw); - try parseDumpLimits(br, bw); + _ = try parseDumpType(step, std.wasm.RefType, r, bw); + try parseDumpLimits(r, bw); }, } } @@ -2471,39 +2470,39 @@ const WasmDumper = struct { .function => { var i: u32 = 0; while (i < entries) : (i += 1) { - try bw.print("index {d}\n", .{try br.takeLeb128(u32)}); + try bw.print("index {d}\n", .{try r.takeLeb128(u32)}); } }, .table => { var i: u32 = 0; while (i < entries) : (i += 1) { - _ = try parseDumpType(step, std.wasm.RefType, br, bw); - try parseDumpLimits(br, bw); + _ = try parseDumpType(step, std.wasm.RefType, r, bw); + try parseDumpLimits(r, bw); } }, .memory => { var i: u32 = 0; while (i < entries) : (i += 1) { - try parseDumpLimits(br, bw); + try parseDumpLimits(r, bw); } }, .global => { var i: u32 = 0; while (i < entries) : (i += 1) { - _ = try parseDumpType(step, std.wasm.Valtype, br, bw); - try bw.print("mutable {}\n", .{0x01 == try br.takeLeb128(u1)}); - try parseDumpInit(step, br, bw); + _ = try parseDumpType(step, std.wasm.Valtype, r, bw); + try bw.print("mutable {}\n", .{0x01 == try r.takeLeb128(u1)}); + try parseDumpInit(step, r, bw); } }, .@"export" => { var i: u32 = 0; while (i < entries) : (i += 1) { - const name = try br.take(try br.takeLeb128(u32)); - const kind = br.takeEnum(std.wasm.ExternalKind, .little) catch |err| switch (err) { + const name = try r.take(try r.takeLeb128(u32)); + const kind = r.takeEnum(std.wasm.ExternalKind, .little) catch |err| switch (err) { error.InvalidEnumTag => return step.fail("invalid export kind value", .{}), else => |e| return e, }; - const index = try br.takeLeb128(u32); + const index = try r.takeLeb128(u32); try bw.print( \\name {s} \\kind {s} @@ -2515,14 +2514,14 @@ const WasmDumper = struct { .element => { var i: u32 = 0; while (i < entries) : (i += 1) { - try bw.print("table index {d}\n", .{try br.takeLeb128(u32)}); - try parseDumpInit(step, br, bw); + try bw.print("table index {d}\n", .{try r.takeLeb128(u32)}); + try parseDumpInit(step, r, bw); - const function_indexes = try br.takeLeb128(u32); + const function_indexes = try r.takeLeb128(u32); var function_index: u32 = 0; try bw.print("indexes {d}\n", .{function_indexes}); while (function_index < function_indexes) : (function_index += 1) { - try bw.print("index {d}\n", .{try br.takeLeb128(u32)}); + try bw.print("index {d}\n", .{try r.takeLeb128(u32)}); } } }, @@ -2534,21 +2533,21 @@ const WasmDumper = struct { passive: bool, memidx: bool, unused: u30, - } = @bitCast(try br.takeLeb128(u32)); - const index = if (flags.memidx) try br.takeLeb128(u32) else 0; + } = @bitCast(try r.takeLeb128(u32)); + const index = if (flags.memidx) try r.takeLeb128(u32) else 0; try bw.print("memory index 0x{x}\n", .{index}); - if (!flags.passive) try parseDumpInit(step, br, bw); - const size = try br.takeLeb128(u32); + if (!flags.passive) try parseDumpInit(step, r, bw); + const size = try r.takeLeb128(u32); try bw.print("size {d}\n", .{size}); - _ = try br.discard(.limited(size)); // we do not care about the content of the segments + _ = try r.discard(.limited(size)); // we do not care about the content of the segments } }, else => unreachable, } } - fn parseDumpType(step: *Step, comptime E: type, br: *std.io.Reader, bw: *Writer) !E { - const tag = br.takeEnum(E, .little) catch |err| switch (err) { + fn parseDumpType(step: *Step, comptime E: type, r: *std.io.Reader, bw: *Writer) !E { + const tag = r.takeEnum(E, .little) catch |err| switch (err) { error.InvalidEnumTag => return step.fail("invalid wasm type value", .{}), else => |e| return e, }; @@ -2556,42 +2555,42 @@ const WasmDumper = struct { return tag; } - fn parseDumpLimits(br: *std.io.Reader, bw: *Writer) !void { - const flags = try br.takeLeb128(u8); - const min = try br.takeLeb128(u32); + fn parseDumpLimits(r: *std.io.Reader, bw: *Writer) !void { + const flags = try r.takeLeb128(u8); + const min = try r.takeLeb128(u32); try bw.print("min {x}\n", .{min}); - if (flags != 0) try bw.print("max {x}\n", .{try br.takeLeb128(u32)}); + if (flags != 0) try bw.print("max {x}\n", .{try r.takeLeb128(u32)}); } - fn parseDumpInit(step: *Step, br: *std.io.Reader, bw: *Writer) !void { - const opcode = br.takeEnum(std.wasm.Opcode, .little) catch |err| switch (err) { + fn parseDumpInit(step: *Step, r: *std.io.Reader, bw: *Writer) !void { + const opcode = r.takeEnum(std.wasm.Opcode, .little) catch |err| switch (err) { error.InvalidEnumTag => return step.fail("invalid wasm opcode", .{}), else => |e| return e, }; switch (opcode) { - .i32_const => try bw.print("i32.const {x}\n", .{try br.takeLeb128(i32)}), - .i64_const => try bw.print("i64.const {x}\n", .{try br.takeLeb128(i64)}), - .f32_const => try bw.print("f32.const {x}\n", .{@as(f32, @bitCast(try br.takeInt(u32, .little)))}), - .f64_const => try bw.print("f64.const {x}\n", .{@as(f64, @bitCast(try br.takeInt(u64, .little)))}), - .global_get => try bw.print("global.get {x}\n", .{try br.takeLeb128(u32)}), + .i32_const => try bw.print("i32.const {x}\n", .{try r.takeLeb128(i32)}), + .i64_const => try bw.print("i64.const {x}\n", .{try r.takeLeb128(i64)}), + .f32_const => try bw.print("f32.const {x}\n", .{@as(f32, @bitCast(try r.takeInt(u32, .little)))}), + .f64_const => try bw.print("f64.const {x}\n", .{@as(f64, @bitCast(try r.takeInt(u64, .little)))}), + .global_get => try bw.print("global.get {x}\n", .{try r.takeLeb128(u32)}), else => unreachable, } - const end_opcode = try br.takeLeb128(u8); + const end_opcode = try r.takeLeb128(u8); if (end_opcode != @intFromEnum(std.wasm.Opcode.end)) { return step.fail("expected 'end' opcode in init expression", .{}); } } /// https://webassembly.github.io/spec/core/appendix/custom.html - fn parseDumpNames(step: *Step, br: *std.io.Reader, bw: *Writer) !void { + fn parseDumpNames(step: *Step, r: *std.io.Reader, bw: *Writer) !void { var subsection_br: std.io.Reader = undefined; - while (br.seek < br.buffer.len) { - switch (try parseDumpType(step, std.wasm.NameSubsection, br, bw)) { + while (r.seek < r.buffer.len) { + switch (try parseDumpType(step, std.wasm.NameSubsection, r, bw)) { // The module name subsection ... consists of a single name // that is assigned to the module itself. .module => { - subsection_br = .fixed(try br.take(try br.takeLeb128(u32))); + subsection_br = .fixed(try r.take(try r.takeLeb128(u32))); const name = try subsection_br.take(try subsection_br.takeLeb128(u32)); try bw.print( \\name {s} @@ -2603,15 +2602,15 @@ const WasmDumper = struct { // The function name subsection ... consists of a name map // assigning function names to function indices. .function, .global, .data_segment => { - subsection_br = .fixed(try br.take(try br.takeLeb128(u32))); - const entries = try br.takeLeb128(u32); + subsection_br = .fixed(try r.take(try r.takeLeb128(u32))); + const entries = try r.takeLeb128(u32); try bw.print( \\names {d} \\ , .{entries}); for (0..entries) |_| { - const index = try br.takeLeb128(u32); - const name = try br.take(try br.takeLeb128(u32)); + const index = try r.takeLeb128(u32); + const name = try r.take(try r.takeLeb128(u32)); try bw.print( \\index {d} \\name {s} @@ -2633,16 +2632,16 @@ const WasmDumper = struct { } } - fn parseDumpProducers(br: *std.io.Reader, bw: *Writer) !void { - const field_count = try br.takeLeb128(u32); + fn parseDumpProducers(r: *std.io.Reader, bw: *Writer) !void { + const field_count = try r.takeLeb128(u32); try bw.print( \\fields {d} \\ , .{field_count}); var current_field: u32 = 0; while (current_field < field_count) : (current_field += 1) { - const field_name = try br.take(try br.takeLeb128(u32)); - const value_count = try br.takeLeb128(u32); + const field_name = try r.take(try r.takeLeb128(u32)); + const value_count = try r.takeLeb128(u32); try bw.print( \\field_name {s} \\values {d} @@ -2650,8 +2649,8 @@ const WasmDumper = struct { , .{ field_name, value_count }); var current_value: u32 = 0; while (current_value < value_count) : (current_value += 1) { - const value = try br.take(try br.takeLeb128(u32)); - const version = try br.take(try br.takeLeb128(u32)); + const value = try r.take(try r.takeLeb128(u32)); + const version = try r.take(try r.takeLeb128(u32)); try bw.print( \\value_name {s} \\version {s} @@ -2661,8 +2660,8 @@ const WasmDumper = struct { } } - fn parseDumpFeatures(br: *std.io.Reader, bw: *Writer) !void { - const feature_count = try br.takeLeb128(u32); + fn parseDumpFeatures(r: *std.io.Reader, bw: *Writer) !void { + const feature_count = try r.takeLeb128(u32); try bw.print( \\features {d} \\ @@ -2670,8 +2669,8 @@ const WasmDumper = struct { var index: u32 = 0; while (index < feature_count) : (index += 1) { - const prefix_byte = try br.takeLeb128(u8); - const feature_name = try br.take(try br.takeLeb128(u32)); + const prefix_byte = try r.takeLeb128(u8); + const feature_name = try r.take(try r.takeLeb128(u32)); try bw.print( \\{c} {s} \\ diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index 680fdce60c..bd78eab655 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -1350,7 +1350,7 @@ pub const Reader = struct { }; var remaining = std.math.cast(u64, offset) orelse return seek_err; while (remaining > 0) { - const n = discard(&r.interface, .limited(remaining)) catch |err| { + const n = discard(&r.interface, .limited64(remaining)) catch |err| { r.seek_err = err; return err; }; @@ -1385,7 +1385,7 @@ pub const Reader = struct { const max_buffers_len = 16; fn stream(io_reader: *std.io.Reader, w: *std.io.Writer, limit: std.io.Limit) std.io.Reader.StreamError!usize { - const r: *Reader = @fieldParentPtr("interface", io_reader); + const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader)); switch (r.mode) { .positional, .streaming => return w.sendFile(r, limit) catch |write_err| switch (write_err) { error.Unimplemented => { @@ -1459,7 +1459,7 @@ pub const Reader = struct { } fn discard(io_reader: *std.io.Reader, limit: std.io.Limit) std.io.Reader.Error!usize { - const r: *Reader = @fieldParentPtr("interface", io_reader); + const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader)); const file = r.file; const pos = r.pos; switch (r.mode) { @@ -1661,7 +1661,7 @@ pub const Writer = struct { } pub fn drain(io_w: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { - const w: *Writer = @fieldParentPtr("interface", io_w); + const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w)); const handle = w.file.handle; const buffered = io_w.buffered(); if (is_windows) switch (w.mode) { @@ -1789,7 +1789,7 @@ pub const Writer = struct { file_reader: *Reader, limit: std.io.Limit, ) std.io.Writer.FileError!usize { - const w: *Writer = @fieldParentPtr("interface", io_w); + const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w)); const out_fd = w.file.handle; const in_fd = file_reader.file.handle; // TODO try using copy_file_range on FreeBSD diff --git a/lib/std/io.zig b/lib/std/io.zig index 0aa56470e4..5339318cd1 100644 --- a/lib/std/io.zig +++ b/lib/std/io.zig @@ -24,6 +24,12 @@ pub const Limit = enum(usize) { return @enumFromInt(n); } + /// Any value grater than `std.math.maxInt(usize)` is interpreted to mean + /// `.unlimited`. + pub fn limited64(n: u64) Limit { + return @enumFromInt(@min(n, std.math.maxInt(usize))); + } + pub fn countVec(data: []const []const u8) Limit { var total: usize = 0; for (data) |d| total += d.len; @@ -38,6 +44,10 @@ pub const Limit = enum(usize) { return @min(n, @intFromEnum(l)); } + pub fn minInt64(l: Limit, n: u64) usize { + return @min(n, @intFromEnum(l)); + } + pub fn slice(l: Limit, s: []u8) []u8 { return s[0..l.minInt(s.len)]; } diff --git a/lib/std/io/Writer.zig b/lib/std/io/Writer.zig index b1d1f78064..db9d780592 100644 --- a/lib/std/io/Writer.zig +++ b/lib/std/io/Writer.zig @@ -1958,7 +1958,7 @@ pub fn discardingSendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) F if (File.Handle == void) return error.Unimplemented; w.end = 0; if (file_reader.getSize()) |size| { - const n = limit.minInt(size - file_reader.pos); + const n = limit.minInt64(size - file_reader.pos); file_reader.seekBy(@intCast(n)) catch return error.Unimplemented; w.end = 0; return n; @@ -2256,7 +2256,7 @@ pub const Allocating = struct { defer setArrayList(a, list); const pos = file_reader.pos; const additional = if (file_reader.getSize()) |size| size - pos else |_| std.atomic.cache_line; - list.ensureUnusedCapacity(gpa, limit.minInt(additional)) catch return error.WriteFailed; + list.ensureUnusedCapacity(gpa, limit.minInt64(additional)) catch return error.WriteFailed; const dest = limit.slice(list.unusedCapacitySlice()); const n = file_reader.read(dest) catch |err| switch (err) { error.ReadFailed => return error.ReadFailed, diff --git a/lib/std/zig/system/linux.zig b/lib/std/zig/system/linux.zig index d8cff2403f..754b87d932 100644 --- a/lib/std/zig/system/linux.zig +++ b/lib/std/zig/system/linux.zig @@ -391,7 +391,7 @@ pub fn detectNativeCpuAndFeatures() ?Target.Cpu { const current_arch = builtin.cpu.arch; switch (current_arch) { .arm, .armeb, .thumb, .thumbeb => { - return ArmCpuinfoParser.parse(current_arch, f.reader()) catch null; + return ArmCpuinfoParser.parse(current_arch, f.deprecatedReader()) catch null; }, .aarch64, .aarch64_be => { const registers = [12]u64{ diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 26780fa940..b58d94e2a3 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1295,7 +1295,7 @@ pub const DeclGen = struct { } const ai = ty.arrayInfo(zcu); if (ai.elem_type.eql(.u8, zcu)) { - var literal: StringLiteral = .init(w, ty.arrayLenIncludingSentinel(zcu)); + var literal: StringLiteral = .init(w, @intCast(ty.arrayLenIncludingSentinel(zcu))); try literal.start(); var index: usize = 0; while (index < ai.len) : (index += 1) { @@ -1854,7 +1854,7 @@ pub const DeclGen = struct { const ai = ty.arrayInfo(zcu); if (ai.elem_type.eql(.u8, zcu)) { const c_len = ty.arrayLenIncludingSentinel(zcu); - var literal: StringLiteral = .init(w, c_len); + var literal: StringLiteral = .init(w, @intCast(c_len)); try literal.start(); var index: u64 = 0; while (index < c_len) : (index += 1) -- cgit v1.2.3 From e97a0ffb60b8137bd3eed88069c6cf6dbe1f9319 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 Jul 2025 14:53:48 -0700 Subject: std.Build.Step.CheckObject: mostly revert to master branch the updated code to use new std.io API is crashing --- lib/std/Build/Step/CheckObject.zig | 1170 +++++++++++++++++++----------------- 1 file changed, 634 insertions(+), 536 deletions(-) (limited to 'lib/std/Build/Step/CheckObject.zig') diff --git a/lib/std/Build/Step/CheckObject.zig b/lib/std/Build/Step/CheckObject.zig index 3d140d9969..06ad26ccc8 100644 --- a/lib/std/Build/Step/CheckObject.zig +++ b/lib/std/Build/Step/CheckObject.zig @@ -36,7 +36,7 @@ pub fn create( .makeFn = make, }), .source = source.dupe(owner), - .checks = .init(gpa), + .checks = std.ArrayList(Check).init(gpa), .obj_format = obj_format, }; check_object.source.addStepDependencies(&check_object.step); @@ -75,7 +75,7 @@ const Action = struct { b: *std.Build, step: *Step, haystack: []const u8, - global_vars: *std.StringHashMap(u64), + global_vars: anytype, ) !bool { assert(act.tag == .extract); const hay = mem.trim(u8, haystack, " "); @@ -154,11 +154,11 @@ const Action = struct { /// Will return true if the `phrase` is correctly parsed into an RPN program and /// its reduced, computed value compares using `op` with the expected value, either /// a literal or another extracted variable. - fn computeCmp(act: Action, b: *std.Build, step: *Step, global_vars: std.StringHashMap(u64)) !bool { + fn computeCmp(act: Action, b: *std.Build, step: *Step, global_vars: anytype) !bool { const gpa = step.owner.allocator; const phrase = act.phrase.resolve(b, step); - var op_stack: std.ArrayList(enum { add, sub, mod, mul }) = .init(gpa); - var values: std.ArrayList(u64) = .init(gpa); + var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa); + var values = std.ArrayList(u64).init(gpa); var it = mem.tokenizeScalar(u8, phrase, ' '); while (it.next()) |next| { @@ -230,11 +230,11 @@ const ComputeCompareExpected = struct { literal: u64, }, - pub fn format(value: ComputeCompareExpected, bw: *Writer) Writer.Error!void { - try bw.print("{s} ", .{@tagName(value.op)}); + pub fn format(value: ComputeCompareExpected, w: *Writer) Writer.Error!void { + try w.print("{t} ", .{value.op}); switch (value.value) { - .variable => |name| try bw.writeAll(name), - .literal => |x| try bw.print("{x}", .{x}), + .variable => |name| try w.writeAll(name), + .literal => |x| try w.print("{x}", .{x}), } } }; @@ -242,63 +242,56 @@ const ComputeCompareExpected = struct { const Check = struct { kind: Kind, payload: Payload, - allocator: Allocator, - data: std.ArrayListUnmanaged(u8), - actions: std.ArrayListUnmanaged(Action), + data: std.ArrayList(u8), + actions: std.ArrayList(Action), fn create(allocator: Allocator, kind: Kind) Check { return .{ .kind = kind, .payload = .{ .none = {} }, - .allocator = allocator, - .data = .empty, - .actions = .empty, + .data = std.ArrayList(u8).init(allocator), + .actions = std.ArrayList(Action).init(allocator), }; } - fn dumpSection(gpa: Allocator, name: [:0]const u8) Check { - var check = Check.create(gpa, .dump_section); + fn dumpSection(allocator: Allocator, name: [:0]const u8) Check { + var check = Check.create(allocator, .dump_section); const off: u32 = @intCast(check.data.items.len); - check.data.print(gpa, "{s}\x00", .{name}) catch @panic("OOM"); + check.data.writer().print("{s}\x00", .{name}) catch @panic("OOM"); check.payload = .{ .dump_section = off }; return check; } fn extract(check: *Check, phrase: SearchPhrase) void { - const gpa = check.allocator; - check.actions.append(gpa, .{ + check.actions.append(.{ .tag = .extract, .phrase = phrase, }) catch @panic("OOM"); } fn exact(check: *Check, phrase: SearchPhrase) void { - const gpa = check.allocator; - check.actions.append(gpa, .{ + check.actions.append(.{ .tag = .exact, .phrase = phrase, }) catch @panic("OOM"); } fn contains(check: *Check, phrase: SearchPhrase) void { - const gpa = check.allocator; - check.actions.append(gpa, .{ + check.actions.append(.{ .tag = .contains, .phrase = phrase, }) catch @panic("OOM"); } fn notPresent(check: *Check, phrase: SearchPhrase) void { - const gpa = check.allocator; - check.actions.append(gpa, .{ + check.actions.append(.{ .tag = .not_present, .phrase = phrase, }) catch @panic("OOM"); } fn computeCmp(check: *Check, phrase: SearchPhrase, expected: ComputeCompareExpected) void { - const gpa = check.allocator; - check.actions.append(gpa, .{ + check.actions.append(.{ .tag = .compute_cmp, .phrase = phrase, .expected = expected, @@ -751,14 +744,14 @@ const MachODumper = struct { }, .SYMTAB => { const lc = cmd.cast(macho.symtab_command).?; - const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(ctx.data[lc.symoff..].ptr))[0..lc.nsyms]; + const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(ctx.data.ptr + lc.symoff))[0..lc.nsyms]; const strtab = ctx.data[lc.stroff..][0..lc.strsize]; try ctx.symtab.appendUnalignedSlice(ctx.gpa, symtab); try ctx.strtab.appendSlice(ctx.gpa, strtab); }, .DYSYMTAB => { const lc = cmd.cast(macho.dysymtab_command).?; - const indexes = @as([*]align(1) const u32, @ptrCast(ctx.data[lc.indirectsymoff..].ptr))[0..lc.nindirectsyms]; + const indexes = @as([*]align(1) const u32, @ptrCast(ctx.data.ptr + lc.indirectsymoff))[0..lc.nindirectsyms]; try ctx.indsymtab.appendUnalignedSlice(ctx.gpa, indexes); }, .LOAD_DYLIB, @@ -776,7 +769,7 @@ const MachODumper = struct { fn getString(ctx: ObjectContext, off: u32) [:0]const u8 { assert(off < ctx.strtab.items.len); - return mem.sliceTo(@as([*:0]const u8, @ptrCast(ctx.strtab.items[off..].ptr)), 0); + return mem.sliceTo(@as([*:0]const u8, @ptrCast(ctx.strtab.items.ptr + off)), 0); } fn getLoadCommandIterator(ctx: ObjectContext) macho.LoadCommandIterator { @@ -806,7 +799,7 @@ const MachODumper = struct { return null; } - fn dumpHeader(hdr: macho.mach_header_64, bw: *Writer) !void { + fn dumpHeader(hdr: macho.mach_header_64, writer: anytype) !void { const cputype = switch (hdr.cputype) { macho.CPU_TYPE_ARM64 => "ARM64", macho.CPU_TYPE_X86_64 => "X86_64", @@ -827,7 +820,7 @@ const MachODumper = struct { else => "Unknown", }; - try bw.print( + try writer.print( \\header \\cputype {s} \\filetype {s} @@ -842,41 +835,41 @@ const MachODumper = struct { }); if (hdr.flags > 0) { - if (hdr.flags & macho.MH_NOUNDEFS != 0) try bw.writeAll(" NOUNDEFS"); - if (hdr.flags & macho.MH_INCRLINK != 0) try bw.writeAll(" INCRLINK"); - if (hdr.flags & macho.MH_DYLDLINK != 0) try bw.writeAll(" DYLDLINK"); - if (hdr.flags & macho.MH_BINDATLOAD != 0) try bw.writeAll(" BINDATLOAD"); - if (hdr.flags & macho.MH_PREBOUND != 0) try bw.writeAll(" PREBOUND"); - if (hdr.flags & macho.MH_SPLIT_SEGS != 0) try bw.writeAll(" SPLIT_SEGS"); - if (hdr.flags & macho.MH_LAZY_INIT != 0) try bw.writeAll(" LAZY_INIT"); - if (hdr.flags & macho.MH_TWOLEVEL != 0) try bw.writeAll(" TWOLEVEL"); - if (hdr.flags & macho.MH_FORCE_FLAT != 0) try bw.writeAll(" FORCE_FLAT"); - if (hdr.flags & macho.MH_NOMULTIDEFS != 0) try bw.writeAll(" NOMULTIDEFS"); - if (hdr.flags & macho.MH_NOFIXPREBINDING != 0) try bw.writeAll(" NOFIXPREBINDING"); - if (hdr.flags & macho.MH_PREBINDABLE != 0) try bw.writeAll(" PREBINDABLE"); - if (hdr.flags & macho.MH_ALLMODSBOUND != 0) try bw.writeAll(" ALLMODSBOUND"); - if (hdr.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) try bw.writeAll(" SUBSECTIONS_VIA_SYMBOLS"); - if (hdr.flags & macho.MH_CANONICAL != 0) try bw.writeAll(" CANONICAL"); - if (hdr.flags & macho.MH_WEAK_DEFINES != 0) try bw.writeAll(" WEAK_DEFINES"); - if (hdr.flags & macho.MH_BINDS_TO_WEAK != 0) try bw.writeAll(" BINDS_TO_WEAK"); - if (hdr.flags & macho.MH_ALLOW_STACK_EXECUTION != 0) try bw.writeAll(" ALLOW_STACK_EXECUTION"); - if (hdr.flags & macho.MH_ROOT_SAFE != 0) try bw.writeAll(" ROOT_SAFE"); - if (hdr.flags & macho.MH_SETUID_SAFE != 0) try bw.writeAll(" SETUID_SAFE"); - if (hdr.flags & macho.MH_NO_REEXPORTED_DYLIBS != 0) try bw.writeAll(" NO_REEXPORTED_DYLIBS"); - if (hdr.flags & macho.MH_PIE != 0) try bw.writeAll(" PIE"); - if (hdr.flags & macho.MH_DEAD_STRIPPABLE_DYLIB != 0) try bw.writeAll(" DEAD_STRIPPABLE_DYLIB"); - if (hdr.flags & macho.MH_HAS_TLV_DESCRIPTORS != 0) try bw.writeAll(" HAS_TLV_DESCRIPTORS"); - if (hdr.flags & macho.MH_NO_HEAP_EXECUTION != 0) try bw.writeAll(" NO_HEAP_EXECUTION"); - if (hdr.flags & macho.MH_APP_EXTENSION_SAFE != 0) try bw.writeAll(" APP_EXTENSION_SAFE"); - if (hdr.flags & macho.MH_NLIST_OUTOFSYNC_WITH_DYLDINFO != 0) try bw.writeAll(" NLIST_OUTOFSYNC_WITH_DYLDINFO"); + if (hdr.flags & macho.MH_NOUNDEFS != 0) try writer.writeAll(" NOUNDEFS"); + if (hdr.flags & macho.MH_INCRLINK != 0) try writer.writeAll(" INCRLINK"); + if (hdr.flags & macho.MH_DYLDLINK != 0) try writer.writeAll(" DYLDLINK"); + if (hdr.flags & macho.MH_BINDATLOAD != 0) try writer.writeAll(" BINDATLOAD"); + if (hdr.flags & macho.MH_PREBOUND != 0) try writer.writeAll(" PREBOUND"); + if (hdr.flags & macho.MH_SPLIT_SEGS != 0) try writer.writeAll(" SPLIT_SEGS"); + if (hdr.flags & macho.MH_LAZY_INIT != 0) try writer.writeAll(" LAZY_INIT"); + if (hdr.flags & macho.MH_TWOLEVEL != 0) try writer.writeAll(" TWOLEVEL"); + if (hdr.flags & macho.MH_FORCE_FLAT != 0) try writer.writeAll(" FORCE_FLAT"); + if (hdr.flags & macho.MH_NOMULTIDEFS != 0) try writer.writeAll(" NOMULTIDEFS"); + if (hdr.flags & macho.MH_NOFIXPREBINDING != 0) try writer.writeAll(" NOFIXPREBINDING"); + if (hdr.flags & macho.MH_PREBINDABLE != 0) try writer.writeAll(" PREBINDABLE"); + if (hdr.flags & macho.MH_ALLMODSBOUND != 0) try writer.writeAll(" ALLMODSBOUND"); + if (hdr.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) try writer.writeAll(" SUBSECTIONS_VIA_SYMBOLS"); + if (hdr.flags & macho.MH_CANONICAL != 0) try writer.writeAll(" CANONICAL"); + if (hdr.flags & macho.MH_WEAK_DEFINES != 0) try writer.writeAll(" WEAK_DEFINES"); + if (hdr.flags & macho.MH_BINDS_TO_WEAK != 0) try writer.writeAll(" BINDS_TO_WEAK"); + if (hdr.flags & macho.MH_ALLOW_STACK_EXECUTION != 0) try writer.writeAll(" ALLOW_STACK_EXECUTION"); + if (hdr.flags & macho.MH_ROOT_SAFE != 0) try writer.writeAll(" ROOT_SAFE"); + if (hdr.flags & macho.MH_SETUID_SAFE != 0) try writer.writeAll(" SETUID_SAFE"); + if (hdr.flags & macho.MH_NO_REEXPORTED_DYLIBS != 0) try writer.writeAll(" NO_REEXPORTED_DYLIBS"); + if (hdr.flags & macho.MH_PIE != 0) try writer.writeAll(" PIE"); + if (hdr.flags & macho.MH_DEAD_STRIPPABLE_DYLIB != 0) try writer.writeAll(" DEAD_STRIPPABLE_DYLIB"); + if (hdr.flags & macho.MH_HAS_TLV_DESCRIPTORS != 0) try writer.writeAll(" HAS_TLV_DESCRIPTORS"); + if (hdr.flags & macho.MH_NO_HEAP_EXECUTION != 0) try writer.writeAll(" NO_HEAP_EXECUTION"); + if (hdr.flags & macho.MH_APP_EXTENSION_SAFE != 0) try writer.writeAll(" APP_EXTENSION_SAFE"); + if (hdr.flags & macho.MH_NLIST_OUTOFSYNC_WITH_DYLDINFO != 0) try writer.writeAll(" NLIST_OUTOFSYNC_WITH_DYLDINFO"); } - try bw.writeByte('\n'); + try writer.writeByte('\n'); } - fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, bw: *Writer) !void { + fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void { // print header first - try bw.print( + try writer.print( \\LC {d} \\cmd {s} \\cmdsize {d} @@ -885,8 +878,8 @@ const MachODumper = struct { switch (lc.cmd()) { .SEGMENT_64 => { const seg = lc.cast(macho.segment_command_64).?; - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\segname {s} \\vmaddr {x} \\vmsize {x} @@ -901,8 +894,8 @@ const MachODumper = struct { }); for (lc.getSections()) |sect| { - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\sectname {s} \\addr {x} \\size {x} @@ -924,8 +917,8 @@ const MachODumper = struct { .REEXPORT_DYLIB, => { const dylib = lc.cast(macho.dylib_command).?; - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\name {s} \\timestamp {d} \\current version {x} @@ -940,16 +933,16 @@ const MachODumper = struct { .MAIN => { const main = lc.cast(macho.entry_point_command).?; - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\entryoff {x} \\stacksize {x} , .{ main.entryoff, main.stacksize }); }, .RPATH => { - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\path {s} , .{ lc.getRpathPathName(), @@ -958,8 +951,8 @@ const MachODumper = struct { .UUID => { const uuid = lc.cast(macho.uuid_command).?; - try bw.writeByte('\n'); - try bw.print("uuid {x}", .{&uuid.uuid}); + try writer.writeByte('\n'); + try writer.print("uuid {x}", .{&uuid.uuid}); }, .DATA_IN_CODE, @@ -967,8 +960,8 @@ const MachODumper = struct { .CODE_SIGNATURE, => { const llc = lc.cast(macho.linkedit_data_command).?; - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\dataoff {x} \\datasize {x} , .{ llc.dataoff, llc.datasize }); @@ -976,8 +969,8 @@ const MachODumper = struct { .DYLD_INFO_ONLY => { const dlc = lc.cast(macho.dyld_info_command).?; - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\rebaseoff {x} \\rebasesize {x} \\bindoff {x} @@ -1004,8 +997,8 @@ const MachODumper = struct { .SYMTAB => { const slc = lc.cast(macho.symtab_command).?; - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\symoff {x} \\nsyms {x} \\stroff {x} @@ -1020,8 +1013,8 @@ const MachODumper = struct { .DYSYMTAB => { const dlc = lc.cast(macho.dysymtab_command).?; - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\ilocalsym {x} \\nlocalsym {x} \\iextdefsym {x} @@ -1044,8 +1037,8 @@ const MachODumper = struct { .BUILD_VERSION => { const blc = lc.cast(macho.build_version_command).?; - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\platform {s} \\minos {d}.{d}.{d} \\sdk {d}.{d}.{d} @@ -1061,12 +1054,12 @@ const MachODumper = struct { blc.ntools, }); for (lc.getBuildVersionTools()) |tool| { - try bw.writeByte('\n'); + try writer.writeByte('\n'); switch (tool.tool) { - .CLANG, .SWIFT, .LD, .LLD, .ZIG => try bw.print("tool {s}\n", .{@tagName(tool.tool)}), - else => |x| try bw.print("tool {d}\n", .{@intFromEnum(x)}), + .CLANG, .SWIFT, .LD, .LLD, .ZIG => try writer.print("tool {s}\n", .{@tagName(tool.tool)}), + else => |x| try writer.print("tool {d}\n", .{@intFromEnum(x)}), } - try bw.print( + try writer.print( \\version {d}.{d}.{d} , .{ tool.version >> 16, @@ -1082,8 +1075,8 @@ const MachODumper = struct { .VERSION_MIN_TVOS, => { const vlc = lc.cast(macho.version_min_command).?; - try bw.writeByte('\n'); - try bw.print( + try writer.writeByte('\n'); + try writer.print( \\version {d}.{d}.{d} \\sdk {d}.{d}.{d} , .{ @@ -1100,8 +1093,8 @@ const MachODumper = struct { } } - fn dumpSymtab(ctx: ObjectContext, bw: *Writer) !void { - try bw.writeAll(symtab_label ++ "\n"); + fn dumpSymtab(ctx: ObjectContext, writer: anytype) !void { + try writer.writeAll(symtab_label ++ "\n"); for (ctx.symtab.items) |sym| { const sym_name = ctx.getString(sym.n_strx); @@ -1116,32 +1109,32 @@ const MachODumper = struct { macho.N_STSYM => "STSYM", else => "UNKNOWN STAB", }; - try bw.print("{x}", .{sym.n_value}); + try writer.print("{x}", .{sym.n_value}); if (sym.n_sect > 0) { const sect = ctx.sections.items[sym.n_sect - 1]; - try bw.print(" ({s},{s})", .{ sect.segName(), sect.sectName() }); + try writer.print(" ({s},{s})", .{ sect.segName(), sect.sectName() }); } - try bw.print(" {s} (stab) {s}\n", .{ tt, sym_name }); + try writer.print(" {s} (stab) {s}\n", .{ tt, sym_name }); } else if (sym.sect()) { const sect = ctx.sections.items[sym.n_sect - 1]; - try bw.print("{x} ({s},{s})", .{ + try writer.print("{x} ({s},{s})", .{ sym.n_value, sect.segName(), sect.sectName(), }); - if (sym.n_desc & macho.REFERENCED_DYNAMICALLY != 0) try bw.writeAll(" [referenced dynamically]"); - if (sym.weakDef()) try bw.writeAll(" weak"); - if (sym.weakRef()) try bw.writeAll(" weakref"); + if (sym.n_desc & macho.REFERENCED_DYNAMICALLY != 0) try writer.writeAll(" [referenced dynamically]"); + if (sym.weakDef()) try writer.writeAll(" weak"); + if (sym.weakRef()) try writer.writeAll(" weakref"); if (sym.ext()) { - if (sym.pext()) try bw.writeAll(" private"); - try bw.writeAll(" external"); - } else if (sym.pext()) try bw.writeAll(" (was private external)"); - try bw.print(" {s}\n", .{sym_name}); + if (sym.pext()) try writer.writeAll(" private"); + try writer.writeAll(" external"); + } else if (sym.pext()) try writer.writeAll(" (was private external)"); + try writer.print(" {s}\n", .{sym_name}); } else if (sym.tentative()) { const alignment = (sym.n_desc >> 8) & 0x0F; - try bw.print(" 0x{x:0>16} (common) (alignment 2^{d})", .{ sym.n_value, alignment }); - if (sym.ext()) try bw.writeAll(" external"); - try bw.print(" {s}\n", .{sym_name}); + try writer.print(" 0x{x:0>16} (common) (alignment 2^{d})", .{ sym.n_value, alignment }); + if (sym.ext()) try writer.writeAll(" external"); + try writer.print(" {s}\n", .{sym_name}); } else if (sym.undf()) { const ordinal = @divFloor(@as(i16, @bitCast(sym.n_desc)), macho.N_SYMBOL_RESOLVER); const import_name = blk: { @@ -1160,10 +1153,10 @@ const MachODumper = struct { const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len; break :blk basename[0..ext]; }; - try bw.writeAll("(undefined)"); - if (sym.weakRef()) try bw.writeAll(" weakref"); - if (sym.ext()) try bw.writeAll(" external"); - try bw.print(" {s} (from {s})\n", .{ + try writer.writeAll("(undefined)"); + if (sym.weakRef()) try writer.writeAll(" weakref"); + if (sym.ext()) try writer.writeAll(" external"); + try writer.print(" {s} (from {s})\n", .{ sym_name, import_name, }); @@ -1171,8 +1164,8 @@ const MachODumper = struct { } } - fn dumpIndirectSymtab(ctx: ObjectContext, bw: *Writer) !void { - try bw.writeAll(indirect_symtab_label ++ "\n"); + fn dumpIndirectSymtab(ctx: ObjectContext, writer: anytype) !void { + try writer.writeAll(indirect_symtab_label ++ "\n"); var sects_buffer: [3]macho.section_64 = undefined; const sects = blk: { @@ -1210,33 +1203,35 @@ const MachODumper = struct { break :blk @sizeOf(u64); }; - try bw.print("{s},{s}\n", .{ sect.segName(), sect.sectName() }); - try bw.print("nentries {d}\n", .{end - start}); + try writer.print("{s},{s}\n", .{ sect.segName(), sect.sectName() }); + try writer.print("nentries {d}\n", .{end - start}); for (ctx.indsymtab.items[start..end], 0..) |index, j| { const sym = ctx.symtab.items[index]; const addr = sect.addr + entry_size * j; - try bw.print("0x{x} {d} {s}\n", .{ addr, index, ctx.getString(sym.n_strx) }); + try writer.print("0x{x} {d} {s}\n", .{ addr, index, ctx.getString(sym.n_strx) }); } } } - fn dumpRebaseInfo(ctx: ObjectContext, data: []const u8, bw: *Writer) !void { - var rebases: std.ArrayList(u64) = .init(ctx.gpa); + fn dumpRebaseInfo(ctx: ObjectContext, data: []const u8, writer: anytype) !void { + var rebases = std.ArrayList(u64).init(ctx.gpa); defer rebases.deinit(); try ctx.parseRebaseInfo(data, &rebases); mem.sort(u64, rebases.items, {}, std.sort.asc(u64)); for (rebases.items) |addr| { - try bw.print("0x{x}\n", .{addr}); + try writer.print("0x{x}\n", .{addr}); } } fn parseRebaseInfo(ctx: ObjectContext, data: []const u8, rebases: *std.ArrayList(u64)) !void { - var r: std.io.Reader = .fixed(data); + var stream = std.io.fixedBufferStream(data); + var creader = std.io.countingReader(stream.reader()); + const reader = creader.reader(); var seg_id: ?u8 = null; var offset: u64 = 0; while (true) { - const byte = r.takeByte() catch break; + const byte = reader.readByte() catch break; const opc = byte & macho.REBASE_OPCODE_MASK; const imm = byte & macho.REBASE_IMMEDIATE_MASK; switch (opc) { @@ -1244,17 +1239,17 @@ const MachODumper = struct { macho.REBASE_OPCODE_SET_TYPE_IMM => {}, macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => { seg_id = imm; - offset = try r.takeLeb128(u64); + offset = try std.leb.readUleb128(u64, reader); }, macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED => { offset += imm * @sizeOf(u64); }, macho.REBASE_OPCODE_ADD_ADDR_ULEB => { - const addend = try r.takeLeb128(u64); + const addend = try std.leb.readUleb128(u64, reader); offset += addend; }, macho.REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB => { - const addend = try r.takeLeb128(u64); + const addend = try std.leb.readUleb128(u64, reader); const seg = ctx.segments.items[seg_id.?]; const addr = seg.vmaddr + offset; try rebases.append(addr); @@ -1271,11 +1266,11 @@ const MachODumper = struct { ntimes = imm; }, macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES => { - ntimes = try r.takeLeb128(u64); + ntimes = try std.leb.readUleb128(u64, reader); }, macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB => { - ntimes = try r.takeLeb128(u64); - skip = try r.takeLeb128(u64); + ntimes = try std.leb.readUleb128(u64, reader); + skip = try std.leb.readUleb128(u64, reader); }, else => unreachable, } @@ -1317,8 +1312,8 @@ const MachODumper = struct { }; }; - fn dumpBindInfo(ctx: ObjectContext, data: []const u8, bw: *Writer) !void { - var bindings: std.ArrayList(Binding) = .init(ctx.gpa); + fn dumpBindInfo(ctx: ObjectContext, data: []const u8, writer: anytype) !void { + var bindings = std.ArrayList(Binding).init(ctx.gpa); defer { for (bindings.items) |*b| { b.deinit(ctx.gpa); @@ -1328,20 +1323,22 @@ const MachODumper = struct { try ctx.parseBindInfo(data, &bindings); mem.sort(Binding, bindings.items, {}, Binding.lessThan); for (bindings.items) |binding| { - try bw.print("0x{x} [addend: {d}]", .{ binding.address, binding.addend }); - try bw.writeAll(" ("); + try writer.print("0x{x} [addend: {d}]", .{ binding.address, binding.addend }); + try writer.writeAll(" ("); switch (binding.tag) { - .self => try bw.writeAll("self"), - .exe => try bw.writeAll("main executable"), - .flat => try bw.writeAll("flat lookup"), - .ord => try bw.writeAll(std.fs.path.basename(ctx.imports.items[binding.ordinal - 1])), + .self => try writer.writeAll("self"), + .exe => try writer.writeAll("main executable"), + .flat => try writer.writeAll("flat lookup"), + .ord => try writer.writeAll(std.fs.path.basename(ctx.imports.items[binding.ordinal - 1])), } - try bw.print(") {s}\n", .{binding.name}); + try writer.print(") {s}\n", .{binding.name}); } } fn parseBindInfo(ctx: ObjectContext, data: []const u8, bindings: *std.ArrayList(Binding)) !void { - var r: std.io.Reader = .fixed(data); + var stream = std.io.fixedBufferStream(data); + var creader = std.io.countingReader(stream.reader()); + const reader = creader.reader(); var seg_id: ?u8 = null; var tag: Binding.Tag = .self; @@ -1349,10 +1346,11 @@ const MachODumper = struct { var offset: u64 = 0; var addend: i64 = 0; - var name_buf: std.io.Writer.Allocating = .init(ctx.gpa); + var name_buf = std.ArrayList(u8).init(ctx.gpa); defer name_buf.deinit(); - while (r.takeByte()) |byte| { + while (true) { + const byte = reader.readByte() catch break; const opc = byte & macho.BIND_OPCODE_MASK; const imm = byte & macho.BIND_IMMEDIATE_MASK; switch (opc) { @@ -1373,18 +1371,18 @@ const MachODumper = struct { }, macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => { seg_id = imm; - offset = try r.takeLeb128(u64); + offset = try std.leb.readUleb128(u64, reader); }, macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => { name_buf.clearRetainingCapacity(); - _ = try r.streamDelimiterLimit(&name_buf.writer, 0, .limited(std.math.maxInt(u32))); - try name_buf.writer.writeByte(0); + try reader.readUntilDelimiterArrayList(&name_buf, 0, std.math.maxInt(u32)); + try name_buf.append(0); }, macho.BIND_OPCODE_SET_ADDEND_SLEB => { - addend = try r.takeLeb128(i64); + addend = try std.leb.readIleb128(i64, reader); }, macho.BIND_OPCODE_ADD_ADDR_ULEB => { - const x = try r.takeLeb128(u64); + const x = try std.leb.readUleb128(u64, reader); offset = @intCast(@as(i64, @intCast(offset)) + @as(i64, @bitCast(x))); }, macho.BIND_OPCODE_DO_BIND, @@ -1399,14 +1397,14 @@ const MachODumper = struct { switch (opc) { macho.BIND_OPCODE_DO_BIND => {}, macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB => { - add_addr = try r.takeLeb128(u64); + add_addr = try std.leb.readUleb128(u64, reader); }, macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED => { add_addr = imm * @sizeOf(u64); }, macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB => { - count = try r.takeLeb128(u64); - skip = try r.takeLeb128(u64); + count = try std.leb.readUleb128(u64, reader); + skip = try std.leb.readUleb128(u64, reader); }, else => unreachable, } @@ -1420,25 +1418,25 @@ const MachODumper = struct { .addend = addend, .tag = tag, .ordinal = ordinal, - .name = try ctx.gpa.dupe(u8, name_buf.getWritten()), + .name = try ctx.gpa.dupe(u8, name_buf.items), }); offset += skip + @sizeOf(u64) + add_addr; } }, else => break, } - } else |_| {} + } } - fn dumpExportsTrie(ctx: ObjectContext, data: []const u8, bw: *Writer) !void { + fn dumpExportsTrie(ctx: ObjectContext, data: []const u8, writer: anytype) !void { const seg = ctx.getSegmentByName("__TEXT") orelse return; var arena = std.heap.ArenaAllocator.init(ctx.gpa); defer arena.deinit(); - var exports: std.ArrayList(Export) = .init(arena.allocator()); - var r: std.io.Reader = .fixed(data); - try parseTrieNode(arena.allocator(), &r, "", &exports); + var exports = std.ArrayList(Export).init(arena.allocator()); + var it = TrieIterator{ .data = data }; + try parseTrieNode(arena.allocator(), &it, "", &exports); mem.sort(Export, exports.items, {}, Export.lessThan); @@ -1447,26 +1445,66 @@ const MachODumper = struct { .@"export" => { const info = exp.data.@"export"; if (info.kind != .regular or info.weak) { - try bw.writeByte('['); + try writer.writeByte('['); } switch (info.kind) { .regular => {}, - .absolute => try bw.writeAll("ABS, "), - .tlv => try bw.writeAll("THREAD_LOCAL, "), + .absolute => try writer.writeAll("ABS, "), + .tlv => try writer.writeAll("THREAD_LOCAL, "), } - if (info.weak) try bw.writeAll("WEAK"); + if (info.weak) try writer.writeAll("WEAK"); if (info.kind != .regular or info.weak) { - try bw.writeAll("] "); + try writer.writeAll("] "); } - try bw.print("{x} ", .{seg.vmaddr + info.vmoffset}); + try writer.print("{x} ", .{seg.vmaddr + info.vmoffset}); }, else => {}, } - try bw.print("{s}\n", .{exp.name}); + try writer.print("{s}\n", .{exp.name}); } } + const TrieIterator = struct { + data: []const u8, + pos: usize = 0, + + fn getStream(it: *TrieIterator) std.io.FixedBufferStream([]const u8) { + return std.io.fixedBufferStream(it.data[it.pos..]); + } + + fn readUleb128(it: *TrieIterator) !u64 { + var stream = it.getStream(); + var creader = std.io.countingReader(stream.reader()); + const reader = creader.reader(); + const value = try std.leb.readUleb128(u64, reader); + it.pos += creader.bytes_read; + return value; + } + + fn readString(it: *TrieIterator) ![:0]const u8 { + var stream = it.getStream(); + const reader = stream.reader(); + + var count: usize = 0; + while (true) : (count += 1) { + const byte = try reader.readByte(); + if (byte == 0) break; + } + + const str = @as([*:0]const u8, @ptrCast(it.data.ptr + it.pos))[0..count :0]; + it.pos += count + 1; + return str; + } + + fn readByte(it: *TrieIterator) !u8 { + var stream = it.getStream(); + const value = try stream.reader().readByte(); + it.pos += 1; + return value; + } + }; + const Export = struct { name: []const u8, tag: enum { @"export", reexport, stub_resolver }, @@ -1506,17 +1544,17 @@ const MachODumper = struct { fn parseTrieNode( arena: Allocator, - r: *std.io.Reader, + it: *TrieIterator, prefix: []const u8, exports: *std.ArrayList(Export), ) !void { - const size = try r.takeLeb128(u64); + const size = try it.readUleb128(); if (size > 0) { - const flags = try r.takeLeb128(u8); + const flags = try it.readUleb128(); switch (flags) { macho.EXPORT_SYMBOL_FLAGS_REEXPORT => { - const ord = try r.takeLeb128(u64); - const name = try r.takeSentinel(0); + const ord = try it.readUleb128(); + const name = try arena.dupe(u8, try it.readString()); try exports.append(.{ .name = if (name.len > 0) name else prefix, .tag = .reexport, @@ -1524,8 +1562,8 @@ const MachODumper = struct { }); }, macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER => { - const stub_offset = try r.takeLeb128(u64); - const resolver_offset = try r.takeLeb128(u64); + const stub_offset = try it.readUleb128(); + const resolver_offset = try it.readUleb128(); try exports.append(.{ .name = prefix, .tag = .stub_resolver, @@ -1536,7 +1574,7 @@ const MachODumper = struct { }); }, else => { - const vmoff = try r.takeLeb128(u64); + const vmoff = try it.readUleb128(); try exports.append(.{ .name = prefix, .tag = .@"export", @@ -1555,21 +1593,21 @@ const MachODumper = struct { } } - const nedges = try r.takeByte(); + const nedges = try it.readByte(); for (0..nedges) |_| { - const label = try r.takeSentinel(0); - const off = try r.takeLeb128(usize); + const label = try it.readString(); + const off = try it.readUleb128(); const prefix_label = try std.fmt.allocPrint(arena, "{s}{s}", .{ prefix, label }); - const seek = r.seek; - r.seek = off; - try parseTrieNode(arena, r, prefix_label, exports); - r.seek = seek; + const curr = it.pos; + it.pos = off; + try parseTrieNode(arena, it, prefix_label, exports); + it.pos = curr; } } - fn dumpSection(ctx: ObjectContext, sect: macho.section_64, bw: *Writer) !void { + fn dumpSection(ctx: ObjectContext, sect: macho.section_64, writer: anytype) !void { const data = ctx.data[sect.offset..][0..sect.size]; - try bw.print("{s}", .{data}); + try writer.print("{s}", .{data}); } }; @@ -1583,30 +1621,29 @@ const MachODumper = struct { var ctx = ObjectContext{ .gpa = gpa, .data = bytes, .header = hdr }; try ctx.parse(); - var aw: std.io.Writer.Allocating = .init(gpa); - defer aw.deinit(); - const bw = &aw.writer; + var output = std.ArrayList(u8).init(gpa); + const writer = output.writer(); switch (check.kind) { .headers => { - try ObjectContext.dumpHeader(ctx.header, bw); + try ObjectContext.dumpHeader(ctx.header, writer); var it = ctx.getLoadCommandIterator(); var i: usize = 0; while (it.next()) |cmd| { - try ObjectContext.dumpLoadCommand(cmd, i, bw); - try bw.writeByte('\n'); + try ObjectContext.dumpLoadCommand(cmd, i, writer); + try writer.writeByte('\n'); i += 1; } }, .symtab => if (ctx.symtab.items.len > 0) { - try ctx.dumpSymtab(bw); + try ctx.dumpSymtab(writer); } else return step.fail("no symbol table found", .{}), .indirect_symtab => if (ctx.symtab.items.len > 0 and ctx.indsymtab.items.len > 0) { - try ctx.dumpIndirectSymtab(bw); + try ctx.dumpIndirectSymtab(writer); } else return step.fail("no indirect symbol table found", .{}), .dyld_rebase, @@ -1621,26 +1658,26 @@ const MachODumper = struct { switch (check.kind) { .dyld_rebase => if (lc.rebase_size > 0) { const data = ctx.data[lc.rebase_off..][0..lc.rebase_size]; - try bw.writeAll(dyld_rebase_label ++ "\n"); - try ctx.dumpRebaseInfo(data, bw); + try writer.writeAll(dyld_rebase_label ++ "\n"); + try ctx.dumpRebaseInfo(data, writer); } else return step.fail("no rebase data found", .{}), .dyld_bind => if (lc.bind_size > 0) { const data = ctx.data[lc.bind_off..][0..lc.bind_size]; - try bw.writeAll(dyld_bind_label ++ "\n"); - try ctx.dumpBindInfo(data, bw); + try writer.writeAll(dyld_bind_label ++ "\n"); + try ctx.dumpBindInfo(data, writer); } else return step.fail("no bind data found", .{}), .dyld_weak_bind => if (lc.weak_bind_size > 0) { const data = ctx.data[lc.weak_bind_off..][0..lc.weak_bind_size]; - try bw.writeAll(dyld_weak_bind_label ++ "\n"); - try ctx.dumpBindInfo(data, bw); + try writer.writeAll(dyld_weak_bind_label ++ "\n"); + try ctx.dumpBindInfo(data, writer); } else return step.fail("no weak bind data found", .{}), .dyld_lazy_bind => if (lc.lazy_bind_size > 0) { const data = ctx.data[lc.lazy_bind_off..][0..lc.lazy_bind_size]; - try bw.writeAll(dyld_lazy_bind_label ++ "\n"); - try ctx.dumpBindInfo(data, bw); + try writer.writeAll(dyld_lazy_bind_label ++ "\n"); + try ctx.dumpBindInfo(data, writer); } else return step.fail("no lazy bind data found", .{}), else => unreachable, @@ -1652,8 +1689,8 @@ const MachODumper = struct { const lc = cmd.cast(macho.dyld_info_command).?; if (lc.export_size > 0) { const data = ctx.data[lc.export_off..][0..lc.export_size]; - try bw.writeAll(exports_label ++ "\n"); - try ctx.dumpExportsTrie(data, bw); + try writer.writeAll(exports_label ++ "\n"); + try ctx.dumpExportsTrie(data, writer); break :blk; } } @@ -1661,20 +1698,20 @@ const MachODumper = struct { }, .dump_section => { - const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(check.data.items[check.payload.dump_section..].ptr)), 0); + const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(check.data.items.ptr + check.payload.dump_section)), 0); const sep_index = mem.indexOfScalar(u8, name, ',') orelse return step.fail("invalid section name: {s}", .{name}); const segname = name[0..sep_index]; const sectname = name[sep_index + 1 ..]; const sect = ctx.getSectionByName(segname, sectname) orelse return step.fail("section '{s}' not found", .{name}); - try ctx.dumpSection(sect, bw); + try ctx.dumpSection(sect, writer); }, else => return step.fail("invalid check kind for MachO file format: {s}", .{@tagName(check.kind)}), } - return aw.toOwnedSlice(); + return output.toOwnedSlice(); } }; @@ -1693,138 +1730,161 @@ const ElfDumper = struct { fn parseAndDumpArchive(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; - var r: std.io.Reader = .fixed(bytes); + var stream = std.io.fixedBufferStream(bytes); + const reader = stream.reader(); - if (!mem.eql(u8, try r.takeArray(elf.ARMAG.len), elf.ARMAG)) return error.InvalidArchiveMagicNumber; + const magic = try reader.readBytesNoEof(elf.ARMAG.len); + if (!mem.eql(u8, &magic, elf.ARMAG)) { + return error.InvalidArchiveMagicNumber; + } - var ctx: ArchiveContext = .{ + var ctx = ArchiveContext{ .gpa = gpa, .data = bytes, - .symtab = &.{}, - .strtab = &.{}, - .objects = .empty, + .strtab = &[0]u8{}, }; - defer ctx.deinit(); + defer { + for (ctx.objects.items) |*object| { + gpa.free(object.name); + } + ctx.objects.deinit(gpa); + } + + while (true) { + if (stream.pos >= ctx.data.len) break; + if (!mem.isAligned(stream.pos, 2)) stream.pos += 1; - while (r.seek < bytes.len) { - const hdr_seek = std.mem.alignForward(usize, r.seek, 2); - r.seek = hdr_seek; - const hdr = try r.takeStruct(elf.ar_hdr); + const hdr = try reader.readStruct(elf.ar_hdr); if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) return error.InvalidArchiveHeaderMagicNumber; - const data = try r.take(try hdr.size()); + const size = try hdr.size(); + defer { + _ = stream.seekBy(size) catch {}; + } if (hdr.isSymtab()) { - try ctx.parseSymtab(data, .p32); + try ctx.parseSymtab(ctx.data[stream.pos..][0..size], .p32); continue; } if (hdr.isSymtab64()) { - try ctx.parseSymtab(data, .p64); + try ctx.parseSymtab(ctx.data[stream.pos..][0..size], .p64); continue; } if (hdr.isStrtab()) { - ctx.strtab = data; + ctx.strtab = ctx.data[stream.pos..][0..size]; continue; } if (hdr.isSymdef() or hdr.isSymdefSorted()) continue; - const name = hdr.name() orelse ctx.getString((try hdr.nameOffset()).?); - try ctx.objects.putNoClobber(gpa, hdr_seek, .{ - .name = name, - .data = data, - }); + const name = if (hdr.name()) |name| + try gpa.dupe(u8, name) + else if (try hdr.nameOffset()) |off| + try gpa.dupe(u8, ctx.getString(off)) + else + unreachable; + + try ctx.objects.append(gpa, .{ .name = name, .off = stream.pos, .len = size }); } - var aw: std.io.Writer.Allocating = .init(gpa); - defer aw.deinit(); - const bw = &aw.writer; + var output = std.ArrayList(u8).init(gpa); + const writer = output.writer(); switch (check.kind) { - .archive_symtab => if (ctx.symtab.len > 0) { - try ctx.dumpSymtab(bw); + .archive_symtab => if (ctx.symtab.items.len > 0) { + try ctx.dumpSymtab(writer); } else return step.fail("no archive symbol table found", .{}), - else => if (ctx.objects.count() > 0) { - try ctx.dumpObjects(step, check, bw); + else => if (ctx.objects.items.len > 0) { + try ctx.dumpObjects(step, check, writer); } else return step.fail("empty archive", .{}), } - return aw.toOwnedSlice(); + return output.toOwnedSlice(); } const ArchiveContext = struct { gpa: Allocator, data: []const u8, - symtab: []ArSymtabEntry, + symtab: std.ArrayListUnmanaged(ArSymtabEntry) = .empty, strtab: []const u8, - objects: std.AutoArrayHashMapUnmanaged(usize, struct { name: []const u8, data: []const u8 }), + objects: std.ArrayListUnmanaged(struct { name: []const u8, off: usize, len: usize }) = .empty, - fn deinit(ctx: *ArchiveContext) void { - ctx.gpa.free(ctx.symtab); - ctx.objects.deinit(ctx.gpa); - } - - fn parseSymtab(ctx: *ArchiveContext, data: []const u8, ptr_width: enum { p32, p64 }) !void { - var r: std.io.Reader = .fixed(data); + fn parseSymtab(ctx: *ArchiveContext, raw: []const u8, ptr_width: enum { p32, p64 }) !void { + var stream = std.io.fixedBufferStream(raw); + const reader = stream.reader(); const num = switch (ptr_width) { - .p32 => try r.takeInt(u32, .big), - .p64 => try r.takeInt(u64, .big), + .p32 => try reader.readInt(u32, .big), + .p64 => try reader.readInt(u64, .big), }; const ptr_size: usize = switch (ptr_width) { .p32 => @sizeOf(u32), .p64 => @sizeOf(u64), }; - _ = try r.discard(.limited(num * ptr_size)); - const strtab = r.buffered(); + const strtab_off = (num + 1) * ptr_size; + const strtab_len = raw.len - strtab_off; + const strtab = raw[strtab_off..][0..strtab_len]; - assert(ctx.symtab.len == 0); - ctx.symtab = try ctx.gpa.alloc(ArSymtabEntry, num); + try ctx.symtab.ensureTotalCapacityPrecise(ctx.gpa, num); var stroff: usize = 0; - for (ctx.symtab) |*entry| { + for (0..num) |_| { const off = switch (ptr_width) { - .p32 => try r.takeInt(u32, .big), - .p64 => try r.takeInt(u64, .big), + .p32 => try reader.readInt(u32, .big), + .p64 => try reader.readInt(u64, .big), }; - const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(strtab[stroff..].ptr)), 0); + const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(strtab.ptr + stroff)), 0); stroff += name.len + 1; - entry.* = .{ .off = off, .name = name }; + ctx.symtab.appendAssumeCapacity(.{ .off = off, .name = name }); } } - fn dumpSymtab(ctx: ArchiveContext, bw: *Writer) !void { - var symbols: std.AutoArrayHashMap(usize, std.ArrayList([]const u8)) = .init(ctx.gpa); + fn dumpSymtab(ctx: ArchiveContext, writer: anytype) !void { + var files = std.AutoHashMap(usize, []const u8).init(ctx.gpa); + defer files.deinit(); + try files.ensureUnusedCapacity(@intCast(ctx.objects.items.len)); + + for (ctx.objects.items) |object| { + files.putAssumeCapacityNoClobber(object.off - @sizeOf(elf.ar_hdr), object.name); + } + + var symbols = std.AutoArrayHashMap(usize, std.ArrayList([]const u8)).init(ctx.gpa); defer { - for (symbols.values()) |*value| value.deinit(); + for (symbols.values()) |*value| { + value.deinit(); + } symbols.deinit(); } - for (ctx.symtab) |entry| { + for (ctx.symtab.items) |entry| { const gop = try symbols.getOrPut(@intCast(entry.off)); - if (!gop.found_existing) gop.value_ptr.* = .init(ctx.gpa); + if (!gop.found_existing) { + gop.value_ptr.* = std.ArrayList([]const u8).init(ctx.gpa); + } try gop.value_ptr.append(entry.name); } - try bw.print("{s}\n", .{archive_symtab_label}); + try writer.print("{s}\n", .{archive_symtab_label}); for (symbols.keys(), symbols.values()) |off, values| { - try bw.print("in object {s}\n", .{ctx.objects.get(off).?.name}); - for (values.items) |value| try bw.print("{s}\n", .{value}); + try writer.print("in object {s}\n", .{files.get(off).?}); + for (values.items) |value| { + try writer.print("{s}\n", .{value}); + } } } - fn dumpObjects(ctx: ArchiveContext, step: *Step, check: Check, bw: *Writer) !void { - for (ctx.objects.values()) |object| { - try bw.print("object {s}\n", .{object.name}); - const output = try parseAndDumpObject(step, check, object.data); + fn dumpObjects(ctx: ArchiveContext, step: *Step, check: Check, writer: anytype) !void { + for (ctx.objects.items) |object| { + try writer.print("object {s}\n", .{object.name}); + const output = try parseAndDumpObject(step, check, ctx.data[object.off..][0..object.len]); defer ctx.gpa.free(output); - try bw.print("{s}\n", .{output}); + try writer.print("{s}\n", .{output}); } } fn getString(ctx: ArchiveContext, off: u32) []const u8 { assert(off < ctx.strtab.len); - const name = mem.sliceTo(@as([*:'\n']const u8, @ptrCast(ctx.strtab[off..].ptr)), 0); + const name = mem.sliceTo(@as([*:'\n']const u8, @ptrCast(ctx.strtab.ptr + off)), 0); return name[0 .. name.len - 1]; } @@ -1836,23 +1896,24 @@ const ElfDumper = struct { fn parseAndDumpObject(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; - var r: std.io.Reader = .fixed(bytes); + var stream = std.io.fixedBufferStream(bytes); + const reader = stream.reader(); - const hdr = try r.takeStruct(elf.Elf64_Ehdr); - if (!mem.eql(u8, hdr.e_ident[0..4], "\x7fELF")) return error.InvalidMagicNumber; + const hdr = try reader.readStruct(elf.Elf64_Ehdr); + if (!mem.eql(u8, hdr.e_ident[0..4], "\x7fELF")) { + return error.InvalidMagicNumber; + } - const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(bytes[hdr.e_shoff..].ptr))[0..hdr.e_shnum]; - const phdrs = @as([*]align(1) const elf.Elf64_Phdr, @ptrCast(bytes[hdr.e_phoff..].ptr))[0..hdr.e_phnum]; + const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(bytes.ptr + hdr.e_shoff))[0..hdr.e_shnum]; + const phdrs = @as([*]align(1) const elf.Elf64_Phdr, @ptrCast(bytes.ptr + hdr.e_phoff))[0..hdr.e_phnum]; - var ctx: ObjectContext = .{ + var ctx = ObjectContext{ .gpa = gpa, .data = bytes, .hdr = hdr, .shdrs = shdrs, .phdrs = phdrs, .shstrtab = undefined, - .symtab = .{}, - .dysymtab = .{}, }; ctx.shstrtab = ctx.getSectionContents(ctx.hdr.e_shstrndx); @@ -1883,121 +1944,120 @@ const ElfDumper = struct { else => {}, }; - var aw: std.io.Writer.Allocating = .init(gpa); - defer aw.deinit(); - const bw = &aw.writer; + var output = std.ArrayList(u8).init(gpa); + const writer = output.writer(); switch (check.kind) { .headers => { - try ctx.dumpHeader(bw); - try ctx.dumpShdrs(bw); - try ctx.dumpPhdrs(bw); + try ctx.dumpHeader(writer); + try ctx.dumpShdrs(writer); + try ctx.dumpPhdrs(writer); }, .symtab => if (ctx.symtab.symbols.len > 0) { - try ctx.dumpSymtab(.symtab, bw); + try ctx.dumpSymtab(.symtab, writer); } else return step.fail("no symbol table found", .{}), .dynamic_symtab => if (ctx.dysymtab.symbols.len > 0) { - try ctx.dumpSymtab(.dysymtab, bw); + try ctx.dumpSymtab(.dysymtab, writer); } else return step.fail("no dynamic symbol table found", .{}), .dynamic_section => if (ctx.getSectionByName(".dynamic")) |shndx| { - try ctx.dumpDynamicSection(shndx, bw); + try ctx.dumpDynamicSection(shndx, writer); } else return step.fail("no .dynamic section found", .{}), .dump_section => { - const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(check.data.items[check.payload.dump_section..].ptr)), 0); + const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(check.data.items.ptr + check.payload.dump_section)), 0); const shndx = ctx.getSectionByName(name) orelse return step.fail("no '{s}' section found", .{name}); - try ctx.dumpSection(shndx, bw); + try ctx.dumpSection(shndx, writer); }, else => return step.fail("invalid check kind for ELF file format: {s}", .{@tagName(check.kind)}), } - return aw.toOwnedSlice(); + return output.toOwnedSlice(); } const ObjectContext = struct { gpa: Allocator, data: []const u8, - hdr: *align(1) const elf.Elf64_Ehdr, + hdr: elf.Elf64_Ehdr, shdrs: []align(1) const elf.Elf64_Shdr, phdrs: []align(1) const elf.Elf64_Phdr, shstrtab: []const u8, - symtab: Symtab, - dysymtab: Symtab, + symtab: Symtab = .{}, + dysymtab: Symtab = .{}, - fn dumpHeader(ctx: ObjectContext, bw: *Writer) !void { - try bw.writeAll("header\n"); - try bw.print("type {s}\n", .{@tagName(ctx.hdr.e_type)}); - try bw.print("entry {x}\n", .{ctx.hdr.e_entry}); + fn dumpHeader(ctx: ObjectContext, writer: anytype) !void { + try writer.writeAll("header\n"); + try writer.print("type {s}\n", .{@tagName(ctx.hdr.e_type)}); + try writer.print("entry {x}\n", .{ctx.hdr.e_entry}); } - fn dumpPhdrs(ctx: ObjectContext, bw: *Writer) !void { + fn dumpPhdrs(ctx: ObjectContext, writer: anytype) !void { if (ctx.phdrs.len == 0) return; - try bw.writeAll("program headers\n"); + try writer.writeAll("program headers\n"); for (ctx.phdrs, 0..) |phdr, phndx| { - try bw.print("phdr {d}\n", .{phndx}); - try bw.print("type {f}\n", .{fmtPhType(phdr.p_type)}); - try bw.print("vaddr {x}\n", .{phdr.p_vaddr}); - try bw.print("paddr {x}\n", .{phdr.p_paddr}); - try bw.print("offset {x}\n", .{phdr.p_offset}); - try bw.print("memsz {x}\n", .{phdr.p_memsz}); - try bw.print("filesz {x}\n", .{phdr.p_filesz}); - try bw.print("align {x}\n", .{phdr.p_align}); + try writer.print("phdr {d}\n", .{phndx}); + try writer.print("type {f}\n", .{fmtPhType(phdr.p_type)}); + try writer.print("vaddr {x}\n", .{phdr.p_vaddr}); + try writer.print("paddr {x}\n", .{phdr.p_paddr}); + try writer.print("offset {x}\n", .{phdr.p_offset}); + try writer.print("memsz {x}\n", .{phdr.p_memsz}); + try writer.print("filesz {x}\n", .{phdr.p_filesz}); + try writer.print("align {x}\n", .{phdr.p_align}); { const flags = phdr.p_flags; - try bw.writeAll("flags"); - if (flags > 0) try bw.writeByte(' '); + try writer.writeAll("flags"); + if (flags > 0) try writer.writeByte(' '); if (flags & elf.PF_R != 0) { - try bw.writeByte('R'); + try writer.writeByte('R'); } if (flags & elf.PF_W != 0) { - try bw.writeByte('W'); + try writer.writeByte('W'); } if (flags & elf.PF_X != 0) { - try bw.writeByte('E'); + try writer.writeByte('E'); } if (flags & elf.PF_MASKOS != 0) { - try bw.writeAll("OS"); + try writer.writeAll("OS"); } if (flags & elf.PF_MASKPROC != 0) { - try bw.writeAll("PROC"); + try writer.writeAll("PROC"); } - try bw.writeByte('\n'); + try writer.writeByte('\n'); } } } - fn dumpShdrs(ctx: ObjectContext, bw: *Writer) !void { + fn dumpShdrs(ctx: ObjectContext, writer: anytype) !void { if (ctx.shdrs.len == 0) return; - try bw.writeAll("section headers\n"); + try writer.writeAll("section headers\n"); for (ctx.shdrs, 0..) |shdr, shndx| { - try bw.print("shdr {d}\n", .{shndx}); - try bw.print("name {s}\n", .{ctx.getSectionName(shndx)}); - try bw.print("type {f}\n", .{fmtShType(shdr.sh_type)}); - try bw.print("addr {x}\n", .{shdr.sh_addr}); - try bw.print("offset {x}\n", .{shdr.sh_offset}); - try bw.print("size {x}\n", .{shdr.sh_size}); - try bw.print("addralign {x}\n", .{shdr.sh_addralign}); + try writer.print("shdr {d}\n", .{shndx}); + try writer.print("name {s}\n", .{ctx.getSectionName(shndx)}); + try writer.print("type {f}\n", .{fmtShType(shdr.sh_type)}); + try writer.print("addr {x}\n", .{shdr.sh_addr}); + try writer.print("offset {x}\n", .{shdr.sh_offset}); + try writer.print("size {x}\n", .{shdr.sh_size}); + try writer.print("addralign {x}\n", .{shdr.sh_addralign}); // TODO dump formatted sh_flags } } - fn dumpDynamicSection(ctx: ObjectContext, shndx: usize, bw: *Writer) !void { + fn dumpDynamicSection(ctx: ObjectContext, shndx: usize, writer: anytype) !void { const shdr = ctx.shdrs[shndx]; const strtab = ctx.getSectionContents(shdr.sh_link); const data = ctx.getSectionContents(shndx); const nentries = @divExact(data.len, @sizeOf(elf.Elf64_Dyn)); const entries = @as([*]align(1) const elf.Elf64_Dyn, @ptrCast(data.ptr))[0..nentries]; - try bw.writeAll(ElfDumper.dynamic_section_label ++ "\n"); + try writer.writeAll(ElfDumper.dynamic_section_label ++ "\n"); for (entries) |entry| { const key = @as(u64, @bitCast(entry.d_tag)); @@ -2038,7 +2098,7 @@ const ElfDumper = struct { elf.DT_NULL => "NULL", else => "UNKNOWN", }; - try bw.print("{s}", .{key_str}); + try writer.print("{s}", .{key_str}); switch (key) { elf.DT_NEEDED, @@ -2047,7 +2107,7 @@ const ElfDumper = struct { elf.DT_RUNPATH, => { const name = getString(strtab, @intCast(value)); - try bw.print(" {s}", .{name}); + try writer.print(" {s}", .{name}); }, elf.DT_INIT_ARRAY, @@ -2065,7 +2125,7 @@ const ElfDumper = struct { elf.DT_INIT, elf.DT_FINI, elf.DT_NULL, - => try bw.print(" {x}", .{value}), + => try writer.print(" {x}", .{value}), elf.DT_INIT_ARRAYSZ, elf.DT_FINI_ARRAYSZ, @@ -2075,77 +2135,77 @@ const ElfDumper = struct { elf.DT_RELASZ, elf.DT_RELAENT, elf.DT_RELACOUNT, - => try bw.print(" {d}", .{value}), + => try writer.print(" {d}", .{value}), - elf.DT_PLTREL => try bw.writeAll(switch (value) { + elf.DT_PLTREL => try writer.writeAll(switch (value) { elf.DT_REL => " REL", elf.DT_RELA => " RELA", else => " UNKNOWN", }), elf.DT_FLAGS => if (value > 0) { - if (value & elf.DF_ORIGIN != 0) try bw.writeAll(" ORIGIN"); - if (value & elf.DF_SYMBOLIC != 0) try bw.writeAll(" SYMBOLIC"); - if (value & elf.DF_TEXTREL != 0) try bw.writeAll(" TEXTREL"); - if (value & elf.DF_BIND_NOW != 0) try bw.writeAll(" BIND_NOW"); - if (value & elf.DF_STATIC_TLS != 0) try bw.writeAll(" STATIC_TLS"); + if (value & elf.DF_ORIGIN != 0) try writer.writeAll(" ORIGIN"); + if (value & elf.DF_SYMBOLIC != 0) try writer.writeAll(" SYMBOLIC"); + if (value & elf.DF_TEXTREL != 0) try writer.writeAll(" TEXTREL"); + if (value & elf.DF_BIND_NOW != 0) try writer.writeAll(" BIND_NOW"); + if (value & elf.DF_STATIC_TLS != 0) try writer.writeAll(" STATIC_TLS"); }, elf.DT_FLAGS_1 => if (value > 0) { - if (value & elf.DF_1_NOW != 0) try bw.writeAll(" NOW"); - if (value & elf.DF_1_GLOBAL != 0) try bw.writeAll(" GLOBAL"); - if (value & elf.DF_1_GROUP != 0) try bw.writeAll(" GROUP"); - if (value & elf.DF_1_NODELETE != 0) try bw.writeAll(" NODELETE"); - if (value & elf.DF_1_LOADFLTR != 0) try bw.writeAll(" LOADFLTR"); - if (value & elf.DF_1_INITFIRST != 0) try bw.writeAll(" INITFIRST"); - if (value & elf.DF_1_NOOPEN != 0) try bw.writeAll(" NOOPEN"); - if (value & elf.DF_1_ORIGIN != 0) try bw.writeAll(" ORIGIN"); - if (value & elf.DF_1_DIRECT != 0) try bw.writeAll(" DIRECT"); - if (value & elf.DF_1_TRANS != 0) try bw.writeAll(" TRANS"); - if (value & elf.DF_1_INTERPOSE != 0) try bw.writeAll(" INTERPOSE"); - if (value & elf.DF_1_NODEFLIB != 0) try bw.writeAll(" NODEFLIB"); - if (value & elf.DF_1_NODUMP != 0) try bw.writeAll(" NODUMP"); - if (value & elf.DF_1_CONFALT != 0) try bw.writeAll(" CONFALT"); - if (value & elf.DF_1_ENDFILTEE != 0) try bw.writeAll(" ENDFILTEE"); - if (value & elf.DF_1_DISPRELDNE != 0) try bw.writeAll(" DISPRELDNE"); - if (value & elf.DF_1_DISPRELPND != 0) try bw.writeAll(" DISPRELPND"); - if (value & elf.DF_1_NODIRECT != 0) try bw.writeAll(" NODIRECT"); - if (value & elf.DF_1_IGNMULDEF != 0) try bw.writeAll(" IGNMULDEF"); - if (value & elf.DF_1_NOKSYMS != 0) try bw.writeAll(" NOKSYMS"); - if (value & elf.DF_1_NOHDR != 0) try bw.writeAll(" NOHDR"); - if (value & elf.DF_1_EDITED != 0) try bw.writeAll(" EDITED"); - if (value & elf.DF_1_NORELOC != 0) try bw.writeAll(" NORELOC"); - if (value & elf.DF_1_SYMINTPOSE != 0) try bw.writeAll(" SYMINTPOSE"); - if (value & elf.DF_1_GLOBAUDIT != 0) try bw.writeAll(" GLOBAUDIT"); - if (value & elf.DF_1_SINGLETON != 0) try bw.writeAll(" SINGLETON"); - if (value & elf.DF_1_STUB != 0) try bw.writeAll(" STUB"); - if (value & elf.DF_1_PIE != 0) try bw.writeAll(" PIE"); + if (value & elf.DF_1_NOW != 0) try writer.writeAll(" NOW"); + if (value & elf.DF_1_GLOBAL != 0) try writer.writeAll(" GLOBAL"); + if (value & elf.DF_1_GROUP != 0) try writer.writeAll(" GROUP"); + if (value & elf.DF_1_NODELETE != 0) try writer.writeAll(" NODELETE"); + if (value & elf.DF_1_LOADFLTR != 0) try writer.writeAll(" LOADFLTR"); + if (value & elf.DF_1_INITFIRST != 0) try writer.writeAll(" INITFIRST"); + if (value & elf.DF_1_NOOPEN != 0) try writer.writeAll(" NOOPEN"); + if (value & elf.DF_1_ORIGIN != 0) try writer.writeAll(" ORIGIN"); + if (value & elf.DF_1_DIRECT != 0) try writer.writeAll(" DIRECT"); + if (value & elf.DF_1_TRANS != 0) try writer.writeAll(" TRANS"); + if (value & elf.DF_1_INTERPOSE != 0) try writer.writeAll(" INTERPOSE"); + if (value & elf.DF_1_NODEFLIB != 0) try writer.writeAll(" NODEFLIB"); + if (value & elf.DF_1_NODUMP != 0) try writer.writeAll(" NODUMP"); + if (value & elf.DF_1_CONFALT != 0) try writer.writeAll(" CONFALT"); + if (value & elf.DF_1_ENDFILTEE != 0) try writer.writeAll(" ENDFILTEE"); + if (value & elf.DF_1_DISPRELDNE != 0) try writer.writeAll(" DISPRELDNE"); + if (value & elf.DF_1_DISPRELPND != 0) try writer.writeAll(" DISPRELPND"); + if (value & elf.DF_1_NODIRECT != 0) try writer.writeAll(" NODIRECT"); + if (value & elf.DF_1_IGNMULDEF != 0) try writer.writeAll(" IGNMULDEF"); + if (value & elf.DF_1_NOKSYMS != 0) try writer.writeAll(" NOKSYMS"); + if (value & elf.DF_1_NOHDR != 0) try writer.writeAll(" NOHDR"); + if (value & elf.DF_1_EDITED != 0) try writer.writeAll(" EDITED"); + if (value & elf.DF_1_NORELOC != 0) try writer.writeAll(" NORELOC"); + if (value & elf.DF_1_SYMINTPOSE != 0) try writer.writeAll(" SYMINTPOSE"); + if (value & elf.DF_1_GLOBAUDIT != 0) try writer.writeAll(" GLOBAUDIT"); + if (value & elf.DF_1_SINGLETON != 0) try writer.writeAll(" SINGLETON"); + if (value & elf.DF_1_STUB != 0) try writer.writeAll(" STUB"); + if (value & elf.DF_1_PIE != 0) try writer.writeAll(" PIE"); }, - else => try bw.print(" {x}", .{value}), + else => try writer.print(" {x}", .{value}), } - try bw.writeByte('\n'); + try writer.writeByte('\n'); } } - fn dumpSymtab(ctx: ObjectContext, comptime @"type": enum { symtab, dysymtab }, bw: *Writer) !void { + fn dumpSymtab(ctx: ObjectContext, comptime @"type": enum { symtab, dysymtab }, writer: anytype) !void { const symtab = switch (@"type") { .symtab => ctx.symtab, .dysymtab => ctx.dysymtab, }; - try bw.writeAll(switch (@"type") { + try writer.writeAll(switch (@"type") { .symtab => symtab_label, .dysymtab => dynamic_symtab_label, } ++ "\n"); for (symtab.symbols, 0..) |sym, index| { - try bw.print("{x} {x}", .{ sym.st_value, sym.st_size }); + try writer.print("{x} {x}", .{ sym.st_value, sym.st_size }); { if (elf.SHN_LORESERVE <= sym.st_shndx and sym.st_shndx < elf.SHN_HIRESERVE) { if (elf.SHN_LOPROC <= sym.st_shndx and sym.st_shndx < elf.SHN_HIPROC) { - try bw.print(" LO+{d}", .{sym.st_shndx - elf.SHN_LOPROC}); + try writer.print(" LO+{d}", .{sym.st_shndx - elf.SHN_LOPROC}); } else { const sym_ndx = switch (sym.st_shndx) { elf.SHN_ABS => "ABS", @@ -2153,12 +2213,12 @@ const ElfDumper = struct { elf.SHN_LIVEPATCH => "LIV", else => "UNK", }; - try bw.print(" {s}", .{sym_ndx}); + try writer.print(" {s}", .{sym_ndx}); } } else if (sym.st_shndx == elf.SHN_UNDEF) { - try bw.writeAll(" UND"); + try writer.writeAll(" UND"); } else { - try bw.print(" {x}", .{sym.st_shndx}); + try writer.print(" {x}", .{sym.st_shndx}); } } @@ -2175,12 +2235,12 @@ const ElfDumper = struct { elf.STT_NUM => "NUM", elf.STT_GNU_IFUNC => "IFUNC", else => if (elf.STT_LOPROC <= tt and tt < elf.STT_HIPROC) { - break :blk try bw.print(" LOPROC+{d}", .{tt - elf.STT_LOPROC}); + break :blk try writer.print(" LOPROC+{d}", .{tt - elf.STT_LOPROC}); } else if (elf.STT_LOOS <= tt and tt < elf.STT_HIOS) { - break :blk try bw.print(" LOOS+{d}", .{tt - elf.STT_LOOS}); + break :blk try writer.print(" LOOS+{d}", .{tt - elf.STT_LOOS}); } else "UNK", }; - try bw.print(" {s}", .{sym_type}); + try writer.print(" {s}", .{sym_type}); } blk: { @@ -2191,28 +2251,28 @@ const ElfDumper = struct { elf.STB_WEAK => "WEAK", elf.STB_NUM => "NUM", else => if (elf.STB_LOPROC <= bind and bind < elf.STB_HIPROC) { - break :blk try bw.print(" LOPROC+{d}", .{bind - elf.STB_LOPROC}); + break :blk try writer.print(" LOPROC+{d}", .{bind - elf.STB_LOPROC}); } else if (elf.STB_LOOS <= bind and bind < elf.STB_HIOS) { - break :blk try bw.print(" LOOS+{d}", .{bind - elf.STB_LOOS}); + break :blk try writer.print(" LOOS+{d}", .{bind - elf.STB_LOOS}); } else "UNKNOWN", }; - try bw.print(" {s}", .{sym_bind}); + try writer.print(" {s}", .{sym_bind}); } const sym_vis = @as(elf.STV, @enumFromInt(@as(u2, @truncate(sym.st_other)))); - try bw.print(" {s}", .{@tagName(sym_vis)}); + try writer.print(" {s}", .{@tagName(sym_vis)}); const sym_name = switch (sym.st_type()) { elf.STT_SECTION => ctx.getSectionName(sym.st_shndx), else => symtab.getName(index).?, }; - try bw.print(" {s}\n", .{sym_name}); + try writer.print(" {s}\n", .{sym_name}); } } - fn dumpSection(ctx: ObjectContext, shndx: usize, bw: *Writer) !void { + fn dumpSection(ctx: ObjectContext, shndx: usize, writer: anytype) !void { const data = ctx.getSectionContents(shndx); - try bw.print("{s}", .{data}); + try writer.print("{s}", .{data}); } inline fn getSectionName(ctx: ObjectContext, shndx: usize) []const u8 { @@ -2250,15 +2310,15 @@ const ElfDumper = struct { }; fn getString(strtab: []const u8, off: u32) []const u8 { - const str = strtab[off..]; - return str[0..std.mem.indexOfScalar(u8, str, 0).?]; + assert(off < strtab.len); + return mem.sliceTo(@as([*:0]const u8, @ptrCast(strtab.ptr + off)), 0); } fn fmtShType(sh_type: u32) std.fmt.Formatter(u32, formatShType) { return .{ .data = sh_type }; } - fn formatShType(sh_type: u32, w: *Writer) Writer.Error!void { + fn formatShType(sh_type: u32, writer: *Writer) Writer.Error!void { const name = switch (sh_type) { elf.SHT_NULL => "NULL", elf.SHT_PROGBITS => "PROGBITS", @@ -2284,21 +2344,21 @@ const ElfDumper = struct { elf.SHT_GNU_VERNEED => "VERNEED", elf.SHT_GNU_VERSYM => "VERSYM", else => if (elf.SHT_LOOS <= sh_type and sh_type < elf.SHT_HIOS) { - return try w.print("LOOS+0x{x}", .{sh_type - elf.SHT_LOOS}); + return try writer.print("LOOS+0x{x}", .{sh_type - elf.SHT_LOOS}); } else if (elf.SHT_LOPROC <= sh_type and sh_type < elf.SHT_HIPROC) { - return try w.print("LOPROC+0x{x}", .{sh_type - elf.SHT_LOPROC}); + return try writer.print("LOPROC+0x{x}", .{sh_type - elf.SHT_LOPROC}); } else if (elf.SHT_LOUSER <= sh_type and sh_type < elf.SHT_HIUSER) { - return try w.print("LOUSER+0x{x}", .{sh_type - elf.SHT_LOUSER}); + return try writer.print("LOUSER+0x{x}", .{sh_type - elf.SHT_LOUSER}); } else "UNKNOWN", }; - try w.writeAll(name); + try writer.writeAll(name); } fn fmtPhType(ph_type: u32) std.fmt.Formatter(u32, formatPhType) { return .{ .data = ph_type }; } - fn formatPhType(ph_type: u32, w: *Writer) Writer.Error!void { + fn formatPhType(ph_type: u32, writer: *Writer) Writer.Error!void { const p_type = switch (ph_type) { elf.PT_NULL => "NULL", elf.PT_LOAD => "LOAD", @@ -2313,12 +2373,12 @@ const ElfDumper = struct { elf.PT_GNU_STACK => "GNU_STACK", elf.PT_GNU_RELRO => "GNU_RELRO", else => if (elf.PT_LOOS <= ph_type and ph_type < elf.PT_HIOS) { - return try w.print("LOOS+0x{x}", .{ph_type - elf.PT_LOOS}); + return try writer.print("LOOS+0x{x}", .{ph_type - elf.PT_LOOS}); } else if (elf.PT_LOPROC <= ph_type and ph_type < elf.PT_HIPROC) { - return try w.print("LOPROC+0x{x}", .{ph_type - elf.PT_LOPROC}); + return try writer.print("LOPROC+0x{x}", .{ph_type - elf.PT_LOPROC}); } else "UNKNOWN", }; - try w.writeAll(p_type); + try writer.writeAll(p_type); } }; @@ -2327,38 +2387,49 @@ const WasmDumper = struct { fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; - var r: std.io.Reader = .fixed(bytes); - - const buf = try r.takeArray(8); - if (!mem.eql(u8, buf[0..4], &std.wasm.magic)) return error.InvalidMagicByte; - if (!mem.eql(u8, buf[4..8], &std.wasm.version)) return error.UnsupportedWasmVersion; + var fbs = std.io.fixedBufferStream(bytes); + const reader = fbs.reader(); - var aw: std.io.Writer.Allocating = .init(gpa); - defer aw.deinit(); - const bw = &aw.writer; + const buf = try reader.readBytesNoEof(8); + if (!mem.eql(u8, buf[0..4], &std.wasm.magic)) { + return error.InvalidMagicByte; + } + if (!mem.eql(u8, buf[4..], &std.wasm.version)) { + return error.UnsupportedWasmVersion; + } - parseAndDumpInner(step, check, &r, bw) catch |err| switch (err) { - error.EndOfStream => try bw.writeAll("\n"), + var output = std.ArrayList(u8).init(gpa); + defer output.deinit(); + parseAndDumpInner(step, check, bytes, &fbs, &output) catch |err| switch (err) { + error.EndOfStream => try output.appendSlice("\n"), else => |e| return e, }; - return aw.toOwnedSlice(); + return output.toOwnedSlice(); } fn parseAndDumpInner( step: *Step, check: Check, - r: *std.io.Reader, - bw: *Writer, + bytes: []const u8, + fbs: *std.io.FixedBufferStream([]const u8), + output: *std.ArrayList(u8), ) !void { + const reader = fbs.reader(); + const writer = output.writer(); + switch (check.kind) { - .headers => while (r.takeEnum(std.wasm.Section, .little)) |section| { - var section_reader: std.io.Reader = .fixed(try r.take(try r.takeLeb128(u32))); - try parseAndDumpSection(step, section, §ion_reader, bw); - } else |err| switch (err) { - error.InvalidEnumTag => return step.fail("invalid section id", .{}), - error.EndOfStream => {}, - else => |e| return e, + .headers => { + while (reader.readByte()) |current_byte| { + const section = std.enums.fromInt(std.wasm.Section, current_byte) orelse { + return step.fail("Found invalid section id '{d}'", .{current_byte}); + }; + + const section_length = try std.leb.readUleb128(u32, reader); + try parseAndDumpSection(step, section, bytes[fbs.pos..][0..section_length], writer); + fbs.pos += section_length; + } else |_| {} // reached end of stream }, + else => return step.fail("invalid check kind for Wasm file format: {s}", .{@tagName(check.kind)}), } } @@ -2366,13 +2437,16 @@ const WasmDumper = struct { fn parseAndDumpSection( step: *Step, section: std.wasm.Section, - r: *std.io.Reader, - bw: *Writer, + data: []const u8, + writer: anytype, ) !void { - try bw.print( + var fbs = std.io.fixedBufferStream(data); + const reader = fbs.reader(); + + try writer.print( \\Section {s} \\size {d} - , .{ @tagName(section), r.buffer.len }); + , .{ @tagName(section), data.len }); switch (section) { .type, @@ -2386,83 +2460,96 @@ const WasmDumper = struct { .code, .data, => { - const entries = try r.takeLeb128(u32); - try bw.print("\nentries {d}\n", .{entries}); - try parseSection(step, section, r, entries, bw); + const entries = try std.leb.readUleb128(u32, reader); + try writer.print("\nentries {d}\n", .{entries}); + try parseSection(step, section, data[fbs.pos..], entries, writer); }, .custom => { - const name = try r.take(try r.takeLeb128(u32)); - try bw.print("\nname {s}\n", .{name}); + const name_length = try std.leb.readUleb128(u32, reader); + const name = data[fbs.pos..][0..name_length]; + fbs.pos += name_length; + try writer.print("\nname {s}\n", .{name}); if (mem.eql(u8, name, "name")) { - try parseDumpNames(step, r, bw); + try parseDumpNames(step, reader, writer, data); } else if (mem.eql(u8, name, "producers")) { - try parseDumpProducers(r, bw); + try parseDumpProducers(reader, writer, data); } else if (mem.eql(u8, name, "target_features")) { - try parseDumpFeatures(r, bw); + try parseDumpFeatures(reader, writer, data); } // TODO: Implement parsing and dumping other custom sections (such as relocations) }, .start => { - const start = try r.takeLeb128(u32); - try bw.print("\nstart {d}\n", .{start}); + const start = try std.leb.readUleb128(u32, reader); + try writer.print("\nstart {d}\n", .{start}); }, .data_count => { - const count = try r.takeLeb128(u32); - try bw.print("\ncount {d}\n", .{count}); + const count = try std.leb.readUleb128(u32, reader); + try writer.print("\ncount {d}\n", .{count}); }, else => {}, // skip unknown sections } } - fn parseSection(step: *Step, section: std.wasm.Section, r: *std.io.Reader, entries: u32, bw: *Writer) !void { + fn parseSection(step: *Step, section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { + var fbs = std.io.fixedBufferStream(data); + const reader = fbs.reader(); + switch (section) { .type => { var i: u32 = 0; while (i < entries) : (i += 1) { - const func_type = try r.takeByte(); + const func_type = try reader.readByte(); if (func_type != std.wasm.function_type) { return step.fail("expected function type, found byte '{d}'", .{func_type}); } - const params = try r.takeLeb128(u32); - try bw.print("params {d}\n", .{params}); + const params = try std.leb.readUleb128(u32, reader); + try writer.print("params {d}\n", .{params}); var index: u32 = 0; while (index < params) : (index += 1) { - _ = try parseDumpType(step, std.wasm.Valtype, r, bw); + _ = try parseDumpType(step, std.wasm.Valtype, reader, writer); } else index = 0; - const returns = try r.takeLeb128(u32); - try bw.print("returns {d}\n", .{returns}); + const returns = try std.leb.readUleb128(u32, reader); + try writer.print("returns {d}\n", .{returns}); while (index < returns) : (index += 1) { - _ = try parseDumpType(step, std.wasm.Valtype, r, bw); + _ = try parseDumpType(step, std.wasm.Valtype, reader, writer); } } }, .import => { var i: u32 = 0; while (i < entries) : (i += 1) { - const module_name = try r.take(try r.takeLeb128(u32)); - const name = try r.take(try r.takeLeb128(u32)); - const kind = r.takeEnum(std.wasm.ExternalKind, .little) catch |err| switch (err) { - error.InvalidEnumTag => return step.fail("invalid import kind", .{}), - else => |e| return e, + const module_name_len = try std.leb.readUleb128(u32, reader); + const module_name = data[fbs.pos..][0..module_name_len]; + fbs.pos += module_name_len; + const name_len = try std.leb.readUleb128(u32, reader); + const name = data[fbs.pos..][0..name_len]; + fbs.pos += name_len; + + const kind = std.enums.fromInt(std.wasm.ExternalKind, try reader.readByte()) orelse { + return step.fail("invalid import kind", .{}); }; - try bw.print( + try writer.print( \\module {s} \\name {s} \\kind {s} , .{ module_name, name, @tagName(kind) }); - try bw.writeByte('\n'); + try writer.writeByte('\n'); switch (kind) { - .function => try bw.print("index {d}\n", .{try r.takeLeb128(u32)}), - .memory => try parseDumpLimits(r, bw), + .function => { + try writer.print("index {d}\n", .{try std.leb.readUleb128(u32, reader)}); + }, + .memory => { + try parseDumpLimits(reader, writer); + }, .global => { - _ = try parseDumpType(step, std.wasm.Valtype, r, bw); - try bw.print("mutable {}\n", .{0x01 == try r.takeLeb128(u32)}); + _ = try parseDumpType(step, std.wasm.Valtype, reader, writer); + try writer.print("mutable {}\n", .{0x01 == try std.leb.readUleb128(u32, reader)}); }, .table => { - _ = try parseDumpType(step, std.wasm.RefType, r, bw); - try parseDumpLimits(r, bw); + _ = try parseDumpType(step, std.wasm.RefType, reader, writer); + try parseDumpLimits(reader, writer); }, } } @@ -2470,58 +2557,60 @@ const WasmDumper = struct { .function => { var i: u32 = 0; while (i < entries) : (i += 1) { - try bw.print("index {d}\n", .{try r.takeLeb128(u32)}); + try writer.print("index {d}\n", .{try std.leb.readUleb128(u32, reader)}); } }, .table => { var i: u32 = 0; while (i < entries) : (i += 1) { - _ = try parseDumpType(step, std.wasm.RefType, r, bw); - try parseDumpLimits(r, bw); + _ = try parseDumpType(step, std.wasm.RefType, reader, writer); + try parseDumpLimits(reader, writer); } }, .memory => { var i: u32 = 0; while (i < entries) : (i += 1) { - try parseDumpLimits(r, bw); + try parseDumpLimits(reader, writer); } }, .global => { var i: u32 = 0; while (i < entries) : (i += 1) { - _ = try parseDumpType(step, std.wasm.Valtype, r, bw); - try bw.print("mutable {}\n", .{0x01 == try r.takeLeb128(u1)}); - try parseDumpInit(step, r, bw); + _ = try parseDumpType(step, std.wasm.Valtype, reader, writer); + try writer.print("mutable {}\n", .{0x01 == try std.leb.readUleb128(u1, reader)}); + try parseDumpInit(step, reader, writer); } }, .@"export" => { var i: u32 = 0; while (i < entries) : (i += 1) { - const name = try r.take(try r.takeLeb128(u32)); - const kind = r.takeEnum(std.wasm.ExternalKind, .little) catch |err| switch (err) { - error.InvalidEnumTag => return step.fail("invalid export kind value", .{}), - else => |e| return e, + const name_len = try std.leb.readUleb128(u32, reader); + const name = data[fbs.pos..][0..name_len]; + fbs.pos += name_len; + const kind_byte = try std.leb.readUleb128(u8, reader); + const kind = std.enums.fromInt(std.wasm.ExternalKind, kind_byte) orelse { + return step.fail("invalid export kind value '{d}'", .{kind_byte}); }; - const index = try r.takeLeb128(u32); - try bw.print( + const index = try std.leb.readUleb128(u32, reader); + try writer.print( \\name {s} \\kind {s} \\index {d} , .{ name, @tagName(kind), index }); - try bw.writeByte('\n'); + try writer.writeByte('\n'); } }, .element => { var i: u32 = 0; while (i < entries) : (i += 1) { - try bw.print("table index {d}\n", .{try r.takeLeb128(u32)}); - try parseDumpInit(step, r, bw); + try writer.print("table index {d}\n", .{try std.leb.readUleb128(u32, reader)}); + try parseDumpInit(step, reader, writer); - const function_indexes = try r.takeLeb128(u32); + const function_indexes = try std.leb.readUleb128(u32, reader); var function_index: u32 = 0; - try bw.print("indexes {d}\n", .{function_indexes}); + try writer.print("indexes {d}\n", .{function_indexes}); while (function_index < function_indexes) : (function_index += 1) { - try bw.print("index {d}\n", .{try r.takeLeb128(u32)}); + try writer.print("index {d}\n", .{try std.leb.readUleb128(u32, reader)}); } } }, @@ -2529,95 +2618,101 @@ const WasmDumper = struct { .data => { var i: u32 = 0; while (i < entries) : (i += 1) { - const flags: packed struct(u32) { - passive: bool, - memidx: bool, - unused: u30, - } = @bitCast(try r.takeLeb128(u32)); - const index = if (flags.memidx) try r.takeLeb128(u32) else 0; - try bw.print("memory index 0x{x}\n", .{index}); - if (!flags.passive) try parseDumpInit(step, r, bw); - const size = try r.takeLeb128(u32); - try bw.print("size {d}\n", .{size}); - _ = try r.discard(.limited(size)); // we do not care about the content of the segments + const flags = try std.leb.readUleb128(u32, reader); + const index = if (flags & 0x02 != 0) + try std.leb.readUleb128(u32, reader) + else + 0; + try writer.print("memory index 0x{x}\n", .{index}); + if (flags == 0) { + try parseDumpInit(step, reader, writer); + } + + const size = try std.leb.readUleb128(u32, reader); + try writer.print("size {d}\n", .{size}); + try reader.skipBytes(size, .{}); // we do not care about the content of the segments } }, else => unreachable, } } - fn parseDumpType(step: *Step, comptime E: type, r: *std.io.Reader, bw: *Writer) !E { - const tag = r.takeEnum(E, .little) catch |err| switch (err) { - error.InvalidEnumTag => return step.fail("invalid wasm type value", .{}), - else => |e| return e, + fn parseDumpType(step: *Step, comptime E: type, reader: anytype, writer: anytype) !E { + const byte = try reader.readByte(); + const tag = std.enums.fromInt(E, byte) orelse { + return step.fail("invalid wasm type value '{d}'", .{byte}); }; - try bw.print("type {s}\n", .{@tagName(tag)}); + try writer.print("type {s}\n", .{@tagName(tag)}); return tag; } - fn parseDumpLimits(r: *std.io.Reader, bw: *Writer) !void { - const flags = try r.takeLeb128(u8); - const min = try r.takeLeb128(u32); + fn parseDumpLimits(reader: anytype, writer: anytype) !void { + const flags = try std.leb.readUleb128(u8, reader); + const min = try std.leb.readUleb128(u32, reader); - try bw.print("min {x}\n", .{min}); - if (flags != 0) try bw.print("max {x}\n", .{try r.takeLeb128(u32)}); + try writer.print("min {x}\n", .{min}); + if (flags != 0) { + try writer.print("max {x}\n", .{try std.leb.readUleb128(u32, reader)}); + } } - fn parseDumpInit(step: *Step, r: *std.io.Reader, bw: *Writer) !void { - const opcode = r.takeEnum(std.wasm.Opcode, .little) catch |err| switch (err) { - error.InvalidEnumTag => return step.fail("invalid wasm opcode", .{}), - else => |e| return e, + fn parseDumpInit(step: *Step, reader: anytype, writer: anytype) !void { + const byte = try reader.readByte(); + const opcode = std.enums.fromInt(std.wasm.Opcode, byte) orelse { + return step.fail("invalid wasm opcode '{d}'", .{byte}); }; switch (opcode) { - .i32_const => try bw.print("i32.const {x}\n", .{try r.takeLeb128(i32)}), - .i64_const => try bw.print("i64.const {x}\n", .{try r.takeLeb128(i64)}), - .f32_const => try bw.print("f32.const {x}\n", .{@as(f32, @bitCast(try r.takeInt(u32, .little)))}), - .f64_const => try bw.print("f64.const {x}\n", .{@as(f64, @bitCast(try r.takeInt(u64, .little)))}), - .global_get => try bw.print("global.get {x}\n", .{try r.takeLeb128(u32)}), + .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readIleb128(i32, reader)}), + .i64_const => try writer.print("i64.const {x}\n", .{try std.leb.readIleb128(i64, reader)}), + .f32_const => try writer.print("f32.const {x}\n", .{@as(f32, @bitCast(try reader.readInt(u32, .little)))}), + .f64_const => try writer.print("f64.const {x}\n", .{@as(f64, @bitCast(try reader.readInt(u64, .little)))}), + .global_get => try writer.print("global.get {x}\n", .{try std.leb.readUleb128(u32, reader)}), else => unreachable, } - const end_opcode = try r.takeLeb128(u8); + const end_opcode = try std.leb.readUleb128(u8, reader); if (end_opcode != @intFromEnum(std.wasm.Opcode.end)) { return step.fail("expected 'end' opcode in init expression", .{}); } } /// https://webassembly.github.io/spec/core/appendix/custom.html - fn parseDumpNames(step: *Step, r: *std.io.Reader, bw: *Writer) !void { - var subsection_br: std.io.Reader = undefined; - while (r.seek < r.buffer.len) { - switch (try parseDumpType(step, std.wasm.NameSubsection, r, bw)) { + fn parseDumpNames(step: *Step, reader: anytype, writer: anytype, data: []const u8) !void { + while (reader.context.pos < data.len) { + switch (try parseDumpType(step, std.wasm.NameSubsection, reader, writer)) { // The module name subsection ... consists of a single name // that is assigned to the module itself. .module => { - subsection_br = .fixed(try r.take(try r.takeLeb128(u32))); - const name = try subsection_br.take(try subsection_br.takeLeb128(u32)); - try bw.print( - \\name {s} - \\ - , .{name}); - if (subsection_br.seek != subsection_br.buffer.len) return error.BadSubsectionSize; + const size = try std.leb.readUleb128(u32, reader); + const name_len = try std.leb.readUleb128(u32, reader); + if (size != name_len + 1) return error.BadSubsectionSize; + if (reader.context.pos + name_len > data.len) return error.UnexpectedEndOfStream; + try writer.print("name {s}\n", .{data[reader.context.pos..][0..name_len]}); + reader.context.pos += name_len; }, // The function name subsection ... consists of a name map // assigning function names to function indices. .function, .global, .data_segment => { - subsection_br = .fixed(try r.take(try r.takeLeb128(u32))); - const entries = try r.takeLeb128(u32); - try bw.print( + const size = try std.leb.readUleb128(u32, reader); + const entries = try std.leb.readUleb128(u32, reader); + try writer.print( + \\size {d} \\names {d} \\ - , .{entries}); + , .{ size, entries }); for (0..entries) |_| { - const index = try r.takeLeb128(u32); - const name = try r.take(try r.takeLeb128(u32)); - try bw.print( + const index = try std.leb.readUleb128(u32, reader); + const name_len = try std.leb.readUleb128(u32, reader); + if (reader.context.pos + name_len > data.len) return error.UnexpectedEndOfStream; + const name = data[reader.context.pos..][0..name_len]; + reader.context.pos += name.len; + + try writer.print( \\index {d} \\name {s} \\ , .{ index, name }); } - if (subsection_br.seek != subsection_br.buffer.len) return error.BadSubsectionSize; }, // The local name subsection ... consists of an indirect name @@ -2632,49 +2727,52 @@ const WasmDumper = struct { } } - fn parseDumpProducers(r: *std.io.Reader, bw: *Writer) !void { - const field_count = try r.takeLeb128(u32); - try bw.print( - \\fields {d} - \\ - , .{field_count}); + fn parseDumpProducers(reader: anytype, writer: anytype, data: []const u8) !void { + const field_count = try std.leb.readUleb128(u32, reader); + try writer.print("fields {d}\n", .{field_count}); var current_field: u32 = 0; while (current_field < field_count) : (current_field += 1) { - const field_name = try r.take(try r.takeLeb128(u32)); - const value_count = try r.takeLeb128(u32); - try bw.print( + const field_name_length = try std.leb.readUleb128(u32, reader); + const field_name = data[reader.context.pos..][0..field_name_length]; + reader.context.pos += field_name_length; + + const value_count = try std.leb.readUleb128(u32, reader); + try writer.print( \\field_name {s} \\values {d} - \\ , .{ field_name, value_count }); + try writer.writeByte('\n'); var current_value: u32 = 0; while (current_value < value_count) : (current_value += 1) { - const value = try r.take(try r.takeLeb128(u32)); - const version = try r.take(try r.takeLeb128(u32)); - try bw.print( + const value_length = try std.leb.readUleb128(u32, reader); + const value = data[reader.context.pos..][0..value_length]; + reader.context.pos += value_length; + + const version_length = try std.leb.readUleb128(u32, reader); + const version = data[reader.context.pos..][0..version_length]; + reader.context.pos += version_length; + + try writer.print( \\value_name {s} \\version {s} - \\ , .{ value, version }); + try writer.writeByte('\n'); } } } - fn parseDumpFeatures(r: *std.io.Reader, bw: *Writer) !void { - const feature_count = try r.takeLeb128(u32); - try bw.print( - \\features {d} - \\ - , .{feature_count}); + fn parseDumpFeatures(reader: anytype, writer: anytype, data: []const u8) !void { + const feature_count = try std.leb.readUleb128(u32, reader); + try writer.print("features {d}\n", .{feature_count}); var index: u32 = 0; while (index < feature_count) : (index += 1) { - const prefix_byte = try r.takeLeb128(u8); - const feature_name = try r.take(try r.takeLeb128(u32)); - try bw.print( - \\{c} {s} - \\ - , .{ prefix_byte, feature_name }); + const prefix_byte = try std.leb.readUleb128(u8, reader); + const name_length = try std.leb.readUleb128(u32, reader); + const feature_name = data[reader.context.pos..][0..name_length]; + reader.context.pos += name_length; + + try writer.print("{c} {s}\n", .{ prefix_byte, feature_name }); } } }; -- cgit v1.2.3