From bc2cf0c173465f4e3ac60fe2907dfedd2eebf8eb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Jul 2025 17:33:02 -0700 Subject: [PATCH] eliminate all uses of std.io.Writer.count except for CBE --- lib/std/debug.zig | 4 +- lib/std/fmt.zig | 6 +- lib/std/http/Client.zig | 14 ++--- lib/std/io/Reader.zig | 12 ++-- lib/std/io/Writer.zig | 117 +++++++++++++++-------------------- lib/std/zig/ErrorBundle.zig | 12 +++- src/arch/x86_64/Encoding.zig | 6 +- src/link/Elf/Atom.zig | 4 +- 8 files changed, 81 insertions(+), 94 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 4e1bbfcbd1..5da650266e 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1603,10 +1603,10 @@ test "manage resources correctly" { // self-hosted debug info is still too buggy if (builtin.zig_backend != .stage2_llvm) return error.SkipZigTest; - var writer: std.io.Writer = .discarding(&.{}); + var discarding: std.io.Writer.Discarding = .init(&.{}); var di = try SelfInfo.open(testing.allocator); defer di.deinit(); - try printSourceAtAddress(&di, &writer, showMyTrace(), io.tty.detectConfig(.stderr())); + try printSourceAtAddress(&di, &discarding.writer, showMyTrace(), io.tty.detectConfig(.stderr())); } noinline fn showMyTrace() usize { diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 56780c8593..aa6b5c1670 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -772,11 +772,11 @@ pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintErr /// Count the characters needed for format. pub fn count(comptime fmt: []const u8, args: anytype) usize { var trash_buffer: [64]u8 = undefined; - var w: Writer = .discarding(&trash_buffer); - w.print(fmt, args) catch |err| switch (err) { + var dw: Writer.Discarding = .init(&trash_buffer); + dw.writer.print(fmt, args) catch |err| switch (err) { error.WriteFailed => unreachable, }; - return w.count; + return @intCast(dw.count + dw.writer.end); } pub fn allocPrint(gpa: Allocator, comptime fmt: []const u8, args: anytype) Allocator.Error![]u8 { diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index b68f3291ab..838411bebc 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -1293,13 +1293,14 @@ pub const basic_authorization = struct { const user: Uri.Component = uri.user orelse .empty; const password: Uri.Component = uri.password orelse .empty; - var w: std.io.Writer = .discarding(&.{}); - user.formatUser(&w) catch unreachable; // discarding - const user_len = w.count; + var dw: std.io.Writer.Discarding = .init(&.{}); + user.formatUser(&dw.writer) catch unreachable; // discarding + const user_len = dw.count + dw.writer.end; - w.count = 0; - password.formatPassword(&w) catch unreachable; // discarding - const password_len = w.count; + dw.count = 0; + dw.writer.end = 0; + password.formatPassword(&dw.writer) catch unreachable; // discarding + const password_len = dw.count + dw.writer.end; return valueLength(@intCast(user_len), @intCast(password_len)); } @@ -1311,7 +1312,6 @@ pub const basic_authorization = struct { var buf: [max_user_len + ":".len + max_password_len]u8 = undefined; var w: std.io.Writer = .fixed(&buf); user.formatUser(&w) catch unreachable; // fixed - assert(w.count <= max_user_len); password.formatPassword(&w) catch unreachable; // fixed @memcpy(out[0..prefix.len], prefix); diff --git a/lib/std/io/Reader.zig b/lib/std/io/Reader.zig index 0d7b891899..6cfb21839f 100644 --- a/lib/std/io/Reader.zig +++ b/lib/std/io/Reader.zig @@ -132,10 +132,8 @@ pub fn stream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize { r.seek += n; return n; } - const before = w.count; const n = try r.vtable.stream(r, w, limit); assert(n <= @intFromEnum(limit)); - assert(w.count == before + n); return n; } @@ -158,17 +156,17 @@ pub fn discard(r: *Reader, limit: Limit) Error!usize { pub fn defaultDiscard(r: *Reader, limit: Limit) Error!usize { assert(r.seek == 0); assert(r.end == 0); - var w: Writer = .discarding(r.buffer); - const n = r.stream(&w, limit) catch |err| switch (err) { + var dw: Writer.Discarding = .init(r.buffer); + const n = r.stream(&dw.writer, limit) catch |err| switch (err) { error.WriteFailed => unreachable, error.ReadFailed => return error.ReadFailed, error.EndOfStream => return error.EndOfStream, }; if (n > @intFromEnum(limit)) { const over_amt = n - @intFromEnum(limit); - r.seek = w.end - over_amt; - r.end = w.end; - assert(r.end <= w.buffer.len); // limit may be exceeded only by an amount within buffer capacity. + r.seek = dw.writer.end - over_amt; + r.end = dw.writer.end; + assert(r.end <= dw.writer.buffer.len); // limit may be exceeded only by an amount within buffer capacity. return @intFromEnum(limit); } return n; diff --git a/lib/std/io/Writer.zig b/lib/std/io/Writer.zig index 8cf0e50a8b..fa2380edd3 100644 --- a/lib/std/io/Writer.zig +++ b/lib/std/io/Writer.zig @@ -14,12 +14,6 @@ vtable: *const VTable, buffer: []u8, /// In `buffer` before this are buffered bytes, after this is `undefined`. end: usize = 0, -/// Tracks total number of bytes written to this `Writer`. This value -/// only increases. In the case of fixed mode, this value always equals `end`. -/// -/// This value is maintained by the interface; `VTable` function -/// implementations need not modify it. -count: usize = 0, pub const VTable = struct { /// Sends bytes to the logical sink. A write will only be sent here if it @@ -117,8 +111,7 @@ pub const FileError = error{ Unimplemented, }; -/// Writes to `buffer` and returns `error.WriteFailed` when it is full. Unless -/// modified externally, `count` will always equal `end`. +/// Writes to `buffer` and returns `error.WriteFailed` when it is full. pub fn fixed(buffer: []u8) Writer { return .{ .vtable = &.{ .drain = fixedDrain }, @@ -137,16 +130,6 @@ pub const failing: Writer = .{ }, }; -pub fn discarding(buffer: []u8) Writer { - return .{ - .vtable = &.{ - .drain = discardingDrain, - .sendFile = discardingSendFile, - }, - .buffer = buffer, - }; -} - /// Returns the contents not yet drained. pub fn buffered(w: *const Writer) []u8 { return w.buffer[0..w.end]; @@ -178,12 +161,7 @@ pub fn writeSplat(w: *Writer, data: []const []const u8, splat: usize) Error!usiz assert(data.len > 0); const buffer = w.buffer; const count = countSplat(data, splat); - if (w.end + count > buffer.len) { - const n = try w.vtable.drain(w, data, splat); - w.count += n; - return n; - } - w.count += count; + if (w.end + count > buffer.len) return w.vtable.drain(w, data, splat); for (data) |bytes| { @memcpy(buffer[w.end..][0..bytes.len], bytes); w.end += bytes.len; @@ -236,7 +214,6 @@ pub fn writeSplatHeader( if (new_end <= w.buffer.len) { @memcpy(w.buffer[w.end..][0..header.len], header); w.end = new_end; - w.count += header.len; return header.len + try writeSplat(w, data, splat); } var vecs: [8][]const u8 = undefined; // Arbitrarily chosen size. @@ -249,9 +226,7 @@ pub fn writeSplatHeader( if (vecs.len - i == 0) break; } const new_splat = if (vecs[i - 1].ptr == data[data.len - 1].ptr) splat else 1; - const n = try w.vtable.drain(w, vecs[0..i], new_splat); - w.count += n; - return n; + return w.vtable.drain(w, vecs[0..i], new_splat); } /// Equivalent to `writeSplatHeader` but writes at most `limit` bytes. @@ -429,7 +404,6 @@ pub fn ensureUnusedCapacity(w: *Writer, n: usize) Error!void { pub fn undo(w: *Writer, n: usize) void { w.end -= n; - w.count -= n; } /// After calling `writableSliceGreedy`, this function tracks how many bytes @@ -440,13 +414,11 @@ pub fn advance(w: *Writer, n: usize) void { const new_end = w.end + n; assert(new_end <= w.buffer.len); w.end = new_end; - w.count += n; } /// After calling `writableVector`, this function tracks how many bytes were /// written to it. pub fn advanceVector(w: *Writer, n: usize) usize { - w.count += n; return consume(w, n); } @@ -504,12 +476,9 @@ pub fn write(w: *Writer, bytes: []const u8) Error!usize { @branchHint(.likely); @memcpy(w.buffer[w.end..][0..bytes.len], bytes); w.end += bytes.len; - w.count += bytes.len; return bytes.len; } - const n = try w.vtable.drain(w, &.{bytes}, 1); - w.count += n; - return n; + return w.vtable.drain(w, &.{bytes}, 1); } /// Asserts `buffer` capacity exceeds `preserve_length`. @@ -519,7 +488,6 @@ pub fn writePreserve(w: *Writer, preserve_length: usize, bytes: []const u8) Erro @branchHint(.likely); @memcpy(w.buffer[w.end..][0..bytes.len], bytes); w.end += bytes.len; - w.count += bytes.len; return bytes.len; } const temp_end = w.end -| preserve_length; @@ -527,7 +495,6 @@ pub fn writePreserve(w: *Writer, preserve_length: usize, bytes: []const u8) Erro w.end = temp_end; defer w.end += preserved.len; const n = try w.vtable.drain(w, &.{bytes}, 1); - w.count += n; assert(w.end <= temp_end + preserved.len); @memmove(w.buffer[w.end..][0..preserved.len], preserved); return n; @@ -560,15 +527,11 @@ pub fn print(w: *Writer, comptime format: []const u8, args: anytype) Error!void pub fn writeByte(w: *Writer, byte: u8) Error!void { while (w.buffer.len - w.end == 0) { const n = try w.vtable.drain(w, &.{&.{byte}}, 1); - if (n > 0) { - w.count += 1; - return; - } + if (n > 0) return; } else { @branchHint(.likely); w.buffer[w.end] = byte; w.end += 1; - w.count += 1; } } @@ -581,7 +544,6 @@ pub fn writeBytePreserve(w: *Writer, preserve_length: usize, byte: u8) Error!voi @branchHint(.likely); w.buffer[w.end] = byte; w.end += 1; - w.count += 1; } } @@ -690,12 +652,10 @@ pub fn sendFileHeader( if (new_end <= w.buffer.len) { @memcpy(w.buffer[w.end..][0..header.len], header); w.end = new_end; - w.count += header.len; return header.len + try w.vtable.sendFile(w, file_reader, limit); } const buffered_contents = limit.slice(file_reader.interface.buffered()); const n = try w.vtable.drain(w, &.{ header, buffered_contents }, 1); - w.count += n; file_reader.interface.toss(n - header.len); return n; } @@ -1950,29 +1910,52 @@ pub fn failingSendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) File return error.WriteFailed; } -pub fn discardingDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize { - const slice = data[0 .. data.len - 1]; - const pattern = data[slice.len..]; - var written: usize = pattern.len * splat; - for (slice) |bytes| written += bytes.len; - w.end = 0; - return written; -} +pub const Discarding = struct { + count: u64, + writer: Writer, -pub fn discardingSendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize { - if (File.Handle == void) return error.Unimplemented; - w.end = 0; - if (file_reader.getSize()) |size| { - const n = limit.minInt64(size - file_reader.pos); - file_reader.seekBy(@intCast(n)) catch return error.Unimplemented; - w.end = 0; - return n; - } else |_| { - // Error is observable on `file_reader` instance, and it is better to - // treat the file as a pipe. - return error.Unimplemented; + pub fn init(buffer: []u8) Discarding { + return .{ + .count = 0, + .writer = .{ + .vtable = &.{ + .drain = Discarding.drain, + .sendFile = Discarding.sendFile, + }, + .buffer = buffer, + }, + }; } -} + + pub fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize { + const d: *Discarding = @alignCast(@fieldParentPtr("writer", w)); + const slice = data[0 .. data.len - 1]; + const pattern = data[slice.len..]; + var written: usize = pattern.len * splat; + for (slice) |bytes| written += bytes.len; + d.count += w.end + written; + w.end = 0; + return written; + } + + pub fn sendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize { + if (File.Handle == void) return error.Unimplemented; + const d: *Discarding = @alignCast(@fieldParentPtr("writer", w)); + d.count += w.end; + w.end = 0; + if (file_reader.getSize()) |size| { + const n = limit.minInt64(size - file_reader.pos); + file_reader.seekBy(@intCast(n)) catch return error.Unimplemented; + w.end = 0; + d.count += n; + return n; + } else |_| { + // Error is observable on `file_reader` instance, and it is better to + // treat the file as a pipe. + return error.Unimplemented; + } + } +}; /// Removes the first `n` bytes from `buffer` by shifting buffer contents, /// returning how many bytes are left after consuming the entire buffer, or @@ -2219,9 +2202,7 @@ pub const Allocating = struct { } pub fn shrinkRetainingCapacity(a: *Allocating, new_len: usize) void { - const shrink_by = a.writer.end - new_len; a.writer.end = new_len; - a.writer.count -= shrink_by; } pub fn clearRetainingCapacity(a: *Allocating) void { diff --git a/lib/std/zig/ErrorBundle.zig b/lib/std/zig/ErrorBundle.zig index 22b3a7b460..b398d5eb4c 100644 --- a/lib/std/zig/ErrorBundle.zig +++ b/lib/std/zig/ErrorBundle.zig @@ -195,22 +195,30 @@ fn renderErrorMessageToWriter( ) (Writer.Error || std.posix.UnexpectedError)!void { const ttyconf = options.ttyconf; const err_msg = eb.getErrorMessage(err_msg_index); - const prefix_start = w.count; if (err_msg.src_loc != .none) { const src = eb.extraData(SourceLocation, @intFromEnum(err_msg.src_loc)); + var prefix: std.io.Writer.Discarding = .init(&.{}); try w.splatByteAll(' ', indent); + prefix.count += indent; try ttyconf.setColor(w, .bold); try w.print("{s}:{d}:{d}: ", .{ eb.nullTerminatedString(src.data.src_path), src.data.line + 1, src.data.column + 1, }); + try prefix.writer.print("{s}:{d}:{d}: ", .{ + eb.nullTerminatedString(src.data.src_path), + src.data.line + 1, + src.data.column + 1, + }); try ttyconf.setColor(w, color); try w.writeAll(kind); + prefix.count += kind.len; try w.writeAll(": "); + prefix.count += 2; // This is the length of the part before the error message: // e.g. "file.zig:4:5: error: " - const prefix_len = w.count - prefix_start; + const prefix_len: usize = @intCast(prefix.count); try ttyconf.setColor(w, .reset); try ttyconf.setColor(w, .bold); if (err_msg.count == 1) { diff --git a/src/arch/x86_64/Encoding.zig b/src/arch/x86_64/Encoding.zig index 6956b25a3f..9b6f6bac5c 100644 --- a/src/arch/x86_64/Encoding.zig +++ b/src/arch/x86_64/Encoding.zig @@ -1016,8 +1016,8 @@ fn estimateInstructionLength(prefix: Prefix, encoding: Encoding, ops: []const Op // By using a buffer with maximum length of encoded instruction, we can use // the `end` field of the Writer for the count. var buf: [16]u8 = undefined; - var trash = std.io.Writer.discarding(&buf); - inst.encode(&trash, .{ + var trash: std.io.Writer.Discarding = .init(&buf); + inst.encode(&trash.writer, .{ .allow_frame_locs = true, .allow_symbols = true, }) catch { @@ -1027,7 +1027,7 @@ fn estimateInstructionLength(prefix: Prefix, encoding: Encoding, ops: []const Op // (`estimateInstructionLength`) has the wrong function signature. @panic("unexpected failure to encode"); }; - return @intCast(trash.end); + return trash.writer.end; } const mnemonic_to_encodings_map = init: { diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 14951e7217..5966bae02c 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -1390,8 +1390,8 @@ const x86_64 = struct { // TODO: hack to force imm32s in the assembler .{ .imm = .s(-129) }, }, t) catch return false; - var trash = std.io.Writer.discarding(&.{}); - inst.encode(&trash, .{}) catch return false; + var trash: std.io.Writer.Discarding = .init(&.{}); + inst.encode(&trash.writer, .{}) catch return false; return true; }, else => return false,