From af24e722fba2245d50473dc7a6fb7671851e07dc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 23 Jun 2025 03:48:12 -0700 Subject: [PATCH] update some stuff to std.io API --- lib/std/debug.zig | 2 +- lib/std/fs/Dir.zig | 7 ++---- lib/std/fs/File.zig | 19 ++++++++++------ lib/std/http.zig | 9 +++++++- lib/std/http/Client.zig | 4 ++-- lib/std/http/test.zig | 3 ++- lib/std/io/Reader.zig | 34 ++++++++++++++++++++++++---- lib/std/io/Writer.zig | 44 ++++++++++++++++++++---------------- lib/std/net.zig | 8 ++----- lib/std/tar/test.zig | 6 ++--- lib/std/tz.zig | 8 ++++--- lib/std/zig/system/linux.zig | 15 +++++------- lib/std/zip/test.zig | 10 ++++---- 13 files changed, 102 insertions(+), 67 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 603cc3f3ff..01a80d309c 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -588,7 +588,7 @@ pub fn panicExtra( // a minor annoyance with this is that it will result in the NoSpaceLeft // error being part of the @panic stack trace (but that error should // only happen rarely) - const msg = if (bw.print(format, args)) |_| bw.getWritten() else |_| blk: { + const msg = if (bw.print(format, args)) |_| bw.buffered() else |_| blk: { @memcpy(buf[size..], trunc_msg); break :blk &buf; }; diff --git a/lib/std/fs/Dir.zig b/lib/std/fs/Dir.zig index 4d79ab1b70..820bf40563 100644 --- a/lib/std/fs/Dir.zig +++ b/lib/std/fs/Dir.zig @@ -2612,10 +2612,7 @@ pub fn updateFile( var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = actual_mode }); defer atomic_file.deinit(); - var src_reader: File.Reader = .{ - .file = src_file, - .size = src_stat.size, - }; + var src_reader: File.Reader = .initSize(src_file, &.{}, src_stat.size); var buffer: [2000]u8 = undefined; var dest_writer = atomic_file.file_writer.writer(&buffer); @@ -2658,7 +2655,7 @@ pub fn copyFile( var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = mode }); defer atomic_file.deinit(); - try copy_file(in_file.handle, atomic_file.file.handle, size); + try copy_file(in_file.handle, atomic_file.file_writer.file.handle, size); try atomic_file.finish(); } diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index fb4d1314c7..2ab77a6bf5 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -961,6 +961,14 @@ pub const Reader = struct { }; } + pub fn initSize(file: File, buffer: []u8, size: u64) Reader { + return .{ + .file = file, + .interface = initInterface(buffer), + .size = size, + }; + } + pub fn getSize(r: *Reader) GetEndPosError!u64 { return r.size orelse { if (r.size_err) |err| return err; @@ -1436,18 +1444,15 @@ pub fn readerStreaming(file: File) Reader { /// /// Positional is more threadsafe, since the global seek position is not /// affected. -pub fn writer(file: File) Writer { - return .{ .file = file }; +pub fn writer(file: File, buffer: []u8) Writer { + return .init(file, buffer); } /// Positional is more threadsafe, since the global seek position is not /// affected, but when such syscalls are not available, preemptively choosing /// `Writer.Mode.streaming` will skip a failed syscall. -pub fn writerStreaming(file: File) Writer { - return .{ - .file = file, - .mode = .streaming, - }; +pub fn writerStreaming(file: File, buffer: []u8) Writer { + return .initMode(file, buffer, .streaming); } const range_off: windows.LARGE_INTEGER = 0; diff --git a/lib/std/http.zig b/lib/std/http.zig index 56fad6c502..1ff20dd3f6 100644 --- a/lib/std/http.zig +++ b/lib/std/http.zig @@ -433,12 +433,18 @@ pub const Reader = struct { /// /// See also: /// * `interfaceDecompressing` - pub fn bodyReader(reader: *Reader, transfer_encoding: TransferEncoding, content_length: ?u64) std.io.Reader { + pub fn bodyReader( + reader: *Reader, + buffer: []u8, + transfer_encoding: TransferEncoding, + content_length: ?u64, + ) std.io.Reader { assert(reader.state == .received_head); return switch (transfer_encoding) { .chunked => { reader.state = .{ .body_remaining_chunk_len = .head }; return .{ + .buffer = buffer, .context = reader, .vtable = &.{ .read = chunkedRead, @@ -450,6 +456,7 @@ pub const Reader = struct { if (content_length) |len| { reader.state = .{ .body_remaining_content_length = len }; return .{ + .buffer = buffer, .context = reader, .vtable = &.{ .read = contentLengthRead, diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index 35ae366c96..a2bfae3d91 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -667,11 +667,11 @@ pub const Response = struct { /// /// See also: /// * `readerDecompressing` - pub fn reader(response: *Response) std.io.Reader { + pub fn reader(response: *Response, buffer: []u8) std.io.Reader { const req = response.request; if (!req.method.responseHasBody()) return .ending; const head = &response.head; - return req.reader.bodyReader(head.transfer_encoding, head.content_length); + return req.reader.bodyReader(buffer, head.transfer_encoding, head.content_length); } /// If compressed body has been negotiated this will return decompressed bytes. diff --git a/lib/std/http/test.zig b/lib/std/http/test.zig index 3a2eac7c95..6a6e284f11 100644 --- a/lib/std/http/test.zig +++ b/lib/std/http/test.zig @@ -1198,8 +1198,9 @@ test "redirect to different connection" { try req.sendBodiless(); var response = try req.receiveHead(&redirect_buffer); + var reader = response.reader(&.{}); - const body = try response.reader().readRemainingAlloc(gpa, .limited(8192)); + const body = try reader.allocRemaining(gpa, .limited(8192)); defer gpa.free(body); try expectEqualStrings("good job, you pass", body); diff --git a/lib/std/io/Reader.zig b/lib/std/io/Reader.zig index 7dffea827b..4690562706 100644 --- a/lib/std/io/Reader.zig +++ b/lib/std/io/Reader.zig @@ -162,9 +162,9 @@ pub fn defaultDiscard(r: *Reader, limit: Limit) Error!usize { }; if (n > @intFromEnum(limit)) { const over_amt = n - @intFromEnum(limit); - assert(over_amt <= w.buffer.len); // limit may be exceeded only by an amount within buffer capacity. 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. return @intFromEnum(limit); } return n; @@ -740,12 +740,20 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { } if (seek > 0) { const remainder = buffer[seek..]; - std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); + @memmove(buffer[0..remainder.len], remainder); r.end = remainder.len; r.seek = 0; } + var writer: Writer = .{ + .buffer = r.buffer, + .vtable = &.{ .drain = Writer.fixedDrain }, + }; while (r.end < r.buffer.len) { - const n = try r.unbuffered_reader.readVec(&.{r.buffer[r.end..]}); + writer.end = r.end; + const n = r.vtable.stream(r, &writer, .limited(r.buffer.len - r.end)) catch |err| switch (err) { + error.WriteFailed => unreachable, + else => |e| return e, + }; const prev_end = r.end; r.end = prev_end + n; if (std.mem.indexOfScalarPos(u8, r.buffer[0..r.end], prev_end, delimiter)) |end| { @@ -929,8 +937,16 @@ pub fn fill(r: *Reader, n: usize) Error!void { return; } rebaseCapacity(r, n); + var writer: Writer = .{ + .buffer = r.buffer, + .vtable = &.{ .drain = Writer.fixedDrain }, + }; while (r.end < r.seek + n) { - r.end += try r.unbuffered_reader.readVec(&.{r.buffer[r.end..]}); + writer.end = r.end; + r.end += r.vtable.stream(r, &writer, .limited(r.buffer.len - r.end)) catch |err| switch (err) { + error.WriteFailed => unreachable, + else => |e| return e, + }; } } @@ -941,7 +957,15 @@ pub fn fill(r: *Reader, n: usize) Error!void { /// Asserts buffer capacity is at least 1. pub fn fillMore(r: *Reader) Error!void { rebaseCapacity(r, 1); - r.end += try r.unbuffered_reader.readVec(&.{r.buffer[r.end..]}); + var writer: Writer = .{ + .buffer = r.buffer, + .end = r.end, + .vtable = &.{ .drain = Writer.fixedDrain }, + }; + r.end += r.vtable.stream(r, &writer, .limited(r.buffer.len - r.end)) catch |err| switch (err) { + error.WriteFailed => unreachable, + else => |e| return e, + }; } /// Returns the next byte from the stream or returns `error.EndOfStream`. diff --git a/lib/std/io/Writer.zig b/lib/std/io/Writer.zig index 509356de0c..1a1dff4fd7 100644 --- a/lib/std/io/Writer.zig +++ b/lib/std/io/Writer.zig @@ -1722,7 +1722,7 @@ pub fn unimplementedSendFile(w: *Writer, file_reader: *File.Reader, limit: Limit /// time to return an error. However, we still need to make sure all of the /// available buffer has been filled. Also, it may be called from `flush` in /// which case it should return successfully. -fn fixedDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize { +pub fn fixedDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize { if (data.len == 0) return 0; for (data[0 .. data.len - 1]) |bytes| { const dest = w.buffer[w.end..]; @@ -1860,24 +1860,30 @@ pub const Allocating = struct { pub fn init(allocator: Allocator) Allocating { return .{ .allocator = allocator, - .interface = init_interface, - .buffer = &.{}, + .interface = .{ + .buffer = &.{}, + .vtable = &vtable, + }, }; } pub fn initCapacity(allocator: Allocator, capacity: usize) error{OutOfMemory}!Allocating { return .{ .allocator = allocator, - .interface = init_interface, - .buffer = try allocator.alloc(u8, capacity), + .interface = .{ + .buffer = try allocator.alloc(u8, capacity), + .vtable = &vtable, + }, }; } pub fn initOwnedSlice(allocator: Allocator, slice: []u8) Allocating { return .{ .allocator = allocator, - .interface = init_interface, - .buffer = slice, + .interface = .{ + .buffer = slice, + .vtable = &vtable, + }, }; } @@ -1886,23 +1892,21 @@ pub const Allocating = struct { defer array_list.* = .empty; return .{ .allocator = allocator, - .interface = init_interface, - .buffer = array_list.allocatedSlice(), - .end = array_list.items.len, + .interface = .{ + .vtable = &vtable, + .buffer = array_list.allocatedSlice(), + .end = array_list.items.len, + }, }; } - const init_interface: Writer = .{ - .interface = .{ - .vtable = &.{ - .drain = Allocating.drain, - .sendFile = Allocating.sendFile, - }, - }, + const vtable: VTable = .{ + .drain = Allocating.drain, + .sendFile = Allocating.sendFile, }; pub fn deinit(a: *Allocating) void { - a.allocator.free(a.buffer); + a.allocator.free(a.interface.buffer); a.* = undefined; } @@ -1983,8 +1987,8 @@ pub const Allocating = struct { } fn setArrayList(a: *Allocating, list: std.ArrayListUnmanaged(u8)) void { - a.buffer = list.allocatedSlice(); - a.end = list.items.len; + a.interface.buffer = list.allocatedSlice(); + a.interface.end = list.items.len; } test Allocating { diff --git a/lib/std/net.zig b/lib/std/net.zig index aec9cf9077..a4b9c4f36a 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -970,7 +970,7 @@ pub fn getAddressList(gpa: Allocator, name: []const u8, port: u16) GetAddressLis const name_c = try gpa.dupeZ(u8, name); defer gpa.free(name_c); - const port_c = try std.fmt.allocPrintZ(gpa, "{}", .{port}); + const port_c = try std.fmt.allocPrintSentinel(gpa, "{d}", .{port}, 0); defer gpa.free(port_c); const hints: posix.addrinfo = .{ @@ -1985,10 +1985,7 @@ pub const Stream = struct { return .{ .stream = stream, .interface = .{ - .context = undefined, - .vtable = &.{ - .drain = drain, - }, + .vtable = &.{ .drain = drain }, .buffer = buffer, }, }; @@ -2090,7 +2087,6 @@ pub const Stream = struct { pub fn init(stream: Stream, buffer: []u8) Writer { return .{ .interface = .{ - .context = undefined, .vtable = &.{ .drain = drain, .sendFile = sendFile, diff --git a/lib/std/tar/test.zig b/lib/std/tar/test.zig index f3b55ae523..29d405f928 100644 --- a/lib/std/tar/test.zig +++ b/lib/std/tar/test.zig @@ -347,7 +347,7 @@ test "run test cases" { for (cases) |case| { var br: std.io.Reader = .fixed(case.data); - var iter = tar.iterator(&br, .{ + var iter: tar.Iterator = .init(&br, .{ .file_name_buffer = &file_name_buffer, .link_name_buffer = &link_name_buffer, }); @@ -391,7 +391,7 @@ test "pax/gnu long names with small buffer" { for (long_name_cases) |case| { var br: std.io.Reader = .fixed(case.data); - var iter = tar.iterator(&br, .{ + var iter: tar.Iterator = .init(&br, .{ .file_name_buffer = &min_file_name_buffer, .link_name_buffer = &min_link_name_buffer, }); @@ -412,7 +412,7 @@ test "insufficient buffer in Header name filed" { var min_link_name_buffer: [100]u8 = undefined; var br: std.io.Reader = .fixed(cases[0].data); - var iter = tar.iterator(&br, .{ + var iter: tar.Iterator = .init(&br, .{ .file_name_buffer = &min_file_name_buffer, .link_name_buffer = &min_link_name_buffer, }); diff --git a/lib/std/tz.zig b/lib/std/tz.zig index ccfe13fc3f..04cf4c9741 100644 --- a/lib/std/tz.zig +++ b/lib/std/tz.zig @@ -55,10 +55,12 @@ pub const Tz = struct { }; pub fn parse(allocator: std.mem.Allocator, reader: *std.io.Reader) !Tz { - var legacy_header = try reader.takeStruct(Header); + var legacy_header = (try reader.takeStruct(Header)).*; if (!std.mem.eql(u8, &legacy_header.magic, "TZif")) return error.BadHeader; - if (legacy_header.version != 0 and legacy_header.version != '2' and legacy_header.version != '3') return error.BadVersion; - + switch (legacy_header.version) { + 0, '2', '3' => {}, + else => return error.BadVersion, + } if (builtin.target.cpu.arch.endian() != std.builtin.Endian.big) { std.mem.byteSwapAllFields(@TypeOf(legacy_header.counts), &legacy_header.counts); } diff --git a/lib/std/zig/system/linux.zig b/lib/std/zig/system/linux.zig index 7233bbbb6d..4da0c2a55f 100644 --- a/lib/std/zig/system/linux.zig +++ b/lib/std/zig/system/linux.zig @@ -355,20 +355,17 @@ fn testParser( // When all the lines have been analyzed the finalize method is called. fn CpuinfoParser(comptime impl: anytype) type { return struct { - fn parse(arch: Target.Cpu.Arch, reader: anytype) anyerror!?Target.Cpu { - var line_buf: [1024]u8 = undefined; + fn parse(arch: Target.Cpu.Arch, reader: *std.io.Reader) !?Target.Cpu { var obj: impl = .{}; - - while (true) { - const line = (try reader.readUntilDelimiterOrEof(&line_buf, '\n')) orelse break; + while (reader.takeDelimiterExclusive('\n')) |line| { const colon_pos = mem.indexOfScalar(u8, line, ':') orelse continue; const key = mem.trimEnd(u8, line[0..colon_pos], " \t"); const value = mem.trimStart(u8, line[colon_pos + 1 ..], " \t"); - - if (!try obj.line_hook(key, value)) - break; + if (!try obj.line_hook(key, value)) break; + } else |err| switch (err) { + error.EndOfStream => {}, + else => |e| return e, } - return obj.finalize(arch); } }; diff --git a/lib/std/zip/test.zig b/lib/std/zip/test.zig index e167615af6..a173ce95a3 100644 --- a/lib/std/zip/test.zig +++ b/lib/std/zip/test.zig @@ -51,10 +51,10 @@ const FileStore = struct { uncompressed_size: usize, }; -fn makeZip(file_writer: *std.fs.File.Writer, files: []const File, options: WriteZipOptions) !std.io.Reader { +fn makeZip(file_writer: *std.fs.File.Writer, files: []const File, options: WriteZipOptions) !void { const store = try std.testing.allocator.alloc(FileStore, files.len); defer std.testing.allocator.free(store); - return makeZipWithStore(file_writer, files, options, store); + try makeZipWithStore(file_writer, files, options, store); } fn makeZipWithStore( @@ -312,7 +312,8 @@ fn testZipWithStore( var file = tmp.createFile(); defer file.close(); - var file_writer = file.writer(); + var buffer: [100]u8 = undefined; + var file_writer = file.writer(&buffer); try makeZipWithStore(&file_writer, test_files, write_opt, store); var file_reader = file_writer.moveToReader(); try zip.extract(tmp.dir, &file_reader, options); @@ -323,7 +324,8 @@ fn testZipError(expected_error: anyerror, file: File, options: zip.ExtractOption defer tmp.cleanup(); const tmp_file = tmp.createFile(); defer tmp_file.close(); - var file_writer = tmp_file.writer(); + var buffer: [100]u8 = undefined; + var file_writer = tmp_file.writer(&buffer); var store: [1]FileStore = undefined; try makeZipWithStore(&file_writer, &[_]File{file}, .{}, &store); var file_reader = file_writer.moveToReader();