authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-04-16 23:01:33-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:27-07:00
loga4fdda6ae04ffc72234d2c6baf22e13d9a5a99a3
treeb8389bab611e71d14db626c1491185aa20f4c7bd
parent20a784f7136143e4afa4d9d1d85fc0fa6d69d777

std.io: redo Reader and Writer yet again

explicit error sets ahoy matey delete some sus APIs from File that need to be reworked

53 files changed, 1374 insertions(+), 1966 deletions(-)

lib/compiler/test_runner.zig+10-5
...@@ -2,7 +2,6 @@...@@ -2,7 +2,6 @@
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4const std = @import("std");4const std = @import("std");
5const io = std.io;
6const testing = std.testing;5const testing = std.testing;
7const assert = std.debug.assert;6const assert = std.debug.assert;
87
...@@ -65,15 +64,21 @@ pub fn main() void {...@@ -65,15 +64,21 @@ pub fn main() void {
65 }64 }
66}65}
6766
67var stdin_buffer: [std.heap.page_size_min]u8 align(std.heap.page_size_min) = undefined;
68var stdout_buffer: [std.heap.page_size_min]u8 align(std.heap.page_size_min) = undefined;
69
68fn mainServer() !void {70fn mainServer() !void {
69 @disableInstrumentation();71 @disableInstrumentation();
72 var stdin_reader = std.fs.File.stdin().reader();
73 var stdout_writer = std.fs.File.stdout().writer();
74 var stdin_buffered_reader: std.io.BufferedReader = undefined;
75 stdin_buffered_reader.init(stdin_reader.interface(), &stdin_buffer);
76 var stdout_buffered_writer = stdout_writer.interface().buffered(&stdout_buffer);
70 var server = try std.zig.Server.init(.{77 var server = try std.zig.Server.init(.{
71 .gpa = fba.allocator(),78 .in = &stdin_buffered_reader,
72 .in = .stdin(),79 .out = &stdout_buffered_writer,
73 .out = .stdout(),
74 .zig_version = builtin.zig_version_string,80 .zig_version = builtin.zig_version_string,
75 });81 });
76 defer server.deinit();
7782
78 if (builtin.fuzz) {83 if (builtin.fuzz) {
79 const coverage_id = fuzzer_coverage_id();84 const coverage_id = fuzzer_coverage_id();
lib/std/Build.zig+2-3
...@@ -2766,9 +2766,8 @@ fn dumpBadDirnameHelp(...@@ -2766,9 +2766,8 @@ fn dumpBadDirnameHelp(
2766 comptime msg: []const u8,2766 comptime msg: []const u8,
2767 args: anytype,2767 args: anytype,
2768) anyerror!void {2768) anyerror!void {
2769 var buffered_writer = debug.lockStdErr2(&.{});2769 const w = debug.lockStderrWriter();
2770 defer debug.unlockStdErr();2770 defer debug.unlockStderrWriter();
2771 const w = &buffered_writer;
27722771
2773 const stderr: fs.File = .stderr();2772 const stderr: fs.File = .stderr();
2774 try w.print(msg, args);2773 try w.print(msg, args);
lib/std/Build/Cache.zig+1-1
...@@ -333,7 +333,7 @@ pub const Manifest = struct {...@@ -333,7 +333,7 @@ pub const Manifest = struct {
333 pub const Diagnostic = union(enum) {333 pub const Diagnostic = union(enum) {
334 none,334 none,
335 manifest_create: fs.File.OpenError,335 manifest_create: fs.File.OpenError,
336 manifest_read: anyerror,336 manifest_read: fs.File.ReadError,
337 manifest_lock: fs.File.LockError,337 manifest_lock: fs.File.LockError,
338 manifest_seek: fs.File.SeekError,338 manifest_seek: fs.File.SeekError,
339 file_open: FileOp,339 file_open: FileOp,
lib/std/Build/Fuzz.zig+6-6
...@@ -124,9 +124,9 @@ fn rebuildTestsWorkerRunFallible(run: *Step.Run, ttyconf: std.io.tty.Config, par...@@ -124,9 +124,9 @@ fn rebuildTestsWorkerRunFallible(run: *Step.Run, ttyconf: std.io.tty.Config, par
124 const show_stderr = compile.step.result_stderr.len > 0;124 const show_stderr = compile.step.result_stderr.len > 0;
125125
126 if (show_error_msgs or show_compile_errors or show_stderr) {126 if (show_error_msgs or show_compile_errors or show_stderr) {
127 var bw = std.debug.lockStdErr2(&.{});127 const bw = std.debug.lockStderrWriter();
128 defer std.debug.unlockStdErr();128 defer std.debug.unlockStderrWriter();
129 build_runner.printErrorMessages(gpa, &compile.step, .{ .ttyconf = ttyconf }, &bw, false) catch {};129 build_runner.printErrorMessages(gpa, &compile.step, .{ .ttyconf = ttyconf }, bw, false) catch {};
130 }130 }
131131
132 const rebuilt_bin_path = result catch |err| switch (err) {132 const rebuilt_bin_path = result catch |err| switch (err) {
...@@ -151,9 +151,9 @@ fn fuzzWorkerRun(...@@ -151,9 +151,9 @@ fn fuzzWorkerRun(
151151
152 run.rerunInFuzzMode(web_server, unit_test_index, prog_node) catch |err| switch (err) {152 run.rerunInFuzzMode(web_server, unit_test_index, prog_node) catch |err| switch (err) {
153 error.MakeFailed => {153 error.MakeFailed => {
154 var bw = std.debug.lockStdErr2(&.{});154 const bw = std.debug.lockStderrWriter();
155 defer std.debug.unlockStdErr();155 defer std.debug.unlockStderrWriter();
156 build_runner.printErrorMessages(gpa, &run.step, .{ .ttyconf = ttyconf }, &bw, false) catch {};156 build_runner.printErrorMessages(gpa, &run.step, .{ .ttyconf = ttyconf }, bw, false) catch {};
157 return;157 return;
158 },158 },
159 else => {159 else => {
lib/std/Build/Step/CheckObject.zig+1-1
...@@ -233,7 +233,7 @@ const ComputeCompareExpected = struct {...@@ -233,7 +233,7 @@ const ComputeCompareExpected = struct {
233 value: ComputeCompareExpected,233 value: ComputeCompareExpected,
234 bw: *std.io.BufferedWriter,234 bw: *std.io.BufferedWriter,
235 comptime fmt: []const u8,235 comptime fmt: []const u8,
236 ) anyerror!void {236 ) !void {
237 if (fmt.len != 0) std.fmt.invalidFmtError(fmt, value);237 if (fmt.len != 0) std.fmt.invalidFmtError(fmt, value);
238 try bw.print("{s} ", .{@tagName(value.op)});238 try bw.print("{s} ", .{@tagName(value.op)});
239 switch (value.value) {239 switch (value.value) {
lib/std/Progress.zig+32
...@@ -606,6 +606,38 @@ pub fn unlockStdErr() void {...@@ -606,6 +606,38 @@ pub fn unlockStdErr() void {
606 stderr_mutex.unlock();606 stderr_mutex.unlock();
607}607}
608608
609/// Protected by `stderr_mutex`.
610var stderr_buffered_writer: std.io.BufferedWriter = .{
611 .unbuffered_writer = stderr_file_writer.interface(),
612 .buffer = &.{},
613};
614/// Protected by `stderr_mutex`.
615var stderr_file_writer: std.fs.File.Writer = .{
616 .file = if (is_windows) undefined else .stderr(),
617 .mode = .streaming,
618};
619
620/// Allows the caller to freely write to the returned `std.io.BufferedWriter`,
621/// initialized with `buffer`, until `unlockStderrWriter` is called.
622///
623/// During the lock, any `std.Progress` information is cleared from the terminal.
624///
625/// The lock is recursive; the same thread may hold the lock multiple times.
626pub fn lockStderrWriter(buffer: []u8) *std.io.BufferedWriter {
627 stderr_mutex.lock();
628 clearWrittenWithEscapeCodes() catch {};
629 if (is_windows) stderr_file_writer.file = .stderr();
630 stderr_buffered_writer.flush() catch {};
631 stderr_buffered_writer.buffer = buffer;
632 return &stderr_buffered_writer;
633}
634
635pub fn unlockStderrWriter() void {
636 stderr_buffered_writer.flush() catch {};
637 stderr_buffered_writer.buffer = &.{};
638 stderr_mutex.unlock();
639}
640
609fn ipcThreadRun(fd: posix.fd_t) anyerror!void {641fn ipcThreadRun(fd: posix.fd_t) anyerror!void {
610 // Store this data in the thread so that it does not need to be part of the642 // Store this data in the thread so that it does not need to be part of the
611 // linker data of the main executable.643 // linker data of the main executable.
lib/std/Target.zig+1-1
...@@ -301,7 +301,7 @@ pub const Os = struct {...@@ -301,7 +301,7 @@ pub const Os = struct {
301301
302 /// This function is defined to serialize a Zig source code representation of this302 /// This function is defined to serialize a Zig source code representation of this
303 /// type, that, when parsed, will deserialize into the same data.303 /// type, that, when parsed, will deserialize into the same data.
304 pub fn format(ver: WindowsVersion, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) anyerror!void {304 pub fn format(ver: WindowsVersion, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) std.io.Writer.Error!void {
305 const maybe_name = std.enums.tagName(WindowsVersion, ver);305 const maybe_name = std.enums.tagName(WindowsVersion, ver);
306 if (comptime std.mem.eql(u8, fmt_str, "s")) {306 if (comptime std.mem.eql(u8, fmt_str, "s")) {
307 if (maybe_name) |name|307 if (maybe_name) |name|
lib/std/Uri.zig+4-4
...@@ -40,7 +40,7 @@ pub const Component = union(enum) {...@@ -40,7 +40,7 @@ pub const Component = union(enum) {
40 };40 };
41 }41 }
4242
43 pub fn format(component: Component, bw: *std.io.BufferedWriter, comptime fmt: []const u8) anyerror!void {43 pub fn format(component: Component, bw: *std.io.BufferedWriter, comptime fmt: []const u8) std.io.Writer.Error!void {
44 if (fmt.len == 0) {44 if (fmt.len == 0) {
45 try bw.print("std.Uri.Component{{ .{s} = \"{}\" }}", .{45 try bw.print("std.Uri.Component{{ .{s} = \"{}\" }}", .{
46 @tagName(component),46 @tagName(component),
...@@ -95,7 +95,7 @@ pub const Component = union(enum) {...@@ -95,7 +95,7 @@ pub const Component = union(enum) {
95 bw: *std.io.BufferedWriter,95 bw: *std.io.BufferedWriter,
96 raw: []const u8,96 raw: []const u8,
97 comptime isValidChar: fn (u8) bool,97 comptime isValidChar: fn (u8) bool,
98 ) anyerror!void {98 ) std.io.Writer.Error!void {
99 var start: usize = 0;99 var start: usize = 0;
100 for (raw, 0..) |char, index| {100 for (raw, 0..) |char, index| {
101 if (isValidChar(char)) continue;101 if (isValidChar(char)) continue;
...@@ -236,7 +236,7 @@ pub const WriteToStreamOptions = struct {...@@ -236,7 +236,7 @@ pub const WriteToStreamOptions = struct {
236 port: bool = true,236 port: bool = true,
237};237};
238238
239pub fn writeToStream(uri: Uri, options: WriteToStreamOptions, bw: *std.io.BufferedWriter) anyerror!void {239pub fn writeToStream(uri: Uri, options: WriteToStreamOptions, bw: *std.io.BufferedWriter) std.io.Writer.Error!void {
240 if (options.scheme) {240 if (options.scheme) {
241 try bw.print("{s}:", .{uri.scheme});241 try bw.print("{s}:", .{uri.scheme});
242 if (options.authority and uri.host != null) {242 if (options.authority and uri.host != null) {
...@@ -273,7 +273,7 @@ pub fn writeToStream(uri: Uri, options: WriteToStreamOptions, bw: *std.io.Buffer...@@ -273,7 +273,7 @@ pub fn writeToStream(uri: Uri, options: WriteToStreamOptions, bw: *std.io.Buffer
273 }273 }
274}274}
275275
276pub fn format(uri: Uri, bw: *std.io.BufferedWriter, comptime fmt: []const u8) anyerror!void {276pub fn format(uri: Uri, bw: *std.io.BufferedWriter, comptime fmt: []const u8) std.io.Writer.Error!void {
277 const scheme = comptime std.mem.indexOfScalar(u8, fmt, ';') != null or fmt.len == 0;277 const scheme = comptime std.mem.indexOfScalar(u8, fmt, ';') != null or fmt.len == 0;
278 const authentication = comptime std.mem.indexOfScalar(u8, fmt, '@') != null or fmt.len == 0;278 const authentication = comptime std.mem.indexOfScalar(u8, fmt, '@') != null or fmt.len == 0;
279 const authority = comptime std.mem.indexOfScalar(u8, fmt, '+') != null or fmt.len == 0;279 const authority = comptime std.mem.indexOfScalar(u8, fmt, '+') != null or fmt.len == 0;
lib/std/array_list.zig+3-1
...@@ -908,7 +908,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig...@@ -908,7 +908,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
908 var aw: std.io.AllocatingWriter = undefined;908 var aw: std.io.AllocatingWriter = undefined;
909 const bw = aw.fromArrayList(gpa, self);909 const bw = aw.fromArrayList(gpa, self);
910 defer self.* = aw.toArrayList();910 defer self.* = aw.toArrayList();
911 return @errorCast(bw.print(fmt, args));911 return bw.print(fmt, args) catch |err| switch (err) {
912 error.WriteFailed => return error.OutOfMemory,
913 };
912 }914 }
913915
914 pub fn printAssumeCapacity(self: *Self, comptime fmt: []const u8, args: anytype) void {916 pub fn printAssumeCapacity(self: *Self, comptime fmt: []const u8, args: anytype) void {
lib/std/builtin.zig+1-1
...@@ -34,7 +34,7 @@ pub const StackTrace = struct {...@@ -34,7 +34,7 @@ pub const StackTrace = struct {
34 index: usize,34 index: usize,
35 instruction_addresses: []usize,35 instruction_addresses: []usize,
3636
37 pub fn format(st: StackTrace, bw: *std.io.BufferedWriter, comptime fmt: []const u8) anyerror!void {37 pub fn format(st: StackTrace, bw: *std.io.BufferedWriter, comptime fmt: []const u8) !void {
38 comptime if (fmt.len != 0) unreachable;38 comptime if (fmt.len != 0) unreachable;
3939
40 // TODO: re-evaluate whether to use format() methods at all.40 // TODO: re-evaluate whether to use format() methods at all.
lib/std/compress/flate.zig+4-4
...@@ -9,7 +9,7 @@ pub const deflate = @import("flate/deflate.zig");...@@ -9,7 +9,7 @@ pub const deflate = @import("flate/deflate.zig");
9pub const inflate = @import("flate/inflate.zig");9pub const inflate = @import("flate/inflate.zig");
1010
11/// Decompress compressed data from reader and write plain data to the writer.11/// Decompress compressed data from reader and write plain data to the writer.
12pub fn decompress(reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter) anyerror!void {12pub fn decompress(reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter) std.io.Writer.Error!void {
13 try inflate.decompress(.raw, reader, writer);13 try inflate.decompress(.raw, reader, writer);
14}14}
1515
...@@ -19,7 +19,7 @@ pub const Decompressor = inflate.Decompressor(.raw);...@@ -19,7 +19,7 @@ pub const Decompressor = inflate.Decompressor(.raw);
19pub const Options = deflate.Options;19pub const Options = deflate.Options;
2020
21/// Compress plain data from reader and write compressed data to the writer.21/// Compress plain data from reader and write compressed data to the writer.
22pub fn compress(reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter, options: Options) anyerror!void {22pub fn compress(reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter, options: Options) std.io.Writer.Error!void {
23 try deflate.compress(.raw, reader, writer, options);23 try deflate.compress(.raw, reader, writer, options);
24}24}
2525
...@@ -28,7 +28,7 @@ pub const Compressor = deflate.Compressor(.raw);...@@ -28,7 +28,7 @@ pub const Compressor = deflate.Compressor(.raw);
28/// Huffman only compression. Without Lempel-Ziv match searching. Faster28/// Huffman only compression. Without Lempel-Ziv match searching. Faster
29/// compression, less memory requirements but bigger compressed sizes.29/// compression, less memory requirements but bigger compressed sizes.
30pub const huffman = struct {30pub const huffman = struct {
31 pub fn compress(reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter) anyerror!void {31 pub fn compress(reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter) std.io.Writer.Error!void {
32 try deflate.huffman.compress(.raw, reader, writer);32 try deflate.huffman.compress(.raw, reader, writer);
33 }33 }
3434
...@@ -37,7 +37,7 @@ pub const huffman = struct {...@@ -37,7 +37,7 @@ pub const huffman = struct {
3737
38// No compression store only. Compressed size is slightly bigger than plain.38// No compression store only. Compressed size is slightly bigger than plain.
39pub const store = struct {39pub const store = struct {
40 pub fn compress(reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter) anyerror!void {40 pub fn compress(reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter) std.io.Writer.Error!void {
41 try deflate.store.compress(.raw, reader, writer);41 try deflate.store.compress(.raw, reader, writer);
42 }42 }
4343
lib/std/compress/flate/BitWriter.zig+3-3
...@@ -39,7 +39,7 @@ pub fn setWriter(self: *Self, new_writer: *std.io.BufferedWriter) void {...@@ -39,7 +39,7 @@ pub fn setWriter(self: *Self, new_writer: *std.io.BufferedWriter) void {
39 self.inner_writer = new_writer;39 self.inner_writer = new_writer;
40}40}
4141
42pub fn flush(self: *Self) anyerror!void {42pub fn flush(self: *Self) std.io.Writer.Error!void {
43 var n = self.nbytes;43 var n = self.nbytes;
44 while (self.nbits != 0) {44 while (self.nbits != 0) {
45 self.bytes[n] = @as(u8, @truncate(self.bits));45 self.bytes[n] = @as(u8, @truncate(self.bits));
...@@ -56,7 +56,7 @@ pub fn flush(self: *Self) anyerror!void {...@@ -56,7 +56,7 @@ pub fn flush(self: *Self) anyerror!void {
56 self.nbytes = 0;56 self.nbytes = 0;
57}57}
5858
59pub fn writeBits(self: *Self, b: u32, nb: u32) anyerror!void {59pub fn writeBits(self: *Self, b: u32, nb: u32) std.io.Writer.Error!void {
60 self.bits |= @as(u64, @intCast(b)) << @as(u6, @intCast(self.nbits));60 self.bits |= @as(u64, @intCast(b)) << @as(u6, @intCast(self.nbits));
61 self.nbits += nb;61 self.nbits += nb;
62 if (self.nbits < 48)62 if (self.nbits < 48)
...@@ -74,7 +74,7 @@ pub fn writeBits(self: *Self, b: u32, nb: u32) anyerror!void {...@@ -74,7 +74,7 @@ pub fn writeBits(self: *Self, b: u32, nb: u32) anyerror!void {
74 self.nbits -= 48;74 self.nbits -= 48;
75}75}
7676
77pub fn writeBytes(self: *Self, bytes: []const u8) anyerror!void {77pub fn writeBytes(self: *Self, bytes: []const u8) std.io.Writer.Error!void {
78 var n = self.nbytes;78 var n = self.nbytes;
79 if (self.nbits & 7 != 0) {79 if (self.nbits & 7 != 0) {
80 return error.UnfinishedBits;80 return error.UnfinishedBits;
lib/std/compress/flate/BlockWriter.zig+10-10
...@@ -42,7 +42,7 @@ pub fn init(writer: *std.io.BufferedWriter) Self {...@@ -42,7 +42,7 @@ pub fn init(writer: *std.io.BufferedWriter) Self {
42/// That is after final block; when last byte could be incomplete or42/// That is after final block; when last byte could be incomplete or
43/// after stored block; which is aligned to the byte boundary (it has x43/// after stored block; which is aligned to the byte boundary (it has x
44/// padding bits after first 3 bits).44/// padding bits after first 3 bits).
45pub fn flush(self: *Self) anyerror!void {45pub fn flush(self: *Self) std.io.Writer.Error!void {
46 try self.bit_writer.flush();46 try self.bit_writer.flush();
47}47}
4848
...@@ -50,7 +50,7 @@ pub fn setWriter(self: *Self, new_writer: *std.io.BufferedWriter) void {...@@ -50,7 +50,7 @@ pub fn setWriter(self: *Self, new_writer: *std.io.BufferedWriter) void {
50 self.bit_writer.setWriter(new_writer);50 self.bit_writer.setWriter(new_writer);
51}51}
5252
53fn writeCode(self: *Self, c: hc.HuffCode) anyerror!void {53fn writeCode(self: *Self, c: hc.HuffCode) std.io.Writer.Error!void {
54 try self.bit_writer.writeBits(c.code, c.len);54 try self.bit_writer.writeBits(c.code, c.len);
55}55}
5656
...@@ -232,7 +232,7 @@ fn dynamicHeader(...@@ -232,7 +232,7 @@ fn dynamicHeader(
232 num_distances: u32,232 num_distances: u32,
233 num_codegens: u32,233 num_codegens: u32,
234 eof: bool,234 eof: bool,
235) anyerror!void {235) std.io.Writer.Error!void {
236 const first_bits: u32 = if (eof) 5 else 4;236 const first_bits: u32 = if (eof) 5 else 4;
237 try self.bit_writer.writeBits(first_bits, 3);237 try self.bit_writer.writeBits(first_bits, 3);
238 try self.bit_writer.writeBits(num_literals - 257, 5);238 try self.bit_writer.writeBits(num_literals - 257, 5);
...@@ -272,7 +272,7 @@ fn dynamicHeader(...@@ -272,7 +272,7 @@ fn dynamicHeader(
272 }272 }
273}273}
274274
275fn storedHeader(self: *Self, length: usize, eof: bool) anyerror!void {275fn storedHeader(self: *Self, length: usize, eof: bool) std.io.Writer.Error!void {
276 assert(length <= 65535);276 assert(length <= 65535);
277 const flag: u32 = if (eof) 1 else 0;277 const flag: u32 = if (eof) 1 else 0;
278 try self.bit_writer.writeBits(flag, 3);278 try self.bit_writer.writeBits(flag, 3);
...@@ -282,7 +282,7 @@ fn storedHeader(self: *Self, length: usize, eof: bool) anyerror!void {...@@ -282,7 +282,7 @@ fn storedHeader(self: *Self, length: usize, eof: bool) anyerror!void {
282 try self.bit_writer.writeBits(~l, 16);282 try self.bit_writer.writeBits(~l, 16);
283}283}
284284
285fn fixedHeader(self: *Self, eof: bool) anyerror!void {285fn fixedHeader(self: *Self, eof: bool) std.io.Writer.Error!void {
286 // Indicate that we are a fixed Huffman block286 // Indicate that we are a fixed Huffman block
287 var value: u32 = 2;287 var value: u32 = 2;
288 if (eof) {288 if (eof) {
...@@ -296,7 +296,7 @@ fn fixedHeader(self: *Self, eof: bool) anyerror!void {...@@ -296,7 +296,7 @@ fn fixedHeader(self: *Self, eof: bool) anyerror!void {
296// is larger than the original bytes, the data will be written as a296// is larger than the original bytes, the data will be written as a
297// stored block.297// stored block.
298// If the input is null, the tokens will always be Huffman encoded.298// If the input is null, the tokens will always be Huffman encoded.
299pub fn write(self: *Self, tokens: []const Token, eof: bool, input: ?[]const u8) anyerror!void {299pub fn write(self: *Self, tokens: []const Token, eof: bool, input: ?[]const u8) std.io.Writer.Error!void {
300 const lit_and_dist = self.indexTokens(tokens);300 const lit_and_dist = self.indexTokens(tokens);
301 const num_literals = lit_and_dist.num_literals;301 const num_literals = lit_and_dist.num_literals;
302 const num_distances = lit_and_dist.num_distances;302 const num_distances = lit_and_dist.num_distances;
...@@ -374,7 +374,7 @@ pub fn write(self: *Self, tokens: []const Token, eof: bool, input: ?[]const u8)...@@ -374,7 +374,7 @@ pub fn write(self: *Self, tokens: []const Token, eof: bool, input: ?[]const u8)
374 try self.writeTokens(tokens, &literal_encoding.codes, &distance_encoding.codes);374 try self.writeTokens(tokens, &literal_encoding.codes, &distance_encoding.codes);
375}375}
376376
377pub fn storedBlock(self: *Self, input: []const u8, eof: bool) anyerror!void {377pub fn storedBlock(self: *Self, input: []const u8, eof: bool) std.io.Writer.Error!void {
378 try self.storedHeader(input.len, eof);378 try self.storedHeader(input.len, eof);
379 try self.bit_writer.writeBytes(input);379 try self.bit_writer.writeBytes(input);
380}380}
...@@ -389,7 +389,7 @@ fn dynamicBlock(...@@ -389,7 +389,7 @@ fn dynamicBlock(
389 tokens: []const Token,389 tokens: []const Token,
390 eof: bool,390 eof: bool,
391 input: ?[]const u8,391 input: ?[]const u8,
392) anyerror!void {392) std.io.Writer.Error!void {
393 const total_tokens = self.indexTokens(tokens);393 const total_tokens = self.indexTokens(tokens);
394 const num_literals = total_tokens.num_literals;394 const num_literals = total_tokens.num_literals;
395 const num_distances = total_tokens.num_distances;395 const num_distances = total_tokens.num_distances;
...@@ -486,7 +486,7 @@ fn writeTokens(...@@ -486,7 +486,7 @@ fn writeTokens(
486 tokens: []const Token,486 tokens: []const Token,
487 le_codes: []hc.HuffCode,487 le_codes: []hc.HuffCode,
488 oe_codes: []hc.HuffCode,488 oe_codes: []hc.HuffCode,
489) anyerror!void {489) std.io.Writer.Error!void {
490 for (tokens) |t| {490 for (tokens) |t| {
491 if (t.kind == Token.Kind.literal) {491 if (t.kind == Token.Kind.literal) {
492 try self.writeCode(le_codes[t.literal()]);492 try self.writeCode(le_codes[t.literal()]);
...@@ -513,7 +513,7 @@ fn writeTokens(...@@ -513,7 +513,7 @@ fn writeTokens(
513513
514// Encodes a block of bytes as either Huffman encoded literals or uncompressed bytes514// Encodes a block of bytes as either Huffman encoded literals or uncompressed bytes
515// if the results only gains very little from compression.515// if the results only gains very little from compression.
516pub fn huffmanBlock(self: *Self, input: []const u8, eof: bool) anyerror!void {516pub fn huffmanBlock(self: *Self, input: []const u8, eof: bool) std.io.Writer.Error!void {
517 // Add everything as literals517 // Add everything as literals
518 histogram(input, &self.literal_freq);518 histogram(input, &self.literal_freq);
519519
lib/std/compress/flate/inflate.zig+48-23
...@@ -66,6 +66,8 @@ pub fn Inflate(comptime container: Container, comptime Lookahead: type) type {...@@ -66,6 +66,8 @@ pub fn Inflate(comptime container: Container, comptime Lookahead: type) type {
66 block_type: u2 = 0b11,66 block_type: u2 = 0b11,
67 state: ReadState = .protocol_header,67 state: ReadState = .protocol_header,
6868
69 read_err: Error!void = {},
70
69 const ReadState = enum {71 const ReadState = enum {
70 protocol_header,72 protocol_header,
71 block_header,73 block_header,
...@@ -76,19 +78,21 @@ pub fn Inflate(comptime container: Container, comptime Lookahead: type) type {...@@ -76,19 +78,21 @@ pub fn Inflate(comptime container: Container, comptime Lookahead: type) type {
7678
77 const Self = @This();79 const Self = @This();
7880
79 pub const Error = anyerror || Container.Error || hfd.Error || error{81 pub const Error = Container.Error || hfd.Error || error{
80 InvalidCode,82 InvalidCode,
81 InvalidMatch,83 InvalidMatch,
82 InvalidBlockType,84 InvalidBlockType,
83 WrongStoredBlockNlen,85 WrongStoredBlockNlen,
84 InvalidDynamicBlockHeader,86 InvalidDynamicBlockHeader,
87 EndOfStream,
88 ReadFailed,
85 };89 };
8690
87 pub fn init(bw: *std.io.BufferedReader) Self {91 pub fn init(bw: *std.io.BufferedReader) Self {
88 return .{ .bits = LookaheadBitReader.init(bw) };92 return .{ .bits = LookaheadBitReader.init(bw) };
89 }93 }
9094
91 fn blockHeader(self: *Self) anyerror!void {95 fn blockHeader(self: *Self) Error!void {
92 self.bfinal = try self.bits.read(u1);96 self.bfinal = try self.bits.read(u1);
93 self.block_type = try self.bits.read(u2);97 self.block_type = try self.bits.read(u2);
94 }98 }
...@@ -326,7 +330,7 @@ pub fn Inflate(comptime container: Container, comptime Lookahead: type) type {...@@ -326,7 +330,7 @@ pub fn Inflate(comptime container: Container, comptime Lookahead: type) type {
326 /// returned bytes means end of stream reached. With limit=0 returns as330 /// returned bytes means end of stream reached. With limit=0 returns as
327 /// much data it can. It newer will be more than 65536 bytes, which is331 /// much data it can. It newer will be more than 65536 bytes, which is
328 /// size of internal buffer.332 /// size of internal buffer.
329 /// TODO merge this logic into reader_streamRead and reader_streamReadVec333 /// TODO merge this logic into readerRead and readerReadVec
330 pub fn get(self: *Self, limit: usize) Error![]const u8 {334 pub fn get(self: *Self, limit: usize) Error![]const u8 {
331 while (true) {335 while (true) {
332 const out = self.hist.readAtMost(limit);336 const out = self.hist.readAtMost(limit);
...@@ -339,42 +343,63 @@ pub fn Inflate(comptime container: Container, comptime Lookahead: type) type {...@@ -339,42 +343,63 @@ pub fn Inflate(comptime container: Container, comptime Lookahead: type) type {
339 }343 }
340 }344 }
341345
342 fn reader_streamRead(346 fn readerRead(
343 ctx: ?*anyopaque,347 context: ?*anyopaque,
344 bw: *std.io.BufferedWriter,348 bw: *std.io.BufferedWriter,
345 limit: std.io.Reader.Limit,349 limit: std.io.Reader.Limit,
346 ) anyerror!std.io.Reader.Status {350 ) std.io.Reader.RwError!usize {
347 const self: *Self = @alignCast(@ptrCast(ctx));351 const self: *Self = @alignCast(@ptrCast(context));
348 const out = try bw.writableSlice(1);352 const out = try bw.writableSlice(1);
349 const in = try self.get(limit.min(out.len));353 const in = self.get(limit.min(out.len)) catch |err| switch (err) {
354 error.EndOfStream => return error.EndOfStream,
355 error.ReadFailed => return error.ReadFailed,
356 else => |e| {
357 self.read_err = e;
358 return error.ReadFailed;
359 },
360 };
361 if (in.len == 0) return error.EndOfStream;
350 @memcpy(out[0..in.len], in);362 @memcpy(out[0..in.len], in);
351 bw.advance(in.len);363 bw.advance(in.len);
352 return .{ .len = in.len, .end = in.len == 0 };364 return in.len;
353 }365 }
354366
355 fn reader_streamReadVec(ctx: ?*anyopaque, data: []const []u8) anyerror!std.io.Reader.Status {367 fn readerReadVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
356 const self: *Self = @alignCast(@ptrCast(ctx));368 const self: *Self = @alignCast(@ptrCast(context));
369 return readVec(self, data) catch |err| switch (err) {
370 error.EndOfStream => return error.EndOfStream,
371 error.ReadFailed => return error.ReadFailed,
372 else => |e| {
373 self.read_err = e;
374 return error.ReadFailed;
375 },
376 };
377 }
378
379 fn readerDiscard(context: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize {
380 _ = context;
381 _ = limit;
382 @panic("TODO");
383 }
384
385 pub fn readVec(self: *Self, data: []const []u8) Error!usize {
357 for (data) |out| {386 for (data) |out| {
358 if (out.len == 0) continue;387 if (out.len == 0) continue;
359 const in = try self.get(out.len);388 const in = try self.get(out.len);
360 @memcpy(out[0..in.len], in);389 @memcpy(out[0..in.len], in);
361 return .{ .len = @intCast(in.len), .end = in.len == 0 };390 if (in.len == 0) return error.EndOfStream;
391 return in.len;
362 }392 }
363 return .{};393 return 0;
364 }
365
366 pub fn streamReadVec(self: *Self, data: []const []u8) anyerror!std.io.Reader.Status {
367 return reader_streamReadVec(self, data);
368 }394 }
369395
370 pub fn reader(self: *Self) std.io.Reader {396 pub fn reader(self: *Self) std.io.Reader {
371 return .{397 return .{
372 .context = self,398 .context = self,
373 .vtable = &.{399 .vtable = &.{
374 .posRead = null,400 .read = readerRead,
375 .posReadVec = null,401 .readVec = readerReadVec,
376 .streamRead = reader_streamRead,402 .discard = readerDiscard,
377 .streamReadVec = reader_streamReadVec,
378 },403 },
379 };404 };
380 }405 }
...@@ -656,7 +681,7 @@ pub fn BitReader(comptime T: type) type {...@@ -656,7 +681,7 @@ pub fn BitReader(comptime T: type) type {
656 (self.nbits >> 3); // 0 for 0-7, 1 for 8-16, ... same as / 8681 (self.nbits >> 3); // 0 for 0-7, 1 for 8-16, ... same as / 8
657682
658 var buf: [t_bytes]u8 = [_]u8{0} ** t_bytes;683 var buf: [t_bytes]u8 = [_]u8{0} ** t_bytes;
659 const bytes_read = self.forward_reader.partialRead(buf[0..empty_bytes]) catch 0;684 const bytes_read = self.forward_reader.readShort(buf[0..empty_bytes]) catch 0;
660 if (bytes_read > 0) {685 if (bytes_read > 0) {
661 const u: T = std.mem.readInt(T, buf[0..t_bytes], .little);686 const u: T = std.mem.readInt(T, buf[0..t_bytes], .little);
662 self.bits |= u << @as(Tshift, @intCast(self.nbits));687 self.bits |= u << @as(Tshift, @intCast(self.nbits));
...@@ -669,7 +694,7 @@ pub fn BitReader(comptime T: type) type {...@@ -669,7 +694,7 @@ pub fn BitReader(comptime T: type) type {
669 }694 }
670695
671 /// Read exactly buf.len bytes into buf.696 /// Read exactly buf.len bytes into buf.
672 pub fn readAll(self: *Self, buf: []u8) anyerror!void {697 pub fn readAll(self: *Self, buf: []u8) std.io.Reader.Error!void {
673 assert(self.alignBits() == 0); // internal bits must be at byte boundary698 assert(self.alignBits() == 0); // internal bits must be at byte boundary
674699
675 // First read from internal bits buffer.700 // First read from internal bits buffer.
lib/std/compress/lzma.zig+6-6
...@@ -11,7 +11,7 @@ pub const RangeDecoder = struct {...@@ -11,7 +11,7 @@ pub const RangeDecoder = struct {
11 range: u32,11 range: u32,
12 code: u32,12 code: u32,
1313
14 pub fn init(rd: *RangeDecoder, br: *std.io.BufferedReader) anyerror!usize {14 pub fn init(rd: *RangeDecoder, br: *std.io.BufferedReader) std.io.Reader.Error!usize {
15 const reserved = try br.takeByte();15 const reserved = try br.takeByte();
16 if (reserved != 0) return error.CorruptInput;16 if (reserved != 0) return error.CorruptInput;
17 rd.* = .{17 rd.* = .{
...@@ -222,7 +222,7 @@ pub const Decode = struct {...@@ -222,7 +222,7 @@ pub const Decode = struct {
222 dict_size: u32,222 dict_size: u32,
223 unpacked_size: ?u64,223 unpacked_size: ?u64,
224224
225 pub fn readHeader(br: *std.io.BufferedReader, options: Options) anyerror!Params {225 pub fn readHeader(br: *std.io.BufferedReader, options: Options) std.io.Reader.Error!Params {
226 var props = try br.readByte();226 var props = try br.readByte();
227 if (props >= 225) {227 if (props >= 225) {
228 return error.CorruptInput;228 return error.CorruptInput;
...@@ -537,7 +537,7 @@ pub const Decode = struct {...@@ -537,7 +537,7 @@ pub const Decode = struct {
537537
538pub const Decompress = struct {538pub const Decompress = struct {
539 pub const Error =539 pub const Error =
540 anyerror ||540 std.io.Reader.Error ||
541 Allocator.Error ||541 Allocator.Error ||
542 error{ CorruptInput, EndOfStream, Overflow };542 error{ CorruptInput, EndOfStream, Overflow };
543543
...@@ -668,7 +668,7 @@ const LzCircularBuffer = struct {...@@ -668,7 +668,7 @@ const LzCircularBuffer = struct {
668 allocator: Allocator,668 allocator: Allocator,
669 lit: u8,669 lit: u8,
670 bw: *std.io.BufferedWriter,670 bw: *std.io.BufferedWriter,
671 ) anyerror!void {671 ) std.io.Writer.Error!void {
672 try self.set(allocator, self.cursor, lit);672 try self.set(allocator, self.cursor, lit);
673 self.cursor += 1;673 self.cursor += 1;
674 self.len += 1;674 self.len += 1;
...@@ -687,7 +687,7 @@ const LzCircularBuffer = struct {...@@ -687,7 +687,7 @@ const LzCircularBuffer = struct {
687 len: usize,687 len: usize,
688 dist: usize,688 dist: usize,
689 bw: *std.io.BufferedWriter,689 bw: *std.io.BufferedWriter,
690 ) anyerror!void {690 ) std.io.Writer.Error!void {
691 if (dist > self.dict_size or dist > self.len) {691 if (dist > self.dict_size or dist > self.len) {
692 return error.CorruptInput;692 return error.CorruptInput;
693 }693 }
...@@ -704,7 +704,7 @@ const LzCircularBuffer = struct {...@@ -704,7 +704,7 @@ const LzCircularBuffer = struct {
704 }704 }
705 }705 }
706706
707 pub fn finish(self: *Self, bw: *std.io.BufferedWriter) anyerror!void {707 pub fn finish(self: *Self, bw: *std.io.BufferedWriter) std.io.Writer.Error!void {
708 if (self.cursor > 0) {708 if (self.cursor > 0) {
709 try bw.writeAll(self.buf.items[0..self.cursor]);709 try bw.writeAll(self.buf.items[0..self.cursor]);
710 self.cursor = 0;710 self.cursor = 0;
lib/std/compress/lzma2.zig+1-1
...@@ -2,7 +2,7 @@ const std = @import("../std.zig");...@@ -2,7 +2,7 @@ const std = @import("../std.zig");
2const Allocator = std.mem.Allocator;2const Allocator = std.mem.Allocator;
3const lzma = std.compress.lzma;3const lzma = std.compress.lzma;
44
5pub fn decompress(gpa: Allocator, reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter) anyerror!void {5pub fn decompress(gpa: Allocator, reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter) std.io.Reader.RwError!void {
6 var decoder = try Decode.init(gpa);6 var decoder = try Decode.init(gpa);
7 defer decoder.deinit(gpa);7 defer decoder.deinit(gpa);
8 return decoder.decompress(gpa, reader, writer);8 return decoder.decompress(gpa, reader, writer);
lib/std/compress/zstandard.zig+1-1
...@@ -39,7 +39,7 @@ pub const Decompressor = struct {...@@ -39,7 +39,7 @@ pub const Decompressor = struct {
39 write_index: usize = 0,39 write_index: usize = 0,
40 };40 };
4141
42 pub const Error = anyerror || error{42 pub const Error = std.io.Reader.Error || error{
43 ChecksumFailure,43 ChecksumFailure,
44 DictionaryIdFlagUnsupported,44 DictionaryIdFlagUnsupported,
45 MalformedBlock,45 MalformedBlock,
lib/std/crypto/tls/Client.zig+26-32
...@@ -69,28 +69,15 @@ application_cipher: tls.ApplicationCipher,...@@ -69,28 +69,15 @@ application_cipher: tls.ApplicationCipher,
69/// this connection.69/// this connection.
70ssl_key_log: ?*SslKeyLog,70ssl_key_log: ?*SslKeyLog,
7171
72pub const Diagnostics = union {72pub const Diagnostics = union(enum) {
73 /// Populated on `error.WriteFailure` and `error.ReadFailure`.73 /// Any `ReadFailure` and `WriteFailure` was due to `input` or `output`
74 err: anyerror,74 /// returning the error, respectively.
75 transitive,
75 /// Populated on `error.TlsAlert`.76 /// Populated on `error.TlsAlert`.
76 ///77 ///
77 /// If this isn't a error alert, then it's a closure alert, which makes78 /// If this isn't a error alert, then it's a closure alert, which makes
78 /// no sense in a handshake.79 /// no sense in a handshake.
79 alert: tls.AlertDescription,80 alert: tls.AlertDescription,
80
81 fn wrapWrite(d: *Diagnostics, returned: anyerror!void) error{WriteFailure}!void {
82 returned catch |err| {
83 d.* = .{ .err = err };
84 return error.WriteFailure;
85 };
86 }
87
88 fn wrapRead(d: *Diagnostics, returned: anyerror!void) error{ReadFailure}!void {
89 returned catch |err| {
90 d.* = .{ .err = err };
91 return error.ReadFailure;
92 };
93 }
94};81};
9582
96pub const SslKeyLog = struct {83pub const SslKeyLog = struct {
...@@ -205,7 +192,7 @@ pub fn init(...@@ -205,7 +192,7 @@ pub fn init(
205) InitError!void {192) InitError!void {
206 assert(input.storage.buffer.len >= min_buffer_len);193 assert(input.storage.buffer.len >= min_buffer_len);
207 assert(output.buffer.len >= min_buffer_len);194 assert(output.buffer.len >= min_buffer_len);
208 const diags = &client.diagnostics;195 client.diagnostics = .transient;
209 const host = switch (options.host) {196 const host = switch (options.host) {
210 .no_verification => "",197 .no_verification => "",
211 .explicit => |host| host,198 .explicit => |host| host,
...@@ -298,7 +285,7 @@ pub fn init(...@@ -298,7 +285,7 @@ pub fn init(
298285
299 {286 {
300 var iovecs: [2][]const u8 = .{ cleartext_header, host };287 var iovecs: [2][]const u8 = .{ cleartext_header, host };
301 try diags.wrapWrite(output.writevAll(iovecs[0..if (host.len == 0) 1 else 2]));288 try output.writevAll(iovecs[0..if (host.len == 0) 1 else 2]);
302 }289 }
303290
304 var tls_version: tls.ProtocolVersion = undefined;291 var tls_version: tls.ProtocolVersion = undefined;
...@@ -350,12 +337,12 @@ pub fn init(...@@ -350,12 +337,12 @@ pub fn init(
350 var handshake_buffer: [tls.max_ciphertext_record_len]u8 = undefined;337 var handshake_buffer: [tls.max_ciphertext_record_len]u8 = undefined;
351 var d: tls.Decoder = .{ .buf = &handshake_buffer };338 var d: tls.Decoder = .{ .buf = &handshake_buffer };
352 fragment: while (true) {339 fragment: while (true) {
353 try diags.wrapRead(d.readAtLeastOurAmt(input, tls.record_header_len));340 try d.readAtLeastOurAmt(input, tls.record_header_len);
354 const record_header = d.buf[d.idx..][0..tls.record_header_len];341 const record_header = d.buf[d.idx..][0..tls.record_header_len];
355 const record_ct = d.decode(tls.ContentType);342 const record_ct = d.decode(tls.ContentType);
356 d.skip(2); // legacy_version343 d.skip(2); // legacy_version
357 const record_len = d.decode(u16);344 const record_len = d.decode(u16);
358 try diags.wrapRead(d.readAtLeast(input, record_len));345 try d.readAtLeast(input, record_len);
359 var record_decoder = try d.sub(record_len);346 var record_decoder = try d.sub(record_len);
360 var ctd, const ct = content: switch (cipher_state) {347 var ctd, const ct = content: switch (cipher_state) {
361 .cleartext => .{ record_decoder, record_ct },348 .cleartext => .{ record_decoder, record_ct },
...@@ -433,7 +420,7 @@ pub fn init(...@@ -433,7 +420,7 @@ pub fn init(
433 const level = ctd.decode(tls.AlertLevel);420 const level = ctd.decode(tls.AlertLevel);
434 const desc = ctd.decode(tls.AlertDescription);421 const desc = ctd.decode(tls.AlertDescription);
435 _ = level;422 _ = level;
436 diags.* = .{ .alert = desc };423 client.diagnostics = .{ .alert = desc };
437 return error.TlsAlert;424 return error.TlsAlert;
438 },425 },
439 .change_cipher_spec => {426 .change_cipher_spec => {
...@@ -775,7 +762,7 @@ pub fn init(...@@ -775,7 +762,7 @@ pub fn init(
775 &client_change_cipher_spec_msg,762 &client_change_cipher_spec_msg,
776 &client_verify_msg,763 &client_verify_msg,
777 };764 };
778 try diags.wrapWrite(output.writevAll(&all_msgs_vec));765 try output.writevAll(&all_msgs_vec);
779 },766 },
780 }767 }
781 write_seq += 1;768 write_seq += 1;
...@@ -840,7 +827,7 @@ pub fn init(...@@ -840,7 +827,7 @@ pub fn init(
840 &client_change_cipher_spec_msg,827 &client_change_cipher_spec_msg,
841 &finished_msg,828 &finished_msg,
842 };829 };
843 try diags.wrapWrite(output.writevAll(&all_msgs_vec));830 try output.writevAll(&all_msgs_vec);
844831
845 const client_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "c ap traffic", &handshake_hash, P.Hash.digest_length);832 const client_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "c ap traffic", &handshake_hash, P.Hash.digest_length);
846 const server_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "s ap traffic", &handshake_hash, P.Hash.digest_length);833 const server_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "s ap traffic", &handshake_hash, P.Hash.digest_length);
...@@ -905,8 +892,9 @@ pub fn init(...@@ -905,8 +892,9 @@ pub fn init(
905 client.reader.init(.{892 client.reader.init(.{
906 .context = client,893 .context = client,
907 .vtable = &.{894 .vtable = &.{
908 .read = reader_read,895 .read = read,
909 .readv = reader_readv,896 .readVec = readVec,
897 .discard = discard,
910 },898 },
911 }, input.storage.buffer[0..0]);899 }, input.storage.buffer[0..0]);
912 return;900 return;
...@@ -933,7 +921,7 @@ pub fn writer(c: *Client) std.io.Writer {...@@ -933,7 +921,7 @@ pub fn writer(c: *Client) std.io.Writer {
933 };921 };
934}922}
935923
936fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {924fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
937 const c: *Client = @alignCast(@ptrCast(context));925 const c: *Client = @alignCast(@ptrCast(context));
938 const sliced_data = if (splat == 0) data[0..data.len -| 1] else data;926 const sliced_data = if (splat == 0) data[0..data.len -| 1] else data;
939 const output = &c.output;927 const output = &c.output;
...@@ -953,7 +941,7 @@ fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyer...@@ -953,7 +941,7 @@ fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyer
953/// Sends a `close_notify` alert, which is necessary for the server to941/// Sends a `close_notify` alert, which is necessary for the server to
954/// distinguish between a properly finished TLS session, or a truncation942/// distinguish between a properly finished TLS session, or a truncation
955/// attack.943/// attack.
956pub fn end(c: *Client) anyerror!void {944pub fn end(c: *Client) std.io.Writer.Error!void {
957 const output = &c.output;945 const output = &c.output;
958 const ciphertext_buf = try output.writableSlice(min_buffer_len);946 const ciphertext_buf = try output.writableSlice(min_buffer_len);
959 const prepared = prepareCiphertextRecord(c, ciphertext_buf, &tls.close_notify_alert, .alert);947 const prepared = prepareCiphertextRecord(c, ciphertext_buf, &tls.close_notify_alert, .alert);
...@@ -1070,18 +1058,18 @@ pub fn eof(c: Client) bool {...@@ -1070,18 +1058,18 @@ pub fn eof(c: Client) bool {
1070 c.partial_ciphertext_idx >= c.partial_ciphertext_end;1058 c.partial_ciphertext_idx >= c.partial_ciphertext_end;
1071}1059}
10721060
1073fn reader_read(1061fn read(
1074 context: ?*anyopaque,1062 context: ?*anyopaque,
1075 bw: *std.io.BufferedWriter,1063 bw: *std.io.BufferedWriter,
1076 limit: std.io.Reader.Limit,1064 limit: std.io.Reader.Limit,
1077) anyerror!std.io.Reader.Status {1065) std.io.Reader.RwError!std.io.Reader.Status {
1078 const buf = limit.slice(try bw.writableSlice(1));1066 const buf = limit.slice(try bw.writableSlice(1));
1079 const status = try reader_readv(context, &.{buf});1067 const status = try readVec(context, &.{buf});
1080 bw.advance(status.len);1068 bw.advance(status.len);
1081 return status;1069 return status;
1082}1070}
10831071
1084fn reader_readv(context: ?*anyopaque, data: []const []u8) anyerror!std.io.Reader.Status {1072fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
1085 const c: *Client = @ptrCast(@alignCast(context));1073 const c: *Client = @ptrCast(@alignCast(context));
1086 if (c.eof()) return .{ .end = true };1074 if (c.eof()) return .{ .end = true };
10871075
...@@ -1429,6 +1417,12 @@ fn reader_readv(context: ?*anyopaque, data: []const []u8) anyerror!std.io.Reader...@@ -1429,6 +1417,12 @@ fn reader_readv(context: ?*anyopaque, data: []const []u8) anyerror!std.io.Reader
1429 }1417 }
1430}1418}
14311419
1420fn discard(context: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize {
1421 _ = context;
1422 _ = limit;
1423 @panic("TODO");
1424}
1425
1432fn logSecrets(key_log_file: std.fs.File, context: anytype, secrets: anytype) void {1426fn logSecrets(key_log_file: std.fs.File, context: anytype, secrets: anytype) void {
1433 const locked = if (key_log_file.lock(.exclusive)) |_| true else |_| false;1427 const locked = if (key_log_file.lock(.exclusive)) |_| true else |_| false;
1434 defer if (locked) key_log_file.unlock();1428 defer if (locked) key_log_file.unlock();
lib/std/debug.zig+32-29
...@@ -210,16 +210,19 @@ pub fn unlockStdErr() void {...@@ -210,16 +210,19 @@ pub fn unlockStdErr() void {
210///210///
211/// Returns a `std.io.BufferedWriter` with empty buffer, meaning that it is211/// Returns a `std.io.BufferedWriter` with empty buffer, meaning that it is
212/// in fact unbuffered and does not need to be flushed.212/// in fact unbuffered and does not need to be flushed.
213pub fn lockStdErr2(buffer: []u8) std.io.BufferedWriter {213pub fn lockStderrWriter(buffer: []u8) *std.io.BufferedWriter {
214 std.Progress.lockStdErr();214 return std.Progress.lockStderrWriter(buffer);
215 return std.fs.File.stderr().writer().buffered(buffer);215}
216
217pub fn unlockStderrWriter() void {
218 std.Progress.unlockStderrWriter();
216}219}
217220
218/// Print to stderr, unbuffered, and silently returning on failure. Intended221/// Print to stderr, unbuffered, and silently returning on failure. Intended
219/// for use in "printf debugging." Use `std.log` functions for proper logging.222/// for use in "printf debugging". Use `std.log` functions for proper logging.
220pub fn print(comptime fmt: []const u8, args: anytype) void {223pub fn print(comptime fmt: []const u8, args: anytype) void {
221 var bw = lockStdErr2(&.{});224 const bw = lockStderrWriter(&.{});
222 defer unlockStdErr();225 defer unlockStderrWriter();
223 nosuspend bw.print(fmt, args) catch return;226 nosuspend bw.print(fmt, args) catch return;
224}227}
225228
...@@ -242,10 +245,10 @@ pub fn getSelfDebugInfo() !*SelfInfo {...@@ -242,10 +245,10 @@ pub fn getSelfDebugInfo() !*SelfInfo {
242/// Tries to print a hexadecimal view of the bytes, unbuffered, and ignores any error returned.245/// Tries to print a hexadecimal view of the bytes, unbuffered, and ignores any error returned.
243/// Obtains the stderr mutex while dumping.246/// Obtains the stderr mutex while dumping.
244pub fn dumpHex(bytes: []const u8) void {247pub fn dumpHex(bytes: []const u8) void {
245 var bw = lockStdErr2(&.{});248 const bw = lockStderrWriter(&.{});
246 defer unlockStdErr();249 defer unlockStderrWriter();
247 const ttyconf = std.io.tty.detectConfig(.stderr());250 const ttyconf = std.io.tty.detectConfig(.stderr());
248 dumpHexFallible(&bw, ttyconf, bytes) catch {};251 dumpHexFallible(bw, ttyconf, bytes) catch {};
249}252}
250253
251/// Prints a hexadecimal view of the bytes, returning any error that occurs.254/// Prints a hexadecimal view of the bytes, returning any error that occurs.
...@@ -320,9 +323,9 @@ test dumpHexFallible {...@@ -320,9 +323,9 @@ test dumpHexFallible {
320323
321/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.324/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
322pub fn dumpCurrentStackTrace(start_addr: ?usize) void {325pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
323 var stderr = lockStdErr2(&.{});326 const stderr = lockStderrWriter(&.{});
324 defer unlockStdErr();327 defer unlockStderrWriter();
325 nosuspend dumpCurrentStackTraceToWriter(start_addr, &stderr) catch return;328 nosuspend dumpCurrentStackTraceToWriter(start_addr, stderr) catch return;
326}329}
327330
328/// Prints the current stack trace to the provided writer.331/// Prints the current stack trace to the provided writer.
...@@ -516,14 +519,14 @@ pub fn dumpStackTrace(stack_trace: std.builtin.StackTrace) void {...@@ -516,14 +519,14 @@ pub fn dumpStackTrace(stack_trace: std.builtin.StackTrace) void {
516 nosuspend {519 nosuspend {
517 if (builtin.target.cpu.arch.isWasm()) {520 if (builtin.target.cpu.arch.isWasm()) {
518 if (native_os == .wasi) {521 if (native_os == .wasi) {
519 var stderr = lockStdErr2(&.{});522 const stderr = lockStderrWriter(&.{});
520 defer unlockStdErr();523 defer unlockStderrWriter();
521 stderr.writeAll("Unable to dump stack trace: not implemented for Wasm\n") catch return;524 stderr.writeAll("Unable to dump stack trace: not implemented for Wasm\n") catch return;
522 }525 }
523 return;526 return;
524 }527 }
525 var stderr = lockStdErr2(&.{});528 const stderr = lockStderrWriter(&.{});
526 defer unlockStdErr();529 defer unlockStderrWriter();
527 if (builtin.strip_debug_info) {530 if (builtin.strip_debug_info) {
528 stderr.writeAll("Unable to dump stack trace: debug info stripped\n") catch return;531 stderr.writeAll("Unable to dump stack trace: debug info stripped\n") catch return;
529 return;532 return;
...@@ -532,7 +535,7 @@ pub fn dumpStackTrace(stack_trace: std.builtin.StackTrace) void {...@@ -532,7 +535,7 @@ pub fn dumpStackTrace(stack_trace: std.builtin.StackTrace) void {
532 stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;535 stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
533 return;536 return;
534 };537 };
535 writeStackTrace(stack_trace, &stderr, debug_info, io.tty.detectConfig(.stderr())) catch |err| {538 writeStackTrace(stack_trace, stderr, debug_info, io.tty.detectConfig(.stderr())) catch |err| {
536 stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;539 stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
537 return;540 return;
538 };541 };
...@@ -683,8 +686,8 @@ pub fn defaultPanic(...@@ -683,8 +686,8 @@ pub fn defaultPanic(
683 _ = panicking.fetchAdd(1, .seq_cst);686 _ = panicking.fetchAdd(1, .seq_cst);
684687
685 {688 {
686 var stderr = lockStdErr2(&.{});689 const stderr = lockStderrWriter(&.{});
687 defer unlockStdErr();690 defer unlockStderrWriter();
688691
689 if (builtin.single_threaded) {692 if (builtin.single_threaded) {
690 stderr.print("panic: ", .{}) catch posix.abort();693 stderr.print("panic: ", .{}) catch posix.abort();
...@@ -695,7 +698,7 @@ pub fn defaultPanic(...@@ -695,7 +698,7 @@ pub fn defaultPanic(
695 stderr.print("{s}\n", .{msg}) catch posix.abort();698 stderr.print("{s}\n", .{msg}) catch posix.abort();
696699
697 if (@errorReturnTrace()) |t| dumpStackTrace(t.*);700 if (@errorReturnTrace()) |t| dumpStackTrace(t.*);
698 dumpCurrentStackTraceToWriter(first_trace_addr orelse @returnAddress(), &stderr) catch {};701 dumpCurrentStackTraceToWriter(first_trace_addr orelse @returnAddress(), stderr) catch {};
699 }702 }
700703
701 waitForOtherThreadToFinishPanicking();704 waitForOtherThreadToFinishPanicking();
...@@ -1468,8 +1471,8 @@ fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopa...@@ -1468,8 +1471,8 @@ fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopa
1468}1471}
14691472
1470fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*anyopaque) void {1473fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*anyopaque) void {
1471 var stderr = lockStdErr2(&.{});1474 const stderr = lockStderrWriter(&.{});
1472 defer unlockStdErr();1475 defer unlockStderrWriter();
1473 _ = switch (sig) {1476 _ = switch (sig) {
1474 posix.SIG.SEGV => if (native_arch == .x86_64 and native_os == .linux and code == 128) // SI_KERNEL1477 posix.SIG.SEGV => if (native_arch == .x86_64 and native_os == .linux and code == 128) // SI_KERNEL
1475 // x86_64 doesn't have a full 64-bit virtual address space.1478 // x86_64 doesn't have a full 64-bit virtual address space.
...@@ -1517,7 +1520,7 @@ fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*anyopaque)...@@ -1517,7 +1520,7 @@ fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*anyopaque)
1517 }, @ptrCast(ctx)).__mcontext_data;1520 }, @ptrCast(ctx)).__mcontext_data;
1518 }1521 }
1519 relocateContext(&new_ctx);1522 relocateContext(&new_ctx);
1520 dumpStackTraceFromBase(&new_ctx, &stderr);1523 dumpStackTraceFromBase(&new_ctx, stderr);
1521 },1524 },
1522 else => {},1525 else => {},
1523 }1526 }
...@@ -1547,10 +1550,10 @@ fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, msg: u8, label:...@@ -1547,10 +1550,10 @@ fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, msg: u8, label:
1547 _ = panicking.fetchAdd(1, .seq_cst);1550 _ = panicking.fetchAdd(1, .seq_cst);
15481551
1549 {1552 {
1550 var stderr = lockStdErr2(&.{});1553 const stderr = lockStderrWriter(&.{});
1551 defer unlockStdErr();1554 defer unlockStderrWriter();
15521555
1553 dumpSegfaultInfoWindows(info, msg, label, &stderr);1556 dumpSegfaultInfoWindows(info, msg, label, stderr);
1554 }1557 }
15551558
1556 waitForOtherThreadToFinishPanicking();1559 waitForOtherThreadToFinishPanicking();
...@@ -1665,8 +1668,8 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize...@@ -1665,8 +1668,8 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize
1665 if (!enabled) return;1668 if (!enabled) return;
16661669
1667 const tty_config = io.tty.detectConfig(.stderr());1670 const tty_config = io.tty.detectConfig(.stderr());
1668 var stderr = lockStdErr2(&.{});1671 const stderr = lockStderrWriter(&.{});
1669 defer unlockStdErr();1672 defer unlockStderrWriter();
1670 const end = @min(t.index, size);1673 const end = @min(t.index, size);
1671 const debug_info = getSelfDebugInfo() catch |err| {1674 const debug_info = getSelfDebugInfo() catch |err| {
1672 stderr.print(1675 stderr.print(
...@@ -1683,7 +1686,7 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize...@@ -1683,7 +1686,7 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize
1683 .index = frames.len,1686 .index = frames.len,
1684 .instruction_addresses = frames,1687 .instruction_addresses = frames,
1685 };1688 };
1686 writeStackTrace(stack_trace, &stderr, debug_info, tty_config) catch continue;1689 writeStackTrace(stack_trace, stderr, debug_info, tty_config) catch continue;
1687 }1690 }
1688 if (t.index > end) {1691 if (t.index > end) {
1689 stderr.print("{d} more traces not shown; consider increasing trace size\n", .{1692 stderr.print("{d} more traces not shown; consider increasing trace size\n", .{
lib/std/debug/Dwarf.zig+6-18
...@@ -2212,7 +2212,7 @@ pub const ElfModule = struct {...@@ -2212,7 +2212,7 @@ pub const ElfModule = struct {
2212 var separate_debug_filename: ?[]const u8 = null;2212 var separate_debug_filename: ?[]const u8 = null;
2213 var separate_debug_crc: ?u32 = null;2213 var separate_debug_crc: ?u32 = null;
22142214
2215 shdrs: for (shdrs) |*shdr| {2215 for (shdrs) |*shdr| {
2216 if (shdr.sh_type == elf.SHT_NULL or shdr.sh_type == elf.SHT_NOBITS) continue;2216 if (shdr.sh_type == elf.SHT_NULL or shdr.sh_type == elf.SHT_NOBITS) continue;
2217 const name = mem.sliceTo(header_strings[shdr.sh_name..], 0);2217 const name = mem.sliceTo(header_strings[shdr.sh_name..], 0);
22182218
...@@ -2243,24 +2243,12 @@ pub const ElfModule = struct {...@@ -2243,24 +2243,12 @@ pub const ElfModule = struct {
22432243
2244 var zlib_stream: std.compress.zlib.Decompressor = .init(&section_reader);2244 var zlib_stream: std.compress.zlib.Decompressor = .init(&section_reader);
22452245
2246 const decompressed_section = try gpa.alloc(u8, ch_size);2246 const decompressed_section = zlib_stream.reader().readAlloc(gpa, ch_size) catch continue;
2247 errdefer gpa.free(decompressed_section);2247 if (decompressed_section.len != ch_size) {
22482248 gpa.free(decompressed_section);
2249 {2249 continue;
2250 var i: usize = 0;
2251 while (true) {
2252 const status = zlib_stream.streamReadVec(&.{decompressed_section[i..]}) catch {
2253 gpa.free(decompressed_section);
2254 continue :shdrs;
2255 };
2256 i += status.len;
2257 if (i == decompressed_section.len) break;
2258 if (status.end) {
2259 gpa.free(decompressed_section);
2260 continue :shdrs;
2261 }
2262 }
2263 }2250 }
2251 errdefer gpa.free(decompressed_section);
22642252
2265 break :blk .{2253 break :blk .{
2266 .data = decompressed_section,2254 .data = decompressed_section,
lib/std/debug/Dwarf/expression.zig+4-4
...@@ -62,7 +62,7 @@ pub const Error = error{...@@ -62,7 +62,7 @@ pub const Error = error{
62 InvalidTypeLength,62 InvalidTypeLength,
6363
64 TruncatedIntegralType,64 TruncatedIntegralType,
65} || abi.RegBytesError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero };65} || abi.RegBytesError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero, ReadFailed };
6666
67/// A stack machine that can decode and run DWARF expressions.67/// A stack machine that can decode and run DWARF expressions.
68/// Expressions can be decoded for non-native address size and endianness,68/// Expressions can be decoded for non-native address size and endianness,
...@@ -259,7 +259,7 @@ pub fn StackMachine(comptime options: Options) type {...@@ -259,7 +259,7 @@ pub fn StackMachine(comptime options: Options) type {
259 allocator: std.mem.Allocator,259 allocator: std.mem.Allocator,
260 context: Context,260 context: Context,
261 initial_value: ?usize,261 initial_value: ?usize,
262 ) anyerror!?Value {262 ) Error!?Value {
263 if (initial_value) |i| try self.stack.append(allocator, .{ .generic = i });263 if (initial_value) |i| try self.stack.append(allocator, .{ .generic = i });
264 var reader: std.io.BufferedReader = undefined;264 var reader: std.io.BufferedReader = undefined;
265 reader.initFixed(expression);265 reader.initFixed(expression);
...@@ -274,13 +274,13 @@ pub fn StackMachine(comptime options: Options) type {...@@ -274,13 +274,13 @@ pub fn StackMachine(comptime options: Options) type {
274 reader: *std.io.BufferedReader,274 reader: *std.io.BufferedReader,
275 allocator: std.mem.Allocator,275 allocator: std.mem.Allocator,
276 context: Context,276 context: Context,
277 ) anyerror!bool {277 ) Error!bool {
278 if (@sizeOf(usize) != @sizeOf(Address) or options.endian != native_endian)278 if (@sizeOf(usize) != @sizeOf(Address) or options.endian != native_endian)
279 @compileError("Execution of non-native address sizes / endianness is not supported");279 @compileError("Execution of non-native address sizes / endianness is not supported");
280280
281 const opcode = reader.takeByte() catch |err| switch (err) {281 const opcode = reader.takeByte() catch |err| switch (err) {
282 error.EndOfStream => return false,282 error.EndOfStream => return false,
283 else => |e| return @errorCast(e),283 error.ReadFailed => return error.ReadFailed,
284 };284 };
285 if (options.call_frame_context and !isOpcodeValidInCFA(opcode)) return error.InvalidCFAOpcode;285 if (options.call_frame_context and !isOpcodeValidInCFA(opcode)) return error.InvalidCFAOpcode;
286 const operand = try readOperand(reader, opcode, context);286 const operand = try readOperand(reader, opcode, context);
lib/std/fifo.zig+17-10
...@@ -238,23 +238,30 @@ pub fn LinearFifo(...@@ -238,23 +238,30 @@ pub fn LinearFifo(
238 return .{238 return .{
239 .context = self,239 .context = self,
240 .vtable = &.{240 .vtable = &.{
241 .read = &reader_read,241 .read = &readerRead,
242 .readv = &reader_readv,242 .readVec = &readerReadVec,
243 .discard = &readerDiscard,
243 },244 },
244 };245 };
245 }246 }
246 fn reader_read(247 fn readerRead(
247 ctx: ?*anyopaque,248 ctx: ?*anyopaque,
248 bw: *std.io.BufferedWriter,249 bw: *std.io.BufferedWriter,
249 limit: std.io.Reader.Limit,250 limit: std.io.Reader.Limit,
250 ) anyerror!std.io.Reader.Status {251 ) std.io.Reader.RwError!usize {
251 const fifo: *Self = @alignCast(@ptrCast(ctx));252 const fifo: *Self = @alignCast(@ptrCast(ctx));
252 _ = fifo;253 _ = fifo;
253 _ = bw;254 _ = bw;
254 _ = limit;255 _ = limit;
255 @panic("TODO");256 @panic("TODO");
256 }257 }
257 fn reader_readv(ctx: ?*anyopaque, data: []const []u8) anyerror!std.io.Reader.Status {258 fn readerReadVec(ctx: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
259 const fifo: *Self = @alignCast(@ptrCast(ctx));
260 _ = fifo;
261 _ = data;
262 @panic("TODO");
263 }
264 fn readerDiscard(ctx: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
258 const fifo: *Self = @alignCast(@ptrCast(ctx));265 const fifo: *Self = @alignCast(@ptrCast(ctx));
259 _ = fifo;266 _ = fifo;
260 _ = data;267 _ = data;
...@@ -351,26 +358,26 @@ pub fn LinearFifo(...@@ -351,26 +358,26 @@ pub fn LinearFifo(
351 return .{358 return .{
352 .context = fifo,359 .context = fifo,
353 .vtable = &.{360 .vtable = &.{
354 .writeSplat = writer_writeSplat,361 .writeSplat = writerWriteSplat,
355 .writeFile = writer_writeFile,362 .writeFile = writerWriteFile,
356 },363 },
357 };364 };
358 }365 }
359 fn writer_writeSplat(ctx: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize {366 fn writerWriteSplat(ctx: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
360 const fifo: *Self = @alignCast(@ptrCast(ctx));367 const fifo: *Self = @alignCast(@ptrCast(ctx));
361 _ = fifo;368 _ = fifo;
362 _ = data;369 _ = data;
363 _ = splat;370 _ = splat;
364 @panic("TODO");371 @panic("TODO");
365 }372 }
366 fn writer_writeFile(373 fn writerWriteFile(
367 ctx: ?*anyopaque,374 ctx: ?*anyopaque,
368 file: std.fs.File,375 file: std.fs.File,
369 offset: std.io.Writer.Offset,376 offset: std.io.Writer.Offset,
370 limit: std.io.Writer.Limit,377 limit: std.io.Writer.Limit,
371 headers_and_trailers: []const []const u8,378 headers_and_trailers: []const []const u8,
372 headers_len: usize,379 headers_len: usize,
373 ) anyerror!usize {380 ) std.io.Writer.Error!usize {
374 const fifo: *Self = @alignCast(@ptrCast(ctx));381 const fifo: *Self = @alignCast(@ptrCast(ctx));
375 _ = fifo;382 _ = fifo;
376 _ = file;383 _ = file;
lib/std/fmt.zig+28-19
...@@ -1,8 +1,8 @@...@@ -1,8 +1,8 @@
1//! String formatting and parsing.1//! String formatting and parsing.
22
3const std = @import("std.zig");
4const builtin = @import("builtin");3const builtin = @import("builtin");
54
5const std = @import("std.zig");
6const io = std.io;6const io = std.io;
7const math = std.math;7const math = std.math;
8const assert = std.debug.assert;8const assert = std.debug.assert;
...@@ -12,6 +12,7 @@ const meta = std.meta;...@@ -12,6 +12,7 @@ const meta = std.meta;
12const lossyCast = math.lossyCast;12const lossyCast = math.lossyCast;
13const expectFmt = std.testing.expectFmt;13const expectFmt = std.testing.expectFmt;
14const testing = std.testing;14const testing = std.testing;
15const Allocator = std.mem.Allocator;
1516
16pub const float = @import("fmt/float.zig");17pub const float = @import("fmt/float.zig");
1718
...@@ -91,7 +92,7 @@ pub const Options = struct {...@@ -91,7 +92,7 @@ pub const Options = struct {
91/// A user type may be a `struct`, `vector`, `union` or `enum` type.92/// A user type may be a `struct`, `vector`, `union` or `enum` type.
92///93///
93/// To print literal curly braces, escape them by writing them twice, e.g. `{{` or `}}`.94/// To print literal curly braces, escape them by writing them twice, e.g. `{{` or `}}`.
94pub fn format(bw: *std.io.BufferedWriter, comptime fmt: []const u8, args: anytype) anyerror!void {95pub fn format(bw: *std.io.BufferedWriter, comptime fmt: []const u8, args: anytype) std.io.Writer.Error!void {
95 const ArgsType = @TypeOf(args);96 const ArgsType = @TypeOf(args);
96 const args_type_info = @typeInfo(ArgsType);97 const args_type_info = @typeInfo(ArgsType);
97 if (args_type_info != .@"struct") {98 if (args_type_info != .@"struct") {
...@@ -531,7 +532,7 @@ pub fn Formatter(comptime formatFn: anytype) type {...@@ -531,7 +532,7 @@ pub fn Formatter(comptime formatFn: anytype) type {
531 const Data = @typeInfo(@TypeOf(formatFn)).@"fn".params[0].type.?;532 const Data = @typeInfo(@TypeOf(formatFn)).@"fn".params[0].type.?;
532 return struct {533 return struct {
533 data: Data,534 data: Data,
534 pub fn format(self: @This(), writer: *std.io.BufferedWriter, comptime fmt: []const u8) anyerror!void {535 pub fn format(self: @This(), writer: *std.io.BufferedWriter, comptime fmt: []const u8) std.io.Writer.Error!void {
535 try formatFn(self.data, writer, fmt);536 try formatFn(self.data, writer, fmt);
536 }537 }
537 };538 };
...@@ -833,8 +834,7 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintErro...@@ -833,8 +834,7 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintErro
833 var bw: std.io.BufferedWriter = undefined;834 var bw: std.io.BufferedWriter = undefined;
834 bw.initFixed(buf);835 bw.initFixed(buf);
835 bw.print(fmt, args) catch |err| switch (err) {836 bw.print(fmt, args) catch |err| switch (err) {
836 error.NoSpaceLeft => return error.NoSpaceLeft,837 error.WriteFailed => return error.NoSpaceLeft,
837 else => unreachable,
838 };838 };
839 return bw.getWritten();839 return bw.getWritten();
840}840}
...@@ -846,25 +846,34 @@ pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintErr...@@ -846,25 +846,34 @@ pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintErr
846846
847/// Count the characters needed for format.847/// Count the characters needed for format.
848pub fn count(comptime fmt: []const u8, args: anytype) usize {848pub fn count(comptime fmt: []const u8, args: anytype) usize {
849 var buffer: [std.atomic.cache_line]u8 = undefined;849 var trash_buffer: [std.atomic.cache_line]u8 = undefined;
850 var bw = std.io.Writer.null.buffered(&buffer);850 var null_writer: std.io.Writer.Null = undefined;
851 var bw = null_writer.writer().buffered(&trash_buffer);
851 bw.print(fmt, args) catch unreachable;852 bw.print(fmt, args) catch unreachable;
852 return bw.count;853 return bw.count;
853}854}
854855
855pub const AllocPrintError = error{OutOfMemory};856pub fn allocPrint(gpa: Allocator, comptime fmt: []const u8, args: anytype) Allocator.Error![]u8 {
856857 var aw: std.io.AllocatingWriter = undefined;
857pub fn allocPrint(allocator: mem.Allocator, comptime fmt: []const u8, args: anytype) AllocPrintError![]u8 {858 try aw.initCapacity(gpa, fmt.len);
858 const size = math.cast(usize, count(fmt, args)) orelse return error.OutOfMemory;859 aw.buffered_writer.print(fmt, args) catch |err| switch (err) {
859 const buf = try allocator.alloc(u8, size);860 error.WriteFailed => return error.OutOfMemory,
860 return bufPrint(buf, fmt, args) catch |err| switch (err) {
861 error.NoSpaceLeft => unreachable, // we just counted the size above
862 };861 };
863}862 return aw.toOwnedSlice();
864863}
865pub fn allocPrintZ(allocator: mem.Allocator, comptime fmt: []const u8, args: anytype) AllocPrintError![:0]u8 {864
866 const result = try allocPrint(allocator, fmt ++ "\x00", args);865pub fn allocPrintSentinel(
867 return result[0 .. result.len - 1 :0];866 gpa: Allocator,
867 comptime fmt: []const u8,
868 args: anytype,
869 comptime sentinel: u8,
870) Allocator.Error![:sentinel]u8 {
871 var aw: std.io.AllocatingWriter = undefined;
872 try aw.initCapacity(gpa, fmt.len);
873 aw.buffered_writer.print(fmt, args) catch |err| switch (err) {
874 error.WriteFailed => return error.OutOfMemory,
875 };
876 return aw.toOwnedSliceSentinel(sentinel);
868}877}
869878
870pub inline fn comptimePrint(comptime fmt: []const u8, args: anytype) *const [count(fmt, args):0]u8 {879pub inline fn comptimePrint(comptime fmt: []const u8, args: anytype) *const [count(fmt, args):0]u8 {
lib/std/fs/Dir.zig+5-11
...@@ -2619,10 +2619,13 @@ pub fn updateFile(...@@ -2619,10 +2619,13 @@ pub fn updateFile(
2619 var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = actual_mode });2619 var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = actual_mode });
2620 defer atomic_file.deinit();2620 defer atomic_file.deinit();
26212621
2622 try atomic_file.file.writeFileAll(src_file, .{ .in_len = src_stat.size });2622 try atomic_file.file.writeFileAll(src_file, .{
2623 .offset = .zero,
2624 .limit = .limited(src_stat.size),
2625 });
2623 try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime);2626 try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime);
2624 try atomic_file.finish();2627 try atomic_file.finish();
2625 return PrevStatus.stale;2628 return .stale;
2626}2629}
26272630
2628pub const CopyFileError = File.OpenError || File.StatError ||2631pub const CopyFileError = File.OpenError || File.StatError ||
...@@ -2833,15 +2836,6 @@ pub fn setPermissions(self: Dir, permissions: Permissions) SetPermissionsError!v...@@ -2833,15 +2836,6 @@ pub fn setPermissions(self: Dir, permissions: Permissions) SetPermissionsError!v
2833 try file.setPermissions(permissions);2836 try file.setPermissions(permissions);
2834}2837}
28352838
2836const Metadata = File.Metadata;
2837pub const MetadataError = File.MetadataError;
2838
2839/// Returns a `Metadata` struct, representing the permissions on the directory
2840pub fn metadata(self: Dir) MetadataError!Metadata {
2841 const file: File = .{ .handle = self.fd };
2842 return try file.metadata();
2843}
2844
2845const Dir = @This();2839const Dir = @This();
2846const builtin = @import("builtin");2840const builtin = @import("builtin");
2847const std = @import("../std.zig");2841const std = @import("../std.zig");
lib/std/fs/File.zig+398-877
...@@ -363,8 +363,10 @@ pub fn getPos(self: File) GetSeekPosError!u64 {...@@ -363,8 +363,10 @@ pub fn getPos(self: File) GetSeekPosError!u64 {
363 return posix.lseek_CUR_get(self.handle);363 return posix.lseek_CUR_get(self.handle);
364}364}
365365
366pub const GetEndPosError = std.os.windows.GetFileSizeError || StatError;
367
366/// TODO: integrate with async I/O368/// TODO: integrate with async I/O
367pub fn getEndPos(self: File) GetSeekPosError!u64 {369pub fn getEndPos(self: File) GetEndPosError!u64 {
368 if (builtin.os.tag == .windows) {370 if (builtin.os.tag == .windows) {
369 return windows.GetFileSizeEx(self.handle);371 return windows.GetFileSizeEx(self.handle);
370 }372 }
...@@ -489,7 +491,6 @@ pub const Stat = struct {...@@ -489,7 +491,6 @@ pub const Stat = struct {
489pub const StatError = posix.FStatError;491pub const StatError = posix.FStatError;
490492
491/// Returns `Stat` containing basic information about the `File`.493/// Returns `Stat` containing basic information about the `File`.
492/// Use `metadata` to retrieve more detailed information (e.g. creation time, permissions).
493/// TODO: integrate with async I/O494/// TODO: integrate with async I/O
494pub fn stat(self: File) StatError!Stat {495pub fn stat(self: File) StatError!Stat {
495 if (builtin.os.tag == .windows) {496 if (builtin.os.tag == .windows) {
...@@ -755,361 +756,6 @@ pub fn setPermissions(self: File, permissions: Permissions) SetPermissionsError!...@@ -755,361 +756,6 @@ pub fn setPermissions(self: File, permissions: Permissions) SetPermissionsError!
755 }756 }
756}757}
757758
758/// Cross-platform representation of file metadata.
759/// Platform-specific functionality is available through the `inner` field.
760pub const Metadata = struct {
761 /// Exposes platform-specific functionality.
762 inner: switch (builtin.os.tag) {
763 .windows => MetadataWindows,
764 .linux => MetadataLinux,
765 .wasi => MetadataWasi,
766 else => MetadataUnix,
767 },
768
769 const Self = @This();
770
771 /// Returns the size of the file
772 pub fn size(self: Self) u64 {
773 return self.inner.size();
774 }
775
776 /// Returns a `Permissions` struct, representing the permissions on the file
777 pub fn permissions(self: Self) Permissions {
778 return self.inner.permissions();
779 }
780
781 /// Returns the `Kind` of file.
782 /// On Windows, can only return: `.file`, `.directory`, `.sym_link` or `.unknown`
783 pub fn kind(self: Self) Kind {
784 return self.inner.kind();
785 }
786
787 /// Returns the last time the file was accessed in nanoseconds since UTC 1970-01-01
788 pub fn accessed(self: Self) i128 {
789 return self.inner.accessed();
790 }
791
792 /// Returns the time the file was modified in nanoseconds since UTC 1970-01-01
793 pub fn modified(self: Self) i128 {
794 return self.inner.modified();
795 }
796
797 /// Returns the time the file was created in nanoseconds since UTC 1970-01-01
798 /// On Windows, this cannot return null
799 /// On Linux, this returns null if the filesystem does not support creation times
800 /// On Unices, this returns null if the filesystem or OS does not support creation times
801 /// On MacOS, this returns the ctime if the filesystem does not support creation times; this is insanity, and yet another reason to hate on Apple
802 pub fn created(self: Self) ?i128 {
803 return self.inner.created();
804 }
805};
806
807pub const MetadataUnix = struct {
808 stat: posix.Stat,
809
810 const Self = @This();
811
812 /// Returns the size of the file
813 pub fn size(self: Self) u64 {
814 return @intCast(self.stat.size);
815 }
816
817 /// Returns a `Permissions` struct, representing the permissions on the file
818 pub fn permissions(self: Self) Permissions {
819 return .{ .inner = .{ .mode = self.stat.mode } };
820 }
821
822 /// Returns the `Kind` of the file
823 pub fn kind(self: Self) Kind {
824 if (builtin.os.tag == .wasi and !builtin.link_libc) return switch (self.stat.filetype) {
825 .BLOCK_DEVICE => .block_device,
826 .CHARACTER_DEVICE => .character_device,
827 .DIRECTORY => .directory,
828 .SYMBOLIC_LINK => .sym_link,
829 .REGULAR_FILE => .file,
830 .SOCKET_STREAM, .SOCKET_DGRAM => .unix_domain_socket,
831 else => .unknown,
832 };
833
834 const m = self.stat.mode & posix.S.IFMT;
835
836 switch (m) {
837 posix.S.IFBLK => return .block_device,
838 posix.S.IFCHR => return .character_device,
839 posix.S.IFDIR => return .directory,
840 posix.S.IFIFO => return .named_pipe,
841 posix.S.IFLNK => return .sym_link,
842 posix.S.IFREG => return .file,
843 posix.S.IFSOCK => return .unix_domain_socket,
844 else => {},
845 }
846
847 if (builtin.os.tag.isSolarish()) switch (m) {
848 posix.S.IFDOOR => return .door,
849 posix.S.IFPORT => return .event_port,
850 else => {},
851 };
852
853 return .unknown;
854 }
855
856 /// Returns the last time the file was accessed in nanoseconds since UTC 1970-01-01
857 pub fn accessed(self: Self) i128 {
858 const atime = self.stat.atime();
859 return @as(i128, atime.sec) * std.time.ns_per_s + atime.nsec;
860 }
861
862 /// Returns the last time the file was modified in nanoseconds since UTC 1970-01-01
863 pub fn modified(self: Self) i128 {
864 const mtime = self.stat.mtime();
865 return @as(i128, mtime.sec) * std.time.ns_per_s + mtime.nsec;
866 }
867
868 /// Returns the time the file was created in nanoseconds since UTC 1970-01-01.
869 /// Returns null if this is not supported by the OS or filesystem
870 pub fn created(self: Self) ?i128 {
871 if (!@hasDecl(@TypeOf(self.stat), "birthtime")) return null;
872 const birthtime = self.stat.birthtime();
873
874 // If the filesystem doesn't support this the value *should* be:
875 // On FreeBSD: nsec = 0, sec = -1
876 // On NetBSD and OpenBSD: nsec = 0, sec = 0
877 // On MacOS, it is set to ctime -- we cannot detect this!!
878 switch (builtin.os.tag) {
879 .freebsd => if (birthtime.sec == -1 and birthtime.nsec == 0) return null,
880 .netbsd, .openbsd => if (birthtime.sec == 0 and birthtime.nsec == 0) return null,
881 .macos => {},
882 else => @compileError("Creation time detection not implemented for OS"),
883 }
884
885 return @as(i128, birthtime.sec) * std.time.ns_per_s + birthtime.nsec;
886 }
887};
888
889/// `MetadataUnix`, but using Linux's `statx` syscall.
890pub const MetadataLinux = struct {
891 statx: std.os.linux.Statx,
892
893 const Self = @This();
894
895 /// Returns the size of the file
896 pub fn size(self: Self) u64 {
897 return self.statx.size;
898 }
899
900 /// Returns a `Permissions` struct, representing the permissions on the file
901 pub fn permissions(self: Self) Permissions {
902 return Permissions{ .inner = PermissionsUnix{ .mode = self.statx.mode } };
903 }
904
905 /// Returns the `Kind` of the file
906 pub fn kind(self: Self) Kind {
907 const m = self.statx.mode & posix.S.IFMT;
908
909 switch (m) {
910 posix.S.IFBLK => return .block_device,
911 posix.S.IFCHR => return .character_device,
912 posix.S.IFDIR => return .directory,
913 posix.S.IFIFO => return .named_pipe,
914 posix.S.IFLNK => return .sym_link,
915 posix.S.IFREG => return .file,
916 posix.S.IFSOCK => return .unix_domain_socket,
917 else => {},
918 }
919
920 return .unknown;
921 }
922
923 /// Returns the last time the file was accessed in nanoseconds since UTC 1970-01-01
924 pub fn accessed(self: Self) i128 {
925 return @as(i128, self.statx.atime.sec) * std.time.ns_per_s + self.statx.atime.nsec;
926 }
927
928 /// Returns the last time the file was modified in nanoseconds since UTC 1970-01-01
929 pub fn modified(self: Self) i128 {
930 return @as(i128, self.statx.mtime.sec) * std.time.ns_per_s + self.statx.mtime.nsec;
931 }
932
933 /// Returns the time the file was created in nanoseconds since UTC 1970-01-01.
934 /// Returns null if this is not supported by the filesystem, or on kernels before than version 4.11
935 pub fn created(self: Self) ?i128 {
936 if (self.statx.mask & std.os.linux.STATX_BTIME == 0) return null;
937 return @as(i128, self.statx.btime.sec) * std.time.ns_per_s + self.statx.btime.nsec;
938 }
939};
940
941pub const MetadataWasi = struct {
942 stat: std.os.wasi.filestat_t,
943
944 pub fn size(self: @This()) u64 {
945 return self.stat.size;
946 }
947
948 pub fn permissions(self: @This()) Permissions {
949 return .{ .inner = .{ .mode = self.stat.mode } };
950 }
951
952 pub fn kind(self: @This()) Kind {
953 return switch (self.stat.filetype) {
954 .BLOCK_DEVICE => .block_device,
955 .CHARACTER_DEVICE => .character_device,
956 .DIRECTORY => .directory,
957 .SYMBOLIC_LINK => .sym_link,
958 .REGULAR_FILE => .file,
959 .SOCKET_STREAM, .SOCKET_DGRAM => .unix_domain_socket,
960 else => .unknown,
961 };
962 }
963
964 pub fn accessed(self: @This()) i128 {
965 return self.stat.atim;
966 }
967
968 pub fn modified(self: @This()) i128 {
969 return self.stat.mtim;
970 }
971
972 pub fn created(self: @This()) ?i128 {
973 return self.stat.ctim;
974 }
975};
976
977pub const MetadataWindows = struct {
978 attributes: windows.DWORD,
979 reparse_tag: windows.DWORD,
980 _size: u64,
981 access_time: i128,
982 modified_time: i128,
983 creation_time: i128,
984
985 const Self = @This();
986
987 /// Returns the size of the file
988 pub fn size(self: Self) u64 {
989 return self._size;
990 }
991
992 /// Returns a `Permissions` struct, representing the permissions on the file
993 pub fn permissions(self: Self) Permissions {
994 return .{ .inner = .{ .attributes = self.attributes } };
995 }
996
997 /// Returns the `Kind` of the file.
998 /// Can only return: `.file`, `.directory`, `.sym_link` or `.unknown`
999 pub fn kind(self: Self) Kind {
1000 if (self.attributes & windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) {
1001 if (self.reparse_tag & windows.reparse_tag_name_surrogate_bit != 0) {
1002 return .sym_link;
1003 }
1004 } else if (self.attributes & windows.FILE_ATTRIBUTE_DIRECTORY != 0) {
1005 return .directory;
1006 } else {
1007 return .file;
1008 }
1009 return .unknown;
1010 }
1011
1012 /// Returns the last time the file was accessed in nanoseconds since UTC 1970-01-01
1013 pub fn accessed(self: Self) i128 {
1014 return self.access_time;
1015 }
1016
1017 /// Returns the time the file was modified in nanoseconds since UTC 1970-01-01
1018 pub fn modified(self: Self) i128 {
1019 return self.modified_time;
1020 }
1021
1022 /// Returns the time the file was created in nanoseconds since UTC 1970-01-01.
1023 /// This never returns null, only returning an optional for compatibility with other OSes
1024 pub fn created(self: Self) ?i128 {
1025 return self.creation_time;
1026 }
1027};
1028
1029pub const MetadataError = posix.FStatError;
1030
1031pub fn metadata(self: File) MetadataError!Metadata {
1032 return .{
1033 .inner = switch (builtin.os.tag) {
1034 .windows => blk: {
1035 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
1036 var info: windows.FILE_ALL_INFORMATION = undefined;
1037
1038 const rc = windows.ntdll.NtQueryInformationFile(self.handle, &io_status_block, &info, @sizeOf(windows.FILE_ALL_INFORMATION), .FileAllInformation);
1039 switch (rc) {
1040 .SUCCESS => {},
1041 // Buffer overflow here indicates that there is more information available than was able to be stored in the buffer
1042 // size provided. This is treated as success because the type of variable-length information that this would be relevant for
1043 // (name, volume name, etc) we don't care about.
1044 .BUFFER_OVERFLOW => {},
1045 .INVALID_PARAMETER => unreachable,
1046 .ACCESS_DENIED => return error.AccessDenied,
1047 else => return windows.unexpectedStatus(rc),
1048 }
1049
1050 const reparse_tag: windows.DWORD = reparse_blk: {
1051 if (info.BasicInformation.FileAttributes & windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) {
1052 var tag_info: windows.FILE_ATTRIBUTE_TAG_INFO = undefined;
1053 const tag_rc = windows.ntdll.NtQueryInformationFile(self.handle, &io_status_block, &tag_info, @sizeOf(windows.FILE_ATTRIBUTE_TAG_INFO), .FileAttributeTagInformation);
1054 switch (tag_rc) {
1055 .SUCCESS => {},
1056 // INFO_LENGTH_MISMATCH and ACCESS_DENIED are the only documented possible errors
1057 // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/d295752f-ce89-4b98-8553-266d37c84f0e
1058 .INFO_LENGTH_MISMATCH => unreachable,
1059 .ACCESS_DENIED => return error.AccessDenied,
1060 else => return windows.unexpectedStatus(rc),
1061 }
1062 break :reparse_blk tag_info.ReparseTag;
1063 }
1064 break :reparse_blk 0;
1065 };
1066
1067 break :blk .{
1068 .attributes = info.BasicInformation.FileAttributes,
1069 .reparse_tag = reparse_tag,
1070 ._size = @as(u64, @bitCast(info.StandardInformation.EndOfFile)),
1071 .access_time = windows.fromSysTime(info.BasicInformation.LastAccessTime),
1072 .modified_time = windows.fromSysTime(info.BasicInformation.LastWriteTime),
1073 .creation_time = windows.fromSysTime(info.BasicInformation.CreationTime),
1074 };
1075 },
1076 .linux => blk: {
1077 var stx = std.mem.zeroes(linux.Statx);
1078
1079 // We are gathering information for Metadata, which is meant to contain all the
1080 // native OS information about the file, so use all known flags.
1081 const rc = linux.statx(
1082 self.handle,
1083 "",
1084 linux.AT.EMPTY_PATH,
1085 linux.STATX_BASIC_STATS | linux.STATX_BTIME,
1086 &stx,
1087 );
1088
1089 switch (linux.E.init(rc)) {
1090 .SUCCESS => {},
1091 .ACCES => unreachable,
1092 .BADF => unreachable,
1093 .FAULT => unreachable,
1094 .INVAL => unreachable,
1095 .LOOP => unreachable,
1096 .NAMETOOLONG => unreachable,
1097 .NOENT => unreachable,
1098 .NOMEM => return error.SystemResources,
1099 .NOTDIR => unreachable,
1100 else => |err| return posix.unexpectedErrno(err),
1101 }
1102
1103 break :blk .{
1104 .statx = stx,
1105 };
1106 },
1107 .wasi => .{ .stat = try std.os.fstat_wasi(self.handle) },
1108 else => .{ .stat = try posix.fstat(self.handle) },
1109 },
1110 };
1111}
1112
1113pub const UpdateTimesError = posix.FutimensError || windows.SetFileTimeError;759pub const UpdateTimesError = posix.FutimensError || windows.SetFileTimeError;
1114760
1115/// The underlying file system may have a different granularity than nanoseconds,761/// The underlying file system may have a different granularity than nanoseconds,
...@@ -1193,18 +839,6 @@ pub fn read(self: File, buffer: []u8) ReadError!usize {...@@ -1193,18 +839,6 @@ pub fn read(self: File, buffer: []u8) ReadError!usize {
1193 return posix.read(self.handle, buffer);839 return posix.read(self.handle, buffer);
1194}840}
1195841
1196/// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
1197/// means the file reached the end. Reaching the end of a file is not an error condition.
1198pub fn readAll(self: File, buffer: []u8) ReadError!usize {
1199 var index: usize = 0;
1200 while (index != buffer.len) {
1201 const amt = try self.read(buffer[index..]);
1202 if (amt == 0) break;
1203 index += amt;
1204 }
1205 return index;
1206}
1207
1208/// On Windows, this function currently does alter the file pointer.842/// On Windows, this function currently does alter the file pointer.
1209/// https://github.com/ziglang/zig/issues/12783843/// https://github.com/ziglang/zig/issues/12783
1210pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {844pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {
...@@ -1215,25 +849,10 @@ pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {...@@ -1215,25 +849,10 @@ pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {
1215 return posix.pread(self.handle, buffer, offset);849 return posix.pread(self.handle, buffer, offset);
1216}850}
1217851
1218/// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
1219/// means the file reached the end. Reaching the end of a file is not an error condition.
1220/// On Windows, this function currently does alter the file pointer.
1221/// https://github.com/ziglang/zig/issues/12783
1222pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!usize {
1223 var index: usize = 0;
1224 while (index != buffer.len) {
1225 const amt = try self.pread(buffer[index..], offset + index);
1226 if (amt == 0) break;
1227 index += amt;
1228 }
1229 return index;
1230}
1231
1232/// See https://github.com/ziglang/zig/issues/7699852/// See https://github.com/ziglang/zig/issues/7699
1233pub fn readv(self: File, iovecs: []const posix.iovec) ReadError!usize {853pub fn readv(self: File, iovecs: []const posix.iovec) ReadError!usize {
1234 if (is_windows) {854 if (is_windows) {
1235 // TODO improve this to use ReadFileScatter855 if (iovecs.len == 0) return 0;
1236 if (iovecs.len == 0) return @as(usize, 0);
1237 const first = iovecs[0];856 const first = iovecs[0];
1238 return windows.ReadFile(self.handle, first.base[0..first.len], null);857 return windows.ReadFile(self.handle, first.base[0..first.len], null);
1239 }858 }
...@@ -1241,55 +860,12 @@ pub fn readv(self: File, iovecs: []const posix.iovec) ReadError!usize {...@@ -1241,55 +860,12 @@ pub fn readv(self: File, iovecs: []const posix.iovec) ReadError!usize {
1241 return posix.readv(self.handle, iovecs);860 return posix.readv(self.handle, iovecs);
1242}861}
1243862
1244/// Returns the number of bytes read. If the number read is smaller than the total bytes
1245/// from all the buffers, it means the file reached the end. Reaching the end of a file
1246/// is not an error condition.
1247///
1248/// The `iovecs` parameter is mutable because:
1249/// * This function needs to mutate the fields in order to handle partial
1250/// reads from the underlying OS layer.
1251/// * The OS layer expects pointer addresses to be inside the application's address space
1252/// even if the length is zero. Meanwhile, in Zig, slices may have undefined pointer
1253/// addresses when the length is zero. So this function modifies the base fields
1254/// when the length is zero.
1255///
1256/// Related open issue: https://github.com/ziglang/zig/issues/7699
1257pub fn readvAll(self: File, iovecs: []posix.iovec) ReadError!usize {
1258 if (iovecs.len == 0) return 0;
1259
1260 // We use the address of this local variable for all zero-length
1261 // vectors so that the OS does not complain that we are giving it
1262 // addresses outside the application's address space.
1263 var garbage: [1]u8 = undefined;
1264 for (iovecs) |*v| {
1265 if (v.len == 0) v.base = &garbage;
1266 }
1267
1268 var i: usize = 0;
1269 var off: usize = 0;
1270 while (true) {
1271 var amt = try self.readv(iovecs[i..]);
1272 var eof = amt == 0;
1273 off += amt;
1274 while (amt >= iovecs[i].len) {
1275 amt -= iovecs[i].len;
1276 i += 1;
1277 if (i >= iovecs.len) return off;
1278 eof = false;
1279 }
1280 if (eof) return off;
1281 iovecs[i].base += amt;
1282 iovecs[i].len -= amt;
1283 }
1284}
1285
1286/// See https://github.com/ziglang/zig/issues/7699863/// See https://github.com/ziglang/zig/issues/7699
1287/// On Windows, this function currently does alter the file pointer.864/// On Windows, this function currently does alter the file pointer.
1288/// https://github.com/ziglang/zig/issues/12783865/// https://github.com/ziglang/zig/issues/12783
1289pub fn preadv(self: File, iovecs: []const posix.iovec, offset: u64) PReadError!usize {866pub fn preadv(self: File, iovecs: []const posix.iovec, offset: u64) PReadError!usize {
1290 if (is_windows) {867 if (is_windows) {
1291 // TODO improve this to use ReadFileScatter868 if (iovecs.len == 0) return 0;
1292 if (iovecs.len == 0) return @as(usize, 0);
1293 const first = iovecs[0];869 const first = iovecs[0];
1294 return windows.ReadFile(self.handle, first.base[0..first.len], offset);870 return windows.ReadFile(self.handle, first.base[0..first.len], offset);
1295 }871 }
...@@ -1297,35 +873,6 @@ pub fn preadv(self: File, iovecs: []const posix.iovec, offset: u64) PReadError!u...@@ -1297,35 +873,6 @@ pub fn preadv(self: File, iovecs: []const posix.iovec, offset: u64) PReadError!u
1297 return posix.preadv(self.handle, iovecs, offset);873 return posix.preadv(self.handle, iovecs, offset);
1298}874}
1299875
1300/// Returns the number of bytes read. If the number read is smaller than the total bytes
1301/// from all the buffers, it means the file reached the end. Reaching the end of a file
1302/// is not an error condition.
1303/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
1304/// order to handle partial reads from the underlying OS layer.
1305/// See https://github.com/ziglang/zig/issues/7699
1306/// On Windows, this function currently does alter the file pointer.
1307/// https://github.com/ziglang/zig/issues/12783
1308pub fn preadvAll(self: File, iovecs: []posix.iovec, offset: u64) PReadError!usize {
1309 if (iovecs.len == 0) return 0;
1310
1311 var i: usize = 0;
1312 var off: usize = 0;
1313 while (true) {
1314 var amt = try self.preadv(iovecs[i..], offset + off);
1315 var eof = amt == 0;
1316 off += amt;
1317 while (amt >= iovecs[i].len) {
1318 amt -= iovecs[i].len;
1319 i += 1;
1320 if (i >= iovecs.len) return off;
1321 eof = false;
1322 }
1323 if (eof) return off;
1324 iovecs[i].base += amt;
1325 iovecs[i].len -= amt;
1326 }
1327}
1328
1329pub const WriteError = posix.WriteError;876pub const WriteError = posix.WriteError;
1330pub const PWriteError = posix.PWriteError;877pub const PWriteError = posix.PWriteError;
1331878
...@@ -1337,6 +884,7 @@ pub fn write(self: File, bytes: []const u8) WriteError!usize {...@@ -1337,6 +884,7 @@ pub fn write(self: File, bytes: []const u8) WriteError!usize {
1337 return posix.write(self.handle, bytes);884 return posix.write(self.handle, bytes);
1338}885}
1339886
887/// One-shot alternative to `writer`.
1340pub fn writeAll(self: File, bytes: []const u8) WriteError!void {888pub fn writeAll(self: File, bytes: []const u8) WriteError!void {
1341 var index: usize = 0;889 var index: usize = 0;
1342 while (index < bytes.len) {890 while (index < bytes.len) {
...@@ -1354,21 +902,11 @@ pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize {...@@ -1354,21 +902,11 @@ pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize {
1354 return posix.pwrite(self.handle, bytes, offset);902 return posix.pwrite(self.handle, bytes, offset);
1355}903}
1356904
1357/// On Windows, this function currently does alter the file pointer.
1358/// https://github.com/ziglang/zig/issues/12783
1359pub fn pwriteAll(self: File, bytes: []const u8, offset: u64) PWriteError!void {
1360 var index: usize = 0;
1361 while (index < bytes.len) {
1362 index += try self.pwrite(bytes[index..], offset + index);
1363 }
1364}
1365
1366/// See https://github.com/ziglang/zig/issues/7699905/// See https://github.com/ziglang/zig/issues/7699
1367/// See equivalent function: `std.net.Stream.writev`.
1368pub fn writev(self: File, iovecs: []const posix.iovec_const) WriteError!usize {906pub fn writev(self: File, iovecs: []const posix.iovec_const) WriteError!usize {
1369 if (is_windows) {907 if (is_windows) {
1370 // TODO improve this to use WriteFileScatter908 // TODO improve this to use WriteFileScatter
1371 if (iovecs.len == 0) return @as(usize, 0);909 if (iovecs.len == 0) return 0;
1372 const first = iovecs[0];910 const first = iovecs[0];
1373 return windows.WriteFile(self.handle, first.base[0..first.len], null);911 return windows.WriteFile(self.handle, first.base[0..first.len], null);
1374 }912 }
...@@ -1376,46 +914,12 @@ pub fn writev(self: File, iovecs: []const posix.iovec_const) WriteError!usize {...@@ -1376,46 +914,12 @@ pub fn writev(self: File, iovecs: []const posix.iovec_const) WriteError!usize {
1376 return posix.writev(self.handle, iovecs);914 return posix.writev(self.handle, iovecs);
1377}915}
1378916
1379/// The `iovecs` parameter is mutable because:
1380/// * This function needs to mutate the fields in order to handle partial
1381/// writes from the underlying OS layer.
1382/// * The OS layer expects pointer addresses to be inside the application's address space
1383/// even if the length is zero. Meanwhile, in Zig, slices may have undefined pointer
1384/// addresses when the length is zero. So this function modifies the base fields
1385/// when the length is zero.
1386/// See https://github.com/ziglang/zig/issues/7699
1387/// See equivalent function: `std.net.Stream.writevAll`.
1388pub fn writevAll(self: File, iovecs: []posix.iovec_const) WriteError!void {
1389 if (iovecs.len == 0) return;
1390
1391 // We use the address of this local variable for all zero-length
1392 // vectors so that the OS does not complain that we are giving it
1393 // addresses outside the application's address space.
1394 var garbage: [1]u8 = undefined;
1395 for (iovecs) |*v| {
1396 if (v.len == 0) v.base = &garbage;
1397 }
1398
1399 var i: usize = 0;
1400 while (true) {
1401 var amt = try self.writev(iovecs[i..]);
1402 while (amt >= iovecs[i].len) {
1403 amt -= iovecs[i].len;
1404 i += 1;
1405 if (i >= iovecs.len) return;
1406 }
1407 iovecs[i].base += amt;
1408 iovecs[i].len -= amt;
1409 }
1410}
1411
1412/// See https://github.com/ziglang/zig/issues/7699917/// See https://github.com/ziglang/zig/issues/7699
1413/// On Windows, this function currently does alter the file pointer.918/// On Windows, this function currently does alter the file pointer.
1414/// https://github.com/ziglang/zig/issues/12783919/// https://github.com/ziglang/zig/issues/12783
1415pub fn pwritev(self: File, iovecs: []posix.iovec_const, offset: u64) PWriteError!usize {920pub fn pwritev(self: File, iovecs: []posix.iovec_const, offset: u64) PWriteError!usize {
1416 if (is_windows) {921 if (is_windows) {
1417 // TODO improve this to use WriteFileScatter922 if (iovecs.len == 0) return 0;
1418 if (iovecs.len == 0) return @as(usize, 0);
1419 const first = iovecs[0];923 const first = iovecs[0];
1420 return windows.WriteFile(self.handle, first.base[0..first.len], offset);924 return windows.WriteFile(self.handle, first.base[0..first.len], offset);
1421 }925 }
...@@ -1423,410 +927,426 @@ pub fn pwritev(self: File, iovecs: []posix.iovec_const, offset: u64) PWriteError...@@ -1423,410 +927,426 @@ pub fn pwritev(self: File, iovecs: []posix.iovec_const, offset: u64) PWriteError
1423 return posix.pwritev(self.handle, iovecs, offset);927 return posix.pwritev(self.handle, iovecs, offset);
1424}928}
1425929
1426/// The `iovecs` parameter is mutable because this function needs to mutate the fields in930pub const WriteFileError = PReadError || WriteError;
1427/// order to handle partial writes from the underlying OS layer.
1428/// See https://github.com/ziglang/zig/issues/7699
1429/// On Windows, this function currently does alter the file pointer.
1430/// https://github.com/ziglang/zig/issues/12783
1431pub fn pwritevAll(self: File, iovecs: []posix.iovec_const, offset: u64) PWriteError!void {
1432 if (iovecs.len == 0) return;
1433931
1434 var i: usize = 0;932pub fn writeFileAll(self: File, in_file: File, options: BufferedWriter.WriteFileOptions) WriteFileError!void {
1435 var off: u64 = 0;933 var file_writer = self.writer();
1436 while (true) {934 var bw = file_writer.interface().buffered(&.{});
1437 var amt = try self.pwritev(iovecs[i..], offset + off);935 bw.writeFileAll(in_file, options) catch |err| switch (err) {
1438 off += amt;936 error.WriteFailed => if (file_writer.err) |_| unreachable else |e| return e,
1439 while (amt >= iovecs[i].len) {937 else => |e| return e,
1440 amt -= iovecs[i].len;938 };
1441 i += 1;
1442 if (i >= iovecs.len) return;
1443 }
1444 iovecs[i].base += amt;
1445 iovecs[i].len -= amt;
1446 }
1447}939}
1448940
1449pub const CopyRangeError = posix.CopyFileRangeError;941pub const Reader = struct {
942 file: File,
943 err: ReadError!void = {},
944 mode: Reader.Mode = .positional,
945 pos: u64 = 0,
946 size: ?u64 = null,
947 size_err: GetEndPosError!void = {},
948 seek_err: SeekError!void = {},
1450949
1451pub fn copyRange(in: File, in_offset: u64, out: File, out_offset: u64, len: u64) CopyRangeError!u64 {950 pub const Mode = enum { streaming, positional };
1452 const adjusted_len = math.cast(usize, len) orelse maxInt(usize);
1453 const result = try posix.copy_file_range(in.handle, in_offset, out.handle, out_offset, adjusted_len, 0);
1454 return result;
1455}
1456951
1457/// Returns the number of bytes copied. If the number read is smaller than `buffer.len`, it952 pub fn interface(r: *Reader) std.io.Reader {
1458/// means the in file reached the end. Reaching the end of a file is not an error condition.953 return .{
1459pub fn copyRangeAll(in: File, in_offset: u64, out: File, out_offset: u64, len: u64) CopyRangeError!u64 {954 .context = r,
1460 var total_bytes_copied: u64 = 0;955 .vtable = &.{
1461 var in_off = in_offset;956 .read = Reader.read,
1462 var out_off = out_offset;957 .readVec = Reader.readVec,
1463 while (total_bytes_copied < len) {958 .discard = Reader.discard,
1464 const amt_copied = try copyRange(in, in_off, out, out_off, len - total_bytes_copied);959 },
1465 if (amt_copied == 0) return total_bytes_copied;960 };
1466 total_bytes_copied += amt_copied;
1467 in_off += amt_copied;
1468 out_off += amt_copied;
1469 }961 }
1470 return total_bytes_copied;
1471}
1472
1473pub const WriteFileOptions = struct {
1474 in_offset: u64 = 0,
1475
1476 /// `null` means the entire file. `0` means no bytes from the file.
1477 /// When this is `null`, trailers must be sent in a separate writev() call
1478 /// due to a flaw in the BSD sendfile API. Other operating systems, such as
1479 /// Linux, already do this anyway due to API limitations.
1480 /// If the size of the source file is known, passing the size here will save one syscall.
1481 in_len: ?u64 = null,
1482962
1483 headers_and_trailers: []posix.iovec_const = &[0]posix.iovec_const{},963 /// Number of slices to store on the stack, when trying to send as many byte
1484964 /// vectors through the underlying read calls as possible.
1485 /// The trailer count is inferred from `headers_and_trailers.len - header_count`965 const max_buffers_len = 16;
1486 header_count: usize = 0,966
1487};967 fn read(
1488968 context: ?*anyopaque,
1489pub const WriteFileError = ReadError || error{EndOfStream} || WriteError;969 bw: *BufferedWriter,
1490970 limit: std.io.Reader.Limit,
1491pub fn writeFileAll(self: File, in_file: File, args: WriteFileOptions) WriteFileError!void {971 ) std.io.Reader.RwError!usize {
1492 return self.writeFileAllSendfile(in_file, args) catch |err| switch (err) {972 const r: *Reader = @ptrCast(@alignCast(context));
1493 error.Unseekable,973 const file = r.file;
1494 error.FastOpenAlreadyInProgress,974 const pos = r.pos;
1495 error.MessageTooBig,975 switch (r.mode) {
1496 error.FileDescriptorNotASocket,976 .positional => {
1497 error.NetworkUnreachable,977 const size = r.size orelse {
1498 error.NetworkSubsystemFailed,978 if (r.file.getEndPos()) |size| {
1499 => return self.writeFileUnseekableAll(in_file, args),979 r.size = size;
980 } else |err| {
981 r.size_err = err;
982 r.mode = .streaming;
983 }
984 return 0;
985 };
986 const new_limit: std.io.Reader.Limit = .limited(limit.min(size - pos));
987 const n = bw.writeFile(file, .init(pos), new_limit, &.{}, 0) catch |err| switch (err) {
988 error.WriteFailed => return error.WriteFailed,
989 error.Unseekable => {
990 r.mode = .streaming;
991 assert(pos == 0);
992 return 0;
993 },
994 else => |e| {
995 r.err = e;
996 return error.ReadFailed;
997 },
998 };
999 r.pos = pos + n;
1000 return n;
1001 },
1002 .streaming => {
1003 const n = bw.writeFile(file, .none, limit, &.{}, 0) catch |err| switch (err) {
1004 error.WriteFailed => return error.WriteFailed,
1005 error.Unseekable => unreachable, // Passing `Offset.none`.
1006 else => |e| {
1007 r.err = e;
1008 return error.ReadFailed;
1009 },
1010 };
1011 r.pos = pos + n;
1012 return n;
1013 },
1014 }
1015 }
15001016
1501 else => |e| return e,1017 fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
1502 };1018 const r: *Reader = @ptrCast(@alignCast(context));
1503}1019 const handle = r.file.handle;
1020 const pos = r.pos;
1021
1022 switch (r.mode) {
1023 .positional => {
1024 if (is_windows) {
1025 // Unfortunately, `ReadFileScatter` cannot be used since it requires
1026 // page alignment, so we are stuck using only the first slice.
1027 // Avoid empty slices to prevent false positive end detections.
1028 var i: usize = 0;
1029 while (true) : (i += 1) {
1030 if (i >= data.len) return .{};
1031 if (data[i].len > 0) break;
1032 }
1033 const n = windows.ReadFile(handle, data[i], pos) catch |err| {
1034 r.err = err;
1035 return error.ReadFailed;
1036 };
1037 if (n == 0) return error.EndOfFile;
1038 r.pos = pos + n;
1039 return n;
1040 }
15041041
1505/// Does not try seeking in either of the File parameters.1042 var iovecs: [max_buffers_len]std.posix.iovec = undefined;
1506/// See `writeFileAll` as an alternative to calling this.1043 var iovecs_i: usize = 0;
1507pub fn writeFileUnseekableAll(out_file: File, in_file: File, args: WriteFileOptions) WriteFileError!void {1044 for (data) |d| {
1508 _ = out_file;1045 // Since the OS checks pointer address before length, we must omit
1509 _ = in_file;1046 // length-zero vectors.
1510 _ = args;1047 if (d.len == 0) continue;
1511 @panic("TODO call writeFileUnseekable multiple times");1048 iovecs[iovecs_i] = .{ .base = d.ptr, .len = d.len };
1512}1049 iovecs_i += 1;
1050 if (iovecs_i >= iovecs.len) break;
1051 }
1052 const send_vecs = iovecs[0..iovecs_i];
1053 if (send_vecs.len == 0) return 0; // Prevent false positive end detection on empty `data`.
1054 const n = posix.preadv(handle, send_vecs, pos) catch |err| switch (err) {
1055 error.Unseekable => {
1056 r.mode = .streaming;
1057 assert(pos == 0);
1058 return 0;
1059 },
1060 else => |e| {
1061 r.err = e;
1062 return error.ReadFailed;
1063 },
1064 };
1065 if (n == 0) return error.EndOfStream;
1066 r.pos = pos + n;
1067 return n;
1068 },
1069 .streaming => {
1070 if (is_windows) {
1071 // Unfortunately, `ReadFileScatter` cannot be used since it requires
1072 // page alignment, so we are stuck using only the first slice.
1073 // Avoid empty slices to prevent false positive end detections.
1074 var i: usize = 0;
1075 while (true) : (i += 1) {
1076 if (i >= data.len) return .{};
1077 if (data[i].len > 0) break;
1078 }
1079 const n = windows.ReadFile(handle, data[i], null) catch |err| {
1080 r.err = err;
1081 return error.ReadFailed;
1082 };
1083 if (n == 0) return error.EndOfFile;
1084 r.pos = pos + n;
1085 return n;
1086 }
15131087
1514/// Low level function which can fail for OS-specific reasons.1088 var iovecs: [max_buffers_len]std.posix.iovec = undefined;
1515/// See `writeFileAll` as an alternative to calling this.1089 var iovecs_i: usize = 0;
1516/// TODO integrate with async I/O1090 for (data) |d| {
1517fn writeFileAllSendfile(self: File, in_file: File, args: WriteFileOptions) posix.SendFileError!void {1091 // Since the OS checks pointer address before length, we must omit
1518 const count = blk: {1092 // length-zero vectors.
1519 if (args.in_len) |l| {1093 if (d.len == 0) continue;
1520 if (l == 0) {1094 iovecs[iovecs_i] = .{ .base = d.ptr, .len = d.len };
1521 return self.writevAll(args.headers_and_trailers);1095 iovecs_i += 1;
1522 } else {1096 if (iovecs_i >= iovecs.len) break;
1523 break :blk l;1097 }
1524 }1098 const send_vecs = iovecs[0..iovecs_i];
1525 } else {1099 if (send_vecs.len == 0) return 0; // Prevent false positive end detection on empty `data`.
1526 break :blk 0;1100 const n = posix.readv(handle, send_vecs) catch |err| {
1527 }1101 r.err = err;
1528 };1102 return error.ReadFailed;
1529 const headers = args.headers_and_trailers[0..args.header_count];1103 };
1530 const trailers = args.headers_and_trailers[args.header_count..];1104 if (n == 0) return error.EndOfStream;
1531 const zero_iovec = &[0]posix.iovec_const{};1105 r.pos = pos + n;
1532 // When reading the whole file, we cannot put the trailers in the sendfile() syscall,1106 return n;
1533 // because we have no way to determine whether a partial write is past the end of the file or not.1107 },
1534 const trls = if (count == 0) zero_iovec else trailers;
1535 const offset = args.in_offset;
1536 const out_fd = self.handle;
1537 const in_fd = in_file.handle;
1538 const flags = 0;
1539 var amt: usize = 0;
1540 hdrs: {
1541 var i: usize = 0;
1542 while (i < headers.len) {
1543 amt = try posix.sendfile(out_fd, in_fd, offset, count, headers[i..], trls, flags);
1544 while (amt >= headers[i].len) {
1545 amt -= headers[i].len;
1546 i += 1;
1547 if (i >= headers.len) break :hdrs;
1548 }
1549 headers[i].base += amt;
1550 headers[i].len -= amt;
1551 }
1552 }
1553 if (count == 0) {
1554 var off: u64 = amt;
1555 while (true) {
1556 amt = try posix.sendfile(out_fd, in_fd, offset + off, 0, zero_iovec, zero_iovec, flags);
1557 if (amt == 0) break;
1558 off += amt;
1559 }
1560 } else {
1561 var off: u64 = amt;
1562 while (off < count) {
1563 amt = try posix.sendfile(out_fd, in_fd, offset + off, count - off, zero_iovec, trailers, flags);
1564 off += amt;
1565 }1108 }
1566 amt = @as(usize, @intCast(off - count));
1567 }1109 }
1568 var i: usize = 0;1110
1569 while (i < trailers.len) {1111 fn discard(context: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize {
1570 while (amt >= trailers[i].len) {1112 const r: *Reader = @ptrCast(@alignCast(context));
1571 amt -= trailers[i].len;1113 const file = r.file;
1572 i += 1;1114 const pos = r.pos;
1573 if (i >= trailers.len) return;1115 switch (r.mode) {
1116 .positional => {
1117 const size = r.size orelse {
1118 if (file.getEndPos()) |size| {
1119 r.size = size;
1120 } else |err| {
1121 r.size_err = err;
1122 r.mode = .streaming;
1123 }
1124 return 0;
1125 };
1126 const delta = @min(@intFromEnum(limit), size - pos);
1127 r.pos = pos + delta;
1128 return delta;
1129 },
1130 .streaming => {
1131 // Unfortunately we can't seek forward without knowing the
1132 // size because the seek syscalls provided to us will not
1133 // return the true end position if a seek would exceed the
1134 // end.
1135 fallback: {
1136 if (r.size_err) |_| {
1137 if (r.seek_err) |_| {
1138 break :fallback;
1139 } else |_| {}
1140 } else |_| {}
1141 var trash_buffer: [std.atomic.cache_line]u8 = undefined;
1142 const trash = &trash_buffer;
1143 if (is_windows) {
1144 const n = windows.ReadFile(file.handle, trash, null) catch |err| {
1145 r.err = err;
1146 return error.ReadFailed;
1147 };
1148 r.pos = pos + n;
1149 return n;
1150 }
1151 var iovecs: [max_buffers_len]std.posix.iovec = undefined;
1152 var iovecs_i: usize = 0;
1153 var remaining = @intFromEnum(limit);
1154 while (remaining > 0 and iovecs_i >= iovecs.len) {
1155 iovecs[iovecs_i] = .{ .base = trash, .len = @min(trash.len, remaining) };
1156 remaining -= iovecs[iovecs_i].len;
1157 iovecs_i += 1;
1158 }
1159 const n = posix.readv(file.handle, iovecs[0..iovecs_i]) catch |err| {
1160 r.err = err;
1161 return error.ReadFailed;
1162 };
1163 r.pos = pos + n;
1164 return n;
1165 }
1166 const size = r.size orelse {
1167 if (file.getEndPos()) |size| {
1168 r.size = size;
1169 } else |err| {
1170 r.size_err = err;
1171 }
1172 return 0;
1173 };
1174 const n = @min(size - pos, std.math.maxInt(i64), @intFromEnum(limit));
1175 file.seekBy(n) catch |err| {
1176 r.seek_err = err;
1177 return 0;
1178 };
1179 r.pos = pos + n;
1180 return n;
1181 },
1574 }1182 }
1575 trailers[i].base += amt;
1576 trailers[i].len -= amt;
1577 amt = try posix.writev(self.handle, trailers[i..]);
1578 }1183 }
1579}1184};
1580
1581pub fn reader(file: File) std.io.Reader {
1582 return .{
1583 .context = handleToOpaque(file.handle),
1584 .vtable = &.{
1585 .read = streamRead,
1586 .readv = streamReadVec,
1587 },
1588 };
1589}
1590
1591pub fn positionalReader(file: File) std.io.PositionalReader {
1592 return .{
1593 .context = handleToOpaque(file.handle),
1594 .vtable = &.{
1595 .read = posRead,
1596 .readv = posReadVec,
1597 },
1598 };
1599}
1600
1601pub fn writer(file: File) std.io.Writer {
1602 return .{
1603 .context = handleToOpaque(file.handle),
1604 .vtable = &.{
1605 .writeSplat = writeSplat,
1606 .writeFile = writeFile,
1607 },
1608 };
1609}
1610
1611/// Number of slices to store on the stack, when trying to send as many byte
1612/// vectors through the underlying write calls as possible.
1613const max_buffers_len = 16;
1614
1615fn posRead(
1616 context: ?*anyopaque,
1617 bw: *std.io.BufferedWriter,
1618 limit: std.io.Reader.Limit,
1619 offset: u64,
1620) std.io.Reader.Result {
1621 const file = opaqueToFile(context);
1622 return bw.writeFile(file, .init(offset), limit, &.{}, 0);
1623}
1624
1625fn posReadVec(context: *anyopaque, data: []const []u8, offset: u64) anyerror!std.io.Reader.Status {
1626 const file = opaqueToFile(context);
1627 const n = try file.preadv(data, offset);
1628 return .{
1629 .len = n,
1630 .end = n == 0,
1631 };
1632}
16331185
1634pub fn streamRead(1186pub const Writer = struct {
1635 context: ?*anyopaque,1187 file: File,
1636 bw: *std.io.BufferedWriter,1188 err: WriteError!void = {},
1637 limit: std.io.Reader.Limit,1189 mode: Writer.Mode = .positional,
1638) anyerror!std.io.Reader.Status {1190 pos: u64 = 0,
1639 const file = opaqueToFile(context);
1640 const n = try bw.writeFile(file, .none, limit, &.{}, 0);
1641 return .{
1642 .len = @intCast(n),
1643 .end = n == 0,
1644 };
1645}
16461191
1647pub fn streamReadVec(context: ?*anyopaque, data: []const []u8) anyerror!std.io.Reader.Status {1192 pub const Mode = Reader.Mode;
1648 const handle = opaqueToHandle(context);
16491193
1650 if (is_windows) {1194 /// Number of slices to store on the stack, when trying to send as many byte
1651 // Unfortunately, `ReadFileScatter` cannot be used since it requires1195 /// vectors through the underlying write calls as possible.
1652 // page alignment, so we are stuck using only the first slice.1196 const max_buffers_len = 16;
1653 // Avoid empty slices to prevent false positive end detections.
1654 var i: usize = 0;
1655 while (true) : (i += 1) {
1656 if (i >= data.len) return .{};
1657 if (data[i].len > 0) break;
1658 }
1659 const n = try windows.ReadFile(handle, data[i], null);
1660 return .{ .len = n, .end = n == 0 };
1661 }
16621197
1663 var iovecs: [max_buffers_len]std.posix.iovec = undefined;1198 pub fn interface(w: *Writer) std.io.Writer {
1664 var iovecs_i: usize = 0;1199 return .{
1665 for (data) |d| {1200 .context = w,
1666 // Since the OS checks pointer address before length, we must omit1201 .vtable = &.{
1667 // length-zero vectors.1202 .writeSplat = writeSplat,
1668 if (d.len == 0) continue;1203 .writeFile = writeFile,
1669 iovecs[iovecs_i] = .{ .base = d.ptr, .len = d.len };1204 },
1670 iovecs_i += 1;1205 };
1671 if (iovecs_i >= iovecs.len) break;
1672 }1206 }
1673 const send_vecs = iovecs[0..iovecs_i];
1674 if (send_vecs.len == 0) return .{}; // Prevent false positive end detection on empty `data`.
1675 const n = try posix.readv(handle, send_vecs);
1676 return .{ .len = @intCast(n), .end = n == 0 };
1677}
16781207
1679pub fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize {1208 pub fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
1680 const handle = opaqueToHandle(context);1209 const w: *Writer = @ptrCast(@alignCast(context));
1681 var splat_buffer: [256]u8 = undefined;1210 const handle = w.file.handle;
1682 if (is_windows) {1211 var splat_buffer: [256]u8 = undefined;
1683 if (data.len == 1 and splat == 0) return 0;1212 if (is_windows) {
1684 return windows.WriteFile(handle, data[0], null);1213 if (data.len == 1 and splat == 0) return 0;
1685 }1214 return windows.WriteFile(handle, data[0], null);
1686 var iovecs: [max_buffers_len]std.posix.iovec_const = undefined;1215 }
1687 var len: usize = @min(iovecs.len, data.len);1216 var iovecs: [max_buffers_len]std.posix.iovec_const = undefined;
1688 for (iovecs[0..len], data[0..len]) |*v, d| v.* = .{1217 var len: usize = @min(iovecs.len, data.len);
1689 .base = if (d.len == 0) "" else d.ptr, // OS sadly checks ptr addr before length.1218 for (iovecs[0..len], data[0..len]) |*v, d| v.* = .{
1690 .len = d.len,1219 .base = if (d.len == 0) "" else d.ptr, // OS sadly checks ptr addr before length.
1691 };1220 .len = d.len,
1692 switch (splat) {1221 };
1693 0 => return std.posix.writev(handle, iovecs[0 .. len - 1]),1222 switch (splat) {
1694 1 => return std.posix.writev(handle, iovecs[0..len]),1223 0 => return std.posix.writev(handle, iovecs[0 .. len - 1]) catch |err| {
1695 else => {1224 w.err = err;
1696 const pattern = data[data.len - 1];1225 return error.WriteFailed;
1697 if (pattern.len == 1) {1226 },
1698 const memset_len = @min(splat_buffer.len, splat);1227 1 => return std.posix.writev(handle, iovecs[0..len]) catch |err| {
1699 const buf = splat_buffer[0..memset_len];1228 w.err = err;
1700 @memset(buf, pattern[0]);1229 return error.WriteFailed;
1701 iovecs[len - 1] = .{ .base = buf.ptr, .len = buf.len };1230 },
1702 var remaining_splat = splat - buf.len;1231 else => {
1703 while (remaining_splat > splat_buffer.len and len < iovecs.len) {1232 const pattern = data[data.len - 1];
1704 iovecs[len] = .{ .base = &splat_buffer, .len = splat_buffer.len };1233 if (pattern.len == 1) {
1705 remaining_splat -= splat_buffer.len;1234 const memset_len = @min(splat_buffer.len, splat);
1706 len += 1;1235 const buf = splat_buffer[0..memset_len];
1707 }1236 @memset(buf, pattern[0]);
1708 if (remaining_splat > 0 and len < iovecs.len) {1237 iovecs[len - 1] = .{ .base = buf.ptr, .len = buf.len };
1709 iovecs[len] = .{ .base = &splat_buffer, .len = remaining_splat };1238 var remaining_splat = splat - buf.len;
1710 len += 1;1239 while (remaining_splat > splat_buffer.len and len < iovecs.len) {
1240 iovecs[len] = .{ .base = &splat_buffer, .len = splat_buffer.len };
1241 remaining_splat -= splat_buffer.len;
1242 len += 1;
1243 }
1244 if (remaining_splat > 0 and len < iovecs.len) {
1245 iovecs[len] = .{ .base = &splat_buffer, .len = remaining_splat };
1246 len += 1;
1247 }
1248 return std.posix.writev(handle, iovecs[0..len]) catch |err| {
1249 w.err = err;
1250 return error.WriteFailed;
1251 };
1711 }1252 }
1712 return std.posix.writev(handle, iovecs[0..len]);1253 },
1713 }1254 }
1714 },1255 return std.posix.writev(handle, iovecs[0..len]) catch |err| {
1256 w.err = err;
1257 return error.WriteFailed;
1258 };
1715 }1259 }
1716 return std.posix.writev(handle, iovecs[0..len]);
1717}
17181260
1719pub fn writeFile(1261 pub fn writeFile(
1720 context: ?*anyopaque,1262 context: ?*anyopaque,
1721 in_file: std.fs.File,1263 in_file: std.fs.File,
1722 in_offset: std.io.Writer.Offset,1264 in_offset: std.io.Writer.Offset,
1723 in_limit: std.io.Writer.Limit,1265 in_limit: std.io.Writer.Limit,
1724 headers_and_trailers: []const []const u8,1266 headers_and_trailers: []const []const u8,
1725 headers_len: usize,1267 headers_len: usize,
1726) anyerror!usize {1268 ) std.io.Writer.FileError!usize {
1727 const out_fd = opaqueToHandle(context);1269 const w: *Writer = @ptrCast(@alignCast(context));
1728 const in_fd = in_file.handle;1270 const out_fd = w.file.handle;
1729 const len_int = switch (in_limit) {1271 const in_fd = in_file.handle;
1730 .nothing => return writeSplat(context, headers_and_trailers, 1),1272 const len_int = switch (in_limit) {
1731 .unlimited => 0,1273 .nothing => return writeSplat(context, headers_and_trailers, 1),
1732 else => in_limit.toInt().?,1274 .unlimited => 0,
1733 };1275 else => in_limit.toInt().?,
1734 if (native_os == .linux) sf: {1276 };
1735 // Linux sendfile does not support headers or trailers but it does1277 // TODO try using copy_file_range on linux
1736 // support a streaming read from in_file.1278 // TODO try using copy_file_range on freebsd
1737 if (headers_len > 0) return writeSplat(context, headers_and_trailers[0..headers_len], 1);1279 if (native_os == .linux) sf: {
1738 const max_count = 0x7ffff000; // Avoid EINVAL.1280 // Linux sendfile does not support headers or trailers but it does
1739 const smaller_len = if (len_int == 0) max_count else @min(len_int, max_count);1281 // support a streaming read from in_file.
1740 var off: std.os.linux.off_t = undefined;1282 if (headers_len > 0) return writeSplat(context, headers_and_trailers[0..headers_len], 1);
1741 const off_ptr: ?*std.os.linux.off_t = if (in_offset.toInt()) |offset| b: {1283 const max_count = 0x7ffff000; // Avoid EINVAL.
1742 off = std.math.cast(std.os.linux.off_t, offset) orelse1284 const smaller_len = if (len_int == 0) max_count else @min(len_int, max_count);
1285 var off: std.os.linux.off_t = undefined;
1286 const off_ptr: ?*std.os.linux.off_t = if (in_offset.toInt()) |offset| b: {
1287 off = std.math.cast(std.os.linux.off_t, offset) orelse
1288 return writeSplat(context, headers_and_trailers, 1);
1289 break :b &off;
1290 } else null;
1291 if (true) @panic("TODO");
1292 const n = std.os.linux.wrapped.sendfile(out_fd, in_fd, off_ptr, smaller_len) catch |err| switch (err) {
1293 error.UnsupportedOperation => break :sf,
1294 error.Unseekable => break :sf,
1295 error.Unexpected => break :sf,
1296 else => |e| return e,
1297 };
1298 if (in_offset.toInt()) |offset| {
1299 assert(n == off - offset);
1300 } else if (n == 0 and len_int == 0) {
1301 // The caller wouldn't be able to tell that the file transfer is
1302 // done and would incorrectly repeat the same call.
1743 return writeSplat(context, headers_and_trailers, 1);1303 return writeSplat(context, headers_and_trailers, 1);
1744 break :b &off;1304 }
1745 } else null;1305 return n;
1746 if (true) @panic("TODO");1306 }
1747 const n = std.os.linux.wrapped.sendfile(out_fd, in_fd, off_ptr, smaller_len) catch |err| switch (err) {1307 var iovecs_buffer: [max_buffers_len]std.posix.iovec_const = undefined;
1748 error.UnsupportedOperation => break :sf,1308 const iovecs = iovecs_buffer[0..@min(iovecs_buffer.len, headers_and_trailers.len)];
1749 error.Unseekable => break :sf,1309 for (iovecs, headers_and_trailers[0..iovecs.len]) |*v, d| v.* = .{ .base = d.ptr, .len = d.len };
1750 error.Unexpected => break :sf,1310 const headers = iovecs[0..@min(headers_len, iovecs.len)];
1311 const trailers = iovecs[headers.len..];
1312 const flags = 0;
1313 return posix.sendfile(out_fd, in_fd, in_offset, len_int, headers, trailers, flags) catch |err| switch (err) {
1314 error.Unseekable,
1315 error.FastOpenAlreadyInProgress,
1316 error.MessageTooBig,
1317 error.FileDescriptorNotASocket,
1318 error.NetworkUnreachable,
1319 error.NetworkSubsystemFailed,
1320 => return writeFileUnseekable(out_fd, in_fd, in_offset, in_limit, headers_and_trailers, headers_len),
1321
1751 else => |e| return e,1322 else => |e| return e,
1752 };1323 };
1753 if (in_offset.toInt()) |offset| {
1754 assert(n == off - offset);
1755 } else if (n == 0 and len_int == 0) {
1756 // The caller wouldn't be able to tell that the file transfer is
1757 // done and would incorrectly repeat the same call.
1758 return writeSplat(context, headers_and_trailers, 1);
1759 }
1760 return n;
1761 }1324 }
1762 var iovecs_buffer: [max_buffers_len]std.posix.iovec_const = undefined;
1763 const iovecs = iovecs_buffer[0..@min(iovecs_buffer.len, headers_and_trailers.len)];
1764 for (iovecs, headers_and_trailers[0..iovecs.len]) |*v, d| v.* = .{ .base = d.ptr, .len = d.len };
1765 const headers = iovecs[0..@min(headers_len, iovecs.len)];
1766 const trailers = iovecs[headers.len..];
1767 const flags = 0;
1768 return posix.sendfile(out_fd, in_fd, in_offset, len_int, headers, trailers, flags) catch |err| switch (err) {
1769 error.Unseekable,
1770 error.FastOpenAlreadyInProgress,
1771 error.MessageTooBig,
1772 error.FileDescriptorNotASocket,
1773 error.NetworkUnreachable,
1774 error.NetworkSubsystemFailed,
1775 => return writeFileUnseekable(out_fd, in_fd, in_offset, in_limit, headers_and_trailers, headers_len),
1776
1777 else => |e| return e,
1778 };
1779}
17801325
1781fn writeFileUnseekable(1326 fn writeFileUnseekable(
1782 out_fd: Handle,1327 out_fd: Handle,
1783 in_fd: Handle,1328 in_fd: Handle,
1784 in_offset: u64,1329 in_offset: u64,
1785 in_limit: std.io.Writer.Limit,1330 in_limit: std.io.Writer.Limit,
1786 headers_and_trailers: []const []const u8,1331 headers_and_trailers: []const []const u8,
1787 headers_len: usize,1332 headers_len: usize,
1788) anyerror!usize {1333 ) std.io.Writer.FileError!usize {
1789 _ = out_fd;1334 _ = out_fd;
1790 _ = in_fd;1335 _ = in_fd;
1791 _ = in_offset;1336 _ = in_offset;
1792 _ = in_limit;1337 _ = in_limit;
1793 _ = headers_and_trailers;1338 _ = headers_and_trailers;
1794 _ = headers_len;1339 _ = headers_len;
1795 @panic("TODO writeFileUnseekable");1340 @panic("TODO writeFileUnseekable");
1796}1341 }
17971342};
1798fn handleToOpaque(handle: Handle) ?*anyopaque {
1799 return switch (@typeInfo(Handle)) {
1800 .pointer => @ptrCast(handle),
1801 .int => @ptrFromInt(@as(u32, @bitCast(handle))),
1802 else => @compileError("unhandled"),
1803 };
1804}
1805
1806fn opaqueToHandle(userdata: ?*anyopaque) Handle {
1807 return switch (@typeInfo(Handle)) {
1808 .pointer => @ptrCast(userdata),
1809 .int => @intCast(@intFromPtr(userdata)),
1810 else => @compileError("unhandled"),
1811 };
1812}
18131343
1814fn opaqueToFile(userdata: ?*anyopaque) File {1344pub fn reader(file: File) Reader {
1815 return .{ .handle = opaqueToHandle(userdata) };1345 return .{ .file = file };
1816}1346}
18171347
1818pub const SeekableStream = io.SeekableStream(1348pub fn writer(file: File) Writer {
1819 File,1349 return .{ .file = file };
1820 SeekError,
1821 GetSeekPosError,
1822 seekTo,
1823 seekBy,
1824 getPos,
1825 getEndPos,
1826);
1827
1828pub fn seekableStream(file: File) SeekableStream {
1829 return .{ .context = file };
1830}1350}
18311351
1832const range_off: windows.LARGE_INTEGER = 0;1352const range_off: windows.LARGE_INTEGER = 0;
...@@ -2008,3 +1528,4 @@ const linux = std.os.linux;...@@ -2008,3 +1528,4 @@ const linux = std.os.linux;
2008const windows = std.os.windows;1528const windows = std.os.windows;
2009const maxInt = std.math.maxInt;1529const maxInt = std.math.maxInt;
2010const Alignment = std.mem.Alignment;1530const Alignment = std.mem.Alignment;
1531const BufferedWriter = std.io.BufferedWriter;
lib/std/fs/test.zig-107
...@@ -1953,113 +1953,6 @@ test "chown" {...@@ -1953,113 +1953,6 @@ test "chown" {
1953 try dir.chown(null, null);1953 try dir.chown(null, null);
1954}1954}
19551955
1956test "File.Metadata" {
1957 var tmp = tmpDir(.{});
1958 defer tmp.cleanup();
1959
1960 const file = try tmp.dir.createFile("test_file", .{ .read = true });
1961 defer file.close();
1962
1963 const metadata = try file.metadata();
1964 try testing.expectEqual(File.Kind.file, metadata.kind());
1965 try testing.expectEqual(@as(u64, 0), metadata.size());
1966 _ = metadata.accessed();
1967 _ = metadata.modified();
1968 _ = metadata.created();
1969}
1970
1971test "File.Permissions" {
1972 if (native_os == .wasi)
1973 return error.SkipZigTest;
1974
1975 var tmp = tmpDir(.{});
1976 defer tmp.cleanup();
1977
1978 const file = try tmp.dir.createFile("test_file", .{ .read = true });
1979 defer file.close();
1980
1981 const metadata = try file.metadata();
1982 var permissions = metadata.permissions();
1983
1984 try testing.expect(!permissions.readOnly());
1985 permissions.setReadOnly(true);
1986 try testing.expect(permissions.readOnly());
1987
1988 try file.setPermissions(permissions);
1989 const new_permissions = (try file.metadata()).permissions();
1990 try testing.expect(new_permissions.readOnly());
1991
1992 // Must be set to non-read-only to delete
1993 permissions.setReadOnly(false);
1994 try file.setPermissions(permissions);
1995}
1996
1997test "File.PermissionsUnix" {
1998 if (native_os == .windows or native_os == .wasi)
1999 return error.SkipZigTest;
2000
2001 var tmp = tmpDir(.{});
2002 defer tmp.cleanup();
2003
2004 const file = try tmp.dir.createFile("test_file", .{ .mode = 0o666, .read = true });
2005 defer file.close();
2006
2007 const metadata = try file.metadata();
2008 var permissions = metadata.permissions();
2009
2010 permissions.setReadOnly(true);
2011 try testing.expect(permissions.readOnly());
2012 try testing.expect(!permissions.inner.unixHas(.user, .write));
2013 permissions.inner.unixSet(.user, .{ .write = true });
2014 try testing.expect(!permissions.readOnly());
2015 try testing.expect(permissions.inner.unixHas(.user, .write));
2016 try testing.expect(permissions.inner.mode & 0o400 != 0);
2017
2018 permissions.setReadOnly(true);
2019 try file.setPermissions(permissions);
2020 permissions = (try file.metadata()).permissions();
2021 try testing.expect(permissions.readOnly());
2022
2023 // Must be set to non-read-only to delete
2024 permissions.setReadOnly(false);
2025 try file.setPermissions(permissions);
2026
2027 const permissions_unix = File.PermissionsUnix.unixNew(0o754);
2028 try testing.expect(permissions_unix.unixHas(.user, .execute));
2029 try testing.expect(!permissions_unix.unixHas(.other, .execute));
2030}
2031
2032test "delete a read-only file on windows" {
2033 if (native_os != .windows)
2034 return error.SkipZigTest;
2035
2036 var tmp = testing.tmpDir(.{});
2037 defer tmp.cleanup();
2038
2039 const file = try tmp.dir.createFile("test_file", .{ .read = true });
2040 defer file.close();
2041 // Create a file and make it read-only
2042 const metadata = try file.metadata();
2043 var permissions = metadata.permissions();
2044 permissions.setReadOnly(true);
2045 try file.setPermissions(permissions);
2046
2047 // If the OS and filesystem support it, POSIX_SEMANTICS and IGNORE_READONLY_ATTRIBUTE
2048 // is used meaning that the deletion of a read-only file will succeed.
2049 // Otherwise, this delete will fail and the read-only flag must be unset before it's
2050 // able to be deleted.
2051 const delete_result = tmp.dir.deleteFile("test_file");
2052 if (delete_result) {
2053 try testing.expectError(error.FileNotFound, tmp.dir.deleteFile("test_file"));
2054 } else |err| {
2055 try testing.expectEqual(@as(anyerror, error.AccessDenied), err);
2056 // Now make the file not read-only
2057 permissions.setReadOnly(false);
2058 try file.setPermissions(permissions);
2059 try tmp.dir.deleteFile("test_file");
2060 }
2061}
2062
2063test "delete a setAsCwd directory on Windows" {1956test "delete a setAsCwd directory on Windows" {
2064 if (native_os != .windows) return error.SkipZigTest;1957 if (native_os != .windows) return error.SkipZigTest;
20651958
lib/std/http/Client.zig+11-11
...@@ -386,7 +386,7 @@ pub const Connection = struct {...@@ -386,7 +386,7 @@ pub const Connection = struct {
386 }386 }
387 }387 }
388388
389 pub fn flush(c: *Connection) anyerror!void {389 pub fn flush(c: *Connection) std.io.Writer.Error!void {
390 try c.writer.flush();390 try c.writer.flush();
391 if (c.protocol == .tls) {391 if (c.protocol == .tls) {
392 if (disable_tls) unreachable;392 if (disable_tls) unreachable;
...@@ -398,7 +398,7 @@ pub const Connection = struct {...@@ -398,7 +398,7 @@ pub const Connection = struct {
398 /// If the connection is a TLS connection, sends the close_notify alert.398 /// If the connection is a TLS connection, sends the close_notify alert.
399 ///399 ///
400 /// Flushes all buffers.400 /// Flushes all buffers.
401 pub fn end(c: *Connection) anyerror!void {401 pub fn end(c: *Connection) std.io.Writer.Error!void {
402 try c.writer.flush();402 try c.writer.flush();
403 if (c.protocol == .tls) {403 if (c.protocol == .tls) {
404 if (disable_tls) unreachable;404 if (disable_tls) unreachable;
...@@ -826,7 +826,7 @@ pub const Request = struct {...@@ -826,7 +826,7 @@ pub const Request = struct {
826 }826 }
827827
828 /// Send the HTTP request headers to the server.828 /// Send the HTTP request headers to the server.
829 pub fn send(req: *Request) anyerror!void {829 pub fn send(req: *Request) std.io.Writer.Error!void {
830 assert(req.transfer_encoding == .none or req.method.requestHasBody());830 assert(req.transfer_encoding == .none or req.method.requestHasBody());
831831
832 const connection = req.connection.?;832 const connection = req.connection.?;
...@@ -959,7 +959,7 @@ pub const Request = struct {...@@ -959,7 +959,7 @@ pub const Request = struct {
959959
960 /// TODO collapse each error set into its own meta error code, and store960 /// TODO collapse each error set into its own meta error code, and store
961 /// the underlying error code as a field on Request961 /// the underlying error code as a field on Request
962 pub const WaitError = RequestError || anyerror || TransferReadError ||962 pub const WaitError = RequestError || std.io.Writer.Error || TransferReadError ||
963 proto.HeadersParser.CheckCompleteHeadError || Response.ParseError ||963 proto.HeadersParser.CheckCompleteHeadError || Response.ParseError ||
964 error{964 error{
965 TooManyHttpRedirects,965 TooManyHttpRedirects,
...@@ -1156,7 +1156,7 @@ pub const Request = struct {...@@ -1156,7 +1156,7 @@ pub const Request = struct {
1156 };1156 };
1157 }1157 }
11581158
1159 fn chunked_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {1159 fn chunked_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
1160 const req: *Request = @ptrCast(@alignCast(context));1160 const req: *Request = @ptrCast(@alignCast(context));
1161 var total: usize = 0;1161 var total: usize = 0;
1162 for (data) |bytes| total += bytes.len;1162 for (data) |bytes| total += bytes.len;
...@@ -1187,7 +1187,7 @@ pub const Request = struct {...@@ -1187,7 +1187,7 @@ pub const Request = struct {
1187 len: std.io.Writer.FileLen,1187 len: std.io.Writer.FileLen,
1188 headers_and_trailers: []const []const u8,1188 headers_and_trailers: []const []const u8,
1189 headers_len: usize,1189 headers_len: usize,
1190 ) anyerror!usize {1190 ) std.io.Writer.Error!usize {
1191 if (len == .entire_file) return error.Unimplemented;1191 if (len == .entire_file) return error.Unimplemented;
1192 const req: *Request = @ptrCast(@alignCast(context));1192 const req: *Request = @ptrCast(@alignCast(context));
1193 var total: usize = len.int();1193 var total: usize = len.int();
...@@ -1213,7 +1213,7 @@ pub const Request = struct {...@@ -1213,7 +1213,7 @@ pub const Request = struct {
1213 return total;1213 return total;
1214 }1214 }
12151215
1216 fn cl_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {1216 fn cl_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
1217 const req: *Request = @ptrCast(@alignCast(context));1217 const req: *Request = @ptrCast(@alignCast(context));
1218 const n = try req.connection.?.writer.writeSplat(data, splat);1218 const n = try req.connection.?.writer.writeSplat(data, splat);
1219 req.transfer_encoding.content_length -= n;1219 req.transfer_encoding.content_length -= n;
...@@ -1227,7 +1227,7 @@ pub const Request = struct {...@@ -1227,7 +1227,7 @@ pub const Request = struct {
1227 len: std.io.Writer.FileLen,1227 len: std.io.Writer.FileLen,
1228 headers_and_trailers: []const []const u8,1228 headers_and_trailers: []const []const u8,
1229 headers_len: usize,1229 headers_len: usize,
1230 ) anyerror!usize {1230 ) std.io.Writer.Error!usize {
1231 const req: *Request = @ptrCast(@alignCast(context));1231 const req: *Request = @ptrCast(@alignCast(context));
1232 const n = try req.connection.?.writer.writeFile(file, offset, len, headers_and_trailers, headers_len);1232 const n = try req.connection.?.writer.writeFile(file, offset, len, headers_and_trailers, headers_len);
1233 req.transfer_encoding.content_length -= n;1233 req.transfer_encoding.content_length -= n;
...@@ -1236,7 +1236,7 @@ pub const Request = struct {...@@ -1236,7 +1236,7 @@ pub const Request = struct {
12361236
1237 /// Finish the body of a request. This notifies the server that you have no more data to send.1237 /// Finish the body of a request. This notifies the server that you have no more data to send.
1238 /// Must be called after `send`.1238 /// Must be called after `send`.
1239 pub fn finish(req: *Request) anyerror!void {1239 pub fn finish(req: *Request) std.io.Writer.Error!void {
1240 switch (req.transfer_encoding) {1240 switch (req.transfer_encoding) {
1241 .chunked => try req.connection.?.writer.writeAll("0\r\n\r\n"),1241 .chunked => try req.connection.?.writer.writeAll("0\r\n\r\n"),
1242 .content_length => |len| assert(len == 0),1242 .content_length => |len| assert(len == 0),
...@@ -1353,7 +1353,7 @@ pub const basic_authorization = struct {...@@ -1353,7 +1353,7 @@ pub const basic_authorization = struct {
1353 return bw.getWritten();1353 return bw.getWritten();
1354 }1354 }
13551355
1356 pub fn write(uri: Uri, out: *std.io.BufferedWriter) anyerror!void {1356 pub fn write(uri: Uri, out: *std.io.BufferedWriter) std.io.Writer.Error!void {
1357 var buf: [max_user_len + ":".len + max_password_len]u8 = undefined;1357 var buf: [max_user_len + ":".len + max_password_len]u8 = undefined;
1358 var bw: std.io.BufferedWriter = undefined;1358 var bw: std.io.BufferedWriter = undefined;
1359 bw.initFixed(&buf);1359 bw.initFixed(&buf);
...@@ -1574,7 +1574,7 @@ pub fn connect(...@@ -1574,7 +1574,7 @@ pub fn connect(
15741574
1575/// TODO collapse each error set into its own meta error code, and store1575/// TODO collapse each error set into its own meta error code, and store
1576/// the underlying error code as a field on Request1576/// the underlying error code as a field on Request
1577pub const RequestError = ConnectTcpError || ConnectErrorPartial || anyerror ||1577pub const RequestError = ConnectTcpError || ConnectErrorPartial || std.io.Writer.Error ||
1578 std.fmt.ParseIntError || Connection.WriteError ||1578 std.fmt.ParseIntError || Connection.WriteError ||
1579 error{1579 error{
1580 UnsupportedUriScheme,1580 UnsupportedUriScheme,
lib/std/http/Server.zig+34-40
...@@ -19,7 +19,7 @@ out: *std.io.BufferedWriter,...@@ -19,7 +19,7 @@ out: *std.io.BufferedWriter,
19/// same connection, and makes invalid API usage cause assertion failures19/// same connection, and makes invalid API usage cause assertion failures
20/// rather than HTTP protocol violations.20/// rather than HTTP protocol violations.
21state: State,21state: State,
22in_err: anyerror,22head_parse_err: Request.Head.ParseError,
2323
24pub const State = enum {24pub const State = enum {
25 /// The connection is available to be used for the first time, or reused.25 /// The connection is available to be used for the first time, or reused.
...@@ -53,8 +53,8 @@ pub const ReceiveHeadError = error{...@@ -53,8 +53,8 @@ pub const ReceiveHeadError = error{
53 /// The HTTP specification suggests to respond with a 431 status code53 /// The HTTP specification suggests to respond with a 431 status code
54 /// before closing the connection.54 /// before closing the connection.
55 HttpHeadersOversize,55 HttpHeadersOversize,
56 /// Client sent headers that did not conform to the HTTP protocol.56 /// Client sent headers that did not conform to the HTTP protocol;
57 /// `in_err` is populated with a `Request.Head.ParseError`.57 /// `head_parse_err` is populated.
58 HttpHeadersInvalid,58 HttpHeadersInvalid,
59 /// Partial HTTP request was received but the connection was closed before59 /// Partial HTTP request was received but the connection was closed before
60 /// fully receiving the headers.60 /// fully receiving the headers.
...@@ -62,7 +62,7 @@ pub const ReceiveHeadError = error{...@@ -62,7 +62,7 @@ pub const ReceiveHeadError = error{
62 /// The client sent 0 bytes of headers before closing the stream.62 /// The client sent 0 bytes of headers before closing the stream.
63 /// In other words, a keep-alive connection was finally closed.63 /// In other words, a keep-alive connection was finally closed.
64 HttpConnectionClosing,64 HttpConnectionClosing,
65 /// Error occurred reading from `in`; `in_err` is populated.65 /// Transitive error occurred reading from `in`.
66 ReadFailure,66 ReadFailure,
67};67};
6868
...@@ -79,23 +79,22 @@ pub fn receiveHead(s: *Server) ReceiveHeadError!Request {...@@ -79,23 +79,22 @@ pub fn receiveHead(s: *Server) ReceiveHeadError!Request {
7979
80 while (true) {80 while (true) {
81 if (head_end >= in.bufferContents().len) return error.HttpHeadersOversize;81 if (head_end >= in.bufferContents().len) return error.HttpHeadersOversize;
82 const buf = (in.peekGreedy(head_end + 1) catch |err| {82 const buf = in.peekGreedy(head_end + 1) catch |err| switch (err) {
83 s.in_err = err;83 error.EndOfStream => switch (head_end) {
84 return error.ReadFailure;84 0 => return error.HttpConnectionClosing,
85 }) orelse switch (head_end) {85 else => return error.HttpRequestTruncated,
86 0 => return error.HttpConnectionClosing,86 },
87 else => return error.HttpRequestTruncated,87 error.ReadFailure => return error.ReadFailure,
88 };88 };
89 head_end += hp.feed(buf[head_end..]);89 head_end += hp.feed(buf[head_end..]);
90 if (hp.state == .finished) return .{90 if (hp.state == .finished) return .{
91 .server = s,91 .server = s,
92 .head_end = head_end,92 .head_end = head_end,
93 .head = Request.Head.parse(buf[0..head_end]) catch |err| {93 .head = Request.Head.parse(buf[0..head_end]) catch |err| {
94 s.in_err = err;94 s.head_parse_err = err;
95 return error.HttpHeadersInvalid;95 return error.HttpHeadersInvalid;
96 },96 },
97 .reader_state = undefined,97 .reader_state = undefined,
98 .write_error = undefined,
99 };98 };
100 }99 }
101}100}
...@@ -109,8 +108,6 @@ pub const Request = struct {...@@ -109,8 +108,6 @@ pub const Request = struct {
109 remaining_content_length: u64,108 remaining_content_length: u64,
110 chunk_parser: http.ChunkParser,109 chunk_parser: http.ChunkParser,
111 },110 },
112 /// Populated when `error.HttpContinueWriteFailed` is received.
113 write_error: anyerror,
114111
115 pub const Compression = union(enum) {112 pub const Compression = union(enum) {
116 deflate: std.compress.zlib.Decompressor,113 deflate: std.compress.zlib.Decompressor,
...@@ -310,7 +307,6 @@ pub const Request = struct {...@@ -310,7 +307,6 @@ pub const Request = struct {
310 .head_end = request_bytes.len,307 .head_end = request_bytes.len,
311 .head = undefined,308 .head = undefined,
312 .reader_state = undefined,309 .reader_state = undefined,
313 .write_error = undefined,
314 };310 };
315311
316 var it = request.iterateHeaders();312 var it = request.iterateHeaders();
...@@ -375,7 +371,7 @@ pub const Request = struct {...@@ -375,7 +371,7 @@ pub const Request = struct {
375 request: *Request,371 request: *Request,
376 content: []const u8,372 content: []const u8,
377 options: RespondOptions,373 options: RespondOptions,
378 ) anyerror!void {374 ) std.io.Writer.Error!void {
379 const max_extra_headers = 25;375 const max_extra_headers = 25;
380 assert(options.status != .@"continue");376 assert(options.status != .@"continue");
381 assert(options.extra_headers.len <= max_extra_headers);377 assert(options.extra_headers.len <= max_extra_headers);
...@@ -581,7 +577,7 @@ pub const Request = struct {...@@ -581,7 +577,7 @@ pub const Request = struct {
581 ctx: ?*anyopaque,577 ctx: ?*anyopaque,
582 bw: *std.io.BufferedWriter,578 bw: *std.io.BufferedWriter,
583 limit: std.io.Reader.Limit,579 limit: std.io.Reader.Limit,
584 ) anyerror!std.io.Reader.Status {580 ) std.io.Reader.Error!std.io.Reader.Status {
585 const request: *Request = @alignCast(@ptrCast(ctx));581 const request: *Request = @alignCast(@ptrCast(ctx));
586 _ = request;582 _ = request;
587 _ = bw;583 _ = bw;
...@@ -589,7 +585,7 @@ pub const Request = struct {...@@ -589,7 +585,7 @@ pub const Request = struct {
589 @panic("TODO");585 @panic("TODO");
590 }586 }
591587
592 fn contentLengthReader_readv(ctx: ?*anyopaque, data: []const []u8) anyerror!std.io.Reader.Status {588 fn contentLengthReader_readv(ctx: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
593 const request: *Request = @alignCast(@ptrCast(ctx));589 const request: *Request = @alignCast(@ptrCast(ctx));
594 _ = request;590 _ = request;
595 _ = data;591 _ = data;
...@@ -600,7 +596,7 @@ pub const Request = struct {...@@ -600,7 +596,7 @@ pub const Request = struct {
600 ctx: ?*anyopaque,596 ctx: ?*anyopaque,
601 bw: *std.io.BufferedWriter,597 bw: *std.io.BufferedWriter,
602 limit: std.io.Reader.Limit,598 limit: std.io.Reader.Limit,
603 ) anyerror!std.io.Reader.Status {599 ) std.io.Reader.Error!usize {
604 const request: *Request = @alignCast(@ptrCast(ctx));600 const request: *Request = @alignCast(@ptrCast(ctx));
605 _ = request;601 _ = request;
606 _ = bw;602 _ = bw;
...@@ -608,7 +604,7 @@ pub const Request = struct {...@@ -608,7 +604,7 @@ pub const Request = struct {
608 @panic("TODO");604 @panic("TODO");
609 }605 }
610606
611 fn chunkedReader_readv(ctx: ?*anyopaque, data: []const []u8) anyerror!std.io.Reader.Status {607 fn chunkedReader_readv(ctx: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
612 const request: *Request = @alignCast(@ptrCast(ctx));608 const request: *Request = @alignCast(@ptrCast(ctx));
613 _ = request;609 _ = request;
614 _ = data;610 _ = data;
...@@ -732,9 +728,10 @@ pub const Request = struct {...@@ -732,9 +728,10 @@ pub const Request = struct {
732 }728 }
733729
734 pub const ReaderError = error{730 pub const ReaderError = error{
735 /// Failed to write "100-continue" to the stream. Error value is731 /// Failed to write "100-continue" to the stream.
736 /// stored in `Request.write_error`.732 WriteFailed,
737 HttpContinueWriteFailed,733 /// Failed to write "100-continue" to the stream because it ended.
734 EndOfStream,
738 /// The client sent an expect HTTP header value other than735 /// The client sent an expect HTTP header value other than
739 /// "100-continue".736 /// "100-continue".
740 HttpExpectationFailed,737 HttpExpectationFailed,
...@@ -755,10 +752,7 @@ pub const Request = struct {...@@ -755,10 +752,7 @@ pub const Request = struct {
755 if (request.head.expect) |expect| {752 if (request.head.expect) |expect| {
756 if (mem.eql(u8, expect, "100-continue")) {753 if (mem.eql(u8, expect, "100-continue")) {
757 var w = request.server.connection.stream.writer().unbuffered();754 var w = request.server.connection.stream.writer().unbuffered();
758 w.writeAll("HTTP/1.1 100 Continue\r\n\r\n") catch |err| {755 try w.writeAll("HTTP/1.1 100 Continue\r\n\r\n");
759 request.write_error = err;
760 return error.HttpContinueWriteFailed;
761 };
762 request.head.expect = null;756 request.head.expect = null;
763 } else {757 } else {
764 return error.HttpExpectationFailed;758 return error.HttpExpectationFailed;
...@@ -854,7 +848,7 @@ pub const Response = struct {...@@ -854,7 +848,7 @@ pub const Response = struct {
854 /// Otherwise, transfer-encoding: chunked is being used, and it writes the848 /// Otherwise, transfer-encoding: chunked is being used, and it writes the
855 /// end-of-stream message, then flushes the stream to the system.849 /// end-of-stream message, then flushes the stream to the system.
856 /// Respects the value of `elide_body` to omit all data after the headers.850 /// Respects the value of `elide_body` to omit all data after the headers.
857 pub fn end(r: *Response) anyerror!void {851 pub fn end(r: *Response) std.io.Writer.Error!void {
858 switch (r.transfer_encoding) {852 switch (r.transfer_encoding) {
859 .content_length => |len| {853 .content_length => |len| {
860 assert(len == 0); // Trips when end() called before all bytes written.854 assert(len == 0); // Trips when end() called before all bytes written.
...@@ -879,7 +873,7 @@ pub const Response = struct {...@@ -879,7 +873,7 @@ pub const Response = struct {
879 /// flushes the stream to the system.873 /// flushes the stream to the system.
880 /// Respects the value of `elide_body` to omit all data after the headers.874 /// Respects the value of `elide_body` to omit all data after the headers.
881 /// Asserts there are at most 25 trailers.875 /// Asserts there are at most 25 trailers.
882 pub fn endChunked(r: *Response, options: EndChunkedOptions) anyerror!void {876 pub fn endChunked(r: *Response, options: EndChunkedOptions) std.io.Writer.Error!void {
883 assert(r.transfer_encoding == .chunked);877 assert(r.transfer_encoding == .chunked);
884 try flush_chunked(r, options.trailers);878 try flush_chunked(r, options.trailers);
885 r.* = undefined;879 r.* = undefined;
...@@ -889,14 +883,14 @@ pub const Response = struct {...@@ -889,14 +883,14 @@ pub const Response = struct {
889 /// would not exceed the content-length value sent in the HTTP header.883 /// would not exceed the content-length value sent in the HTTP header.
890 /// May return 0, which does not indicate end of stream. The caller decides884 /// May return 0, which does not indicate end of stream. The caller decides
891 /// when the end of stream occurs by calling `end`.885 /// when the end of stream occurs by calling `end`.
892 pub fn write(r: *Response, bytes: []const u8) anyerror!usize {886 pub fn write(r: *Response, bytes: []const u8) std.io.Writer.Error!usize {
893 switch (r.transfer_encoding) {887 switch (r.transfer_encoding) {
894 .content_length, .none => return @errorCast(cl_writeSplat(r, &.{bytes}, 1)),888 .content_length, .none => return @errorCast(cl_writeSplat(r, &.{bytes}, 1)),
895 .chunked => return @errorCast(chunked_writeSplat(r, &.{bytes}, 1)),889 .chunked => return @errorCast(chunked_writeSplat(r, &.{bytes}, 1)),
896 }890 }
897 }891 }
898892
899 fn cl_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize {893 fn cl_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
900 _ = splat;894 _ = splat;
901 return cl_write(context, data[0]); // TODO: try to send all the data895 return cl_write(context, data[0]); // TODO: try to send all the data
902 }896 }
...@@ -908,7 +902,7 @@ pub const Response = struct {...@@ -908,7 +902,7 @@ pub const Response = struct {
908 limit: std.io.Writer.Limit,902 limit: std.io.Writer.Limit,
909 headers_and_trailers: []const []const u8,903 headers_and_trailers: []const []const u8,
910 headers_len: usize,904 headers_len: usize,
911 ) anyerror!usize {905 ) std.io.Writer.Error!usize {
912 _ = context;906 _ = context;
913 _ = file;907 _ = file;
914 _ = offset;908 _ = offset;
...@@ -918,7 +912,7 @@ pub const Response = struct {...@@ -918,7 +912,7 @@ pub const Response = struct {
918 return error.Unimplemented;912 return error.Unimplemented;
919 }913 }
920914
921 fn cl_write(context: ?*anyopaque, bytes: []const u8) anyerror!usize {915 fn cl_write(context: ?*anyopaque, bytes: []const u8) std.io.Writer.Error!usize {
922 const r: *Response = @alignCast(@ptrCast(context));916 const r: *Response = @alignCast(@ptrCast(context));
923917
924 var trash: u64 = std.math.maxInt(u64);918 var trash: u64 = std.math.maxInt(u64);
...@@ -963,7 +957,7 @@ pub const Response = struct {...@@ -963,7 +957,7 @@ pub const Response = struct {
963 return bytes.len;957 return bytes.len;
964 }958 }
965959
966 fn chunked_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize {960 fn chunked_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
967 _ = splat;961 _ = splat;
968 return chunked_write(context, data[0]); // TODO: try to send all the data962 return chunked_write(context, data[0]); // TODO: try to send all the data
969 }963 }
...@@ -975,7 +969,7 @@ pub const Response = struct {...@@ -975,7 +969,7 @@ pub const Response = struct {
975 limit: std.io.Writer.Limit,969 limit: std.io.Writer.Limit,
976 headers_and_trailers: []const []const u8,970 headers_and_trailers: []const []const u8,
977 headers_len: usize,971 headers_len: usize,
978 ) anyerror!usize {972 ) std.io.Writer.Error!usize {
979 _ = context;973 _ = context;
980 _ = file;974 _ = file;
981 _ = offset;975 _ = offset;
...@@ -985,7 +979,7 @@ pub const Response = struct {...@@ -985,7 +979,7 @@ pub const Response = struct {
985 return error.Unimplemented; // TODO lower to a call to writeFile on the output979 return error.Unimplemented; // TODO lower to a call to writeFile on the output
986 }980 }
987981
988 fn chunked_write(context: ?*anyopaque, bytes: []const u8) anyerror!usize {982 fn chunked_write(context: ?*anyopaque, bytes: []const u8) std.io.Writer.Error!usize {
989 const r: *Response = @alignCast(@ptrCast(context));983 const r: *Response = @alignCast(@ptrCast(context));
990 assert(r.transfer_encoding == .chunked);984 assert(r.transfer_encoding == .chunked);
991985
...@@ -1024,7 +1018,7 @@ pub const Response = struct {...@@ -1024,7 +1018,7 @@ pub const Response = struct {
10241018
1025 /// If using content-length, asserts that writing these bytes to the client1019 /// If using content-length, asserts that writing these bytes to the client
1026 /// would not exceed the content-length value sent in the HTTP header.1020 /// would not exceed the content-length value sent in the HTTP header.
1027 pub fn writeAll(r: *Response, bytes: []const u8) anyerror!void {1021 pub fn writeAll(r: *Response, bytes: []const u8) std.io.Writer.Error!void {
1028 var index: usize = 0;1022 var index: usize = 0;
1029 while (index < bytes.len) {1023 while (index < bytes.len) {
1030 index += try write(r, bytes[index..]);1024 index += try write(r, bytes[index..]);
...@@ -1034,21 +1028,21 @@ pub const Response = struct {...@@ -1034,21 +1028,21 @@ pub const Response = struct {
1034 /// Sends all buffered data to the client.1028 /// Sends all buffered data to the client.
1035 /// This is redundant after calling `end`.1029 /// This is redundant after calling `end`.
1036 /// Respects the value of `elide_body` to omit all data after the headers.1030 /// Respects the value of `elide_body` to omit all data after the headers.
1037 pub fn flush(r: *Response) anyerror!void {1031 pub fn flush(r: *Response) std.io.Writer.Error!void {
1038 switch (r.transfer_encoding) {1032 switch (r.transfer_encoding) {
1039 .none, .content_length => return flush_cl(r),1033 .none, .content_length => return flush_cl(r),
1040 .chunked => return flush_chunked(r, null),1034 .chunked => return flush_chunked(r, null),
1041 }1035 }
1042 }1036 }
10431037
1044 fn flush_cl(r: *Response) anyerror!void {1038 fn flush_cl(r: *Response) std.io.Writer.Error!void {
1045 var w = r.stream.writer().unbuffered();1039 var w = r.stream.writer().unbuffered();
1046 try w.writeAll(r.send_buffer[r.send_buffer_start..r.send_buffer_end]);1040 try w.writeAll(r.send_buffer[r.send_buffer_start..r.send_buffer_end]);
1047 r.send_buffer_start = 0;1041 r.send_buffer_start = 0;
1048 r.send_buffer_end = 0;1042 r.send_buffer_end = 0;
1049 }1043 }
10501044
1051 fn flush_chunked(r: *Response, end_trailers: ?[]const http.Header) anyerror!void {1045 fn flush_chunked(r: *Response, end_trailers: ?[]const http.Header) std.io.Writer.Error!void {
1052 const max_trailers = 25;1046 const max_trailers = 25;
1053 if (end_trailers) |trailers| assert(trailers.len <= max_trailers);1047 if (end_trailers) |trailers| assert(trailers.len <= max_trailers);
1054 assert(r.transfer_encoding == .chunked);1048 assert(r.transfer_encoding == .chunked);
lib/std/http/WebSocket.zig+4-2
...@@ -194,14 +194,16 @@ fn recvReadInt(ws: *WebSocket, comptime I: type) !I {...@@ -194,14 +194,16 @@ fn recvReadInt(ws: *WebSocket, comptime I: type) !I {
194 };194 };
195}195}
196196
197pub fn writeMessage(ws: *WebSocket, message: []const u8, opcode: Opcode) anyerror!void {197pub const WriteError = std.http.Server.Response.WriteError;
198
199pub fn writeMessage(ws: *WebSocket, message: []const u8, opcode: Opcode) WriteError!void {
198 const iovecs: [1]std.posix.iovec_const = .{200 const iovecs: [1]std.posix.iovec_const = .{
199 .{ .base = message.ptr, .len = message.len },201 .{ .base = message.ptr, .len = message.len },
200 };202 };
201 return writeMessagev(ws, &iovecs, opcode);203 return writeMessagev(ws, &iovecs, opcode);
202}204}
203205
204pub fn writeMessagev(ws: *WebSocket, message: []const std.posix.iovec_const, opcode: Opcode) anyerror!void {206pub fn writeMessagev(ws: *WebSocket, message: []const std.posix.iovec_const, opcode: Opcode) WriteError!void {
205 const total_len = l: {207 const total_len = l: {
206 var total_len: u64 = 0;208 var total_len: u64 = 0;
207 for (message) |iovec| total_len += iovec.len;209 for (message) |iovec| total_len += iovec.len;
lib/std/io.zig-3
...@@ -17,8 +17,6 @@ const Alignment = std.mem.Alignment;...@@ -17,8 +17,6 @@ const Alignment = std.mem.Alignment;
17pub const Reader = @import("io/Reader.zig");17pub const Reader = @import("io/Reader.zig");
18pub const Writer = @import("io/Writer.zig");18pub const Writer = @import("io/Writer.zig");
1919
20pub const PositionalReader = @import("io/PositionalReader.zig");
21
22pub const BufferedReader = @import("io/BufferedReader.zig");20pub const BufferedReader = @import("io/BufferedReader.zig");
23pub const BufferedWriter = @import("io/BufferedWriter.zig");21pub const BufferedWriter = @import("io/BufferedWriter.zig");
24pub const AllocatingWriter = @import("io/AllocatingWriter.zig");22pub const AllocatingWriter = @import("io/AllocatingWriter.zig");
...@@ -453,7 +451,6 @@ test {...@@ -453,7 +451,6 @@ test {
453 _ = BufferedReader;451 _ = BufferedReader;
454 _ = Reader;452 _ = Reader;
455 _ = Writer;453 _ = Writer;
456 _ = PositionalReader;
457 _ = AllocatingWriter;454 _ = AllocatingWriter;
458 _ = @import("io/bit_reader.zig");455 _ = @import("io/bit_reader.zig");
459 _ = @import("io/bit_writer.zig");456 _ = @import("io/bit_writer.zig");
lib/std/io/AllocatingWriter.zig+6-6
...@@ -130,7 +130,7 @@ pub fn clearRetainingCapacity(aw: *AllocatingWriter) void {...@@ -130,7 +130,7 @@ pub fn clearRetainingCapacity(aw: *AllocatingWriter) void {
130 aw.shrinkRetainingCapacity(0);130 aw.shrinkRetainingCapacity(0);
131}131}
132132
133fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize {133fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
134 const aw: *AllocatingWriter = @alignCast(@ptrCast(context));134 const aw: *AllocatingWriter = @alignCast(@ptrCast(context));
135 const start_len = aw.written.len;135 const start_len = aw.written.len;
136 const bw = &aw.buffered_writer;136 const bw = &aw.buffered_writer;
...@@ -145,7 +145,7 @@ fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anye...@@ -145,7 +145,7 @@ fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anye
145 const pattern = data[data.len - 1];145 const pattern = data[data.len - 1];
146 var new_capacity: usize = list.capacity + pattern.len * splat;146 var new_capacity: usize = list.capacity + pattern.len * splat;
147 for (rest) |bytes| new_capacity += bytes.len;147 for (rest) |bytes| new_capacity += bytes.len;
148 try list.ensureTotalCapacity(aw.allocator, new_capacity + 1);148 list.ensureTotalCapacity(aw.allocator, new_capacity + 1) catch return error.WriteFailed;
149 for (rest) |bytes| list.appendSliceAssumeCapacity(bytes);149 for (rest) |bytes| list.appendSliceAssumeCapacity(bytes);
150 appendPatternAssumeCapacity(&list, pattern, splat);150 appendPatternAssumeCapacity(&list, pattern, splat);
151 aw.written = list.items;151 aw.written = list.items;
...@@ -168,7 +168,7 @@ fn writeFile(...@@ -168,7 +168,7 @@ fn writeFile(
168 limit: std.io.Writer.Limit,168 limit: std.io.Writer.Limit,
169 headers_and_trailers_full: []const []const u8,169 headers_and_trailers_full: []const []const u8,
170 headers_len_full: usize,170 headers_len_full: usize,
171) anyerror!usize {171) std.io.Writer.FileError!usize {
172 const aw: *AllocatingWriter = @alignCast(@ptrCast(context));172 const aw: *AllocatingWriter = @alignCast(@ptrCast(context));
173 const gpa = aw.allocator;173 const gpa = aw.allocator;
174 var list = aw.toArrayList();174 var list = aw.toArrayList();
...@@ -184,14 +184,14 @@ fn writeFile(...@@ -184,14 +184,14 @@ fn writeFile(
184 const limit_int = limit.toInt() orelse {184 const limit_int = limit.toInt() orelse {
185 var new_capacity: usize = list.capacity + std.atomic.cache_line;185 var new_capacity: usize = list.capacity + std.atomic.cache_line;
186 for (headers_and_trailers) |bytes| new_capacity += bytes.len;186 for (headers_and_trailers) |bytes| new_capacity += bytes.len;
187 try list.ensureTotalCapacity(gpa, new_capacity);187 list.ensureTotalCapacity(gpa, new_capacity) catch return error.WriteFailed;
188 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);188 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);
189 const dest = list.items.ptr[list.items.len..list.capacity];189 const dest = list.items.ptr[list.items.len..list.capacity];
190 const n = try file.pread(dest, pos);190 const n = try file.pread(dest, pos);
191 if (n == 0) {191 if (n == 0) {
192 new_capacity = list.capacity;192 new_capacity = list.capacity;
193 for (trailers) |bytes| new_capacity += bytes.len;193 for (trailers) |bytes| new_capacity += bytes.len;
194 try list.ensureTotalCapacity(gpa, new_capacity);194 list.ensureTotalCapacity(gpa, new_capacity) catch return error.WriteFailed;
195 for (trailers) |bytes| list.appendSliceAssumeCapacity(bytes);195 for (trailers) |bytes| list.appendSliceAssumeCapacity(bytes);
196 return list.items.len - start_len;196 return list.items.len - start_len;
197 }197 }
...@@ -200,7 +200,7 @@ fn writeFile(...@@ -200,7 +200,7 @@ fn writeFile(
200 };200 };
201 var new_capacity: usize = list.capacity + limit_int;201 var new_capacity: usize = list.capacity + limit_int;
202 for (headers_and_trailers) |bytes| new_capacity += bytes.len;202 for (headers_and_trailers) |bytes| new_capacity += bytes.len;
203 try list.ensureTotalCapacity(gpa, new_capacity);203 list.ensureTotalCapacity(gpa, new_capacity) catch return error.WriteFailed;
204 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);204 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);
205 const dest = list.items.ptr[list.items.len..][0..limit_int];205 const dest = list.items.ptr[list.items.len..][0..limit_int];
206 const n = try file.pread(dest, pos);206 const n = try file.pread(dest, pos);
lib/std/io/BufferedReader.zig+164-215
...@@ -23,74 +23,23 @@ pub fn init(br: *BufferedReader, r: Reader, buffer: []u8) void {...@@ -23,74 +23,23 @@ pub fn init(br: *BufferedReader, r: Reader, buffer: []u8) void {
23 br.storage.initFixed(buffer);23 br.storage.initFixed(buffer);
24}24}
2525
26const eof_writer: std.io.Writer.VTable = .{
27 .writeSplat = eof_writeSplat,
28 .writeFile = eof_writeFile,
29};
30const eof_reader: std.io.Reader.VTable = .{
31 .read = eof_read,
32 .readv = eof_readv,
33};
34
35fn eof_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize {
36 _ = context;
37 _ = data;
38 _ = splat;
39 return error.NoSpaceLeft;
40}
41
42fn eof_writeFile(
43 context: ?*anyopaque,
44 file: std.fs.File,
45 offset: std.io.Writer.Offset,
46 limit: std.io.Writer.Limit,
47 headers_and_trailers: []const []const u8,
48 headers_len: usize,
49) anyerror!usize {
50 _ = context;
51 _ = file;
52 _ = offset;
53 _ = limit;
54 _ = headers_and_trailers;
55 _ = headers_len;
56 return error.NoSpaceLeft;
57}
58
59fn eof_read(ctx: ?*anyopaque, bw: *std.io.BufferedWriter, limit: Reader.Limit) anyerror!Reader.Status {
60 _ = ctx;
61 _ = bw;
62 _ = limit;
63 return error.EndOfStream;
64}
65
66fn eof_readv(ctx: ?*anyopaque, data: []const []u8) anyerror!Reader.Status {
67 _ = ctx;
68 _ = data;
69 return error.EndOfStream;
70}
71
72/// Constructs `br` such that it will read from `buffer` and then end.26/// Constructs `br` such that it will read from `buffer` and then end.
27/// TODO either remove the const cast here or make methods of this file return a const slice
73pub fn initFixed(br: *BufferedReader, buffer: []const u8) void {28pub fn initFixed(br: *BufferedReader, buffer: []const u8) void {
74 br.* = .{29 br.* = .{
75 .seek = 0,30 .seek = 0,
76 .storage = .{31 .storage = .{
77 .buffer = @constCast(buffer),32 .buffer = @constCast(buffer),
78 .unbuffered_writer = .{33 .unbuffered_writer = .failing,
79 .context = undefined,
80 .vtable = &eof_writer,
81 },
82 },
83 .unbuffered_reader = .{
84 .context = undefined,
85 .vtable = &eof_reader,
86 },34 },
35 .unbuffered_reader = .ending,
87 };36 };
88}37}
8938
90pub fn storageBuffer(br: *BufferedReader) []u8 {39pub fn storageBuffer(br: *BufferedReader) []u8 {
91 const storage = &br.storage;40 const storage = &br.storage;
92 assert(storage.unbuffered_writer.vtable == &eof_writer);41 assert(storage.unbuffered_writer.vtable == std.io.Writer.failing.vtable);
93 assert(br.unbuffered_reader.vtable == &eof_reader);42 assert(br.unbuffered_reader.vtable == Reader.ending.vtable);
94 return storage.buffer;43 return storage.buffer;
95}44}
9645
...@@ -106,47 +55,43 @@ pub fn reader(br: *BufferedReader) Reader {...@@ -106,47 +55,43 @@ pub fn reader(br: *BufferedReader) Reader {
106 return .{55 return .{
107 .context = br,56 .context = br,
108 .vtable = &.{57 .vtable = &.{
109 .read = passthru_read,58 .read = passthruRead,
110 .readv = passthru_readv,59 .readVec = passthruReadVec,
111 },60 },
112 };61 };
113}62}
11463
115fn passthru_read(ctx: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) anyerror!Reader.RwResult {64fn passthruRead(ctx: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) Reader.RwError!usize {
116 const br: *BufferedReader = @alignCast(@ptrCast(ctx));65 const br: *BufferedReader = @alignCast(@ptrCast(ctx));
117 const storage = &br.storage;66 const storage = &br.storage;
118 const buffer = storage.buffer[0..storage.end];67 const buffer = storage.buffer[0..storage.end];
119 const buffered = buffer[br.seek..];68 const buffered = buffer[br.seek..];
120 const limited = buffered[0..limit.min(buffered.len)];69 const limited = buffered[0..limit.min(buffered.len)];
121 if (limited.len > 0) {70 if (limited.len > 0) {
122 const result = bw.writeSplat(limited, 1);71 const n = try bw.writeSplat(limited, 1);
123 br.seek += result.len;72 br.seek += n;
124 return .{73 return n;
125 .len = result.len,
126 .write_err = result.err,
127 .write_end = result.end,
128 };
129 }74 }
130 return br.unbuffered_reader.read(bw, limit);75 return br.unbuffered_reader.read(bw, limit);
131}76}
13277
133fn passthru_readv(ctx: ?*anyopaque, data: []const []u8) anyerror!Reader.Status {78fn passthruReadVec(ctx: ?*anyopaque, data: []const []u8) Reader.Error!usize {
134 const br: *BufferedReader = @alignCast(@ptrCast(ctx));79 const br: *BufferedReader = @alignCast(@ptrCast(ctx));
135 _ = br;80 _ = br;
136 _ = data;81 _ = data;
137 @panic("TODO");82 @panic("TODO");
138}83}
13984
140pub fn seekBy(br: *BufferedReader, seek_by: i64) anyerror!void {85pub fn seekBy(br: *BufferedReader, seek_by: i64) !void {
141 if (seek_by < 0) try br.seekBackwardBy(@abs(seek_by)) else try br.seekForwardBy(@abs(seek_by));86 if (seek_by < 0) try br.seekBackwardBy(@abs(seek_by)) else try br.seekForwardBy(@abs(seek_by));
142}87}
14388
144pub fn seekBackwardBy(br: *BufferedReader, seek_by: u64) anyerror!void {89pub fn seekBackwardBy(br: *BufferedReader, seek_by: u64) !void {
145 if (seek_by > br.storage.end - br.seek) return error.Unseekable; // TODO90 if (seek_by > br.storage.end - br.seek) return error.Unseekable; // TODO
146 br.seek += @abs(seek_by);91 br.seek += @abs(seek_by);
147}92}
14893
149pub fn seekForwardBy(br: *BufferedReader, seek_by: u64) anyerror!void {94pub fn seekForwardBy(br: *BufferedReader, seek_by: u64) !void {
150 const seek, const need_unbuffered_seek = @subWithOverflow(br.seek, @abs(seek_by));95 const seek, const need_unbuffered_seek = @subWithOverflow(br.seek, @abs(seek_by));
151 if (need_unbuffered_seek > 0) return error.Unseekable; // TODO96 if (need_unbuffered_seek > 0) return error.Unseekable; // TODO
152 br.seek = seek;97 br.seek = seek;
...@@ -166,27 +111,11 @@ pub fn seekForwardBy(br: *BufferedReader, seek_by: u64) anyerror!void {...@@ -166,27 +111,11 @@ pub fn seekForwardBy(br: *BufferedReader, seek_by: u64) anyerror!void {
166/// See also:111/// See also:
167/// * `peekGreedy`112/// * `peekGreedy`
168/// * `toss`113/// * `toss`
169pub fn peek(br: *BufferedReader, n: usize) anyerror![]u8 {114pub fn peek(br: *BufferedReader, n: usize) Reader.Error![]u8 {
170 return (try br.peekGreedy(n))[0..n];115 const storage = &br.storage;
171}116 assert(n <= storage.buffer.len);
172117 try br.fill(n);
173/// Returns the next `n` bytes from `unbuffered_reader`, filling the buffer as118 return storage.buffer[br.seek..][0..n];
174/// necessary.
175///
176/// Invalidates previously returned values from `peek`.
177///
178/// Asserts that the `BufferedReader` was initialized with a buffer capacity at
179/// least as big as `n`.
180///
181/// If there are fewer than `n` bytes left in the stream, `null` is returned
182/// instead.
183///
184/// See also:
185/// * `peekGreedy`
186/// * `toss`
187pub fn peek2(br: *BufferedReader, n: usize) anyerror!?[]u8 {
188 if (try br.peekGreedy(n)) |buf| return buf[0..n];
189 return null;
190}119}
191120
192/// Returns all the next buffered bytes from `unbuffered_reader`, after filling121/// Returns all the next buffered bytes from `unbuffered_reader`, after filling
...@@ -203,30 +132,11 @@ pub fn peek2(br: *BufferedReader, n: usize) anyerror!?[]u8 {...@@ -203,30 +132,11 @@ pub fn peek2(br: *BufferedReader, n: usize) anyerror!?[]u8 {
203/// See also:132/// See also:
204/// * `peek`133/// * `peek`
205/// * `toss`134/// * `toss`
206pub fn peekGreedy(br: *BufferedReader, n: usize) anyerror![]u8 {135pub fn peekGreedy(br: *BufferedReader, n: usize) Reader.Error![]u8 {
207 assert(n <= br.storage.buffer.len);136 const storage = &br.storage;
208 if (try br.fill(n)) return br.bufferContents();137 assert(n <= storage.buffer.len);
209 return error.EndOfStream;138 try br.fill(n);
210}139 return storage.buffer[br.seek..storage.end];
211
212/// Returns all the next buffered bytes from `unbuffered_reader`, after filling
213/// the buffer to ensure it contains at least `n` bytes.
214///
215/// Invalidates previously returned values from `peek` and `peekGreedy`.
216///
217/// Asserts that the `BufferedReader` was initialized with a buffer capacity at
218/// least as big as `n`.
219///
220/// If there are fewer than `n` bytes left in the stream, `null` is returned
221/// instead.
222///
223/// See also:
224/// * `peek`
225/// * `toss`
226pub fn peekGreedy2(br: *BufferedReader, n: usize) anyerror!?[]u8 {
227 assert(n <= br.storage.buffer.len);
228 if (try br.fill(n)) return br.bufferContents();
229 return null;
230}140}
231141
232/// Skips the next `n` bytes from the stream, advancing the seek position. This142/// Skips the next `n` bytes from the stream, advancing the seek position. This
...@@ -242,8 +152,11 @@ pub fn toss(br: *BufferedReader, n: usize) void {...@@ -242,8 +152,11 @@ pub fn toss(br: *BufferedReader, n: usize) void {
242 assert(br.seek <= br.storage.end);152 assert(br.seek <= br.storage.end);
243}153}
244154
245/// Equivalent to `peek` + `toss`.155/// Equivalent to `peek` followed by `toss`.
246pub fn take(br: *BufferedReader, n: usize) anyerror![]u8 {156///
157/// The data returned is invalidated by the next call to `take`, `peek`,
158/// `fill`, and functions with those prefixes.
159pub fn take(br: *BufferedReader, n: usize) Reader.Error![]u8 {
247 const result = try br.peek(n);160 const result = try br.peek(n);
248 br.toss(n);161 br.toss(n);
249 return result;162 return result;
...@@ -260,7 +173,7 @@ pub fn take(br: *BufferedReader, n: usize) anyerror![]u8 {...@@ -260,7 +173,7 @@ pub fn take(br: *BufferedReader, n: usize) anyerror![]u8 {
260///173///
261/// See also:174/// See also:
262/// * `take`175/// * `take`
263pub fn takeArray(br: *BufferedReader, comptime n: usize) anyerror!*[n]u8 {176pub fn takeArray(br: *BufferedReader, comptime n: usize) Reader.Error!*[n]u8 {
264 return (try br.take(n))[0..n];177 return (try br.take(n))[0..n];
265}178}
266179
...@@ -272,10 +185,10 @@ pub fn takeArray(br: *BufferedReader, comptime n: usize) anyerror!*[n]u8 {...@@ -272,10 +185,10 @@ pub fn takeArray(br: *BufferedReader, comptime n: usize) anyerror!*[n]u8 {
272///185///
273/// See also:186/// See also:
274/// * `toss`187/// * `toss`
275/// * `discardUntilEnd`188/// * `discardRemaining`
276/// * `discardUpTo`189/// * `discardShort`
277pub fn discard(br: *BufferedReader, n: usize) anyerror!void {190pub fn discard(br: *BufferedReader, n: usize) Reader.Error!void {
278 if ((try br.discardUpTo(n)) != n) return error.EndOfStream;191 if ((try br.discardShort(n)) != n) return error.EndOfStream;
279}192}
280193
281/// Skips the next `n` bytes from the stream, advancing the seek position.194/// Skips the next `n` bytes from the stream, advancing the seek position.
...@@ -288,35 +201,35 @@ pub fn discard(br: *BufferedReader, n: usize) anyerror!void {...@@ -288,35 +201,35 @@ pub fn discard(br: *BufferedReader, n: usize) anyerror!void {
288/// See also:201/// See also:
289/// * `discard`202/// * `discard`
290/// * `toss`203/// * `toss`
291/// * `discardUntilEnd`204/// * `discardRemaining`
292pub fn discardUpTo(br: *BufferedReader, n: usize) anyerror!usize {205pub fn discardShort(br: *BufferedReader, n: usize) Reader.ShortError!usize {
293 const storage = &br.storage;206 const storage = &br.storage;
294 var remaining = n;207 const proposed_seek = br.seek + n;
295 while (remaining > 0) {208 if (proposed_seek <= storage.end) {
296 const proposed_seek = br.seek + remaining;209 @branchHint(.likely);
297 if (proposed_seek <= storage.end) {210 br.seek = proposed_seek;
298 br.seek = proposed_seek;211 return n;
299 return n;212 }
300 }213 var remaining = n - (storage.end - br.seek);
301 remaining -= (storage.end - br.seek);214 storage.end = 0;
302 storage.end = 0;215 br.seek = 0;
303 br.seek = 0;216 while (true) {
304 const result = try br.unbuffered_reader.read(storage, .unlimited);217 const discard_len = br.unbuffered_reader.discard(remaining, .unlimited) catch |err| switch (err) {
305 assert(result.len == storage.end);218 error.EndOfStream => return n - remaining,
306 if (remaining <= storage.end) continue;219 error.ReadFailed => return error.ReadFailed,
307 if (result.end) return n - remaining;220 };
221 remaining -= discard_len;
222 if (remaining == 0) return n;
308 }223 }
309 return n;
310}224}
311225
312/// Reads the stream until the end, ignoring all the data.226/// Reads the stream until the end, ignoring all the data.
313/// Returns the number of bytes discarded.227/// Returns the number of bytes discarded.
314pub fn discardUntilEnd(br: *BufferedReader) anyerror!usize {228pub fn discardRemaining(br: *BufferedReader) Reader.ShortError!usize {
315 const storage = &br.storage;229 const storage = &br.storage;
316 var total: usize = storage.end;230 const buffered_len = storage.end;
317 storage.end = 0;231 storage.end = 0;
318 total += try br.unbuffered_reader.discardUntilEnd();232 return buffered_len + try br.unbuffered_reader.discardRemaining();
319 return total;
320}233}
321234
322/// Fill `buffer` with the next `buffer.len` bytes from the stream, advancing235/// Fill `buffer` with the next `buffer.len` bytes from the stream, advancing
...@@ -329,7 +242,7 @@ pub fn discardUntilEnd(br: *BufferedReader) anyerror!usize {...@@ -329,7 +242,7 @@ pub fn discardUntilEnd(br: *BufferedReader) anyerror!usize {
329///242///
330/// See also:243/// See also:
331/// * `peek`244/// * `peek`
332pub fn read(br: *BufferedReader, buffer: []u8) anyerror!void {245pub fn read(br: *BufferedReader, buffer: []u8) Reader.Error!void {
333 const storage = &br.storage;246 const storage = &br.storage;
334 const in_buffer = storage.buffer[0..storage.end];247 const in_buffer = storage.buffer[0..storage.end];
335 const seek = br.seek;248 const seek = br.seek;
...@@ -344,7 +257,12 @@ pub fn read(br: *BufferedReader, buffer: []u8) anyerror!void {...@@ -344,7 +257,12 @@ pub fn read(br: *BufferedReader, buffer: []u8) anyerror!void {
344 br.seek = 0;257 br.seek = 0;
345 var i: usize = in_buffer.len;258 var i: usize = in_buffer.len;
346 while (true) {259 while (true) {
347 const status = try br.unbuffered_reader.read(storage, .unlimited);260 // TODO if remaining buffer len is greater than storage len, read directly into buffer
261 const read_len = br.unbuffered_reader.read(storage, .unlimited) catch |err| switch (err) {
262 error.WriteFailed => storage.end,
263 else => |e| return e,
264 };
265 assert(read_len == storage.end);
348 const next_i = i + storage.end;266 const next_i = i + storage.end;
349 if (next_i >= buffer.len) {267 if (next_i >= buffer.len) {
350 const remaining = buffer[i..];268 const remaining = buffer[i..];
...@@ -352,46 +270,48 @@ pub fn read(br: *BufferedReader, buffer: []u8) anyerror!void {...@@ -352,46 +270,48 @@ pub fn read(br: *BufferedReader, buffer: []u8) anyerror!void {
352 br.seek = remaining.len;270 br.seek = remaining.len;
353 return;271 return;
354 }272 }
355 if (status.end) return error.EndOfStream;
356 @memcpy(buffer[i..next_i], storage.buffer[0..storage.end]);273 @memcpy(buffer[i..next_i], storage.buffer[0..storage.end]);
357 storage.end = 0;274 storage.end = 0;
358 i = next_i;275 i = next_i;
359 }276 }
360}277}
361278
362/// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it279/// Returns the number of bytes read, which is less than `buffer.len` if and
363/// means the stream reached the end. Reaching the end of a stream is not an error280/// only if the stream reached the end.
364/// condition.281pub fn readShort(br: *BufferedReader, buffer: []u8) Reader.ShortError!usize {
365pub fn partialRead(br: *BufferedReader, buffer: []u8) anyerror!usize {
366 _ = br;282 _ = br;
367 _ = buffer;283 _ = buffer;
368 @panic("TODO");284 @panic("TODO");
369}285}
370286
287pub const DelimiterInclusiveError = error{
288 /// See the `Reader` implementation for detailed diagnostics.
289 ReadFailed,
290 /// Stream ended before the delimiter was found.
291 EndOfStream,
292 /// The delimiter was not found within a number of bytes matching the
293 /// capacity of the `BufferedReader`.
294 StreamTooLong,
295};
296
371/// Returns a slice of the next bytes of buffered data from the stream until297/// Returns a slice of the next bytes of buffered data from the stream until
372/// `sentinel` is found, advancing the seek position.298/// `sentinel` is found, advancing the seek position.
373///299///
374/// Returned slice has a sentinel.300/// Returned slice has a sentinel.
375///301///
376/// If the stream ends before the sentinel is found, `error.EndOfStream` is
377/// returned.
378///
379/// If the sentinel is not found within a number of bytes matching the
380/// capacity of the `BufferedReader`, `error.StreamTooLong` is returned.
381///
382/// Invalidates previously returned values from `peek`.302/// Invalidates previously returned values from `peek`.
383///303///
384/// See also:304/// See also:
385/// * `peekSentinel`305/// * `peekSentinel`
386/// * `takeDelimiterExclusive`306/// * `takeDelimiterExclusive`
387/// * `takeDelimiterInclusive`307/// * `takeDelimiterInclusive`
388pub fn takeSentinel(br: *BufferedReader, comptime sentinel: u8) anyerror![:sentinel]u8 {308pub fn takeSentinel(br: *BufferedReader, comptime sentinel: u8) DelimiterInclusiveError![:sentinel]u8 {
389 const result = try br.peekSentinel(sentinel);309 const result = try br.peekSentinel(sentinel);
390 br.toss(result.len + 1);310 br.toss(result.len + 1);
391 return result;311 return result;
392}312}
393313
394pub fn peekSentinel(br: *BufferedReader, comptime sentinel: u8) anyerror![:sentinel]u8 {314pub fn peekSentinel(br: *BufferedReader, comptime sentinel: u8) DelimiterInclusiveError![:sentinel]u8 {
395 const result = try br.takeDelimiterInclusive(sentinel);315 const result = try br.takeDelimiterInclusive(sentinel);
396 return result[0 .. result.len - 1 :sentinel];316 return result[0 .. result.len - 1 :sentinel];
397}317}
...@@ -401,28 +321,30 @@ pub fn peekSentinel(br: *BufferedReader, comptime sentinel: u8) anyerror![:senti...@@ -401,28 +321,30 @@ pub fn peekSentinel(br: *BufferedReader, comptime sentinel: u8) anyerror![:senti
401///321///
402/// Returned slice includes the delimiter as the last byte.322/// Returned slice includes the delimiter as the last byte.
403///323///
404/// If the stream ends before the delimiter is found, `error.EndOfStream` is
405/// returned.
406///
407/// If the delimiter is not found within a number of bytes matching the
408/// capacity of the `BufferedReader`, `error.StreamTooLong` is returned.
409///
410/// Invalidates previously returned values from `peek`.324/// Invalidates previously returned values from `peek`.
411///325///
412/// See also:326/// See also:
413/// * `takeSentinel`327/// * `takeSentinel`
414/// * `takeDelimiterExclusive`328/// * `takeDelimiterExclusive`
415/// * `peekDelimiterInclusive`329/// * `peekDelimiterInclusive`
416pub fn takeDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 {330pub fn takeDelimiterInclusive(br: *BufferedReader, delimiter: u8) DelimiterInclusiveError![]u8 {
417 const result = try br.peekDelimiterInclusive(delimiter);331 const result = try br.peekDelimiterInclusive(delimiter);
418 br.toss(result.len);332 br.toss(result.len);
419 return result;333 return result;
420}334}
421335
422pub fn peekDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 {336pub fn peekDelimiterInclusive(br: *BufferedReader, delimiter: u8) DelimiterInclusiveError![]u8 {
423 return (try br.peekDelimiterInclusiveUnlessEnd(delimiter)) orelse error.EndOfStream;337 return (try br.peekDelimiterInclusiveUnlessEnd(delimiter)) orelse error.EndOfStream;
424}338}
425339
340pub const DelimiterExclusiveError = error{
341 /// See the `Reader` implementation for detailed diagnostics.
342 ReadFailed,
343 /// The delimiter was not found within a number of bytes matching the
344 /// capacity of the `BufferedReader`.
345 StreamTooLong,
346};
347
426/// Returns a slice of the next bytes of buffered data from the stream until348/// Returns a slice of the next bytes of buffered data from the stream until
427/// `delimiter` is found, advancing the seek position.349/// `delimiter` is found, advancing the seek position.
428///350///
...@@ -430,32 +352,33 @@ pub fn peekDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8...@@ -430,32 +352,33 @@ pub fn peekDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8
430///352///
431/// End-of-stream is treated equivalent to a delimiter.353/// End-of-stream is treated equivalent to a delimiter.
432///354///
433/// If the delimiter is not found within a number of bytes matching the
434/// capacity of the `BufferedReader`, `error.StreamTooLong` is returned.
435///
436/// Invalidates previously returned values from `peek`.355/// Invalidates previously returned values from `peek`.
437///356///
438/// See also:357/// See also:
439/// * `takeSentinel`358/// * `takeSentinel`
440/// * `takeDelimiterInclusive`359/// * `takeDelimiterInclusive`
441/// * `peekDelimiterExclusive`360/// * `peekDelimiterExclusive`
442pub fn takeDelimiterExclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 {361pub fn takeDelimiterExclusive(br: *BufferedReader, delimiter: u8) DelimiterExclusiveError![]u8 {
443 const result_unless_end = try br.peekDelimiterInclusiveUnlessEnd(delimiter);362 const result = br.peekDelimiterInclusiveUnlessEnd(delimiter) catch |err| switch (err) {
444 const result = result_unless_end orelse {363 error.EndOfStream => {
445 br.toss(br.storage.end);364 br.toss(br.storage.end);
446 return br.storage.buffer[0..br.storage.end];365 return br.storage.buffer[0..br.storage.end];
366 },
367 else => |e| return e,
447 };368 };
448 br.toss(result.len);369 br.toss(result.len);
449 return result[0 .. result.len - 1];370 return result[0 .. result.len - 1];
450}371}
451372
452pub fn peekDelimiterExclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 {373pub fn peekDelimiterExclusive(br: *BufferedReader, delimiter: u8) DelimiterExclusiveError![]u8 {
453 const result_unless_end = try br.peekDelimiterInclusiveUnlessEnd(delimiter);374 const result = br.peekDelimiterInclusiveUnlessEnd(delimiter) catch |err| switch (err) {
454 const result = result_unless_end orelse return br.storage.buffer[0..br.storage.end];375 error.EndOfStream => return br.storage.buffer[0..br.storage.end],
376 else => |e| return e,
377 };
455 return result[0 .. result.len - 1];378 return result[0 .. result.len - 1];
456}379}
457380
458fn peekDelimiterInclusiveUnlessEnd(br: *BufferedReader, delimiter: u8) anyerror!?[]u8 {381fn peekDelimiterInclusiveUnlessEnd(br: *BufferedReader, delimiter: u8) DelimiterInclusiveError!?[]u8 {
459 const storage = &br.storage;382 const storage = &br.storage;
460 const buffer = storage.buffer[0..storage.end];383 const buffer = storage.buffer[0..storage.end];
461 const seek = br.seek;384 const seek = br.seek;
...@@ -469,21 +392,29 @@ fn peekDelimiterInclusiveUnlessEnd(br: *BufferedReader, delimiter: u8) anyerror!...@@ -469,21 +392,29 @@ fn peekDelimiterInclusiveUnlessEnd(br: *BufferedReader, delimiter: u8) anyerror!
469 storage.end = i;392 storage.end = i;
470 br.seek = 0;393 br.seek = 0;
471 while (i < storage.buffer.len) {394 while (i < storage.buffer.len) {
472 const status = try br.unbuffered_reader.read(storage, .unlimited);395 const eos = eos: {
473 if (std.mem.indexOfScalarPos(u8, storage.buffer[0..storage.end], i, delimiter)) |end| return storage.buffer[0 .. end + 1];396 const read_len = br.unbuffered_reader.read(storage, .unlimited) catch |err| switch (err) {
474 if (status.end) return null;397 error.WriteFailed => storage.end - i,
398 error.ReadFailed => return error.ReadFailed,
399 error.EndOfStream => break :eos true,
400 };
401 assert(read_len == storage.end - i);
402 break :eos false;
403 };
404 if (std.mem.indexOfScalarPos(u8, storage.buffer[0..storage.end], i, delimiter)) |end| {
405 return storage.buffer[0 .. end + 1];
406 }
407 if (eos) return error.EndOfStream;
475 i = storage.end;408 i = storage.end;
476 }409 }
477 return error.StreamTooLong;410 return error.StreamTooLong;
478}411}
479412
480/// Appends to `bw` contents by reading from the stream until `delimiter` is found.413/// Appends to `bw` contents by reading from the stream until `delimiter` is
481/// Does not write the delimiter itself.414/// found. Does not write the delimiter itself.
482///
483/// If stream ends before delimiter found, returns `error.EndOfStream`.
484///415///
485/// Returns number of bytes streamed.416/// Returns number of bytes streamed.
486pub fn streamReadDelimiter(br: *BufferedReader, bw: *std.io.BufferedWriter, delimiter: u8) anyerror!usize {417pub fn streamReadDelimiter(br: *BufferedReader, bw: *BufferedWriter, delimiter: u8) Reader.Error!usize {
487 _ = br;418 _ = br;
488 _ = bw;419 _ = bw;
489 _ = delimiter;420 _ = delimiter;
...@@ -495,29 +426,35 @@ pub fn streamReadDelimiter(br: *BufferedReader, bw: *std.io.BufferedWriter, deli...@@ -495,29 +426,35 @@ pub fn streamReadDelimiter(br: *BufferedReader, bw: *std.io.BufferedWriter, deli
495///426///
496/// Succeeds if stream ends before delimiter found.427/// Succeeds if stream ends before delimiter found.
497///428///
498/// Returns number of bytes streamed as well as whether the input reached the end.429/// Returns number of bytes streamed. The end is not signaled to the writer.
499/// The end is not signaled to the writer.
500pub fn streamReadDelimiterExclusive(430pub fn streamReadDelimiterExclusive(
501 br: *BufferedReader,431 br: *BufferedReader,
502 bw: *std.io.BufferedWriter,432 bw: *BufferedWriter,
503 delimiter: u8,433 delimiter: u8,
504) anyerror!Reader.Status {434) Reader.ShortError!usize {
505 _ = br;435 _ = br;
506 _ = bw;436 _ = bw;
507 _ = delimiter;437 _ = delimiter;
508 @panic("TODO");438 @panic("TODO");
509}439}
510440
441pub const StreamDelimiterLimitedError = Reader.ShortError || error{
442 /// Stream ended before the delimiter was found.
443 EndOfStream,
444 /// The delimiter was not found within the limit.
445 StreamTooLong,
446};
447
511/// Appends to `bw` contents by reading from the stream until `delimiter` is found.448/// Appends to `bw` contents by reading from the stream until `delimiter` is found.
512/// Does not write the delimiter itself.449/// Does not write the delimiter itself.
513///450//
514/// If `limit` is exceeded, returns `error.StreamTooLong`.451/// Returns number of bytes streamed.
515pub fn streamReadDelimiterLimited(452pub fn streamReadDelimiterLimited(
516 br: *BufferedReader,453 br: *BufferedReader,
517 bw: *BufferedWriter,454 bw: *BufferedWriter,
518 delimiter: u8,455 delimiter: u8,
519 limit: usize,456 limit: Reader.Limit,
520) anyerror!void {457) StreamDelimiterLimitedError!usize {
521 _ = br;458 _ = br;
522 _ = bw;459 _ = bw;
523 _ = delimiter;460 _ = delimiter;
...@@ -529,7 +466,7 @@ pub fn streamReadDelimiterLimited(...@@ -529,7 +466,7 @@ pub fn streamReadDelimiterLimited(
529/// including the delimiter.466/// including the delimiter.
530///467///
531/// If end of stream is found, this function succeeds.468/// If end of stream is found, this function succeeds.
532pub fn discardDelimiterExclusive(br: *BufferedReader, delimiter: u8) anyerror!void {469pub fn discardDelimiterInclusive(br: *BufferedReader, delimiter: u8) Reader.Error!void {
533 _ = br;470 _ = br;
534 _ = delimiter;471 _ = delimiter;
535 @panic("TODO");472 @panic("TODO");
...@@ -538,8 +475,8 @@ pub fn discardDelimiterExclusive(br: *BufferedReader, delimiter: u8) anyerror!vo...@@ -538,8 +475,8 @@ pub fn discardDelimiterExclusive(br: *BufferedReader, delimiter: u8) anyerror!vo
538/// Reads from the stream until specified byte is found, discarding all data,475/// Reads from the stream until specified byte is found, discarding all data,
539/// excluding the delimiter.476/// excluding the delimiter.
540///477///
541/// If end of stream is found, `error.EndOfStream` is returned.478/// Succeeds if stream ends before delimiter found.
542pub fn discardDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror!void {479pub fn discardDelimiterExclusive(br: *BufferedReader, delimiter: u8) Reader.ShortError!void {
543 _ = br;480 _ = br;
544 _ = delimiter;481 _ = delimiter;
545 @panic("TODO");482 @panic("TODO");
...@@ -548,69 +485,75 @@ pub fn discardDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror!vo...@@ -548,69 +485,75 @@ pub fn discardDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror!vo
548/// Fills the buffer such that it contains at least `n` bytes, without485/// Fills the buffer such that it contains at least `n` bytes, without
549/// advancing the seek position.486/// advancing the seek position.
550///487///
551/// Returns `false` if and only if there are fewer than `n` bytes remaining.488/// Returns `error.EndOfStream` if and only if there are fewer than `n` bytes
489/// remaining.
552///490///
553/// Asserts buffer capacity is at least `n`.491/// Asserts buffer capacity is at least `n`.
554pub fn fill(br: *BufferedReader, n: usize) anyerror!bool {492pub fn fill(br: *BufferedReader, n: usize) Reader.Error!void {
555 const storage = &br.storage;493 const storage = &br.storage;
556 assert(n <= storage.buffer.len);494 assert(n <= storage.buffer.len);
557 const buffer = storage.buffer[0..storage.end];495 const buffer = storage.buffer[0..storage.end];
558 const seek = br.seek;496 const seek = br.seek;
559 if (seek + n <= buffer.len) {497 if (seek + n <= buffer.len) {
560 @branchHint(.likely);498 @branchHint(.likely);
561 return true;499 return;
562 }500 }
563 const remainder = buffer[seek..];501 const remainder = buffer[seek..];
564 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);502 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
565 storage.end = remainder.len;503 storage.end = remainder.len;
566 br.seek = 0;504 br.seek = 0;
567 while (true) {505 while (true) {
568 const status = try br.unbuffered_reader.read(storage, .unlimited);506 const read_len = br.unbuffered_reader.read(storage, .unlimited) catch |err| switch (err) {
569 if (n <= storage.end) return true;507 error.WriteFailed => storage.end - remainder.len,
570 if (status.end) return false;508 else => |e| return e,
509 };
510 assert(storage.end == remainder.len + read_len);
511 if (n <= storage.end) return;
571 }512 }
572}513}
573514
574/// Reads 1 byte from the stream or returns `error.EndOfStream`.515/// Reads 1 byte from the stream or returns `error.EndOfStream`.
575pub fn takeByte(br: *BufferedReader) anyerror!u8 {516pub fn takeByte(br: *BufferedReader) Reader.Error!u8 {
576 const storage = &br.storage;517 const storage = &br.storage;
577 const buffer = storage.buffer[0..storage.end];518 const buffer = storage.buffer[0..storage.end];
578 const seek = br.seek;519 const seek = br.seek;
579 if (seek >= buffer.len) {520 if (seek >= buffer.len) {
580 @branchHint(.unlikely);521 @branchHint(.unlikely);
581 const filled = try fill(br, 1);522 try fill(br, 1);
582 if (!filled) return error.EndOfStream;
583 }523 }
584 br.seek = seek + 1;524 br.seek = seek + 1;
585 return buffer[seek];525 return buffer[seek];
586}526}
587527
588/// Same as `takeByte` except the returned byte is signed.528/// Same as `takeByte` except the returned byte is signed.
589pub fn takeByteSigned(br: *BufferedReader) anyerror!i8 {529pub fn takeByteSigned(br: *BufferedReader) Reader.Error!i8 {
590 return @bitCast(try br.takeByte());530 return @bitCast(try br.takeByte());
591}531}
592532
593/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.533/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
594pub inline fn takeInt(br: *BufferedReader, comptime T: type, endian: std.builtin.Endian) anyerror!T {534pub inline fn takeInt(br: *BufferedReader, comptime T: type, endian: std.builtin.Endian) Reader.Error!T {
595 const n = @divExact(@typeInfo(T).int.bits, 8);535 const n = @divExact(@typeInfo(T).int.bits, 8);
596 return std.mem.readInt(T, try br.takeArray(n), endian);536 return std.mem.readInt(T, try br.takeArray(n), endian);
597}537}
598538
599/// Asserts the buffer was initialized with a capacity at least `n`.539/// Asserts the buffer was initialized with a capacity at least `n`.
600pub fn takeVarInt(br: *BufferedReader, comptime Int: type, endian: std.builtin.Endian, n: usize) anyerror!Int {540pub fn takeVarInt(br: *BufferedReader, comptime Int: type, endian: std.builtin.Endian, n: usize) Reader.Error!Int {
601 assert(n <= @sizeOf(Int));541 assert(n <= @sizeOf(Int));
602 return std.mem.readVarInt(Int, try br.take(n), endian);542 return std.mem.readVarInt(Int, try br.take(n), endian);
603}543}
604544
605/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.545/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
606pub fn takeStruct(br: *BufferedReader, comptime T: type) anyerror!*align(1) T {546pub fn takeStruct(br: *BufferedReader, comptime T: type) Reader.Error!*align(1) T {
607 // Only extern and packed structs have defined in-memory layout.547 // Only extern and packed structs have defined in-memory layout.
608 comptime assert(@typeInfo(T).@"struct".layout != .auto);548 comptime assert(@typeInfo(T).@"struct".layout != .auto);
609 return @ptrCast(try br.takeArray(@sizeOf(T)));549 return @ptrCast(try br.takeArray(@sizeOf(T)));
610}550}
611551
612/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.552/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
613pub fn takeStructEndian(br: *BufferedReader, comptime T: type, endian: std.builtin.Endian) anyerror!T {553///
554/// This function is inline to avoid referencing `std.mem.byteSwapAllFields`
555/// when `endian` is comptime-known and matches the host endianness.
556pub inline fn takeStructEndian(br: *BufferedReader, comptime T: type, endian: std.builtin.Endian) Reader.Error!T {
614 var res = (try br.takeStruct(T)).*;557 var res = (try br.takeStruct(T)).*;
615 if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);558 if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
616 return res;559 return res;
...@@ -621,14 +564,16 @@ pub fn takeStructEndian(br: *BufferedReader, comptime T: type, endian: std.built...@@ -621,14 +564,16 @@ pub fn takeStructEndian(br: *BufferedReader, comptime T: type, endian: std.built
621/// it. Otherwise, returns `error.InvalidEnumTag`.564/// it. Otherwise, returns `error.InvalidEnumTag`.
622///565///
623/// Asserts the buffer was initialized with a capacity at least `@sizeOf(Enum)`.566/// Asserts the buffer was initialized with a capacity at least `@sizeOf(Enum)`.
624pub fn takeEnum(br: *BufferedReader, comptime Enum: type, endian: std.builtin.Endian) anyerror!Enum {567pub fn takeEnum(br: *BufferedReader, comptime Enum: type, endian: std.builtin.Endian) Reader.Error!Enum {
625 const Tag = @typeInfo(Enum).@"enum".tag_type;568 const Tag = @typeInfo(Enum).@"enum".tag_type;
626 const int = try br.takeInt(Tag, endian);569 const int = try br.takeInt(Tag, endian);
627 return std.meta.intToEnum(Enum, int);570 return std.meta.intToEnum(Enum, int);
628}571}
629572
573pub const TakeLeb128Error = Reader.Error || error{Overflow};
574
630/// Read a single LEB128 value as type T, or `error.Overflow` if the value cannot fit.575/// Read a single LEB128 value as type T, or `error.Overflow` if the value cannot fit.
631pub fn takeLeb128(br: *BufferedReader, comptime Result: type) anyerror!Result {576pub fn takeLeb128(br: *BufferedReader, comptime Result: type) TakeLeb128Error!Result {
632 const result_info = @typeInfo(Result).int;577 const result_info = @typeInfo(Result).int;
633 return std.math.cast(Result, try br.takeMultipleOf7Leb128(@Type(.{ .int = .{578 return std.math.cast(Result, try br.takeMultipleOf7Leb128(@Type(.{ .int = .{
634 .signedness = result_info.signedness,579 .signedness = result_info.signedness,
...@@ -636,7 +581,7 @@ pub fn takeLeb128(br: *BufferedReader, comptime Result: type) anyerror!Result {...@@ -636,7 +581,7 @@ pub fn takeLeb128(br: *BufferedReader, comptime Result: type) anyerror!Result {
636 } }))) orelse error.Overflow;581 } }))) orelse error.Overflow;
637}582}
638583
639fn takeMultipleOf7Leb128(br: *BufferedReader, comptime Result: type) anyerror!Result {584fn takeMultipleOf7Leb128(br: *BufferedReader, comptime Result: type) TakeLeb128Error!Result {
640 const result_info = @typeInfo(Result).int;585 const result_info = @typeInfo(Result).int;
641 comptime assert(result_info.bits % 7 == 0);586 comptime assert(result_info.bits % 7 == 0);
642 var remaining_bits: std.math.Log2IntCeil(Result) = result_info.bits;587 var remaining_bits: std.math.Log2IntCeil(Result) = result_info.bits;
...@@ -708,7 +653,7 @@ test discard {...@@ -708,7 +653,7 @@ test discard {
708 try testing.expectError(error.EndOfStream, br.discard(1));653 try testing.expectError(error.EndOfStream, br.discard(1));
709}654}
710655
711test discardUntilEnd {656test discardRemaining {
712 return error.Unimplemented;657 return error.Unimplemented;
713}658}
714659
...@@ -795,3 +740,7 @@ test takeEnum {...@@ -795,3 +740,7 @@ test takeEnum {
795test takeLeb128 {740test takeLeb128 {
796 return error.Unimplemented;741 return error.Unimplemented;
797}742}
743
744test readShort {
745 return error.Unimplemented;
746}
lib/std/io/BufferedWriter.zig+93-70
...@@ -35,19 +35,19 @@ pub fn writer(bw: *BufferedWriter) Writer {...@@ -35,19 +35,19 @@ pub fn writer(bw: *BufferedWriter) Writer {
35 return .{35 return .{
36 .context = bw,36 .context = bw,
37 .vtable = &.{37 .vtable = &.{
38 .writeSplat = passthru_writeSplat,38 .writeSplat = passthruWriteSplat,
39 .writeFile = passthru_writeFile,39 .writeFile = passthruWriteFile,
40 },40 },
41 };41 };
42}42}
4343
44const fixed_vtable: Writer.VTable = .{44const fixed_vtable: Writer.VTable = .{
45 .writeSplat = fixed_writeSplat,45 .writeSplat = fixedWriteSplat,
46 .writeFile = Writer.unimplemented_writeFile,46 .writeFile = Writer.failingWriteFile,
47};47};
4848
49/// Replaces the `BufferedWriter` with one that writes to `buffer` and returns49/// Replaces the `BufferedWriter` with one that writes to `buffer` and returns
50/// `error.NoSpaceLeft` when it is full. `end` and `count` will always be50/// `error.WriteFailed` when it is full. `end` and `count` will always be
51/// equal.51/// equal.
52pub fn initFixed(bw: *BufferedWriter, buffer: []u8) void {52pub fn initFixed(bw: *BufferedWriter, buffer: []u8) void {
53 bw.* = .{53 bw.* = .{
...@@ -72,10 +72,10 @@ pub fn reset(bw: *BufferedWriter) void {...@@ -72,10 +72,10 @@ pub fn reset(bw: *BufferedWriter) void {
72 bw.count = 0;72 bw.count = 0;
73}73}
7474
75pub fn flush(bw: *BufferedWriter) anyerror!void {75pub fn flush(bw: *BufferedWriter) Writer.Error!void {
76 const send_buffer = bw.buffer[0..bw.end];76 const send_buffer = bw.buffer[0..bw.end];
77 var index: usize = 0;77 var index: usize = 0;
78 while (index < send_buffer.len) index += try bw.unbuffered_writer.writev(&.{send_buffer[index..]});78 while (index < send_buffer.len) index += try bw.unbuffered_writer.writeVec(&.{send_buffer[index..]});
79 bw.end = 0;79 bw.end = 0;
80}80}
8181
...@@ -84,7 +84,7 @@ pub fn unusedCapacitySlice(bw: *const BufferedWriter) []u8 {...@@ -84,7 +84,7 @@ pub fn unusedCapacitySlice(bw: *const BufferedWriter) []u8 {
84}84}
8585
86/// Asserts the provided buffer has total capacity enough for `minimum_length`.86/// Asserts the provided buffer has total capacity enough for `minimum_length`.
87pub fn writableSlice(bw: *BufferedWriter, minimum_length: usize) anyerror![]u8 {87pub fn writableSlice(bw: *BufferedWriter, minimum_length: usize) Writer.Error![]u8 {
88 assert(bw.buffer.len >= minimum_length);88 assert(bw.buffer.len >= minimum_length);
89 const cap_slice = bw.buffer[bw.end..];89 const cap_slice = bw.buffer[bw.end..];
90 if (cap_slice.len >= minimum_length) {90 if (cap_slice.len >= minimum_length) {
...@@ -92,7 +92,7 @@ pub fn writableSlice(bw: *BufferedWriter, minimum_length: usize) anyerror![]u8 {...@@ -92,7 +92,7 @@ pub fn writableSlice(bw: *BufferedWriter, minimum_length: usize) anyerror![]u8 {
92 return cap_slice;92 return cap_slice;
93 }93 }
94 const buffer = bw.buffer[0..bw.end];94 const buffer = bw.buffer[0..bw.end];
95 const n = try bw.unbuffered_writer.writev(&.{buffer});95 const n = try bw.unbuffered_writer.writeVec(&.{buffer});
96 if (n == buffer.len) {96 if (n == buffer.len) {
97 @branchHint(.likely);97 @branchHint(.likely);
98 bw.end = 0;98 bw.end = 0;
...@@ -115,11 +115,11 @@ pub fn advance(bw: *BufferedWriter, n: usize) void {...@@ -115,11 +115,11 @@ pub fn advance(bw: *BufferedWriter, n: usize) void {
115}115}
116116
117/// The `data` parameter is mutable because this function needs to mutate the117/// The `data` parameter is mutable because this function needs to mutate the
118/// fields in order to handle partial writes from `Writer.VTable.writev`.118/// fields in order to handle partial writes from `Writer.VTable.writeVec`.
119pub fn writevAll(bw: *BufferedWriter, data: [][]const u8) anyerror!void {119pub fn writeVecAll(bw: *BufferedWriter, data: [][]const u8) Writer.Error!void {
120 var i: usize = 0;120 var i: usize = 0;
121 while (true) {121 while (true) {
122 var n = try passthru_writeSplat(bw, data[i..], 1);122 var n = try passthruWriteSplat(bw, data[i..], 1);
123 const len = data[i].len;123 const len = data[i].len;
124 while (n >= len) {124 while (n >= len) {
125 n -= len;125 n -= len;
...@@ -130,15 +130,15 @@ pub fn writevAll(bw: *BufferedWriter, data: [][]const u8) anyerror!void {...@@ -130,15 +130,15 @@ pub fn writevAll(bw: *BufferedWriter, data: [][]const u8) anyerror!void {
130 }130 }
131}131}
132132
133pub fn writeSplat(bw: *BufferedWriter, data: []const []const u8, splat: usize) anyerror!usize {133pub fn writeSplat(bw: *BufferedWriter, data: []const []const u8, splat: usize) Writer.Error!usize {
134 return passthru_writeSplat(bw, data, splat);134 return passthruWriteSplat(bw, data, splat);
135}135}
136136
137pub fn writev(bw: *BufferedWriter, data: []const []const u8) anyerror!usize {137pub fn writeVec(bw: *BufferedWriter, data: []const []const u8) Writer.Error!usize {
138 return passthru_writeSplat(bw, data, 1);138 return passthruWriteSplat(bw, data, 1);
139}139}
140140
141fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize {141fn passthruWriteSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) Writer.Error!usize {
142 const bw: *BufferedWriter = @alignCast(@ptrCast(context));142 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
143 const buffer = bw.buffer;143 const buffer = bw.buffer;
144 const start_end = bw.end;144 const start_end = bw.end;
...@@ -258,11 +258,11 @@ fn track(count: *usize, n: usize) usize {...@@ -258,11 +258,11 @@ fn track(count: *usize, n: usize) usize {
258/// When this function is called it means the buffer got full, so it's time258/// When this function is called it means the buffer got full, so it's time
259/// to return an error. However, we still need to make sure all of the259/// to return an error. However, we still need to make sure all of the
260/// available buffer has been filled.260/// available buffer has been filled.
261fn fixed_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize {261fn fixedWriteSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) Writer.Error!usize {
262 const bw: *BufferedWriter = @alignCast(@ptrCast(context));262 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
263 for (data) |bytes| {263 for (data) |bytes| {
264 const dest = bw.buffer[bw.end..];264 const dest = bw.buffer[bw.end..];
265 if (dest.len == 0) return error.NoSpaceLeft;265 if (dest.len == 0) return error.WriteFailed;
266 const len = @min(bytes.len, dest.len);266 const len = @min(bytes.len, dest.len);
267 @memcpy(dest[0..len], bytes[0..len]);267 @memcpy(dest[0..len], bytes[0..len]);
268 bw.end += len;268 bw.end += len;
...@@ -277,16 +277,16 @@ fn fixed_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize...@@ -277,16 +277,16 @@ fn fixed_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize
277 }277 }
278 bw.end = bw.buffer.len;278 bw.end = bw.buffer.len;
279 bw.count = bw.end;279 bw.count = bw.end;
280 return error.NoSpaceLeft;280 return error.WriteFailed;
281}281}
282282
283pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize {283pub fn write(bw: *BufferedWriter, bytes: []const u8) Writer.Error!usize {
284 const buffer = bw.buffer;284 const buffer = bw.buffer;
285 const end = bw.end;285 const end = bw.end;
286 const new_end = end + bytes.len;286 const new_end = end + bytes.len;
287 if (new_end > buffer.len) {287 if (new_end > buffer.len) {
288 var data: [2][]const u8 = .{ buffer[0..end], bytes };288 var data: [2][]const u8 = .{ buffer[0..end], bytes };
289 const n = try bw.unbuffered_writer.writev(&data);289 const n = try bw.unbuffered_writer.writeVec(&data);
290 if (n < end) {290 if (n < end) {
291 @branchHint(.unlikely);291 @branchHint(.unlikely);
292 const remainder = buffer[n..end];292 const remainder = buffer[n..end];
...@@ -304,16 +304,16 @@ pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize {...@@ -304,16 +304,16 @@ pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize {
304304
305/// Calls `write` as many times as necessary such that all of `bytes` are305/// Calls `write` as many times as necessary such that all of `bytes` are
306/// transferred.306/// transferred.
307pub fn writeAll(bw: *BufferedWriter, bytes: []const u8) anyerror!void {307pub fn writeAll(bw: *BufferedWriter, bytes: []const u8) Writer.Error!void {
308 var index: usize = 0;308 var index: usize = 0;
309 while (index < bytes.len) index += try bw.write(bytes[index..]);309 while (index < bytes.len) index += try bw.write(bytes[index..]);
310}310}
311311
312pub fn print(bw: *BufferedWriter, comptime format: []const u8, args: anytype) anyerror!void {312pub fn print(bw: *BufferedWriter, comptime format: []const u8, args: anytype) Writer.Error!void {
313 try std.fmt.format(bw, format, args);313 try std.fmt.format(bw, format, args);
314}314}
315315
316pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void {316pub fn writeByte(bw: *BufferedWriter, byte: u8) Writer.Error!void {
317 const buffer = bw.buffer[0..bw.end];317 const buffer = bw.buffer[0..bw.end];
318 if (buffer.len < bw.buffer.len) {318 if (buffer.len < bw.buffer.len) {
319 @branchHint(.likely);319 @branchHint(.likely);
...@@ -324,7 +324,7 @@ pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void {...@@ -324,7 +324,7 @@ pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void {
324 }324 }
325 var buffers: [2][]const u8 = .{ buffer, &.{byte} };325 var buffers: [2][]const u8 = .{ buffer, &.{byte} };
326 while (true) {326 while (true) {
327 const n = try bw.unbuffered_writer.writev(&buffers);327 const n = try bw.unbuffered_writer.writeVec(&buffers);
328 if (n == 0) {328 if (n == 0) {
329 @branchHint(.unlikely);329 @branchHint(.unlikely);
330 continue;330 continue;
...@@ -352,7 +352,7 @@ pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void {...@@ -352,7 +352,7 @@ pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void {
352352
353/// Writes the same byte many times, performing the underlying write call as353/// Writes the same byte many times, performing the underlying write call as
354/// many times as necessary.354/// many times as necessary.
355pub fn splatByteAll(bw: *BufferedWriter, byte: u8, n: usize) anyerror!void {355pub fn splatByteAll(bw: *BufferedWriter, byte: u8, n: usize) Writer.Error!void {
356 var remaining: usize = n;356 var remaining: usize = n;
357 while (remaining > 0) remaining -= try bw.splatByte(byte, remaining);357 while (remaining > 0) remaining -= try bw.splatByte(byte, remaining);
358}358}
...@@ -360,13 +360,13 @@ pub fn splatByteAll(bw: *BufferedWriter, byte: u8, n: usize) anyerror!void {...@@ -360,13 +360,13 @@ pub fn splatByteAll(bw: *BufferedWriter, byte: u8, n: usize) anyerror!void {
360/// Writes the same byte many times, allowing short writes.360/// Writes the same byte many times, allowing short writes.
361///361///
362/// Does maximum of one underlying `Writer.VTable.writeSplat`.362/// Does maximum of one underlying `Writer.VTable.writeSplat`.
363pub fn splatByte(bw: *BufferedWriter, byte: u8, n: usize) anyerror!usize {363pub fn splatByte(bw: *BufferedWriter, byte: u8, n: usize) Writer.Error!usize {
364 return passthru_writeSplat(bw, &.{&.{byte}}, n);364 return passthruWriteSplat(bw, &.{&.{byte}}, n);
365}365}
366366
367/// Writes the same slice many times, performing the underlying write call as367/// Writes the same slice many times, performing the underlying write call as
368/// many times as necessary.368/// many times as necessary.
369pub fn splatBytesAll(bw: *BufferedWriter, bytes: []const u8, splat: usize) anyerror!void {369pub fn splatBytesAll(bw: *BufferedWriter, bytes: []const u8, splat: usize) Writer.Error!void {
370 var remaining_bytes: usize = bytes.len * splat;370 var remaining_bytes: usize = bytes.len * splat;
371 remaining_bytes -= try bw.splatBytes(bytes, splat);371 remaining_bytes -= try bw.splatBytes(bytes, splat);
372 while (remaining_bytes > 0) {372 while (remaining_bytes > 0) {
...@@ -378,26 +378,28 @@ pub fn splatBytesAll(bw: *BufferedWriter, bytes: []const u8, splat: usize) anyer...@@ -378,26 +378,28 @@ pub fn splatBytesAll(bw: *BufferedWriter, bytes: []const u8, splat: usize) anyer
378378
379/// Writes the same slice many times, allowing short writes.379/// Writes the same slice many times, allowing short writes.
380///380///
381/// Does maximum of one underlying `Writer.VTable.writev`.381/// Does maximum of one underlying `Writer.VTable.writeVec`.
382pub fn splatBytes(bw: *BufferedWriter, bytes: []const u8, n: usize) anyerror!usize {382pub fn splatBytes(bw: *BufferedWriter, bytes: []const u8, n: usize) Writer.Error!usize {
383 return passthru_writeSplat(bw, &.{bytes}, n);383 return passthruWriteSplat(bw, &.{bytes}, n);
384}384}
385385
386/// Asserts the `buffer` was initialized with a capacity of at least `@sizeOf(T)` bytes.386/// Asserts the `buffer` was initialized with a capacity of at least `@sizeOf(T)` bytes.
387pub inline fn writeInt(bw: *BufferedWriter, comptime T: type, value: T, endian: std.builtin.Endian) anyerror!void {387pub inline fn writeInt(bw: *BufferedWriter, comptime T: type, value: T, endian: std.builtin.Endian) Writer.Error!void {
388 var bytes: [@divExact(@typeInfo(T).int.bits, 8)]u8 = undefined;388 var bytes: [@divExact(@typeInfo(T).int.bits, 8)]u8 = undefined;
389 std.mem.writeInt(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value, endian);389 std.mem.writeInt(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value, endian);
390 return bw.writeAll(&bytes);390 return bw.writeAll(&bytes);
391}391}
392392
393pub fn writeStruct(bw: *BufferedWriter, value: anytype) anyerror!void {393pub fn writeStruct(bw: *BufferedWriter, value: anytype) Writer.Error!void {
394 // Only extern and packed structs have defined in-memory layout.394 // Only extern and packed structs have defined in-memory layout.
395 comptime assert(@typeInfo(@TypeOf(value)).@"struct".layout != .auto);395 comptime assert(@typeInfo(@TypeOf(value)).@"struct".layout != .auto);
396 return bw.writeAll(std.mem.asBytes(&value));396 return bw.writeAll(std.mem.asBytes(&value));
397}397}
398398
399pub fn writeStructEndian(bw: *BufferedWriter, value: anytype, endian: std.builtin.Endian) anyerror!void {399/// The function is inline to avoid the dead code in case `endian` is
400 // TODO: make sure this value is not a reference type400/// comptime-known and matches host endianness.
401/// TODO: make sure this value is not a reference type
402pub inline fn writeStructEndian(bw: *BufferedWriter, value: anytype, endian: std.builtin.Endian) Writer.Error!void {
401 if (native_endian == endian) {403 if (native_endian == endian) {
402 return bw.writeStruct(value);404 return bw.writeStruct(value);
403 } else {405 } else {
...@@ -407,6 +409,27 @@ pub fn writeStructEndian(bw: *BufferedWriter, value: anytype, endian: std.builti...@@ -407,6 +409,27 @@ pub fn writeStructEndian(bw: *BufferedWriter, value: anytype, endian: std.builti
407 }409 }
408}410}
409411
412pub inline fn writeArrayEndian(
413 bw: *BufferedWriter,
414 Elem: type,
415 array: []const Elem,
416 endian: std.builtin.Endian,
417) Writer.Error!void {
418 if (native_endian == endian) {
419 return writeAll(bw, @ptrCast(array));
420 } else {
421 return bw.writeArraySwap(bw, Elem, array);
422 }
423}
424
425/// Asserts that the buffer storage capacity is at least enough to store `@sizeOf(Elem)`
426pub fn writeArraySwap(bw: *BufferedWriter, Elem: type, array: []const Elem) Writer.Error!void {
427 // copy to storage first, then swap in place
428 _ = bw;
429 _ = array;
430 @panic("TODO");
431}
432
410pub fn writeFile(433pub fn writeFile(
411 bw: *BufferedWriter,434 bw: *BufferedWriter,
412 file: std.fs.File,435 file: std.fs.File,
...@@ -414,18 +437,18 @@ pub fn writeFile(...@@ -414,18 +437,18 @@ pub fn writeFile(
414 limit: Writer.Limit,437 limit: Writer.Limit,
415 headers_and_trailers: []const []const u8,438 headers_and_trailers: []const []const u8,
416 headers_len: usize,439 headers_len: usize,
417) anyerror!usize {440) Writer.FileError!usize {
418 return passthru_writeFile(bw, file, offset, limit, headers_and_trailers, headers_len);441 return passthruWriteFile(bw, file, offset, limit, headers_and_trailers, headers_len);
419}442}
420443
421fn passthru_writeFile(444fn passthruWriteFile(
422 context: ?*anyopaque,445 context: ?*anyopaque,
423 file: std.fs.File,446 file: std.fs.File,
424 offset: Writer.Offset,447 offset: Writer.Offset,
425 limit: Writer.Limit,448 limit: Writer.Limit,
426 headers_and_trailers: []const []const u8,449 headers_and_trailers: []const []const u8,
427 headers_len: usize,450 headers_len: usize,
428) anyerror!usize {451) Writer.FileError!usize {
429 const bw: *BufferedWriter = @alignCast(@ptrCast(context));452 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
430 const buffer = bw.buffer;453 const buffer = bw.buffer;
431 if (buffer.len == 0) return track(454 if (buffer.len == 0) return track(
...@@ -468,8 +491,8 @@ fn passthru_writeFile(...@@ -468,8 +491,8 @@ fn passthru_writeFile(
468 bw.end = 0;491 bw.end = 0;
469 return track(&bw.count, n - start_end);492 return track(&bw.count, n - start_end);
470 }493 }
471 // Have not made it past the headers yet; must call `writev`.494 // Have not made it past the headers yet; must call `writeVec`.
472 const n = try bw.unbuffered_writer.writev(buffers[0 .. buffers_len + 1]);495 const n = try bw.unbuffered_writer.writeVec(buffers[0 .. buffers_len + 1]);
473 if (n < end) {496 if (n < end) {
474 @branchHint(.unlikely);497 @branchHint(.unlikely);
475 const remainder = buffer[n..end];498 const remainder = buffer[n..end];
...@@ -505,7 +528,7 @@ pub const WriteFileOptions = struct {...@@ -505,7 +528,7 @@ pub const WriteFileOptions = struct {
505 /// size here will save one syscall.528 /// size here will save one syscall.
506 limit: Writer.Limit = .unlimited,529 limit: Writer.Limit = .unlimited,
507 /// Headers and trailers must be passed together so that in case `len` is530 /// Headers and trailers must be passed together so that in case `len` is
508 /// zero, they can be forwarded directly to `Writer.VTable.writev`.531 /// zero, they can be forwarded directly to `Writer.VTable.writeVec`.
509 ///532 ///
510 /// The parameter is mutable because this function needs to mutate the533 /// The parameter is mutable because this function needs to mutate the
511 /// fields in order to handle partial writes from `Writer.VTable.writeFile`.534 /// fields in order to handle partial writes from `Writer.VTable.writeFile`.
...@@ -515,11 +538,11 @@ pub const WriteFileOptions = struct {...@@ -515,11 +538,11 @@ pub const WriteFileOptions = struct {
515 headers_len: usize = 0,538 headers_len: usize = 0,
516};539};
517540
518pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOptions) anyerror!void {541pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOptions) Writer.FileError!void {
519 const headers_and_trailers = options.headers_and_trailers;542 const headers_and_trailers = options.headers_and_trailers;
520 const headers = headers_and_trailers[0..options.headers_len];543 const headers = headers_and_trailers[0..options.headers_len];
521 switch (options.limit) {544 switch (options.limit) {
522 .nothing => return bw.writevAll(headers_and_trailers),545 .nothing => return bw.writeVecAll(headers_and_trailers),
523 .unlimited => {546 .unlimited => {
524 // When reading the whole file, we cannot include the trailers in the547 // When reading the whole file, we cannot include the trailers in the
525 // call that reads from the file handle, because we have no way to548 // call that reads from the file handle, because we have no way to
...@@ -564,7 +587,7 @@ pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOp...@@ -564,7 +587,7 @@ pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOp
564 if (i >= headers_and_trailers.len) return;587 if (i >= headers_and_trailers.len) return;
565 }588 }
566 headers_and_trailers[i] = headers_and_trailers[i][n..];589 headers_and_trailers[i] = headers_and_trailers[i][n..];
567 return bw.writevAll(headers_and_trailers[i..]);590 return bw.writeVecAll(headers_and_trailers[i..]);
568 }591 }
569 offset = offset.advance(n);592 offset = offset.advance(n);
570 len -= n;593 len -= n;
...@@ -579,7 +602,7 @@ pub fn alignBuffer(...@@ -579,7 +602,7 @@ pub fn alignBuffer(
579 width: usize,602 width: usize,
580 alignment: std.fmt.Alignment,603 alignment: std.fmt.Alignment,
581 fill: u8,604 fill: u8,
582) anyerror!void {605) Writer.Error!void {
583 const padding = if (buffer.len < width) width - buffer.len else 0;606 const padding = if (buffer.len < width) width - buffer.len else 0;
584 if (padding == 0) {607 if (padding == 0) {
585 @branchHint(.likely);608 @branchHint(.likely);
...@@ -604,11 +627,11 @@ pub fn alignBuffer(...@@ -604,11 +627,11 @@ pub fn alignBuffer(
604 }627 }
605}628}
606629
607pub fn alignBufferOptions(bw: *BufferedWriter, buffer: []const u8, options: std.fmt.Options) anyerror!void {630pub fn alignBufferOptions(bw: *BufferedWriter, buffer: []const u8, options: std.fmt.Options) Writer.Error!void {
608 return bw.alignBuffer(buffer, options.width orelse buffer.len, options.alignment, options.fill);631 return bw.alignBuffer(buffer, options.width orelse buffer.len, options.alignment, options.fill);
609}632}
610633
611pub fn printAddress(bw: *BufferedWriter, value: anytype) anyerror!void {634pub fn printAddress(bw: *BufferedWriter, value: anytype) Writer.Error!void {
612 const T = @TypeOf(value);635 const T = @TypeOf(value);
613 switch (@typeInfo(T)) {636 switch (@typeInfo(T)) {
614 .pointer => |info| {637 .pointer => |info| {
...@@ -638,7 +661,7 @@ pub fn printValue(...@@ -638,7 +661,7 @@ pub fn printValue(
638 options: std.fmt.Options,661 options: std.fmt.Options,
639 value: anytype,662 value: anytype,
640 max_depth: usize,663 max_depth: usize,
641) anyerror!void {664) Writer.Error!void {
642 const T = @TypeOf(value);665 const T = @TypeOf(value);
643 const actual_fmt = comptime if (std.mem.eql(u8, fmt, ANY))666 const actual_fmt = comptime if (std.mem.eql(u8, fmt, ANY))
644 defaultFormatString(T)667 defaultFormatString(T)
...@@ -791,7 +814,7 @@ pub fn printValue(...@@ -791,7 +814,7 @@ pub fn printValue(
791 },814 },
792 else => {815 else => {
793 var buffers: [2][]const u8 = .{ @typeName(ptr_info.child), "@" };816 var buffers: [2][]const u8 = .{ @typeName(ptr_info.child), "@" };
794 try bw.writevAll(&buffers);817 try bw.writeVecAll(&buffers);
795 try bw.printIntOptions(@intFromPtr(value), 16, .lower, options);818 try bw.printIntOptions(@intFromPtr(value), 16, .lower, options);
796 return;819 return;
797 },820 },
...@@ -896,7 +919,7 @@ pub fn printInt(...@@ -896,7 +919,7 @@ pub fn printInt(
896 comptime fmt: []const u8,919 comptime fmt: []const u8,
897 options: std.fmt.Options,920 options: std.fmt.Options,
898 value: anytype,921 value: anytype,
899) anyerror!void {922) Writer.Error!void {
900 const int_value = if (@TypeOf(value) == comptime_int) blk: {923 const int_value = if (@TypeOf(value) == comptime_int) blk: {
901 const Int = std.math.IntFittingRange(value, value);924 const Int = std.math.IntFittingRange(value, value);
902 break :blk @as(Int, value);925 break :blk @as(Int, value);
...@@ -940,15 +963,15 @@ pub fn printInt(...@@ -940,15 +963,15 @@ pub fn printInt(
940 comptime unreachable;963 comptime unreachable;
941}964}
942965
943pub fn printAsciiChar(bw: *BufferedWriter, c: u8, options: std.fmt.Options) anyerror!void {966pub fn printAsciiChar(bw: *BufferedWriter, c: u8, options: std.fmt.Options) Writer.Error!void {
944 return bw.alignBufferOptions(@as(*const [1]u8, &c), options);967 return bw.alignBufferOptions(@as(*const [1]u8, &c), options);
945}968}
946969
947pub fn printAscii(bw: *BufferedWriter, bytes: []const u8, options: std.fmt.Options) anyerror!void {970pub fn printAscii(bw: *BufferedWriter, bytes: []const u8, options: std.fmt.Options) Writer.Error!void {
948 return bw.alignBufferOptions(bytes, options);971 return bw.alignBufferOptions(bytes, options);
949}972}
950973
951pub fn printUnicodeCodepoint(bw: *BufferedWriter, c: u21, options: std.fmt.Options) anyerror!void {974pub fn printUnicodeCodepoint(bw: *BufferedWriter, c: u21, options: std.fmt.Options) Writer.Error!void {
952 var buf: [4]u8 = undefined;975 var buf: [4]u8 = undefined;
953 const len = try std.unicode.utf8Encode(c, &buf);976 const len = try std.unicode.utf8Encode(c, &buf);
954 return bw.alignBufferOptions(buf[0..len], options);977 return bw.alignBufferOptions(buf[0..len], options);
...@@ -960,7 +983,7 @@ pub fn printIntOptions(...@@ -960,7 +983,7 @@ pub fn printIntOptions(
960 base: u8,983 base: u8,
961 case: std.fmt.Case,984 case: std.fmt.Case,
962 options: std.fmt.Options,985 options: std.fmt.Options,
963) anyerror!void {986) Writer.Error!void {
964 assert(base >= 2);987 assert(base >= 2);
965988
966 const int_value = if (@TypeOf(value) == comptime_int) blk: {989 const int_value = if (@TypeOf(value) == comptime_int) blk: {
...@@ -1027,7 +1050,7 @@ pub fn printFloat(...@@ -1027,7 +1050,7 @@ pub fn printFloat(
1027 comptime fmt: []const u8,1050 comptime fmt: []const u8,
1028 options: std.fmt.Options,1051 options: std.fmt.Options,
1029 value: anytype,1052 value: anytype,
1030) anyerror!void {1053) Writer.Error!void {
1031 var buf: [std.fmt.float.bufferSize(.decimal, f64)]u8 = undefined;1054 var buf: [std.fmt.float.bufferSize(.decimal, f64)]u8 = undefined;
10321055
1033 if (fmt.len > 1) invalidFmtError(fmt, value);1056 if (fmt.len > 1) invalidFmtError(fmt, value);
...@@ -1054,7 +1077,7 @@ pub fn printFloat(...@@ -1054,7 +1077,7 @@ pub fn printFloat(
1054 }1077 }
1055}1078}
10561079
1057pub fn printFloatHexadecimal(bw: *BufferedWriter, value: anytype, opt_precision: ?usize) anyerror!void {1080pub fn printFloatHexadecimal(bw: *BufferedWriter, value: anytype, opt_precision: ?usize) Writer.Error!void {
1058 if (std.math.signbit(value)) try bw.writeByte('-');1081 if (std.math.signbit(value)) try bw.writeByte('-');
1059 if (std.math.isNan(value)) return bw.writeAll("nan");1082 if (std.math.isNan(value)) return bw.writeAll("nan");
1060 if (std.math.isInf(value)) return bw.writeAll("inf");1083 if (std.math.isInf(value)) return bw.writeAll("inf");
...@@ -1168,7 +1191,7 @@ pub fn printByteSize(...@@ -1168,7 +1191,7 @@ pub fn printByteSize(
1168 value: u64,1191 value: u64,
1169 comptime units: ByteSizeUnits,1192 comptime units: ByteSizeUnits,
1170 options: std.fmt.Options,1193 options: std.fmt.Options,
1171) anyerror!void {1194) Writer.Error!void {
1172 if (value == 0) return bw.alignBufferOptions("0B", options);1195 if (value == 0) return bw.alignBufferOptions("0B", options);
1173 // The worst case in terms of space needed is 32 bytes + 3 for the suffix.1196 // The worst case in terms of space needed is 32 bytes + 3 for the suffix.
1174 var buf: [std.fmt.float.min_buffer_size + 3]u8 = undefined;1197 var buf: [std.fmt.float.min_buffer_size + 3]u8 = undefined;
...@@ -1248,12 +1271,12 @@ pub fn invalidFmtError(comptime fmt: []const u8, value: anytype) noreturn {...@@ -1248,12 +1271,12 @@ pub fn invalidFmtError(comptime fmt: []const u8, value: anytype) noreturn {
1248 @compileError("invalid format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'");1271 @compileError("invalid format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'");
1249}1272}
12501273
1251pub fn printDurationSigned(bw: *BufferedWriter, ns: i64) anyerror!void {1274pub fn printDurationSigned(bw: *BufferedWriter, ns: i64) Writer.Error!void {
1252 if (ns < 0) try bw.writeByte('-');1275 if (ns < 0) try bw.writeByte('-');
1253 return bw.printDurationUnsigned(@abs(ns));1276 return bw.printDurationUnsigned(@abs(ns));
1254}1277}
12551278
1256pub fn printDurationUnsigned(bw: *BufferedWriter, ns: u64) anyerror!void {1279pub fn printDurationUnsigned(bw: *BufferedWriter, ns: u64) Writer.Error!void {
1257 var ns_remaining = ns;1280 var ns_remaining = ns;
1258 inline for (.{1281 inline for (.{
1259 .{ .ns = 365 * std.time.ns_per_day, .sep = 'y' },1282 .{ .ns = 365 * std.time.ns_per_day, .sep = 'y' },
...@@ -1303,7 +1326,7 @@ pub fn printDurationUnsigned(bw: *BufferedWriter, ns: u64) anyerror!void {...@@ -1303,7 +1326,7 @@ pub fn printDurationUnsigned(bw: *BufferedWriter, ns: u64) anyerror!void {
1303/// Writes number of nanoseconds according to its signed magnitude:1326/// Writes number of nanoseconds according to its signed magnitude:
1304/// `[#y][#w][#d][#h][#m]#[.###][n|u|m]s`1327/// `[#y][#w][#d][#h][#m]#[.###][n|u|m]s`
1305/// `nanoseconds` must be an integer that coerces into `u64` or `i64`.1328/// `nanoseconds` must be an integer that coerces into `u64` or `i64`.
1306pub fn printDuration(bw: *BufferedWriter, nanoseconds: anytype, options: std.fmt.Options) anyerror!void {1329pub fn printDuration(bw: *BufferedWriter, nanoseconds: anytype, options: std.fmt.Options) Writer.Error!void {
1307 // worst case: "-XXXyXXwXXdXXhXXmXX.XXXs".len = 241330 // worst case: "-XXXyXXwXXdXXhXXmXX.XXXs".len = 24
1308 var buf: [24]u8 = undefined;1331 var buf: [24]u8 = undefined;
1309 var sub_bw: BufferedWriter = undefined;1332 var sub_bw: BufferedWriter = undefined;
...@@ -1315,7 +1338,7 @@ pub fn printDuration(bw: *BufferedWriter, nanoseconds: anytype, options: std.fmt...@@ -1315,7 +1338,7 @@ pub fn printDuration(bw: *BufferedWriter, nanoseconds: anytype, options: std.fmt
1315 return bw.alignBufferOptions(sub_bw.getWritten(), options);1338 return bw.alignBufferOptions(sub_bw.getWritten(), options);
1316}1339}
13171340
1318pub fn printHex(bw: *BufferedWriter, bytes: []const u8, case: std.fmt.Case) anyerror!void {1341pub fn printHex(bw: *BufferedWriter, bytes: []const u8, case: std.fmt.Case) Writer.Error!void {
1319 const charset = switch (case) {1342 const charset = switch (case) {
1320 .upper => "0123456789ABCDEF",1343 .upper => "0123456789ABCDEF",
1321 .lower => "0123456789abcdef",1344 .lower => "0123456789abcdef",
...@@ -1326,7 +1349,7 @@ pub fn printHex(bw: *BufferedWriter, bytes: []const u8, case: std.fmt.Case) anye...@@ -1326,7 +1349,7 @@ pub fn printHex(bw: *BufferedWriter, bytes: []const u8, case: std.fmt.Case) anye
1326 }1349 }
1327}1350}
13281351
1329pub fn printBase64(bw: *BufferedWriter, bytes: []const u8) anyerror!void {1352pub fn printBase64(bw: *BufferedWriter, bytes: []const u8) Writer.Error!void {
1330 var chunker = std.mem.window(u8, bytes, 3, 3);1353 var chunker = std.mem.window(u8, bytes, 3, 3);
1331 var temp: [5]u8 = undefined;1354 var temp: [5]u8 = undefined;
1332 while (chunker.next()) |chunk| {1355 while (chunker.next()) |chunk| {
...@@ -1335,7 +1358,7 @@ pub fn printBase64(bw: *BufferedWriter, bytes: []const u8) anyerror!void {...@@ -1335,7 +1358,7 @@ pub fn printBase64(bw: *BufferedWriter, bytes: []const u8) anyerror!void {
1335}1358}
13361359
1337/// Write a single unsigned integer as LEB128 to the given writer.1360/// Write a single unsigned integer as LEB128 to the given writer.
1338pub fn writeUleb128(bw: *BufferedWriter, value: anytype) anyerror!void {1361pub fn writeUleb128(bw: *BufferedWriter, value: anytype) Writer.Error!void {
1339 try bw.writeLeb128(switch (@typeInfo(@TypeOf(value))) {1362 try bw.writeLeb128(switch (@typeInfo(@TypeOf(value))) {
1340 .comptime_int => @as(std.math.IntFittingRange(0, @abs(value)), value),1363 .comptime_int => @as(std.math.IntFittingRange(0, @abs(value)), value),
1341 .int => |value_info| switch (value_info.signedness) {1364 .int => |value_info| switch (value_info.signedness) {
...@@ -1347,7 +1370,7 @@ pub fn writeUleb128(bw: *BufferedWriter, value: anytype) anyerror!void {...@@ -1347,7 +1370,7 @@ pub fn writeUleb128(bw: *BufferedWriter, value: anytype) anyerror!void {
1347}1370}
13481371
1349/// Write a single signed integer as LEB128 to the given writer.1372/// Write a single signed integer as LEB128 to the given writer.
1350pub fn writeSleb128(bw: *BufferedWriter, value: anytype) anyerror!void {1373pub fn writeSleb128(bw: *BufferedWriter, value: anytype) Writer.Error!void {
1351 try bw.writeLeb128(switch (@typeInfo(@TypeOf(value))) {1374 try bw.writeLeb128(switch (@typeInfo(@TypeOf(value))) {
1352 .comptime_int => @as(std.math.IntFittingRange(@min(value, -1), @max(0, value)), value),1375 .comptime_int => @as(std.math.IntFittingRange(@min(value, -1), @max(0, value)), value),
1353 .int => |value_info| switch (value_info.signedness) {1376 .int => |value_info| switch (value_info.signedness) {
...@@ -1359,7 +1382,7 @@ pub fn writeSleb128(bw: *BufferedWriter, value: anytype) anyerror!void {...@@ -1359,7 +1382,7 @@ pub fn writeSleb128(bw: *BufferedWriter, value: anytype) anyerror!void {
1359}1382}
13601383
1361/// Write a single integer as LEB128 to the given writer.1384/// Write a single integer as LEB128 to the given writer.
1362pub fn writeLeb128(bw: *BufferedWriter, value: anytype) anyerror!void {1385pub fn writeLeb128(bw: *BufferedWriter, value: anytype) Writer.Error!void {
1363 const value_info = @typeInfo(@TypeOf(value)).int;1386 const value_info = @typeInfo(@TypeOf(value)).int;
1364 try bw.writeMultipleOf7Leb128(@as(@Type(.{ .int = .{1387 try bw.writeMultipleOf7Leb128(@as(@Type(.{ .int = .{
1365 .signedness = value_info.signedness,1388 .signedness = value_info.signedness,
...@@ -1367,7 +1390,7 @@ pub fn writeLeb128(bw: *BufferedWriter, value: anytype) anyerror!void {...@@ -1367,7 +1390,7 @@ pub fn writeLeb128(bw: *BufferedWriter, value: anytype) anyerror!void {
1367 } }), value));1390 } }), value));
1368}1391}
13691392
1370fn writeMultipleOf7Leb128(bw: *BufferedWriter, value: anytype) anyerror!void {1393fn writeMultipleOf7Leb128(bw: *BufferedWriter, value: anytype) Writer.Error!void {
1371 const value_info = @typeInfo(@TypeOf(value)).int;1394 const value_info = @typeInfo(@TypeOf(value)).int;
1372 comptime assert(value_info.bits % 7 == 0);1395 comptime assert(value_info.bits % 7 == 0);
1373 var remaining = value;1396 var remaining = value;
...@@ -1409,7 +1432,7 @@ test "formatValue max_depth" {...@@ -1409,7 +1432,7 @@ test "formatValue max_depth" {
1409 comptime fmt: []const u8,1432 comptime fmt: []const u8,
1410 options: std.fmt.Options,1433 options: std.fmt.Options,
1411 bw: *BufferedWriter,1434 bw: *BufferedWriter,
1412 ) anyerror!void {1435 ) Writer.Error!void {
1413 _ = options;1436 _ = options;
1414 if (fmt.len == 0) {1437 if (fmt.len == 0) {
1415 return bw.print("({d:.3},{d:.3})", .{ self.x, self.y });1438 return bw.print("({d:.3},{d:.3})", .{ self.x, self.y });
lib/std/io/PositionalReader.zig deleted-64
...@@ -1,64 +0,0 @@
1const std = @import("../std.zig");
2const PositionalReader = @This();
3const assert = std.debug.assert;
4
5context: ?*anyopaque,
6vtable: *const VTable,
7
8pub const VTable = struct {
9 /// Writes bytes starting from `offset` to `bw`.
10 ///
11 /// Returns the number of bytes written, which will be at minimum `0` and
12 /// at most `limit`. The number of bytes written, including zero, does not
13 /// indicate end of stream.
14 ///
15 /// If the resource represented by the reader has an internal seek
16 /// position, it is not mutated.
17 ///
18 /// The implementation should do a maximum of one underlying read call.
19 ///
20 /// If `error.Unseekable` is returned, the resource cannot be used via a
21 /// positional reading interface.
22 read: *const fn (ctx: ?*anyopaque, bw: *std.io.BufferedWriter, limit: Limit, offset: u64) anyerror!Status,
23
24 /// Writes bytes starting from `offset` to `data`.
25 ///
26 /// Returns the number of bytes written, which will be at minimum `0` and
27 /// at most `limit`. The number of bytes written, including zero, does not
28 /// indicate end of stream.
29 ///
30 /// If the resource represented by the reader has an internal seek
31 /// position, it is not mutated.
32 ///
33 /// The implementation should do a maximum of one underlying read call.
34 ///
35 /// If `error.Unseekable` is returned, the resource cannot be used via a
36 /// positional reading interface.
37 readv: *const fn (ctx: ?*anyopaque, data: []const []u8, offset: u64) anyerror!Status,
38};
39
40pub const Len = std.io.Reader.Len;
41pub const Status = std.io.Reader.Status;
42pub const Limit = std.io.Reader.Limit;
43
44pub fn read(pr: PositionalReader, bw: *std.io.BufferedWriter, limit: Limit, offset: u64) anyerror!Status {
45 return pr.vtable.read(pr.context, bw, limit, offset);
46}
47
48pub fn readv(pr: PositionalReader, data: []const []u8, offset: u64) anyerror!Status {
49 return pr.vtable.read(pr.context, data, offset);
50}
51
52/// Returns total number of bytes written to `w`.
53///
54/// May return `error.Unseekable`, indicating this function cannot be used to
55/// read from the reader.
56pub fn readAll(pr: PositionalReader, w: *std.io.BufferedWriter, start_offset: u64) anyerror!usize {
57 const readFn = pr.vtable.read;
58 var offset: u64 = start_offset;
59 while (true) {
60 const status = try readFn(pr.context, w, .none, offset);
61 offset += status.len;
62 if (status.end) return @intCast(offset - start_offset);
63 }
64}
lib/std/io/Reader.zig+128-36
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const Reader = @This();2const Reader = @This();
3const assert = std.debug.assert;3const assert = std.debug.assert;
4const BufferedWriter = std.io.BufferedWriter;
45
5context: ?*anyopaque,6context: ?*anyopaque,
6vtable: *const VTable,7vtable: *const VTable,
...@@ -16,35 +17,54 @@ pub const VTable = struct {...@@ -16,35 +17,54 @@ pub const VTable = struct {
16 /// accordance with the number of bytes return from this function.17 /// accordance with the number of bytes return from this function.
17 ///18 ///
18 /// The implementation should do a maximum of one underlying read call.19 /// The implementation should do a maximum of one underlying read call.
19 ///20 read: *const fn (context: ?*anyopaque, bw: *BufferedWriter, limit: Limit) RwError!usize,
20 /// If `error.Unstreamable` is returned, the resource cannot be used via a
21 /// streaming reading interface.
22 read: *const fn (ctx: ?*anyopaque, bw: *std.io.BufferedWriter, limit: Limit) anyerror!Status,
2321
24 /// Writes bytes from the internally tracked stream position to `data`.22 /// Writes bytes from the internally tracked stream position to `data`.
25 ///23 ///
26 /// Returns the number of bytes written, which will be at minimum `0` and at24 /// Returns the number of bytes written, which will be at minimum `0` and
27 /// most `limit`. The number of bytes read, including zero, does not25 /// at most the sum of each data slice length. The number of bytes read,
28 /// indicate end of stream.26 /// including zero, does not indicate end of stream.
29 ///27 ///
30 /// If the reader has an internal seek position, it moves forward in28 /// If the reader has an internal seek position, it moves forward in
31 /// accordance with the number of bytes return from this function.29 /// accordance with the number of bytes return from this function.
32 ///30 ///
33 /// The implementation should do a maximum of one underlying read call.31 /// The implementation should do a maximum of one underlying read call.
32 readVec: *const fn (context: ?*anyopaque, data: []const []u8) Error!usize,
33
34 /// Consumes bytes from the internally tracked stream position without
35 /// providing access to them.
34 ///36 ///
35 /// If `error.Unstreamable` is returned, the resource cannot be used via a37 /// Returns the number of bytes discarded, which will be at minimum `0` and
36 /// streaming reading interface.38 /// at most `limit`. The number of bytes returned, including zero, does not
37 readv: *const fn (ctx: ?*anyopaque, data: []const []u8) anyerror!Status,39 /// indicate end of stream.
40 ///
41 /// If the reader has an internal seek position, it moves forward in
42 /// accordance with the number of bytes return from this function.
43 ///
44 /// The implementation should do a maximum of one underlying read call.
45 discard: *const fn (context: ?*anyopaque, limit: Limit) Error!usize,
38};46};
3947
40pub const Len = @Type(.{ .int = .{ .signedness = .unsigned, .bits = @bitSizeOf(usize) - 1 } });48pub const RwError = RwAllError || error{
49 /// End of stream indicated from the `Reader`. This error cannot originate
50 /// from the `Writer`.
51 EndOfStream,
52};
53
54pub const Error = ShortError || error{
55 EndOfStream,
56};
57
58/// For functions that handle end of stream as a success case.
59pub const RwAllError = ShortError || error{
60 /// See the `Writer` implementation for detailed diagnostics.
61 WriteFailed,
62};
4163
42pub const Status = packed struct(usize) {64/// For functions that cannot fail with `error.EndOfStream`.
43 /// Number of bytes that were transferred. Zero does not mean end of65pub const ShortError = error{
44 /// stream.66 /// See the `Reader` implementation for detailed diagnostics.
45 len: Len = 0,67 ReadFailed,
46 /// Indicates end of stream.
47 end: bool = false,
48};68};
4969
50pub const Limit = enum(usize) {70pub const Limit = enum(usize) {
...@@ -93,50 +113,122 @@ pub const Limit = enum(usize) {...@@ -93,50 +113,122 @@ pub const Limit = enum(usize) {
93 }113 }
94};114};
95115
96pub fn read(r: Reader, w: *std.io.BufferedWriter, limit: Limit) anyerror!Status {116pub fn read(r: Reader, bw: *BufferedWriter, limit: Limit) RwError!usize {
97 return r.vtable.read(r.context, w, limit);117 return r.vtable.read(r.context, bw, limit);
98}118}
99119
100pub fn readv(r: Reader, data: []const []u8) anyerror!Status {120pub fn readVec(r: Reader, data: []const []u8) Error!usize {
101 return r.vtable.readv(r.context, data);121 return r.vtable.readVec(r.context, data);
102}122}
103123
104/// Returns total number of bytes written to `w`.124pub fn discard(r: Reader, limit: Limit) Error!usize {
105pub fn readAll(r: Reader, w: *std.io.BufferedWriter) anyerror!usize {125 return r.vtable.discard(r.context, limit);
126}
127
128/// Returns total number of bytes written to `bw`.
129pub fn readAll(r: Reader, bw: *BufferedWriter) RwAllError!usize {
106 const readFn = r.vtable.read;130 const readFn = r.vtable.read;
107 var offset: usize = 0;131 var offset: usize = 0;
108 while (true) {132 while (true) {
109 const status = try readFn(r.context, w, .unlimited);133 offset += readFn(r.context, bw, .unlimited) catch |err| switch (err) {
110 offset += status.len;134 error.EndOfStream => return offset,
111 if (status.end) return offset;135 else => |e| return e,
136 };
112 }137 }
113}138}
114139
140/// Consumes the stream until the end, ignoring all the data, returning the
141/// number of bytes discarded.
142pub fn discardRemaining(r: Reader) ShortError!usize {
143 const discardFn = r.vtable.discard;
144 var offset: usize = 0;
145 while (true) {
146 offset += discardFn(r.context, .unlimited) catch |err| switch (err) {
147 error.EndOfStream => return offset,
148 else => |e| return e,
149 };
150 }
151}
152
153pub const ReadAllocError = std.mem.Allocator.Error || ShortError;
154
115/// Allocates enough memory to hold all the contents of the stream. If the allocated155/// Allocates enough memory to hold all the contents of the stream. If the allocated
116/// memory would be greater than `max_size`, returns `error.StreamTooLong`.156/// memory would be greater than `max_size`, returns `error.StreamTooLong`.
117///157///
118/// Caller owns returned memory.158/// Caller owns returned memory.
119///159///
120/// If this function returns an error, the contents from the stream read so far are lost.160/// If this function returns an error, the contents from the stream read so far are lost.
121pub fn readAlloc(r: Reader, gpa: std.mem.Allocator, max_size: usize) anyerror![]u8 {161pub fn readAlloc(r: Reader, gpa: std.mem.Allocator, max_size: usize) ReadAllocError![]u8 {
122 const readFn = r.vtable.read;162 const readFn = r.vtable.read;
123 var aw: std.io.AllocatingWriter = undefined;163 var aw: std.io.AllocatingWriter = undefined;
124 errdefer aw.deinit();164 errdefer aw.deinit();
125 aw.init(gpa);165 aw.init(gpa);
126 var remaining = max_size;166 var remaining = max_size;
127 while (remaining > 0) {167 while (remaining > 0) {
128 const status = try readFn(r.context, &aw.buffered_writer, .limited(remaining));168 const n = readFn(r.context, &aw.buffered_writer, .limited(remaining)) catch |err| switch (err) {
129 if (status.end) break;169 error.WriteFailed => return error.OutOfMemory,
130 remaining -= status.len;170 error.EndOfStream => break,
171 error.ReadFailed => return error.ReadFailed,
172 };
173 remaining -= n;
131 }174 }
132 return aw.toOwnedSlice();175 return aw.toOwnedSlice();
133}176}
134177
135/// Reads the stream until the end, ignoring all the data.178pub const failing: Reader = .{
136/// Returns the number of bytes discarded.179 .context = undefined,
137pub fn discardUntilEnd(r: Reader) anyerror!usize {180 .vtable = &.{
138 var bw = std.io.Writer.null.unbuffered();181 .read = failingRead,
139 return r.readAll(&bw);182 .readVec = failingReadVec,
183 .discard = failingDiscard,
184 },
185};
186
187pub const ending: Reader = .{
188 .context = undefined,
189 .vtable = &.{
190 .read = endingRead,
191 .readVec = endingReadVec,
192 .discard = endingDiscard,
193 },
194};
195
196fn endingRead(context: ?*anyopaque, bw: *BufferedWriter, limit: Limit) RwError!usize {
197 _ = context;
198 _ = bw;
199 _ = limit;
200 return error.EndOfStream;
201}
202
203fn endingReadVec(context: ?*anyopaque, data: []const []u8) Error!usize {
204 _ = context;
205 _ = data;
206 return error.EndOfStream;
207}
208
209fn endingDiscard(context: ?*anyopaque, limit: Limit) Error!usize {
210 _ = context;
211 _ = limit;
212 return error.EndOfStream;
213}
214
215fn failingRead(context: ?*anyopaque, bw: *BufferedWriter, limit: Limit) RwError!usize {
216 _ = context;
217 _ = bw;
218 _ = limit;
219 return error.ReadFailed;
220}
221
222fn failingReadVec(context: ?*anyopaque, data: []const []u8) Error!usize {
223 _ = context;
224 _ = data;
225 return error.ReadFailed;
226}
227
228fn failingDiscard(context: ?*anyopaque, limit: Limit) Error!usize {
229 _ = context;
230 _ = limit;
231 return error.ReadFailed;
140}232}
141233
142test "readAlloc when the backing reader provides one byte at a time" {234test "readAlloc when the backing reader provides one byte at a time" {
...@@ -144,7 +236,7 @@ test "readAlloc when the backing reader provides one byte at a time" {...@@ -144,7 +236,7 @@ test "readAlloc when the backing reader provides one byte at a time" {
144 str: []const u8,236 str: []const u8,
145 curr: usize,237 curr: usize,
146238
147 fn read(self: *@This(), dest: []u8) anyerror!usize {239 fn read(self: *@This(), dest: []u8) usize {
148 if (self.str.len <= self.curr or dest.len == 0)240 if (self.str.len <= self.curr or dest.len == 0)
149 return 0;241 return 0;
150242
lib/std/io/Writer.zig+41-63
...@@ -2,6 +2,8 @@ const std = @import("../std.zig");...@@ -2,6 +2,8 @@ const std = @import("../std.zig");
2const assert = std.debug.assert;2const assert = std.debug.assert;
3const Writer = @This();3const Writer = @This();
44
5pub const Null = @import("Writer/Null.zig");
6
5context: ?*anyopaque,7context: ?*anyopaque,
6vtable: *const VTable,8vtable: *const VTable,
79
...@@ -16,8 +18,8 @@ pub const VTable = struct {...@@ -16,8 +18,8 @@ pub const VTable = struct {
16 ///18 ///
17 /// Number of bytes returned may be zero, which does not mean19 /// Number of bytes returned may be zero, which does not mean
18 /// end-of-stream. A subsequent call may return nonzero, or may signal end20 /// end-of-stream. A subsequent call may return nonzero, or may signal end
19 /// of stream via an error.21 /// of stream via `error.WriteFailed`.
20 writeSplat: *const fn (ctx: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize,22 writeSplat: *const fn (ctx: ?*anyopaque, data: []const []const u8, splat: usize) Error!usize,
2123
22 /// Writes contents from an open file. `headers` are written first, then `len`24 /// Writes contents from an open file. `headers` are written first, then `len`
23 /// bytes of `file` starting from `offset`, then `trailers`.25 /// bytes of `file` starting from `offset`, then `trailers`.
...@@ -27,7 +29,7 @@ pub const VTable = struct {...@@ -27,7 +29,7 @@ pub const VTable = struct {
27 ///29 ///
28 /// Number of bytes returned may be zero, which does not mean30 /// Number of bytes returned may be zero, which does not mean
29 /// end-of-stream. A subsequent call may return nonzero, or may signal end31 /// end-of-stream. A subsequent call may return nonzero, or may signal end
30 /// of stream via an error.32 /// of stream via `error.WriteFailed`.
31 writeFile: *const fn (33 writeFile: *const fn (
32 ctx: ?*anyopaque,34 ctx: ?*anyopaque,
33 file: std.fs.File,35 file: std.fs.File,
...@@ -37,12 +39,19 @@ pub const VTable = struct {...@@ -37,12 +39,19 @@ pub const VTable = struct {
37 /// Maximum amount of bytes to read from the file.39 /// Maximum amount of bytes to read from the file.
38 limit: Limit,40 limit: Limit,
39 /// Headers and trailers must be passed together so that in case `len` is41 /// Headers and trailers must be passed together so that in case `len` is
40 /// zero, they can be forwarded directly to `VTable.writev`.42 /// zero, they can be forwarded directly to `VTable.writeVec`.
41 headers_and_trailers: []const []const u8,43 headers_and_trailers: []const []const u8,
42 headers_len: usize,44 headers_len: usize,
43 ) anyerror!usize,45 ) FileError!usize,
46};
47
48pub const Error = error{
49 /// See the `Writer` implementation for detailed diagnostics.
50 WriteFailed,
44};51};
4552
53pub const FileError = Error || std.fs.File.PReadError;
54
46pub const Limit = std.io.Reader.Limit;55pub const Limit = std.io.Reader.Limit;
4756
48pub const Offset = enum(u64) {57pub const Offset = enum(u64) {
...@@ -69,11 +78,11 @@ pub const Offset = enum(u64) {...@@ -69,11 +78,11 @@ pub const Offset = enum(u64) {
69 }78 }
70};79};
7180
72pub fn writev(w: Writer, data: []const []const u8) anyerror!usize {81pub fn writeVec(w: Writer, data: []const []const u8) Error!usize {
73 return w.vtable.writeSplat(w.context, data, 1);82 return w.vtable.writeSplat(w.context, data, 1);
74}83}
7584
76pub fn writeSplat(w: Writer, data: []const []const u8, splat: usize) anyerror!usize {85pub fn writeSplat(w: Writer, data: []const []const u8, splat: usize) Error!usize {
77 return w.vtable.writeSplat(w.context, data, splat);86 return w.vtable.writeSplat(w.context, data, splat);
78}87}
7988
...@@ -84,27 +93,10 @@ pub fn writeFile(...@@ -84,27 +93,10 @@ pub fn writeFile(
84 limit: Limit,93 limit: Limit,
85 headers_and_trailers: []const []const u8,94 headers_and_trailers: []const []const u8,
86 headers_len: usize,95 headers_len: usize,
87) anyerror!usize {96) FileError!usize {
88 return w.vtable.writeFile(w.context, file, offset, limit, headers_and_trailers, headers_len);97 return w.vtable.writeFile(w.context, file, offset, limit, headers_and_trailers, headers_len);
89}98}
9099
91pub fn unimplemented_writeFile(
92 context: ?*anyopaque,
93 file: std.fs.File,
94 offset: Offset,
95 limit: Limit,
96 headers_and_trailers: []const []const u8,
97 headers_len: usize,
98) anyerror!usize {
99 _ = context;
100 _ = file;
101 _ = offset;
102 _ = limit;
103 _ = headers_and_trailers;
104 _ = headers_len;
105 return error.Unimplemented;
106}
107
108pub fn buffered(w: Writer, buffer: []u8) std.io.BufferedWriter {100pub fn buffered(w: Writer, buffer: []u8) std.io.BufferedWriter {
109 return .{101 return .{
110 .buffer = buffer,102 .buffer = buffer,
...@@ -116,52 +108,38 @@ pub fn unbuffered(w: Writer) std.io.BufferedWriter {...@@ -116,52 +108,38 @@ pub fn unbuffered(w: Writer) std.io.BufferedWriter {
116 return w.buffered(&.{});108 return w.buffered(&.{});
117}109}
118110
119/// A `Writer` that discards all data.111pub fn failingWriteSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) Error!usize {
120pub const @"null": Writer = .{
121 .context = undefined,
122 .vtable = &.{
123 .writeSplat = null_writeSplat,
124 .writeFile = null_writeFile,
125 },
126};
127
128fn null_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize {
129 _ = context;112 _ = context;
130 const headers = data[0 .. data.len - 1];113 _ = data;
131 const pattern = data[headers.len..];114 _ = splat;
132 var written: usize = pattern.len * splat;115 return error.WriteFailed;
133 for (headers) |bytes| written += bytes.len;
134 return written;
135}116}
136117
137fn null_writeFile(118pub fn failingWriteFile(
138 context: ?*anyopaque,119 context: ?*anyopaque,
139 file: std.fs.File,120 file: std.fs.File,
140 offset: Offset,121 offset: std.io.Writer.Offset,
141 limit: Limit,122 limit: std.io.Writer.Limit,
142 headers_and_trailers: []const []const u8,123 headers_and_trailers: []const []const u8,
143 headers_len: usize,124 headers_len: usize,
144) anyerror!usize {125) Error!usize {
145 _ = context;126 _ = context;
146 var n: usize = 0;127 _ = file;
147 if (offset == .none) {128 _ = offset;
148 @panic("TODO seek the file forwards");129 _ = limit;
149 }130 _ = headers_and_trailers;
150 const limit_int = limit.toInt() orelse {131 _ = headers_len;
151 const headers = headers_and_trailers[0..headers_len];132 return error.WriteFailed;
152 for (headers) |bytes| n += bytes.len;
153 if (offset.toInt()) |off| {
154 const stat = try file.stat();
155 n += stat.size - off;
156 for (headers_and_trailers[headers_len..]) |bytes| n += bytes.len;
157 return n;
158 }
159 @panic("TODO stream from file until eof, counting");
160 };
161 for (headers_and_trailers) |bytes| n += bytes.len;
162 return limit_int + n;
163}133}
164134
165test @"null" {135pub const failing: Writer = .{
166 try @"null".writeAll("yay");136 .context = undefined,
137 .vtable = &.{
138 .writeSplat = failingWriteSplat,
139 .writeFile = failingWriteFile,
140 },
141};
142
143test {
144 _ = Null;
167}145}
lib/std/io/Writer/Null.zig created+66
...@@ -0,0 +1,66 @@
1//! A `Writer` that discards all data.
2
3const std = @import("../../std.zig");
4const Writer = std.io.Writer;
5
6const NullWriter = @This();
7
8err: Error,
9
10pub const Error = std.fs.File.StatError;
11
12pub fn writer(nw: *NullWriter) Writer {
13 return .{
14 .context = nw,
15 .vtable = &.{
16 .writeSplat = writeSplat,
17 .writeFile = writeFile,
18 },
19 };
20}
21
22fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) Writer.Error!usize {
23 _ = context;
24 const headers = data[0 .. data.len - 1];
25 const pattern = data[headers.len..];
26 var written: usize = pattern.len * splat;
27 for (headers) |bytes| written += bytes.len;
28 return written;
29}
30
31fn writeFile(
32 context: ?*anyopaque,
33 file: std.fs.File,
34 offset: Writer.Offset,
35 limit: Writer.Limit,
36 headers_and_trailers: []const []const u8,
37 headers_len: usize,
38) Writer.Error!usize {
39 const nw: *NullWriter = @alignCast(@ptrCast(context));
40 var n: usize = 0;
41 if (offset == .none) {
42 @panic("TODO seek the file forwards");
43 }
44 const limit_int = limit.toInt() orelse {
45 const headers = headers_and_trailers[0..headers_len];
46 for (headers) |bytes| n += bytes.len;
47 if (offset.toInt()) |off| {
48 const stat = file.stat() catch |err| {
49 nw.err = err;
50 return error.WriteFailed;
51 };
52 n += stat.size - off;
53 for (headers_and_trailers[headers_len..]) |bytes| n += bytes.len;
54 return n;
55 }
56 @panic("TODO stream from file until eof, counting");
57 };
58 for (headers_and_trailers) |bytes| n += bytes.len;
59 return limit_int + n;
60}
61
62test "writing a small string" {
63 var nw: NullWriter = undefined;
64 var bw = nw.writer().unbuffered();
65 try bw.writeAll("yay");
66}
lib/std/io/tty.zig+3-1
...@@ -71,7 +71,9 @@ pub const Config = union(enum) {...@@ -71,7 +71,9 @@ pub const Config = union(enum) {
71 reset_attributes: u16,71 reset_attributes: u16,
72 };72 };
7373
74 pub fn setColor(conf: Config, bw: *std.io.BufferedWriter, color: Color) anyerror!void {74 pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || std.io.Writer.Error;
75
76 pub fn setColor(conf: Config, bw: *std.io.BufferedWriter, color: Color) SetColorError!void {
75 nosuspend switch (conf) {77 nosuspend switch (conf) {
76 .no_color => return,78 .no_color => return,
77 .escape_codes => {79 .escape_codes => {
lib/std/json/Stringify.zig+16-14
...@@ -77,7 +77,9 @@ const safety_checks: @TypeOf(safety_checks_hint) = if (build_mode_has_safety)...@@ -77,7 +77,9 @@ const safety_checks: @TypeOf(safety_checks_hint) = if (build_mode_has_safety)
77else77else
78 .assumed_correct;78 .assumed_correct;
7979
80pub fn beginArray(self: *Stringify) anyerror!void {80pub const Error = std.io.Writer.Error;
81
82pub fn beginArray(self: *Stringify) Error!void {
81 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);83 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
82 try self.valueStart();84 try self.valueStart();
83 try self.writer.writeByte('[');85 try self.writer.writeByte('[');
...@@ -85,7 +87,7 @@ pub fn beginArray(self: *Stringify) anyerror!void {...@@ -85,7 +87,7 @@ pub fn beginArray(self: *Stringify) anyerror!void {
85 self.next_punctuation = .none;87 self.next_punctuation = .none;
86}88}
8789
88pub fn beginObject(self: *Stringify) anyerror!void {90pub fn beginObject(self: *Stringify) Error!void {
89 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);91 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
90 try self.valueStart();92 try self.valueStart();
91 try self.writer.writeByte('{');93 try self.writer.writeByte('{');
...@@ -93,7 +95,7 @@ pub fn beginObject(self: *Stringify) anyerror!void {...@@ -93,7 +95,7 @@ pub fn beginObject(self: *Stringify) anyerror!void {
93 self.next_punctuation = .none;95 self.next_punctuation = .none;
94}96}
9597
96pub fn endArray(self: *Stringify) anyerror!void {98pub fn endArray(self: *Stringify) Error!void {
97 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);99 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
98 self.popIndentation(.array);100 self.popIndentation(.array);
99 switch (self.next_punctuation) {101 switch (self.next_punctuation) {
...@@ -107,7 +109,7 @@ pub fn endArray(self: *Stringify) anyerror!void {...@@ -107,7 +109,7 @@ pub fn endArray(self: *Stringify) anyerror!void {
107 self.valueDone();109 self.valueDone();
108}110}
109111
110pub fn endObject(self: *Stringify) anyerror!void {112pub fn endObject(self: *Stringify) Error!void {
111 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);113 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
112 self.popIndentation(.object);114 self.popIndentation(.object);
113 switch (self.next_punctuation) {115 switch (self.next_punctuation) {
...@@ -213,7 +215,7 @@ fn isComplete(self: *const Stringify) bool {...@@ -213,7 +215,7 @@ fn isComplete(self: *const Stringify) bool {
213/// assuming the resulting formatted string represents a single complete value;215/// assuming the resulting formatted string represents a single complete value;
214/// e.g. `"1"`, `"[]"`, `"[1,2]"`, not `"1,2"`.216/// e.g. `"1"`, `"[]"`, `"[1,2]"`, not `"1,2"`.
215/// This function may be useful for doing your own number formatting.217/// This function may be useful for doing your own number formatting.
216pub fn print(self: *Stringify, comptime fmt: []const u8, args: anytype) anyerror!void {218pub fn print(self: *Stringify, comptime fmt: []const u8, args: anytype) Error!void {
217 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);219 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
218 try self.valueStart();220 try self.valueStart();
219 try self.writer.print(fmt, args);221 try self.writer.print(fmt, args);
...@@ -274,7 +276,7 @@ pub fn endWriteRaw(self: *Stringify) void {...@@ -274,7 +276,7 @@ pub fn endWriteRaw(self: *Stringify) void {
274/// `key` is the string content of the property name.276/// `key` is the string content of the property name.
275/// Surrounding quotes will be added and any special characters will be escaped.277/// Surrounding quotes will be added and any special characters will be escaped.
276/// See also `objectFieldRaw`.278/// See also `objectFieldRaw`.
277pub fn objectField(self: *Stringify, key: []const u8) anyerror!void {279pub fn objectField(self: *Stringify, key: []const u8) Error!void {
278 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);280 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
279 try self.objectFieldStart();281 try self.objectFieldStart();
280 try encodeJsonString(key, self.options, self.writer);282 try encodeJsonString(key, self.options, self.writer);
...@@ -284,7 +286,7 @@ pub fn objectField(self: *Stringify, key: []const u8) anyerror!void {...@@ -284,7 +286,7 @@ pub fn objectField(self: *Stringify, key: []const u8) anyerror!void {
284/// `quoted_key` is the complete bytes of the key including quotes and any necessary escape sequences.286/// `quoted_key` is the complete bytes of the key including quotes and any necessary escape sequences.
285/// A few assertions are performed on the given value to ensure that the caller of this function understands the API contract.287/// A few assertions are performed on the given value to ensure that the caller of this function understands the API contract.
286/// See also `objectField`.288/// See also `objectField`.
287pub fn objectFieldRaw(self: *Stringify, quoted_key: []const u8) anyerror!void {289pub fn objectFieldRaw(self: *Stringify, quoted_key: []const u8) Error!void {
288 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);290 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
289 assert(quoted_key.len >= 2 and quoted_key[0] == '"' and quoted_key[quoted_key.len - 1] == '"'); // quoted_key should be "quoted".291 assert(quoted_key.len >= 2 and quoted_key[0] == '"' and quoted_key[quoted_key.len - 1] == '"'); // quoted_key should be "quoted".
290 try self.objectFieldStart();292 try self.objectFieldStart();
...@@ -343,7 +345,7 @@ pub fn endObjectFieldRaw(self: *Stringify) void {...@@ -343,7 +345,7 @@ pub fn endObjectFieldRaw(self: *Stringify) void {
343///345///
344/// See also alternative functions `print` and `beginWriteRaw`.346/// See also alternative functions `print` and `beginWriteRaw`.
345/// For writing object field names, use `objectField` instead.347/// For writing object field names, use `objectField` instead.
346pub fn write(self: *Stringify, v: anytype) anyerror!void {348pub fn write(self: *Stringify, v: anytype) Error!void {
347 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);349 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
348 const T = @TypeOf(v);350 const T = @TypeOf(v);
349 switch (@typeInfo(T)) {351 switch (@typeInfo(T)) {
...@@ -568,7 +570,7 @@ pub const Options = struct {...@@ -568,7 +570,7 @@ pub const Options = struct {
568/// Writes the given value to the `std.io.Writer` writer.570/// Writes the given value to the `std.io.Writer` writer.
569/// See `Stringify` for how the given value is serialized into JSON.571/// See `Stringify` for how the given value is serialized into JSON.
570/// The maximum nesting depth of the output JSON document is 256.572/// The maximum nesting depth of the output JSON document is 256.
571pub fn value(v: anytype, options: Options, writer: *std.io.BufferedWriter) anyerror!void {573pub fn value(v: anytype, options: Options, writer: *std.io.BufferedWriter) Error!void {
572 var s: Stringify = .{ .writer = writer, .options = options };574 var s: Stringify = .{ .writer = writer, .options = options };
573 try s.write(v);575 try s.write(v);
574}576}
...@@ -632,7 +634,7 @@ test valueAlloc {...@@ -632,7 +634,7 @@ test valueAlloc {
632 try std.testing.expectEqualStrings(expected, actual);634 try std.testing.expectEqualStrings(expected, actual);
633}635}
634636
635fn outputUnicodeEscape(codepoint: u21, bw: *std.io.BufferedWriter) anyerror!void {637fn outputUnicodeEscape(codepoint: u21, bw: *std.io.BufferedWriter) Error!void {
636 if (codepoint <= 0xFFFF) {638 if (codepoint <= 0xFFFF) {
637 // If the character is in the Basic Multilingual Plane (U+0000 through U+FFFF),639 // If the character is in the Basic Multilingual Plane (U+0000 through U+FFFF),
638 // then it may be represented as a six-character sequence: a reverse solidus, followed640 // then it may be represented as a six-character sequence: a reverse solidus, followed
...@@ -652,7 +654,7 @@ fn outputUnicodeEscape(codepoint: u21, bw: *std.io.BufferedWriter) anyerror!void...@@ -652,7 +654,7 @@ fn outputUnicodeEscape(codepoint: u21, bw: *std.io.BufferedWriter) anyerror!void
652 }654 }
653}655}
654656
655fn outputSpecialEscape(c: u8, writer: *std.io.BufferedWriter) anyerror!void {657fn outputSpecialEscape(c: u8, writer: *std.io.BufferedWriter) Error!void {
656 switch (c) {658 switch (c) {
657 '\\' => try writer.writeAll("\\\\"),659 '\\' => try writer.writeAll("\\\\"),
658 '\"' => try writer.writeAll("\\\""),660 '\"' => try writer.writeAll("\\\""),
...@@ -666,14 +668,14 @@ fn outputSpecialEscape(c: u8, writer: *std.io.BufferedWriter) anyerror!void {...@@ -666,14 +668,14 @@ fn outputSpecialEscape(c: u8, writer: *std.io.BufferedWriter) anyerror!void {
666}668}
667669
668/// Write `string` to `writer` as a JSON encoded string.670/// Write `string` to `writer` as a JSON encoded string.
669pub fn encodeJsonString(string: []const u8, options: Options, writer: *std.io.BufferedWriter) anyerror!void {671pub fn encodeJsonString(string: []const u8, options: Options, writer: *std.io.BufferedWriter) Error!void {
670 try writer.writeByte('\"');672 try writer.writeByte('\"');
671 try encodeJsonStringChars(string, options, writer);673 try encodeJsonStringChars(string, options, writer);
672 try writer.writeByte('\"');674 try writer.writeByte('\"');
673}675}
674676
675/// Write `chars` to `writer` as JSON encoded string characters.677/// Write `chars` to `writer` as JSON encoded string characters.
676pub fn encodeJsonStringChars(chars: []const u8, options: Options, writer: *std.io.BufferedWriter) anyerror!void {678pub fn encodeJsonStringChars(chars: []const u8, options: Options, writer: *std.io.BufferedWriter) Error!void {
677 var write_cursor: usize = 0;679 var write_cursor: usize = 0;
678 var i: usize = 0;680 var i: usize = 0;
679 if (options.escape_unicode) {681 if (options.escape_unicode) {
...@@ -722,7 +724,7 @@ test "json write stream" {...@@ -722,7 +724,7 @@ test "json write stream" {
722 try testBasicWriteStream(&w);724 try testBasicWriteStream(&w);
723}725}
724726
725fn testBasicWriteStream(w: *Stringify) anyerror!void {727fn testBasicWriteStream(w: *Stringify) Error!void {
726 w.writer.reset();728 w.writer.reset();
727729
728 try w.beginObject();730 try w.beginObject();
lib/std/json/dynamic.zig+3-3
...@@ -51,10 +51,10 @@ pub const Value = union(enum) {...@@ -51,10 +51,10 @@ pub const Value = union(enum) {
51 }51 }
5252
53 pub fn dump(v: Value) void {53 pub fn dump(v: Value) void {
54 var bw = std.debug.lockStdErr2(&.{});54 const bw = std.debug.lockStderrWriter(&.{});
55 defer std.debug.unlockStdErr();55 defer std.debug.unlockStderrWriter();
5656
57 json.Stringify.value(v, .{}, &bw) catch return;57 json.Stringify.value(v, .{}, bw) catch return;
58 }58 }
5959
60 pub fn jsonStringify(value: @This(), jws: anytype) !void {60 pub fn jsonStringify(value: @This(), jws: anytype) !void {
lib/std/log.zig+3-6
...@@ -149,12 +149,9 @@ pub fn defaultLog(...@@ -149,12 +149,9 @@ pub fn defaultLog(
149 const level_txt = comptime message_level.asText();149 const level_txt = comptime message_level.asText();
150 const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";150 const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
151 var buffer: [1024]u8 = undefined;151 var buffer: [1024]u8 = undefined;
152 var bw: std.io.BufferedWriter = std.debug.lockStdErr2(&buffer);152 const bw = std.debug.lockStderrWriter(&buffer);
153 defer std.debug.unlockStdErr();153 defer std.debug.unlockStderrWriter();
154 nosuspend {154 bw.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return;
155 bw.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return;
156 bw.flush() catch return;
157 }
158}155}
159156
160/// Returns a scoped logging namespace that logs all messages using the scope157/// Returns a scoped logging namespace that logs all messages using the scope
lib/std/math/big/int.zig+1-1
...@@ -2322,7 +2322,7 @@ pub const Const = struct {...@@ -2322,7 +2322,7 @@ pub const Const = struct {
2322 /// this function will fail to print the string, printing "(BigInt)" instead of a number.2322 /// this function will fail to print the string, printing "(BigInt)" instead of a number.
2323 /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.2323 /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.
2324 /// See `toString` and `toStringAlloc` for a way to print big integers without failure.2324 /// See `toString` and `toStringAlloc` for a way to print big integers without failure.
2325 pub fn format(self: Const, bw: *std.io.BufferedWriter, comptime fmt: []const u8) anyerror!void {2325 pub fn format(self: Const, bw: *std.io.BufferedWriter, comptime fmt: []const u8) !void {
2326 comptime var base = 10;2326 comptime var base = 10;
2327 comptime var case: std.fmt.Case = .lower;2327 comptime var case: std.fmt.Case = .lower;
23282328
lib/std/net.zig+7-7
...@@ -850,8 +850,8 @@ pub fn tcpConnectToAddress(address: Address) TcpConnectToAddressError!Stream {...@@ -850,8 +850,8 @@ pub fn tcpConnectToAddress(address: Address) TcpConnectToAddressError!Stream {
850}850}
851851
852// TODO: Instead of having a massive error set, make the error set have categories, and then852// TODO: Instead of having a massive error set, make the error set have categories, and then
853// store the sub-error as a diagnostic anyerror value.853// store the sub-error as a diagnostic value.
854const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || anyerror || posix.SocketError || posix.BindError || posix.SetSockOptError || error{854const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || posix.SocketError || posix.BindError || posix.SetSockOptError || error{
855 TemporaryNameServerFailure,855 TemporaryNameServerFailure,
856 NameServerFailure,856 NameServerFailure,
857 AddressFamilyNotSupported,857 AddressFamilyNotSupported,
...@@ -1873,14 +1873,14 @@ pub const Stream = struct {...@@ -1873,14 +1873,14 @@ pub const Stream = struct {
1873 context: ?*anyopaque,1873 context: ?*anyopaque,
1874 bw: *std.io.BufferedWriter,1874 bw: *std.io.BufferedWriter,
1875 limit: std.io.Reader.Limit,1875 limit: std.io.Reader.Limit,
1876 ) anyerror!std.io.Reader.Status {1876 ) std.io.Reader.Error!usize {
1877 const buf = limit.slice(try bw.writableSlice(1));1877 const buf = limit.slice(try bw.writableSlice(1));
1878 const status = try windows_readv(context, &.{buf});1878 const status = try windows_readv(context, &.{buf});
1879 bw.advance(status.len);1879 bw.advance(status.len);
1880 return status;1880 return status;
1881 }1881 }
18821882
1883 fn windows_readv(context: ?*anyopaque, data: []const []u8) anyerror!std.io.Reader.Status {1883 fn windows_readv(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
1884 var iovecs: [max_buffers_len]windows.WSABUF = undefined;1884 var iovecs: [max_buffers_len]windows.WSABUF = undefined;
1885 var iovecs_i: usize = 0;1885 var iovecs_i: usize = 0;
1886 for (data) |d| {1886 for (data) |d| {
...@@ -1915,7 +1915,7 @@ pub const Stream = struct {...@@ -1915,7 +1915,7 @@ pub const Stream = struct {
1915 return .{ .len = n, .end = n == 0 };1915 return .{ .len = n, .end = n == 0 };
1916 }1916 }
19171917
1918 fn windows_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {1918 fn windows_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
1919 comptime assert(native_os == .windows);1919 comptime assert(native_os == .windows);
1920 if (data.len == 1 and splat == 0) return 0;1920 if (data.len == 1 and splat == 0) return 0;
1921 var splat_buffer: [256]u8 = undefined;1921 var splat_buffer: [256]u8 = undefined;
...@@ -1974,7 +1974,7 @@ pub const Stream = struct {...@@ -1974,7 +1974,7 @@ pub const Stream = struct {
1974 return n;1974 return n;
1975 }1975 }
19761976
1977 fn posix_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize {1977 fn posix_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
1978 const sock_fd = opaqueToHandle(context);1978 const sock_fd = opaqueToHandle(context);
1979 comptime assert(native_os != .windows);1979 comptime assert(native_os != .windows);
1980 var splat_buffer: [256]u8 = undefined;1980 var splat_buffer: [256]u8 = undefined;
...@@ -2028,7 +2028,7 @@ pub const Stream = struct {...@@ -2028,7 +2028,7 @@ pub const Stream = struct {
2028 in_len: std.io.Writer.FileLen,2028 in_len: std.io.Writer.FileLen,
2029 headers_and_trailers: []const []const u8,2029 headers_and_trailers: []const []const u8,
2030 headers_len: usize,2030 headers_len: usize,
2031 ) anyerror!usize {2031 ) std.io.Writer.FileError!usize {
2032 const len_int = switch (in_len) {2032 const len_int = switch (in_len) {
2033 .zero => return windows_writeSplat(context, headers_and_trailers, 1),2033 .zero => return windows_writeSplat(context, headers_and_trailers, 1),
2034 .entire_file => std.math.maxInt(usize),2034 .entire_file => std.math.maxInt(usize),
lib/std/tar.zig+1-1
...@@ -603,7 +603,7 @@ fn PaxIterator(comptime ReaderType: type) type {...@@ -603,7 +603,7 @@ fn PaxIterator(comptime ReaderType: type) type {
603 return null;603 return null;
604 }604 }
605605
606 fn readUntil(self: *Self, delimiter: u8) anyerror![]const u8 {606 fn readUntil(self: *Self, delimiter: u8) ![]const u8 {
607 var fbs: std.io.BufferedWriter = undefined;607 var fbs: std.io.BufferedWriter = undefined;
608 fbs.initFixed(&self.scratch);608 fbs.initFixed(&self.scratch);
609 try self.reader.streamUntilDelimiter(&fbs, delimiter, null);609 try self.reader.streamUntilDelimiter(&fbs, delimiter, null);
lib/std/testing.zig+4-4
...@@ -390,8 +390,8 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const...@@ -390,8 +390,8 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
390 const actual_window = actual[window_start..@min(actual.len, window_start + max_window_size)];390 const actual_window = actual[window_start..@min(actual.len, window_start + max_window_size)];
391 const actual_truncated = window_start + actual_window.len < actual.len;391 const actual_truncated = window_start + actual_window.len < actual.len;
392392
393 var bw = std.debug.lockStdErr2(&.{});393 const bw = std.debug.lockStderrWriter(&.{});
394 defer std.debug.unlockStdErr();394 defer std.debug.unlockStderrWriter();
395 const ttyconf = std.io.tty.detectConfig(.stderr());395 const ttyconf = std.io.tty.detectConfig(.stderr());
396 var differ = if (T == u8) BytesDiffer{396 var differ = if (T == u8) BytesDiffer{
397 .expected = expected_window,397 .expected = expected_window,
...@@ -416,7 +416,7 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const...@@ -416,7 +416,7 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
416 print("... truncated ...\n", .{});416 print("... truncated ...\n", .{});
417 }417 }
418 }418 }
419 differ.write(&bw) catch {};419 differ.write(bw) catch {};
420 if (expected_truncated) {420 if (expected_truncated) {
421 const end_offset = window_start + expected_window.len;421 const end_offset = window_start + expected_window.len;
422 const num_missing_items = expected.len - (window_start + expected_window.len);422 const num_missing_items = expected.len - (window_start + expected_window.len);
...@@ -438,7 +438,7 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const...@@ -438,7 +438,7 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
438 print("... truncated ...\n", .{});438 print("... truncated ...\n", .{});
439 }439 }
440 }440 }
441 differ.write(&bw) catch {};441 differ.write(bw) catch {};
442 if (actual_truncated) {442 if (actual_truncated) {
443 const end_offset = window_start + actual_window.len;443 const end_offset = window_start + actual_window.len;
444 const num_missing_items = actual.len - (window_start + actual_window.len);444 const num_missing_items = actual.len - (window_start + actual_window.len);
lib/std/zig/Ast.zig+2-2
...@@ -207,7 +207,7 @@ pub fn renderAlloc(tree: Ast, gpa: Allocator) RenderError![]u8 {...@@ -207,7 +207,7 @@ pub fn renderAlloc(tree: Ast, gpa: Allocator) RenderError![]u8 {
207 return aw.toOwnedSlice();207 return aw.toOwnedSlice();
208}208}
209209
210pub fn render(tree: Ast, gpa: Allocator, bw: *std.io.BufferedWriter, fixups: Fixups) anyerror!void {210pub fn render(tree: Ast, gpa: Allocator, bw: *std.io.BufferedWriter, fixups: Fixups) RenderError!void {
211 return @import("./render.zig").renderTree(gpa, bw, tree, fixups);211 return @import("./render.zig").renderTree(gpa, bw, tree, fixups);
212}212}
213213
...@@ -315,7 +315,7 @@ pub fn rootDecls(tree: Ast) []const Node.Index {...@@ -315,7 +315,7 @@ pub fn rootDecls(tree: Ast) []const Node.Index {
315 }315 }
316}316}
317317
318pub fn renderError(tree: Ast, parse_error: Error, bw: *std.io.BufferedWriter) anyerror!void {318pub fn renderError(tree: Ast, parse_error: Error, bw: *std.io.BufferedWriter) std.io.Writer.Error!void {
319 switch (parse_error.tag) {319 switch (parse_error.tag) {
320 .asterisk_after_ptr_deref => {320 .asterisk_after_ptr_deref => {
321 // Note that the token will point at the `.*` but ideally the source321 // Note that the token will point at the `.*` but ideally the source
lib/std/zig/ErrorBundle.zig+5-6
...@@ -158,13 +158,12 @@ pub const RenderOptions = struct {...@@ -158,13 +158,12 @@ pub const RenderOptions = struct {
158158
159pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void {159pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void {
160 var buffer: [256]u8 = undefined;160 var buffer: [256]u8 = undefined;
161 var bw = std.debug.lockStdErr2(&buffer);161 const bw = std.debug.lockStderrWriter(&buffer);
162 defer std.debug.unlockStdErr();162 defer std.debug.unlockStderrWriter();
163 renderToWriter(eb, options, &bw) catch return;163 renderToWriter(eb, options, bw) catch return;
164 bw.flush() catch return;
165}164}
166165
167pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, bw: *std.io.BufferedWriter) anyerror!void {166pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, bw: *std.io.BufferedWriter) std.io.Writer.Error!void {
168 if (eb.extra.len == 0) return;167 if (eb.extra.len == 0) return;
169 for (eb.getMessages()) |err_msg| {168 for (eb.getMessages()) |err_msg| {
170 try renderErrorMessageToWriter(eb, options, err_msg, bw, "error", .red, 0);169 try renderErrorMessageToWriter(eb, options, err_msg, bw, "error", .red, 0);
...@@ -187,7 +186,7 @@ fn renderErrorMessageToWriter(...@@ -187,7 +186,7 @@ fn renderErrorMessageToWriter(
187 kind: []const u8,186 kind: []const u8,
188 color: std.io.tty.Color,187 color: std.io.tty.Color,
189 indent: usize,188 indent: usize,
190) anyerror!void {189) std.io.Writer.Error!void {
191 const ttyconf = options.ttyconf;190 const ttyconf = options.ttyconf;
192 const err_msg = eb.getErrorMessage(err_msg_index);191 const err_msg = eb.getErrorMessage(err_msg_index);
193 const prefix_start = bw.count;192 const prefix_start = bw.count;
lib/std/zig/Server.zig+42-145
...@@ -1,6 +1,5 @@...@@ -1,6 +1,5 @@
1in: std.fs.File,1in: *std.io.BufferedReader,
2out: std.fs.File,2out: *std.io.BufferedWriter,
3receive_fifo: std.fifo.LinearFifo(u8, .Dynamic),
43
5pub const Message = struct {4pub const Message = struct {
6 pub const Header = extern struct {5 pub const Header = extern struct {
...@@ -94,9 +93,8 @@ pub const Message = struct {...@@ -94,9 +93,8 @@ pub const Message = struct {
94};93};
9594
96pub const Options = struct {95pub const Options = struct {
97 gpa: Allocator,96 in: *std.io.BufferedReader,
98 in: std.fs.File,97 out: *std.io.BufferedWriter,
99 out: std.fs.File,
100 zig_version: []const u8,98 zig_version: []const u8,
101};99};
102100
...@@ -104,96 +102,40 @@ pub fn init(options: Options) !Server {...@@ -104,96 +102,40 @@ pub fn init(options: Options) !Server {
104 var s: Server = .{102 var s: Server = .{
105 .in = options.in,103 .in = options.in,
106 .out = options.out,104 .out = options.out,
107 .receive_fifo = std.fifo.LinearFifo(u8, .Dynamic).init(options.gpa),
108 };105 };
109 try s.serveStringMessage(.zig_version, options.zig_version);106 try s.serveStringMessage(.zig_version, options.zig_version);
110 return s;107 return s;
111}108}
112109
113pub fn deinit(s: *Server) void {
114 s.receive_fifo.deinit();
115 s.* = undefined;
116}
117
118pub fn receiveMessage(s: *Server) !InMessage.Header {110pub fn receiveMessage(s: *Server) !InMessage.Header {
119 const Header = InMessage.Header;111 return try s.in.takeStructEndian(InMessage.Header, .little);
120 const fifo = &s.receive_fifo;
121 var last_amt_zero = false;
122
123 while (true) {
124 const buf = fifo.readableSlice(0);
125 assert(fifo.readableLength() == buf.len);
126 if (buf.len >= @sizeOf(Header)) {
127 const header: *align(1) const Header = @ptrCast(buf[0..@sizeOf(Header)]);
128 const bytes_len = bswap(header.bytes_len);
129 const tag = bswap(header.tag);
130
131 if (buf.len - @sizeOf(Header) >= bytes_len) {
132 fifo.discard(@sizeOf(Header));
133 return .{
134 .tag = tag,
135 .bytes_len = bytes_len,
136 };
137 } else {
138 const needed = bytes_len - (buf.len - @sizeOf(Header));
139 const write_buffer = try fifo.writableWithSize(needed);
140 const amt = try s.in.read(write_buffer);
141 fifo.update(amt);
142 continue;
143 }
144 }
145
146 const write_buffer = try fifo.writableWithSize(256);
147 const amt = try s.in.read(write_buffer);
148 fifo.update(amt);
149 if (amt == 0) {
150 if (last_amt_zero) return error.BrokenPipe;
151 last_amt_zero = true;
152 }
153 }
154}112}
155113
156pub fn receiveBody_u32(s: *Server) !u32 {114pub fn receiveBody_u32(s: *Server) !u32 {
157 const fifo = &s.receive_fifo;115 return s.in.takeInt(u32, .little);
158 const buf = fifo.readableSlice(0);
159 const result = @as(*align(1) const u32, @ptrCast(buf[0..4])).*;
160 fifo.discard(4);
161 return bswap(result);
162}116}
163117
164pub fn serveStringMessage(s: *Server, tag: OutMessage.Tag, msg: []const u8) !void {118pub fn serveStringMessage(s: *Server, tag: OutMessage.Tag, msg: []const u8) !void {
165 return s.serveMessage(.{119 try s.serveMessageHeader(.{
166 .tag = tag,120 .tag = tag,
167 .bytes_len = @as(u32, @intCast(msg.len)),121 .bytes_len = @intCast(msg.len),
168 }, &.{msg});122 });
123 try s.out.writeAll(msg);
124 try s.out.flush();
169}125}
170126
171pub fn serveMessage(127/// Don't forget to flush!
172 s: *const Server,128pub fn serveMessageHeader(s: *const Server, header: OutMessage.Header) !void {
173 header: OutMessage.Header,129 try s.out.writeStructEndian(header, .little);
174 bufs: []const []const u8,
175) !void {
176 var iovecs: [10]std.posix.iovec_const = undefined;
177 const header_le = bswap(header);
178 iovecs[0] = .{
179 .base = @as([*]const u8, @ptrCast(&header_le)),
180 .len = @sizeOf(OutMessage.Header),
181 };
182 for (bufs, iovecs[1 .. bufs.len + 1]) |buf, *iovec| {
183 iovec.* = .{
184 .base = buf.ptr,
185 .len = buf.len,
186 };
187 }
188 try s.out.writevAll(iovecs[0 .. bufs.len + 1]);
189}130}
190131
191pub fn serveU64Message(s: *Server, tag: OutMessage.Tag, int: u64) !void {132pub fn serveU64Message(s: *const Server, tag: OutMessage.Tag, int: u64) !void {
192 const msg_le = bswap(int);133 try serveMessageHeader(s, .{
193 return s.serveMessage(.{
194 .tag = tag,134 .tag = tag,
195 .bytes_len = @sizeOf(u64),135 .bytes_len = @sizeOf(u64),
196 }, &.{std.mem.asBytes(&msg_le)});136 });
137 try s.out.writeInt(u64, int, .little);
138 try s.out.flush();
197}139}
198140
199pub fn serveEmitDigest(141pub fn serveEmitDigest(
...@@ -201,26 +143,22 @@ pub fn serveEmitDigest(...@@ -201,26 +143,22 @@ pub fn serveEmitDigest(
201 digest: *const [Cache.bin_digest_len]u8,143 digest: *const [Cache.bin_digest_len]u8,
202 header: OutMessage.EmitDigest,144 header: OutMessage.EmitDigest,
203) !void {145) !void {
204 try s.serveMessage(.{146 try s.serveMessageHeader(.{
205 .tag = .emit_digest,147 .tag = .emit_digest,
206 .bytes_len = @intCast(digest.len + @sizeOf(OutMessage.EmitDigest)),148 .bytes_len = @intCast(digest.len + @sizeOf(OutMessage.EmitDigest)),
207 }, &.{
208 std.mem.asBytes(&header),
209 digest,
210 });149 });
150 try s.out.writeStructEndian(header, .little);
151 try s.out.writeAll(digest);
152 try s.out.flush();
211}153}
212154
213pub fn serveTestResults(155pub fn serveTestResults(s: *Server, msg: OutMessage.TestResults) !void {
214 s: *Server,156 try s.serveMessageHeader(.{
215 msg: OutMessage.TestResults,
216) !void {
217 const msg_le = bswap(msg);
218 try s.serveMessage(.{
219 .tag = .test_results,157 .tag = .test_results,
220 .bytes_len = @intCast(@sizeOf(OutMessage.TestResults)),158 .bytes_len = @intCast(@sizeOf(OutMessage.TestResults)),
221 }, &.{
222 std.mem.asBytes(&msg_le),
223 });159 });
160 try s.out.writeStructEndian(msg, .little);
161 try s.out.flush();
224}162}
225163
226pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void {164pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void {
...@@ -230,81 +168,40 @@ pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void {...@@ -230,81 +168,40 @@ pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void {
230 };168 };
231 const bytes_len = @sizeOf(OutMessage.ErrorBundle) +169 const bytes_len = @sizeOf(OutMessage.ErrorBundle) +
232 4 * error_bundle.extra.len + error_bundle.string_bytes.len;170 4 * error_bundle.extra.len + error_bundle.string_bytes.len;
233 try s.serveMessage(.{171 try s.serveMessageHeader(.{
234 .tag = .error_bundle,172 .tag = .error_bundle,
235 .bytes_len = @intCast(bytes_len),173 .bytes_len = @intCast(bytes_len),
236 }, &.{
237 std.mem.asBytes(&eb_hdr),
238 // TODO: implement @ptrCast between slices changing the length
239 std.mem.sliceAsBytes(error_bundle.extra),
240 error_bundle.string_bytes,
241 });174 });
175 try s.out.writeStructEndian(eb_hdr, .little);
176 try s.out.writeArrayEndian(u32, error_bundle.extra, .little);
177 try s.out.writeAll(error_bundle.string_bytes);
178 try s.out.flush();
242}179}
243180
244pub const TestMetadata = struct {181pub const TestMetadata = struct {
245 names: []u32,182 names: []const u32,
246 expected_panic_msgs: []u32,183 expected_panic_msgs: []const u32,
247 string_bytes: []const u8,184 string_bytes: []const u8,
248};185};
249186
250pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void {187pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void {
251 const header: OutMessage.TestMetadata = .{188 const header: OutMessage.TestMetadata = .{
252 .tests_len = bswap(@as(u32, @intCast(test_metadata.names.len))),189 .tests_len = @as(u32, @intCast(test_metadata.names.len)),
253 .string_bytes_len = bswap(@as(u32, @intCast(test_metadata.string_bytes.len))),190 .string_bytes_len = @as(u32, @intCast(test_metadata.string_bytes.len)),
254 };191 };
255 const trailing = 2;192 const trailing = 2;
256 const bytes_len = @sizeOf(OutMessage.TestMetadata) +193 const bytes_len = @sizeOf(OutMessage.TestMetadata) +
257 trailing * @sizeOf(u32) * test_metadata.names.len + test_metadata.string_bytes.len;194 trailing * @sizeOf(u32) * test_metadata.names.len + test_metadata.string_bytes.len;
258195
259 if (need_bswap) {196 try s.serveMessageHeader(.{
260 bswap_u32_array(test_metadata.names);
261 bswap_u32_array(test_metadata.expected_panic_msgs);
262 }
263 defer if (need_bswap) {
264 bswap_u32_array(test_metadata.names);
265 bswap_u32_array(test_metadata.expected_panic_msgs);
266 };
267
268 return s.serveMessage(.{
269 .tag = .test_metadata,197 .tag = .test_metadata,
270 .bytes_len = @intCast(bytes_len),198 .bytes_len = @intCast(bytes_len),
271 }, &.{
272 std.mem.asBytes(&header),
273 // TODO: implement @ptrCast between slices changing the length
274 std.mem.sliceAsBytes(test_metadata.names),
275 std.mem.sliceAsBytes(test_metadata.expected_panic_msgs),
276 test_metadata.string_bytes,
277 });199 });
278}200 try s.out.writeStructEndian(header, .little);
279201 try s.out.writeArrayEndian(u32, test_metadata.names, .little);
280fn bswap(x: anytype) @TypeOf(x) {202 try s.out.writeArrayEndian(u32, test_metadata.expected_panic_msgs, .little);
281 if (!need_bswap) return x;203 try s.out.writeAll(test_metadata.string_bytes);
282204 try s.out.flush();
283 const T = @TypeOf(x);
284 switch (@typeInfo(T)) {
285 .@"enum" => return @as(T, @enumFromInt(@byteSwap(@intFromEnum(x)))),
286 .int => return @byteSwap(x),
287 .@"struct" => |info| switch (info.layout) {
288 .@"extern" => {
289 var result: T = undefined;
290 inline for (info.fields) |field| {
291 @field(result, field.name) = bswap(@field(x, field.name));
292 }
293 return result;
294 },
295 .@"packed" => {
296 const I = info.backing_integer.?;
297 return @as(T, @bitCast(@byteSwap(@as(I, @bitCast(x)))));
298 },
299 .auto => @compileError("auto layout struct"),
300 },
301 else => @compileError("bswap on type " ++ @typeName(T)),
302 }
303}
304
305fn bswap_u32_array(slice: []u32) void {
306 comptime assert(need_bswap);
307 for (slice) |*elem| elem.* = @byteSwap(elem.*);
308}205}
309206
310const OutMessage = std.zig.Server.Message;207const OutMessage = std.zig.Server.Message;
lib/std/zig/ZonGen.zig+1-1
...@@ -520,7 +520,7 @@ pub fn parseStrLit(...@@ -520,7 +520,7 @@ pub fn parseStrLit(
520 tree: Ast,520 tree: Ast,
521 node: Ast.Node.Index,521 node: Ast.Node.Index,
522 writer: *std.io.BufferedWriter,522 writer: *std.io.BufferedWriter,
523) anyerror!std.zig.string_literal.Result {523) error{OutOfMemory}!std.zig.string_literal.Result {
524 switch (tree.nodeTag(node)) {524 switch (tree.nodeTag(node)) {
525 .string_literal => {525 .string_literal => {
526 const token = tree.nodeMainToken(node);526 const token = tree.nodeMainToken(node);
lib/std/zig/llvm/BitcodeReader.zig+3-3
...@@ -170,7 +170,7 @@ pub fn next(bc: *BitcodeReader) !?Item {...@@ -170,7 +170,7 @@ pub fn next(bc: *BitcodeReader) !?Item {
170 }170 }
171}171}
172172
173pub fn skipBlock(bc: *BitcodeReader, block: Block) anyerror!void {173pub fn skipBlock(bc: *BitcodeReader, block: Block) !void {
174 assert(bc.bit_offset == 0);174 assert(bc.bit_offset == 0);
175 try bc.br.discard(4 * @as(u34, block.len));175 try bc.br.discard(4 * @as(u34, block.len));
176 try bc.endBlock();176 try bc.endBlock();
...@@ -369,12 +369,12 @@ fn align32Bits(bc: *BitcodeReader) void {...@@ -369,12 +369,12 @@ fn align32Bits(bc: *BitcodeReader) void {
369 bc.bit_offset = 0;369 bc.bit_offset = 0;
370}370}
371371
372fn read32Bits(bc: *BitcodeReader) anyerror!u32 {372fn read32Bits(bc: *BitcodeReader) !u32 {
373 assert(bc.bit_offset == 0);373 assert(bc.bit_offset == 0);
374 return bc.br.takeInt(u32, .little);374 return bc.br.takeInt(u32, .little);
375}375}
376376
377fn readBytes(bc: *BitcodeReader, bytes: []u8) anyerror!void {377fn readBytes(bc: *BitcodeReader, bytes: []u8) !void {
378 assert(bc.bit_offset == 0);378 assert(bc.bit_offset == 0);
379 try bc.br.read(bytes);379 try bc.br.read(bytes);
380380
lib/std/zig/llvm/Builder.zig+32-32
...@@ -91,7 +91,7 @@ pub const String = enum(u32) {...@@ -91,7 +91,7 @@ pub const String = enum(u32) {
91 string: String,91 string: String,
92 builder: *const Builder,92 builder: *const Builder,
93 };93 };
94 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) anyerror!void {94 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) std.io.Writer.Error!void {
95 if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_|95 if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_|
96 @compileError("invalid format string: '" ++ fmt_str ++ "'");96 @compileError("invalid format string: '" ++ fmt_str ++ "'");
97 assert(data.string != .none);97 assert(data.string != .none);
...@@ -649,7 +649,7 @@ pub const Type = enum(u32) {...@@ -649,7 +649,7 @@ pub const Type = enum(u32) {
649 type: Type,649 type: Type,
650 builder: *const Builder,650 builder: *const Builder,
651 };651 };
652 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) anyerror!void {652 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) std.io.Writer.Error!void {
653 assert(data.type != .none);653 assert(data.type != .none);
654 if (comptime std.mem.eql(u8, fmt_str, "m")) {654 if (comptime std.mem.eql(u8, fmt_str, "m")) {
655 const item = data.builder.type_items.items[@intFromEnum(data.type)];655 const item = data.builder.type_items.items[@intFromEnum(data.type)];
...@@ -1129,7 +1129,7 @@ pub const Attribute = union(Kind) {...@@ -1129,7 +1129,7 @@ pub const Attribute = union(Kind) {
1129 attribute_index: Index,1129 attribute_index: Index,
1130 builder: *const Builder,1130 builder: *const Builder,
1131 };1131 };
1132 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) anyerror!void {1132 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) std.io.Writer.Error!void {
1133 if (comptime std.mem.indexOfNone(u8, fmt_str, "\"#")) |_|1133 if (comptime std.mem.indexOfNone(u8, fmt_str, "\"#")) |_|
1134 @compileError("invalid format string: '" ++ fmt_str ++ "'");1134 @compileError("invalid format string: '" ++ fmt_str ++ "'");
1135 const attribute = data.attribute_index.toAttribute(data.builder);1135 const attribute = data.attribute_index.toAttribute(data.builder);
...@@ -1568,7 +1568,7 @@ pub const Attributes = enum(u32) {...@@ -1568,7 +1568,7 @@ pub const Attributes = enum(u32) {
1568 attributes: Attributes,1568 attributes: Attributes,
1569 builder: *const Builder,1569 builder: *const Builder,
1570 };1570 };
1571 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) anyerror!void {1571 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) std.io.Writer.Error!void {
1572 for (data.attributes.slice(data.builder)) |attribute_index| try Attribute.Index.format(.{1572 for (data.attributes.slice(data.builder)) |attribute_index| try Attribute.Index.format(.{
1573 .attribute_index = attribute_index,1573 .attribute_index = attribute_index,
1574 .builder = data.builder,1574 .builder = data.builder,
...@@ -1761,11 +1761,11 @@ pub const Linkage = enum(u4) {...@@ -1761,11 +1761,11 @@ pub const Linkage = enum(u4) {
1761 extern_weak = 7,1761 extern_weak = 7,
1762 external = 0,1762 external = 0,
17631763
1764 pub fn format(self: Linkage, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void {1764 pub fn format(self: Linkage, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void {
1765 if (self != .external) try bw.print(" {s}", .{@tagName(self)});1765 if (self != .external) try bw.print(" {s}", .{@tagName(self)});
1766 }1766 }
17671767
1768 fn formatOptional(data: ?Linkage, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void {1768 fn formatOptional(data: ?Linkage, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void {
1769 if (data) |linkage| try bw.print(" {s}", .{@tagName(linkage)});1769 if (data) |linkage| try bw.print(" {s}", .{@tagName(linkage)});
1770 }1770 }
1771 pub fn fmtOptional(self: ?Linkage) std.fmt.Formatter(formatOptional) {1771 pub fn fmtOptional(self: ?Linkage) std.fmt.Formatter(formatOptional) {
...@@ -1778,7 +1778,7 @@ pub const Preemption = enum {...@@ -1778,7 +1778,7 @@ pub const Preemption = enum {
1778 dso_local,1778 dso_local,
1779 implicit_dso_local,1779 implicit_dso_local,
17801780
1781 pub fn format(self: Preemption, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void {1781 pub fn format(self: Preemption, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void {
1782 if (self == .dso_local) try bw.print(" {s}", .{@tagName(self)});1782 if (self == .dso_local) try bw.print(" {s}", .{@tagName(self)});
1783 }1783 }
1784};1784};
...@@ -1799,8 +1799,8 @@ pub const Visibility = enum(u2) {...@@ -1799,8 +1799,8 @@ pub const Visibility = enum(u2) {
1799 pub fn format(1799 pub fn format(
1800 self: Visibility,1800 self: Visibility,
1801 comptime format_string: []const u8,1801 comptime format_string: []const u8,
1802 writer: anytype,1802 writer: *std.io.BufferedWriter,
1803 ) @TypeOf(writer).Error!void {1803 ) std.io.Writer.Error!void {
1804 comptime assert(format_string.len == 0);1804 comptime assert(format_string.len == 0);
1805 if (self != .default) try writer.print(" {s}", .{@tagName(self)});1805 if (self != .default) try writer.print(" {s}", .{@tagName(self)});
1806 }1806 }
...@@ -1811,7 +1811,7 @@ pub const DllStorageClass = enum(u2) {...@@ -1811,7 +1811,7 @@ pub const DllStorageClass = enum(u2) {
1811 dllimport = 1,1811 dllimport = 1,
1812 dllexport = 2,1812 dllexport = 2,
18131813
1814 pub fn format(self: DllStorageClass, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void {1814 pub fn format(self: DllStorageClass, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void {
1815 if (self != .default) try bw.print(" {s}", .{@tagName(self)});1815 if (self != .default) try bw.print(" {s}", .{@tagName(self)});
1816 }1816 }
1817};1817};
...@@ -1823,7 +1823,7 @@ pub const ThreadLocal = enum(u3) {...@@ -1823,7 +1823,7 @@ pub const ThreadLocal = enum(u3) {
1823 initialexec = 3,1823 initialexec = 3,
1824 localexec = 4,1824 localexec = 4,
18251825
1826 pub fn format(self: ThreadLocal, bw: *std.io.BufferedWriter, comptime prefix: []const u8) anyerror!void {1826 pub fn format(self: ThreadLocal, bw: *std.io.BufferedWriter, comptime prefix: []const u8) std.io.Writer.Error!void {
1827 if (self == .default) return;1827 if (self == .default) return;
1828 try bw.print("{s}thread_local", .{prefix});1828 try bw.print("{s}thread_local", .{prefix});
1829 if (self != .generaldynamic) try bw.print("({s})", .{@tagName(self)});1829 if (self != .generaldynamic) try bw.print("({s})", .{@tagName(self)});
...@@ -1837,7 +1837,7 @@ pub const UnnamedAddr = enum(u2) {...@@ -1837,7 +1837,7 @@ pub const UnnamedAddr = enum(u2) {
1837 unnamed_addr = 1,1837 unnamed_addr = 1,
1838 local_unnamed_addr = 2,1838 local_unnamed_addr = 2,
18391839
1840 pub fn format(self: UnnamedAddr, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void {1840 pub fn format(self: UnnamedAddr, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void {
1841 if (self != .default) try bw.print(" {s}", .{@tagName(self)});1841 if (self != .default) try bw.print(" {s}", .{@tagName(self)});
1842 }1842 }
1843};1843};
...@@ -1931,7 +1931,7 @@ pub const AddrSpace = enum(u24) {...@@ -1931,7 +1931,7 @@ pub const AddrSpace = enum(u24) {
1931 pub const funcref: AddrSpace = @enumFromInt(20);1931 pub const funcref: AddrSpace = @enumFromInt(20);
1932 };1932 };
19331933
1934 pub fn format(self: AddrSpace, bw: *std.io.BufferedWriter, comptime prefix: []const u8) anyerror!void {1934 pub fn format(self: AddrSpace, bw: *std.io.BufferedWriter, comptime prefix: []const u8) std.io.Writer.Error!void {
1935 if (self != .default) try bw.print("{s}addrspace({d})", .{ prefix, @intFromEnum(self) });1935 if (self != .default) try bw.print("{s}addrspace({d})", .{ prefix, @intFromEnum(self) });
1936 }1936 }
1937};1937};
...@@ -1940,7 +1940,7 @@ pub const ExternallyInitialized = enum {...@@ -1940,7 +1940,7 @@ pub const ExternallyInitialized = enum {
1940 default,1940 default,
1941 externally_initialized,1941 externally_initialized,
19421942
1943 pub fn format(self: ExternallyInitialized, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void {1943 pub fn format(self: ExternallyInitialized, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void {
1944 if (self != .default) try bw.print(" {s}", .{@tagName(self)});1944 if (self != .default) try bw.print(" {s}", .{@tagName(self)});
1945 }1945 }
1946};1946};
...@@ -1964,7 +1964,7 @@ pub const Alignment = enum(u6) {...@@ -1964,7 +1964,7 @@ pub const Alignment = enum(u6) {
1964 return if (self == .default) 0 else (@intFromEnum(self) + 1);1964 return if (self == .default) 0 else (@intFromEnum(self) + 1);
1965 }1965 }
19661966
1967 pub fn format(self: Alignment, bw: *std.io.BufferedWriter, comptime prefix: []const u8) anyerror!void {1967 pub fn format(self: Alignment, bw: *std.io.BufferedWriter, comptime prefix: []const u8) std.io.Writer.Error!void {
1968 try bw.print("{s}align {d}", .{ prefix, self.toByteUnits() orelse return });1968 try bw.print("{s}align {d}", .{ prefix, self.toByteUnits() orelse return });
1969 }1969 }
1970};1970};
...@@ -2038,7 +2038,7 @@ pub const CallConv = enum(u10) {...@@ -2038,7 +2038,7 @@ pub const CallConv = enum(u10) {
20382038
2039 pub const default = CallConv.ccc;2039 pub const default = CallConv.ccc;
20402040
2041 pub fn format(self: CallConv, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void {2041 pub fn format(self: CallConv, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void {
2042 switch (self) {2042 switch (self) {
2043 default => {},2043 default => {},
2044 .fastcc,2044 .fastcc,
...@@ -2119,7 +2119,7 @@ pub const StrtabString = enum(u32) {...@@ -2119,7 +2119,7 @@ pub const StrtabString = enum(u32) {
2119 string: StrtabString,2119 string: StrtabString,
2120 builder: *const Builder,2120 builder: *const Builder,
2121 };2121 };
2122 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) anyerror!void {2122 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) std.io.Writer.Error!void {
2123 if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_|2123 if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_|
2124 @compileError("invalid format string: '" ++ fmt_str ++ "'");2124 @compileError("invalid format string: '" ++ fmt_str ++ "'");
2125 assert(data.string != .none);2125 assert(data.string != .none);
...@@ -2306,7 +2306,7 @@ pub const Global = struct {...@@ -2306,7 +2306,7 @@ pub const Global = struct {
2306 global: Index,2306 global: Index,
2307 builder: *const Builder,2307 builder: *const Builder,
2308 };2308 };
2309 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void {2309 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void {
2310 try bw.print("@{f}", .{2310 try bw.print("@{f}", .{
2311 data.global.unwrap(data.builder).name(data.builder).fmt(data.builder),2311 data.global.unwrap(data.builder).name(data.builder).fmt(data.builder),
2312 });2312 });
...@@ -4752,7 +4752,7 @@ pub const Function = struct {...@@ -4752,7 +4752,7 @@ pub const Function = struct {
4752 function: Function.Index,4752 function: Function.Index,
4753 builder: *Builder,4753 builder: *Builder,
4754 };4754 };
4755 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) anyerror!void {4755 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) std.io.Writer.Error!void {
4756 if (comptime std.mem.indexOfNone(u8, fmt_str, ", %")) |_|4756 if (comptime std.mem.indexOfNone(u8, fmt_str, ", %")) |_|
4757 @compileError("invalid format string: '" ++ fmt_str ++ "'");4757 @compileError("invalid format string: '" ++ fmt_str ++ "'");
4758 if (comptime std.mem.indexOfScalar(u8, fmt_str, ',') != null) {4758 if (comptime std.mem.indexOfScalar(u8, fmt_str, ',') != null) {
...@@ -6944,7 +6944,7 @@ pub const MemoryAccessKind = enum(u1) {...@@ -6944,7 +6944,7 @@ pub const MemoryAccessKind = enum(u1) {
6944 normal,6944 normal,
6945 @"volatile",6945 @"volatile",
69466946
6947 pub fn format(self: MemoryAccessKind, bw: *std.io.BufferedWriter, comptime prefix: []const u8) anyerror!void {6947 pub fn format(self: MemoryAccessKind, bw: *std.io.BufferedWriter, comptime prefix: []const u8) std.io.Writer.Error!void {
6948 if (self != .normal) try bw.print("{s}{s}", .{ prefix, @tagName(self) });6948 if (self != .normal) try bw.print("{s}{s}", .{ prefix, @tagName(self) });
6949 }6949 }
6950};6950};
...@@ -6953,7 +6953,7 @@ pub const SyncScope = enum(u1) {...@@ -6953,7 +6953,7 @@ pub const SyncScope = enum(u1) {
6953 singlethread,6953 singlethread,
6954 system,6954 system,
69556955
6956 pub fn format(self: SyncScope, bw: *std.io.BufferedWriter, comptime prefix: []const u8) anyerror!void {6956 pub fn format(self: SyncScope, bw: *std.io.BufferedWriter, comptime prefix: []const u8) std.io.Writer.Error!void {
6957 if (self != .system) try bw.print(6957 if (self != .system) try bw.print(
6958 \\{s}syncscope("{s}")6958 \\{s}syncscope("{s}")
6959 , .{ prefix, @tagName(self) });6959 , .{ prefix, @tagName(self) });
...@@ -6969,7 +6969,7 @@ pub const AtomicOrdering = enum(u3) {...@@ -6969,7 +6969,7 @@ pub const AtomicOrdering = enum(u3) {
6969 acq_rel = 5,6969 acq_rel = 5,
6970 seq_cst = 6,6970 seq_cst = 6,
69716971
6972 pub fn format(self: AtomicOrdering, bw: *std.io.BufferedWriter, comptime prefix: []const u8) anyerror!void {6972 pub fn format(self: AtomicOrdering, bw: *std.io.BufferedWriter, comptime prefix: []const u8) std.io.Writer.Error!void {
6973 if (self != .none) try bw.print("{s}{s}", .{ prefix, @tagName(self) });6973 if (self != .none) try bw.print("{s}{s}", .{ prefix, @tagName(self) });
6974 }6974 }
6975};6975};
...@@ -7385,7 +7385,7 @@ pub const Constant = enum(u32) {...@@ -7385,7 +7385,7 @@ pub const Constant = enum(u32) {
7385 constant: Constant,7385 constant: Constant,
7386 builder: *Builder,7386 builder: *Builder,
7387 };7387 };
7388 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) anyerror!void {7388 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) std.io.Writer.Error!void {
7389 if (comptime std.mem.indexOfNone(u8, fmt_str, ", %")) |_|7389 if (comptime std.mem.indexOfNone(u8, fmt_str, ", %")) |_|
7390 @compileError("invalid format string: '" ++ fmt_str ++ "'");7390 @compileError("invalid format string: '" ++ fmt_str ++ "'");
7391 if (comptime std.mem.indexOfScalar(u8, fmt_str, ',') != null) {7391 if (comptime std.mem.indexOfScalar(u8, fmt_str, ',') != null) {
...@@ -7712,7 +7712,7 @@ pub const Value = enum(u32) {...@@ -7712,7 +7712,7 @@ pub const Value = enum(u32) {
7712 function: Function.Index,7712 function: Function.Index,
7713 builder: *Builder,7713 builder: *Builder,
7714 };7714 };
7715 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) anyerror!void {7715 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) std.io.Writer.Error!void {
7716 switch (data.value.unwrap()) {7716 switch (data.value.unwrap()) {
7717 .instruction => |instruction| try Function.Instruction.Index.format(.{7717 .instruction => |instruction| try Function.Instruction.Index.format(.{
7718 .instruction = instruction,7718 .instruction = instruction,
...@@ -7757,7 +7757,7 @@ pub const MetadataString = enum(u32) {...@@ -7757,7 +7757,7 @@ pub const MetadataString = enum(u32) {
7757 metadata_string: MetadataString,7757 metadata_string: MetadataString,
7758 builder: *const Builder,7758 builder: *const Builder,
7759 };7759 };
7760 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void {7760 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void {
7761 try printEscapedString(data.metadata_string.slice(data.builder), .always_quote, bw);7761 try printEscapedString(data.metadata_string.slice(data.builder), .always_quote, bw);
7762 }7762 }
7763 fn fmt(self: MetadataString, builder: *const Builder) std.fmt.Formatter(format) {7763 fn fmt(self: MetadataString, builder: *const Builder) std.fmt.Formatter(format) {
...@@ -7922,7 +7922,7 @@ pub const Metadata = enum(u32) {...@@ -7922,7 +7922,7 @@ pub const Metadata = enum(u32) {
7922 AllCallsDescribed: bool = false,7922 AllCallsDescribed: bool = false,
7923 Unused: u2 = 0,7923 Unused: u2 = 0,
79247924
7925 pub fn format(self: DIFlags, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void {7925 pub fn format(self: DIFlags, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void {
7926 var need_pipe = false;7926 var need_pipe = false;
7927 inline for (@typeInfo(DIFlags).@"struct".fields) |field| {7927 inline for (@typeInfo(DIFlags).@"struct".fields) |field| {
7928 switch (@typeInfo(field.type)) {7928 switch (@typeInfo(field.type)) {
...@@ -7979,7 +7979,7 @@ pub const Metadata = enum(u32) {...@@ -7979,7 +7979,7 @@ pub const Metadata = enum(u32) {
7979 ObjCDirect: bool = false,7979 ObjCDirect: bool = false,
7980 Unused: u20 = 0,7980 Unused: u20 = 0,
79817981
7982 pub fn format(self: DISPFlags, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void {7982 pub fn format(self: DISPFlags, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void {
7983 var need_pipe = false;7983 var need_pipe = false;
7984 inline for (@typeInfo(DISPFlags).@"struct".fields) |field| {7984 inline for (@typeInfo(DISPFlags).@"struct".fields) |field| {
7985 switch (@typeInfo(field.type)) {7985 switch (@typeInfo(field.type)) {
...@@ -8196,7 +8196,7 @@ pub const Metadata = enum(u32) {...@@ -8196,7 +8196,7 @@ pub const Metadata = enum(u32) {
8196 };8196 };
8197 };8197 };
8198 };8198 };
8199 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) anyerror!void {8199 fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) std.io.Writer.Error!void {
8200 if (data.node == .none) return;8200 if (data.node == .none) return;
82018201
8202 const is_specialized = fmt_str.len > 0 and fmt_str[0] == 'S';8202 const is_specialized = fmt_str.len > 0 and fmt_str[0] == 'S';
...@@ -8371,7 +8371,7 @@ pub const Metadata = enum(u32) {...@@ -8371,7 +8371,7 @@ pub const Metadata = enum(u32) {
8371 },8371 },
8372 nodes: anytype,8372 nodes: anytype,
8373 bw: *std.io.BufferedWriter,8373 bw: *std.io.BufferedWriter,
8374 ) anyerror!void {8374 ) !void {
8375 comptime var fmt_str: []const u8 = "";8375 comptime var fmt_str: []const u8 = "";
8376 const names = comptime std.meta.fieldNames(@TypeOf(nodes));8376 const names = comptime std.meta.fieldNames(@TypeOf(nodes));
8377 comptime var fields: [2 + names.len]std.builtin.Type.StructField = undefined;8377 comptime var fields: [2 + names.len]std.builtin.Type.StructField = undefined;
...@@ -9379,14 +9379,14 @@ pub fn printToFile(self: *Builder, path: []const u8) bool {...@@ -9379,14 +9379,14 @@ pub fn printToFile(self: *Builder, path: []const u8) bool {
9379 return true;9379 return true;
9380}9380}
93819381
9382pub fn printBuffered(self: *Builder, writer: std.io.Writer) anyerror!void {9382pub fn printBuffered(self: *Builder, writer: std.io.Writer) std.io.Writer.Error!void {
9383 var buffer: [4096]u8 = undefined;9383 var buffer: [4096]u8 = undefined;
9384 var bw = writer.buffered(&buffer);9384 var bw = writer.buffered(&buffer);
9385 try self.print(&bw);9385 try self.print(&bw);
9386 try bw.flush();9386 try bw.flush();
9387}9387}
93889388
9389pub fn print(self: *Builder, bw: *std.io.BufferedWriter) anyerror!void {9389pub fn print(self: *Builder, bw: *std.io.BufferedWriter) std.io.Writer.Error!void {
9390 var need_newline = false;9390 var need_newline = false;
9391 var metadata_formatter: Metadata.Formatter = .{ .builder = self, .need_comma = undefined };9391 var metadata_formatter: Metadata.Formatter = .{ .builder = self, .need_comma = undefined };
9392 defer metadata_formatter.map.deinit(self.gpa);9392 defer metadata_formatter.map.deinit(self.gpa);
...@@ -10458,7 +10458,7 @@ fn isValidIdentifier(id: []const u8) bool {...@@ -10458,7 +10458,7 @@ fn isValidIdentifier(id: []const u8) bool {
10458}10458}
1045910459
10460const QuoteBehavior = enum { always_quote, quote_unless_valid_identifier };10460const QuoteBehavior = enum { always_quote, quote_unless_valid_identifier };
10461fn printEscapedString(slice: []const u8, quotes: QuoteBehavior, bw: *std.io.BufferedWriter) anyerror!void {10461fn printEscapedString(slice: []const u8, quotes: QuoteBehavior, bw: *std.io.BufferedWriter) std.io.Writer.Error!void {
10462 const need_quotes = switch (quotes) {10462 const need_quotes = switch (quotes) {
10463 .always_quote => true,10463 .always_quote => true,
10464 .quote_unless_valid_identifier => !isValidIdentifier(slice),10464 .quote_unless_valid_identifier => !isValidIdentifier(slice),
lib/std/zig/render.zig+53-51
...@@ -10,6 +10,8 @@ const primitives = std.zig.primitives;...@@ -10,6 +10,8 @@ const primitives = std.zig.primitives;
10const indent_delta = 4;10const indent_delta = 4;
11const asm_indent_delta = 2;11const asm_indent_delta = 2;
1212
13pub const Error = Ast.RenderError;
14
13pub const Fixups = struct {15pub const Fixups = struct {
14 /// The key is the mut token (`var`/`const`) of the variable declaration16 /// The key is the mut token (`var`/`const`) of the variable declaration
15 /// that should have a `_ = foo;` inserted afterwards.17 /// that should have a `_ = foo;` inserted afterwards.
...@@ -75,7 +77,7 @@ const Render = struct {...@@ -75,7 +77,7 @@ const Render = struct {
75 fixups: Fixups,77 fixups: Fixups,
76};78};
7779
78pub fn renderTree(gpa: Allocator, bw: *std.io.BufferedWriter, tree: Ast, fixups: Fixups) anyerror!void {80pub fn renderTree(gpa: Allocator, bw: *std.io.BufferedWriter, tree: Ast, fixups: Fixups) Error!void {
79 assert(tree.errors.len == 0); // Cannot render an invalid tree.81 assert(tree.errors.len == 0); // Cannot render an invalid tree.
80 var auto_indenting_stream: AutoIndentingStream = .init(gpa, bw, indent_delta);82 var auto_indenting_stream: AutoIndentingStream = .init(gpa, bw, indent_delta);
81 defer auto_indenting_stream.deinit();83 defer auto_indenting_stream.deinit();
...@@ -111,7 +113,7 @@ pub fn renderTree(gpa: Allocator, bw: *std.io.BufferedWriter, tree: Ast, fixups:...@@ -111,7 +113,7 @@ pub fn renderTree(gpa: Allocator, bw: *std.io.BufferedWriter, tree: Ast, fixups:
111}113}
112114
113/// Render all members in the given slice, keeping empty lines where appropriate115/// Render all members in the given slice, keeping empty lines where appropriate
114fn renderMembers(r: *Render, members: []const Ast.Node.Index) anyerror!void {116fn renderMembers(r: *Render, members: []const Ast.Node.Index) Error!void {
115 const tree = r.tree;117 const tree = r.tree;
116 if (members.len == 0) return;118 if (members.len == 0) return;
117 const container: Container = for (members) |member| {119 const container: Container = for (members) |member| {
...@@ -135,7 +137,7 @@ fn renderMember(...@@ -135,7 +137,7 @@ fn renderMember(
135 container: Container,137 container: Container,
136 decl: Ast.Node.Index,138 decl: Ast.Node.Index,
137 space: Space,139 space: Space,
138) anyerror!void {140) Error!void {
139 const tree = r.tree;141 const tree = r.tree;
140 const ais = r.ais;142 const ais = r.ais;
141 if (r.fixups.omit_nodes.contains(decl)) return;143 if (r.fixups.omit_nodes.contains(decl)) return;
...@@ -305,7 +307,7 @@ fn renderMember(...@@ -305,7 +307,7 @@ fn renderMember(
305}307}
306308
307/// Render all expressions in the slice, keeping empty lines where appropriate309/// Render all expressions in the slice, keeping empty lines where appropriate
308fn renderExpressions(r: *Render, expressions: []const Ast.Node.Index, space: Space) anyerror!void {310fn renderExpressions(r: *Render, expressions: []const Ast.Node.Index, space: Space) Error!void {
309 if (expressions.len == 0) return;311 if (expressions.len == 0) return;
310 try renderExpression(r, expressions[0], space);312 try renderExpression(r, expressions[0], space);
311 for (expressions[1..]) |expression| {313 for (expressions[1..]) |expression| {
...@@ -314,7 +316,7 @@ fn renderExpressions(r: *Render, expressions: []const Ast.Node.Index, space: Spa...@@ -314,7 +316,7 @@ fn renderExpressions(r: *Render, expressions: []const Ast.Node.Index, space: Spa
314 }316 }
315}317}
316318
317fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) anyerror!void {319fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
318 const tree = r.tree;320 const tree = r.tree;
319 const ais = r.ais;321 const ais = r.ais;
320 if (r.fixups.replace_nodes_with_string.get(node)) |replacement| {322 if (r.fixups.replace_nodes_with_string.get(node)) |replacement| {
...@@ -886,7 +888,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) anyerror!voi...@@ -886,7 +888,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) anyerror!voi
886888
887/// Same as `renderExpression`, but afterwards looks for any889/// Same as `renderExpression`, but afterwards looks for any
888/// append_string_after_node fixups to apply890/// append_string_after_node fixups to apply
889fn renderExpressionFixup(r: *Render, node: Ast.Node.Index, space: Space) anyerror!void {891fn renderExpressionFixup(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
890 const ais = r.ais;892 const ais = r.ais;
891 try renderExpression(r, node, space);893 try renderExpression(r, node, space);
892 if (r.fixups.append_string_after_node.get(node)) |bytes| {894 if (r.fixups.append_string_after_node.get(node)) |bytes| {
...@@ -898,7 +900,7 @@ fn renderArrayType(...@@ -898,7 +900,7 @@ fn renderArrayType(
898 r: *Render,900 r: *Render,
899 array_type: Ast.full.ArrayType,901 array_type: Ast.full.ArrayType,
900 space: Space,902 space: Space,
901) anyerror!void {903) Error!void {
902 const tree = r.tree;904 const tree = r.tree;
903 const ais = r.ais;905 const ais = r.ais;
904 const rbracket = tree.firstToken(array_type.ast.elem_type) - 1;906 const rbracket = tree.firstToken(array_type.ast.elem_type) - 1;
...@@ -916,7 +918,7 @@ fn renderArrayType(...@@ -916,7 +918,7 @@ fn renderArrayType(
916 return renderExpression(r, array_type.ast.elem_type, space);918 return renderExpression(r, array_type.ast.elem_type, space);
917}919}
918920
919fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) anyerror!void {921fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!void {
920 const tree = r.tree;922 const tree = r.tree;
921 const main_token = ptr_type.ast.main_token;923 const main_token = ptr_type.ast.main_token;
922 switch (ptr_type.size) {924 switch (ptr_type.size) {
...@@ -1010,7 +1012,7 @@ fn renderSlice(...@@ -1010,7 +1012,7 @@ fn renderSlice(
1010 slice_node: Ast.Node.Index,1012 slice_node: Ast.Node.Index,
1011 slice: Ast.full.Slice,1013 slice: Ast.full.Slice,
1012 space: Space,1014 space: Space,
1013) anyerror!void {1015) Error!void {
1014 const tree = r.tree;1016 const tree = r.tree;
1015 const after_start_space_bool = nodeCausesSliceOpSpace(tree.nodeTag(slice.ast.start)) or1017 const after_start_space_bool = nodeCausesSliceOpSpace(tree.nodeTag(slice.ast.start)) or
1016 if (slice.ast.end.unwrap()) |end| nodeCausesSliceOpSpace(tree.nodeTag(end)) else false;1018 if (slice.ast.end.unwrap()) |end| nodeCausesSliceOpSpace(tree.nodeTag(end)) else false;
...@@ -1043,7 +1045,7 @@ fn renderAsmOutput(...@@ -1043,7 +1045,7 @@ fn renderAsmOutput(
1043 r: *Render,1045 r: *Render,
1044 asm_output: Ast.Node.Index,1046 asm_output: Ast.Node.Index,
1045 space: Space,1047 space: Space,
1046) anyerror!void {1048) Error!void {
1047 const tree = r.tree;1049 const tree = r.tree;
1048 assert(tree.nodeTag(asm_output) == .asm_output);1050 assert(tree.nodeTag(asm_output) == .asm_output);
1049 const symbolic_name = tree.nodeMainToken(asm_output);1051 const symbolic_name = tree.nodeMainToken(asm_output);
...@@ -1069,7 +1071,7 @@ fn renderAsmInput(...@@ -1069,7 +1071,7 @@ fn renderAsmInput(
1069 r: *Render,1071 r: *Render,
1070 asm_input: Ast.Node.Index,1072 asm_input: Ast.Node.Index,
1071 space: Space,1073 space: Space,
1072) anyerror!void {1074) Error!void {
1073 const tree = r.tree;1075 const tree = r.tree;
1074 assert(tree.nodeTag(asm_input) == .asm_input);1076 assert(tree.nodeTag(asm_input) == .asm_input);
1075 const symbolic_name = tree.nodeMainToken(asm_input);1077 const symbolic_name = tree.nodeMainToken(asm_input);
...@@ -1091,7 +1093,7 @@ fn renderVarDecl(...@@ -1091,7 +1093,7 @@ fn renderVarDecl(
1091 ignore_comptime_token: bool,1093 ignore_comptime_token: bool,
1092 /// `comma_space` and `space` are used for destructure LHS decls.1094 /// `comma_space` and `space` are used for destructure LHS decls.
1093 space: Space,1095 space: Space,
1094) anyerror!void {1096) Error!void {
1095 try renderVarDeclWithoutFixups(r, var_decl, ignore_comptime_token, space);1097 try renderVarDeclWithoutFixups(r, var_decl, ignore_comptime_token, space);
1096 if (r.fixups.unused_var_decls.contains(var_decl.ast.mut_token + 1)) {1098 if (r.fixups.unused_var_decls.contains(var_decl.ast.mut_token + 1)) {
1097 // Discard the variable like this: `_ = foo;`1099 // Discard the variable like this: `_ = foo;`
...@@ -1109,7 +1111,7 @@ fn renderVarDeclWithoutFixups(...@@ -1109,7 +1111,7 @@ fn renderVarDeclWithoutFixups(
1109 ignore_comptime_token: bool,1111 ignore_comptime_token: bool,
1110 /// `comma_space` and `space` are used for destructure LHS decls.1112 /// `comma_space` and `space` are used for destructure LHS decls.
1111 space: Space,1113 space: Space,
1112) anyerror!void {1114) Error!void {
1113 const tree = r.tree;1115 const tree = r.tree;
1114 const ais = r.ais;1116 const ais = r.ais;
11151117
...@@ -1221,7 +1223,7 @@ fn renderVarDeclWithoutFixups(...@@ -1221,7 +1223,7 @@ fn renderVarDeclWithoutFixups(
1221 ais.popIndent();1223 ais.popIndent();
1222}1224}
12231225
1224fn renderIf(r: *Render, if_node: Ast.full.If, space: Space) anyerror!void {1226fn renderIf(r: *Render, if_node: Ast.full.If, space: Space) Error!void {
1225 return renderWhile(r, .{1227 return renderWhile(r, .{
1226 .ast = .{1228 .ast = .{
1227 .while_token = if_node.ast.if_token,1229 .while_token = if_node.ast.if_token,
...@@ -1240,7 +1242,7 @@ fn renderIf(r: *Render, if_node: Ast.full.If, space: Space) anyerror!void {...@@ -1240,7 +1242,7 @@ fn renderIf(r: *Render, if_node: Ast.full.If, space: Space) anyerror!void {
12401242
1241/// Note that this function is additionally used to render if expressions, with1243/// Note that this function is additionally used to render if expressions, with
1242/// respective values set to null.1244/// respective values set to null.
1243fn renderWhile(r: *Render, while_node: Ast.full.While, space: Space) anyerror!void {1245fn renderWhile(r: *Render, while_node: Ast.full.While, space: Space) Error!void {
1244 const tree = r.tree;1246 const tree = r.tree;
12451247
1246 if (while_node.label_token) |label| {1248 if (while_node.label_token) |label| {
...@@ -1310,7 +1312,7 @@ fn renderThenElse(...@@ -1310,7 +1312,7 @@ fn renderThenElse(
1310 maybe_error_token: ?Ast.TokenIndex,1312 maybe_error_token: ?Ast.TokenIndex,
1311 opt_else_expr: Ast.Node.OptionalIndex,1313 opt_else_expr: Ast.Node.OptionalIndex,
1312 space: Space,1314 space: Space,
1313) anyerror!void {1315) Error!void {
1314 const tree = r.tree;1316 const tree = r.tree;
1315 const ais = r.ais;1317 const ais = r.ais;
1316 const then_expr_is_block = nodeIsBlock(tree.nodeTag(then_expr));1318 const then_expr_is_block = nodeIsBlock(tree.nodeTag(then_expr));
...@@ -1365,7 +1367,7 @@ fn renderThenElse(...@@ -1365,7 +1367,7 @@ fn renderThenElse(
1365 }1367 }
1366}1368}
13671369
1368fn renderFor(r: *Render, for_node: Ast.full.For, space: Space) anyerror!void {1370fn renderFor(r: *Render, for_node: Ast.full.For, space: Space) Error!void {
1369 const tree = r.tree;1371 const tree = r.tree;
1370 const ais = r.ais;1372 const ais = r.ais;
1371 const token_tags = tree.tokens.items(.tag);1373 const token_tags = tree.tokens.items(.tag);
...@@ -1440,7 +1442,7 @@ fn renderContainerField(...@@ -1440,7 +1442,7 @@ fn renderContainerField(
1440 container: Container,1442 container: Container,
1441 field_param: Ast.full.ContainerField,1443 field_param: Ast.full.ContainerField,
1442 space: Space,1444 space: Space,
1443) anyerror!void {1445) Error!void {
1444 const tree = r.tree;1446 const tree = r.tree;
1445 const ais = r.ais;1447 const ais = r.ais;
1446 var field = field_param;1448 var field = field_param;
...@@ -1549,7 +1551,7 @@ fn renderBuiltinCall(...@@ -1549,7 +1551,7 @@ fn renderBuiltinCall(
1549 builtin_token: Ast.TokenIndex,1551 builtin_token: Ast.TokenIndex,
1550 params: []const Ast.Node.Index,1552 params: []const Ast.Node.Index,
1551 space: Space,1553 space: Space,
1552) anyerror!void {1554) Error!void {
1553 const tree = r.tree;1555 const tree = r.tree;
1554 const ais = r.ais;1556 const ais = r.ais;
15551557
...@@ -1622,7 +1624,7 @@ fn renderBuiltinCall(...@@ -1622,7 +1624,7 @@ fn renderBuiltinCall(
1622 }1624 }
1623}1625}
16241626
1625fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) anyerror!void {1627fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!void {
1626 const tree = r.tree;1628 const tree = r.tree;
1627 const ais = r.ais;1629 const ais = r.ais;
16281630
...@@ -1847,7 +1849,7 @@ fn renderSwitchCase(...@@ -1847,7 +1849,7 @@ fn renderSwitchCase(
1847 r: *Render,1849 r: *Render,
1848 switch_case: Ast.full.SwitchCase,1850 switch_case: Ast.full.SwitchCase,
1849 space: Space,1851 space: Space,
1850) anyerror!void {1852) Error!void {
1851 const ais = r.ais;1853 const ais = r.ais;
1852 const tree = r.tree;1854 const tree = r.tree;
1853 const trailing_comma = tree.tokenTag(switch_case.ast.arrow_token - 1) == .comma;1855 const trailing_comma = tree.tokenTag(switch_case.ast.arrow_token - 1) == .comma;
...@@ -1909,7 +1911,7 @@ fn renderBlock(...@@ -1909,7 +1911,7 @@ fn renderBlock(
1909 block_node: Ast.Node.Index,1911 block_node: Ast.Node.Index,
1910 statements: []const Ast.Node.Index,1912 statements: []const Ast.Node.Index,
1911 space: Space,1913 space: Space,
1912) anyerror!void {1914) Error!void {
1913 const tree = r.tree;1915 const tree = r.tree;
1914 const ais = r.ais;1916 const ais = r.ais;
1915 const lbrace = tree.nodeMainToken(block_node);1917 const lbrace = tree.nodeMainToken(block_node);
...@@ -1934,7 +1936,7 @@ fn finishRenderBlock(...@@ -1934,7 +1936,7 @@ fn finishRenderBlock(
1934 block_node: Ast.Node.Index,1936 block_node: Ast.Node.Index,
1935 statements: []const Ast.Node.Index,1937 statements: []const Ast.Node.Index,
1936 space: Space,1938 space: Space,
1937) anyerror!void {1939) Error!void {
1938 const tree = r.tree;1940 const tree = r.tree;
1939 const ais = r.ais;1941 const ais = r.ais;
1940 for (statements, 0..) |stmt, i| {1942 for (statements, 0..) |stmt, i| {
...@@ -1962,7 +1964,7 @@ fn renderStructInit(...@@ -1962,7 +1964,7 @@ fn renderStructInit(
1962 struct_node: Ast.Node.Index,1964 struct_node: Ast.Node.Index,
1963 struct_init: Ast.full.StructInit,1965 struct_init: Ast.full.StructInit,
1964 space: Space,1966 space: Space,
1965) anyerror!void {1967) Error!void {
1966 const tree = r.tree;1968 const tree = r.tree;
1967 const ais = r.ais;1969 const ais = r.ais;
19681970
...@@ -2033,7 +2035,7 @@ fn renderArrayInit(...@@ -2033,7 +2035,7 @@ fn renderArrayInit(
2033 r: *Render,2035 r: *Render,
2034 array_init: Ast.full.ArrayInit,2036 array_init: Ast.full.ArrayInit,
2035 space: Space,2037 space: Space,
2036) anyerror!void {2038) Error!void {
2037 const tree = r.tree;2039 const tree = r.tree;
2038 const ais = r.ais;2040 const ais = r.ais;
2039 const gpa = r.gpa;2041 const gpa = r.gpa;
...@@ -2263,7 +2265,7 @@ fn renderContainerDecl(...@@ -2263,7 +2265,7 @@ fn renderContainerDecl(
2263 container_decl_node: Ast.Node.Index,2265 container_decl_node: Ast.Node.Index,
2264 container_decl: Ast.full.ContainerDecl,2266 container_decl: Ast.full.ContainerDecl,
2265 space: Space,2267 space: Space,
2266) anyerror!void {2268) Error!void {
2267 const tree = r.tree;2269 const tree = r.tree;
2268 const ais = r.ais;2270 const ais = r.ais;
22692271
...@@ -2382,7 +2384,7 @@ fn renderAsm(...@@ -2382,7 +2384,7 @@ fn renderAsm(
2382 r: *Render,2384 r: *Render,
2383 asm_node: Ast.full.Asm,2385 asm_node: Ast.full.Asm,
2384 space: Space,2386 space: Space,
2385) anyerror!void {2387) Error!void {
2386 const tree = r.tree;2388 const tree = r.tree;
2387 const ais = r.ais;2389 const ais = r.ais;
23882390
...@@ -2548,7 +2550,7 @@ fn renderCall(...@@ -2548,7 +2550,7 @@ fn renderCall(
2548 r: *Render,2550 r: *Render,
2549 call: Ast.full.Call,2551 call: Ast.full.Call,
2550 space: Space,2552 space: Space,
2551) anyerror!void {2553) Error!void {
2552 if (call.async_token) |async_token| {2554 if (call.async_token) |async_token| {
2553 try renderToken(r, async_token, .space);2555 try renderToken(r, async_token, .space);
2554 }2556 }
...@@ -2561,7 +2563,7 @@ fn renderParamList(...@@ -2561,7 +2563,7 @@ fn renderParamList(
2561 lparen: Ast.TokenIndex,2563 lparen: Ast.TokenIndex,
2562 params: []const Ast.Node.Index,2564 params: []const Ast.Node.Index,
2563 space: Space,2565 space: Space,
2564) anyerror!void {2566) Error!void {
2565 const tree = r.tree;2567 const tree = r.tree;
2566 const ais = r.ais;2568 const ais = r.ais;
25672569
...@@ -2614,7 +2616,7 @@ fn renderParamList(...@@ -2614,7 +2616,7 @@ fn renderParamList(
26142616
2615/// Render an expression, and the comma that follows it, if it is present in the source.2617/// Render an expression, and the comma that follows it, if it is present in the source.
2616/// If a comma is present, and `space` is `Space.comma`, render only a single comma.2618/// If a comma is present, and `space` is `Space.comma`, render only a single comma.
2617fn renderExpressionComma(r: *Render, node: Ast.Node.Index, space: Space) anyerror!void {2619fn renderExpressionComma(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
2618 const tree = r.tree;2620 const tree = r.tree;
2619 const maybe_comma = tree.lastToken(node) + 1;2621 const maybe_comma = tree.lastToken(node) + 1;
2620 if (tree.tokenTag(maybe_comma) == .comma and space != .comma) {2622 if (tree.tokenTag(maybe_comma) == .comma and space != .comma) {
...@@ -2627,7 +2629,7 @@ fn renderExpressionComma(r: *Render, node: Ast.Node.Index, space: Space) anyerro...@@ -2627,7 +2629,7 @@ fn renderExpressionComma(r: *Render, node: Ast.Node.Index, space: Space) anyerro
26272629
2628/// Render a token, and the comma that follows it, if it is present in the source.2630/// Render a token, and the comma that follows it, if it is present in the source.
2629/// If a comma is present, and `space` is `Space.comma`, render only a single comma.2631/// If a comma is present, and `space` is `Space.comma`, render only a single comma.
2630fn renderTokenComma(r: *Render, token: Ast.TokenIndex, space: Space) anyerror!void {2632fn renderTokenComma(r: *Render, token: Ast.TokenIndex, space: Space) Error!void {
2631 const tree = r.tree;2633 const tree = r.tree;
2632 const maybe_comma = token + 1;2634 const maybe_comma = token + 1;
2633 if (tree.tokenTag(maybe_comma) == .comma and space != .comma) {2635 if (tree.tokenTag(maybe_comma) == .comma and space != .comma) {
...@@ -2640,7 +2642,7 @@ fn renderTokenComma(r: *Render, token: Ast.TokenIndex, space: Space) anyerror!vo...@@ -2640,7 +2642,7 @@ fn renderTokenComma(r: *Render, token: Ast.TokenIndex, space: Space) anyerror!vo
26402642
2641/// Render an identifier, and the comma that follows it, if it is present in the source.2643/// Render an identifier, and the comma that follows it, if it is present in the source.
2642/// If a comma is present, and `space` is `Space.comma`, render only a single comma.2644/// If a comma is present, and `space` is `Space.comma`, render only a single comma.
2643fn renderIdentifierComma(r: *Render, token: Ast.TokenIndex, space: Space, quote: QuoteBehavior) anyerror!void {2645fn renderIdentifierComma(r: *Render, token: Ast.TokenIndex, space: Space, quote: QuoteBehavior) Error!void {
2644 const tree = r.tree;2646 const tree = r.tree;
2645 const maybe_comma = token + 1;2647 const maybe_comma = token + 1;
2646 if (tree.tokenTag(maybe_comma) == .comma and space != .comma) {2648 if (tree.tokenTag(maybe_comma) == .comma and space != .comma) {
...@@ -2672,7 +2674,7 @@ const Space = enum {...@@ -2672,7 +2674,7 @@ const Space = enum {
2672 skip,2674 skip,
2673};2675};
26742676
2675fn renderToken(r: *Render, token_index: Ast.TokenIndex, space: Space) anyerror!void {2677fn renderToken(r: *Render, token_index: Ast.TokenIndex, space: Space) Error!void {
2676 const tree = r.tree;2678 const tree = r.tree;
2677 const ais = r.ais;2679 const ais = r.ais;
2678 const lexeme = tokenSliceForRender(tree, token_index);2680 const lexeme = tokenSliceForRender(tree, token_index);
...@@ -2680,7 +2682,7 @@ fn renderToken(r: *Render, token_index: Ast.TokenIndex, space: Space) anyerror!v...@@ -2680,7 +2682,7 @@ fn renderToken(r: *Render, token_index: Ast.TokenIndex, space: Space) anyerror!v
2680 try renderSpace(r, token_index, lexeme.len, space);2682 try renderSpace(r, token_index, lexeme.len, space);
2681}2683}
26822684
2683fn renderTokenOverrideSpaceMode(r: *Render, token_index: Ast.TokenIndex, space: Space, override_space: Space) anyerror!void {2685fn renderTokenOverrideSpaceMode(r: *Render, token_index: Ast.TokenIndex, space: Space, override_space: Space) Error!void {
2684 const tree = r.tree;2686 const tree = r.tree;
2685 const ais = r.ais;2687 const ais = r.ais;
2686 const lexeme = tokenSliceForRender(tree, token_index);2688 const lexeme = tokenSliceForRender(tree, token_index);
...@@ -2690,7 +2692,7 @@ fn renderTokenOverrideSpaceMode(r: *Render, token_index: Ast.TokenIndex, space:...@@ -2690,7 +2692,7 @@ fn renderTokenOverrideSpaceMode(r: *Render, token_index: Ast.TokenIndex, space:
2690 try renderSpace(r, token_index, lexeme.len, space);2692 try renderSpace(r, token_index, lexeme.len, space);
2691}2693}
26922694
2693fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space: Space) anyerror!void {2695fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space: Space) Error!void {
2694 const tree = r.tree;2696 const tree = r.tree;
2695 const ais = r.ais;2697 const ais = r.ais;
26962698
...@@ -2735,7 +2737,7 @@ fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space...@@ -2735,7 +2737,7 @@ fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space
2735 }2737 }
2736}2738}
27372739
2738fn renderOnlySpace(r: *Render, space: Space) anyerror!void {2740fn renderOnlySpace(r: *Render, space: Space) Error!void {
2739 const ais = r.ais;2741 const ais = r.ais;
2740 switch (space) {2742 switch (space) {
2741 .none => {},2743 .none => {},
...@@ -2754,7 +2756,7 @@ const QuoteBehavior = enum {...@@ -2754,7 +2756,7 @@ const QuoteBehavior = enum {
2754 eagerly_unquote_except_underscore,2756 eagerly_unquote_except_underscore,
2755};2757};
27562758
2757fn renderIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space, quote: QuoteBehavior) anyerror!void {2759fn renderIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space, quote: QuoteBehavior) Error!void {
2758 const tree = r.tree;2760 const tree = r.tree;
2759 assert(tree.tokenTag(token_index) == .identifier);2761 assert(tree.tokenTag(token_index) == .identifier);
2760 const lexeme = tokenSliceForRender(tree, token_index);2762 const lexeme = tokenSliceForRender(tree, token_index);
...@@ -2940,7 +2942,7 @@ fn hasMultilineString(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.Tok...@@ -2940,7 +2942,7 @@ fn hasMultilineString(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.Tok
29402942
2941/// Assumes that start is the first byte past the previous token and2943/// Assumes that start is the first byte past the previous token and
2942/// that end is the last byte before the next token.2944/// that end is the last byte before the next token.
2943fn renderComments(r: *Render, start: usize, end: usize) anyerror!bool {2945fn renderComments(r: *Render, start: usize, end: usize) Error!bool {
2944 const tree = r.tree;2946 const tree = r.tree;
2945 const ais = r.ais;2947 const ais = r.ais;
29462948
...@@ -3003,12 +3005,12 @@ fn renderComments(r: *Render, start: usize, end: usize) anyerror!bool {...@@ -3003,12 +3005,12 @@ fn renderComments(r: *Render, start: usize, end: usize) anyerror!bool {
3003 return index != start;3005 return index != start;
3004}3006}
30053007
3006fn renderExtraNewline(r: *Render, node: Ast.Node.Index) anyerror!void {3008fn renderExtraNewline(r: *Render, node: Ast.Node.Index) Error!void {
3007 return renderExtraNewlineToken(r, r.tree.firstToken(node));3009 return renderExtraNewlineToken(r, r.tree.firstToken(node));
3008}3010}
30093011
3010/// Check if there is an empty line immediately before the given token. If so, render it.3012/// Check if there is an empty line immediately before the given token. If so, render it.
3011fn renderExtraNewlineToken(r: *Render, token_index: Ast.TokenIndex) anyerror!void {3013fn renderExtraNewlineToken(r: *Render, token_index: Ast.TokenIndex) Error!void {
3012 const tree = r.tree;3014 const tree = r.tree;
3013 const ais = r.ais;3015 const ais = r.ais;
3014 const token_start = tree.tokenStart(token_index);3016 const token_start = tree.tokenStart(token_index);
...@@ -3036,7 +3038,7 @@ fn renderExtraNewlineToken(r: *Render, token_index: Ast.TokenIndex) anyerror!voi...@@ -3036,7 +3038,7 @@ fn renderExtraNewlineToken(r: *Render, token_index: Ast.TokenIndex) anyerror!voi
30363038
3037/// end_token is the token one past the last doc comment token. This function3039/// end_token is the token one past the last doc comment token. This function
3038/// searches backwards from there.3040/// searches backwards from there.
3039fn renderDocComments(r: *Render, end_token: Ast.TokenIndex) anyerror!void {3041fn renderDocComments(r: *Render, end_token: Ast.TokenIndex) Error!void {
3040 const tree = r.tree;3042 const tree = r.tree;
3041 // Search backwards for the first doc comment.3043 // Search backwards for the first doc comment.
3042 if (end_token == 0) return;3044 if (end_token == 0) return;
...@@ -3067,7 +3069,7 @@ fn renderDocComments(r: *Render, end_token: Ast.TokenIndex) anyerror!void {...@@ -3067,7 +3069,7 @@ fn renderDocComments(r: *Render, end_token: Ast.TokenIndex) anyerror!void {
3067}3069}
30683070
3069/// start_token is first container doc comment token.3071/// start_token is first container doc comment token.
3070fn renderContainerDocComments(r: *Render, start_token: Ast.TokenIndex) anyerror!void {3072fn renderContainerDocComments(r: *Render, start_token: Ast.TokenIndex) Error!void {
3071 const tree = r.tree;3073 const tree = r.tree;
3072 var tok = start_token;3074 var tok = start_token;
3073 while (tree.tokenTag(tok) == .container_doc_comment) : (tok += 1) {3075 while (tree.tokenTag(tok) == .container_doc_comment) : (tok += 1) {
...@@ -3081,7 +3083,7 @@ fn renderContainerDocComments(r: *Render, start_token: Ast.TokenIndex) anyerror!...@@ -3081,7 +3083,7 @@ fn renderContainerDocComments(r: *Render, start_token: Ast.TokenIndex) anyerror!
3081 }3083 }
3082}3084}
30833085
3084fn discardAllParams(r: *Render, fn_proto_node: Ast.Node.Index) anyerror!void {3086fn discardAllParams(r: *Render, fn_proto_node: Ast.Node.Index) Error!void {
3085 const tree = &r.tree;3087 const tree = &r.tree;
3086 const ais = r.ais;3088 const ais = r.ais;
3087 var buf: [1]Ast.Node.Index = undefined;3089 var buf: [1]Ast.Node.Index = undefined;
...@@ -3129,7 +3131,7 @@ fn anythingBetween(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenI...@@ -3129,7 +3131,7 @@ fn anythingBetween(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenI
3129 return false;3131 return false;
3130}3132}
31313133
3132fn writeFixingWhitespace(bw: *std.io.BufferedWriter, slice: []const u8) anyerror!void {3134fn writeFixingWhitespace(bw: *std.io.BufferedWriter, slice: []const u8) Error!void {
3133 for (slice) |byte| switch (byte) {3135 for (slice) |byte| switch (byte) {
3134 '\t' => try bw.splatByteAll(' ', indent_delta),3136 '\t' => try bw.splatByteAll(' ', indent_delta),
3135 '\r' => {},3137 '\r' => {},
...@@ -3308,7 +3310,7 @@ const AutoIndentingStream = struct {...@@ -3308,7 +3310,7 @@ const AutoIndentingStream = struct {
3308 self.space_stack.deinit();3310 self.space_stack.deinit();
3309 }3311 }
33103312
3311 pub fn writeAll(ais: *AutoIndentingStream, bytes: []const u8) anyerror!void {3313 pub fn writeAll(ais: *AutoIndentingStream, bytes: []const u8) Error!void {
3312 if (bytes.len == 0) return;3314 if (bytes.len == 0) return;
3313 try ais.applyIndent();3315 try ais.applyIndent();
3314 if (ais.disabled_offset == null) try ais.underlying_writer.writeAll(bytes);3316 if (ais.disabled_offset == null) try ais.underlying_writer.writeAll(bytes);
...@@ -3317,19 +3319,19 @@ const AutoIndentingStream = struct {...@@ -3317,19 +3319,19 @@ const AutoIndentingStream = struct {
33173319
3318 /// Assumes that if the printed data ends with a newline, it is directly3320 /// Assumes that if the printed data ends with a newline, it is directly
3319 /// contained in the format string.3321 /// contained in the format string.
3320 pub fn print(ais: *AutoIndentingStream, comptime format: []const u8, args: anytype) anyerror!void {3322 pub fn print(ais: *AutoIndentingStream, comptime format: []const u8, args: anytype) Error!void {
3321 try ais.applyIndent();3323 try ais.applyIndent();
3322 if (ais.disabled_offset == null) try ais.underlying_writer.print(format, args);3324 if (ais.disabled_offset == null) try ais.underlying_writer.print(format, args);
3323 if (format[format.len - 1] == '\n') ais.resetLine();3325 if (format[format.len - 1] == '\n') ais.resetLine();
3324 }3326 }
33253327
3326 pub fn writeByte(ais: *AutoIndentingStream, byte: u8) anyerror!void {3328 pub fn writeByte(ais: *AutoIndentingStream, byte: u8) Error!void {
3327 try ais.applyIndent();3329 try ais.applyIndent();
3328 if (ais.disabled_offset == null) try ais.underlying_writer.writeByte(byte);3330 if (ais.disabled_offset == null) try ais.underlying_writer.writeByte(byte);
3329 assert(byte != '\n');3331 assert(byte != '\n');
3330 }3332 }
33313333
3332 pub fn splatByteAll(ais: *AutoIndentingStream, byte: u8, n: usize) anyerror!void {3334 pub fn splatByteAll(ais: *AutoIndentingStream, byte: u8, n: usize) Error!void {
3333 assert(byte != '\n');3335 assert(byte != '\n');
3334 try ais.applyIndent();3336 try ais.applyIndent();
3335 if (ais.disabled_offset == null) try ais.underlying_writer.splatByteAll(byte, n);3337 if (ais.disabled_offset == null) try ais.underlying_writer.splatByteAll(byte, n);
...@@ -3350,13 +3352,13 @@ const AutoIndentingStream = struct {...@@ -3350,13 +3352,13 @@ const AutoIndentingStream = struct {
3350 ais.indent_delta = new_indent_delta;3352 ais.indent_delta = new_indent_delta;
3351 }3353 }
33523354
3353 pub fn insertNewline(ais: *AutoIndentingStream) anyerror!void {3355 pub fn insertNewline(ais: *AutoIndentingStream) Error!void {
3354 if (ais.disabled_offset == null) try ais.underlying_writer.writeByte('\n');3356 if (ais.disabled_offset == null) try ais.underlying_writer.writeByte('\n');
3355 ais.resetLine();3357 ais.resetLine();
3356 }3358 }
33573359
3358 /// Insert a newline unless the current line is blank3360 /// Insert a newline unless the current line is blank
3359 pub fn maybeInsertNewline(ais: *AutoIndentingStream) anyerror!void {3361 pub fn maybeInsertNewline(ais: *AutoIndentingStream) Error!void {
3360 if (!ais.current_line_empty)3362 if (!ais.current_line_empty)
3361 try ais.insertNewline();3363 try ais.insertNewline();
3362 }3364 }
...@@ -3483,7 +3485,7 @@ const AutoIndentingStream = struct {...@@ -3483,7 +3485,7 @@ const AutoIndentingStream = struct {
3483 }3485 }
34843486
3485 /// Writes ' ' bytes if the current line is empty3487 /// Writes ' ' bytes if the current line is empty
3486 fn applyIndent(ais: *AutoIndentingStream) anyerror!void {3488 fn applyIndent(ais: *AutoIndentingStream) Error!void {
3487 const current_indent = ais.currentIndent();3489 const current_indent = ais.currentIndent();
3488 if (ais.current_line_empty and current_indent > 0) {3490 if (ais.current_line_empty and current_indent > 0) {
3489 if (ais.disabled_offset == null) {3491 if (ais.disabled_offset == null) {
lib/ubsan_rt.zig+1-1
...@@ -119,7 +119,7 @@ const Value = extern struct {...@@ -119,7 +119,7 @@ const Value = extern struct {
119 }119 }
120 }120 }
121121
122 pub fn format(value: Value, bw: *std.io.BufferedWriter, comptime fmt: []const u8) anyerror!void {122 pub fn format(value: Value, bw: *std.io.BufferedWriter, comptime fmt: []const u8) !void {
123 comptime assert(fmt.len == 0);123 comptime assert(fmt.len == 0);
124124
125 // Work around x86_64 backend limitation.125 // Work around x86_64 backend limitation.