update some stuff to std.io API

This commit is contained in:
Andrew Kelley 2025-06-23 03:48:12 -07:00
parent 52b3275eb2
commit af24e722fb
13 changed files with 102 additions and 67 deletions

View File

@ -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;
};

View File

@ -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();
}

View File

@ -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;

View File

@ -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,

View File

@ -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.

View File

@ -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);

View File

@ -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`.

View File

@ -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 {

View File

@ -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,

View File

@ -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,
});

View File

@ -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);
}

View File

@ -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);
}
};

View File

@ -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();