From d62f22cc4d2f17db0877cd9316bf8be9470d04a5 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 21 Apr 2025 16:32:37 -0400 Subject: [PATCH] link: port to `std.io.BufferedReader` API changes --- lib/std/io/BufferedReader.zig | 74 ++++++++++++++++++++++++----------- src/Compilation.zig | 25 ++++++------ src/Zcu/PerThread.zig | 4 +- src/link.zig | 48 ++++++++++++++++------- src/main.zig | 4 +- 5 files changed, 103 insertions(+), 52 deletions(-) diff --git a/lib/std/io/BufferedReader.zig b/lib/std/io/BufferedReader.zig index c6d711865d..2a76af547d 100644 --- a/lib/std/io/BufferedReader.zig +++ b/lib/std/io/BufferedReader.zig @@ -484,11 +484,13 @@ fn peekDelimiterInclusiveUnlessEnd(br: *BufferedReader, delimiter: u8) Delimiter /// found. Does not write the delimiter itself. /// /// Returns number of bytes streamed. -pub fn streamReadDelimiter(br: *BufferedReader, bw: *BufferedWriter, delimiter: u8) Reader.Error!usize { - _ = br; - _ = bw; - _ = delimiter; - @panic("TODO"); +pub fn streamToDelimiter(br: *BufferedReader, bw: *BufferedWriter, delimiter: u8) Reader.RwError!usize { + const amount, const to = try br.streamToAny(bw, delimiter, .unlimited); + return switch (to) { + .delimiter => amount, + .limit => unreachable, + .end => error.EndOfStream, + }; } /// Appends to `bw` contents by reading from the stream until `delimiter` is found. @@ -497,18 +499,19 @@ pub fn streamReadDelimiter(br: *BufferedReader, bw: *BufferedWriter, delimiter: /// Succeeds if stream ends before delimiter found. /// /// Returns number of bytes streamed. The end is not signaled to the writer. -pub fn streamReadDelimiterExclusive( +pub fn streamToDelimiterOrEnd( br: *BufferedReader, bw: *BufferedWriter, delimiter: u8, -) Reader.ShortError!usize { - _ = br; - _ = bw; - _ = delimiter; - @panic("TODO"); +) Reader.RwAllError!usize { + const amount, const to = try br.streamToAny(bw, delimiter, .unlimited); + return switch (to) { + .delimiter, .end => amount, + .limit => unreachable, + }; } -pub const StreamDelimiterLimitedError = Reader.ShortError || error{ +pub const StreamDelimiterLimitedError = Reader.RwAllError || error{ /// Stream ended before the delimiter was found. EndOfStream, /// The delimiter was not found within the limit. @@ -517,19 +520,46 @@ pub const StreamDelimiterLimitedError = Reader.ShortError || error{ /// Appends to `bw` contents by reading from the stream until `delimiter` is found. /// Does not write the delimiter itself. -// +/// /// Returns number of bytes streamed. -pub fn streamReadDelimiterLimited( +pub fn streamToDelimiterOrLimit( br: *BufferedReader, bw: *BufferedWriter, delimiter: u8, limit: Reader.Limit, ) StreamDelimiterLimitedError!usize { - _ = br; - _ = bw; - _ = delimiter; - _ = limit; - @panic("TODO"); + const amount, const to = try br.streamToAny(bw, delimiter, limit); + return switch (to) { + .delimiter => amount, + .limit => error.StreamTooLong, + .end => error.EndOfStream, + }; +} + +fn streamToAny( + br: *BufferedReader, + bw: *BufferedWriter, + delimiter: ?u8, + limit: Reader.Limit, +) Reader.RwAllError!struct { usize, enum { delimiter, limit, end } } { + var amount: usize = 0; + var remaining = limit; + while (remaining.nonzero()) { + const available = remaining.slice(br.peekGreedy(1) catch |err| switch (err) { + error.ReadFailed => |e| return e, + error.EndOfStream => return .{ amount, .end }, + }); + if (delimiter) |d| if (std.mem.indexOfScalar(u8, available, d)) |delimiter_index| { + try bw.writeAll(available[0..delimiter_index]); + br.toss(delimiter_index + 1); + return .{ amount + delimiter_index, .delimiter }; + }; + try bw.writeAll(available); + br.toss(available.len); + amount += available.len; + remaining = remaining.subtract(available.len).?; + } + return .{ amount, .limit }; } /// Reads from the stream until specified byte is found, discarding all data, @@ -824,15 +854,15 @@ test peekDelimiterExclusive { return error.Unimplemented; } -test streamReadDelimiter { +test streamToDelimiter { return error.Unimplemented; } -test streamReadDelimiterExclusive { +test streamToDelimiterOrEnd { return error.Unimplemented; } -test streamReadDelimiterLimited { +test streamToDelimiterOrLimit { return error.Unimplemented; } diff --git a/src/Compilation.zig b/src/Compilation.zig index 07a83b97cb..42e8f78e18 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1000,11 +1000,14 @@ pub const CObject = struct { defer file.close(); file.seekTo(diag.src_loc.offset + 1 - diag.src_loc.column) catch break :source_line 0; - var line = std.ArrayList(u8).init(eb.gpa); - defer line.deinit(); - file.reader().readUntilDelimiterArrayList(&line, '\n', 1 << 10) catch break :source_line 0; - - break :source_line try eb.addString(line.items); + var buffer: [1 << 10]u8 = undefined; + var fr = file.reader(); + var br = fr.interface().buffered(&buffer); + var bw: std.io.BufferedWriter = undefined; + bw.initFixed(&buffer); + break :source_line try eb.addString( + buffer[0 .. br.streamToDelimiterOrEnd(&bw, '\n') catch break :source_line 0], + ); }; return .{ @@ -3781,7 +3784,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { if (!refs.contains(anal_unit)) continue; } - std.log.scoped(.zcu).debug("analysis error '{s}' reported from unit '{}'", .{ + std.log.scoped(.zcu).debug("analysis error '{s}' reported from unit '{f}'", .{ error_msg.msg, zcu.fmtAnalUnit(anal_unit), }); @@ -3941,12 +3944,12 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { // This AU is referenced and has a transitive compile error, meaning it referenced something with a compile error. // However, we haven't reported any such error. // This is a compiler bug. - var stderr = std.debug.lockStdErr2(&.{}); - defer std.debug.unlockStdErr(); - try stderr.writeAll("referenced transitive analysis errors, but none actually emitted\n"); - try stderr.print("{} [transitive failure]\n", .{zcu.fmtAnalUnit(failed_unit)}); + var stderr_bw = std.debug.lockStderrWriter(&.{}); + defer std.debug.unlockStderrWriter(); + try stderr_bw.writeAll("referenced transitive analysis errors, but none actually emitted\n"); + try stderr_bw.print("{f} [transitive failure]\n", .{zcu.fmtAnalUnit(failed_unit)}); while (ref) |r| { - try stderr.print("referenced by: {}{s}\n", .{ + try stderr_bw.print("referenced by: {f}{s}\n", .{ zcu.fmtAnalUnit(r.referencer), if (zcu.transitive_failed_analysis.contains(r.referencer)) " [transitive failure]" else "", }); diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 8a68a6de31..e7a9ce9a02 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -268,7 +268,7 @@ pub fn updateFile( file.zir = try AstGen.generate(gpa, file.tree.?); Zcu.saveZirCache(gpa, cache_file, stat, file.zir.?) catch |err| switch (err) { error.OutOfMemory => |e| return e, - else => log.warn("unable to write cached ZIR code for {} to {}{s}: {s}", .{ + else => log.warn("unable to write cached ZIR code for {f} to {f}{s}: {s}", .{ file.path.fmt(comp), cache_directory, &hex_digest, @errorName(err), }), }; @@ -276,7 +276,7 @@ pub fn updateFile( .zon => { file.zoir = try ZonGen.generate(gpa, file.tree.?, .{}); Zcu.saveZoirCache(cache_file, stat, file.zoir.?) catch |err| { - log.warn("unable to write cached ZOIR code for {} to {}{s}: {s}", .{ + log.warn("unable to write cached ZOIR code for {f} to {f}{s}: {s}", .{ file.path.fmt(comp), cache_directory, &hex_digest, @errorName(err), }); }; diff --git a/src/link.zig b/src/link.zig index d76762f11d..198f59425c 100644 --- a/src/link.zig +++ b/src/link.zig @@ -1023,15 +1023,24 @@ pub const File = struct { }; } - fn loadGnuLdScript(base: *File, path: Path, parent_query: UnresolvedInput.Query, file: fs.File) anyerror!void { + fn loadGnuLdScript( + base: *File, + path: Path, + parent_query: UnresolvedInput.Query, + file: fs.File, + ) anyerror!void { const diags = &base.comp.link_diags; const gpa = base.comp.gpa; const stat = try file.stat(); const size = std.math.cast(u32, stat.size) orelse return error.FileTooBig; const buf = try gpa.alloc(u8, size); defer gpa.free(buf); - const n = try file.preadAll(buf, 0); - if (buf.len != n) return error.UnexpectedEndOfFile; + var fr = file.reader(); + var br = fr.interface().unbuffered(); + br.readSlice(buf) catch |err| switch (err) { + error.ReadFailed => if (fr.err) |_| unreachable else |e| return e, + error.EndOfStream => return error.UnexpectedEndOfFile, + }; var ld_script = try LdScript.parse(gpa, diags, path, buf); defer ld_script.deinit(gpa); for (ld_script.args) |arg| { @@ -2092,24 +2101,33 @@ fn resolvePathInputLib( }; 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 buf = ld_script_bytes.items[0..n]; - if (mem.startsWith(u8, buf, std.elf.MAGIC) or mem.startsWith(u8, buf, std.elf.ARMAG)) { - // Appears to be an ELF or archive file. - return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query); + var fr = file.reader(); + var br = fr.interface().unbuffered(); + ok: { + br.readSlice(ld_script_bytes.items) catch |err| switch (err) { + error.ReadFailed => fatal("failed to read '{f'}': {s}", .{ + test_path, + @errorName(if (fr.err) |_| unreachable else |e| e), + }), + error.EndOfStream => break :ok, + }; + if (mem.startsWith(u8, ld_script_bytes.items, std.elf.MAGIC) or + mem.startsWith(u8, ld_script_bytes.items, std.elf.ARMAG)) + { + // Appears to be an ELF or archive file. + return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query); + } } const stat = file.stat() catch |err| fatal("failed to stat {f}: {s}", .{ test_path, @errorName(err) }); const size = std.math.cast(u32, stat.size) orelse fatal("{f}: linker script too big", .{test_path}); try ld_script_bytes.resize(gpa, size); - const buf2 = ld_script_bytes.items[n..]; - const n2 = file.preadAll(buf2, n) catch |err| - fatal("failed to read {f}: {s}", .{ test_path, @errorName(err) }); - if (n2 != buf2.len) fatal("failed to read {f}: unexpected end of file", .{test_path}); - var diags = Diags.init(gpa); + br.readSlice(ld_script_bytes.items[@intCast(fr.pos)..]) catch |err| switch (err) { + error.ReadFailed => if (fr.err) |_| unreachable else |e| fatal("failed to read {f}: {s}", .{ test_path, @errorName(e) }), + error.EndOfStream => fatal("failed to read {f}: unexpected end of file", .{test_path}), + }; + var diags: Diags = .init(gpa); defer diags.deinit(); const ld_script_result = LdScript.parse(gpa, &diags, test_path, ld_script_bytes.items); if (diags.hasErrors()) { diff --git a/src/main.zig b/src/main.zig index 68ee6d854e..70805c1165 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3511,7 +3511,7 @@ fn buildOutputType( if (t.arch == target.cpu.arch and t.os == target.os.tag) { // If there's a `glibc_min`, there's also an `os_ver`. if (t.glibc_min) |glibc_min| { - std.log.info("zig can provide libc for related target {s}-{s}.{}-{s}.{d}.{d}", .{ + std.log.info("zig can provide libc for related target {s}-{s}.{f}-{s}.{d}.{d}", .{ @tagName(t.arch), @tagName(t.os), t.os_ver.?, @@ -3520,7 +3520,7 @@ fn buildOutputType( glibc_min.minor, }); } else if (t.os_ver) |os_ver| { - std.log.info("zig can provide libc for related target {s}-{s}.{}-{s}", .{ + std.log.info("zig can provide libc for related target {s}-{s}.{f}-{s}", .{ @tagName(t.arch), @tagName(t.os), os_ver,