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 @@
22const builtin = @import("builtin");
33
44const std = @import("std");
5const io = std.io;
65const testing = std.testing;
76const assert = std.debug.assert;
87
......@@ -65,15 +64,21 @@ pub fn main() void {
6564 }
6665}
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
6870fn mainServer() !void {
6971 @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);
7077 var server = try std.zig.Server.init(.{
71 .gpa = fba.allocator(),
72 .in = .stdin(),
73 .out = .stdout(),
78 .in = &stdin_buffered_reader,
79 .out = &stdout_buffered_writer,
7480 .zig_version = builtin.zig_version_string,
7581 });
76 defer server.deinit();
7782
7883 if (builtin.fuzz) {
7984 const coverage_id = fuzzer_coverage_id();
lib/std/Build.zig+2-3
......@@ -2766,9 +2766,8 @@ fn dumpBadDirnameHelp(
27662766 comptime msg: []const u8,
27672767 args: anytype,
27682768) anyerror!void {
2769 var buffered_writer = debug.lockStdErr2(&.{});
2770 defer debug.unlockStdErr();
2771 const w = &buffered_writer;
2769 const w = debug.lockStderrWriter();
2770 defer debug.unlockStderrWriter();
27722771
27732772 const stderr: fs.File = .stderr();
27742773 try w.print(msg, args);
lib/std/Build/Cache.zig+1-1
......@@ -333,7 +333,7 @@ pub const Manifest = struct {
333333 pub const Diagnostic = union(enum) {
334334 none,
335335 manifest_create: fs.File.OpenError,
336 manifest_read: anyerror,
336 manifest_read: fs.File.ReadError,
337337 manifest_lock: fs.File.LockError,
338338 manifest_seek: fs.File.SeekError,
339339 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
124124 const show_stderr = compile.step.result_stderr.len > 0;
125125
126126 if (show_error_msgs or show_compile_errors or show_stderr) {
127 var bw = std.debug.lockStdErr2(&.{});
128 defer std.debug.unlockStdErr();
129 build_runner.printErrorMessages(gpa, &compile.step, .{ .ttyconf = ttyconf }, &bw, false) catch {};
127 const bw = std.debug.lockStderrWriter();
128 defer std.debug.unlockStderrWriter();
129 build_runner.printErrorMessages(gpa, &compile.step, .{ .ttyconf = ttyconf }, bw, false) catch {};
130130 }
131131
132132 const rebuilt_bin_path = result catch |err| switch (err) {
......@@ -151,9 +151,9 @@ fn fuzzWorkerRun(
151151
152152 run.rerunInFuzzMode(web_server, unit_test_index, prog_node) catch |err| switch (err) {
153153 error.MakeFailed => {
154 var bw = std.debug.lockStdErr2(&.{});
155 defer std.debug.unlockStdErr();
156 build_runner.printErrorMessages(gpa, &run.step, .{ .ttyconf = ttyconf }, &bw, false) catch {};
154 const bw = std.debug.lockStderrWriter();
155 defer std.debug.unlockStderrWriter();
156 build_runner.printErrorMessages(gpa, &run.step, .{ .ttyconf = ttyconf }, bw, false) catch {};
157157 return;
158158 },
159159 else => {
lib/std/Build/Step/CheckObject.zig+1-1
......@@ -233,7 +233,7 @@ const ComputeCompareExpected = struct {
233233 value: ComputeCompareExpected,
234234 bw: *std.io.BufferedWriter,
235235 comptime fmt: []const u8,
236 ) anyerror!void {
236 ) !void {
237237 if (fmt.len != 0) std.fmt.invalidFmtError(fmt, value);
238238 try bw.print("{s} ", .{@tagName(value.op)});
239239 switch (value.value) {
lib/std/Progress.zig+32
......@@ -606,6 +606,38 @@ pub fn unlockStdErr() void {
606606 stderr_mutex.unlock();
607607}
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
609641fn ipcThreadRun(fd: posix.fd_t) anyerror!void {
610642 // Store this data in the thread so that it does not need to be part of the
611643 // linker data of the main executable.
lib/std/Target.zig+1-1
......@@ -301,7 +301,7 @@ pub const Os = struct {
301301
302302 /// This function is defined to serialize a Zig source code representation of this
303303 /// 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 {
305305 const maybe_name = std.enums.tagName(WindowsVersion, ver);
306306 if (comptime std.mem.eql(u8, fmt_str, "s")) {
307307 if (maybe_name) |name|
lib/std/Uri.zig+4-4
......@@ -40,7 +40,7 @@ pub const Component = union(enum) {
4040 };
4141 }
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 {
4444 if (fmt.len == 0) {
4545 try bw.print("std.Uri.Component{{ .{s} = \"{}\" }}", .{
4646 @tagName(component),
......@@ -95,7 +95,7 @@ pub const Component = union(enum) {
9595 bw: *std.io.BufferedWriter,
9696 raw: []const u8,
9797 comptime isValidChar: fn (u8) bool,
98 ) anyerror!void {
98 ) std.io.Writer.Error!void {
9999 var start: usize = 0;
100100 for (raw, 0..) |char, index| {
101101 if (isValidChar(char)) continue;
......@@ -236,7 +236,7 @@ pub const WriteToStreamOptions = struct {
236236 port: bool = true,
237237};
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 {
240240 if (options.scheme) {
241241 try bw.print("{s}:", .{uri.scheme});
242242 if (options.authority and uri.host != null) {
......@@ -273,7 +273,7 @@ pub fn writeToStream(uri: Uri, options: WriteToStreamOptions, bw: *std.io.Buffer
273273 }
274274}
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 {
277277 const scheme = comptime std.mem.indexOfScalar(u8, fmt, ';') != null or fmt.len == 0;
278278 const authentication = comptime std.mem.indexOfScalar(u8, fmt, '@') != null or fmt.len == 0;
279279 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
908908 var aw: std.io.AllocatingWriter = undefined;
909909 const bw = aw.fromArrayList(gpa, self);
910910 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 };
912914 }
913915
914916 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 {
3434 index: usize,
3535 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 {
3838 comptime if (fmt.len != 0) unreachable;
3939
4040 // 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");
99pub const inflate = @import("flate/inflate.zig");
1010
1111/// 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 {
1313 try inflate.decompress(.raw, reader, writer);
1414}
1515
......@@ -19,7 +19,7 @@ pub const Decompressor = inflate.Decompressor(.raw);
1919pub const Options = deflate.Options;
2020
2121/// 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 {
2323 try deflate.compress(.raw, reader, writer, options);
2424}
2525
......@@ -28,7 +28,7 @@ pub const Compressor = deflate.Compressor(.raw);
2828/// Huffman only compression. Without Lempel-Ziv match searching. Faster
2929/// compression, less memory requirements but bigger compressed sizes.
3030pub 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 {
3232 try deflate.huffman.compress(.raw, reader, writer);
3333 }
3434
......@@ -37,7 +37,7 @@ pub const huffman = struct {
3737
3838// No compression store only. Compressed size is slightly bigger than plain.
3939pub 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 {
4141 try deflate.store.compress(.raw, reader, writer);
4242 }
4343
lib/std/compress/flate/BitWriter.zig+3-3
......@@ -39,7 +39,7 @@ pub fn setWriter(self: *Self, new_writer: *std.io.BufferedWriter) void {
3939 self.inner_writer = new_writer;
4040}
4141
42pub fn flush(self: *Self) anyerror!void {
42pub fn flush(self: *Self) std.io.Writer.Error!void {
4343 var n = self.nbytes;
4444 while (self.nbits != 0) {
4545 self.bytes[n] = @as(u8, @truncate(self.bits));
......@@ -56,7 +56,7 @@ pub fn flush(self: *Self) anyerror!void {
5656 self.nbytes = 0;
5757}
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 {
6060 self.bits |= @as(u64, @intCast(b)) << @as(u6, @intCast(self.nbits));
6161 self.nbits += nb;
6262 if (self.nbits < 48)
......@@ -74,7 +74,7 @@ pub fn writeBits(self: *Self, b: u32, nb: u32) anyerror!void {
7474 self.nbits -= 48;
7575}
7676
77pub fn writeBytes(self: *Self, bytes: []const u8) anyerror!void {
77pub fn writeBytes(self: *Self, bytes: []const u8) std.io.Writer.Error!void {
7878 var n = self.nbytes;
7979 if (self.nbits & 7 != 0) {
8080 return error.UnfinishedBits;
lib/std/compress/flate/BlockWriter.zig+10-10
......@@ -42,7 +42,7 @@ pub fn init(writer: *std.io.BufferedWriter) Self {
4242/// That is after final block; when last byte could be incomplete or
4343/// after stored block; which is aligned to the byte boundary (it has x
4444/// padding bits after first 3 bits).
45pub fn flush(self: *Self) anyerror!void {
45pub fn flush(self: *Self) std.io.Writer.Error!void {
4646 try self.bit_writer.flush();
4747}
4848
......@@ -50,7 +50,7 @@ pub fn setWriter(self: *Self, new_writer: *std.io.BufferedWriter) void {
5050 self.bit_writer.setWriter(new_writer);
5151}
5252
53fn writeCode(self: *Self, c: hc.HuffCode) anyerror!void {
53fn writeCode(self: *Self, c: hc.HuffCode) std.io.Writer.Error!void {
5454 try self.bit_writer.writeBits(c.code, c.len);
5555}
5656
......@@ -232,7 +232,7 @@ fn dynamicHeader(
232232 num_distances: u32,
233233 num_codegens: u32,
234234 eof: bool,
235) anyerror!void {
235) std.io.Writer.Error!void {
236236 const first_bits: u32 = if (eof) 5 else 4;
237237 try self.bit_writer.writeBits(first_bits, 3);
238238 try self.bit_writer.writeBits(num_literals - 257, 5);
......@@ -272,7 +272,7 @@ fn dynamicHeader(
272272 }
273273}
274274
275fn storedHeader(self: *Self, length: usize, eof: bool) anyerror!void {
275fn storedHeader(self: *Self, length: usize, eof: bool) std.io.Writer.Error!void {
276276 assert(length <= 65535);
277277 const flag: u32 = if (eof) 1 else 0;
278278 try self.bit_writer.writeBits(flag, 3);
......@@ -282,7 +282,7 @@ fn storedHeader(self: *Self, length: usize, eof: bool) anyerror!void {
282282 try self.bit_writer.writeBits(~l, 16);
283283}
284284
285fn fixedHeader(self: *Self, eof: bool) anyerror!void {
285fn fixedHeader(self: *Self, eof: bool) std.io.Writer.Error!void {
286286 // Indicate that we are a fixed Huffman block
287287 var value: u32 = 2;
288288 if (eof) {
......@@ -296,7 +296,7 @@ fn fixedHeader(self: *Self, eof: bool) anyerror!void {
296296// is larger than the original bytes, the data will be written as a
297297// stored block.
298298// 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 {
300300 const lit_and_dist = self.indexTokens(tokens);
301301 const num_literals = lit_and_dist.num_literals;
302302 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)
374374 try self.writeTokens(tokens, &literal_encoding.codes, &distance_encoding.codes);
375375}
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 {
378378 try self.storedHeader(input.len, eof);
379379 try self.bit_writer.writeBytes(input);
380380}
......@@ -389,7 +389,7 @@ fn dynamicBlock(
389389 tokens: []const Token,
390390 eof: bool,
391391 input: ?[]const u8,
392) anyerror!void {
392) std.io.Writer.Error!void {
393393 const total_tokens = self.indexTokens(tokens);
394394 const num_literals = total_tokens.num_literals;
395395 const num_distances = total_tokens.num_distances;
......@@ -486,7 +486,7 @@ fn writeTokens(
486486 tokens: []const Token,
487487 le_codes: []hc.HuffCode,
488488 oe_codes: []hc.HuffCode,
489) anyerror!void {
489) std.io.Writer.Error!void {
490490 for (tokens) |t| {
491491 if (t.kind == Token.Kind.literal) {
492492 try self.writeCode(le_codes[t.literal()]);
......@@ -513,7 +513,7 @@ fn writeTokens(
513513
514514// Encodes a block of bytes as either Huffman encoded literals or uncompressed bytes
515515// 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 {
517517 // Add everything as literals
518518 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 {
6666 block_type: u2 = 0b11,
6767 state: ReadState = .protocol_header,
6868
69 read_err: Error!void = {},
70
6971 const ReadState = enum {
7072 protocol_header,
7173 block_header,
......@@ -76,19 +78,21 @@ pub fn Inflate(comptime container: Container, comptime Lookahead: type) type {
7678
7779 const Self = @This();
7880
79 pub const Error = anyerror || Container.Error || hfd.Error || error{
81 pub const Error = Container.Error || hfd.Error || error{
8082 InvalidCode,
8183 InvalidMatch,
8284 InvalidBlockType,
8385 WrongStoredBlockNlen,
8486 InvalidDynamicBlockHeader,
87 EndOfStream,
88 ReadFailed,
8589 };
8690
8791 pub fn init(bw: *std.io.BufferedReader) Self {
8892 return .{ .bits = LookaheadBitReader.init(bw) };
8993 }
9094
91 fn blockHeader(self: *Self) anyerror!void {
95 fn blockHeader(self: *Self) Error!void {
9296 self.bfinal = try self.bits.read(u1);
9397 self.block_type = try self.bits.read(u2);
9498 }
......@@ -326,7 +330,7 @@ pub fn Inflate(comptime container: Container, comptime Lookahead: type) type {
326330 /// returned bytes means end of stream reached. With limit=0 returns as
327331 /// much data it can. It newer will be more than 65536 bytes, which is
328332 /// size of internal buffer.
329 /// TODO merge this logic into reader_streamRead and reader_streamReadVec
333 /// TODO merge this logic into readerRead and readerReadVec
330334 pub fn get(self: *Self, limit: usize) Error![]const u8 {
331335 while (true) {
332336 const out = self.hist.readAtMost(limit);
......@@ -339,42 +343,63 @@ pub fn Inflate(comptime container: Container, comptime Lookahead: type) type {
339343 }
340344 }
341345
342 fn reader_streamRead(
343 ctx: ?*anyopaque,
346 fn readerRead(
347 context: ?*anyopaque,
344348 bw: *std.io.BufferedWriter,
345349 limit: std.io.Reader.Limit,
346 ) anyerror!std.io.Reader.Status {
347 const self: *Self = @alignCast(@ptrCast(ctx));
350 ) std.io.Reader.RwError!usize {
351 const self: *Self = @alignCast(@ptrCast(context));
348352 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;
350362 @memcpy(out[0..in.len], in);
351363 bw.advance(in.len);
352 return .{ .len = in.len, .end = in.len == 0 };
364 return in.len;
353365 }
354366
355 fn reader_streamReadVec(ctx: ?*anyopaque, data: []const []u8) anyerror!std.io.Reader.Status {
356 const self: *Self = @alignCast(@ptrCast(ctx));
367 fn readerReadVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
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 {
357386 for (data) |out| {
358387 if (out.len == 0) continue;
359388 const in = try self.get(out.len);
360389 @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;
362392 }
363 return .{};
364 }
365
366 pub fn streamReadVec(self: *Self, data: []const []u8) anyerror!std.io.Reader.Status {
367 return reader_streamReadVec(self, data);
393 return 0;
368394 }
369395
370396 pub fn reader(self: *Self) std.io.Reader {
371397 return .{
372398 .context = self,
373399 .vtable = &.{
374 .posRead = null,
375 .posReadVec = null,
376 .streamRead = reader_streamRead,
377 .streamReadVec = reader_streamReadVec,
400 .read = readerRead,
401 .readVec = readerReadVec,
402 .discard = readerDiscard,
378403 },
379404 };
380405 }
......@@ -656,7 +681,7 @@ pub fn BitReader(comptime T: type) type {
656681 (self.nbits >> 3); // 0 for 0-7, 1 for 8-16, ... same as / 8
657682
658683 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;
660685 if (bytes_read > 0) {
661686 const u: T = std.mem.readInt(T, buf[0..t_bytes], .little);
662687 self.bits |= u << @as(Tshift, @intCast(self.nbits));
......@@ -669,7 +694,7 @@ pub fn BitReader(comptime T: type) type {
669694 }
670695
671696 /// 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 {
673698 assert(self.alignBits() == 0); // internal bits must be at byte boundary
674699
675700 // First read from internal bits buffer.
lib/std/compress/lzma.zig+6-6
......@@ -11,7 +11,7 @@ pub const RangeDecoder = struct {
1111 range: u32,
1212 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 {
1515 const reserved = try br.takeByte();
1616 if (reserved != 0) return error.CorruptInput;
1717 rd.* = .{
......@@ -222,7 +222,7 @@ pub const Decode = struct {
222222 dict_size: u32,
223223 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 {
226226 var props = try br.readByte();
227227 if (props >= 225) {
228228 return error.CorruptInput;
......@@ -537,7 +537,7 @@ pub const Decode = struct {
537537
538538pub const Decompress = struct {
539539 pub const Error =
540 anyerror ||
540 std.io.Reader.Error ||
541541 Allocator.Error ||
542542 error{ CorruptInput, EndOfStream, Overflow };
543543
......@@ -668,7 +668,7 @@ const LzCircularBuffer = struct {
668668 allocator: Allocator,
669669 lit: u8,
670670 bw: *std.io.BufferedWriter,
671 ) anyerror!void {
671 ) std.io.Writer.Error!void {
672672 try self.set(allocator, self.cursor, lit);
673673 self.cursor += 1;
674674 self.len += 1;
......@@ -687,7 +687,7 @@ const LzCircularBuffer = struct {
687687 len: usize,
688688 dist: usize,
689689 bw: *std.io.BufferedWriter,
690 ) anyerror!void {
690 ) std.io.Writer.Error!void {
691691 if (dist > self.dict_size or dist > self.len) {
692692 return error.CorruptInput;
693693 }
......@@ -704,7 +704,7 @@ const LzCircularBuffer = struct {
704704 }
705705 }
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 {
708708 if (self.cursor > 0) {
709709 try bw.writeAll(self.buf.items[0..self.cursor]);
710710 self.cursor = 0;
lib/std/compress/lzma2.zig+1-1
......@@ -2,7 +2,7 @@ const std = @import("../std.zig");
22const Allocator = std.mem.Allocator;
33const 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 {
66 var decoder = try Decode.init(gpa);
77 defer decoder.deinit(gpa);
88 return decoder.decompress(gpa, reader, writer);
lib/std/compress/zstandard.zig+1-1
......@@ -39,7 +39,7 @@ pub const Decompressor = struct {
3939 write_index: usize = 0,
4040 };
4141
42 pub const Error = anyerror || error{
42 pub const Error = std.io.Reader.Error || error{
4343 ChecksumFailure,
4444 DictionaryIdFlagUnsupported,
4545 MalformedBlock,
lib/std/crypto/tls/Client.zig+26-32
......@@ -69,28 +69,15 @@ application_cipher: tls.ApplicationCipher,
6969/// this connection.
7070ssl_key_log: ?*SslKeyLog,
7171
72pub const Diagnostics = union {
73 /// Populated on `error.WriteFailure` and `error.ReadFailure`.
74 err: anyerror,
72pub const Diagnostics = union(enum) {
73 /// Any `ReadFailure` and `WriteFailure` was due to `input` or `output`
74 /// returning the error, respectively.
75 transitive,
7576 /// Populated on `error.TlsAlert`.
7677 ///
7778 /// If this isn't a error alert, then it's a closure alert, which makes
7879 /// no sense in a handshake.
7980 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 }
9481};
9582
9683pub const SslKeyLog = struct {
......@@ -205,7 +192,7 @@ pub fn init(
205192) InitError!void {
206193 assert(input.storage.buffer.len >= min_buffer_len);
207194 assert(output.buffer.len >= min_buffer_len);
208 const diags = &client.diagnostics;
195 client.diagnostics = .transient;
209196 const host = switch (options.host) {
210197 .no_verification => "",
211198 .explicit => |host| host,
......@@ -298,7 +285,7 @@ pub fn init(
298285
299286 {
300287 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]);
302289 }
303290
304291 var tls_version: tls.ProtocolVersion = undefined;
......@@ -350,12 +337,12 @@ pub fn init(
350337 var handshake_buffer: [tls.max_ciphertext_record_len]u8 = undefined;
351338 var d: tls.Decoder = .{ .buf = &handshake_buffer };
352339 fragment: while (true) {
353 try diags.wrapRead(d.readAtLeastOurAmt(input, tls.record_header_len));
340 try d.readAtLeastOurAmt(input, tls.record_header_len);
354341 const record_header = d.buf[d.idx..][0..tls.record_header_len];
355342 const record_ct = d.decode(tls.ContentType);
356343 d.skip(2); // legacy_version
357344 const record_len = d.decode(u16);
358 try diags.wrapRead(d.readAtLeast(input, record_len));
345 try d.readAtLeast(input, record_len);
359346 var record_decoder = try d.sub(record_len);
360347 var ctd, const ct = content: switch (cipher_state) {
361348 .cleartext => .{ record_decoder, record_ct },
......@@ -433,7 +420,7 @@ pub fn init(
433420 const level = ctd.decode(tls.AlertLevel);
434421 const desc = ctd.decode(tls.AlertDescription);
435422 _ = level;
436 diags.* = .{ .alert = desc };
423 client.diagnostics = .{ .alert = desc };
437424 return error.TlsAlert;
438425 },
439426 .change_cipher_spec => {
......@@ -775,7 +762,7 @@ pub fn init(
775762 &client_change_cipher_spec_msg,
776763 &client_verify_msg,
777764 };
778 try diags.wrapWrite(output.writevAll(&all_msgs_vec));
765 try output.writevAll(&all_msgs_vec);
779766 },
780767 }
781768 write_seq += 1;
......@@ -840,7 +827,7 @@ pub fn init(
840827 &client_change_cipher_spec_msg,
841828 &finished_msg,
842829 };
843 try diags.wrapWrite(output.writevAll(&all_msgs_vec));
830 try output.writevAll(&all_msgs_vec);
844831
845832 const client_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "c ap traffic", &handshake_hash, P.Hash.digest_length);
846833 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(
905892 client.reader.init(.{
906893 .context = client,
907894 .vtable = &.{
908 .read = reader_read,
909 .readv = reader_readv,
895 .read = read,
896 .readVec = readVec,
897 .discard = discard,
910898 },
911899 }, input.storage.buffer[0..0]);
912900 return;
......@@ -933,7 +921,7 @@ pub fn writer(c: *Client) std.io.Writer {
933921 };
934922}
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 {
937925 const c: *Client = @alignCast(@ptrCast(context));
938926 const sliced_data = if (splat == 0) data[0..data.len -| 1] else data;
939927 const output = &c.output;
......@@ -953,7 +941,7 @@ fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyer
953941/// Sends a `close_notify` alert, which is necessary for the server to
954942/// distinguish between a properly finished TLS session, or a truncation
955943/// attack.
956pub fn end(c: *Client) anyerror!void {
944pub fn end(c: *Client) std.io.Writer.Error!void {
957945 const output = &c.output;
958946 const ciphertext_buf = try output.writableSlice(min_buffer_len);
959947 const prepared = prepareCiphertextRecord(c, ciphertext_buf, &tls.close_notify_alert, .alert);
......@@ -1070,18 +1058,18 @@ pub fn eof(c: Client) bool {
10701058 c.partial_ciphertext_idx >= c.partial_ciphertext_end;
10711059}
10721060
1073fn reader_read(
1061fn read(
10741062 context: ?*anyopaque,
10751063 bw: *std.io.BufferedWriter,
10761064 limit: std.io.Reader.Limit,
1077) anyerror!std.io.Reader.Status {
1065) std.io.Reader.RwError!std.io.Reader.Status {
10781066 const buf = limit.slice(try bw.writableSlice(1));
1079 const status = try reader_readv(context, &.{buf});
1067 const status = try readVec(context, &.{buf});
10801068 bw.advance(status.len);
10811069 return status;
10821070}
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 {
10851073 const c: *Client = @ptrCast(@alignCast(context));
10861074 if (c.eof()) return .{ .end = true };
10871075
......@@ -1429,6 +1417,12 @@ fn reader_readv(context: ?*anyopaque, data: []const []u8) anyerror!std.io.Reader
14291417 }
14301418}
14311419
1420fn discard(context: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize {
1421 _ = context;
1422 _ = limit;
1423 @panic("TODO");
1424}
1425
14321426fn logSecrets(key_log_file: std.fs.File, context: anytype, secrets: anytype) void {
14331427 const locked = if (key_log_file.lock(.exclusive)) |_| true else |_| false;
14341428 defer if (locked) key_log_file.unlock();
lib/std/debug.zig+32-29
......@@ -210,16 +210,19 @@ pub fn unlockStdErr() void {
210210///
211211/// Returns a `std.io.BufferedWriter` with empty buffer, meaning that it is
212212/// in fact unbuffered and does not need to be flushed.
213pub fn lockStdErr2(buffer: []u8) std.io.BufferedWriter {
214 std.Progress.lockStdErr();
215 return std.fs.File.stderr().writer().buffered(buffer);
213pub fn lockStderrWriter(buffer: []u8) *std.io.BufferedWriter {
214 return std.Progress.lockStderrWriter(buffer);
215}
216
217pub fn unlockStderrWriter() void {
218 std.Progress.unlockStderrWriter();
216219}
217220
218221/// 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.
220223pub fn print(comptime fmt: []const u8, args: anytype) void {
221 var bw = lockStdErr2(&.{});
222 defer unlockStdErr();
224 const bw = lockStderrWriter(&.{});
225 defer unlockStderrWriter();
223226 nosuspend bw.print(fmt, args) catch return;
224227}
225228
......@@ -242,10 +245,10 @@ pub fn getSelfDebugInfo() !*SelfInfo {
242245/// Tries to print a hexadecimal view of the bytes, unbuffered, and ignores any error returned.
243246/// Obtains the stderr mutex while dumping.
244247pub fn dumpHex(bytes: []const u8) void {
245 var bw = lockStdErr2(&.{});
246 defer unlockStdErr();
248 const bw = lockStderrWriter(&.{});
249 defer unlockStderrWriter();
247250 const ttyconf = std.io.tty.detectConfig(.stderr());
248 dumpHexFallible(&bw, ttyconf, bytes) catch {};
251 dumpHexFallible(bw, ttyconf, bytes) catch {};
249252}
250253
251254/// Prints a hexadecimal view of the bytes, returning any error that occurs.
......@@ -320,9 +323,9 @@ test dumpHexFallible {
320323
321324/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
322325pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
323 var stderr = lockStdErr2(&.{});
324 defer unlockStdErr();
325 nosuspend dumpCurrentStackTraceToWriter(start_addr, &stderr) catch return;
326 const stderr = lockStderrWriter(&.{});
327 defer unlockStderrWriter();
328 nosuspend dumpCurrentStackTraceToWriter(start_addr, stderr) catch return;
326329}
327330
328331/// Prints the current stack trace to the provided writer.
......@@ -516,14 +519,14 @@ pub fn dumpStackTrace(stack_trace: std.builtin.StackTrace) void {
516519 nosuspend {
517520 if (builtin.target.cpu.arch.isWasm()) {
518521 if (native_os == .wasi) {
519 var stderr = lockStdErr2(&.{});
520 defer unlockStdErr();
522 const stderr = lockStderrWriter(&.{});
523 defer unlockStderrWriter();
521524 stderr.writeAll("Unable to dump stack trace: not implemented for Wasm\n") catch return;
522525 }
523526 return;
524527 }
525 var stderr = lockStdErr2(&.{});
526 defer unlockStdErr();
528 const stderr = lockStderrWriter(&.{});
529 defer unlockStderrWriter();
527530 if (builtin.strip_debug_info) {
528531 stderr.writeAll("Unable to dump stack trace: debug info stripped\n") catch return;
529532 return;
......@@ -532,7 +535,7 @@ pub fn dumpStackTrace(stack_trace: std.builtin.StackTrace) void {
532535 stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
533536 return;
534537 };
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| {
536539 stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
537540 return;
538541 };
......@@ -683,8 +686,8 @@ pub fn defaultPanic(
683686 _ = panicking.fetchAdd(1, .seq_cst);
684687
685688 {
686 var stderr = lockStdErr2(&.{});
687 defer unlockStdErr();
689 const stderr = lockStderrWriter(&.{});
690 defer unlockStderrWriter();
688691
689692 if (builtin.single_threaded) {
690693 stderr.print("panic: ", .{}) catch posix.abort();
......@@ -695,7 +698,7 @@ pub fn defaultPanic(
695698 stderr.print("{s}\n", .{msg}) catch posix.abort();
696699
697700 if (@errorReturnTrace()) |t| dumpStackTrace(t.*);
698 dumpCurrentStackTraceToWriter(first_trace_addr orelse @returnAddress(), &stderr) catch {};
701 dumpCurrentStackTraceToWriter(first_trace_addr orelse @returnAddress(), stderr) catch {};
699702 }
700703
701704 waitForOtherThreadToFinishPanicking();
......@@ -1468,8 +1471,8 @@ fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopa
14681471}
14691472
14701473fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*anyopaque) void {
1471 var stderr = lockStdErr2(&.{});
1472 defer unlockStdErr();
1474 const stderr = lockStderrWriter(&.{});
1475 defer unlockStderrWriter();
14731476 _ = switch (sig) {
14741477 posix.SIG.SEGV => if (native_arch == .x86_64 and native_os == .linux and code == 128) // SI_KERNEL
14751478 // 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)
15171520 }, @ptrCast(ctx)).__mcontext_data;
15181521 }
15191522 relocateContext(&new_ctx);
1520 dumpStackTraceFromBase(&new_ctx, &stderr);
1523 dumpStackTraceFromBase(&new_ctx, stderr);
15211524 },
15221525 else => {},
15231526 }
......@@ -1547,10 +1550,10 @@ fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, msg: u8, label:
15471550 _ = panicking.fetchAdd(1, .seq_cst);
15481551
15491552 {
1550 var stderr = lockStdErr2(&.{});
1551 defer unlockStdErr();
1553 const stderr = lockStderrWriter(&.{});
1554 defer unlockStderrWriter();
15521555
1553 dumpSegfaultInfoWindows(info, msg, label, &stderr);
1556 dumpSegfaultInfoWindows(info, msg, label, stderr);
15541557 }
15551558
15561559 waitForOtherThreadToFinishPanicking();
......@@ -1665,8 +1668,8 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize
16651668 if (!enabled) return;
16661669
16671670 const tty_config = io.tty.detectConfig(.stderr());
1668 var stderr = lockStdErr2(&.{});
1669 defer unlockStdErr();
1671 const stderr = lockStderrWriter(&.{});
1672 defer unlockStderrWriter();
16701673 const end = @min(t.index, size);
16711674 const debug_info = getSelfDebugInfo() catch |err| {
16721675 stderr.print(
......@@ -1683,7 +1686,7 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize
16831686 .index = frames.len,
16841687 .instruction_addresses = frames,
16851688 };
1686 writeStackTrace(stack_trace, &stderr, debug_info, tty_config) catch continue;
1689 writeStackTrace(stack_trace, stderr, debug_info, tty_config) catch continue;
16871690 }
16881691 if (t.index > end) {
16891692 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 {
22122212 var separate_debug_filename: ?[]const u8 = null;
22132213 var separate_debug_crc: ?u32 = null;
22142214
2215 shdrs: for (shdrs) |*shdr| {
2215 for (shdrs) |*shdr| {
22162216 if (shdr.sh_type == elf.SHT_NULL or shdr.sh_type == elf.SHT_NOBITS) continue;
22172217 const name = mem.sliceTo(header_strings[shdr.sh_name..], 0);
22182218
......@@ -2243,24 +2243,12 @@ pub const ElfModule = struct {
22432243
22442244 var zlib_stream: std.compress.zlib.Decompressor = .init(&section_reader);
22452245
2246 const decompressed_section = try gpa.alloc(u8, ch_size);
2247 errdefer gpa.free(decompressed_section);
2248
2249 {
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 }
2246 const decompressed_section = zlib_stream.reader().readAlloc(gpa, ch_size) catch continue;
2247 if (decompressed_section.len != ch_size) {
2248 gpa.free(decompressed_section);
2249 continue;
22632250 }
2251 errdefer gpa.free(decompressed_section);
22642252
22652253 break :blk .{
22662254 .data = decompressed_section,
lib/std/debug/Dwarf/expression.zig+4-4
......@@ -62,7 +62,7 @@ pub const Error = error{
6262 InvalidTypeLength,
6363
6464 TruncatedIntegralType,
65} || abi.RegBytesError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero };
65} || abi.RegBytesError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero, ReadFailed };
6666
6767/// A stack machine that can decode and run DWARF expressions.
6868/// Expressions can be decoded for non-native address size and endianness,
......@@ -259,7 +259,7 @@ pub fn StackMachine(comptime options: Options) type {
259259 allocator: std.mem.Allocator,
260260 context: Context,
261261 initial_value: ?usize,
262 ) anyerror!?Value {
262 ) Error!?Value {
263263 if (initial_value) |i| try self.stack.append(allocator, .{ .generic = i });
264264 var reader: std.io.BufferedReader = undefined;
265265 reader.initFixed(expression);
......@@ -274,13 +274,13 @@ pub fn StackMachine(comptime options: Options) type {
274274 reader: *std.io.BufferedReader,
275275 allocator: std.mem.Allocator,
276276 context: Context,
277 ) anyerror!bool {
277 ) Error!bool {
278278 if (@sizeOf(usize) != @sizeOf(Address) or options.endian != native_endian)
279279 @compileError("Execution of non-native address sizes / endianness is not supported");
280280
281281 const opcode = reader.takeByte() catch |err| switch (err) {
282282 error.EndOfStream => return false,
283 else => |e| return @errorCast(e),
283 error.ReadFailed => return error.ReadFailed,
284284 };
285285 if (options.call_frame_context and !isOpcodeValidInCFA(opcode)) return error.InvalidCFAOpcode;
286286 const operand = try readOperand(reader, opcode, context);
lib/std/fifo.zig+17-10
......@@ -238,23 +238,30 @@ pub fn LinearFifo(
238238 return .{
239239 .context = self,
240240 .vtable = &.{
241 .read = &reader_read,
242 .readv = &reader_readv,
241 .read = &readerRead,
242 .readVec = &readerReadVec,
243 .discard = &readerDiscard,
243244 },
244245 };
245246 }
246 fn reader_read(
247 fn readerRead(
247248 ctx: ?*anyopaque,
248249 bw: *std.io.BufferedWriter,
249250 limit: std.io.Reader.Limit,
250 ) anyerror!std.io.Reader.Status {
251 ) std.io.Reader.RwError!usize {
251252 const fifo: *Self = @alignCast(@ptrCast(ctx));
252253 _ = fifo;
253254 _ = bw;
254255 _ = limit;
255256 @panic("TODO");
256257 }
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 {
258265 const fifo: *Self = @alignCast(@ptrCast(ctx));
259266 _ = fifo;
260267 _ = data;
......@@ -351,26 +358,26 @@ pub fn LinearFifo(
351358 return .{
352359 .context = fifo,
353360 .vtable = &.{
354 .writeSplat = writer_writeSplat,
355 .writeFile = writer_writeFile,
361 .writeSplat = writerWriteSplat,
362 .writeFile = writerWriteFile,
356363 },
357364 };
358365 }
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 {
360367 const fifo: *Self = @alignCast(@ptrCast(ctx));
361368 _ = fifo;
362369 _ = data;
363370 _ = splat;
364371 @panic("TODO");
365372 }
366 fn writer_writeFile(
373 fn writerWriteFile(
367374 ctx: ?*anyopaque,
368375 file: std.fs.File,
369376 offset: std.io.Writer.Offset,
370377 limit: std.io.Writer.Limit,
371378 headers_and_trailers: []const []const u8,
372379 headers_len: usize,
373 ) anyerror!usize {
380 ) std.io.Writer.Error!usize {
374381 const fifo: *Self = @alignCast(@ptrCast(ctx));
375382 _ = fifo;
376383 _ = file;
lib/std/fmt.zig+28-19
......@@ -1,8 +1,8 @@
11//! String formatting and parsing.
22
3const std = @import("std.zig");
43const builtin = @import("builtin");
54
5const std = @import("std.zig");
66const io = std.io;
77const math = std.math;
88const assert = std.debug.assert;
......@@ -12,6 +12,7 @@ const meta = std.meta;
1212const lossyCast = math.lossyCast;
1313const expectFmt = std.testing.expectFmt;
1414const testing = std.testing;
15const Allocator = std.mem.Allocator;
1516
1617pub const float = @import("fmt/float.zig");
1718
......@@ -91,7 +92,7 @@ pub const Options = struct {
9192/// A user type may be a `struct`, `vector`, `union` or `enum` type.
9293///
9394/// 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 {
9596 const ArgsType = @TypeOf(args);
9697 const args_type_info = @typeInfo(ArgsType);
9798 if (args_type_info != .@"struct") {
......@@ -531,7 +532,7 @@ pub fn Formatter(comptime formatFn: anytype) type {
531532 const Data = @typeInfo(@TypeOf(formatFn)).@"fn".params[0].type.?;
532533 return struct {
533534 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 {
535536 try formatFn(self.data, writer, fmt);
536537 }
537538 };
......@@ -833,8 +834,7 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintErro
833834 var bw: std.io.BufferedWriter = undefined;
834835 bw.initFixed(buf);
835836 bw.print(fmt, args) catch |err| switch (err) {
836 error.NoSpaceLeft => return error.NoSpaceLeft,
837 else => unreachable,
837 error.WriteFailed => return error.NoSpaceLeft,
838838 };
839839 return bw.getWritten();
840840}
......@@ -846,25 +846,34 @@ pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintErr
846846
847847/// Count the characters needed for format.
848848pub fn count(comptime fmt: []const u8, args: anytype) usize {
849 var buffer: [std.atomic.cache_line]u8 = undefined;
850 var bw = std.io.Writer.null.buffered(&buffer);
849 var trash_buffer: [std.atomic.cache_line]u8 = undefined;
850 var null_writer: std.io.Writer.Null = undefined;
851 var bw = null_writer.writer().buffered(&trash_buffer);
851852 bw.print(fmt, args) catch unreachable;
852853 return bw.count;
853854}
854855
855pub const AllocPrintError = error{OutOfMemory};
856
857pub fn allocPrint(allocator: mem.Allocator, comptime fmt: []const u8, args: anytype) AllocPrintError![]u8 {
858 const size = math.cast(usize, count(fmt, args)) orelse return error.OutOfMemory;
859 const buf = try allocator.alloc(u8, size);
860 return bufPrint(buf, fmt, args) catch |err| switch (err) {
861 error.NoSpaceLeft => unreachable, // we just counted the size above
856pub fn allocPrint(gpa: Allocator, comptime fmt: []const u8, args: anytype) Allocator.Error![]u8 {
857 var aw: std.io.AllocatingWriter = undefined;
858 try aw.initCapacity(gpa, fmt.len);
859 aw.buffered_writer.print(fmt, args) catch |err| switch (err) {
860 error.WriteFailed => return error.OutOfMemory,
862861 };
863}
864
865pub fn allocPrintZ(allocator: mem.Allocator, comptime fmt: []const u8, args: anytype) AllocPrintError![:0]u8 {
866 const result = try allocPrint(allocator, fmt ++ "\x00", args);
867 return result[0 .. result.len - 1 :0];
862 return aw.toOwnedSlice();
863}
864
865pub fn allocPrintSentinel(
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);
868877}
869878
870879pub 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(
26192619 var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = actual_mode });
26202620 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 });
26232626 try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime);
26242627 try atomic_file.finish();
2625 return PrevStatus.stale;
2628 return .stale;
26262629}
26272630
26282631pub const CopyFileError = File.OpenError || File.StatError ||
......@@ -2833,15 +2836,6 @@ pub fn setPermissions(self: Dir, permissions: Permissions) SetPermissionsError!v
28332836 try file.setPermissions(permissions);
28342837}
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
28452839const Dir = @This();
28462840const builtin = @import("builtin");
28472841const std = @import("../std.zig");
lib/std/fs/File.zig+398-877
......@@ -363,8 +363,10 @@ pub fn getPos(self: File) GetSeekPosError!u64 {
363363 return posix.lseek_CUR_get(self.handle);
364364}
365365
366pub const GetEndPosError = std.os.windows.GetFileSizeError || StatError;
367
366368/// TODO: integrate with async I/O
367pub fn getEndPos(self: File) GetSeekPosError!u64 {
369pub fn getEndPos(self: File) GetEndPosError!u64 {
368370 if (builtin.os.tag == .windows) {
369371 return windows.GetFileSizeEx(self.handle);
370372 }
......@@ -489,7 +491,6 @@ pub const Stat = struct {
489491pub const StatError = posix.FStatError;
490492
491493/// Returns `Stat` containing basic information about the `File`.
492/// Use `metadata` to retrieve more detailed information (e.g. creation time, permissions).
493494/// TODO: integrate with async I/O
494495pub fn stat(self: File) StatError!Stat {
495496 if (builtin.os.tag == .windows) {
......@@ -755,361 +756,6 @@ pub fn setPermissions(self: File, permissions: Permissions) SetPermissionsError!
755756 }
756757}
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
1113759pub const UpdateTimesError = posix.FutimensError || windows.SetFileTimeError;
1114760
1115761/// The underlying file system may have a different granularity than nanoseconds,
......@@ -1193,18 +839,6 @@ pub fn read(self: File, buffer: []u8) ReadError!usize {
1193839 return posix.read(self.handle, buffer);
1194840}
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
1208842/// On Windows, this function currently does alter the file pointer.
1209843/// https://github.com/ziglang/zig/issues/12783
1210844pub 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 {
1215849 return posix.pread(self.handle, buffer, offset);
1216850}
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
1232852/// See https://github.com/ziglang/zig/issues/7699
1233853pub fn readv(self: File, iovecs: []const posix.iovec) ReadError!usize {
1234854 if (is_windows) {
1235 // TODO improve this to use ReadFileScatter
1236 if (iovecs.len == 0) return @as(usize, 0);
855 if (iovecs.len == 0) return 0;
1237856 const first = iovecs[0];
1238857 return windows.ReadFile(self.handle, first.base[0..first.len], null);
1239858 }
......@@ -1241,55 +860,12 @@ pub fn readv(self: File, iovecs: []const posix.iovec) ReadError!usize {
1241860 return posix.readv(self.handle, iovecs);
1242861}
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
1286863/// See https://github.com/ziglang/zig/issues/7699
1287864/// On Windows, this function currently does alter the file pointer.
1288865/// https://github.com/ziglang/zig/issues/12783
1289866pub fn preadv(self: File, iovecs: []const posix.iovec, offset: u64) PReadError!usize {
1290867 if (is_windows) {
1291 // TODO improve this to use ReadFileScatter
1292 if (iovecs.len == 0) return @as(usize, 0);
868 if (iovecs.len == 0) return 0;
1293869 const first = iovecs[0];
1294870 return windows.ReadFile(self.handle, first.base[0..first.len], offset);
1295871 }
......@@ -1297,35 +873,6 @@ pub fn preadv(self: File, iovecs: []const posix.iovec, offset: u64) PReadError!u
1297873 return posix.preadv(self.handle, iovecs, offset);
1298874}
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
1329876pub const WriteError = posix.WriteError;
1330877pub const PWriteError = posix.PWriteError;
1331878
......@@ -1337,6 +884,7 @@ pub fn write(self: File, bytes: []const u8) WriteError!usize {
1337884 return posix.write(self.handle, bytes);
1338885}
1339886
887/// One-shot alternative to `writer`.
1340888pub fn writeAll(self: File, bytes: []const u8) WriteError!void {
1341889 var index: usize = 0;
1342890 while (index < bytes.len) {
......@@ -1354,21 +902,11 @@ pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize {
1354902 return posix.pwrite(self.handle, bytes, offset);
1355903}
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
1366905/// See https://github.com/ziglang/zig/issues/7699
1367/// See equivalent function: `std.net.Stream.writev`.
1368906pub fn writev(self: File, iovecs: []const posix.iovec_const) WriteError!usize {
1369907 if (is_windows) {
1370908 // TODO improve this to use WriteFileScatter
1371 if (iovecs.len == 0) return @as(usize, 0);
909 if (iovecs.len == 0) return 0;
1372910 const first = iovecs[0];
1373911 return windows.WriteFile(self.handle, first.base[0..first.len], null);
1374912 }
......@@ -1376,46 +914,12 @@ pub fn writev(self: File, iovecs: []const posix.iovec_const) WriteError!usize {
1376914 return posix.writev(self.handle, iovecs);
1377915}
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
1412917/// See https://github.com/ziglang/zig/issues/7699
1413918/// On Windows, this function currently does alter the file pointer.
1414919/// https://github.com/ziglang/zig/issues/12783
1415920pub fn pwritev(self: File, iovecs: []posix.iovec_const, offset: u64) PWriteError!usize {
1416921 if (is_windows) {
1417 // TODO improve this to use WriteFileScatter
1418 if (iovecs.len == 0) return @as(usize, 0);
922 if (iovecs.len == 0) return 0;
1419923 const first = iovecs[0];
1420924 return windows.WriteFile(self.handle, first.base[0..first.len], offset);
1421925 }
......@@ -1423,410 +927,426 @@ pub fn pwritev(self: File, iovecs: []posix.iovec_const, offset: u64) PWriteError
1423927 return posix.pwritev(self.handle, iovecs, offset);
1424928}
1425929
1426/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
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;
930pub const WriteFileError = PReadError || WriteError;
1433931
1434 var i: usize = 0;
1435 var off: u64 = 0;
1436 while (true) {
1437 var amt = try self.pwritev(iovecs[i..], offset + off);
1438 off += amt;
1439 while (amt >= iovecs[i].len) {
1440 amt -= iovecs[i].len;
1441 i += 1;
1442 if (i >= iovecs.len) return;
1443 }
1444 iovecs[i].base += amt;
1445 iovecs[i].len -= amt;
1446 }
932pub fn writeFileAll(self: File, in_file: File, options: BufferedWriter.WriteFileOptions) WriteFileError!void {
933 var file_writer = self.writer();
934 var bw = file_writer.interface().buffered(&.{});
935 bw.writeFileAll(in_file, options) catch |err| switch (err) {
936 error.WriteFailed => if (file_writer.err) |_| unreachable else |e| return e,
937 else => |e| return e,
938 };
1447939}
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 {
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}
950 pub const Mode = enum { streaming, positional };
1456951
1457/// Returns the number of bytes copied. If the number read is smaller than `buffer.len`, it
1458/// means the in file reached the end. Reaching the end of a file is not an error condition.
1459pub fn copyRangeAll(in: File, in_offset: u64, out: File, out_offset: u64, len: u64) CopyRangeError!u64 {
1460 var total_bytes_copied: u64 = 0;
1461 var in_off = in_offset;
1462 var out_off = out_offset;
1463 while (total_bytes_copied < len) {
1464 const amt_copied = try copyRange(in, in_off, out, out_off, len - total_bytes_copied);
1465 if (amt_copied == 0) return total_bytes_copied;
1466 total_bytes_copied += amt_copied;
1467 in_off += amt_copied;
1468 out_off += amt_copied;
952 pub fn interface(r: *Reader) std.io.Reader {
953 return .{
954 .context = r,
955 .vtable = &.{
956 .read = Reader.read,
957 .readVec = Reader.readVec,
958 .discard = Reader.discard,
959 },
960 };
1469961 }
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{},
1484
1485 /// The trailer count is inferred from `headers_and_trailers.len - header_count`
1486 header_count: usize = 0,
1487};
1488
1489pub const WriteFileError = ReadError || error{EndOfStream} || WriteError;
1490
1491pub fn writeFileAll(self: File, in_file: File, args: WriteFileOptions) WriteFileError!void {
1492 return self.writeFileAllSendfile(in_file, args) catch |err| switch (err) {
1493 error.Unseekable,
1494 error.FastOpenAlreadyInProgress,
1495 error.MessageTooBig,
1496 error.FileDescriptorNotASocket,
1497 error.NetworkUnreachable,
1498 error.NetworkSubsystemFailed,
1499 => return self.writeFileUnseekableAll(in_file, args),
963 /// Number of slices to store on the stack, when trying to send as many byte
964 /// vectors through the underlying read calls as possible.
965 const max_buffers_len = 16;
966
967 fn read(
968 context: ?*anyopaque,
969 bw: *BufferedWriter,
970 limit: std.io.Reader.Limit,
971 ) std.io.Reader.RwError!usize {
972 const r: *Reader = @ptrCast(@alignCast(context));
973 const file = r.file;
974 const pos = r.pos;
975 switch (r.mode) {
976 .positional => {
977 const size = r.size orelse {
978 if (r.file.getEndPos()) |size| {
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,
1502 };
1503}
1017 fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
1018 const r: *Reader = @ptrCast(@alignCast(context));
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.
1506/// See `writeFileAll` as an alternative to calling this.
1507pub fn writeFileUnseekableAll(out_file: File, in_file: File, args: WriteFileOptions) WriteFileError!void {
1508 _ = out_file;
1509 _ = in_file;
1510 _ = args;
1511 @panic("TODO call writeFileUnseekable multiple times");
1512}
1042 var iovecs: [max_buffers_len]std.posix.iovec = undefined;
1043 var iovecs_i: usize = 0;
1044 for (data) |d| {
1045 // Since the OS checks pointer address before length, we must omit
1046 // length-zero vectors.
1047 if (d.len == 0) continue;
1048 iovecs[iovecs_i] = .{ .base = d.ptr, .len = d.len };
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.
1515/// See `writeFileAll` as an alternative to calling this.
1516/// TODO integrate with async I/O
1517fn writeFileAllSendfile(self: File, in_file: File, args: WriteFileOptions) posix.SendFileError!void {
1518 const count = blk: {
1519 if (args.in_len) |l| {
1520 if (l == 0) {
1521 return self.writevAll(args.headers_and_trailers);
1522 } else {
1523 break :blk l;
1524 }
1525 } else {
1526 break :blk 0;
1527 }
1528 };
1529 const headers = args.headers_and_trailers[0..args.header_count];
1530 const trailers = args.headers_and_trailers[args.header_count..];
1531 const zero_iovec = &[0]posix.iovec_const{};
1532 // When reading the whole file, we cannot put the trailers in the sendfile() syscall,
1533 // because we have no way to determine whether a partial write is past the end of the file or not.
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;
1088 var iovecs: [max_buffers_len]std.posix.iovec = undefined;
1089 var iovecs_i: usize = 0;
1090 for (data) |d| {
1091 // Since the OS checks pointer address before length, we must omit
1092 // length-zero vectors.
1093 if (d.len == 0) continue;
1094 iovecs[iovecs_i] = .{ .base = d.ptr, .len = d.len };
1095 iovecs_i += 1;
1096 if (iovecs_i >= iovecs.len) break;
1097 }
1098 const send_vecs = iovecs[0..iovecs_i];
1099 if (send_vecs.len == 0) return 0; // Prevent false positive end detection on empty `data`.
1100 const n = posix.readv(handle, send_vecs) catch |err| {
1101 r.err = err;
1102 return error.ReadFailed;
1103 };
1104 if (n == 0) return error.EndOfStream;
1105 r.pos = pos + n;
1106 return n;
1107 },
15651108 }
1566 amt = @as(usize, @intCast(off - count));
15671109 }
1568 var i: usize = 0;
1569 while (i < trailers.len) {
1570 while (amt >= trailers[i].len) {
1571 amt -= trailers[i].len;
1572 i += 1;
1573 if (i >= trailers.len) return;
1110
1111 fn discard(context: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize {
1112 const r: *Reader = @ptrCast(@alignCast(context));
1113 const file = r.file;
1114 const pos = r.pos;
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 },
15741182 }
1575 trailers[i].base += amt;
1576 trailers[i].len -= amt;
1577 amt = try posix.writev(self.handle, trailers[i..]);
15781183 }
1579}
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}
1184};
16331185
1634pub fn streamRead(
1635 context: ?*anyopaque,
1636 bw: *std.io.BufferedWriter,
1637 limit: std.io.Reader.Limit,
1638) anyerror!std.io.Reader.Status {
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}
1186pub const Writer = struct {
1187 file: File,
1188 err: WriteError!void = {},
1189 mode: Writer.Mode = .positional,
1190 pos: u64 = 0,
16461191
1647pub fn streamReadVec(context: ?*anyopaque, data: []const []u8) anyerror!std.io.Reader.Status {
1648 const handle = opaqueToHandle(context);
1192 pub const Mode = Reader.Mode;
16491193
1650 if (is_windows) {
1651 // Unfortunately, `ReadFileScatter` cannot be used since it requires
1652 // page alignment, so we are stuck using only the first slice.
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 }
1194 /// Number of slices to store on the stack, when trying to send as many byte
1195 /// vectors through the underlying write calls as possible.
1196 const max_buffers_len = 16;
16621197
1663 var iovecs: [max_buffers_len]std.posix.iovec = undefined;
1664 var iovecs_i: usize = 0;
1665 for (data) |d| {
1666 // Since the OS checks pointer address before length, we must omit
1667 // length-zero vectors.
1668 if (d.len == 0) continue;
1669 iovecs[iovecs_i] = .{ .base = d.ptr, .len = d.len };
1670 iovecs_i += 1;
1671 if (iovecs_i >= iovecs.len) break;
1198 pub fn interface(w: *Writer) std.io.Writer {
1199 return .{
1200 .context = w,
1201 .vtable = &.{
1202 .writeSplat = writeSplat,
1203 .writeFile = writeFile,
1204 },
1205 };
16721206 }
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 {
1680 const handle = opaqueToHandle(context);
1681 var splat_buffer: [256]u8 = undefined;
1682 if (is_windows) {
1683 if (data.len == 1 and splat == 0) return 0;
1684 return windows.WriteFile(handle, data[0], null);
1685 }
1686 var iovecs: [max_buffers_len]std.posix.iovec_const = undefined;
1687 var len: usize = @min(iovecs.len, data.len);
1688 for (iovecs[0..len], data[0..len]) |*v, d| v.* = .{
1689 .base = if (d.len == 0) "" else d.ptr, // OS sadly checks ptr addr before length.
1690 .len = d.len,
1691 };
1692 switch (splat) {
1693 0 => return std.posix.writev(handle, iovecs[0 .. len - 1]),
1694 1 => return std.posix.writev(handle, iovecs[0..len]),
1695 else => {
1696 const pattern = data[data.len - 1];
1697 if (pattern.len == 1) {
1698 const memset_len = @min(splat_buffer.len, splat);
1699 const buf = splat_buffer[0..memset_len];
1700 @memset(buf, pattern[0]);
1701 iovecs[len - 1] = .{ .base = buf.ptr, .len = buf.len };
1702 var remaining_splat = splat - buf.len;
1703 while (remaining_splat > splat_buffer.len and len < iovecs.len) {
1704 iovecs[len] = .{ .base = &splat_buffer, .len = splat_buffer.len };
1705 remaining_splat -= splat_buffer.len;
1706 len += 1;
1707 }
1708 if (remaining_splat > 0 and len < iovecs.len) {
1709 iovecs[len] = .{ .base = &splat_buffer, .len = remaining_splat };
1710 len += 1;
1208 pub fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
1209 const w: *Writer = @ptrCast(@alignCast(context));
1210 const handle = w.file.handle;
1211 var splat_buffer: [256]u8 = undefined;
1212 if (is_windows) {
1213 if (data.len == 1 and splat == 0) return 0;
1214 return windows.WriteFile(handle, data[0], null);
1215 }
1216 var iovecs: [max_buffers_len]std.posix.iovec_const = undefined;
1217 var len: usize = @min(iovecs.len, data.len);
1218 for (iovecs[0..len], data[0..len]) |*v, d| v.* = .{
1219 .base = if (d.len == 0) "" else d.ptr, // OS sadly checks ptr addr before length.
1220 .len = d.len,
1221 };
1222 switch (splat) {
1223 0 => return std.posix.writev(handle, iovecs[0 .. len - 1]) catch |err| {
1224 w.err = err;
1225 return error.WriteFailed;
1226 },
1227 1 => return std.posix.writev(handle, iovecs[0..len]) catch |err| {
1228 w.err = err;
1229 return error.WriteFailed;
1230 },
1231 else => {
1232 const pattern = data[data.len - 1];
1233 if (pattern.len == 1) {
1234 const memset_len = @min(splat_buffer.len, splat);
1235 const buf = splat_buffer[0..memset_len];
1236 @memset(buf, pattern[0]);
1237 iovecs[len - 1] = .{ .base = buf.ptr, .len = buf.len };
1238 var remaining_splat = splat - buf.len;
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 };
17111252 }
1712 return std.posix.writev(handle, iovecs[0..len]);
1713 }
1714 },
1253 },
1254 }
1255 return std.posix.writev(handle, iovecs[0..len]) catch |err| {
1256 w.err = err;
1257 return error.WriteFailed;
1258 };
17151259 }
1716 return std.posix.writev(handle, iovecs[0..len]);
1717}
17181260
1719pub fn writeFile(
1720 context: ?*anyopaque,
1721 in_file: std.fs.File,
1722 in_offset: std.io.Writer.Offset,
1723 in_limit: std.io.Writer.Limit,
1724 headers_and_trailers: []const []const u8,
1725 headers_len: usize,
1726) anyerror!usize {
1727 const out_fd = opaqueToHandle(context);
1728 const in_fd = in_file.handle;
1729 const len_int = switch (in_limit) {
1730 .nothing => return writeSplat(context, headers_and_trailers, 1),
1731 .unlimited => 0,
1732 else => in_limit.toInt().?,
1733 };
1734 if (native_os == .linux) sf: {
1735 // Linux sendfile does not support headers or trailers but it does
1736 // support a streaming read from in_file.
1737 if (headers_len > 0) return writeSplat(context, headers_and_trailers[0..headers_len], 1);
1738 const max_count = 0x7ffff000; // Avoid EINVAL.
1739 const smaller_len = if (len_int == 0) max_count else @min(len_int, max_count);
1740 var off: std.os.linux.off_t = undefined;
1741 const off_ptr: ?*std.os.linux.off_t = if (in_offset.toInt()) |offset| b: {
1742 off = std.math.cast(std.os.linux.off_t, offset) orelse
1261 pub fn writeFile(
1262 context: ?*anyopaque,
1263 in_file: std.fs.File,
1264 in_offset: std.io.Writer.Offset,
1265 in_limit: std.io.Writer.Limit,
1266 headers_and_trailers: []const []const u8,
1267 headers_len: usize,
1268 ) std.io.Writer.FileError!usize {
1269 const w: *Writer = @ptrCast(@alignCast(context));
1270 const out_fd = w.file.handle;
1271 const in_fd = in_file.handle;
1272 const len_int = switch (in_limit) {
1273 .nothing => return writeSplat(context, headers_and_trailers, 1),
1274 .unlimited => 0,
1275 else => in_limit.toInt().?,
1276 };
1277 // TODO try using copy_file_range on linux
1278 // TODO try using copy_file_range on freebsd
1279 if (native_os == .linux) sf: {
1280 // Linux sendfile does not support headers or trailers but it does
1281 // support a streaming read from in_file.
1282 if (headers_len > 0) return writeSplat(context, headers_and_trailers[0..headers_len], 1);
1283 const max_count = 0x7ffff000; // Avoid EINVAL.
1284 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.
17431303 return writeSplat(context, headers_and_trailers, 1);
1744 break :b &off;
1745 } else null;
1746 if (true) @panic("TODO");
1747 const n = std.os.linux.wrapped.sendfile(out_fd, in_fd, off_ptr, smaller_len) catch |err| switch (err) {
1748 error.UnsupportedOperation => break :sf,
1749 error.Unseekable => break :sf,
1750 error.Unexpected => break :sf,
1304 }
1305 return n;
1306 }
1307 var iovecs_buffer: [max_buffers_len]std.posix.iovec_const = undefined;
1308 const iovecs = iovecs_buffer[0..@min(iovecs_buffer.len, headers_and_trailers.len)];
1309 for (iovecs, headers_and_trailers[0..iovecs.len]) |*v, d| v.* = .{ .base = d.ptr, .len = d.len };
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
17511322 else => |e| return e,
17521323 };
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;
17611324 }
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(
1782 out_fd: Handle,
1783 in_fd: Handle,
1784 in_offset: u64,
1785 in_limit: std.io.Writer.Limit,
1786 headers_and_trailers: []const []const u8,
1787 headers_len: usize,
1788) anyerror!usize {
1789 _ = out_fd;
1790 _ = in_fd;
1791 _ = in_offset;
1792 _ = in_limit;
1793 _ = headers_and_trailers;
1794 _ = headers_len;
1795 @panic("TODO writeFileUnseekable");
1796}
1797
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}
1326 fn writeFileUnseekable(
1327 out_fd: Handle,
1328 in_fd: Handle,
1329 in_offset: u64,
1330 in_limit: std.io.Writer.Limit,
1331 headers_and_trailers: []const []const u8,
1332 headers_len: usize,
1333 ) std.io.Writer.FileError!usize {
1334 _ = out_fd;
1335 _ = in_fd;
1336 _ = in_offset;
1337 _ = in_limit;
1338 _ = headers_and_trailers;
1339 _ = headers_len;
1340 @panic("TODO writeFileUnseekable");
1341 }
1342};
18131343
1814fn opaqueToFile(userdata: ?*anyopaque) File {
1815 return .{ .handle = opaqueToHandle(userdata) };
1344pub fn reader(file: File) Reader {
1345 return .{ .file = file };
18161346}
18171347
1818pub const SeekableStream = io.SeekableStream(
1819 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 };
1348pub fn writer(file: File) Writer {
1349 return .{ .file = file };
18301350}
18311351
18321352const range_off: windows.LARGE_INTEGER = 0;
......@@ -2008,3 +1528,4 @@ const linux = std.os.linux;
20081528const windows = std.os.windows;
20091529const maxInt = std.math.maxInt;
20101530const Alignment = std.mem.Alignment;
1531const BufferedWriter = std.io.BufferedWriter;
lib/std/fs/test.zig-107
......@@ -1953,113 +1953,6 @@ test "chown" {
19531953 try dir.chown(null, null);
19541954}
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
20631956test "delete a setAsCwd directory on Windows" {
20641957 if (native_os != .windows) return error.SkipZigTest;
20651958
lib/std/http/Client.zig+11-11
......@@ -386,7 +386,7 @@ pub const Connection = struct {
386386 }
387387 }
388388
389 pub fn flush(c: *Connection) anyerror!void {
389 pub fn flush(c: *Connection) std.io.Writer.Error!void {
390390 try c.writer.flush();
391391 if (c.protocol == .tls) {
392392 if (disable_tls) unreachable;
......@@ -398,7 +398,7 @@ pub const Connection = struct {
398398 /// If the connection is a TLS connection, sends the close_notify alert.
399399 ///
400400 /// Flushes all buffers.
401 pub fn end(c: *Connection) anyerror!void {
401 pub fn end(c: *Connection) std.io.Writer.Error!void {
402402 try c.writer.flush();
403403 if (c.protocol == .tls) {
404404 if (disable_tls) unreachable;
......@@ -826,7 +826,7 @@ pub const Request = struct {
826826 }
827827
828828 /// 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 {
830830 assert(req.transfer_encoding == .none or req.method.requestHasBody());
831831
832832 const connection = req.connection.?;
......@@ -959,7 +959,7 @@ pub const Request = struct {
959959
960960 /// TODO collapse each error set into its own meta error code, and store
961961 /// 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 ||
963963 proto.HeadersParser.CheckCompleteHeadError || Response.ParseError ||
964964 error{
965965 TooManyHttpRedirects,
......@@ -1156,7 +1156,7 @@ pub const Request = struct {
11561156 };
11571157 }
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 {
11601160 const req: *Request = @ptrCast(@alignCast(context));
11611161 var total: usize = 0;
11621162 for (data) |bytes| total += bytes.len;
......@@ -1187,7 +1187,7 @@ pub const Request = struct {
11871187 len: std.io.Writer.FileLen,
11881188 headers_and_trailers: []const []const u8,
11891189 headers_len: usize,
1190 ) anyerror!usize {
1190 ) std.io.Writer.Error!usize {
11911191 if (len == .entire_file) return error.Unimplemented;
11921192 const req: *Request = @ptrCast(@alignCast(context));
11931193 var total: usize = len.int();
......@@ -1213,7 +1213,7 @@ pub const Request = struct {
12131213 return total;
12141214 }
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 {
12171217 const req: *Request = @ptrCast(@alignCast(context));
12181218 const n = try req.connection.?.writer.writeSplat(data, splat);
12191219 req.transfer_encoding.content_length -= n;
......@@ -1227,7 +1227,7 @@ pub const Request = struct {
12271227 len: std.io.Writer.FileLen,
12281228 headers_and_trailers: []const []const u8,
12291229 headers_len: usize,
1230 ) anyerror!usize {
1230 ) std.io.Writer.Error!usize {
12311231 const req: *Request = @ptrCast(@alignCast(context));
12321232 const n = try req.connection.?.writer.writeFile(file, offset, len, headers_and_trailers, headers_len);
12331233 req.transfer_encoding.content_length -= n;
......@@ -1236,7 +1236,7 @@ pub const Request = struct {
12361236
12371237 /// Finish the body of a request. This notifies the server that you have no more data to send.
12381238 /// Must be called after `send`.
1239 pub fn finish(req: *Request) anyerror!void {
1239 pub fn finish(req: *Request) std.io.Writer.Error!void {
12401240 switch (req.transfer_encoding) {
12411241 .chunked => try req.connection.?.writer.writeAll("0\r\n\r\n"),
12421242 .content_length => |len| assert(len == 0),
......@@ -1353,7 +1353,7 @@ pub const basic_authorization = struct {
13531353 return bw.getWritten();
13541354 }
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 {
13571357 var buf: [max_user_len + ":".len + max_password_len]u8 = undefined;
13581358 var bw: std.io.BufferedWriter = undefined;
13591359 bw.initFixed(&buf);
......@@ -1574,7 +1574,7 @@ pub fn connect(
15741574
15751575/// TODO collapse each error set into its own meta error code, and store
15761576/// the underlying error code as a field on Request
1577pub const RequestError = ConnectTcpError || ConnectErrorPartial || anyerror ||
1577pub const RequestError = ConnectTcpError || ConnectErrorPartial || std.io.Writer.Error ||
15781578 std.fmt.ParseIntError || Connection.WriteError ||
15791579 error{
15801580 UnsupportedUriScheme,
lib/std/http/Server.zig+34-40
......@@ -19,7 +19,7 @@ out: *std.io.BufferedWriter,
1919/// same connection, and makes invalid API usage cause assertion failures
2020/// rather than HTTP protocol violations.
2121state: State,
22in_err: anyerror,
22head_parse_err: Request.Head.ParseError,
2323
2424pub const State = enum {
2525 /// The connection is available to be used for the first time, or reused.
......@@ -53,8 +53,8 @@ pub const ReceiveHeadError = error{
5353 /// The HTTP specification suggests to respond with a 431 status code
5454 /// before closing the connection.
5555 HttpHeadersOversize,
56 /// Client sent headers that did not conform to the HTTP protocol.
57 /// `in_err` is populated with a `Request.Head.ParseError`.
56 /// Client sent headers that did not conform to the HTTP protocol;
57 /// `head_parse_err` is populated.
5858 HttpHeadersInvalid,
5959 /// Partial HTTP request was received but the connection was closed before
6060 /// fully receiving the headers.
......@@ -62,7 +62,7 @@ pub const ReceiveHeadError = error{
6262 /// The client sent 0 bytes of headers before closing the stream.
6363 /// In other words, a keep-alive connection was finally closed.
6464 HttpConnectionClosing,
65 /// Error occurred reading from `in`; `in_err` is populated.
65 /// Transitive error occurred reading from `in`.
6666 ReadFailure,
6767};
6868
......@@ -79,23 +79,22 @@ pub fn receiveHead(s: *Server) ReceiveHeadError!Request {
7979
8080 while (true) {
8181 if (head_end >= in.bufferContents().len) return error.HttpHeadersOversize;
82 const buf = (in.peekGreedy(head_end + 1) catch |err| {
83 s.in_err = err;
84 return error.ReadFailure;
85 }) orelse switch (head_end) {
86 0 => return error.HttpConnectionClosing,
87 else => return error.HttpRequestTruncated,
82 const buf = in.peekGreedy(head_end + 1) catch |err| switch (err) {
83 error.EndOfStream => switch (head_end) {
84 0 => return error.HttpConnectionClosing,
85 else => return error.HttpRequestTruncated,
86 },
87 error.ReadFailure => return error.ReadFailure,
8888 };
8989 head_end += hp.feed(buf[head_end..]);
9090 if (hp.state == .finished) return .{
9191 .server = s,
9292 .head_end = head_end,
9393 .head = Request.Head.parse(buf[0..head_end]) catch |err| {
94 s.in_err = err;
94 s.head_parse_err = err;
9595 return error.HttpHeadersInvalid;
9696 },
9797 .reader_state = undefined,
98 .write_error = undefined,
9998 };
10099 }
101100}
......@@ -109,8 +108,6 @@ pub const Request = struct {
109108 remaining_content_length: u64,
110109 chunk_parser: http.ChunkParser,
111110 },
112 /// Populated when `error.HttpContinueWriteFailed` is received.
113 write_error: anyerror,
114111
115112 pub const Compression = union(enum) {
116113 deflate: std.compress.zlib.Decompressor,
......@@ -310,7 +307,6 @@ pub const Request = struct {
310307 .head_end = request_bytes.len,
311308 .head = undefined,
312309 .reader_state = undefined,
313 .write_error = undefined,
314310 };
315311
316312 var it = request.iterateHeaders();
......@@ -375,7 +371,7 @@ pub const Request = struct {
375371 request: *Request,
376372 content: []const u8,
377373 options: RespondOptions,
378 ) anyerror!void {
374 ) std.io.Writer.Error!void {
379375 const max_extra_headers = 25;
380376 assert(options.status != .@"continue");
381377 assert(options.extra_headers.len <= max_extra_headers);
......@@ -581,7 +577,7 @@ pub const Request = struct {
581577 ctx: ?*anyopaque,
582578 bw: *std.io.BufferedWriter,
583579 limit: std.io.Reader.Limit,
584 ) anyerror!std.io.Reader.Status {
580 ) std.io.Reader.Error!std.io.Reader.Status {
585581 const request: *Request = @alignCast(@ptrCast(ctx));
586582 _ = request;
587583 _ = bw;
......@@ -589,7 +585,7 @@ pub const Request = struct {
589585 @panic("TODO");
590586 }
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 {
593589 const request: *Request = @alignCast(@ptrCast(ctx));
594590 _ = request;
595591 _ = data;
......@@ -600,7 +596,7 @@ pub const Request = struct {
600596 ctx: ?*anyopaque,
601597 bw: *std.io.BufferedWriter,
602598 limit: std.io.Reader.Limit,
603 ) anyerror!std.io.Reader.Status {
599 ) std.io.Reader.Error!usize {
604600 const request: *Request = @alignCast(@ptrCast(ctx));
605601 _ = request;
606602 _ = bw;
......@@ -608,7 +604,7 @@ pub const Request = struct {
608604 @panic("TODO");
609605 }
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 {
612608 const request: *Request = @alignCast(@ptrCast(ctx));
613609 _ = request;
614610 _ = data;
......@@ -732,9 +728,10 @@ pub const Request = struct {
732728 }
733729
734730 pub const ReaderError = error{
735 /// Failed to write "100-continue" to the stream. Error value is
736 /// stored in `Request.write_error`.
737 HttpContinueWriteFailed,
731 /// Failed to write "100-continue" to the stream.
732 WriteFailed,
733 /// Failed to write "100-continue" to the stream because it ended.
734 EndOfStream,
738735 /// The client sent an expect HTTP header value other than
739736 /// "100-continue".
740737 HttpExpectationFailed,
......@@ -755,10 +752,7 @@ pub const Request = struct {
755752 if (request.head.expect) |expect| {
756753 if (mem.eql(u8, expect, "100-continue")) {
757754 var w = request.server.connection.stream.writer().unbuffered();
758 w.writeAll("HTTP/1.1 100 Continue\r\n\r\n") catch |err| {
759 request.write_error = err;
760 return error.HttpContinueWriteFailed;
761 };
755 try w.writeAll("HTTP/1.1 100 Continue\r\n\r\n");
762756 request.head.expect = null;
763757 } else {
764758 return error.HttpExpectationFailed;
......@@ -854,7 +848,7 @@ pub const Response = struct {
854848 /// Otherwise, transfer-encoding: chunked is being used, and it writes the
855849 /// end-of-stream message, then flushes the stream to the system.
856850 /// 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 {
858852 switch (r.transfer_encoding) {
859853 .content_length => |len| {
860854 assert(len == 0); // Trips when end() called before all bytes written.
......@@ -879,7 +873,7 @@ pub const Response = struct {
879873 /// flushes the stream to the system.
880874 /// Respects the value of `elide_body` to omit all data after the headers.
881875 /// 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 {
883877 assert(r.transfer_encoding == .chunked);
884878 try flush_chunked(r, options.trailers);
885879 r.* = undefined;
......@@ -889,14 +883,14 @@ pub const Response = struct {
889883 /// would not exceed the content-length value sent in the HTTP header.
890884 /// May return 0, which does not indicate end of stream. The caller decides
891885 /// 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 {
893887 switch (r.transfer_encoding) {
894888 .content_length, .none => return @errorCast(cl_writeSplat(r, &.{bytes}, 1)),
895889 .chunked => return @errorCast(chunked_writeSplat(r, &.{bytes}, 1)),
896890 }
897891 }
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 {
900894 _ = splat;
901895 return cl_write(context, data[0]); // TODO: try to send all the data
902896 }
......@@ -908,7 +902,7 @@ pub const Response = struct {
908902 limit: std.io.Writer.Limit,
909903 headers_and_trailers: []const []const u8,
910904 headers_len: usize,
911 ) anyerror!usize {
905 ) std.io.Writer.Error!usize {
912906 _ = context;
913907 _ = file;
914908 _ = offset;
......@@ -918,7 +912,7 @@ pub const Response = struct {
918912 return error.Unimplemented;
919913 }
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 {
922916 const r: *Response = @alignCast(@ptrCast(context));
923917
924918 var trash: u64 = std.math.maxInt(u64);
......@@ -963,7 +957,7 @@ pub const Response = struct {
963957 return bytes.len;
964958 }
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 {
967961 _ = splat;
968962 return chunked_write(context, data[0]); // TODO: try to send all the data
969963 }
......@@ -975,7 +969,7 @@ pub const Response = struct {
975969 limit: std.io.Writer.Limit,
976970 headers_and_trailers: []const []const u8,
977971 headers_len: usize,
978 ) anyerror!usize {
972 ) std.io.Writer.Error!usize {
979973 _ = context;
980974 _ = file;
981975 _ = offset;
......@@ -985,7 +979,7 @@ pub const Response = struct {
985979 return error.Unimplemented; // TODO lower to a call to writeFile on the output
986980 }
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 {
989983 const r: *Response = @alignCast(@ptrCast(context));
990984 assert(r.transfer_encoding == .chunked);
991985
......@@ -1024,7 +1018,7 @@ pub const Response = struct {
10241018
10251019 /// If using content-length, asserts that writing these bytes to the client
10261020 /// 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 {
10281022 var index: usize = 0;
10291023 while (index < bytes.len) {
10301024 index += try write(r, bytes[index..]);
......@@ -1034,21 +1028,21 @@ pub const Response = struct {
10341028 /// Sends all buffered data to the client.
10351029 /// This is redundant after calling `end`.
10361030 /// 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 {
10381032 switch (r.transfer_encoding) {
10391033 .none, .content_length => return flush_cl(r),
10401034 .chunked => return flush_chunked(r, null),
10411035 }
10421036 }
10431037
1044 fn flush_cl(r: *Response) anyerror!void {
1038 fn flush_cl(r: *Response) std.io.Writer.Error!void {
10451039 var w = r.stream.writer().unbuffered();
10461040 try w.writeAll(r.send_buffer[r.send_buffer_start..r.send_buffer_end]);
10471041 r.send_buffer_start = 0;
10481042 r.send_buffer_end = 0;
10491043 }
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 {
10521046 const max_trailers = 25;
10531047 if (end_trailers) |trailers| assert(trailers.len <= max_trailers);
10541048 assert(r.transfer_encoding == .chunked);
lib/std/http/WebSocket.zig+4-2
......@@ -194,14 +194,16 @@ fn recvReadInt(ws: *WebSocket, comptime I: type) !I {
194194 };
195195}
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 {
198200 const iovecs: [1]std.posix.iovec_const = .{
199201 .{ .base = message.ptr, .len = message.len },
200202 };
201203 return writeMessagev(ws, &iovecs, opcode);
202204}
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 {
205207 const total_len = l: {
206208 var total_len: u64 = 0;
207209 for (message) |iovec| total_len += iovec.len;
lib/std/io.zig-3
......@@ -17,8 +17,6 @@ const Alignment = std.mem.Alignment;
1717pub const Reader = @import("io/Reader.zig");
1818pub const Writer = @import("io/Writer.zig");
1919
20pub const PositionalReader = @import("io/PositionalReader.zig");
21
2220pub const BufferedReader = @import("io/BufferedReader.zig");
2321pub const BufferedWriter = @import("io/BufferedWriter.zig");
2422pub const AllocatingWriter = @import("io/AllocatingWriter.zig");
......@@ -453,7 +451,6 @@ test {
453451 _ = BufferedReader;
454452 _ = Reader;
455453 _ = Writer;
456 _ = PositionalReader;
457454 _ = AllocatingWriter;
458455 _ = @import("io/bit_reader.zig");
459456 _ = @import("io/bit_writer.zig");
lib/std/io/AllocatingWriter.zig+6-6
......@@ -130,7 +130,7 @@ pub fn clearRetainingCapacity(aw: *AllocatingWriter) void {
130130 aw.shrinkRetainingCapacity(0);
131131}
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 {
134134 const aw: *AllocatingWriter = @alignCast(@ptrCast(context));
135135 const start_len = aw.written.len;
136136 const bw = &aw.buffered_writer;
......@@ -145,7 +145,7 @@ fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anye
145145 const pattern = data[data.len - 1];
146146 var new_capacity: usize = list.capacity + pattern.len * splat;
147147 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;
149149 for (rest) |bytes| list.appendSliceAssumeCapacity(bytes);
150150 appendPatternAssumeCapacity(&list, pattern, splat);
151151 aw.written = list.items;
......@@ -168,7 +168,7 @@ fn writeFile(
168168 limit: std.io.Writer.Limit,
169169 headers_and_trailers_full: []const []const u8,
170170 headers_len_full: usize,
171) anyerror!usize {
171) std.io.Writer.FileError!usize {
172172 const aw: *AllocatingWriter = @alignCast(@ptrCast(context));
173173 const gpa = aw.allocator;
174174 var list = aw.toArrayList();
......@@ -184,14 +184,14 @@ fn writeFile(
184184 const limit_int = limit.toInt() orelse {
185185 var new_capacity: usize = list.capacity + std.atomic.cache_line;
186186 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;
188188 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);
189189 const dest = list.items.ptr[list.items.len..list.capacity];
190190 const n = try file.pread(dest, pos);
191191 if (n == 0) {
192192 new_capacity = list.capacity;
193193 for (trailers) |bytes| new_capacity += bytes.len;
194 try list.ensureTotalCapacity(gpa, new_capacity);
194 list.ensureTotalCapacity(gpa, new_capacity) catch return error.WriteFailed;
195195 for (trailers) |bytes| list.appendSliceAssumeCapacity(bytes);
196196 return list.items.len - start_len;
197197 }
......@@ -200,7 +200,7 @@ fn writeFile(
200200 };
201201 var new_capacity: usize = list.capacity + limit_int;
202202 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;
204204 for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes);
205205 const dest = list.items.ptr[list.items.len..][0..limit_int];
206206 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 {
2323 br.storage.initFixed(buffer);
2424}
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
7226/// 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
7328pub fn initFixed(br: *BufferedReader, buffer: []const u8) void {
7429 br.* = .{
7530 .seek = 0,
7631 .storage = .{
7732 .buffer = @constCast(buffer),
78 .unbuffered_writer = .{
79 .context = undefined,
80 .vtable = &eof_writer,
81 },
82 },
83 .unbuffered_reader = .{
84 .context = undefined,
85 .vtable = &eof_reader,
33 .unbuffered_writer = .failing,
8634 },
35 .unbuffered_reader = .ending,
8736 };
8837}
8938
9039pub fn storageBuffer(br: *BufferedReader) []u8 {
9140 const storage = &br.storage;
92 assert(storage.unbuffered_writer.vtable == &eof_writer);
93 assert(br.unbuffered_reader.vtable == &eof_reader);
41 assert(storage.unbuffered_writer.vtable == std.io.Writer.failing.vtable);
42 assert(br.unbuffered_reader.vtable == Reader.ending.vtable);
9443 return storage.buffer;
9544}
9645
......@@ -106,47 +55,43 @@ pub fn reader(br: *BufferedReader) Reader {
10655 return .{
10756 .context = br,
10857 .vtable = &.{
109 .read = passthru_read,
110 .readv = passthru_readv,
58 .read = passthruRead,
59 .readVec = passthruReadVec,
11160 },
11261 };
11362}
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 {
11665 const br: *BufferedReader = @alignCast(@ptrCast(ctx));
11766 const storage = &br.storage;
11867 const buffer = storage.buffer[0..storage.end];
11968 const buffered = buffer[br.seek..];
12069 const limited = buffered[0..limit.min(buffered.len)];
12170 if (limited.len > 0) {
122 const result = bw.writeSplat(limited, 1);
123 br.seek += result.len;
124 return .{
125 .len = result.len,
126 .write_err = result.err,
127 .write_end = result.end,
128 };
71 const n = try bw.writeSplat(limited, 1);
72 br.seek += n;
73 return n;
12974 }
13075 return br.unbuffered_reader.read(bw, limit);
13176}
13277
133fn passthru_readv(ctx: ?*anyopaque, data: []const []u8) anyerror!Reader.Status {
78fn passthruReadVec(ctx: ?*anyopaque, data: []const []u8) Reader.Error!usize {
13479 const br: *BufferedReader = @alignCast(@ptrCast(ctx));
13580 _ = br;
13681 _ = data;
13782 @panic("TODO");
13883}
13984
140pub fn seekBy(br: *BufferedReader, seek_by: i64) anyerror!void {
85pub fn seekBy(br: *BufferedReader, seek_by: i64) !void {
14186 if (seek_by < 0) try br.seekBackwardBy(@abs(seek_by)) else try br.seekForwardBy(@abs(seek_by));
14287}
14388
144pub fn seekBackwardBy(br: *BufferedReader, seek_by: u64) anyerror!void {
89pub fn seekBackwardBy(br: *BufferedReader, seek_by: u64) !void {
14590 if (seek_by > br.storage.end - br.seek) return error.Unseekable; // TODO
14691 br.seek += @abs(seek_by);
14792}
14893
149pub fn seekForwardBy(br: *BufferedReader, seek_by: u64) anyerror!void {
94pub fn seekForwardBy(br: *BufferedReader, seek_by: u64) !void {
15095 const seek, const need_unbuffered_seek = @subWithOverflow(br.seek, @abs(seek_by));
15196 if (need_unbuffered_seek > 0) return error.Unseekable; // TODO
15297 br.seek = seek;
......@@ -166,27 +111,11 @@ pub fn seekForwardBy(br: *BufferedReader, seek_by: u64) anyerror!void {
166111/// See also:
167112/// * `peekGreedy`
168113/// * `toss`
169pub fn peek(br: *BufferedReader, n: usize) anyerror![]u8 {
170 return (try br.peekGreedy(n))[0..n];
171}
172
173/// Returns the next `n` bytes from `unbuffered_reader`, filling the buffer as
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;
114pub fn peek(br: *BufferedReader, n: usize) Reader.Error![]u8 {
115 const storage = &br.storage;
116 assert(n <= storage.buffer.len);
117 try br.fill(n);
118 return storage.buffer[br.seek..][0..n];
190119}
191120
192121/// Returns all the next buffered bytes from `unbuffered_reader`, after filling
......@@ -203,30 +132,11 @@ pub fn peek2(br: *BufferedReader, n: usize) anyerror!?[]u8 {
203132/// See also:
204133/// * `peek`
205134/// * `toss`
206pub fn peekGreedy(br: *BufferedReader, n: usize) anyerror![]u8 {
207 assert(n <= br.storage.buffer.len);
208 if (try br.fill(n)) return br.bufferContents();
209 return error.EndOfStream;
210}
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;
135pub fn peekGreedy(br: *BufferedReader, n: usize) Reader.Error![]u8 {
136 const storage = &br.storage;
137 assert(n <= storage.buffer.len);
138 try br.fill(n);
139 return storage.buffer[br.seek..storage.end];
230140}
231141
232142/// 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 {
242152 assert(br.seek <= br.storage.end);
243153}
244154
245/// Equivalent to `peek` + `toss`.
246pub fn take(br: *BufferedReader, n: usize) anyerror![]u8 {
155/// Equivalent to `peek` followed by `toss`.
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 {
247160 const result = try br.peek(n);
248161 br.toss(n);
249162 return result;
......@@ -260,7 +173,7 @@ pub fn take(br: *BufferedReader, n: usize) anyerror![]u8 {
260173///
261174/// See also:
262175/// * `take`
263pub fn takeArray(br: *BufferedReader, comptime n: usize) anyerror!*[n]u8 {
176pub fn takeArray(br: *BufferedReader, comptime n: usize) Reader.Error!*[n]u8 {
264177 return (try br.take(n))[0..n];
265178}
266179
......@@ -272,10 +185,10 @@ pub fn takeArray(br: *BufferedReader, comptime n: usize) anyerror!*[n]u8 {
272185///
273186/// See also:
274187/// * `toss`
275/// * `discardUntilEnd`
276/// * `discardUpTo`
277pub fn discard(br: *BufferedReader, n: usize) anyerror!void {
278 if ((try br.discardUpTo(n)) != n) return error.EndOfStream;
188/// * `discardRemaining`
189/// * `discardShort`
190pub fn discard(br: *BufferedReader, n: usize) Reader.Error!void {
191 if ((try br.discardShort(n)) != n) return error.EndOfStream;
279192}
280193
281194/// 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 {
288201/// See also:
289202/// * `discard`
290203/// * `toss`
291/// * `discardUntilEnd`
292pub fn discardUpTo(br: *BufferedReader, n: usize) anyerror!usize {
204/// * `discardRemaining`
205pub fn discardShort(br: *BufferedReader, n: usize) Reader.ShortError!usize {
293206 const storage = &br.storage;
294 var remaining = n;
295 while (remaining > 0) {
296 const proposed_seek = br.seek + remaining;
297 if (proposed_seek <= storage.end) {
298 br.seek = proposed_seek;
299 return n;
300 }
301 remaining -= (storage.end - br.seek);
302 storage.end = 0;
303 br.seek = 0;
304 const result = try br.unbuffered_reader.read(storage, .unlimited);
305 assert(result.len == storage.end);
306 if (remaining <= storage.end) continue;
307 if (result.end) return n - remaining;
207 const proposed_seek = br.seek + n;
208 if (proposed_seek <= storage.end) {
209 @branchHint(.likely);
210 br.seek = proposed_seek;
211 return n;
212 }
213 var remaining = n - (storage.end - br.seek);
214 storage.end = 0;
215 br.seek = 0;
216 while (true) {
217 const discard_len = br.unbuffered_reader.discard(remaining, .unlimited) catch |err| switch (err) {
218 error.EndOfStream => return n - remaining,
219 error.ReadFailed => return error.ReadFailed,
220 };
221 remaining -= discard_len;
222 if (remaining == 0) return n;
308223 }
309 return n;
310224}
311225
312226/// Reads the stream until the end, ignoring all the data.
313227/// Returns the number of bytes discarded.
314pub fn discardUntilEnd(br: *BufferedReader) anyerror!usize {
228pub fn discardRemaining(br: *BufferedReader) Reader.ShortError!usize {
315229 const storage = &br.storage;
316 var total: usize = storage.end;
230 const buffered_len = storage.end;
317231 storage.end = 0;
318 total += try br.unbuffered_reader.discardUntilEnd();
319 return total;
232 return buffered_len + try br.unbuffered_reader.discardRemaining();
320233}
321234
322235/// Fill `buffer` with the next `buffer.len` bytes from the stream, advancing
......@@ -329,7 +242,7 @@ pub fn discardUntilEnd(br: *BufferedReader) anyerror!usize {
329242///
330243/// See also:
331244/// * `peek`
332pub fn read(br: *BufferedReader, buffer: []u8) anyerror!void {
245pub fn read(br: *BufferedReader, buffer: []u8) Reader.Error!void {
333246 const storage = &br.storage;
334247 const in_buffer = storage.buffer[0..storage.end];
335248 const seek = br.seek;
......@@ -344,7 +257,12 @@ pub fn read(br: *BufferedReader, buffer: []u8) anyerror!void {
344257 br.seek = 0;
345258 var i: usize = in_buffer.len;
346259 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);
348266 const next_i = i + storage.end;
349267 if (next_i >= buffer.len) {
350268 const remaining = buffer[i..];
......@@ -352,46 +270,48 @@ pub fn read(br: *BufferedReader, buffer: []u8) anyerror!void {
352270 br.seek = remaining.len;
353271 return;
354272 }
355 if (status.end) return error.EndOfStream;
356273 @memcpy(buffer[i..next_i], storage.buffer[0..storage.end]);
357274 storage.end = 0;
358275 i = next_i;
359276 }
360277}
361278
362/// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
363/// means the stream reached the end. Reaching the end of a stream is not an error
364/// condition.
365pub fn partialRead(br: *BufferedReader, buffer: []u8) anyerror!usize {
279/// Returns the number of bytes read, which is less than `buffer.len` if and
280/// only if the stream reached the end.
281pub fn readShort(br: *BufferedReader, buffer: []u8) Reader.ShortError!usize {
366282 _ = br;
367283 _ = buffer;
368284 @panic("TODO");
369285}
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
371297/// Returns a slice of the next bytes of buffered data from the stream until
372298/// `sentinel` is found, advancing the seek position.
373299///
374300/// Returned slice has a sentinel.
375301///
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///
382302/// Invalidates previously returned values from `peek`.
383303///
384304/// See also:
385305/// * `peekSentinel`
386306/// * `takeDelimiterExclusive`
387307/// * `takeDelimiterInclusive`
388pub fn takeSentinel(br: *BufferedReader, comptime sentinel: u8) anyerror![:sentinel]u8 {
308pub fn takeSentinel(br: *BufferedReader, comptime sentinel: u8) DelimiterInclusiveError![:sentinel]u8 {
389309 const result = try br.peekSentinel(sentinel);
390310 br.toss(result.len + 1);
391311 return result;
392312}
393313
394pub fn peekSentinel(br: *BufferedReader, comptime sentinel: u8) anyerror![:sentinel]u8 {
314pub fn peekSentinel(br: *BufferedReader, comptime sentinel: u8) DelimiterInclusiveError![:sentinel]u8 {
395315 const result = try br.takeDelimiterInclusive(sentinel);
396316 return result[0 .. result.len - 1 :sentinel];
397317}
......@@ -401,28 +321,30 @@ pub fn peekSentinel(br: *BufferedReader, comptime sentinel: u8) anyerror![:senti
401321///
402322/// Returned slice includes the delimiter as the last byte.
403323///
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///
410324/// Invalidates previously returned values from `peek`.
411325///
412326/// See also:
413327/// * `takeSentinel`
414328/// * `takeDelimiterExclusive`
415329/// * `peekDelimiterInclusive`
416pub fn takeDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 {
330pub fn takeDelimiterInclusive(br: *BufferedReader, delimiter: u8) DelimiterInclusiveError![]u8 {
417331 const result = try br.peekDelimiterInclusive(delimiter);
418332 br.toss(result.len);
419333 return result;
420334}
421335
422pub fn peekDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 {
336pub fn peekDelimiterInclusive(br: *BufferedReader, delimiter: u8) DelimiterInclusiveError![]u8 {
423337 return (try br.peekDelimiterInclusiveUnlessEnd(delimiter)) orelse error.EndOfStream;
424338}
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
426348/// Returns a slice of the next bytes of buffered data from the stream until
427349/// `delimiter` is found, advancing the seek position.
428350///
......@@ -430,32 +352,33 @@ pub fn peekDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8
430352///
431353/// End-of-stream is treated equivalent to a delimiter.
432354///
433/// If the delimiter is not found within a number of bytes matching the
434/// capacity of the `BufferedReader`, `error.StreamTooLong` is returned.
435///
436355/// Invalidates previously returned values from `peek`.
437356///
438357/// See also:
439358/// * `takeSentinel`
440359/// * `takeDelimiterInclusive`
441360/// * `peekDelimiterExclusive`
442pub fn takeDelimiterExclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 {
443 const result_unless_end = try br.peekDelimiterInclusiveUnlessEnd(delimiter);
444 const result = result_unless_end orelse {
445 br.toss(br.storage.end);
446 return br.storage.buffer[0..br.storage.end];
361pub fn takeDelimiterExclusive(br: *BufferedReader, delimiter: u8) DelimiterExclusiveError![]u8 {
362 const result = br.peekDelimiterInclusiveUnlessEnd(delimiter) catch |err| switch (err) {
363 error.EndOfStream => {
364 br.toss(br.storage.end);
365 return br.storage.buffer[0..br.storage.end];
366 },
367 else => |e| return e,
447368 };
448369 br.toss(result.len);
449370 return result[0 .. result.len - 1];
450371}
451372
452pub fn peekDelimiterExclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 {
453 const result_unless_end = try br.peekDelimiterInclusiveUnlessEnd(delimiter);
454 const result = result_unless_end orelse return br.storage.buffer[0..br.storage.end];
373pub fn peekDelimiterExclusive(br: *BufferedReader, delimiter: u8) DelimiterExclusiveError![]u8 {
374 const result = br.peekDelimiterInclusiveUnlessEnd(delimiter) catch |err| switch (err) {
375 error.EndOfStream => return br.storage.buffer[0..br.storage.end],
376 else => |e| return e,
377 };
455378 return result[0 .. result.len - 1];
456379}
457380
458fn peekDelimiterInclusiveUnlessEnd(br: *BufferedReader, delimiter: u8) anyerror!?[]u8 {
381fn peekDelimiterInclusiveUnlessEnd(br: *BufferedReader, delimiter: u8) DelimiterInclusiveError!?[]u8 {
459382 const storage = &br.storage;
460383 const buffer = storage.buffer[0..storage.end];
461384 const seek = br.seek;
......@@ -469,21 +392,29 @@ fn peekDelimiterInclusiveUnlessEnd(br: *BufferedReader, delimiter: u8) anyerror!
469392 storage.end = i;
470393 br.seek = 0;
471394 while (i < storage.buffer.len) {
472 const status = try br.unbuffered_reader.read(storage, .unlimited);
473 if (std.mem.indexOfScalarPos(u8, storage.buffer[0..storage.end], i, delimiter)) |end| return storage.buffer[0 .. end + 1];
474 if (status.end) return null;
395 const eos = eos: {
396 const read_len = br.unbuffered_reader.read(storage, .unlimited) catch |err| switch (err) {
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;
475408 i = storage.end;
476409 }
477410 return error.StreamTooLong;
478411}
479412
480/// Appends to `bw` contents by reading from the stream until `delimiter` is found.
481/// Does not write the delimiter itself.
482///
483/// If stream ends before delimiter found, returns `error.EndOfStream`.
413/// Appends to `bw` contents by reading from the stream until `delimiter` is
414/// found. Does not write the delimiter itself.
484415///
485416/// 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 {
487418 _ = br;
488419 _ = bw;
489420 _ = delimiter;
......@@ -495,29 +426,35 @@ pub fn streamReadDelimiter(br: *BufferedReader, bw: *std.io.BufferedWriter, deli
495426///
496427/// Succeeds if stream ends before delimiter found.
497428///
498/// Returns number of bytes streamed as well as whether the input reached the end.
499/// The end is not signaled to the writer.
429/// Returns number of bytes streamed. The end is not signaled to the writer.
500430pub fn streamReadDelimiterExclusive(
501431 br: *BufferedReader,
502 bw: *std.io.BufferedWriter,
432 bw: *BufferedWriter,
503433 delimiter: u8,
504) anyerror!Reader.Status {
434) Reader.ShortError!usize {
505435 _ = br;
506436 _ = bw;
507437 _ = delimiter;
508438 @panic("TODO");
509439}
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
511448/// Appends to `bw` contents by reading from the stream until `delimiter` is found.
512449/// Does not write the delimiter itself.
513///
514/// If `limit` is exceeded, returns `error.StreamTooLong`.
450//
451/// Returns number of bytes streamed.
515452pub fn streamReadDelimiterLimited(
516453 br: *BufferedReader,
517454 bw: *BufferedWriter,
518455 delimiter: u8,
519 limit: usize,
520) anyerror!void {
456 limit: Reader.Limit,
457) StreamDelimiterLimitedError!usize {
521458 _ = br;
522459 _ = bw;
523460 _ = delimiter;
......@@ -529,7 +466,7 @@ pub fn streamReadDelimiterLimited(
529466/// including the delimiter.
530467///
531468/// 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 {
533470 _ = br;
534471 _ = delimiter;
535472 @panic("TODO");
......@@ -538,8 +475,8 @@ pub fn discardDelimiterExclusive(br: *BufferedReader, delimiter: u8) anyerror!vo
538475/// Reads from the stream until specified byte is found, discarding all data,
539476/// excluding the delimiter.
540477///
541/// If end of stream is found, `error.EndOfStream` is returned.
542pub fn discardDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror!void {
478/// Succeeds if stream ends before delimiter found.
479pub fn discardDelimiterExclusive(br: *BufferedReader, delimiter: u8) Reader.ShortError!void {
543480 _ = br;
544481 _ = delimiter;
545482 @panic("TODO");
......@@ -548,69 +485,75 @@ pub fn discardDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror!vo
548485/// Fills the buffer such that it contains at least `n` bytes, without
549486/// advancing the seek position.
550487///
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.
552490///
553491/// 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 {
555493 const storage = &br.storage;
556494 assert(n <= storage.buffer.len);
557495 const buffer = storage.buffer[0..storage.end];
558496 const seek = br.seek;
559497 if (seek + n <= buffer.len) {
560498 @branchHint(.likely);
561 return true;
499 return;
562500 }
563501 const remainder = buffer[seek..];
564502 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
565503 storage.end = remainder.len;
566504 br.seek = 0;
567505 while (true) {
568 const status = try br.unbuffered_reader.read(storage, .unlimited);
569 if (n <= storage.end) return true;
570 if (status.end) return false;
506 const read_len = br.unbuffered_reader.read(storage, .unlimited) catch |err| switch (err) {
507 error.WriteFailed => storage.end - remainder.len,
508 else => |e| return e,
509 };
510 assert(storage.end == remainder.len + read_len);
511 if (n <= storage.end) return;
571512 }
572513}
573514
574515/// 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 {
576517 const storage = &br.storage;
577518 const buffer = storage.buffer[0..storage.end];
578519 const seek = br.seek;
579520 if (seek >= buffer.len) {
580521 @branchHint(.unlikely);
581 const filled = try fill(br, 1);
582 if (!filled) return error.EndOfStream;
522 try fill(br, 1);
583523 }
584524 br.seek = seek + 1;
585525 return buffer[seek];
586526}
587527
588528/// Same as `takeByte` except the returned byte is signed.
589pub fn takeByteSigned(br: *BufferedReader) anyerror!i8 {
529pub fn takeByteSigned(br: *BufferedReader) Reader.Error!i8 {
590530 return @bitCast(try br.takeByte());
591531}
592532
593533/// 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 {
595535 const n = @divExact(@typeInfo(T).int.bits, 8);
596536 return std.mem.readInt(T, try br.takeArray(n), endian);
597537}
598538
599539/// 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 {
601541 assert(n <= @sizeOf(Int));
602542 return std.mem.readVarInt(Int, try br.take(n), endian);
603543}
604544
605545/// 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 {
607547 // Only extern and packed structs have defined in-memory layout.
608548 comptime assert(@typeInfo(T).@"struct".layout != .auto);
609549 return @ptrCast(try br.takeArray(@sizeOf(T)));
610550}
611551
612552/// 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 {
614557 var res = (try br.takeStruct(T)).*;
615558 if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
616559 return res;
......@@ -621,14 +564,16 @@ pub fn takeStructEndian(br: *BufferedReader, comptime T: type, endian: std.built
621564/// it. Otherwise, returns `error.InvalidEnumTag`.
622565///
623566/// 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 {
625568 const Tag = @typeInfo(Enum).@"enum".tag_type;
626569 const int = try br.takeInt(Tag, endian);
627570 return std.meta.intToEnum(Enum, int);
628571}
629572
573pub const TakeLeb128Error = Reader.Error || error{Overflow};
574
630575/// 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 {
632577 const result_info = @typeInfo(Result).int;
633578 return std.math.cast(Result, try br.takeMultipleOf7Leb128(@Type(.{ .int = .{
634579 .signedness = result_info.signedness,
......@@ -636,7 +581,7 @@ pub fn takeLeb128(br: *BufferedReader, comptime Result: type) anyerror!Result {
636581 } }))) orelse error.Overflow;
637582}
638583
639fn takeMultipleOf7Leb128(br: *BufferedReader, comptime Result: type) anyerror!Result {
584fn takeMultipleOf7Leb128(br: *BufferedReader, comptime Result: type) TakeLeb128Error!Result {
640585 const result_info = @typeInfo(Result).int;
641586 comptime assert(result_info.bits % 7 == 0);
642587 var remaining_bits: std.math.Log2IntCeil(Result) = result_info.bits;
......@@ -708,7 +653,7 @@ test discard {
708653 try testing.expectError(error.EndOfStream, br.discard(1));
709654}
710655
711test discardUntilEnd {
656test discardRemaining {
712657 return error.Unimplemented;
713658}
714659
......@@ -795,3 +740,7 @@ test takeEnum {
795740test takeLeb128 {
796741 return error.Unimplemented;
797742}
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 {
3535 return .{
3636 .context = bw,
3737 .vtable = &.{
38 .writeSplat = passthru_writeSplat,
39 .writeFile = passthru_writeFile,
38 .writeSplat = passthruWriteSplat,
39 .writeFile = passthruWriteFile,
4040 },
4141 };
4242}
4343
4444const fixed_vtable: Writer.VTable = .{
45 .writeSplat = fixed_writeSplat,
46 .writeFile = Writer.unimplemented_writeFile,
45 .writeSplat = fixedWriteSplat,
46 .writeFile = Writer.failingWriteFile,
4747};
4848
4949/// Replaces the `BufferedWriter` with one that writes to `buffer` and returns
50/// `error.NoSpaceLeft` when it is full. `end` and `count` will always be
50/// `error.WriteFailed` when it is full. `end` and `count` will always be
5151/// equal.
5252pub fn initFixed(bw: *BufferedWriter, buffer: []u8) void {
5353 bw.* = .{
......@@ -72,10 +72,10 @@ pub fn reset(bw: *BufferedWriter) void {
7272 bw.count = 0;
7373}
7474
75pub fn flush(bw: *BufferedWriter) anyerror!void {
75pub fn flush(bw: *BufferedWriter) Writer.Error!void {
7676 const send_buffer = bw.buffer[0..bw.end];
7777 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..]});
7979 bw.end = 0;
8080}
8181
......@@ -84,7 +84,7 @@ pub fn unusedCapacitySlice(bw: *const BufferedWriter) []u8 {
8484}
8585
8686/// 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 {
8888 assert(bw.buffer.len >= minimum_length);
8989 const cap_slice = bw.buffer[bw.end..];
9090 if (cap_slice.len >= minimum_length) {
......@@ -92,7 +92,7 @@ pub fn writableSlice(bw: *BufferedWriter, minimum_length: usize) anyerror![]u8 {
9292 return cap_slice;
9393 }
9494 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});
9696 if (n == buffer.len) {
9797 @branchHint(.likely);
9898 bw.end = 0;
......@@ -115,11 +115,11 @@ pub fn advance(bw: *BufferedWriter, n: usize) void {
115115}
116116
117117/// The `data` parameter is mutable because this function needs to mutate the
118/// fields in order to handle partial writes from `Writer.VTable.writev`.
119pub fn writevAll(bw: *BufferedWriter, data: [][]const u8) anyerror!void {
118/// fields in order to handle partial writes from `Writer.VTable.writeVec`.
119pub fn writeVecAll(bw: *BufferedWriter, data: [][]const u8) Writer.Error!void {
120120 var i: usize = 0;
121121 while (true) {
122 var n = try passthru_writeSplat(bw, data[i..], 1);
122 var n = try passthruWriteSplat(bw, data[i..], 1);
123123 const len = data[i].len;
124124 while (n >= len) {
125125 n -= len;
......@@ -130,15 +130,15 @@ pub fn writevAll(bw: *BufferedWriter, data: [][]const u8) anyerror!void {
130130 }
131131}
132132
133pub fn writeSplat(bw: *BufferedWriter, data: []const []const u8, splat: usize) anyerror!usize {
134 return passthru_writeSplat(bw, data, splat);
133pub fn writeSplat(bw: *BufferedWriter, data: []const []const u8, splat: usize) Writer.Error!usize {
134 return passthruWriteSplat(bw, data, splat);
135135}
136136
137pub fn writev(bw: *BufferedWriter, data: []const []const u8) anyerror!usize {
138 return passthru_writeSplat(bw, data, 1);
137pub fn writeVec(bw: *BufferedWriter, data: []const []const u8) Writer.Error!usize {
138 return passthruWriteSplat(bw, data, 1);
139139}
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 {
142142 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
143143 const buffer = bw.buffer;
144144 const start_end = bw.end;
......@@ -258,11 +258,11 @@ fn track(count: *usize, n: usize) usize {
258258/// When this function is called it means the buffer got full, so it's time
259259/// to return an error. However, we still need to make sure all of the
260260/// 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 {
262262 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
263263 for (data) |bytes| {
264264 const dest = bw.buffer[bw.end..];
265 if (dest.len == 0) return error.NoSpaceLeft;
265 if (dest.len == 0) return error.WriteFailed;
266266 const len = @min(bytes.len, dest.len);
267267 @memcpy(dest[0..len], bytes[0..len]);
268268 bw.end += len;
......@@ -277,16 +277,16 @@ fn fixed_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize
277277 }
278278 bw.end = bw.buffer.len;
279279 bw.count = bw.end;
280 return error.NoSpaceLeft;
280 return error.WriteFailed;
281281}
282282
283pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize {
283pub fn write(bw: *BufferedWriter, bytes: []const u8) Writer.Error!usize {
284284 const buffer = bw.buffer;
285285 const end = bw.end;
286286 const new_end = end + bytes.len;
287287 if (new_end > buffer.len) {
288288 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);
290290 if (n < end) {
291291 @branchHint(.unlikely);
292292 const remainder = buffer[n..end];
......@@ -304,16 +304,16 @@ pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize {
304304
305305/// Calls `write` as many times as necessary such that all of `bytes` are
306306/// transferred.
307pub fn writeAll(bw: *BufferedWriter, bytes: []const u8) anyerror!void {
307pub fn writeAll(bw: *BufferedWriter, bytes: []const u8) Writer.Error!void {
308308 var index: usize = 0;
309309 while (index < bytes.len) index += try bw.write(bytes[index..]);
310310}
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 {
313313 try std.fmt.format(bw, format, args);
314314}
315315
316pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void {
316pub fn writeByte(bw: *BufferedWriter, byte: u8) Writer.Error!void {
317317 const buffer = bw.buffer[0..bw.end];
318318 if (buffer.len < bw.buffer.len) {
319319 @branchHint(.likely);
......@@ -324,7 +324,7 @@ pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void {
324324 }
325325 var buffers: [2][]const u8 = .{ buffer, &.{byte} };
326326 while (true) {
327 const n = try bw.unbuffered_writer.writev(&buffers);
327 const n = try bw.unbuffered_writer.writeVec(&buffers);
328328 if (n == 0) {
329329 @branchHint(.unlikely);
330330 continue;
......@@ -352,7 +352,7 @@ pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void {
352352
353353/// Writes the same byte many times, performing the underlying write call as
354354/// 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 {
356356 var remaining: usize = n;
357357 while (remaining > 0) remaining -= try bw.splatByte(byte, remaining);
358358}
......@@ -360,13 +360,13 @@ pub fn splatByteAll(bw: *BufferedWriter, byte: u8, n: usize) anyerror!void {
360360/// Writes the same byte many times, allowing short writes.
361361///
362362/// Does maximum of one underlying `Writer.VTable.writeSplat`.
363pub fn splatByte(bw: *BufferedWriter, byte: u8, n: usize) anyerror!usize {
364 return passthru_writeSplat(bw, &.{&.{byte}}, n);
363pub fn splatByte(bw: *BufferedWriter, byte: u8, n: usize) Writer.Error!usize {
364 return passthruWriteSplat(bw, &.{&.{byte}}, n);
365365}
366366
367367/// Writes the same slice many times, performing the underlying write call as
368368/// 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 {
370370 var remaining_bytes: usize = bytes.len * splat;
371371 remaining_bytes -= try bw.splatBytes(bytes, splat);
372372 while (remaining_bytes > 0) {
......@@ -378,26 +378,28 @@ pub fn splatBytesAll(bw: *BufferedWriter, bytes: []const u8, splat: usize) anyer
378378
379379/// Writes the same slice many times, allowing short writes.
380380///
381/// Does maximum of one underlying `Writer.VTable.writev`.
382pub fn splatBytes(bw: *BufferedWriter, bytes: []const u8, n: usize) anyerror!usize {
383 return passthru_writeSplat(bw, &.{bytes}, n);
381/// Does maximum of one underlying `Writer.VTable.writeVec`.
382pub fn splatBytes(bw: *BufferedWriter, bytes: []const u8, n: usize) Writer.Error!usize {
383 return passthruWriteSplat(bw, &.{bytes}, n);
384384}
385385
386386/// 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 {
388388 var bytes: [@divExact(@typeInfo(T).int.bits, 8)]u8 = undefined;
389389 std.mem.writeInt(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value, endian);
390390 return bw.writeAll(&bytes);
391391}
392392
393pub fn writeStruct(bw: *BufferedWriter, value: anytype) anyerror!void {
393pub fn writeStruct(bw: *BufferedWriter, value: anytype) Writer.Error!void {
394394 // Only extern and packed structs have defined in-memory layout.
395395 comptime assert(@typeInfo(@TypeOf(value)).@"struct".layout != .auto);
396396 return bw.writeAll(std.mem.asBytes(&value));
397397}
398398
399pub fn writeStructEndian(bw: *BufferedWriter, value: anytype, endian: std.builtin.Endian) anyerror!void {
400 // TODO: make sure this value is not a reference type
399/// The function is inline to avoid the dead code in case `endian` is
400/// 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 {
401403 if (native_endian == endian) {
402404 return bw.writeStruct(value);
403405 } else {
......@@ -407,6 +409,27 @@ pub fn writeStructEndian(bw: *BufferedWriter, value: anytype, endian: std.builti
407409 }
408410}
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
410433pub fn writeFile(
411434 bw: *BufferedWriter,
412435 file: std.fs.File,
......@@ -414,18 +437,18 @@ pub fn writeFile(
414437 limit: Writer.Limit,
415438 headers_and_trailers: []const []const u8,
416439 headers_len: usize,
417) anyerror!usize {
418 return passthru_writeFile(bw, file, offset, limit, headers_and_trailers, headers_len);
440) Writer.FileError!usize {
441 return passthruWriteFile(bw, file, offset, limit, headers_and_trailers, headers_len);
419442}
420443
421fn passthru_writeFile(
444fn passthruWriteFile(
422445 context: ?*anyopaque,
423446 file: std.fs.File,
424447 offset: Writer.Offset,
425448 limit: Writer.Limit,
426449 headers_and_trailers: []const []const u8,
427450 headers_len: usize,
428) anyerror!usize {
451) Writer.FileError!usize {
429452 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
430453 const buffer = bw.buffer;
431454 if (buffer.len == 0) return track(
......@@ -468,8 +491,8 @@ fn passthru_writeFile(
468491 bw.end = 0;
469492 return track(&bw.count, n - start_end);
470493 }
471 // Have not made it past the headers yet; must call `writev`.
472 const n = try bw.unbuffered_writer.writev(buffers[0 .. buffers_len + 1]);
494 // Have not made it past the headers yet; must call `writeVec`.
495 const n = try bw.unbuffered_writer.writeVec(buffers[0 .. buffers_len + 1]);
473496 if (n < end) {
474497 @branchHint(.unlikely);
475498 const remainder = buffer[n..end];
......@@ -505,7 +528,7 @@ pub const WriteFileOptions = struct {
505528 /// size here will save one syscall.
506529 limit: Writer.Limit = .unlimited,
507530 /// 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`.
509532 ///
510533 /// The parameter is mutable because this function needs to mutate the
511534 /// fields in order to handle partial writes from `Writer.VTable.writeFile`.
......@@ -515,11 +538,11 @@ pub const WriteFileOptions = struct {
515538 headers_len: usize = 0,
516539};
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 {
519542 const headers_and_trailers = options.headers_and_trailers;
520543 const headers = headers_and_trailers[0..options.headers_len];
521544 switch (options.limit) {
522 .nothing => return bw.writevAll(headers_and_trailers),
545 .nothing => return bw.writeVecAll(headers_and_trailers),
523546 .unlimited => {
524547 // When reading the whole file, we cannot include the trailers in the
525548 // 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
564587 if (i >= headers_and_trailers.len) return;
565588 }
566589 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..]);
568591 }
569592 offset = offset.advance(n);
570593 len -= n;
......@@ -579,7 +602,7 @@ pub fn alignBuffer(
579602 width: usize,
580603 alignment: std.fmt.Alignment,
581604 fill: u8,
582) anyerror!void {
605) Writer.Error!void {
583606 const padding = if (buffer.len < width) width - buffer.len else 0;
584607 if (padding == 0) {
585608 @branchHint(.likely);
......@@ -604,11 +627,11 @@ pub fn alignBuffer(
604627 }
605628}
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 {
608631 return bw.alignBuffer(buffer, options.width orelse buffer.len, options.alignment, options.fill);
609632}
610633
611pub fn printAddress(bw: *BufferedWriter, value: anytype) anyerror!void {
634pub fn printAddress(bw: *BufferedWriter, value: anytype) Writer.Error!void {
612635 const T = @TypeOf(value);
613636 switch (@typeInfo(T)) {
614637 .pointer => |info| {
......@@ -638,7 +661,7 @@ pub fn printValue(
638661 options: std.fmt.Options,
639662 value: anytype,
640663 max_depth: usize,
641) anyerror!void {
664) Writer.Error!void {
642665 const T = @TypeOf(value);
643666 const actual_fmt = comptime if (std.mem.eql(u8, fmt, ANY))
644667 defaultFormatString(T)
......@@ -791,7 +814,7 @@ pub fn printValue(
791814 },
792815 else => {
793816 var buffers: [2][]const u8 = .{ @typeName(ptr_info.child), "@" };
794 try bw.writevAll(&buffers);
817 try bw.writeVecAll(&buffers);
795818 try bw.printIntOptions(@intFromPtr(value), 16, .lower, options);
796819 return;
797820 },
......@@ -896,7 +919,7 @@ pub fn printInt(
896919 comptime fmt: []const u8,
897920 options: std.fmt.Options,
898921 value: anytype,
899) anyerror!void {
922) Writer.Error!void {
900923 const int_value = if (@TypeOf(value) == comptime_int) blk: {
901924 const Int = std.math.IntFittingRange(value, value);
902925 break :blk @as(Int, value);
......@@ -940,15 +963,15 @@ pub fn printInt(
940963 comptime unreachable;
941964}
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 {
944967 return bw.alignBufferOptions(@as(*const [1]u8, &c), options);
945968}
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 {
948971 return bw.alignBufferOptions(bytes, options);
949972}
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 {
952975 var buf: [4]u8 = undefined;
953976 const len = try std.unicode.utf8Encode(c, &buf);
954977 return bw.alignBufferOptions(buf[0..len], options);
......@@ -960,7 +983,7 @@ pub fn printIntOptions(
960983 base: u8,
961984 case: std.fmt.Case,
962985 options: std.fmt.Options,
963) anyerror!void {
986) Writer.Error!void {
964987 assert(base >= 2);
965988
966989 const int_value = if (@TypeOf(value) == comptime_int) blk: {
......@@ -1027,7 +1050,7 @@ pub fn printFloat(
10271050 comptime fmt: []const u8,
10281051 options: std.fmt.Options,
10291052 value: anytype,
1030) anyerror!void {
1053) Writer.Error!void {
10311054 var buf: [std.fmt.float.bufferSize(.decimal, f64)]u8 = undefined;
10321055
10331056 if (fmt.len > 1) invalidFmtError(fmt, value);
......@@ -1054,7 +1077,7 @@ pub fn printFloat(
10541077 }
10551078}
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 {
10581081 if (std.math.signbit(value)) try bw.writeByte('-');
10591082 if (std.math.isNan(value)) return bw.writeAll("nan");
10601083 if (std.math.isInf(value)) return bw.writeAll("inf");
......@@ -1168,7 +1191,7 @@ pub fn printByteSize(
11681191 value: u64,
11691192 comptime units: ByteSizeUnits,
11701193 options: std.fmt.Options,
1171) anyerror!void {
1194) Writer.Error!void {
11721195 if (value == 0) return bw.alignBufferOptions("0B", options);
11731196 // The worst case in terms of space needed is 32 bytes + 3 for the suffix.
11741197 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 {
12481271 @compileError("invalid format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'");
12491272}
12501273
1251pub fn printDurationSigned(bw: *BufferedWriter, ns: i64) anyerror!void {
1274pub fn printDurationSigned(bw: *BufferedWriter, ns: i64) Writer.Error!void {
12521275 if (ns < 0) try bw.writeByte('-');
12531276 return bw.printDurationUnsigned(@abs(ns));
12541277}
12551278
1256pub fn printDurationUnsigned(bw: *BufferedWriter, ns: u64) anyerror!void {
1279pub fn printDurationUnsigned(bw: *BufferedWriter, ns: u64) Writer.Error!void {
12571280 var ns_remaining = ns;
12581281 inline for (.{
12591282 .{ .ns = 365 * std.time.ns_per_day, .sep = 'y' },
......@@ -1303,7 +1326,7 @@ pub fn printDurationUnsigned(bw: *BufferedWriter, ns: u64) anyerror!void {
13031326/// Writes number of nanoseconds according to its signed magnitude:
13041327/// `[#y][#w][#d][#h][#m]#[.###][n|u|m]s`
13051328/// `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 {
13071330 // worst case: "-XXXyXXwXXdXXhXXmXX.XXXs".len = 24
13081331 var buf: [24]u8 = undefined;
13091332 var sub_bw: BufferedWriter = undefined;
......@@ -1315,7 +1338,7 @@ pub fn printDuration(bw: *BufferedWriter, nanoseconds: anytype, options: std.fmt
13151338 return bw.alignBufferOptions(sub_bw.getWritten(), options);
13161339}
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 {
13191342 const charset = switch (case) {
13201343 .upper => "0123456789ABCDEF",
13211344 .lower => "0123456789abcdef",
......@@ -1326,7 +1349,7 @@ pub fn printHex(bw: *BufferedWriter, bytes: []const u8, case: std.fmt.Case) anye
13261349 }
13271350}
13281351
1329pub fn printBase64(bw: *BufferedWriter, bytes: []const u8) anyerror!void {
1352pub fn printBase64(bw: *BufferedWriter, bytes: []const u8) Writer.Error!void {
13301353 var chunker = std.mem.window(u8, bytes, 3, 3);
13311354 var temp: [5]u8 = undefined;
13321355 while (chunker.next()) |chunk| {
......@@ -1335,7 +1358,7 @@ pub fn printBase64(bw: *BufferedWriter, bytes: []const u8) anyerror!void {
13351358}
13361359
13371360/// 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 {
13391362 try bw.writeLeb128(switch (@typeInfo(@TypeOf(value))) {
13401363 .comptime_int => @as(std.math.IntFittingRange(0, @abs(value)), value),
13411364 .int => |value_info| switch (value_info.signedness) {
......@@ -1347,7 +1370,7 @@ pub fn writeUleb128(bw: *BufferedWriter, value: anytype) anyerror!void {
13471370}
13481371
13491372/// 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 {
13511374 try bw.writeLeb128(switch (@typeInfo(@TypeOf(value))) {
13521375 .comptime_int => @as(std.math.IntFittingRange(@min(value, -1), @max(0, value)), value),
13531376 .int => |value_info| switch (value_info.signedness) {
......@@ -1359,7 +1382,7 @@ pub fn writeSleb128(bw: *BufferedWriter, value: anytype) anyerror!void {
13591382}
13601383
13611384/// 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 {
13631386 const value_info = @typeInfo(@TypeOf(value)).int;
13641387 try bw.writeMultipleOf7Leb128(@as(@Type(.{ .int = .{
13651388 .signedness = value_info.signedness,
......@@ -1367,7 +1390,7 @@ pub fn writeLeb128(bw: *BufferedWriter, value: anytype) anyerror!void {
13671390 } }), value));
13681391}
13691392
1370fn writeMultipleOf7Leb128(bw: *BufferedWriter, value: anytype) anyerror!void {
1393fn writeMultipleOf7Leb128(bw: *BufferedWriter, value: anytype) Writer.Error!void {
13711394 const value_info = @typeInfo(@TypeOf(value)).int;
13721395 comptime assert(value_info.bits % 7 == 0);
13731396 var remaining = value;
......@@ -1409,7 +1432,7 @@ test "formatValue max_depth" {
14091432 comptime fmt: []const u8,
14101433 options: std.fmt.Options,
14111434 bw: *BufferedWriter,
1412 ) anyerror!void {
1435 ) Writer.Error!void {
14131436 _ = options;
14141437 if (fmt.len == 0) {
14151438 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 @@
11const std = @import("../std.zig");
22const Reader = @This();
33const assert = std.debug.assert;
4const BufferedWriter = std.io.BufferedWriter;
45
56context: ?*anyopaque,
67vtable: *const VTable,
......@@ -16,35 +17,54 @@ pub const VTable = struct {
1617 /// accordance with the number of bytes return from this function.
1718 ///
1819 /// The implementation should do a maximum of one underlying read call.
19 ///
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,
20 read: *const fn (context: ?*anyopaque, bw: *BufferedWriter, limit: Limit) RwError!usize,
2321
2422 /// Writes bytes from the internally tracked stream position to `data`.
2523 ///
26 /// Returns the number of bytes written, which will be at minimum `0` and at
27 /// most `limit`. The number of bytes read, including zero, does not
28 /// indicate end of stream.
24 /// Returns the number of bytes written, which will be at minimum `0` and
25 /// at most the sum of each data slice length. The number of bytes read,
26 /// including zero, does not indicate end of stream.
2927 ///
3028 /// If the reader has an internal seek position, it moves forward in
3129 /// accordance with the number of bytes return from this function.
3230 ///
3331 /// 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.
3436 ///
35 /// If `error.Unstreamable` is returned, the resource cannot be used via a
36 /// streaming reading interface.
37 readv: *const fn (ctx: ?*anyopaque, data: []const []u8) anyerror!Status,
37 /// Returns the number of bytes discarded, which will be at minimum `0` and
38 /// at most `limit`. The number of bytes returned, including zero, does not
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,
3846};
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) {
43 /// Number of bytes that were transferred. Zero does not mean end of
44 /// stream.
45 len: Len = 0,
46 /// Indicates end of stream.
47 end: bool = false,
64/// For functions that cannot fail with `error.EndOfStream`.
65pub const ShortError = error{
66 /// See the `Reader` implementation for detailed diagnostics.
67 ReadFailed,
4868};
4969
5070pub const Limit = enum(usize) {
......@@ -93,50 +113,122 @@ pub const Limit = enum(usize) {
93113 }
94114};
95115
96pub fn read(r: Reader, w: *std.io.BufferedWriter, limit: Limit) anyerror!Status {
97 return r.vtable.read(r.context, w, limit);
116pub fn read(r: Reader, bw: *BufferedWriter, limit: Limit) RwError!usize {
117 return r.vtable.read(r.context, bw, limit);
98118}
99119
100pub fn readv(r: Reader, data: []const []u8) anyerror!Status {
101 return r.vtable.readv(r.context, data);
120pub fn readVec(r: Reader, data: []const []u8) Error!usize {
121 return r.vtable.readVec(r.context, data);
102122}
103123
104/// Returns total number of bytes written to `w`.
105pub fn readAll(r: Reader, w: *std.io.BufferedWriter) anyerror!usize {
124pub fn discard(r: Reader, limit: Limit) Error!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 {
106130 const readFn = r.vtable.read;
107131 var offset: usize = 0;
108132 while (true) {
109 const status = try readFn(r.context, w, .unlimited);
110 offset += status.len;
111 if (status.end) return offset;
133 offset += readFn(r.context, bw, .unlimited) catch |err| switch (err) {
134 error.EndOfStream => return offset,
135 else => |e| return e,
136 };
112137 }
113138}
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
115155/// Allocates enough memory to hold all the contents of the stream. If the allocated
116156/// memory would be greater than `max_size`, returns `error.StreamTooLong`.
117157///
118158/// Caller owns returned memory.
119159///
120160/// 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 {
122162 const readFn = r.vtable.read;
123163 var aw: std.io.AllocatingWriter = undefined;
124164 errdefer aw.deinit();
125165 aw.init(gpa);
126166 var remaining = max_size;
127167 while (remaining > 0) {
128 const status = try readFn(r.context, &aw.buffered_writer, .limited(remaining));
129 if (status.end) break;
130 remaining -= status.len;
168 const n = readFn(r.context, &aw.buffered_writer, .limited(remaining)) catch |err| switch (err) {
169 error.WriteFailed => return error.OutOfMemory,
170 error.EndOfStream => break,
171 error.ReadFailed => return error.ReadFailed,
172 };
173 remaining -= n;
131174 }
132175 return aw.toOwnedSlice();
133176}
134177
135/// Reads the stream until the end, ignoring all the data.
136/// Returns the number of bytes discarded.
137pub fn discardUntilEnd(r: Reader) anyerror!usize {
138 var bw = std.io.Writer.null.unbuffered();
139 return r.readAll(&bw);
178pub const failing: Reader = .{
179 .context = undefined,
180 .vtable = &.{
181 .read = failingRead,
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;
140232}
141233
142234test "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" {
144236 str: []const u8,
145237 curr: usize,
146238
147 fn read(self: *@This(), dest: []u8) anyerror!usize {
239 fn read(self: *@This(), dest: []u8) usize {
148240 if (self.str.len <= self.curr or dest.len == 0)
149241 return 0;
150242
lib/std/io/Writer.zig+41-63
......@@ -2,6 +2,8 @@ const std = @import("../std.zig");
22const assert = std.debug.assert;
33const Writer = @This();
44
5pub const Null = @import("Writer/Null.zig");
6
57context: ?*anyopaque,
68vtable: *const VTable,
79
......@@ -16,8 +18,8 @@ pub const VTable = struct {
1618 ///
1719 /// Number of bytes returned may be zero, which does not mean
1820 /// end-of-stream. A subsequent call may return nonzero, or may signal end
19 /// of stream via an error.
20 writeSplat: *const fn (ctx: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize,
21 /// of stream via `error.WriteFailed`.
22 writeSplat: *const fn (ctx: ?*anyopaque, data: []const []const u8, splat: usize) Error!usize,
2123
2224 /// Writes contents from an open file. `headers` are written first, then `len`
2325 /// bytes of `file` starting from `offset`, then `trailers`.
......@@ -27,7 +29,7 @@ pub const VTable = struct {
2729 ///
2830 /// Number of bytes returned may be zero, which does not mean
2931 /// 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`.
3133 writeFile: *const fn (
3234 ctx: ?*anyopaque,
3335 file: std.fs.File,
......@@ -37,12 +39,19 @@ pub const VTable = struct {
3739 /// Maximum amount of bytes to read from the file.
3840 limit: Limit,
3941 /// 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`.
4143 headers_and_trailers: []const []const u8,
4244 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,
4451};
4552
53pub const FileError = Error || std.fs.File.PReadError;
54
4655pub const Limit = std.io.Reader.Limit;
4756
4857pub const Offset = enum(u64) {
......@@ -69,11 +78,11 @@ pub const Offset = enum(u64) {
6978 }
7079};
7180
72pub fn writev(w: Writer, data: []const []const u8) anyerror!usize {
81pub fn writeVec(w: Writer, data: []const []const u8) Error!usize {
7382 return w.vtable.writeSplat(w.context, data, 1);
7483}
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 {
7786 return w.vtable.writeSplat(w.context, data, splat);
7887}
7988
......@@ -84,27 +93,10 @@ pub fn writeFile(
8493 limit: Limit,
8594 headers_and_trailers: []const []const u8,
8695 headers_len: usize,
87) anyerror!usize {
96) FileError!usize {
8897 return w.vtable.writeFile(w.context, file, offset, limit, headers_and_trailers, headers_len);
8998}
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
108100pub fn buffered(w: Writer, buffer: []u8) std.io.BufferedWriter {
109101 return .{
110102 .buffer = buffer,
......@@ -116,52 +108,38 @@ pub fn unbuffered(w: Writer) std.io.BufferedWriter {
116108 return w.buffered(&.{});
117109}
118110
119/// A `Writer` that discards all data.
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 {
111pub fn failingWriteSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) Error!usize {
129112 _ = context;
130 const headers = data[0 .. data.len - 1];
131 const pattern = data[headers.len..];
132 var written: usize = pattern.len * splat;
133 for (headers) |bytes| written += bytes.len;
134 return written;
113 _ = data;
114 _ = splat;
115 return error.WriteFailed;
135116}
136117
137fn null_writeFile(
118pub fn failingWriteFile(
138119 context: ?*anyopaque,
139120 file: std.fs.File,
140 offset: Offset,
141 limit: Limit,
121 offset: std.io.Writer.Offset,
122 limit: std.io.Writer.Limit,
142123 headers_and_trailers: []const []const u8,
143124 headers_len: usize,
144) anyerror!usize {
125) Error!usize {
145126 _ = context;
146 var n: usize = 0;
147 if (offset == .none) {
148 @panic("TODO seek the file forwards");
149 }
150 const limit_int = limit.toInt() orelse {
151 const headers = headers_and_trailers[0..headers_len];
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;
127 _ = file;
128 _ = offset;
129 _ = limit;
130 _ = headers_and_trailers;
131 _ = headers_len;
132 return error.WriteFailed;
163133}
164134
165test @"null" {
166 try @"null".writeAll("yay");
135pub const failing: Writer = .{
136 .context = undefined,
137 .vtable = &.{
138 .writeSplat = failingWriteSplat,
139 .writeFile = failingWriteFile,
140 },
141};
142
143test {
144 _ = Null;
167145}
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) {
7171 reset_attributes: u16,
7272 };
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 {
7577 nosuspend switch (conf) {
7678 .no_color => return,
7779 .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)
7777else
7878 .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 {
8183 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
8284 try self.valueStart();
8385 try self.writer.writeByte('[');
......@@ -85,7 +87,7 @@ pub fn beginArray(self: *Stringify) anyerror!void {
8587 self.next_punctuation = .none;
8688}
8789
88pub fn beginObject(self: *Stringify) anyerror!void {
90pub fn beginObject(self: *Stringify) Error!void {
8991 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
9092 try self.valueStart();
9193 try self.writer.writeByte('{');
......@@ -93,7 +95,7 @@ pub fn beginObject(self: *Stringify) anyerror!void {
9395 self.next_punctuation = .none;
9496}
9597
96pub fn endArray(self: *Stringify) anyerror!void {
98pub fn endArray(self: *Stringify) Error!void {
9799 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
98100 self.popIndentation(.array);
99101 switch (self.next_punctuation) {
......@@ -107,7 +109,7 @@ pub fn endArray(self: *Stringify) anyerror!void {
107109 self.valueDone();
108110}
109111
110pub fn endObject(self: *Stringify) anyerror!void {
112pub fn endObject(self: *Stringify) Error!void {
111113 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
112114 self.popIndentation(.object);
113115 switch (self.next_punctuation) {
......@@ -213,7 +215,7 @@ fn isComplete(self: *const Stringify) bool {
213215/// assuming the resulting formatted string represents a single complete value;
214216/// e.g. `"1"`, `"[]"`, `"[1,2]"`, not `"1,2"`.
215217/// 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 {
217219 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
218220 try self.valueStart();
219221 try self.writer.print(fmt, args);
......@@ -274,7 +276,7 @@ pub fn endWriteRaw(self: *Stringify) void {
274276/// `key` is the string content of the property name.
275277/// Surrounding quotes will be added and any special characters will be escaped.
276278/// See also `objectFieldRaw`.
277pub fn objectField(self: *Stringify, key: []const u8) anyerror!void {
279pub fn objectField(self: *Stringify, key: []const u8) Error!void {
278280 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
279281 try self.objectFieldStart();
280282 try encodeJsonString(key, self.options, self.writer);
......@@ -284,7 +286,7 @@ pub fn objectField(self: *Stringify, key: []const u8) anyerror!void {
284286/// `quoted_key` is the complete bytes of the key including quotes and any necessary escape sequences.
285287/// A few assertions are performed on the given value to ensure that the caller of this function understands the API contract.
286288/// 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 {
288290 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
289291 assert(quoted_key.len >= 2 and quoted_key[0] == '"' and quoted_key[quoted_key.len - 1] == '"'); // quoted_key should be "quoted".
290292 try self.objectFieldStart();
......@@ -343,7 +345,7 @@ pub fn endObjectFieldRaw(self: *Stringify) void {
343345///
344346/// See also alternative functions `print` and `beginWriteRaw`.
345347/// 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 {
347349 if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
348350 const T = @TypeOf(v);
349351 switch (@typeInfo(T)) {
......@@ -568,7 +570,7 @@ pub const Options = struct {
568570/// Writes the given value to the `std.io.Writer` writer.
569571/// See `Stringify` for how the given value is serialized into JSON.
570572/// 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 {
572574 var s: Stringify = .{ .writer = writer, .options = options };
573575 try s.write(v);
574576}
......@@ -632,7 +634,7 @@ test valueAlloc {
632634 try std.testing.expectEqualStrings(expected, actual);
633635}
634636
635fn outputUnicodeEscape(codepoint: u21, bw: *std.io.BufferedWriter) anyerror!void {
637fn outputUnicodeEscape(codepoint: u21, bw: *std.io.BufferedWriter) Error!void {
636638 if (codepoint <= 0xFFFF) {
637639 // If the character is in the Basic Multilingual Plane (U+0000 through U+FFFF),
638640 // 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
652654 }
653655}
654656
655fn outputSpecialEscape(c: u8, writer: *std.io.BufferedWriter) anyerror!void {
657fn outputSpecialEscape(c: u8, writer: *std.io.BufferedWriter) Error!void {
656658 switch (c) {
657659 '\\' => try writer.writeAll("\\\\"),
658660 '\"' => try writer.writeAll("\\\""),
......@@ -666,14 +668,14 @@ fn outputSpecialEscape(c: u8, writer: *std.io.BufferedWriter) anyerror!void {
666668}
667669
668670/// 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 {
670672 try writer.writeByte('\"');
671673 try encodeJsonStringChars(string, options, writer);
672674 try writer.writeByte('\"');
673675}
674676
675677/// 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 {
677679 var write_cursor: usize = 0;
678680 var i: usize = 0;
679681 if (options.escape_unicode) {
......@@ -722,7 +724,7 @@ test "json write stream" {
722724 try testBasicWriteStream(&w);
723725}
724726
725fn testBasicWriteStream(w: *Stringify) anyerror!void {
727fn testBasicWriteStream(w: *Stringify) Error!void {
726728 w.writer.reset();
727729
728730 try w.beginObject();
lib/std/json/dynamic.zig+3-3
......@@ -51,10 +51,10 @@ pub const Value = union(enum) {
5151 }
5252
5353 pub fn dump(v: Value) void {
54 var bw = std.debug.lockStdErr2(&.{});
55 defer std.debug.unlockStdErr();
54 const bw = std.debug.lockStderrWriter(&.{});
55 defer std.debug.unlockStderrWriter();
5656
57 json.Stringify.value(v, .{}, &bw) catch return;
57 json.Stringify.value(v, .{}, bw) catch return;
5858 }
5959
6060 pub fn jsonStringify(value: @This(), jws: anytype) !void {
lib/std/log.zig+3-6
......@@ -149,12 +149,9 @@ pub fn defaultLog(
149149 const level_txt = comptime message_level.asText();
150150 const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
151151 var buffer: [1024]u8 = undefined;
152 var bw: std.io.BufferedWriter = std.debug.lockStdErr2(&buffer);
153 defer std.debug.unlockStdErr();
154 nosuspend {
155 bw.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return;
156 bw.flush() catch return;
157 }
152 const bw = std.debug.lockStderrWriter(&buffer);
153 defer std.debug.unlockStderrWriter();
154 bw.print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return;
158155}
159156
160157/// 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 {
23222322 /// this function will fail to print the string, printing "(BigInt)" instead of a number.
23232323 /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.
23242324 /// 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 {
23262326 comptime var base = 10;
23272327 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 {
850850}
851851
852852// 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.
854const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || anyerror || posix.SocketError || posix.BindError || posix.SetSockOptError || error{
853// store the sub-error as a diagnostic value.
854const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || posix.SocketError || posix.BindError || posix.SetSockOptError || error{
855855 TemporaryNameServerFailure,
856856 NameServerFailure,
857857 AddressFamilyNotSupported,
......@@ -1873,14 +1873,14 @@ pub const Stream = struct {
18731873 context: ?*anyopaque,
18741874 bw: *std.io.BufferedWriter,
18751875 limit: std.io.Reader.Limit,
1876 ) anyerror!std.io.Reader.Status {
1876 ) std.io.Reader.Error!usize {
18771877 const buf = limit.slice(try bw.writableSlice(1));
18781878 const status = try windows_readv(context, &.{buf});
18791879 bw.advance(status.len);
18801880 return status;
18811881 }
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 {
18841884 var iovecs: [max_buffers_len]windows.WSABUF = undefined;
18851885 var iovecs_i: usize = 0;
18861886 for (data) |d| {
......@@ -1915,7 +1915,7 @@ pub const Stream = struct {
19151915 return .{ .len = n, .end = n == 0 };
19161916 }
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 {
19191919 comptime assert(native_os == .windows);
19201920 if (data.len == 1 and splat == 0) return 0;
19211921 var splat_buffer: [256]u8 = undefined;
......@@ -1974,7 +1974,7 @@ pub const Stream = struct {
19741974 return n;
19751975 }
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 {
19781978 const sock_fd = opaqueToHandle(context);
19791979 comptime assert(native_os != .windows);
19801980 var splat_buffer: [256]u8 = undefined;
......@@ -2028,7 +2028,7 @@ pub const Stream = struct {
20282028 in_len: std.io.Writer.FileLen,
20292029 headers_and_trailers: []const []const u8,
20302030 headers_len: usize,
2031 ) anyerror!usize {
2031 ) std.io.Writer.FileError!usize {
20322032 const len_int = switch (in_len) {
20332033 .zero => return windows_writeSplat(context, headers_and_trailers, 1),
20342034 .entire_file => std.math.maxInt(usize),
lib/std/tar.zig+1-1
......@@ -603,7 +603,7 @@ fn PaxIterator(comptime ReaderType: type) type {
603603 return null;
604604 }
605605
606 fn readUntil(self: *Self, delimiter: u8) anyerror![]const u8 {
606 fn readUntil(self: *Self, delimiter: u8) ![]const u8 {
607607 var fbs: std.io.BufferedWriter = undefined;
608608 fbs.initFixed(&self.scratch);
609609 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
390390 const actual_window = actual[window_start..@min(actual.len, window_start + max_window_size)];
391391 const actual_truncated = window_start + actual_window.len < actual.len;
392392
393 var bw = std.debug.lockStdErr2(&.{});
394 defer std.debug.unlockStdErr();
393 const bw = std.debug.lockStderrWriter(&.{});
394 defer std.debug.unlockStderrWriter();
395395 const ttyconf = std.io.tty.detectConfig(.stderr());
396396 var differ = if (T == u8) BytesDiffer{
397397 .expected = expected_window,
......@@ -416,7 +416,7 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
416416 print("... truncated ...\n", .{});
417417 }
418418 }
419 differ.write(&bw) catch {};
419 differ.write(bw) catch {};
420420 if (expected_truncated) {
421421 const end_offset = window_start + expected_window.len;
422422 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
438438 print("... truncated ...\n", .{});
439439 }
440440 }
441 differ.write(&bw) catch {};
441 differ.write(bw) catch {};
442442 if (actual_truncated) {
443443 const end_offset = window_start + actual_window.len;
444444 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 {
207207 return aw.toOwnedSlice();
208208}
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 {
211211 return @import("./render.zig").renderTree(gpa, bw, tree, fixups);
212212}
213213
......@@ -315,7 +315,7 @@ pub fn rootDecls(tree: Ast) []const Node.Index {
315315 }
316316}
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 {
319319 switch (parse_error.tag) {
320320 .asterisk_after_ptr_deref => {
321321 // 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 {
158158
159159pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void {
160160 var buffer: [256]u8 = undefined;
161 var bw = std.debug.lockStdErr2(&buffer);
162 defer std.debug.unlockStdErr();
163 renderToWriter(eb, options, &bw) catch return;
164 bw.flush() catch return;
161 const bw = std.debug.lockStderrWriter(&buffer);
162 defer std.debug.unlockStderrWriter();
163 renderToWriter(eb, options, bw) catch return;
165164}
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 {
168167 if (eb.extra.len == 0) return;
169168 for (eb.getMessages()) |err_msg| {
170169 try renderErrorMessageToWriter(eb, options, err_msg, bw, "error", .red, 0);
......@@ -187,7 +186,7 @@ fn renderErrorMessageToWriter(
187186 kind: []const u8,
188187 color: std.io.tty.Color,
189188 indent: usize,
190) anyerror!void {
189) std.io.Writer.Error!void {
191190 const ttyconf = options.ttyconf;
192191 const err_msg = eb.getErrorMessage(err_msg_index);
193192 const prefix_start = bw.count;
lib/std/zig/Server.zig+42-145
......@@ -1,6 +1,5 @@
1in: std.fs.File,
2out: std.fs.File,
3receive_fifo: std.fifo.LinearFifo(u8, .Dynamic),
1in: *std.io.BufferedReader,
2out: *std.io.BufferedWriter,
43
54pub const Message = struct {
65 pub const Header = extern struct {
......@@ -94,9 +93,8 @@ pub const Message = struct {
9493};
9594
9695pub const Options = struct {
97 gpa: Allocator,
98 in: std.fs.File,
99 out: std.fs.File,
96 in: *std.io.BufferedReader,
97 out: *std.io.BufferedWriter,
10098 zig_version: []const u8,
10199};
102100
......@@ -104,96 +102,40 @@ pub fn init(options: Options) !Server {
104102 var s: Server = .{
105103 .in = options.in,
106104 .out = options.out,
107 .receive_fifo = std.fifo.LinearFifo(u8, .Dynamic).init(options.gpa),
108105 };
109106 try s.serveStringMessage(.zig_version, options.zig_version);
110107 return s;
111108}
112109
113pub fn deinit(s: *Server) void {
114 s.receive_fifo.deinit();
115 s.* = undefined;
116}
117
118110pub fn receiveMessage(s: *Server) !InMessage.Header {
119 const Header = InMessage.Header;
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 }
111 return try s.in.takeStructEndian(InMessage.Header, .little);
154112}
155113
156114pub fn receiveBody_u32(s: *Server) !u32 {
157 const fifo = &s.receive_fifo;
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);
115 return s.in.takeInt(u32, .little);
162116}
163117
164118pub fn serveStringMessage(s: *Server, tag: OutMessage.Tag, msg: []const u8) !void {
165 return s.serveMessage(.{
119 try s.serveMessageHeader(.{
166120 .tag = tag,
167 .bytes_len = @as(u32, @intCast(msg.len)),
168 }, &.{msg});
121 .bytes_len = @intCast(msg.len),
122 });
123 try s.out.writeAll(msg);
124 try s.out.flush();
169125}
170126
171pub fn serveMessage(
172 s: *const Server,
173 header: OutMessage.Header,
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]);
127/// Don't forget to flush!
128pub fn serveMessageHeader(s: *const Server, header: OutMessage.Header) !void {
129 try s.out.writeStructEndian(header, .little);
189130}
190131
191pub fn serveU64Message(s: *Server, tag: OutMessage.Tag, int: u64) !void {
192 const msg_le = bswap(int);
193 return s.serveMessage(.{
132pub fn serveU64Message(s: *const Server, tag: OutMessage.Tag, int: u64) !void {
133 try serveMessageHeader(s, .{
194134 .tag = tag,
195135 .bytes_len = @sizeOf(u64),
196 }, &.{std.mem.asBytes(&msg_le)});
136 });
137 try s.out.writeInt(u64, int, .little);
138 try s.out.flush();
197139}
198140
199141pub fn serveEmitDigest(
......@@ -201,26 +143,22 @@ pub fn serveEmitDigest(
201143 digest: *const [Cache.bin_digest_len]u8,
202144 header: OutMessage.EmitDigest,
203145) !void {
204 try s.serveMessage(.{
146 try s.serveMessageHeader(.{
205147 .tag = .emit_digest,
206148 .bytes_len = @intCast(digest.len + @sizeOf(OutMessage.EmitDigest)),
207 }, &.{
208 std.mem.asBytes(&header),
209 digest,
210149 });
150 try s.out.writeStructEndian(header, .little);
151 try s.out.writeAll(digest);
152 try s.out.flush();
211153}
212154
213pub fn serveTestResults(
214 s: *Server,
215 msg: OutMessage.TestResults,
216) !void {
217 const msg_le = bswap(msg);
218 try s.serveMessage(.{
155pub fn serveTestResults(s: *Server, msg: OutMessage.TestResults) !void {
156 try s.serveMessageHeader(.{
219157 .tag = .test_results,
220158 .bytes_len = @intCast(@sizeOf(OutMessage.TestResults)),
221 }, &.{
222 std.mem.asBytes(&msg_le),
223159 });
160 try s.out.writeStructEndian(msg, .little);
161 try s.out.flush();
224162}
225163
226164pub 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 {
230168 };
231169 const bytes_len = @sizeOf(OutMessage.ErrorBundle) +
232170 4 * error_bundle.extra.len + error_bundle.string_bytes.len;
233 try s.serveMessage(.{
171 try s.serveMessageHeader(.{
234172 .tag = .error_bundle,
235173 .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,
241174 });
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();
242179}
243180
244181pub const TestMetadata = struct {
245 names: []u32,
246 expected_panic_msgs: []u32,
182 names: []const u32,
183 expected_panic_msgs: []const u32,
247184 string_bytes: []const u8,
248185};
249186
250187pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void {
251188 const header: OutMessage.TestMetadata = .{
252 .tests_len = bswap(@as(u32, @intCast(test_metadata.names.len))),
253 .string_bytes_len = bswap(@as(u32, @intCast(test_metadata.string_bytes.len))),
189 .tests_len = @as(u32, @intCast(test_metadata.names.len)),
190 .string_bytes_len = @as(u32, @intCast(test_metadata.string_bytes.len)),
254191 };
255192 const trailing = 2;
256193 const bytes_len = @sizeOf(OutMessage.TestMetadata) +
257194 trailing * @sizeOf(u32) * test_metadata.names.len + test_metadata.string_bytes.len;
258195
259 if (need_bswap) {
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(.{
196 try s.serveMessageHeader(.{
269197 .tag = .test_metadata,
270198 .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,
277199 });
278}
279
280fn bswap(x: anytype) @TypeOf(x) {
281 if (!need_bswap) return x;
282
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.*);
200 try s.out.writeStructEndian(header, .little);
201 try s.out.writeArrayEndian(u32, test_metadata.names, .little);
202 try s.out.writeArrayEndian(u32, test_metadata.expected_panic_msgs, .little);
203 try s.out.writeAll(test_metadata.string_bytes);
204 try s.out.flush();
308205}
309206
310207const OutMessage = std.zig.Server.Message;
lib/std/zig/ZonGen.zig+1-1
......@@ -520,7 +520,7 @@ pub fn parseStrLit(
520520 tree: Ast,
521521 node: Ast.Node.Index,
522522 writer: *std.io.BufferedWriter,
523) anyerror!std.zig.string_literal.Result {
523) error{OutOfMemory}!std.zig.string_literal.Result {
524524 switch (tree.nodeTag(node)) {
525525 .string_literal => {
526526 const token = tree.nodeMainToken(node);
lib/std/zig/llvm/BitcodeReader.zig+3-3
......@@ -170,7 +170,7 @@ pub fn next(bc: *BitcodeReader) !?Item {
170170 }
171171}
172172
173pub fn skipBlock(bc: *BitcodeReader, block: Block) anyerror!void {
173pub fn skipBlock(bc: *BitcodeReader, block: Block) !void {
174174 assert(bc.bit_offset == 0);
175175 try bc.br.discard(4 * @as(u34, block.len));
176176 try bc.endBlock();
......@@ -369,12 +369,12 @@ fn align32Bits(bc: *BitcodeReader) void {
369369 bc.bit_offset = 0;
370370}
371371
372fn read32Bits(bc: *BitcodeReader) anyerror!u32 {
372fn read32Bits(bc: *BitcodeReader) !u32 {
373373 assert(bc.bit_offset == 0);
374374 return bc.br.takeInt(u32, .little);
375375}
376376
377fn readBytes(bc: *BitcodeReader, bytes: []u8) anyerror!void {
377fn readBytes(bc: *BitcodeReader, bytes: []u8) !void {
378378 assert(bc.bit_offset == 0);
379379 try bc.br.read(bytes);
380380
lib/std/zig/llvm/Builder.zig+32-32
......@@ -91,7 +91,7 @@ pub const String = enum(u32) {
9191 string: String,
9292 builder: *const Builder,
9393 };
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 {
9595 if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_|
9696 @compileError("invalid format string: '" ++ fmt_str ++ "'");
9797 assert(data.string != .none);
......@@ -649,7 +649,7 @@ pub const Type = enum(u32) {
649649 type: Type,
650650 builder: *const Builder,
651651 };
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 {
653653 assert(data.type != .none);
654654 if (comptime std.mem.eql(u8, fmt_str, "m")) {
655655 const item = data.builder.type_items.items[@intFromEnum(data.type)];
......@@ -1129,7 +1129,7 @@ pub const Attribute = union(Kind) {
11291129 attribute_index: Index,
11301130 builder: *const Builder,
11311131 };
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 {
11331133 if (comptime std.mem.indexOfNone(u8, fmt_str, "\"#")) |_|
11341134 @compileError("invalid format string: '" ++ fmt_str ++ "'");
11351135 const attribute = data.attribute_index.toAttribute(data.builder);
......@@ -1568,7 +1568,7 @@ pub const Attributes = enum(u32) {
15681568 attributes: Attributes,
15691569 builder: *const Builder,
15701570 };
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 {
15721572 for (data.attributes.slice(data.builder)) |attribute_index| try Attribute.Index.format(.{
15731573 .attribute_index = attribute_index,
15741574 .builder = data.builder,
......@@ -1761,11 +1761,11 @@ pub const Linkage = enum(u4) {
17611761 extern_weak = 7,
17621762 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 {
17651765 if (self != .external) try bw.print(" {s}", .{@tagName(self)});
17661766 }
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 {
17691769 if (data) |linkage| try bw.print(" {s}", .{@tagName(linkage)});
17701770 }
17711771 pub fn fmtOptional(self: ?Linkage) std.fmt.Formatter(formatOptional) {
......@@ -1778,7 +1778,7 @@ pub const Preemption = enum {
17781778 dso_local,
17791779 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 {
17821782 if (self == .dso_local) try bw.print(" {s}", .{@tagName(self)});
17831783 }
17841784};
......@@ -1799,8 +1799,8 @@ pub const Visibility = enum(u2) {
17991799 pub fn format(
18001800 self: Visibility,
18011801 comptime format_string: []const u8,
1802 writer: anytype,
1803 ) @TypeOf(writer).Error!void {
1802 writer: *std.io.BufferedWriter,
1803 ) std.io.Writer.Error!void {
18041804 comptime assert(format_string.len == 0);
18051805 if (self != .default) try writer.print(" {s}", .{@tagName(self)});
18061806 }
......@@ -1811,7 +1811,7 @@ pub const DllStorageClass = enum(u2) {
18111811 dllimport = 1,
18121812 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 {
18151815 if (self != .default) try bw.print(" {s}", .{@tagName(self)});
18161816 }
18171817};
......@@ -1823,7 +1823,7 @@ pub const ThreadLocal = enum(u3) {
18231823 initialexec = 3,
18241824 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 {
18271827 if (self == .default) return;
18281828 try bw.print("{s}thread_local", .{prefix});
18291829 if (self != .generaldynamic) try bw.print("({s})", .{@tagName(self)});
......@@ -1837,7 +1837,7 @@ pub const UnnamedAddr = enum(u2) {
18371837 unnamed_addr = 1,
18381838 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 {
18411841 if (self != .default) try bw.print(" {s}", .{@tagName(self)});
18421842 }
18431843};
......@@ -1931,7 +1931,7 @@ pub const AddrSpace = enum(u24) {
19311931 pub const funcref: AddrSpace = @enumFromInt(20);
19321932 };
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 {
19351935 if (self != .default) try bw.print("{s}addrspace({d})", .{ prefix, @intFromEnum(self) });
19361936 }
19371937};
......@@ -1940,7 +1940,7 @@ pub const ExternallyInitialized = enum {
19401940 default,
19411941 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 {
19441944 if (self != .default) try bw.print(" {s}", .{@tagName(self)});
19451945 }
19461946};
......@@ -1964,7 +1964,7 @@ pub const Alignment = enum(u6) {
19641964 return if (self == .default) 0 else (@intFromEnum(self) + 1);
19651965 }
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 {
19681968 try bw.print("{s}align {d}", .{ prefix, self.toByteUnits() orelse return });
19691969 }
19701970};
......@@ -2038,7 +2038,7 @@ pub const CallConv = enum(u10) {
20382038
20392039 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 {
20422042 switch (self) {
20432043 default => {},
20442044 .fastcc,
......@@ -2119,7 +2119,7 @@ pub const StrtabString = enum(u32) {
21192119 string: StrtabString,
21202120 builder: *const Builder,
21212121 };
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 {
21232123 if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_|
21242124 @compileError("invalid format string: '" ++ fmt_str ++ "'");
21252125 assert(data.string != .none);
......@@ -2306,7 +2306,7 @@ pub const Global = struct {
23062306 global: Index,
23072307 builder: *const Builder,
23082308 };
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 {
23102310 try bw.print("@{f}", .{
23112311 data.global.unwrap(data.builder).name(data.builder).fmt(data.builder),
23122312 });
......@@ -4752,7 +4752,7 @@ pub const Function = struct {
47524752 function: Function.Index,
47534753 builder: *Builder,
47544754 };
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 {
47564756 if (comptime std.mem.indexOfNone(u8, fmt_str, ", %")) |_|
47574757 @compileError("invalid format string: '" ++ fmt_str ++ "'");
47584758 if (comptime std.mem.indexOfScalar(u8, fmt_str, ',') != null) {
......@@ -6944,7 +6944,7 @@ pub const MemoryAccessKind = enum(u1) {
69446944 normal,
69456945 @"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 {
69486948 if (self != .normal) try bw.print("{s}{s}", .{ prefix, @tagName(self) });
69496949 }
69506950};
......@@ -6953,7 +6953,7 @@ pub const SyncScope = enum(u1) {
69536953 singlethread,
69546954 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 {
69576957 if (self != .system) try bw.print(
69586958 \\{s}syncscope("{s}")
69596959 , .{ prefix, @tagName(self) });
......@@ -6969,7 +6969,7 @@ pub const AtomicOrdering = enum(u3) {
69696969 acq_rel = 5,
69706970 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 {
69736973 if (self != .none) try bw.print("{s}{s}", .{ prefix, @tagName(self) });
69746974 }
69756975};
......@@ -7385,7 +7385,7 @@ pub const Constant = enum(u32) {
73857385 constant: Constant,
73867386 builder: *Builder,
73877387 };
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 {
73897389 if (comptime std.mem.indexOfNone(u8, fmt_str, ", %")) |_|
73907390 @compileError("invalid format string: '" ++ fmt_str ++ "'");
73917391 if (comptime std.mem.indexOfScalar(u8, fmt_str, ',') != null) {
......@@ -7712,7 +7712,7 @@ pub const Value = enum(u32) {
77127712 function: Function.Index,
77137713 builder: *Builder,
77147714 };
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 {
77167716 switch (data.value.unwrap()) {
77177717 .instruction => |instruction| try Function.Instruction.Index.format(.{
77187718 .instruction = instruction,
......@@ -7757,7 +7757,7 @@ pub const MetadataString = enum(u32) {
77577757 metadata_string: MetadataString,
77587758 builder: *const Builder,
77597759 };
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 {
77617761 try printEscapedString(data.metadata_string.slice(data.builder), .always_quote, bw);
77627762 }
77637763 fn fmt(self: MetadataString, builder: *const Builder) std.fmt.Formatter(format) {
......@@ -7922,7 +7922,7 @@ pub const Metadata = enum(u32) {
79227922 AllCallsDescribed: bool = false,
79237923 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 {
79267926 var need_pipe = false;
79277927 inline for (@typeInfo(DIFlags).@"struct".fields) |field| {
79287928 switch (@typeInfo(field.type)) {
......@@ -7979,7 +7979,7 @@ pub const Metadata = enum(u32) {
79797979 ObjCDirect: bool = false,
79807980 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 {
79837983 var need_pipe = false;
79847984 inline for (@typeInfo(DISPFlags).@"struct".fields) |field| {
79857985 switch (@typeInfo(field.type)) {
......@@ -8196,7 +8196,7 @@ pub const Metadata = enum(u32) {
81968196 };
81978197 };
81988198 };
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 {
82008200 if (data.node == .none) return;
82018201
82028202 const is_specialized = fmt_str.len > 0 and fmt_str[0] == 'S';
......@@ -8371,7 +8371,7 @@ pub const Metadata = enum(u32) {
83718371 },
83728372 nodes: anytype,
83738373 bw: *std.io.BufferedWriter,
8374 ) anyerror!void {
8374 ) !void {
83758375 comptime var fmt_str: []const u8 = "";
83768376 const names = comptime std.meta.fieldNames(@TypeOf(nodes));
83778377 comptime var fields: [2 + names.len]std.builtin.Type.StructField = undefined;
......@@ -9379,14 +9379,14 @@ pub fn printToFile(self: *Builder, path: []const u8) bool {
93799379 return true;
93809380}
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 {
93839383 var buffer: [4096]u8 = undefined;
93849384 var bw = writer.buffered(&buffer);
93859385 try self.print(&bw);
93869386 try bw.flush();
93879387}
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 {
93909390 var need_newline = false;
93919391 var metadata_formatter: Metadata.Formatter = .{ .builder = self, .need_comma = undefined };
93929392 defer metadata_formatter.map.deinit(self.gpa);
......@@ -10458,7 +10458,7 @@ fn isValidIdentifier(id: []const u8) bool {
1045810458}
1045910459
1046010460const 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 {
1046210462 const need_quotes = switch (quotes) {
1046310463 .always_quote => true,
1046410464 .quote_unless_valid_identifier => !isValidIdentifier(slice),
lib/std/zig/render.zig+53-51
......@@ -10,6 +10,8 @@ const primitives = std.zig.primitives;
1010const indent_delta = 4;
1111const asm_indent_delta = 2;
1212
13pub const Error = Ast.RenderError;
14
1315pub const Fixups = struct {
1416 /// The key is the mut token (`var`/`const`) of the variable declaration
1517 /// that should have a `_ = foo;` inserted afterwards.
......@@ -75,7 +77,7 @@ const Render = struct {
7577 fixups: Fixups,
7678};
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 {
7981 assert(tree.errors.len == 0); // Cannot render an invalid tree.
8082 var auto_indenting_stream: AutoIndentingStream = .init(gpa, bw, indent_delta);
8183 defer auto_indenting_stream.deinit();
......@@ -111,7 +113,7 @@ pub fn renderTree(gpa: Allocator, bw: *std.io.BufferedWriter, tree: Ast, fixups:
111113}
112114
113115/// 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 {
115117 const tree = r.tree;
116118 if (members.len == 0) return;
117119 const container: Container = for (members) |member| {
......@@ -135,7 +137,7 @@ fn renderMember(
135137 container: Container,
136138 decl: Ast.Node.Index,
137139 space: Space,
138) anyerror!void {
140) Error!void {
139141 const tree = r.tree;
140142 const ais = r.ais;
141143 if (r.fixups.omit_nodes.contains(decl)) return;
......@@ -305,7 +307,7 @@ fn renderMember(
305307}
306308
307309/// 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 {
309311 if (expressions.len == 0) return;
310312 try renderExpression(r, expressions[0], space);
311313 for (expressions[1..]) |expression| {
......@@ -314,7 +316,7 @@ fn renderExpressions(r: *Render, expressions: []const Ast.Node.Index, space: Spa
314316 }
315317}
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 {
318320 const tree = r.tree;
319321 const ais = r.ais;
320322 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
886888
887889/// Same as `renderExpression`, but afterwards looks for any
888890/// 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 {
890892 const ais = r.ais;
891893 try renderExpression(r, node, space);
892894 if (r.fixups.append_string_after_node.get(node)) |bytes| {
......@@ -898,7 +900,7 @@ fn renderArrayType(
898900 r: *Render,
899901 array_type: Ast.full.ArrayType,
900902 space: Space,
901) anyerror!void {
903) Error!void {
902904 const tree = r.tree;
903905 const ais = r.ais;
904906 const rbracket = tree.firstToken(array_type.ast.elem_type) - 1;
......@@ -916,7 +918,7 @@ fn renderArrayType(
916918 return renderExpression(r, array_type.ast.elem_type, space);
917919}
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 {
920922 const tree = r.tree;
921923 const main_token = ptr_type.ast.main_token;
922924 switch (ptr_type.size) {
......@@ -1010,7 +1012,7 @@ fn renderSlice(
10101012 slice_node: Ast.Node.Index,
10111013 slice: Ast.full.Slice,
10121014 space: Space,
1013) anyerror!void {
1015) Error!void {
10141016 const tree = r.tree;
10151017 const after_start_space_bool = nodeCausesSliceOpSpace(tree.nodeTag(slice.ast.start)) or
10161018 if (slice.ast.end.unwrap()) |end| nodeCausesSliceOpSpace(tree.nodeTag(end)) else false;
......@@ -1043,7 +1045,7 @@ fn renderAsmOutput(
10431045 r: *Render,
10441046 asm_output: Ast.Node.Index,
10451047 space: Space,
1046) anyerror!void {
1048) Error!void {
10471049 const tree = r.tree;
10481050 assert(tree.nodeTag(asm_output) == .asm_output);
10491051 const symbolic_name = tree.nodeMainToken(asm_output);
......@@ -1069,7 +1071,7 @@ fn renderAsmInput(
10691071 r: *Render,
10701072 asm_input: Ast.Node.Index,
10711073 space: Space,
1072) anyerror!void {
1074) Error!void {
10731075 const tree = r.tree;
10741076 assert(tree.nodeTag(asm_input) == .asm_input);
10751077 const symbolic_name = tree.nodeMainToken(asm_input);
......@@ -1091,7 +1093,7 @@ fn renderVarDecl(
10911093 ignore_comptime_token: bool,
10921094 /// `comma_space` and `space` are used for destructure LHS decls.
10931095 space: Space,
1094) anyerror!void {
1096) Error!void {
10951097 try renderVarDeclWithoutFixups(r, var_decl, ignore_comptime_token, space);
10961098 if (r.fixups.unused_var_decls.contains(var_decl.ast.mut_token + 1)) {
10971099 // Discard the variable like this: `_ = foo;`
......@@ -1109,7 +1111,7 @@ fn renderVarDeclWithoutFixups(
11091111 ignore_comptime_token: bool,
11101112 /// `comma_space` and `space` are used for destructure LHS decls.
11111113 space: Space,
1112) anyerror!void {
1114) Error!void {
11131115 const tree = r.tree;
11141116 const ais = r.ais;
11151117
......@@ -1221,7 +1223,7 @@ fn renderVarDeclWithoutFixups(
12211223 ais.popIndent();
12221224}
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 {
12251227 return renderWhile(r, .{
12261228 .ast = .{
12271229 .while_token = if_node.ast.if_token,
......@@ -1240,7 +1242,7 @@ fn renderIf(r: *Render, if_node: Ast.full.If, space: Space) anyerror!void {
12401242
12411243/// Note that this function is additionally used to render if expressions, with
12421244/// 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 {
12441246 const tree = r.tree;
12451247
12461248 if (while_node.label_token) |label| {
......@@ -1310,7 +1312,7 @@ fn renderThenElse(
13101312 maybe_error_token: ?Ast.TokenIndex,
13111313 opt_else_expr: Ast.Node.OptionalIndex,
13121314 space: Space,
1313) anyerror!void {
1315) Error!void {
13141316 const tree = r.tree;
13151317 const ais = r.ais;
13161318 const then_expr_is_block = nodeIsBlock(tree.nodeTag(then_expr));
......@@ -1365,7 +1367,7 @@ fn renderThenElse(
13651367 }
13661368}
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 {
13691371 const tree = r.tree;
13701372 const ais = r.ais;
13711373 const token_tags = tree.tokens.items(.tag);
......@@ -1440,7 +1442,7 @@ fn renderContainerField(
14401442 container: Container,
14411443 field_param: Ast.full.ContainerField,
14421444 space: Space,
1443) anyerror!void {
1445) Error!void {
14441446 const tree = r.tree;
14451447 const ais = r.ais;
14461448 var field = field_param;
......@@ -1549,7 +1551,7 @@ fn renderBuiltinCall(
15491551 builtin_token: Ast.TokenIndex,
15501552 params: []const Ast.Node.Index,
15511553 space: Space,
1552) anyerror!void {
1554) Error!void {
15531555 const tree = r.tree;
15541556 const ais = r.ais;
15551557
......@@ -1622,7 +1624,7 @@ fn renderBuiltinCall(
16221624 }
16231625}
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 {
16261628 const tree = r.tree;
16271629 const ais = r.ais;
16281630
......@@ -1847,7 +1849,7 @@ fn renderSwitchCase(
18471849 r: *Render,
18481850 switch_case: Ast.full.SwitchCase,
18491851 space: Space,
1850) anyerror!void {
1852) Error!void {
18511853 const ais = r.ais;
18521854 const tree = r.tree;
18531855 const trailing_comma = tree.tokenTag(switch_case.ast.arrow_token - 1) == .comma;
......@@ -1909,7 +1911,7 @@ fn renderBlock(
19091911 block_node: Ast.Node.Index,
19101912 statements: []const Ast.Node.Index,
19111913 space: Space,
1912) anyerror!void {
1914) Error!void {
19131915 const tree = r.tree;
19141916 const ais = r.ais;
19151917 const lbrace = tree.nodeMainToken(block_node);
......@@ -1934,7 +1936,7 @@ fn finishRenderBlock(
19341936 block_node: Ast.Node.Index,
19351937 statements: []const Ast.Node.Index,
19361938 space: Space,
1937) anyerror!void {
1939) Error!void {
19381940 const tree = r.tree;
19391941 const ais = r.ais;
19401942 for (statements, 0..) |stmt, i| {
......@@ -1962,7 +1964,7 @@ fn renderStructInit(
19621964 struct_node: Ast.Node.Index,
19631965 struct_init: Ast.full.StructInit,
19641966 space: Space,
1965) anyerror!void {
1967) Error!void {
19661968 const tree = r.tree;
19671969 const ais = r.ais;
19681970
......@@ -2033,7 +2035,7 @@ fn renderArrayInit(
20332035 r: *Render,
20342036 array_init: Ast.full.ArrayInit,
20352037 space: Space,
2036) anyerror!void {
2038) Error!void {
20372039 const tree = r.tree;
20382040 const ais = r.ais;
20392041 const gpa = r.gpa;
......@@ -2263,7 +2265,7 @@ fn renderContainerDecl(
22632265 container_decl_node: Ast.Node.Index,
22642266 container_decl: Ast.full.ContainerDecl,
22652267 space: Space,
2266) anyerror!void {
2268) Error!void {
22672269 const tree = r.tree;
22682270 const ais = r.ais;
22692271
......@@ -2382,7 +2384,7 @@ fn renderAsm(
23822384 r: *Render,
23832385 asm_node: Ast.full.Asm,
23842386 space: Space,
2385) anyerror!void {
2387) Error!void {
23862388 const tree = r.tree;
23872389 const ais = r.ais;
23882390
......@@ -2548,7 +2550,7 @@ fn renderCall(
25482550 r: *Render,
25492551 call: Ast.full.Call,
25502552 space: Space,
2551) anyerror!void {
2553) Error!void {
25522554 if (call.async_token) |async_token| {
25532555 try renderToken(r, async_token, .space);
25542556 }
......@@ -2561,7 +2563,7 @@ fn renderParamList(
25612563 lparen: Ast.TokenIndex,
25622564 params: []const Ast.Node.Index,
25632565 space: Space,
2564) anyerror!void {
2566) Error!void {
25652567 const tree = r.tree;
25662568 const ais = r.ais;
25672569
......@@ -2614,7 +2616,7 @@ fn renderParamList(
26142616
26152617/// Render an expression, and the comma that follows it, if it is present in the source.
26162618/// 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 {
26182620 const tree = r.tree;
26192621 const maybe_comma = tree.lastToken(node) + 1;
26202622 if (tree.tokenTag(maybe_comma) == .comma and space != .comma) {
......@@ -2627,7 +2629,7 @@ fn renderExpressionComma(r: *Render, node: Ast.Node.Index, space: Space) anyerro
26272629
26282630/// Render a token, and the comma that follows it, if it is present in the source.
26292631/// 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 {
26312633 const tree = r.tree;
26322634 const maybe_comma = token + 1;
26332635 if (tree.tokenTag(maybe_comma) == .comma and space != .comma) {
......@@ -2640,7 +2642,7 @@ fn renderTokenComma(r: *Render, token: Ast.TokenIndex, space: Space) anyerror!vo
26402642
26412643/// Render an identifier, and the comma that follows it, if it is present in the source.
26422644/// 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 {
26442646 const tree = r.tree;
26452647 const maybe_comma = token + 1;
26462648 if (tree.tokenTag(maybe_comma) == .comma and space != .comma) {
......@@ -2672,7 +2674,7 @@ const Space = enum {
26722674 skip,
26732675};
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 {
26762678 const tree = r.tree;
26772679 const ais = r.ais;
26782680 const lexeme = tokenSliceForRender(tree, token_index);
......@@ -2680,7 +2682,7 @@ fn renderToken(r: *Render, token_index: Ast.TokenIndex, space: Space) anyerror!v
26802682 try renderSpace(r, token_index, lexeme.len, space);
26812683}
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 {
26842686 const tree = r.tree;
26852687 const ais = r.ais;
26862688 const lexeme = tokenSliceForRender(tree, token_index);
......@@ -2690,7 +2692,7 @@ fn renderTokenOverrideSpaceMode(r: *Render, token_index: Ast.TokenIndex, space:
26902692 try renderSpace(r, token_index, lexeme.len, space);
26912693}
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 {
26942696 const tree = r.tree;
26952697 const ais = r.ais;
26962698
......@@ -2735,7 +2737,7 @@ fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space
27352737 }
27362738}
27372739
2738fn renderOnlySpace(r: *Render, space: Space) anyerror!void {
2740fn renderOnlySpace(r: *Render, space: Space) Error!void {
27392741 const ais = r.ais;
27402742 switch (space) {
27412743 .none => {},
......@@ -2754,7 +2756,7 @@ const QuoteBehavior = enum {
27542756 eagerly_unquote_except_underscore,
27552757};
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 {
27582760 const tree = r.tree;
27592761 assert(tree.tokenTag(token_index) == .identifier);
27602762 const lexeme = tokenSliceForRender(tree, token_index);
......@@ -2940,7 +2942,7 @@ fn hasMultilineString(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.Tok
29402942
29412943/// Assumes that start is the first byte past the previous token and
29422944/// 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 {
29442946 const tree = r.tree;
29452947 const ais = r.ais;
29462948
......@@ -3003,12 +3005,12 @@ fn renderComments(r: *Render, start: usize, end: usize) anyerror!bool {
30033005 return index != start;
30043006}
30053007
3006fn renderExtraNewline(r: *Render, node: Ast.Node.Index) anyerror!void {
3008fn renderExtraNewline(r: *Render, node: Ast.Node.Index) Error!void {
30073009 return renderExtraNewlineToken(r, r.tree.firstToken(node));
30083010}
30093011
30103012/// 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 {
30123014 const tree = r.tree;
30133015 const ais = r.ais;
30143016 const token_start = tree.tokenStart(token_index);
......@@ -3036,7 +3038,7 @@ fn renderExtraNewlineToken(r: *Render, token_index: Ast.TokenIndex) anyerror!voi
30363038
30373039/// end_token is the token one past the last doc comment token. This function
30383040/// searches backwards from there.
3039fn renderDocComments(r: *Render, end_token: Ast.TokenIndex) anyerror!void {
3041fn renderDocComments(r: *Render, end_token: Ast.TokenIndex) Error!void {
30403042 const tree = r.tree;
30413043 // Search backwards for the first doc comment.
30423044 if (end_token == 0) return;
......@@ -3067,7 +3069,7 @@ fn renderDocComments(r: *Render, end_token: Ast.TokenIndex) anyerror!void {
30673069}
30683070
30693071/// 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 {
30713073 const tree = r.tree;
30723074 var tok = start_token;
30733075 while (tree.tokenTag(tok) == .container_doc_comment) : (tok += 1) {
......@@ -3081,7 +3083,7 @@ fn renderContainerDocComments(r: *Render, start_token: Ast.TokenIndex) anyerror!
30813083 }
30823084}
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 {
30853087 const tree = &r.tree;
30863088 const ais = r.ais;
30873089 var buf: [1]Ast.Node.Index = undefined;
......@@ -3129,7 +3131,7 @@ fn anythingBetween(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenI
31293131 return false;
31303132}
31313133
3132fn writeFixingWhitespace(bw: *std.io.BufferedWriter, slice: []const u8) anyerror!void {
3134fn writeFixingWhitespace(bw: *std.io.BufferedWriter, slice: []const u8) Error!void {
31333135 for (slice) |byte| switch (byte) {
31343136 '\t' => try bw.splatByteAll(' ', indent_delta),
31353137 '\r' => {},
......@@ -3308,7 +3310,7 @@ const AutoIndentingStream = struct {
33083310 self.space_stack.deinit();
33093311 }
33103312
3311 pub fn writeAll(ais: *AutoIndentingStream, bytes: []const u8) anyerror!void {
3313 pub fn writeAll(ais: *AutoIndentingStream, bytes: []const u8) Error!void {
33123314 if (bytes.len == 0) return;
33133315 try ais.applyIndent();
33143316 if (ais.disabled_offset == null) try ais.underlying_writer.writeAll(bytes);
......@@ -3317,19 +3319,19 @@ const AutoIndentingStream = struct {
33173319
33183320 /// Assumes that if the printed data ends with a newline, it is directly
33193321 /// 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 {
33213323 try ais.applyIndent();
33223324 if (ais.disabled_offset == null) try ais.underlying_writer.print(format, args);
33233325 if (format[format.len - 1] == '\n') ais.resetLine();
33243326 }
33253327
3326 pub fn writeByte(ais: *AutoIndentingStream, byte: u8) anyerror!void {
3328 pub fn writeByte(ais: *AutoIndentingStream, byte: u8) Error!void {
33273329 try ais.applyIndent();
33283330 if (ais.disabled_offset == null) try ais.underlying_writer.writeByte(byte);
33293331 assert(byte != '\n');
33303332 }
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 {
33333335 assert(byte != '\n');
33343336 try ais.applyIndent();
33353337 if (ais.disabled_offset == null) try ais.underlying_writer.splatByteAll(byte, n);
......@@ -3350,13 +3352,13 @@ const AutoIndentingStream = struct {
33503352 ais.indent_delta = new_indent_delta;
33513353 }
33523354
3353 pub fn insertNewline(ais: *AutoIndentingStream) anyerror!void {
3355 pub fn insertNewline(ais: *AutoIndentingStream) Error!void {
33543356 if (ais.disabled_offset == null) try ais.underlying_writer.writeByte('\n');
33553357 ais.resetLine();
33563358 }
33573359
33583360 /// 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 {
33603362 if (!ais.current_line_empty)
33613363 try ais.insertNewline();
33623364 }
......@@ -3483,7 +3485,7 @@ const AutoIndentingStream = struct {
34833485 }
34843486
34853487 /// Writes ' ' bytes if the current line is empty
3486 fn applyIndent(ais: *AutoIndentingStream) anyerror!void {
3488 fn applyIndent(ais: *AutoIndentingStream) Error!void {
34873489 const current_indent = ais.currentIndent();
34883490 if (ais.current_line_empty and current_indent > 0) {
34893491 if (ais.disabled_offset == null) {
lib/ubsan_rt.zig+1-1
......@@ -119,7 +119,7 @@ const Value = extern struct {
119119 }
120120 }
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 {
123123 comptime assert(fmt.len == 0);
124124
125125 // Work around x86_64 backend limitation.