| author | |
| committer | |
| log | a4fdda6ae04ffc72234d2c6baf22e13d9a5a99a3 |
| tree | b8389bab611e71d14db626c1491185aa20f4c7bd |
| parent | 20a784f7136143e4afa4d9d1d85fc0fa6d69d777 |
explicit error sets ahoy matey
delete some sus APIs from File that need to be reworked53 files changed, 1374 insertions(+), 1966 deletions(-)
lib/compiler/test_runner.zig+10-5| ... | ... | @@ -2,7 +2,6 @@ |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | |
| 4 | 4 | const std = @import("std"); |
| 5 | const io = std.io; | |
| 6 | 5 | const testing = std.testing; |
| 7 | 6 | const assert = std.debug.assert; |
| 8 | 7 | |
| ... | ... | @@ -65,15 +64,21 @@ pub fn main() void { |
| 65 | 64 | } |
| 66 | 65 | } |
| 67 | 66 | |
| 67 | var stdin_buffer: [std.heap.page_size_min]u8 align(std.heap.page_size_min) = undefined; | |
| 68 | var stdout_buffer: [std.heap.page_size_min]u8 align(std.heap.page_size_min) = undefined; | |
| 69 | ||
| 68 | 70 | fn mainServer() !void { |
| 69 | 71 | @disableInstrumentation(); |
| 72 | var stdin_reader = std.fs.File.stdin().reader(); | |
| 73 | var stdout_writer = std.fs.File.stdout().writer(); | |
| 74 | var stdin_buffered_reader: std.io.BufferedReader = undefined; | |
| 75 | stdin_buffered_reader.init(stdin_reader.interface(), &stdin_buffer); | |
| 76 | var stdout_buffered_writer = stdout_writer.interface().buffered(&stdout_buffer); | |
| 70 | 77 | 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, | |
| 74 | 80 | .zig_version = builtin.zig_version_string, |
| 75 | 81 | }); |
| 76 | defer server.deinit(); | |
| 77 | 82 | |
| 78 | 83 | if (builtin.fuzz) { |
| 79 | 84 | const coverage_id = fuzzer_coverage_id(); |
lib/std/Build.zig+2-3| ... | ... | @@ -2766,9 +2766,8 @@ fn dumpBadDirnameHelp( |
| 2766 | 2766 | comptime msg: []const u8, |
| 2767 | 2767 | args: anytype, |
| 2768 | 2768 | ) 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(); | |
| 2772 | 2771 | |
| 2773 | 2772 | const stderr: fs.File = .stderr(); |
| 2774 | 2773 | try w.print(msg, args); |
lib/std/Build/Cache.zig+1-1| ... | ... | @@ -333,7 +333,7 @@ pub const Manifest = struct { |
| 333 | 333 | pub const Diagnostic = union(enum) { |
| 334 | 334 | none, |
| 335 | 335 | manifest_create: fs.File.OpenError, |
| 336 | manifest_read: anyerror, | |
| 336 | manifest_read: fs.File.ReadError, | |
| 337 | 337 | manifest_lock: fs.File.LockError, |
| 338 | 338 | manifest_seek: fs.File.SeekError, |
| 339 | 339 | file_open: FileOp, |
lib/std/Build/Fuzz.zig+6-6| ... | ... | @@ -124,9 +124,9 @@ fn rebuildTestsWorkerRunFallible(run: *Step.Run, ttyconf: std.io.tty.Config, par |
| 124 | 124 | const show_stderr = compile.step.result_stderr.len > 0; |
| 125 | 125 | |
| 126 | 126 | 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 {}; | |
| 130 | 130 | } |
| 131 | 131 | |
| 132 | 132 | const rebuilt_bin_path = result catch |err| switch (err) { |
| ... | ... | @@ -151,9 +151,9 @@ fn fuzzWorkerRun( |
| 151 | 151 | |
| 152 | 152 | run.rerunInFuzzMode(web_server, unit_test_index, prog_node) catch |err| switch (err) { |
| 153 | 153 | 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 {}; | |
| 157 | 157 | return; |
| 158 | 158 | }, |
| 159 | 159 | else => { |
lib/std/Build/Step/CheckObject.zig+1-1| ... | ... | @@ -233,7 +233,7 @@ const ComputeCompareExpected = struct { |
| 233 | 233 | value: ComputeCompareExpected, |
| 234 | 234 | bw: *std.io.BufferedWriter, |
| 235 | 235 | comptime fmt: []const u8, |
| 236 | ) anyerror!void { | |
| 236 | ) !void { | |
| 237 | 237 | if (fmt.len != 0) std.fmt.invalidFmtError(fmt, value); |
| 238 | 238 | try bw.print("{s} ", .{@tagName(value.op)}); |
| 239 | 239 | switch (value.value) { |
lib/std/Progress.zig+32| ... | ... | @@ -606,6 +606,38 @@ pub fn unlockStdErr() void { |
| 606 | 606 | stderr_mutex.unlock(); |
| 607 | 607 | } |
| 608 | 608 | |
| 609 | /// Protected by `stderr_mutex`. | |
| 610 | var stderr_buffered_writer: std.io.BufferedWriter = .{ | |
| 611 | .unbuffered_writer = stderr_file_writer.interface(), | |
| 612 | .buffer = &.{}, | |
| 613 | }; | |
| 614 | /// Protected by `stderr_mutex`. | |
| 615 | var 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. | |
| 626 | pub 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 | ||
| 635 | pub fn unlockStderrWriter() void { | |
| 636 | stderr_buffered_writer.flush() catch {}; | |
| 637 | stderr_buffered_writer.buffer = &.{}; | |
| 638 | stderr_mutex.unlock(); | |
| 639 | } | |
| 640 | ||
| 609 | 641 | fn ipcThreadRun(fd: posix.fd_t) anyerror!void { |
| 610 | 642 | // Store this data in the thread so that it does not need to be part of the |
| 611 | 643 | // linker data of the main executable. |
lib/std/Target.zig+1-1| ... | ... | @@ -301,7 +301,7 @@ pub const Os = struct { |
| 301 | 301 | |
| 302 | 302 | /// This function is defined to serialize a Zig source code representation of this |
| 303 | 303 | /// type, that, when parsed, will deserialize into the same data. |
| 304 | pub fn format(ver: WindowsVersion, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) anyerror!void { | |
| 304 | pub fn format(ver: WindowsVersion, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) std.io.Writer.Error!void { | |
| 305 | 305 | const maybe_name = std.enums.tagName(WindowsVersion, ver); |
| 306 | 306 | if (comptime std.mem.eql(u8, fmt_str, "s")) { |
| 307 | 307 | if (maybe_name) |name| |
lib/std/Uri.zig+4-4| ... | ... | @@ -40,7 +40,7 @@ pub const Component = union(enum) { |
| 40 | 40 | }; |
| 41 | 41 | } |
| 42 | 42 | |
| 43 | pub fn format(component: Component, bw: *std.io.BufferedWriter, comptime fmt: []const u8) anyerror!void { | |
| 43 | pub fn format(component: Component, bw: *std.io.BufferedWriter, comptime fmt: []const u8) std.io.Writer.Error!void { | |
| 44 | 44 | if (fmt.len == 0) { |
| 45 | 45 | try bw.print("std.Uri.Component{{ .{s} = \"{}\" }}", .{ |
| 46 | 46 | @tagName(component), |
| ... | ... | @@ -95,7 +95,7 @@ pub const Component = union(enum) { |
| 95 | 95 | bw: *std.io.BufferedWriter, |
| 96 | 96 | raw: []const u8, |
| 97 | 97 | comptime isValidChar: fn (u8) bool, |
| 98 | ) anyerror!void { | |
| 98 | ) std.io.Writer.Error!void { | |
| 99 | 99 | var start: usize = 0; |
| 100 | 100 | for (raw, 0..) |char, index| { |
| 101 | 101 | if (isValidChar(char)) continue; |
| ... | ... | @@ -236,7 +236,7 @@ pub const WriteToStreamOptions = struct { |
| 236 | 236 | port: bool = true, |
| 237 | 237 | }; |
| 238 | 238 | |
| 239 | pub fn writeToStream(uri: Uri, options: WriteToStreamOptions, bw: *std.io.BufferedWriter) anyerror!void { | |
| 239 | pub fn writeToStream(uri: Uri, options: WriteToStreamOptions, bw: *std.io.BufferedWriter) std.io.Writer.Error!void { | |
| 240 | 240 | if (options.scheme) { |
| 241 | 241 | try bw.print("{s}:", .{uri.scheme}); |
| 242 | 242 | if (options.authority and uri.host != null) { |
| ... | ... | @@ -273,7 +273,7 @@ pub fn writeToStream(uri: Uri, options: WriteToStreamOptions, bw: *std.io.Buffer |
| 273 | 273 | } |
| 274 | 274 | } |
| 275 | 275 | |
| 276 | pub fn format(uri: Uri, bw: *std.io.BufferedWriter, comptime fmt: []const u8) anyerror!void { | |
| 276 | pub fn format(uri: Uri, bw: *std.io.BufferedWriter, comptime fmt: []const u8) std.io.Writer.Error!void { | |
| 277 | 277 | const scheme = comptime std.mem.indexOfScalar(u8, fmt, ';') != null or fmt.len == 0; |
| 278 | 278 | const authentication = comptime std.mem.indexOfScalar(u8, fmt, '@') != null or fmt.len == 0; |
| 279 | 279 | const authority = comptime std.mem.indexOfScalar(u8, fmt, '+') != null or fmt.len == 0; |
lib/std/array_list.zig+3-1| ... | ... | @@ -908,7 +908,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 908 | 908 | var aw: std.io.AllocatingWriter = undefined; |
| 909 | 909 | const bw = aw.fromArrayList(gpa, self); |
| 910 | 910 | defer self.* = aw.toArrayList(); |
| 911 | return @errorCast(bw.print(fmt, args)); | |
| 911 | return bw.print(fmt, args) catch |err| switch (err) { | |
| 912 | error.WriteFailed => return error.OutOfMemory, | |
| 913 | }; | |
| 912 | 914 | } |
| 913 | 915 | |
| 914 | 916 | pub fn printAssumeCapacity(self: *Self, comptime fmt: []const u8, args: anytype) void { |
lib/std/builtin.zig+1-1| ... | ... | @@ -34,7 +34,7 @@ pub const StackTrace = struct { |
| 34 | 34 | index: usize, |
| 35 | 35 | instruction_addresses: []usize, |
| 36 | 36 | |
| 37 | pub fn format(st: StackTrace, bw: *std.io.BufferedWriter, comptime fmt: []const u8) anyerror!void { | |
| 37 | pub fn format(st: StackTrace, bw: *std.io.BufferedWriter, comptime fmt: []const u8) !void { | |
| 38 | 38 | comptime if (fmt.len != 0) unreachable; |
| 39 | 39 | |
| 40 | 40 | // TODO: re-evaluate whether to use format() methods at all. |
lib/std/compress/flate.zig+4-4| ... | ... | @@ -9,7 +9,7 @@ pub const deflate = @import("flate/deflate.zig"); |
| 9 | 9 | pub const inflate = @import("flate/inflate.zig"); |
| 10 | 10 | |
| 11 | 11 | /// Decompress compressed data from reader and write plain data to the writer. |
| 12 | pub fn decompress(reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter) anyerror!void { | |
| 12 | pub fn decompress(reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter) std.io.Writer.Error!void { | |
| 13 | 13 | try inflate.decompress(.raw, reader, writer); |
| 14 | 14 | } |
| 15 | 15 | |
| ... | ... | @@ -19,7 +19,7 @@ pub const Decompressor = inflate.Decompressor(.raw); |
| 19 | 19 | pub const Options = deflate.Options; |
| 20 | 20 | |
| 21 | 21 | /// Compress plain data from reader and write compressed data to the writer. |
| 22 | pub fn compress(reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter, options: Options) anyerror!void { | |
| 22 | pub fn compress(reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter, options: Options) std.io.Writer.Error!void { | |
| 23 | 23 | try deflate.compress(.raw, reader, writer, options); |
| 24 | 24 | } |
| 25 | 25 | |
| ... | ... | @@ -28,7 +28,7 @@ pub const Compressor = deflate.Compressor(.raw); |
| 28 | 28 | /// Huffman only compression. Without Lempel-Ziv match searching. Faster |
| 29 | 29 | /// compression, less memory requirements but bigger compressed sizes. |
| 30 | 30 | pub const huffman = struct { |
| 31 | pub fn compress(reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter) anyerror!void { | |
| 31 | pub fn compress(reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter) std.io.Writer.Error!void { | |
| 32 | 32 | try deflate.huffman.compress(.raw, reader, writer); |
| 33 | 33 | } |
| 34 | 34 | |
| ... | ... | @@ -37,7 +37,7 @@ pub const huffman = struct { |
| 37 | 37 | |
| 38 | 38 | // No compression store only. Compressed size is slightly bigger than plain. |
| 39 | 39 | pub const store = struct { |
| 40 | pub fn compress(reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter) anyerror!void { | |
| 40 | pub fn compress(reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter) std.io.Writer.Error!void { | |
| 41 | 41 | try deflate.store.compress(.raw, reader, writer); |
| 42 | 42 | } |
| 43 | 43 |
lib/std/compress/flate/BitWriter.zig+3-3| ... | ... | @@ -39,7 +39,7 @@ pub fn setWriter(self: *Self, new_writer: *std.io.BufferedWriter) void { |
| 39 | 39 | self.inner_writer = new_writer; |
| 40 | 40 | } |
| 41 | 41 | |
| 42 | pub fn flush(self: *Self) anyerror!void { | |
| 42 | pub fn flush(self: *Self) std.io.Writer.Error!void { | |
| 43 | 43 | var n = self.nbytes; |
| 44 | 44 | while (self.nbits != 0) { |
| 45 | 45 | self.bytes[n] = @as(u8, @truncate(self.bits)); |
| ... | ... | @@ -56,7 +56,7 @@ pub fn flush(self: *Self) anyerror!void { |
| 56 | 56 | self.nbytes = 0; |
| 57 | 57 | } |
| 58 | 58 | |
| 59 | pub fn writeBits(self: *Self, b: u32, nb: u32) anyerror!void { | |
| 59 | pub fn writeBits(self: *Self, b: u32, nb: u32) std.io.Writer.Error!void { | |
| 60 | 60 | self.bits |= @as(u64, @intCast(b)) << @as(u6, @intCast(self.nbits)); |
| 61 | 61 | self.nbits += nb; |
| 62 | 62 | if (self.nbits < 48) |
| ... | ... | @@ -74,7 +74,7 @@ pub fn writeBits(self: *Self, b: u32, nb: u32) anyerror!void { |
| 74 | 74 | self.nbits -= 48; |
| 75 | 75 | } |
| 76 | 76 | |
| 77 | pub fn writeBytes(self: *Self, bytes: []const u8) anyerror!void { | |
| 77 | pub fn writeBytes(self: *Self, bytes: []const u8) std.io.Writer.Error!void { | |
| 78 | 78 | var n = self.nbytes; |
| 79 | 79 | if (self.nbits & 7 != 0) { |
| 80 | 80 | return error.UnfinishedBits; |
lib/std/compress/flate/BlockWriter.zig+10-10| ... | ... | @@ -42,7 +42,7 @@ pub fn init(writer: *std.io.BufferedWriter) Self { |
| 42 | 42 | /// That is after final block; when last byte could be incomplete or |
| 43 | 43 | /// after stored block; which is aligned to the byte boundary (it has x |
| 44 | 44 | /// padding bits after first 3 bits). |
| 45 | pub fn flush(self: *Self) anyerror!void { | |
| 45 | pub fn flush(self: *Self) std.io.Writer.Error!void { | |
| 46 | 46 | try self.bit_writer.flush(); |
| 47 | 47 | } |
| 48 | 48 | |
| ... | ... | @@ -50,7 +50,7 @@ pub fn setWriter(self: *Self, new_writer: *std.io.BufferedWriter) void { |
| 50 | 50 | self.bit_writer.setWriter(new_writer); |
| 51 | 51 | } |
| 52 | 52 | |
| 53 | fn writeCode(self: *Self, c: hc.HuffCode) anyerror!void { | |
| 53 | fn writeCode(self: *Self, c: hc.HuffCode) std.io.Writer.Error!void { | |
| 54 | 54 | try self.bit_writer.writeBits(c.code, c.len); |
| 55 | 55 | } |
| 56 | 56 | |
| ... | ... | @@ -232,7 +232,7 @@ fn dynamicHeader( |
| 232 | 232 | num_distances: u32, |
| 233 | 233 | num_codegens: u32, |
| 234 | 234 | eof: bool, |
| 235 | ) anyerror!void { | |
| 235 | ) std.io.Writer.Error!void { | |
| 236 | 236 | const first_bits: u32 = if (eof) 5 else 4; |
| 237 | 237 | try self.bit_writer.writeBits(first_bits, 3); |
| 238 | 238 | try self.bit_writer.writeBits(num_literals - 257, 5); |
| ... | ... | @@ -272,7 +272,7 @@ fn dynamicHeader( |
| 272 | 272 | } |
| 273 | 273 | } |
| 274 | 274 | |
| 275 | fn storedHeader(self: *Self, length: usize, eof: bool) anyerror!void { | |
| 275 | fn storedHeader(self: *Self, length: usize, eof: bool) std.io.Writer.Error!void { | |
| 276 | 276 | assert(length <= 65535); |
| 277 | 277 | const flag: u32 = if (eof) 1 else 0; |
| 278 | 278 | try self.bit_writer.writeBits(flag, 3); |
| ... | ... | @@ -282,7 +282,7 @@ fn storedHeader(self: *Self, length: usize, eof: bool) anyerror!void { |
| 282 | 282 | try self.bit_writer.writeBits(~l, 16); |
| 283 | 283 | } |
| 284 | 284 | |
| 285 | fn fixedHeader(self: *Self, eof: bool) anyerror!void { | |
| 285 | fn fixedHeader(self: *Self, eof: bool) std.io.Writer.Error!void { | |
| 286 | 286 | // Indicate that we are a fixed Huffman block |
| 287 | 287 | var value: u32 = 2; |
| 288 | 288 | if (eof) { |
| ... | ... | @@ -296,7 +296,7 @@ fn fixedHeader(self: *Self, eof: bool) anyerror!void { |
| 296 | 296 | // is larger than the original bytes, the data will be written as a |
| 297 | 297 | // stored block. |
| 298 | 298 | // If the input is null, the tokens will always be Huffman encoded. |
| 299 | pub fn write(self: *Self, tokens: []const Token, eof: bool, input: ?[]const u8) anyerror!void { | |
| 299 | pub fn write(self: *Self, tokens: []const Token, eof: bool, input: ?[]const u8) std.io.Writer.Error!void { | |
| 300 | 300 | const lit_and_dist = self.indexTokens(tokens); |
| 301 | 301 | const num_literals = lit_and_dist.num_literals; |
| 302 | 302 | const num_distances = lit_and_dist.num_distances; |
| ... | ... | @@ -374,7 +374,7 @@ pub fn write(self: *Self, tokens: []const Token, eof: bool, input: ?[]const u8) |
| 374 | 374 | try self.writeTokens(tokens, &literal_encoding.codes, &distance_encoding.codes); |
| 375 | 375 | } |
| 376 | 376 | |
| 377 | pub fn storedBlock(self: *Self, input: []const u8, eof: bool) anyerror!void { | |
| 377 | pub fn storedBlock(self: *Self, input: []const u8, eof: bool) std.io.Writer.Error!void { | |
| 378 | 378 | try self.storedHeader(input.len, eof); |
| 379 | 379 | try self.bit_writer.writeBytes(input); |
| 380 | 380 | } |
| ... | ... | @@ -389,7 +389,7 @@ fn dynamicBlock( |
| 389 | 389 | tokens: []const Token, |
| 390 | 390 | eof: bool, |
| 391 | 391 | input: ?[]const u8, |
| 392 | ) anyerror!void { | |
| 392 | ) std.io.Writer.Error!void { | |
| 393 | 393 | const total_tokens = self.indexTokens(tokens); |
| 394 | 394 | const num_literals = total_tokens.num_literals; |
| 395 | 395 | const num_distances = total_tokens.num_distances; |
| ... | ... | @@ -486,7 +486,7 @@ fn writeTokens( |
| 486 | 486 | tokens: []const Token, |
| 487 | 487 | le_codes: []hc.HuffCode, |
| 488 | 488 | oe_codes: []hc.HuffCode, |
| 489 | ) anyerror!void { | |
| 489 | ) std.io.Writer.Error!void { | |
| 490 | 490 | for (tokens) |t| { |
| 491 | 491 | if (t.kind == Token.Kind.literal) { |
| 492 | 492 | try self.writeCode(le_codes[t.literal()]); |
| ... | ... | @@ -513,7 +513,7 @@ fn writeTokens( |
| 513 | 513 | |
| 514 | 514 | // Encodes a block of bytes as either Huffman encoded literals or uncompressed bytes |
| 515 | 515 | // if the results only gains very little from compression. |
| 516 | pub fn huffmanBlock(self: *Self, input: []const u8, eof: bool) anyerror!void { | |
| 516 | pub fn huffmanBlock(self: *Self, input: []const u8, eof: bool) std.io.Writer.Error!void { | |
| 517 | 517 | // Add everything as literals |
| 518 | 518 | histogram(input, &self.literal_freq); |
| 519 | 519 |
lib/std/compress/flate/inflate.zig+48-23| ... | ... | @@ -66,6 +66,8 @@ pub fn Inflate(comptime container: Container, comptime Lookahead: type) type { |
| 66 | 66 | block_type: u2 = 0b11, |
| 67 | 67 | state: ReadState = .protocol_header, |
| 68 | 68 | |
| 69 | read_err: Error!void = {}, | |
| 70 | ||
| 69 | 71 | const ReadState = enum { |
| 70 | 72 | protocol_header, |
| 71 | 73 | block_header, |
| ... | ... | @@ -76,19 +78,21 @@ pub fn Inflate(comptime container: Container, comptime Lookahead: type) type { |
| 76 | 78 | |
| 77 | 79 | const Self = @This(); |
| 78 | 80 | |
| 79 | pub const Error = anyerror || Container.Error || hfd.Error || error{ | |
| 81 | pub const Error = Container.Error || hfd.Error || error{ | |
| 80 | 82 | InvalidCode, |
| 81 | 83 | InvalidMatch, |
| 82 | 84 | InvalidBlockType, |
| 83 | 85 | WrongStoredBlockNlen, |
| 84 | 86 | InvalidDynamicBlockHeader, |
| 87 | EndOfStream, | |
| 88 | ReadFailed, | |
| 85 | 89 | }; |
| 86 | 90 | |
| 87 | 91 | pub fn init(bw: *std.io.BufferedReader) Self { |
| 88 | 92 | return .{ .bits = LookaheadBitReader.init(bw) }; |
| 89 | 93 | } |
| 90 | 94 | |
| 91 | fn blockHeader(self: *Self) anyerror!void { | |
| 95 | fn blockHeader(self: *Self) Error!void { | |
| 92 | 96 | self.bfinal = try self.bits.read(u1); |
| 93 | 97 | self.block_type = try self.bits.read(u2); |
| 94 | 98 | } |
| ... | ... | @@ -326,7 +330,7 @@ pub fn Inflate(comptime container: Container, comptime Lookahead: type) type { |
| 326 | 330 | /// returned bytes means end of stream reached. With limit=0 returns as |
| 327 | 331 | /// much data it can. It newer will be more than 65536 bytes, which is |
| 328 | 332 | /// size of internal buffer. |
| 329 | /// TODO merge this logic into reader_streamRead and reader_streamReadVec | |
| 333 | /// TODO merge this logic into readerRead and readerReadVec | |
| 330 | 334 | pub fn get(self: *Self, limit: usize) Error![]const u8 { |
| 331 | 335 | while (true) { |
| 332 | 336 | const out = self.hist.readAtMost(limit); |
| ... | ... | @@ -339,42 +343,63 @@ pub fn Inflate(comptime container: Container, comptime Lookahead: type) type { |
| 339 | 343 | } |
| 340 | 344 | } |
| 341 | 345 | |
| 342 | fn reader_streamRead( | |
| 343 | ctx: ?*anyopaque, | |
| 346 | fn readerRead( | |
| 347 | context: ?*anyopaque, | |
| 344 | 348 | bw: *std.io.BufferedWriter, |
| 345 | 349 | 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)); | |
| 348 | 352 | const out = try bw.writableSlice(1); |
| 349 | const in = try self.get(limit.min(out.len)); | |
| 353 | const in = self.get(limit.min(out.len)) catch |err| switch (err) { | |
| 354 | error.EndOfStream => return error.EndOfStream, | |
| 355 | error.ReadFailed => return error.ReadFailed, | |
| 356 | else => |e| { | |
| 357 | self.read_err = e; | |
| 358 | return error.ReadFailed; | |
| 359 | }, | |
| 360 | }; | |
| 361 | if (in.len == 0) return error.EndOfStream; | |
| 350 | 362 | @memcpy(out[0..in.len], in); |
| 351 | 363 | bw.advance(in.len); |
| 352 | return .{ .len = in.len, .end = in.len == 0 }; | |
| 364 | return in.len; | |
| 353 | 365 | } |
| 354 | 366 | |
| 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 { | |
| 357 | 386 | for (data) |out| { |
| 358 | 387 | if (out.len == 0) continue; |
| 359 | 388 | const in = try self.get(out.len); |
| 360 | 389 | @memcpy(out[0..in.len], in); |
| 361 | return .{ .len = @intCast(in.len), .end = in.len == 0 }; | |
| 390 | if (in.len == 0) return error.EndOfStream; | |
| 391 | return in.len; | |
| 362 | 392 | } |
| 363 | return .{}; | |
| 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; | |
| 368 | 394 | } |
| 369 | 395 | |
| 370 | 396 | pub fn reader(self: *Self) std.io.Reader { |
| 371 | 397 | return .{ |
| 372 | 398 | .context = self, |
| 373 | 399 | .vtable = &.{ |
| 374 | .posRead = null, | |
| 375 | .posReadVec = null, | |
| 376 | .streamRead = reader_streamRead, | |
| 377 | .streamReadVec = reader_streamReadVec, | |
| 400 | .read = readerRead, | |
| 401 | .readVec = readerReadVec, | |
| 402 | .discard = readerDiscard, | |
| 378 | 403 | }, |
| 379 | 404 | }; |
| 380 | 405 | } |
| ... | ... | @@ -656,7 +681,7 @@ pub fn BitReader(comptime T: type) type { |
| 656 | 681 | (self.nbits >> 3); // 0 for 0-7, 1 for 8-16, ... same as / 8 |
| 657 | 682 | |
| 658 | 683 | var buf: [t_bytes]u8 = [_]u8{0} ** t_bytes; |
| 659 | const bytes_read = self.forward_reader.partialRead(buf[0..empty_bytes]) catch 0; | |
| 684 | const bytes_read = self.forward_reader.readShort(buf[0..empty_bytes]) catch 0; | |
| 660 | 685 | if (bytes_read > 0) { |
| 661 | 686 | const u: T = std.mem.readInt(T, buf[0..t_bytes], .little); |
| 662 | 687 | self.bits |= u << @as(Tshift, @intCast(self.nbits)); |
| ... | ... | @@ -669,7 +694,7 @@ pub fn BitReader(comptime T: type) type { |
| 669 | 694 | } |
| 670 | 695 | |
| 671 | 696 | /// Read exactly buf.len bytes into buf. |
| 672 | pub fn readAll(self: *Self, buf: []u8) anyerror!void { | |
| 697 | pub fn readAll(self: *Self, buf: []u8) std.io.Reader.Error!void { | |
| 673 | 698 | assert(self.alignBits() == 0); // internal bits must be at byte boundary |
| 674 | 699 | |
| 675 | 700 | // First read from internal bits buffer. |
lib/std/compress/lzma.zig+6-6| ... | ... | @@ -11,7 +11,7 @@ pub const RangeDecoder = struct { |
| 11 | 11 | range: u32, |
| 12 | 12 | code: u32, |
| 13 | 13 | |
| 14 | pub fn init(rd: *RangeDecoder, br: *std.io.BufferedReader) anyerror!usize { | |
| 14 | pub fn init(rd: *RangeDecoder, br: *std.io.BufferedReader) std.io.Reader.Error!usize { | |
| 15 | 15 | const reserved = try br.takeByte(); |
| 16 | 16 | if (reserved != 0) return error.CorruptInput; |
| 17 | 17 | rd.* = .{ |
| ... | ... | @@ -222,7 +222,7 @@ pub const Decode = struct { |
| 222 | 222 | dict_size: u32, |
| 223 | 223 | unpacked_size: ?u64, |
| 224 | 224 | |
| 225 | pub fn readHeader(br: *std.io.BufferedReader, options: Options) anyerror!Params { | |
| 225 | pub fn readHeader(br: *std.io.BufferedReader, options: Options) std.io.Reader.Error!Params { | |
| 226 | 226 | var props = try br.readByte(); |
| 227 | 227 | if (props >= 225) { |
| 228 | 228 | return error.CorruptInput; |
| ... | ... | @@ -537,7 +537,7 @@ pub const Decode = struct { |
| 537 | 537 | |
| 538 | 538 | pub const Decompress = struct { |
| 539 | 539 | pub const Error = |
| 540 | anyerror || | |
| 540 | std.io.Reader.Error || | |
| 541 | 541 | Allocator.Error || |
| 542 | 542 | error{ CorruptInput, EndOfStream, Overflow }; |
| 543 | 543 | |
| ... | ... | @@ -668,7 +668,7 @@ const LzCircularBuffer = struct { |
| 668 | 668 | allocator: Allocator, |
| 669 | 669 | lit: u8, |
| 670 | 670 | bw: *std.io.BufferedWriter, |
| 671 | ) anyerror!void { | |
| 671 | ) std.io.Writer.Error!void { | |
| 672 | 672 | try self.set(allocator, self.cursor, lit); |
| 673 | 673 | self.cursor += 1; |
| 674 | 674 | self.len += 1; |
| ... | ... | @@ -687,7 +687,7 @@ const LzCircularBuffer = struct { |
| 687 | 687 | len: usize, |
| 688 | 688 | dist: usize, |
| 689 | 689 | bw: *std.io.BufferedWriter, |
| 690 | ) anyerror!void { | |
| 690 | ) std.io.Writer.Error!void { | |
| 691 | 691 | if (dist > self.dict_size or dist > self.len) { |
| 692 | 692 | return error.CorruptInput; |
| 693 | 693 | } |
| ... | ... | @@ -704,7 +704,7 @@ const LzCircularBuffer = struct { |
| 704 | 704 | } |
| 705 | 705 | } |
| 706 | 706 | |
| 707 | pub fn finish(self: *Self, bw: *std.io.BufferedWriter) anyerror!void { | |
| 707 | pub fn finish(self: *Self, bw: *std.io.BufferedWriter) std.io.Writer.Error!void { | |
| 708 | 708 | if (self.cursor > 0) { |
| 709 | 709 | try bw.writeAll(self.buf.items[0..self.cursor]); |
| 710 | 710 | self.cursor = 0; |
lib/std/compress/lzma2.zig+1-1| ... | ... | @@ -2,7 +2,7 @@ const std = @import("../std.zig"); |
| 2 | 2 | const Allocator = std.mem.Allocator; |
| 3 | 3 | const lzma = std.compress.lzma; |
| 4 | 4 | |
| 5 | pub fn decompress(gpa: Allocator, reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter) anyerror!void { | |
| 5 | pub fn decompress(gpa: Allocator, reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter) std.io.Reader.RwError!void { | |
| 6 | 6 | var decoder = try Decode.init(gpa); |
| 7 | 7 | defer decoder.deinit(gpa); |
| 8 | 8 | return decoder.decompress(gpa, reader, writer); |
lib/std/compress/zstandard.zig+1-1| ... | ... | @@ -39,7 +39,7 @@ pub const Decompressor = struct { |
| 39 | 39 | write_index: usize = 0, |
| 40 | 40 | }; |
| 41 | 41 | |
| 42 | pub const Error = anyerror || error{ | |
| 42 | pub const Error = std.io.Reader.Error || error{ | |
| 43 | 43 | ChecksumFailure, |
| 44 | 44 | DictionaryIdFlagUnsupported, |
| 45 | 45 | MalformedBlock, |
lib/std/crypto/tls/Client.zig+26-32| ... | ... | @@ -69,28 +69,15 @@ application_cipher: tls.ApplicationCipher, |
| 69 | 69 | /// this connection. |
| 70 | 70 | ssl_key_log: ?*SslKeyLog, |
| 71 | 71 | |
| 72 | pub const Diagnostics = union { | |
| 73 | /// Populated on `error.WriteFailure` and `error.ReadFailure`. | |
| 74 | err: anyerror, | |
| 72 | pub const Diagnostics = union(enum) { | |
| 73 | /// Any `ReadFailure` and `WriteFailure` was due to `input` or `output` | |
| 74 | /// returning the error, respectively. | |
| 75 | transitive, | |
| 75 | 76 | /// Populated on `error.TlsAlert`. |
| 76 | 77 | /// |
| 77 | 78 | /// If this isn't a error alert, then it's a closure alert, which makes |
| 78 | 79 | /// no sense in a handshake. |
| 79 | 80 | alert: tls.AlertDescription, |
| 80 | ||
| 81 | fn wrapWrite(d: *Diagnostics, returned: anyerror!void) error{WriteFailure}!void { | |
| 82 | returned catch |err| { | |
| 83 | d.* = .{ .err = err }; | |
| 84 | return error.WriteFailure; | |
| 85 | }; | |
| 86 | } | |
| 87 | ||
| 88 | fn wrapRead(d: *Diagnostics, returned: anyerror!void) error{ReadFailure}!void { | |
| 89 | returned catch |err| { | |
| 90 | d.* = .{ .err = err }; | |
| 91 | return error.ReadFailure; | |
| 92 | }; | |
| 93 | } | |
| 94 | 81 | }; |
| 95 | 82 | |
| 96 | 83 | pub const SslKeyLog = struct { |
| ... | ... | @@ -205,7 +192,7 @@ pub fn init( |
| 205 | 192 | ) InitError!void { |
| 206 | 193 | assert(input.storage.buffer.len >= min_buffer_len); |
| 207 | 194 | assert(output.buffer.len >= min_buffer_len); |
| 208 | const diags = &client.diagnostics; | |
| 195 | client.diagnostics = .transient; | |
| 209 | 196 | const host = switch (options.host) { |
| 210 | 197 | .no_verification => "", |
| 211 | 198 | .explicit => |host| host, |
| ... | ... | @@ -298,7 +285,7 @@ pub fn init( |
| 298 | 285 | |
| 299 | 286 | { |
| 300 | 287 | var iovecs: [2][]const u8 = .{ cleartext_header, host }; |
| 301 | try diags.wrapWrite(output.writevAll(iovecs[0..if (host.len == 0) 1 else 2])); | |
| 288 | try output.writevAll(iovecs[0..if (host.len == 0) 1 else 2]); | |
| 302 | 289 | } |
| 303 | 290 | |
| 304 | 291 | var tls_version: tls.ProtocolVersion = undefined; |
| ... | ... | @@ -350,12 +337,12 @@ pub fn init( |
| 350 | 337 | var handshake_buffer: [tls.max_ciphertext_record_len]u8 = undefined; |
| 351 | 338 | var d: tls.Decoder = .{ .buf = &handshake_buffer }; |
| 352 | 339 | fragment: while (true) { |
| 353 | try diags.wrapRead(d.readAtLeastOurAmt(input, tls.record_header_len)); | |
| 340 | try d.readAtLeastOurAmt(input, tls.record_header_len); | |
| 354 | 341 | const record_header = d.buf[d.idx..][0..tls.record_header_len]; |
| 355 | 342 | const record_ct = d.decode(tls.ContentType); |
| 356 | 343 | d.skip(2); // legacy_version |
| 357 | 344 | const record_len = d.decode(u16); |
| 358 | try diags.wrapRead(d.readAtLeast(input, record_len)); | |
| 345 | try d.readAtLeast(input, record_len); | |
| 359 | 346 | var record_decoder = try d.sub(record_len); |
| 360 | 347 | var ctd, const ct = content: switch (cipher_state) { |
| 361 | 348 | .cleartext => .{ record_decoder, record_ct }, |
| ... | ... | @@ -433,7 +420,7 @@ pub fn init( |
| 433 | 420 | const level = ctd.decode(tls.AlertLevel); |
| 434 | 421 | const desc = ctd.decode(tls.AlertDescription); |
| 435 | 422 | _ = level; |
| 436 | diags.* = .{ .alert = desc }; | |
| 423 | client.diagnostics = .{ .alert = desc }; | |
| 437 | 424 | return error.TlsAlert; |
| 438 | 425 | }, |
| 439 | 426 | .change_cipher_spec => { |
| ... | ... | @@ -775,7 +762,7 @@ pub fn init( |
| 775 | 762 | &client_change_cipher_spec_msg, |
| 776 | 763 | &client_verify_msg, |
| 777 | 764 | }; |
| 778 | try diags.wrapWrite(output.writevAll(&all_msgs_vec)); | |
| 765 | try output.writevAll(&all_msgs_vec); | |
| 779 | 766 | }, |
| 780 | 767 | } |
| 781 | 768 | write_seq += 1; |
| ... | ... | @@ -840,7 +827,7 @@ pub fn init( |
| 840 | 827 | &client_change_cipher_spec_msg, |
| 841 | 828 | &finished_msg, |
| 842 | 829 | }; |
| 843 | try diags.wrapWrite(output.writevAll(&all_msgs_vec)); | |
| 830 | try output.writevAll(&all_msgs_vec); | |
| 844 | 831 | |
| 845 | 832 | const client_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "c ap traffic", &handshake_hash, P.Hash.digest_length); |
| 846 | 833 | const server_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "s ap traffic", &handshake_hash, P.Hash.digest_length); |
| ... | ... | @@ -905,8 +892,9 @@ pub fn init( |
| 905 | 892 | client.reader.init(.{ |
| 906 | 893 | .context = client, |
| 907 | 894 | .vtable = &.{ |
| 908 | .read = reader_read, | |
| 909 | .readv = reader_readv, | |
| 895 | .read = read, | |
| 896 | .readVec = readVec, | |
| 897 | .discard = discard, | |
| 910 | 898 | }, |
| 911 | 899 | }, input.storage.buffer[0..0]); |
| 912 | 900 | return; |
| ... | ... | @@ -933,7 +921,7 @@ pub fn writer(c: *Client) std.io.Writer { |
| 933 | 921 | }; |
| 934 | 922 | } |
| 935 | 923 | |
| 936 | fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 924 | fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { | |
| 937 | 925 | const c: *Client = @alignCast(@ptrCast(context)); |
| 938 | 926 | const sliced_data = if (splat == 0) data[0..data.len -| 1] else data; |
| 939 | 927 | const output = &c.output; |
| ... | ... | @@ -953,7 +941,7 @@ fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyer |
| 953 | 941 | /// Sends a `close_notify` alert, which is necessary for the server to |
| 954 | 942 | /// distinguish between a properly finished TLS session, or a truncation |
| 955 | 943 | /// attack. |
| 956 | pub fn end(c: *Client) anyerror!void { | |
| 944 | pub fn end(c: *Client) std.io.Writer.Error!void { | |
| 957 | 945 | const output = &c.output; |
| 958 | 946 | const ciphertext_buf = try output.writableSlice(min_buffer_len); |
| 959 | 947 | const prepared = prepareCiphertextRecord(c, ciphertext_buf, &tls.close_notify_alert, .alert); |
| ... | ... | @@ -1070,18 +1058,18 @@ pub fn eof(c: Client) bool { |
| 1070 | 1058 | c.partial_ciphertext_idx >= c.partial_ciphertext_end; |
| 1071 | 1059 | } |
| 1072 | 1060 | |
| 1073 | fn reader_read( | |
| 1061 | fn read( | |
| 1074 | 1062 | context: ?*anyopaque, |
| 1075 | 1063 | bw: *std.io.BufferedWriter, |
| 1076 | 1064 | limit: std.io.Reader.Limit, |
| 1077 | ) anyerror!std.io.Reader.Status { | |
| 1065 | ) std.io.Reader.RwError!std.io.Reader.Status { | |
| 1078 | 1066 | const buf = limit.slice(try bw.writableSlice(1)); |
| 1079 | const status = try reader_readv(context, &.{buf}); | |
| 1067 | const status = try readVec(context, &.{buf}); | |
| 1080 | 1068 | bw.advance(status.len); |
| 1081 | 1069 | return status; |
| 1082 | 1070 | } |
| 1083 | 1071 | |
| 1084 | fn reader_readv(context: ?*anyopaque, data: []const []u8) anyerror!std.io.Reader.Status { | |
| 1072 | fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | |
| 1085 | 1073 | const c: *Client = @ptrCast(@alignCast(context)); |
| 1086 | 1074 | if (c.eof()) return .{ .end = true }; |
| 1087 | 1075 | |
| ... | ... | @@ -1429,6 +1417,12 @@ fn reader_readv(context: ?*anyopaque, data: []const []u8) anyerror!std.io.Reader |
| 1429 | 1417 | } |
| 1430 | 1418 | } |
| 1431 | 1419 | |
| 1420 | fn discard(context: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize { | |
| 1421 | _ = context; | |
| 1422 | _ = limit; | |
| 1423 | @panic("TODO"); | |
| 1424 | } | |
| 1425 | ||
| 1432 | 1426 | fn logSecrets(key_log_file: std.fs.File, context: anytype, secrets: anytype) void { |
| 1433 | 1427 | const locked = if (key_log_file.lock(.exclusive)) |_| true else |_| false; |
| 1434 | 1428 | defer if (locked) key_log_file.unlock(); |
lib/std/debug.zig+32-29| ... | ... | @@ -210,16 +210,19 @@ pub fn unlockStdErr() void { |
| 210 | 210 | /// |
| 211 | 211 | /// Returns a `std.io.BufferedWriter` with empty buffer, meaning that it is |
| 212 | 212 | /// in fact unbuffered and does not need to be flushed. |
| 213 | pub fn lockStdErr2(buffer: []u8) std.io.BufferedWriter { | |
| 214 | std.Progress.lockStdErr(); | |
| 215 | return std.fs.File.stderr().writer().buffered(buffer); | |
| 213 | pub fn lockStderrWriter(buffer: []u8) *std.io.BufferedWriter { | |
| 214 | return std.Progress.lockStderrWriter(buffer); | |
| 215 | } | |
| 216 | ||
| 217 | pub fn unlockStderrWriter() void { | |
| 218 | std.Progress.unlockStderrWriter(); | |
| 216 | 219 | } |
| 217 | 220 | |
| 218 | 221 | /// 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. | |
| 220 | 223 | pub fn print(comptime fmt: []const u8, args: anytype) void { |
| 221 | var bw = lockStdErr2(&.{}); | |
| 222 | defer unlockStdErr(); | |
| 224 | const bw = lockStderrWriter(&.{}); | |
| 225 | defer unlockStderrWriter(); | |
| 223 | 226 | nosuspend bw.print(fmt, args) catch return; |
| 224 | 227 | } |
| 225 | 228 | |
| ... | ... | @@ -242,10 +245,10 @@ pub fn getSelfDebugInfo() !*SelfInfo { |
| 242 | 245 | /// Tries to print a hexadecimal view of the bytes, unbuffered, and ignores any error returned. |
| 243 | 246 | /// Obtains the stderr mutex while dumping. |
| 244 | 247 | pub fn dumpHex(bytes: []const u8) void { |
| 245 | var bw = lockStdErr2(&.{}); | |
| 246 | defer unlockStdErr(); | |
| 248 | const bw = lockStderrWriter(&.{}); | |
| 249 | defer unlockStderrWriter(); | |
| 247 | 250 | const ttyconf = std.io.tty.detectConfig(.stderr()); |
| 248 | dumpHexFallible(&bw, ttyconf, bytes) catch {}; | |
| 251 | dumpHexFallible(bw, ttyconf, bytes) catch {}; | |
| 249 | 252 | } |
| 250 | 253 | |
| 251 | 254 | /// Prints a hexadecimal view of the bytes, returning any error that occurs. |
| ... | ... | @@ -320,9 +323,9 @@ test dumpHexFallible { |
| 320 | 323 | |
| 321 | 324 | /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. |
| 322 | 325 | pub 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; | |
| 326 | 329 | } |
| 327 | 330 | |
| 328 | 331 | /// Prints the current stack trace to the provided writer. |
| ... | ... | @@ -516,14 +519,14 @@ pub fn dumpStackTrace(stack_trace: std.builtin.StackTrace) void { |
| 516 | 519 | nosuspend { |
| 517 | 520 | if (builtin.target.cpu.arch.isWasm()) { |
| 518 | 521 | if (native_os == .wasi) { |
| 519 | var stderr = lockStdErr2(&.{}); | |
| 520 | defer unlockStdErr(); | |
| 522 | const stderr = lockStderrWriter(&.{}); | |
| 523 | defer unlockStderrWriter(); | |
| 521 | 524 | stderr.writeAll("Unable to dump stack trace: not implemented for Wasm\n") catch return; |
| 522 | 525 | } |
| 523 | 526 | return; |
| 524 | 527 | } |
| 525 | var stderr = lockStdErr2(&.{}); | |
| 526 | defer unlockStdErr(); | |
| 528 | const stderr = lockStderrWriter(&.{}); | |
| 529 | defer unlockStderrWriter(); | |
| 527 | 530 | if (builtin.strip_debug_info) { |
| 528 | 531 | stderr.writeAll("Unable to dump stack trace: debug info stripped\n") catch return; |
| 529 | 532 | return; |
| ... | ... | @@ -532,7 +535,7 @@ pub fn dumpStackTrace(stack_trace: std.builtin.StackTrace) void { |
| 532 | 535 | stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return; |
| 533 | 536 | return; |
| 534 | 537 | }; |
| 535 | writeStackTrace(stack_trace, &stderr, debug_info, io.tty.detectConfig(.stderr())) catch |err| { | |
| 538 | writeStackTrace(stack_trace, stderr, debug_info, io.tty.detectConfig(.stderr())) catch |err| { | |
| 536 | 539 | stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return; |
| 537 | 540 | return; |
| 538 | 541 | }; |
| ... | ... | @@ -683,8 +686,8 @@ pub fn defaultPanic( |
| 683 | 686 | _ = panicking.fetchAdd(1, .seq_cst); |
| 684 | 687 | |
| 685 | 688 | { |
| 686 | var stderr = lockStdErr2(&.{}); | |
| 687 | defer unlockStdErr(); | |
| 689 | const stderr = lockStderrWriter(&.{}); | |
| 690 | defer unlockStderrWriter(); | |
| 688 | 691 | |
| 689 | 692 | if (builtin.single_threaded) { |
| 690 | 693 | stderr.print("panic: ", .{}) catch posix.abort(); |
| ... | ... | @@ -695,7 +698,7 @@ pub fn defaultPanic( |
| 695 | 698 | stderr.print("{s}\n", .{msg}) catch posix.abort(); |
| 696 | 699 | |
| 697 | 700 | if (@errorReturnTrace()) |t| dumpStackTrace(t.*); |
| 698 | dumpCurrentStackTraceToWriter(first_trace_addr orelse @returnAddress(), &stderr) catch {}; | |
| 701 | dumpCurrentStackTraceToWriter(first_trace_addr orelse @returnAddress(), stderr) catch {}; | |
| 699 | 702 | } |
| 700 | 703 | |
| 701 | 704 | waitForOtherThreadToFinishPanicking(); |
| ... | ... | @@ -1468,8 +1471,8 @@ fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopa |
| 1468 | 1471 | } |
| 1469 | 1472 | |
| 1470 | 1473 | fn 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(); | |
| 1473 | 1476 | _ = switch (sig) { |
| 1474 | 1477 | posix.SIG.SEGV => if (native_arch == .x86_64 and native_os == .linux and code == 128) // SI_KERNEL |
| 1475 | 1478 | // x86_64 doesn't have a full 64-bit virtual address space. |
| ... | ... | @@ -1517,7 +1520,7 @@ fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*anyopaque) |
| 1517 | 1520 | }, @ptrCast(ctx)).__mcontext_data; |
| 1518 | 1521 | } |
| 1519 | 1522 | relocateContext(&new_ctx); |
| 1520 | dumpStackTraceFromBase(&new_ctx, &stderr); | |
| 1523 | dumpStackTraceFromBase(&new_ctx, stderr); | |
| 1521 | 1524 | }, |
| 1522 | 1525 | else => {}, |
| 1523 | 1526 | } |
| ... | ... | @@ -1547,10 +1550,10 @@ fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, msg: u8, label: |
| 1547 | 1550 | _ = panicking.fetchAdd(1, .seq_cst); |
| 1548 | 1551 | |
| 1549 | 1552 | { |
| 1550 | var stderr = lockStdErr2(&.{}); | |
| 1551 | defer unlockStdErr(); | |
| 1553 | const stderr = lockStderrWriter(&.{}); | |
| 1554 | defer unlockStderrWriter(); | |
| 1552 | 1555 | |
| 1553 | dumpSegfaultInfoWindows(info, msg, label, &stderr); | |
| 1556 | dumpSegfaultInfoWindows(info, msg, label, stderr); | |
| 1554 | 1557 | } |
| 1555 | 1558 | |
| 1556 | 1559 | waitForOtherThreadToFinishPanicking(); |
| ... | ... | @@ -1665,8 +1668,8 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize |
| 1665 | 1668 | if (!enabled) return; |
| 1666 | 1669 | |
| 1667 | 1670 | const tty_config = io.tty.detectConfig(.stderr()); |
| 1668 | var stderr = lockStdErr2(&.{}); | |
| 1669 | defer unlockStdErr(); | |
| 1671 | const stderr = lockStderrWriter(&.{}); | |
| 1672 | defer unlockStderrWriter(); | |
| 1670 | 1673 | const end = @min(t.index, size); |
| 1671 | 1674 | const debug_info = getSelfDebugInfo() catch |err| { |
| 1672 | 1675 | stderr.print( |
| ... | ... | @@ -1683,7 +1686,7 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize |
| 1683 | 1686 | .index = frames.len, |
| 1684 | 1687 | .instruction_addresses = frames, |
| 1685 | 1688 | }; |
| 1686 | writeStackTrace(stack_trace, &stderr, debug_info, tty_config) catch continue; | |
| 1689 | writeStackTrace(stack_trace, stderr, debug_info, tty_config) catch continue; | |
| 1687 | 1690 | } |
| 1688 | 1691 | if (t.index > end) { |
| 1689 | 1692 | stderr.print("{d} more traces not shown; consider increasing trace size\n", .{ |
lib/std/debug/Dwarf.zig+6-18| ... | ... | @@ -2212,7 +2212,7 @@ pub const ElfModule = struct { |
| 2212 | 2212 | var separate_debug_filename: ?[]const u8 = null; |
| 2213 | 2213 | var separate_debug_crc: ?u32 = null; |
| 2214 | 2214 | |
| 2215 | shdrs: for (shdrs) |*shdr| { | |
| 2215 | for (shdrs) |*shdr| { | |
| 2216 | 2216 | if (shdr.sh_type == elf.SHT_NULL or shdr.sh_type == elf.SHT_NOBITS) continue; |
| 2217 | 2217 | const name = mem.sliceTo(header_strings[shdr.sh_name..], 0); |
| 2218 | 2218 | |
| ... | ... | @@ -2243,24 +2243,12 @@ pub const ElfModule = struct { |
| 2243 | 2243 | |
| 2244 | 2244 | var zlib_stream: std.compress.zlib.Decompressor = .init(&section_reader); |
| 2245 | 2245 | |
| 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; | |
| 2263 | 2250 | } |
| 2251 | errdefer gpa.free(decompressed_section); | |
| 2264 | 2252 | |
| 2265 | 2253 | break :blk .{ |
| 2266 | 2254 | .data = decompressed_section, |
lib/std/debug/Dwarf/expression.zig+4-4| ... | ... | @@ -62,7 +62,7 @@ pub const Error = error{ |
| 62 | 62 | InvalidTypeLength, |
| 63 | 63 | |
| 64 | 64 | TruncatedIntegralType, |
| 65 | } || abi.RegBytesError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero }; | |
| 65 | } || abi.RegBytesError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero, ReadFailed }; | |
| 66 | 66 | |
| 67 | 67 | /// A stack machine that can decode and run DWARF expressions. |
| 68 | 68 | /// Expressions can be decoded for non-native address size and endianness, |
| ... | ... | @@ -259,7 +259,7 @@ pub fn StackMachine(comptime options: Options) type { |
| 259 | 259 | allocator: std.mem.Allocator, |
| 260 | 260 | context: Context, |
| 261 | 261 | initial_value: ?usize, |
| 262 | ) anyerror!?Value { | |
| 262 | ) Error!?Value { | |
| 263 | 263 | if (initial_value) |i| try self.stack.append(allocator, .{ .generic = i }); |
| 264 | 264 | var reader: std.io.BufferedReader = undefined; |
| 265 | 265 | reader.initFixed(expression); |
| ... | ... | @@ -274,13 +274,13 @@ pub fn StackMachine(comptime options: Options) type { |
| 274 | 274 | reader: *std.io.BufferedReader, |
| 275 | 275 | allocator: std.mem.Allocator, |
| 276 | 276 | context: Context, |
| 277 | ) anyerror!bool { | |
| 277 | ) Error!bool { | |
| 278 | 278 | if (@sizeOf(usize) != @sizeOf(Address) or options.endian != native_endian) |
| 279 | 279 | @compileError("Execution of non-native address sizes / endianness is not supported"); |
| 280 | 280 | |
| 281 | 281 | const opcode = reader.takeByte() catch |err| switch (err) { |
| 282 | 282 | error.EndOfStream => return false, |
| 283 | else => |e| return @errorCast(e), | |
| 283 | error.ReadFailed => return error.ReadFailed, | |
| 284 | 284 | }; |
| 285 | 285 | if (options.call_frame_context and !isOpcodeValidInCFA(opcode)) return error.InvalidCFAOpcode; |
| 286 | 286 | const operand = try readOperand(reader, opcode, context); |
lib/std/fifo.zig+17-10| ... | ... | @@ -238,23 +238,30 @@ pub fn LinearFifo( |
| 238 | 238 | return .{ |
| 239 | 239 | .context = self, |
| 240 | 240 | .vtable = &.{ |
| 241 | .read = &reader_read, | |
| 242 | .readv = &reader_readv, | |
| 241 | .read = &readerRead, | |
| 242 | .readVec = &readerReadVec, | |
| 243 | .discard = &readerDiscard, | |
| 243 | 244 | }, |
| 244 | 245 | }; |
| 245 | 246 | } |
| 246 | fn reader_read( | |
| 247 | fn readerRead( | |
| 247 | 248 | ctx: ?*anyopaque, |
| 248 | 249 | bw: *std.io.BufferedWriter, |
| 249 | 250 | limit: std.io.Reader.Limit, |
| 250 | ) anyerror!std.io.Reader.Status { | |
| 251 | ) std.io.Reader.RwError!usize { | |
| 251 | 252 | const fifo: *Self = @alignCast(@ptrCast(ctx)); |
| 252 | 253 | _ = fifo; |
| 253 | 254 | _ = bw; |
| 254 | 255 | _ = limit; |
| 255 | 256 | @panic("TODO"); |
| 256 | 257 | } |
| 257 | fn reader_readv(ctx: ?*anyopaque, data: []const []u8) anyerror!std.io.Reader.Status { | |
| 258 | fn readerReadVec(ctx: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | |
| 259 | const fifo: *Self = @alignCast(@ptrCast(ctx)); | |
| 260 | _ = fifo; | |
| 261 | _ = data; | |
| 262 | @panic("TODO"); | |
| 263 | } | |
| 264 | fn readerDiscard(ctx: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | |
| 258 | 265 | const fifo: *Self = @alignCast(@ptrCast(ctx)); |
| 259 | 266 | _ = fifo; |
| 260 | 267 | _ = data; |
| ... | ... | @@ -351,26 +358,26 @@ pub fn LinearFifo( |
| 351 | 358 | return .{ |
| 352 | 359 | .context = fifo, |
| 353 | 360 | .vtable = &.{ |
| 354 | .writeSplat = writer_writeSplat, | |
| 355 | .writeFile = writer_writeFile, | |
| 361 | .writeSplat = writerWriteSplat, | |
| 362 | .writeFile = writerWriteFile, | |
| 356 | 363 | }, |
| 357 | 364 | }; |
| 358 | 365 | } |
| 359 | fn writer_writeSplat(ctx: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 366 | fn writerWriteSplat(ctx: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { | |
| 360 | 367 | const fifo: *Self = @alignCast(@ptrCast(ctx)); |
| 361 | 368 | _ = fifo; |
| 362 | 369 | _ = data; |
| 363 | 370 | _ = splat; |
| 364 | 371 | @panic("TODO"); |
| 365 | 372 | } |
| 366 | fn writer_writeFile( | |
| 373 | fn writerWriteFile( | |
| 367 | 374 | ctx: ?*anyopaque, |
| 368 | 375 | file: std.fs.File, |
| 369 | 376 | offset: std.io.Writer.Offset, |
| 370 | 377 | limit: std.io.Writer.Limit, |
| 371 | 378 | headers_and_trailers: []const []const u8, |
| 372 | 379 | headers_len: usize, |
| 373 | ) anyerror!usize { | |
| 380 | ) std.io.Writer.Error!usize { | |
| 374 | 381 | const fifo: *Self = @alignCast(@ptrCast(ctx)); |
| 375 | 382 | _ = fifo; |
| 376 | 383 | _ = file; |
lib/std/fmt.zig+28-19| ... | ... | @@ -1,8 +1,8 @@ |
| 1 | 1 | //! String formatting and parsing. |
| 2 | 2 | |
| 3 | const std = @import("std.zig"); | |
| 4 | 3 | const builtin = @import("builtin"); |
| 5 | 4 | |
| 5 | const std = @import("std.zig"); | |
| 6 | 6 | const io = std.io; |
| 7 | 7 | const math = std.math; |
| 8 | 8 | const assert = std.debug.assert; |
| ... | ... | @@ -12,6 +12,7 @@ const meta = std.meta; |
| 12 | 12 | const lossyCast = math.lossyCast; |
| 13 | 13 | const expectFmt = std.testing.expectFmt; |
| 14 | 14 | const testing = std.testing; |
| 15 | const Allocator = std.mem.Allocator; | |
| 15 | 16 | |
| 16 | 17 | pub const float = @import("fmt/float.zig"); |
| 17 | 18 | |
| ... | ... | @@ -91,7 +92,7 @@ pub const Options = struct { |
| 91 | 92 | /// A user type may be a `struct`, `vector`, `union` or `enum` type. |
| 92 | 93 | /// |
| 93 | 94 | /// To print literal curly braces, escape them by writing them twice, e.g. `{{` or `}}`. |
| 94 | pub fn format(bw: *std.io.BufferedWriter, comptime fmt: []const u8, args: anytype) anyerror!void { | |
| 95 | pub fn format(bw: *std.io.BufferedWriter, comptime fmt: []const u8, args: anytype) std.io.Writer.Error!void { | |
| 95 | 96 | const ArgsType = @TypeOf(args); |
| 96 | 97 | const args_type_info = @typeInfo(ArgsType); |
| 97 | 98 | if (args_type_info != .@"struct") { |
| ... | ... | @@ -531,7 +532,7 @@ pub fn Formatter(comptime formatFn: anytype) type { |
| 531 | 532 | const Data = @typeInfo(@TypeOf(formatFn)).@"fn".params[0].type.?; |
| 532 | 533 | return struct { |
| 533 | 534 | data: Data, |
| 534 | pub fn format(self: @This(), writer: *std.io.BufferedWriter, comptime fmt: []const u8) anyerror!void { | |
| 535 | pub fn format(self: @This(), writer: *std.io.BufferedWriter, comptime fmt: []const u8) std.io.Writer.Error!void { | |
| 535 | 536 | try formatFn(self.data, writer, fmt); |
| 536 | 537 | } |
| 537 | 538 | }; |
| ... | ... | @@ -833,8 +834,7 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintErro |
| 833 | 834 | var bw: std.io.BufferedWriter = undefined; |
| 834 | 835 | bw.initFixed(buf); |
| 835 | 836 | bw.print(fmt, args) catch |err| switch (err) { |
| 836 | error.NoSpaceLeft => return error.NoSpaceLeft, | |
| 837 | else => unreachable, | |
| 837 | error.WriteFailed => return error.NoSpaceLeft, | |
| 838 | 838 | }; |
| 839 | 839 | return bw.getWritten(); |
| 840 | 840 | } |
| ... | ... | @@ -846,25 +846,34 @@ pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintErr |
| 846 | 846 | |
| 847 | 847 | /// Count the characters needed for format. |
| 848 | 848 | pub 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); | |
| 851 | 852 | bw.print(fmt, args) catch unreachable; |
| 852 | 853 | return bw.count; |
| 853 | 854 | } |
| 854 | 855 | |
| 855 | pub const AllocPrintError = error{OutOfMemory}; | |
| 856 | ||
| 857 | pub 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 | |
| 856 | pub 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, | |
| 862 | 861 | }; |
| 863 | } | |
| 864 | ||
| 865 | pub 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 | ||
| 865 | pub 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); | |
| 868 | 877 | } |
| 869 | 878 | |
| 870 | 879 | pub inline fn comptimePrint(comptime fmt: []const u8, args: anytype) *const [count(fmt, args):0]u8 { |
lib/std/fs/Dir.zig+5-11| ... | ... | @@ -2619,10 +2619,13 @@ pub fn updateFile( |
| 2619 | 2619 | var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = actual_mode }); |
| 2620 | 2620 | defer atomic_file.deinit(); |
| 2621 | 2621 | |
| 2622 | try atomic_file.file.writeFileAll(src_file, .{ .in_len = src_stat.size }); | |
| 2622 | try atomic_file.file.writeFileAll(src_file, .{ | |
| 2623 | .offset = .zero, | |
| 2624 | .limit = .limited(src_stat.size), | |
| 2625 | }); | |
| 2623 | 2626 | try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime); |
| 2624 | 2627 | try atomic_file.finish(); |
| 2625 | return PrevStatus.stale; | |
| 2628 | return .stale; | |
| 2626 | 2629 | } |
| 2627 | 2630 | |
| 2628 | 2631 | pub const CopyFileError = File.OpenError || File.StatError || |
| ... | ... | @@ -2833,15 +2836,6 @@ pub fn setPermissions(self: Dir, permissions: Permissions) SetPermissionsError!v |
| 2833 | 2836 | try file.setPermissions(permissions); |
| 2834 | 2837 | } |
| 2835 | 2838 | |
| 2836 | const Metadata = File.Metadata; | |
| 2837 | pub const MetadataError = File.MetadataError; | |
| 2838 | ||
| 2839 | /// Returns a `Metadata` struct, representing the permissions on the directory | |
| 2840 | pub fn metadata(self: Dir) MetadataError!Metadata { | |
| 2841 | const file: File = .{ .handle = self.fd }; | |
| 2842 | return try file.metadata(); | |
| 2843 | } | |
| 2844 | ||
| 2845 | 2839 | const Dir = @This(); |
| 2846 | 2840 | const builtin = @import("builtin"); |
| 2847 | 2841 | const std = @import("../std.zig"); |
lib/std/fs/File.zig+398-877| ... | ... | @@ -363,8 +363,10 @@ pub fn getPos(self: File) GetSeekPosError!u64 { |
| 363 | 363 | return posix.lseek_CUR_get(self.handle); |
| 364 | 364 | } |
| 365 | 365 | |
| 366 | pub const GetEndPosError = std.os.windows.GetFileSizeError || StatError; | |
| 367 | ||
| 366 | 368 | /// TODO: integrate with async I/O |
| 367 | pub fn getEndPos(self: File) GetSeekPosError!u64 { | |
| 369 | pub fn getEndPos(self: File) GetEndPosError!u64 { | |
| 368 | 370 | if (builtin.os.tag == .windows) { |
| 369 | 371 | return windows.GetFileSizeEx(self.handle); |
| 370 | 372 | } |
| ... | ... | @@ -489,7 +491,6 @@ pub const Stat = struct { |
| 489 | 491 | pub const StatError = posix.FStatError; |
| 490 | 492 | |
| 491 | 493 | /// Returns `Stat` containing basic information about the `File`. |
| 492 | /// Use `metadata` to retrieve more detailed information (e.g. creation time, permissions). | |
| 493 | 494 | /// TODO: integrate with async I/O |
| 494 | 495 | pub fn stat(self: File) StatError!Stat { |
| 495 | 496 | if (builtin.os.tag == .windows) { |
| ... | ... | @@ -755,361 +756,6 @@ pub fn setPermissions(self: File, permissions: Permissions) SetPermissionsError! |
| 755 | 756 | } |
| 756 | 757 | } |
| 757 | 758 | |
| 758 | /// Cross-platform representation of file metadata. | |
| 759 | /// Platform-specific functionality is available through the `inner` field. | |
| 760 | pub 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 | ||
| 807 | pub 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. | |
| 890 | pub 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 | ||
| 941 | pub 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 | ||
| 977 | pub 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 | ||
| 1029 | pub const MetadataError = posix.FStatError; | |
| 1030 | ||
| 1031 | pub 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 | ||
| 1113 | 759 | pub const UpdateTimesError = posix.FutimensError || windows.SetFileTimeError; |
| 1114 | 760 | |
| 1115 | 761 | /// The underlying file system may have a different granularity than nanoseconds, |
| ... | ... | @@ -1193,18 +839,6 @@ pub fn read(self: File, buffer: []u8) ReadError!usize { |
| 1193 | 839 | return posix.read(self.handle, buffer); |
| 1194 | 840 | } |
| 1195 | 841 | |
| 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. | |
| 1198 | pub fn readAll(self: File, buffer: []u8) ReadError!usize { | |
| 1199 | var index: usize = 0; | |
| 1200 | while (index != buffer.len) { | |
| 1201 | const amt = try self.read(buffer[index..]); | |
| 1202 | if (amt == 0) break; | |
| 1203 | index += amt; | |
| 1204 | } | |
| 1205 | return index; | |
| 1206 | } | |
| 1207 | ||
| 1208 | 842 | /// On Windows, this function currently does alter the file pointer. |
| 1209 | 843 | /// https://github.com/ziglang/zig/issues/12783 |
| 1210 | 844 | pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize { |
| ... | ... | @@ -1215,25 +849,10 @@ pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize { |
| 1215 | 849 | return posix.pread(self.handle, buffer, offset); |
| 1216 | 850 | } |
| 1217 | 851 | |
| 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 | |
| 1222 | pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!usize { | |
| 1223 | var index: usize = 0; | |
| 1224 | while (index != buffer.len) { | |
| 1225 | const amt = try self.pread(buffer[index..], offset + index); | |
| 1226 | if (amt == 0) break; | |
| 1227 | index += amt; | |
| 1228 | } | |
| 1229 | return index; | |
| 1230 | } | |
| 1231 | ||
| 1232 | 852 | /// See https://github.com/ziglang/zig/issues/7699 |
| 1233 | 853 | pub fn readv(self: File, iovecs: []const posix.iovec) ReadError!usize { |
| 1234 | 854 | 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; | |
| 1237 | 856 | const first = iovecs[0]; |
| 1238 | 857 | return windows.ReadFile(self.handle, first.base[0..first.len], null); |
| 1239 | 858 | } |
| ... | ... | @@ -1241,55 +860,12 @@ pub fn readv(self: File, iovecs: []const posix.iovec) ReadError!usize { |
| 1241 | 860 | return posix.readv(self.handle, iovecs); |
| 1242 | 861 | } |
| 1243 | 862 | |
| 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 | |
| 1257 | pub fn readvAll(self: File, iovecs: []posix.iovec) ReadError!usize { | |
| 1258 | if (iovecs.len == 0) return 0; | |
| 1259 | ||
| 1260 | // We use the address of this local variable for all zero-length | |
| 1261 | // vectors so that the OS does not complain that we are giving it | |
| 1262 | // addresses outside the application's address space. | |
| 1263 | var garbage: [1]u8 = undefined; | |
| 1264 | for (iovecs) |*v| { | |
| 1265 | if (v.len == 0) v.base = &garbage; | |
| 1266 | } | |
| 1267 | ||
| 1268 | var i: usize = 0; | |
| 1269 | var off: usize = 0; | |
| 1270 | while (true) { | |
| 1271 | var amt = try self.readv(iovecs[i..]); | |
| 1272 | var eof = amt == 0; | |
| 1273 | off += amt; | |
| 1274 | while (amt >= iovecs[i].len) { | |
| 1275 | amt -= iovecs[i].len; | |
| 1276 | i += 1; | |
| 1277 | if (i >= iovecs.len) return off; | |
| 1278 | eof = false; | |
| 1279 | } | |
| 1280 | if (eof) return off; | |
| 1281 | iovecs[i].base += amt; | |
| 1282 | iovecs[i].len -= amt; | |
| 1283 | } | |
| 1284 | } | |
| 1285 | ||
| 1286 | 863 | /// See https://github.com/ziglang/zig/issues/7699 |
| 1287 | 864 | /// On Windows, this function currently does alter the file pointer. |
| 1288 | 865 | /// https://github.com/ziglang/zig/issues/12783 |
| 1289 | 866 | pub fn preadv(self: File, iovecs: []const posix.iovec, offset: u64) PReadError!usize { |
| 1290 | 867 | 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; | |
| 1293 | 869 | const first = iovecs[0]; |
| 1294 | 870 | return windows.ReadFile(self.handle, first.base[0..first.len], offset); |
| 1295 | 871 | } |
| ... | ... | @@ -1297,35 +873,6 @@ pub fn preadv(self: File, iovecs: []const posix.iovec, offset: u64) PReadError!u |
| 1297 | 873 | return posix.preadv(self.handle, iovecs, offset); |
| 1298 | 874 | } |
| 1299 | 875 | |
| 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 | |
| 1308 | pub 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 | ||
| 1329 | 876 | pub const WriteError = posix.WriteError; |
| 1330 | 877 | pub const PWriteError = posix.PWriteError; |
| 1331 | 878 | |
| ... | ... | @@ -1337,6 +884,7 @@ pub fn write(self: File, bytes: []const u8) WriteError!usize { |
| 1337 | 884 | return posix.write(self.handle, bytes); |
| 1338 | 885 | } |
| 1339 | 886 | |
| 887 | /// One-shot alternative to `writer`. | |
| 1340 | 888 | pub fn writeAll(self: File, bytes: []const u8) WriteError!void { |
| 1341 | 889 | var index: usize = 0; |
| 1342 | 890 | while (index < bytes.len) { |
| ... | ... | @@ -1354,21 +902,11 @@ pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize { |
| 1354 | 902 | return posix.pwrite(self.handle, bytes, offset); |
| 1355 | 903 | } |
| 1356 | 904 | |
| 1357 | /// On Windows, this function currently does alter the file pointer. | |
| 1358 | /// https://github.com/ziglang/zig/issues/12783 | |
| 1359 | pub fn pwriteAll(self: File, bytes: []const u8, offset: u64) PWriteError!void { | |
| 1360 | var index: usize = 0; | |
| 1361 | while (index < bytes.len) { | |
| 1362 | index += try self.pwrite(bytes[index..], offset + index); | |
| 1363 | } | |
| 1364 | } | |
| 1365 | ||
| 1366 | 905 | /// See https://github.com/ziglang/zig/issues/7699 |
| 1367 | /// See equivalent function: `std.net.Stream.writev`. | |
| 1368 | 906 | pub fn writev(self: File, iovecs: []const posix.iovec_const) WriteError!usize { |
| 1369 | 907 | if (is_windows) { |
| 1370 | 908 | // TODO improve this to use WriteFileScatter |
| 1371 | if (iovecs.len == 0) return @as(usize, 0); | |
| 909 | if (iovecs.len == 0) return 0; | |
| 1372 | 910 | const first = iovecs[0]; |
| 1373 | 911 | return windows.WriteFile(self.handle, first.base[0..first.len], null); |
| 1374 | 912 | } |
| ... | ... | @@ -1376,46 +914,12 @@ pub fn writev(self: File, iovecs: []const posix.iovec_const) WriteError!usize { |
| 1376 | 914 | return posix.writev(self.handle, iovecs); |
| 1377 | 915 | } |
| 1378 | 916 | |
| 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`. | |
| 1388 | pub fn writevAll(self: File, iovecs: []posix.iovec_const) WriteError!void { | |
| 1389 | if (iovecs.len == 0) return; | |
| 1390 | ||
| 1391 | // We use the address of this local variable for all zero-length | |
| 1392 | // vectors so that the OS does not complain that we are giving it | |
| 1393 | // addresses outside the application's address space. | |
| 1394 | var garbage: [1]u8 = undefined; | |
| 1395 | for (iovecs) |*v| { | |
| 1396 | if (v.len == 0) v.base = &garbage; | |
| 1397 | } | |
| 1398 | ||
| 1399 | var i: usize = 0; | |
| 1400 | while (true) { | |
| 1401 | var amt = try self.writev(iovecs[i..]); | |
| 1402 | while (amt >= iovecs[i].len) { | |
| 1403 | amt -= iovecs[i].len; | |
| 1404 | i += 1; | |
| 1405 | if (i >= iovecs.len) return; | |
| 1406 | } | |
| 1407 | iovecs[i].base += amt; | |
| 1408 | iovecs[i].len -= amt; | |
| 1409 | } | |
| 1410 | } | |
| 1411 | ||
| 1412 | 917 | /// See https://github.com/ziglang/zig/issues/7699 |
| 1413 | 918 | /// On Windows, this function currently does alter the file pointer. |
| 1414 | 919 | /// https://github.com/ziglang/zig/issues/12783 |
| 1415 | 920 | pub fn pwritev(self: File, iovecs: []posix.iovec_const, offset: u64) PWriteError!usize { |
| 1416 | 921 | 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; | |
| 1419 | 923 | const first = iovecs[0]; |
| 1420 | 924 | return windows.WriteFile(self.handle, first.base[0..first.len], offset); |
| 1421 | 925 | } |
| ... | ... | @@ -1423,410 +927,426 @@ pub fn pwritev(self: File, iovecs: []posix.iovec_const, offset: u64) PWriteError |
| 1423 | 927 | return posix.pwritev(self.handle, iovecs, offset); |
| 1424 | 928 | } |
| 1425 | 929 | |
| 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 | |
| 1431 | pub fn pwritevAll(self: File, iovecs: []posix.iovec_const, offset: u64) PWriteError!void { | |
| 1432 | if (iovecs.len == 0) return; | |
| 930 | pub const WriteFileError = PReadError || WriteError; | |
| 1433 | 931 | |
| 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 | } | |
| 932 | pub 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 | }; | |
| 1447 | 939 | } |
| 1448 | 940 | |
| 1449 | pub const CopyRangeError = posix.CopyFileRangeError; | |
| 941 | pub 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 = {}, | |
| 1450 | 949 | |
| 1451 | pub 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 }; | |
| 1456 | 951 | |
| 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. | |
| 1459 | pub 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 | }; | |
| 1469 | 961 | } |
| 1470 | return total_bytes_copied; | |
| 1471 | } | |
| 1472 | ||
| 1473 | pub 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, | |
| 1482 | 962 | |
| 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 | ||
| 1489 | pub const WriteFileError = ReadError || error{EndOfStream} || WriteError; | |
| 1490 | ||
| 1491 | pub 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 | } | |
| 1500 | 1016 | |
| 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 | } | |
| 1504 | 1041 | |
| 1505 | /// Does not try seeking in either of the File parameters. | |
| 1506 | /// See `writeFileAll` as an alternative to calling this. | |
| 1507 | pub 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 | } | |
| 1513 | 1087 | |
| 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 | |
| 1517 | fn 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 | }, | |
| 1565 | 1108 | } |
| 1566 | amt = @as(usize, @intCast(off - count)); | |
| 1567 | 1109 | } |
| 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 | }, | |
| 1574 | 1182 | } |
| 1575 | trailers[i].base += amt; | |
| 1576 | trailers[i].len -= amt; | |
| 1577 | amt = try posix.writev(self.handle, trailers[i..]); | |
| 1578 | 1183 | } |
| 1579 | } | |
| 1580 | ||
| 1581 | pub 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 | ||
| 1591 | pub 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 | ||
| 1601 | pub 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. | |
| 1613 | const max_buffers_len = 16; | |
| 1614 | ||
| 1615 | fn 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 | ||
| 1625 | fn 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 | }; | |
| 1633 | 1185 | |
| 1634 | pub 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 | } | |
| 1186 | pub const Writer = struct { | |
| 1187 | file: File, | |
| 1188 | err: WriteError!void = {}, | |
| 1189 | mode: Writer.Mode = .positional, | |
| 1190 | pos: u64 = 0, | |
| 1646 | 1191 | |
| 1647 | pub fn streamReadVec(context: ?*anyopaque, data: []const []u8) anyerror!std.io.Reader.Status { | |
| 1648 | const handle = opaqueToHandle(context); | |
| 1192 | pub const Mode = Reader.Mode; | |
| 1649 | 1193 | |
| 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; | |
| 1662 | 1197 | |
| 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 | }; | |
| 1672 | 1206 | } |
| 1673 | const send_vecs = iovecs[0..iovecs_i]; | |
| 1674 | if (send_vecs.len == 0) return .{}; // Prevent false positive end detection on empty `data`. | |
| 1675 | const n = try posix.readv(handle, send_vecs); | |
| 1676 | return .{ .len = @intCast(n), .end = n == 0 }; | |
| 1677 | } | |
| 1678 | 1207 | |
| 1679 | pub 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 | }; | |
| 1711 | 1252 | } |
| 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 | }; | |
| 1715 | 1259 | } |
| 1716 | return std.posix.writev(handle, iovecs[0..len]); | |
| 1717 | } | |
| 1718 | 1260 | |
| 1719 | pub 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. | |
| 1743 | 1303 | 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 | ||
| 1751 | 1322 | else => |e| return e, |
| 1752 | 1323 | }; |
| 1753 | if (in_offset.toInt()) |offset| { | |
| 1754 | assert(n == off - offset); | |
| 1755 | } else if (n == 0 and len_int == 0) { | |
| 1756 | // The caller wouldn't be able to tell that the file transfer is | |
| 1757 | // done and would incorrectly repeat the same call. | |
| 1758 | return writeSplat(context, headers_and_trailers, 1); | |
| 1759 | } | |
| 1760 | return n; | |
| 1761 | 1324 | } |
| 1762 | var iovecs_buffer: [max_buffers_len]std.posix.iovec_const = undefined; | |
| 1763 | const iovecs = iovecs_buffer[0..@min(iovecs_buffer.len, headers_and_trailers.len)]; | |
| 1764 | for (iovecs, headers_and_trailers[0..iovecs.len]) |*v, d| v.* = .{ .base = d.ptr, .len = d.len }; | |
| 1765 | const headers = iovecs[0..@min(headers_len, iovecs.len)]; | |
| 1766 | const trailers = iovecs[headers.len..]; | |
| 1767 | const flags = 0; | |
| 1768 | return posix.sendfile(out_fd, in_fd, in_offset, len_int, headers, trailers, flags) catch |err| switch (err) { | |
| 1769 | error.Unseekable, | |
| 1770 | error.FastOpenAlreadyInProgress, | |
| 1771 | error.MessageTooBig, | |
| 1772 | error.FileDescriptorNotASocket, | |
| 1773 | error.NetworkUnreachable, | |
| 1774 | error.NetworkSubsystemFailed, | |
| 1775 | => return writeFileUnseekable(out_fd, in_fd, in_offset, in_limit, headers_and_trailers, headers_len), | |
| 1776 | ||
| 1777 | else => |e| return e, | |
| 1778 | }; | |
| 1779 | } | |
| 1780 | 1325 | |
| 1781 | fn 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 | ||
| 1798 | fn 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 | ||
| 1806 | fn 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 | }; | |
| 1813 | 1343 | |
| 1814 | fn opaqueToFile(userdata: ?*anyopaque) File { | |
| 1815 | return .{ .handle = opaqueToHandle(userdata) }; | |
| 1344 | pub fn reader(file: File) Reader { | |
| 1345 | return .{ .file = file }; | |
| 1816 | 1346 | } |
| 1817 | 1347 | |
| 1818 | pub const SeekableStream = io.SeekableStream( | |
| 1819 | File, | |
| 1820 | SeekError, | |
| 1821 | GetSeekPosError, | |
| 1822 | seekTo, | |
| 1823 | seekBy, | |
| 1824 | getPos, | |
| 1825 | getEndPos, | |
| 1826 | ); | |
| 1827 | ||
| 1828 | pub fn seekableStream(file: File) SeekableStream { | |
| 1829 | return .{ .context = file }; | |
| 1348 | pub fn writer(file: File) Writer { | |
| 1349 | return .{ .file = file }; | |
| 1830 | 1350 | } |
| 1831 | 1351 | |
| 1832 | 1352 | const range_off: windows.LARGE_INTEGER = 0; |
| ... | ... | @@ -2008,3 +1528,4 @@ const linux = std.os.linux; |
| 2008 | 1528 | const windows = std.os.windows; |
| 2009 | 1529 | const maxInt = std.math.maxInt; |
| 2010 | 1530 | const Alignment = std.mem.Alignment; |
| 1531 | const BufferedWriter = std.io.BufferedWriter; |
lib/std/fs/test.zig-107| ... | ... | @@ -1953,113 +1953,6 @@ test "chown" { |
| 1953 | 1953 | try dir.chown(null, null); |
| 1954 | 1954 | } |
| 1955 | 1955 | |
| 1956 | test "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 | ||
| 1971 | test "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 | ||
| 1997 | test "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 | ||
| 2032 | test "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 | ||
| 2063 | 1956 | test "delete a setAsCwd directory on Windows" { |
| 2064 | 1957 | if (native_os != .windows) return error.SkipZigTest; |
| 2065 | 1958 |
lib/std/http/Client.zig+11-11| ... | ... | @@ -386,7 +386,7 @@ pub const Connection = struct { |
| 386 | 386 | } |
| 387 | 387 | } |
| 388 | 388 | |
| 389 | pub fn flush(c: *Connection) anyerror!void { | |
| 389 | pub fn flush(c: *Connection) std.io.Writer.Error!void { | |
| 390 | 390 | try c.writer.flush(); |
| 391 | 391 | if (c.protocol == .tls) { |
| 392 | 392 | if (disable_tls) unreachable; |
| ... | ... | @@ -398,7 +398,7 @@ pub const Connection = struct { |
| 398 | 398 | /// If the connection is a TLS connection, sends the close_notify alert. |
| 399 | 399 | /// |
| 400 | 400 | /// Flushes all buffers. |
| 401 | pub fn end(c: *Connection) anyerror!void { | |
| 401 | pub fn end(c: *Connection) std.io.Writer.Error!void { | |
| 402 | 402 | try c.writer.flush(); |
| 403 | 403 | if (c.protocol == .tls) { |
| 404 | 404 | if (disable_tls) unreachable; |
| ... | ... | @@ -826,7 +826,7 @@ pub const Request = struct { |
| 826 | 826 | } |
| 827 | 827 | |
| 828 | 828 | /// Send the HTTP request headers to the server. |
| 829 | pub fn send(req: *Request) anyerror!void { | |
| 829 | pub fn send(req: *Request) std.io.Writer.Error!void { | |
| 830 | 830 | assert(req.transfer_encoding == .none or req.method.requestHasBody()); |
| 831 | 831 | |
| 832 | 832 | const connection = req.connection.?; |
| ... | ... | @@ -959,7 +959,7 @@ pub const Request = struct { |
| 959 | 959 | |
| 960 | 960 | /// TODO collapse each error set into its own meta error code, and store |
| 961 | 961 | /// the underlying error code as a field on Request |
| 962 | pub const WaitError = RequestError || anyerror || TransferReadError || | |
| 962 | pub const WaitError = RequestError || std.io.Writer.Error || TransferReadError || | |
| 963 | 963 | proto.HeadersParser.CheckCompleteHeadError || Response.ParseError || |
| 964 | 964 | error{ |
| 965 | 965 | TooManyHttpRedirects, |
| ... | ... | @@ -1156,7 +1156,7 @@ pub const Request = struct { |
| 1156 | 1156 | }; |
| 1157 | 1157 | } |
| 1158 | 1158 | |
| 1159 | fn chunked_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 1159 | fn chunked_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { | |
| 1160 | 1160 | const req: *Request = @ptrCast(@alignCast(context)); |
| 1161 | 1161 | var total: usize = 0; |
| 1162 | 1162 | for (data) |bytes| total += bytes.len; |
| ... | ... | @@ -1187,7 +1187,7 @@ pub const Request = struct { |
| 1187 | 1187 | len: std.io.Writer.FileLen, |
| 1188 | 1188 | headers_and_trailers: []const []const u8, |
| 1189 | 1189 | headers_len: usize, |
| 1190 | ) anyerror!usize { | |
| 1190 | ) std.io.Writer.Error!usize { | |
| 1191 | 1191 | if (len == .entire_file) return error.Unimplemented; |
| 1192 | 1192 | const req: *Request = @ptrCast(@alignCast(context)); |
| 1193 | 1193 | var total: usize = len.int(); |
| ... | ... | @@ -1213,7 +1213,7 @@ pub const Request = struct { |
| 1213 | 1213 | return total; |
| 1214 | 1214 | } |
| 1215 | 1215 | |
| 1216 | fn cl_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 1216 | fn cl_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { | |
| 1217 | 1217 | const req: *Request = @ptrCast(@alignCast(context)); |
| 1218 | 1218 | const n = try req.connection.?.writer.writeSplat(data, splat); |
| 1219 | 1219 | req.transfer_encoding.content_length -= n; |
| ... | ... | @@ -1227,7 +1227,7 @@ pub const Request = struct { |
| 1227 | 1227 | len: std.io.Writer.FileLen, |
| 1228 | 1228 | headers_and_trailers: []const []const u8, |
| 1229 | 1229 | headers_len: usize, |
| 1230 | ) anyerror!usize { | |
| 1230 | ) std.io.Writer.Error!usize { | |
| 1231 | 1231 | const req: *Request = @ptrCast(@alignCast(context)); |
| 1232 | 1232 | const n = try req.connection.?.writer.writeFile(file, offset, len, headers_and_trailers, headers_len); |
| 1233 | 1233 | req.transfer_encoding.content_length -= n; |
| ... | ... | @@ -1236,7 +1236,7 @@ pub const Request = struct { |
| 1236 | 1236 | |
| 1237 | 1237 | /// Finish the body of a request. This notifies the server that you have no more data to send. |
| 1238 | 1238 | /// Must be called after `send`. |
| 1239 | pub fn finish(req: *Request) anyerror!void { | |
| 1239 | pub fn finish(req: *Request) std.io.Writer.Error!void { | |
| 1240 | 1240 | switch (req.transfer_encoding) { |
| 1241 | 1241 | .chunked => try req.connection.?.writer.writeAll("0\r\n\r\n"), |
| 1242 | 1242 | .content_length => |len| assert(len == 0), |
| ... | ... | @@ -1353,7 +1353,7 @@ pub const basic_authorization = struct { |
| 1353 | 1353 | return bw.getWritten(); |
| 1354 | 1354 | } |
| 1355 | 1355 | |
| 1356 | pub fn write(uri: Uri, out: *std.io.BufferedWriter) anyerror!void { | |
| 1356 | pub fn write(uri: Uri, out: *std.io.BufferedWriter) std.io.Writer.Error!void { | |
| 1357 | 1357 | var buf: [max_user_len + ":".len + max_password_len]u8 = undefined; |
| 1358 | 1358 | var bw: std.io.BufferedWriter = undefined; |
| 1359 | 1359 | bw.initFixed(&buf); |
| ... | ... | @@ -1574,7 +1574,7 @@ pub fn connect( |
| 1574 | 1574 | |
| 1575 | 1575 | /// TODO collapse each error set into its own meta error code, and store |
| 1576 | 1576 | /// the underlying error code as a field on Request |
| 1577 | pub const RequestError = ConnectTcpError || ConnectErrorPartial || anyerror || | |
| 1577 | pub const RequestError = ConnectTcpError || ConnectErrorPartial || std.io.Writer.Error || | |
| 1578 | 1578 | std.fmt.ParseIntError || Connection.WriteError || |
| 1579 | 1579 | error{ |
| 1580 | 1580 | UnsupportedUriScheme, |
lib/std/http/Server.zig+34-40| ... | ... | @@ -19,7 +19,7 @@ out: *std.io.BufferedWriter, |
| 19 | 19 | /// same connection, and makes invalid API usage cause assertion failures |
| 20 | 20 | /// rather than HTTP protocol violations. |
| 21 | 21 | state: State, |
| 22 | in_err: anyerror, | |
| 22 | head_parse_err: Request.Head.ParseError, | |
| 23 | 23 | |
| 24 | 24 | pub const State = enum { |
| 25 | 25 | /// The connection is available to be used for the first time, or reused. |
| ... | ... | @@ -53,8 +53,8 @@ pub const ReceiveHeadError = error{ |
| 53 | 53 | /// The HTTP specification suggests to respond with a 431 status code |
| 54 | 54 | /// before closing the connection. |
| 55 | 55 | 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. | |
| 58 | 58 | HttpHeadersInvalid, |
| 59 | 59 | /// Partial HTTP request was received but the connection was closed before |
| 60 | 60 | /// fully receiving the headers. |
| ... | ... | @@ -62,7 +62,7 @@ pub const ReceiveHeadError = error{ |
| 62 | 62 | /// The client sent 0 bytes of headers before closing the stream. |
| 63 | 63 | /// In other words, a keep-alive connection was finally closed. |
| 64 | 64 | HttpConnectionClosing, |
| 65 | /// Error occurred reading from `in`; `in_err` is populated. | |
| 65 | /// Transitive error occurred reading from `in`. | |
| 66 | 66 | ReadFailure, |
| 67 | 67 | }; |
| 68 | 68 | |
| ... | ... | @@ -79,23 +79,22 @@ pub fn receiveHead(s: *Server) ReceiveHeadError!Request { |
| 79 | 79 | |
| 80 | 80 | while (true) { |
| 81 | 81 | 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, | |
| 88 | 88 | }; |
| 89 | 89 | head_end += hp.feed(buf[head_end..]); |
| 90 | 90 | if (hp.state == .finished) return .{ |
| 91 | 91 | .server = s, |
| 92 | 92 | .head_end = head_end, |
| 93 | 93 | .head = Request.Head.parse(buf[0..head_end]) catch |err| { |
| 94 | s.in_err = err; | |
| 94 | s.head_parse_err = err; | |
| 95 | 95 | return error.HttpHeadersInvalid; |
| 96 | 96 | }, |
| 97 | 97 | .reader_state = undefined, |
| 98 | .write_error = undefined, | |
| 99 | 98 | }; |
| 100 | 99 | } |
| 101 | 100 | } |
| ... | ... | @@ -109,8 +108,6 @@ pub const Request = struct { |
| 109 | 108 | remaining_content_length: u64, |
| 110 | 109 | chunk_parser: http.ChunkParser, |
| 111 | 110 | }, |
| 112 | /// Populated when `error.HttpContinueWriteFailed` is received. | |
| 113 | write_error: anyerror, | |
| 114 | 111 | |
| 115 | 112 | pub const Compression = union(enum) { |
| 116 | 113 | deflate: std.compress.zlib.Decompressor, |
| ... | ... | @@ -310,7 +307,6 @@ pub const Request = struct { |
| 310 | 307 | .head_end = request_bytes.len, |
| 311 | 308 | .head = undefined, |
| 312 | 309 | .reader_state = undefined, |
| 313 | .write_error = undefined, | |
| 314 | 310 | }; |
| 315 | 311 | |
| 316 | 312 | var it = request.iterateHeaders(); |
| ... | ... | @@ -375,7 +371,7 @@ pub const Request = struct { |
| 375 | 371 | request: *Request, |
| 376 | 372 | content: []const u8, |
| 377 | 373 | options: RespondOptions, |
| 378 | ) anyerror!void { | |
| 374 | ) std.io.Writer.Error!void { | |
| 379 | 375 | const max_extra_headers = 25; |
| 380 | 376 | assert(options.status != .@"continue"); |
| 381 | 377 | assert(options.extra_headers.len <= max_extra_headers); |
| ... | ... | @@ -581,7 +577,7 @@ pub const Request = struct { |
| 581 | 577 | ctx: ?*anyopaque, |
| 582 | 578 | bw: *std.io.BufferedWriter, |
| 583 | 579 | limit: std.io.Reader.Limit, |
| 584 | ) anyerror!std.io.Reader.Status { | |
| 580 | ) std.io.Reader.Error!std.io.Reader.Status { | |
| 585 | 581 | const request: *Request = @alignCast(@ptrCast(ctx)); |
| 586 | 582 | _ = request; |
| 587 | 583 | _ = bw; |
| ... | ... | @@ -589,7 +585,7 @@ pub const Request = struct { |
| 589 | 585 | @panic("TODO"); |
| 590 | 586 | } |
| 591 | 587 | |
| 592 | fn contentLengthReader_readv(ctx: ?*anyopaque, data: []const []u8) anyerror!std.io.Reader.Status { | |
| 588 | fn contentLengthReader_readv(ctx: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | |
| 593 | 589 | const request: *Request = @alignCast(@ptrCast(ctx)); |
| 594 | 590 | _ = request; |
| 595 | 591 | _ = data; |
| ... | ... | @@ -600,7 +596,7 @@ pub const Request = struct { |
| 600 | 596 | ctx: ?*anyopaque, |
| 601 | 597 | bw: *std.io.BufferedWriter, |
| 602 | 598 | limit: std.io.Reader.Limit, |
| 603 | ) anyerror!std.io.Reader.Status { | |
| 599 | ) std.io.Reader.Error!usize { | |
| 604 | 600 | const request: *Request = @alignCast(@ptrCast(ctx)); |
| 605 | 601 | _ = request; |
| 606 | 602 | _ = bw; |
| ... | ... | @@ -608,7 +604,7 @@ pub const Request = struct { |
| 608 | 604 | @panic("TODO"); |
| 609 | 605 | } |
| 610 | 606 | |
| 611 | fn chunkedReader_readv(ctx: ?*anyopaque, data: []const []u8) anyerror!std.io.Reader.Status { | |
| 607 | fn chunkedReader_readv(ctx: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | |
| 612 | 608 | const request: *Request = @alignCast(@ptrCast(ctx)); |
| 613 | 609 | _ = request; |
| 614 | 610 | _ = data; |
| ... | ... | @@ -732,9 +728,10 @@ pub const Request = struct { |
| 732 | 728 | } |
| 733 | 729 | |
| 734 | 730 | 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, | |
| 738 | 735 | /// The client sent an expect HTTP header value other than |
| 739 | 736 | /// "100-continue". |
| 740 | 737 | HttpExpectationFailed, |
| ... | ... | @@ -755,10 +752,7 @@ pub const Request = struct { |
| 755 | 752 | if (request.head.expect) |expect| { |
| 756 | 753 | if (mem.eql(u8, expect, "100-continue")) { |
| 757 | 754 | 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"); | |
| 762 | 756 | request.head.expect = null; |
| 763 | 757 | } else { |
| 764 | 758 | return error.HttpExpectationFailed; |
| ... | ... | @@ -854,7 +848,7 @@ pub const Response = struct { |
| 854 | 848 | /// Otherwise, transfer-encoding: chunked is being used, and it writes the |
| 855 | 849 | /// end-of-stream message, then flushes the stream to the system. |
| 856 | 850 | /// Respects the value of `elide_body` to omit all data after the headers. |
| 857 | pub fn end(r: *Response) anyerror!void { | |
| 851 | pub fn end(r: *Response) std.io.Writer.Error!void { | |
| 858 | 852 | switch (r.transfer_encoding) { |
| 859 | 853 | .content_length => |len| { |
| 860 | 854 | assert(len == 0); // Trips when end() called before all bytes written. |
| ... | ... | @@ -879,7 +873,7 @@ pub const Response = struct { |
| 879 | 873 | /// flushes the stream to the system. |
| 880 | 874 | /// Respects the value of `elide_body` to omit all data after the headers. |
| 881 | 875 | /// Asserts there are at most 25 trailers. |
| 882 | pub fn endChunked(r: *Response, options: EndChunkedOptions) anyerror!void { | |
| 876 | pub fn endChunked(r: *Response, options: EndChunkedOptions) std.io.Writer.Error!void { | |
| 883 | 877 | assert(r.transfer_encoding == .chunked); |
| 884 | 878 | try flush_chunked(r, options.trailers); |
| 885 | 879 | r.* = undefined; |
| ... | ... | @@ -889,14 +883,14 @@ pub const Response = struct { |
| 889 | 883 | /// would not exceed the content-length value sent in the HTTP header. |
| 890 | 884 | /// May return 0, which does not indicate end of stream. The caller decides |
| 891 | 885 | /// when the end of stream occurs by calling `end`. |
| 892 | pub fn write(r: *Response, bytes: []const u8) anyerror!usize { | |
| 886 | pub fn write(r: *Response, bytes: []const u8) std.io.Writer.Error!usize { | |
| 893 | 887 | switch (r.transfer_encoding) { |
| 894 | 888 | .content_length, .none => return @errorCast(cl_writeSplat(r, &.{bytes}, 1)), |
| 895 | 889 | .chunked => return @errorCast(chunked_writeSplat(r, &.{bytes}, 1)), |
| 896 | 890 | } |
| 897 | 891 | } |
| 898 | 892 | |
| 899 | fn cl_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 893 | fn cl_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { | |
| 900 | 894 | _ = splat; |
| 901 | 895 | return cl_write(context, data[0]); // TODO: try to send all the data |
| 902 | 896 | } |
| ... | ... | @@ -908,7 +902,7 @@ pub const Response = struct { |
| 908 | 902 | limit: std.io.Writer.Limit, |
| 909 | 903 | headers_and_trailers: []const []const u8, |
| 910 | 904 | headers_len: usize, |
| 911 | ) anyerror!usize { | |
| 905 | ) std.io.Writer.Error!usize { | |
| 912 | 906 | _ = context; |
| 913 | 907 | _ = file; |
| 914 | 908 | _ = offset; |
| ... | ... | @@ -918,7 +912,7 @@ pub const Response = struct { |
| 918 | 912 | return error.Unimplemented; |
| 919 | 913 | } |
| 920 | 914 | |
| 921 | fn cl_write(context: ?*anyopaque, bytes: []const u8) anyerror!usize { | |
| 915 | fn cl_write(context: ?*anyopaque, bytes: []const u8) std.io.Writer.Error!usize { | |
| 922 | 916 | const r: *Response = @alignCast(@ptrCast(context)); |
| 923 | 917 | |
| 924 | 918 | var trash: u64 = std.math.maxInt(u64); |
| ... | ... | @@ -963,7 +957,7 @@ pub const Response = struct { |
| 963 | 957 | return bytes.len; |
| 964 | 958 | } |
| 965 | 959 | |
| 966 | fn chunked_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 960 | fn chunked_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { | |
| 967 | 961 | _ = splat; |
| 968 | 962 | return chunked_write(context, data[0]); // TODO: try to send all the data |
| 969 | 963 | } |
| ... | ... | @@ -975,7 +969,7 @@ pub const Response = struct { |
| 975 | 969 | limit: std.io.Writer.Limit, |
| 976 | 970 | headers_and_trailers: []const []const u8, |
| 977 | 971 | headers_len: usize, |
| 978 | ) anyerror!usize { | |
| 972 | ) std.io.Writer.Error!usize { | |
| 979 | 973 | _ = context; |
| 980 | 974 | _ = file; |
| 981 | 975 | _ = offset; |
| ... | ... | @@ -985,7 +979,7 @@ pub const Response = struct { |
| 985 | 979 | return error.Unimplemented; // TODO lower to a call to writeFile on the output |
| 986 | 980 | } |
| 987 | 981 | |
| 988 | fn chunked_write(context: ?*anyopaque, bytes: []const u8) anyerror!usize { | |
| 982 | fn chunked_write(context: ?*anyopaque, bytes: []const u8) std.io.Writer.Error!usize { | |
| 989 | 983 | const r: *Response = @alignCast(@ptrCast(context)); |
| 990 | 984 | assert(r.transfer_encoding == .chunked); |
| 991 | 985 | |
| ... | ... | @@ -1024,7 +1018,7 @@ pub const Response = struct { |
| 1024 | 1018 | |
| 1025 | 1019 | /// If using content-length, asserts that writing these bytes to the client |
| 1026 | 1020 | /// would not exceed the content-length value sent in the HTTP header. |
| 1027 | pub fn writeAll(r: *Response, bytes: []const u8) anyerror!void { | |
| 1021 | pub fn writeAll(r: *Response, bytes: []const u8) std.io.Writer.Error!void { | |
| 1028 | 1022 | var index: usize = 0; |
| 1029 | 1023 | while (index < bytes.len) { |
| 1030 | 1024 | index += try write(r, bytes[index..]); |
| ... | ... | @@ -1034,21 +1028,21 @@ pub const Response = struct { |
| 1034 | 1028 | /// Sends all buffered data to the client. |
| 1035 | 1029 | /// This is redundant after calling `end`. |
| 1036 | 1030 | /// Respects the value of `elide_body` to omit all data after the headers. |
| 1037 | pub fn flush(r: *Response) anyerror!void { | |
| 1031 | pub fn flush(r: *Response) std.io.Writer.Error!void { | |
| 1038 | 1032 | switch (r.transfer_encoding) { |
| 1039 | 1033 | .none, .content_length => return flush_cl(r), |
| 1040 | 1034 | .chunked => return flush_chunked(r, null), |
| 1041 | 1035 | } |
| 1042 | 1036 | } |
| 1043 | 1037 | |
| 1044 | fn flush_cl(r: *Response) anyerror!void { | |
| 1038 | fn flush_cl(r: *Response) std.io.Writer.Error!void { | |
| 1045 | 1039 | var w = r.stream.writer().unbuffered(); |
| 1046 | 1040 | try w.writeAll(r.send_buffer[r.send_buffer_start..r.send_buffer_end]); |
| 1047 | 1041 | r.send_buffer_start = 0; |
| 1048 | 1042 | r.send_buffer_end = 0; |
| 1049 | 1043 | } |
| 1050 | 1044 | |
| 1051 | fn flush_chunked(r: *Response, end_trailers: ?[]const http.Header) anyerror!void { | |
| 1045 | fn flush_chunked(r: *Response, end_trailers: ?[]const http.Header) std.io.Writer.Error!void { | |
| 1052 | 1046 | const max_trailers = 25; |
| 1053 | 1047 | if (end_trailers) |trailers| assert(trailers.len <= max_trailers); |
| 1054 | 1048 | assert(r.transfer_encoding == .chunked); |
lib/std/http/WebSocket.zig+4-2| ... | ... | @@ -194,14 +194,16 @@ fn recvReadInt(ws: *WebSocket, comptime I: type) !I { |
| 194 | 194 | }; |
| 195 | 195 | } |
| 196 | 196 | |
| 197 | pub fn writeMessage(ws: *WebSocket, message: []const u8, opcode: Opcode) anyerror!void { | |
| 197 | pub const WriteError = std.http.Server.Response.WriteError; | |
| 198 | ||
| 199 | pub fn writeMessage(ws: *WebSocket, message: []const u8, opcode: Opcode) WriteError!void { | |
| 198 | 200 | const iovecs: [1]std.posix.iovec_const = .{ |
| 199 | 201 | .{ .base = message.ptr, .len = message.len }, |
| 200 | 202 | }; |
| 201 | 203 | return writeMessagev(ws, &iovecs, opcode); |
| 202 | 204 | } |
| 203 | 205 | |
| 204 | pub fn writeMessagev(ws: *WebSocket, message: []const std.posix.iovec_const, opcode: Opcode) anyerror!void { | |
| 206 | pub fn writeMessagev(ws: *WebSocket, message: []const std.posix.iovec_const, opcode: Opcode) WriteError!void { | |
| 205 | 207 | const total_len = l: { |
| 206 | 208 | var total_len: u64 = 0; |
| 207 | 209 | for (message) |iovec| total_len += iovec.len; |
lib/std/io.zig-3| ... | ... | @@ -17,8 +17,6 @@ const Alignment = std.mem.Alignment; |
| 17 | 17 | pub const Reader = @import("io/Reader.zig"); |
| 18 | 18 | pub const Writer = @import("io/Writer.zig"); |
| 19 | 19 | |
| 20 | pub const PositionalReader = @import("io/PositionalReader.zig"); | |
| 21 | ||
| 22 | 20 | pub const BufferedReader = @import("io/BufferedReader.zig"); |
| 23 | 21 | pub const BufferedWriter = @import("io/BufferedWriter.zig"); |
| 24 | 22 | pub const AllocatingWriter = @import("io/AllocatingWriter.zig"); |
| ... | ... | @@ -453,7 +451,6 @@ test { |
| 453 | 451 | _ = BufferedReader; |
| 454 | 452 | _ = Reader; |
| 455 | 453 | _ = Writer; |
| 456 | _ = PositionalReader; | |
| 457 | 454 | _ = AllocatingWriter; |
| 458 | 455 | _ = @import("io/bit_reader.zig"); |
| 459 | 456 | _ = @import("io/bit_writer.zig"); |
lib/std/io/AllocatingWriter.zig+6-6| ... | ... | @@ -130,7 +130,7 @@ pub fn clearRetainingCapacity(aw: *AllocatingWriter) void { |
| 130 | 130 | aw.shrinkRetainingCapacity(0); |
| 131 | 131 | } |
| 132 | 132 | |
| 133 | fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 133 | fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { | |
| 134 | 134 | const aw: *AllocatingWriter = @alignCast(@ptrCast(context)); |
| 135 | 135 | const start_len = aw.written.len; |
| 136 | 136 | const bw = &aw.buffered_writer; |
| ... | ... | @@ -145,7 +145,7 @@ fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anye |
| 145 | 145 | const pattern = data[data.len - 1]; |
| 146 | 146 | var new_capacity: usize = list.capacity + pattern.len * splat; |
| 147 | 147 | for (rest) |bytes| new_capacity += bytes.len; |
| 148 | try list.ensureTotalCapacity(aw.allocator, new_capacity + 1); | |
| 148 | list.ensureTotalCapacity(aw.allocator, new_capacity + 1) catch return error.WriteFailed; | |
| 149 | 149 | for (rest) |bytes| list.appendSliceAssumeCapacity(bytes); |
| 150 | 150 | appendPatternAssumeCapacity(&list, pattern, splat); |
| 151 | 151 | aw.written = list.items; |
| ... | ... | @@ -168,7 +168,7 @@ fn writeFile( |
| 168 | 168 | limit: std.io.Writer.Limit, |
| 169 | 169 | headers_and_trailers_full: []const []const u8, |
| 170 | 170 | headers_len_full: usize, |
| 171 | ) anyerror!usize { | |
| 171 | ) std.io.Writer.FileError!usize { | |
| 172 | 172 | const aw: *AllocatingWriter = @alignCast(@ptrCast(context)); |
| 173 | 173 | const gpa = aw.allocator; |
| 174 | 174 | var list = aw.toArrayList(); |
| ... | ... | @@ -184,14 +184,14 @@ fn writeFile( |
| 184 | 184 | const limit_int = limit.toInt() orelse { |
| 185 | 185 | var new_capacity: usize = list.capacity + std.atomic.cache_line; |
| 186 | 186 | for (headers_and_trailers) |bytes| new_capacity += bytes.len; |
| 187 | try list.ensureTotalCapacity(gpa, new_capacity); | |
| 187 | list.ensureTotalCapacity(gpa, new_capacity) catch return error.WriteFailed; | |
| 188 | 188 | for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes); |
| 189 | 189 | const dest = list.items.ptr[list.items.len..list.capacity]; |
| 190 | 190 | const n = try file.pread(dest, pos); |
| 191 | 191 | if (n == 0) { |
| 192 | 192 | new_capacity = list.capacity; |
| 193 | 193 | for (trailers) |bytes| new_capacity += bytes.len; |
| 194 | try list.ensureTotalCapacity(gpa, new_capacity); | |
| 194 | list.ensureTotalCapacity(gpa, new_capacity) catch return error.WriteFailed; | |
| 195 | 195 | for (trailers) |bytes| list.appendSliceAssumeCapacity(bytes); |
| 196 | 196 | return list.items.len - start_len; |
| 197 | 197 | } |
| ... | ... | @@ -200,7 +200,7 @@ fn writeFile( |
| 200 | 200 | }; |
| 201 | 201 | var new_capacity: usize = list.capacity + limit_int; |
| 202 | 202 | for (headers_and_trailers) |bytes| new_capacity += bytes.len; |
| 203 | try list.ensureTotalCapacity(gpa, new_capacity); | |
| 203 | list.ensureTotalCapacity(gpa, new_capacity) catch return error.WriteFailed; | |
| 204 | 204 | for (headers_and_trailers[0..headers_len]) |bytes| list.appendSliceAssumeCapacity(bytes); |
| 205 | 205 | const dest = list.items.ptr[list.items.len..][0..limit_int]; |
| 206 | 206 | const n = try file.pread(dest, pos); |
lib/std/io/BufferedReader.zig+164-215| ... | ... | @@ -23,74 +23,23 @@ pub fn init(br: *BufferedReader, r: Reader, buffer: []u8) void { |
| 23 | 23 | br.storage.initFixed(buffer); |
| 24 | 24 | } |
| 25 | 25 | |
| 26 | const eof_writer: std.io.Writer.VTable = .{ | |
| 27 | .writeSplat = eof_writeSplat, | |
| 28 | .writeFile = eof_writeFile, | |
| 29 | }; | |
| 30 | const eof_reader: std.io.Reader.VTable = .{ | |
| 31 | .read = eof_read, | |
| 32 | .readv = eof_readv, | |
| 33 | }; | |
| 34 | ||
| 35 | fn eof_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 36 | _ = context; | |
| 37 | _ = data; | |
| 38 | _ = splat; | |
| 39 | return error.NoSpaceLeft; | |
| 40 | } | |
| 41 | ||
| 42 | fn 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 | ||
| 59 | fn 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 | ||
| 66 | fn eof_readv(ctx: ?*anyopaque, data: []const []u8) anyerror!Reader.Status { | |
| 67 | _ = ctx; | |
| 68 | _ = data; | |
| 69 | return error.EndOfStream; | |
| 70 | } | |
| 71 | ||
| 72 | 26 | /// Constructs `br` such that it will read from `buffer` and then end. |
| 27 | /// TODO either remove the const cast here or make methods of this file return a const slice | |
| 73 | 28 | pub fn initFixed(br: *BufferedReader, buffer: []const u8) void { |
| 74 | 29 | br.* = .{ |
| 75 | 30 | .seek = 0, |
| 76 | 31 | .storage = .{ |
| 77 | 32 | .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, | |
| 86 | 34 | }, |
| 35 | .unbuffered_reader = .ending, | |
| 87 | 36 | }; |
| 88 | 37 | } |
| 89 | 38 | |
| 90 | 39 | pub fn storageBuffer(br: *BufferedReader) []u8 { |
| 91 | 40 | 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); | |
| 94 | 43 | return storage.buffer; |
| 95 | 44 | } |
| 96 | 45 | |
| ... | ... | @@ -106,47 +55,43 @@ pub fn reader(br: *BufferedReader) Reader { |
| 106 | 55 | return .{ |
| 107 | 56 | .context = br, |
| 108 | 57 | .vtable = &.{ |
| 109 | .read = passthru_read, | |
| 110 | .readv = passthru_readv, | |
| 58 | .read = passthruRead, | |
| 59 | .readVec = passthruReadVec, | |
| 111 | 60 | }, |
| 112 | 61 | }; |
| 113 | 62 | } |
| 114 | 63 | |
| 115 | fn passthru_read(ctx: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) anyerror!Reader.RwResult { | |
| 64 | fn passthruRead(ctx: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) Reader.RwError!usize { | |
| 116 | 65 | const br: *BufferedReader = @alignCast(@ptrCast(ctx)); |
| 117 | 66 | const storage = &br.storage; |
| 118 | 67 | const buffer = storage.buffer[0..storage.end]; |
| 119 | 68 | const buffered = buffer[br.seek..]; |
| 120 | 69 | const limited = buffered[0..limit.min(buffered.len)]; |
| 121 | 70 | 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; | |
| 129 | 74 | } |
| 130 | 75 | return br.unbuffered_reader.read(bw, limit); |
| 131 | 76 | } |
| 132 | 77 | |
| 133 | fn passthru_readv(ctx: ?*anyopaque, data: []const []u8) anyerror!Reader.Status { | |
| 78 | fn passthruReadVec(ctx: ?*anyopaque, data: []const []u8) Reader.Error!usize { | |
| 134 | 79 | const br: *BufferedReader = @alignCast(@ptrCast(ctx)); |
| 135 | 80 | _ = br; |
| 136 | 81 | _ = data; |
| 137 | 82 | @panic("TODO"); |
| 138 | 83 | } |
| 139 | 84 | |
| 140 | pub fn seekBy(br: *BufferedReader, seek_by: i64) anyerror!void { | |
| 85 | pub fn seekBy(br: *BufferedReader, seek_by: i64) !void { | |
| 141 | 86 | if (seek_by < 0) try br.seekBackwardBy(@abs(seek_by)) else try br.seekForwardBy(@abs(seek_by)); |
| 142 | 87 | } |
| 143 | 88 | |
| 144 | pub fn seekBackwardBy(br: *BufferedReader, seek_by: u64) anyerror!void { | |
| 89 | pub fn seekBackwardBy(br: *BufferedReader, seek_by: u64) !void { | |
| 145 | 90 | if (seek_by > br.storage.end - br.seek) return error.Unseekable; // TODO |
| 146 | 91 | br.seek += @abs(seek_by); |
| 147 | 92 | } |
| 148 | 93 | |
| 149 | pub fn seekForwardBy(br: *BufferedReader, seek_by: u64) anyerror!void { | |
| 94 | pub fn seekForwardBy(br: *BufferedReader, seek_by: u64) !void { | |
| 150 | 95 | const seek, const need_unbuffered_seek = @subWithOverflow(br.seek, @abs(seek_by)); |
| 151 | 96 | if (need_unbuffered_seek > 0) return error.Unseekable; // TODO |
| 152 | 97 | br.seek = seek; |
| ... | ... | @@ -166,27 +111,11 @@ pub fn seekForwardBy(br: *BufferedReader, seek_by: u64) anyerror!void { |
| 166 | 111 | /// See also: |
| 167 | 112 | /// * `peekGreedy` |
| 168 | 113 | /// * `toss` |
| 169 | pub 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` | |
| 187 | pub fn peek2(br: *BufferedReader, n: usize) anyerror!?[]u8 { | |
| 188 | if (try br.peekGreedy(n)) |buf| return buf[0..n]; | |
| 189 | return null; | |
| 114 | pub 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]; | |
| 190 | 119 | } |
| 191 | 120 | |
| 192 | 121 | /// Returns all the next buffered bytes from `unbuffered_reader`, after filling |
| ... | ... | @@ -203,30 +132,11 @@ pub fn peek2(br: *BufferedReader, n: usize) anyerror!?[]u8 { |
| 203 | 132 | /// See also: |
| 204 | 133 | /// * `peek` |
| 205 | 134 | /// * `toss` |
| 206 | pub 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` | |
| 226 | pub 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; | |
| 135 | pub 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]; | |
| 230 | 140 | } |
| 231 | 141 | |
| 232 | 142 | /// Skips the next `n` bytes from the stream, advancing the seek position. This |
| ... | ... | @@ -242,8 +152,11 @@ pub fn toss(br: *BufferedReader, n: usize) void { |
| 242 | 152 | assert(br.seek <= br.storage.end); |
| 243 | 153 | } |
| 244 | 154 | |
| 245 | /// Equivalent to `peek` + `toss`. | |
| 246 | pub 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. | |
| 159 | pub fn take(br: *BufferedReader, n: usize) Reader.Error![]u8 { | |
| 247 | 160 | const result = try br.peek(n); |
| 248 | 161 | br.toss(n); |
| 249 | 162 | return result; |
| ... | ... | @@ -260,7 +173,7 @@ pub fn take(br: *BufferedReader, n: usize) anyerror![]u8 { |
| 260 | 173 | /// |
| 261 | 174 | /// See also: |
| 262 | 175 | /// * `take` |
| 263 | pub fn takeArray(br: *BufferedReader, comptime n: usize) anyerror!*[n]u8 { | |
| 176 | pub fn takeArray(br: *BufferedReader, comptime n: usize) Reader.Error!*[n]u8 { | |
| 264 | 177 | return (try br.take(n))[0..n]; |
| 265 | 178 | } |
| 266 | 179 | |
| ... | ... | @@ -272,10 +185,10 @@ pub fn takeArray(br: *BufferedReader, comptime n: usize) anyerror!*[n]u8 { |
| 272 | 185 | /// |
| 273 | 186 | /// See also: |
| 274 | 187 | /// * `toss` |
| 275 | /// * `discardUntilEnd` | |
| 276 | /// * `discardUpTo` | |
| 277 | pub fn discard(br: *BufferedReader, n: usize) anyerror!void { | |
| 278 | if ((try br.discardUpTo(n)) != n) return error.EndOfStream; | |
| 188 | /// * `discardRemaining` | |
| 189 | /// * `discardShort` | |
| 190 | pub fn discard(br: *BufferedReader, n: usize) Reader.Error!void { | |
| 191 | if ((try br.discardShort(n)) != n) return error.EndOfStream; | |
| 279 | 192 | } |
| 280 | 193 | |
| 281 | 194 | /// Skips the next `n` bytes from the stream, advancing the seek position. |
| ... | ... | @@ -288,35 +201,35 @@ pub fn discard(br: *BufferedReader, n: usize) anyerror!void { |
| 288 | 201 | /// See also: |
| 289 | 202 | /// * `discard` |
| 290 | 203 | /// * `toss` |
| 291 | /// * `discardUntilEnd` | |
| 292 | pub fn discardUpTo(br: *BufferedReader, n: usize) anyerror!usize { | |
| 204 | /// * `discardRemaining` | |
| 205 | pub fn discardShort(br: *BufferedReader, n: usize) Reader.ShortError!usize { | |
| 293 | 206 | 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; | |
| 308 | 223 | } |
| 309 | return n; | |
| 310 | 224 | } |
| 311 | 225 | |
| 312 | 226 | /// Reads the stream until the end, ignoring all the data. |
| 313 | 227 | /// Returns the number of bytes discarded. |
| 314 | pub fn discardUntilEnd(br: *BufferedReader) anyerror!usize { | |
| 228 | pub fn discardRemaining(br: *BufferedReader) Reader.ShortError!usize { | |
| 315 | 229 | const storage = &br.storage; |
| 316 | var total: usize = storage.end; | |
| 230 | const buffered_len = storage.end; | |
| 317 | 231 | storage.end = 0; |
| 318 | total += try br.unbuffered_reader.discardUntilEnd(); | |
| 319 | return total; | |
| 232 | return buffered_len + try br.unbuffered_reader.discardRemaining(); | |
| 320 | 233 | } |
| 321 | 234 | |
| 322 | 235 | /// Fill `buffer` with the next `buffer.len` bytes from the stream, advancing |
| ... | ... | @@ -329,7 +242,7 @@ pub fn discardUntilEnd(br: *BufferedReader) anyerror!usize { |
| 329 | 242 | /// |
| 330 | 243 | /// See also: |
| 331 | 244 | /// * `peek` |
| 332 | pub fn read(br: *BufferedReader, buffer: []u8) anyerror!void { | |
| 245 | pub fn read(br: *BufferedReader, buffer: []u8) Reader.Error!void { | |
| 333 | 246 | const storage = &br.storage; |
| 334 | 247 | const in_buffer = storage.buffer[0..storage.end]; |
| 335 | 248 | const seek = br.seek; |
| ... | ... | @@ -344,7 +257,12 @@ pub fn read(br: *BufferedReader, buffer: []u8) anyerror!void { |
| 344 | 257 | br.seek = 0; |
| 345 | 258 | var i: usize = in_buffer.len; |
| 346 | 259 | while (true) { |
| 347 | const status = try br.unbuffered_reader.read(storage, .unlimited); | |
| 260 | // TODO if remaining buffer len is greater than storage len, read directly into buffer | |
| 261 | const read_len = br.unbuffered_reader.read(storage, .unlimited) catch |err| switch (err) { | |
| 262 | error.WriteFailed => storage.end, | |
| 263 | else => |e| return e, | |
| 264 | }; | |
| 265 | assert(read_len == storage.end); | |
| 348 | 266 | const next_i = i + storage.end; |
| 349 | 267 | if (next_i >= buffer.len) { |
| 350 | 268 | const remaining = buffer[i..]; |
| ... | ... | @@ -352,46 +270,48 @@ pub fn read(br: *BufferedReader, buffer: []u8) anyerror!void { |
| 352 | 270 | br.seek = remaining.len; |
| 353 | 271 | return; |
| 354 | 272 | } |
| 355 | if (status.end) return error.EndOfStream; | |
| 356 | 273 | @memcpy(buffer[i..next_i], storage.buffer[0..storage.end]); |
| 357 | 274 | storage.end = 0; |
| 358 | 275 | i = next_i; |
| 359 | 276 | } |
| 360 | 277 | } |
| 361 | 278 | |
| 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. | |
| 365 | pub 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. | |
| 281 | pub fn readShort(br: *BufferedReader, buffer: []u8) Reader.ShortError!usize { | |
| 366 | 282 | _ = br; |
| 367 | 283 | _ = buffer; |
| 368 | 284 | @panic("TODO"); |
| 369 | 285 | } |
| 370 | 286 | |
| 287 | pub const DelimiterInclusiveError = error{ | |
| 288 | /// See the `Reader` implementation for detailed diagnostics. | |
| 289 | ReadFailed, | |
| 290 | /// Stream ended before the delimiter was found. | |
| 291 | EndOfStream, | |
| 292 | /// The delimiter was not found within a number of bytes matching the | |
| 293 | /// capacity of the `BufferedReader`. | |
| 294 | StreamTooLong, | |
| 295 | }; | |
| 296 | ||
| 371 | 297 | /// Returns a slice of the next bytes of buffered data from the stream until |
| 372 | 298 | /// `sentinel` is found, advancing the seek position. |
| 373 | 299 | /// |
| 374 | 300 | /// Returned slice has a sentinel. |
| 375 | 301 | /// |
| 376 | /// If the stream ends before the sentinel is found, `error.EndOfStream` is | |
| 377 | /// returned. | |
| 378 | /// | |
| 379 | /// If the sentinel is not found within a number of bytes matching the | |
| 380 | /// capacity of the `BufferedReader`, `error.StreamTooLong` is returned. | |
| 381 | /// | |
| 382 | 302 | /// Invalidates previously returned values from `peek`. |
| 383 | 303 | /// |
| 384 | 304 | /// See also: |
| 385 | 305 | /// * `peekSentinel` |
| 386 | 306 | /// * `takeDelimiterExclusive` |
| 387 | 307 | /// * `takeDelimiterInclusive` |
| 388 | pub fn takeSentinel(br: *BufferedReader, comptime sentinel: u8) anyerror![:sentinel]u8 { | |
| 308 | pub fn takeSentinel(br: *BufferedReader, comptime sentinel: u8) DelimiterInclusiveError![:sentinel]u8 { | |
| 389 | 309 | const result = try br.peekSentinel(sentinel); |
| 390 | 310 | br.toss(result.len + 1); |
| 391 | 311 | return result; |
| 392 | 312 | } |
| 393 | 313 | |
| 394 | pub fn peekSentinel(br: *BufferedReader, comptime sentinel: u8) anyerror![:sentinel]u8 { | |
| 314 | pub fn peekSentinel(br: *BufferedReader, comptime sentinel: u8) DelimiterInclusiveError![:sentinel]u8 { | |
| 395 | 315 | const result = try br.takeDelimiterInclusive(sentinel); |
| 396 | 316 | return result[0 .. result.len - 1 :sentinel]; |
| 397 | 317 | } |
| ... | ... | @@ -401,28 +321,30 @@ pub fn peekSentinel(br: *BufferedReader, comptime sentinel: u8) anyerror![:senti |
| 401 | 321 | /// |
| 402 | 322 | /// Returned slice includes the delimiter as the last byte. |
| 403 | 323 | /// |
| 404 | /// If the stream ends before the delimiter is found, `error.EndOfStream` is | |
| 405 | /// returned. | |
| 406 | /// | |
| 407 | /// If the delimiter is not found within a number of bytes matching the | |
| 408 | /// capacity of the `BufferedReader`, `error.StreamTooLong` is returned. | |
| 409 | /// | |
| 410 | 324 | /// Invalidates previously returned values from `peek`. |
| 411 | 325 | /// |
| 412 | 326 | /// See also: |
| 413 | 327 | /// * `takeSentinel` |
| 414 | 328 | /// * `takeDelimiterExclusive` |
| 415 | 329 | /// * `peekDelimiterInclusive` |
| 416 | pub fn takeDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 { | |
| 330 | pub fn takeDelimiterInclusive(br: *BufferedReader, delimiter: u8) DelimiterInclusiveError![]u8 { | |
| 417 | 331 | const result = try br.peekDelimiterInclusive(delimiter); |
| 418 | 332 | br.toss(result.len); |
| 419 | 333 | return result; |
| 420 | 334 | } |
| 421 | 335 | |
| 422 | pub fn peekDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 { | |
| 336 | pub fn peekDelimiterInclusive(br: *BufferedReader, delimiter: u8) DelimiterInclusiveError![]u8 { | |
| 423 | 337 | return (try br.peekDelimiterInclusiveUnlessEnd(delimiter)) orelse error.EndOfStream; |
| 424 | 338 | } |
| 425 | 339 | |
| 340 | pub const DelimiterExclusiveError = error{ | |
| 341 | /// See the `Reader` implementation for detailed diagnostics. | |
| 342 | ReadFailed, | |
| 343 | /// The delimiter was not found within a number of bytes matching the | |
| 344 | /// capacity of the `BufferedReader`. | |
| 345 | StreamTooLong, | |
| 346 | }; | |
| 347 | ||
| 426 | 348 | /// Returns a slice of the next bytes of buffered data from the stream until |
| 427 | 349 | /// `delimiter` is found, advancing the seek position. |
| 428 | 350 | /// |
| ... | ... | @@ -430,32 +352,33 @@ pub fn peekDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 |
| 430 | 352 | /// |
| 431 | 353 | /// End-of-stream is treated equivalent to a delimiter. |
| 432 | 354 | /// |
| 433 | /// If the delimiter is not found within a number of bytes matching the | |
| 434 | /// capacity of the `BufferedReader`, `error.StreamTooLong` is returned. | |
| 435 | /// | |
| 436 | 355 | /// Invalidates previously returned values from `peek`. |
| 437 | 356 | /// |
| 438 | 357 | /// See also: |
| 439 | 358 | /// * `takeSentinel` |
| 440 | 359 | /// * `takeDelimiterInclusive` |
| 441 | 360 | /// * `peekDelimiterExclusive` |
| 442 | pub 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]; | |
| 361 | pub 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, | |
| 447 | 368 | }; |
| 448 | 369 | br.toss(result.len); |
| 449 | 370 | return result[0 .. result.len - 1]; |
| 450 | 371 | } |
| 451 | 372 | |
| 452 | pub 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]; | |
| 373 | pub 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 | }; | |
| 455 | 378 | return result[0 .. result.len - 1]; |
| 456 | 379 | } |
| 457 | 380 | |
| 458 | fn peekDelimiterInclusiveUnlessEnd(br: *BufferedReader, delimiter: u8) anyerror!?[]u8 { | |
| 381 | fn peekDelimiterInclusiveUnlessEnd(br: *BufferedReader, delimiter: u8) DelimiterInclusiveError!?[]u8 { | |
| 459 | 382 | const storage = &br.storage; |
| 460 | 383 | const buffer = storage.buffer[0..storage.end]; |
| 461 | 384 | const seek = br.seek; |
| ... | ... | @@ -469,21 +392,29 @@ fn peekDelimiterInclusiveUnlessEnd(br: *BufferedReader, delimiter: u8) anyerror! |
| 469 | 392 | storage.end = i; |
| 470 | 393 | br.seek = 0; |
| 471 | 394 | 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; | |
| 475 | 408 | i = storage.end; |
| 476 | 409 | } |
| 477 | 410 | return error.StreamTooLong; |
| 478 | 411 | } |
| 479 | 412 | |
| 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. | |
| 484 | 415 | /// |
| 485 | 416 | /// Returns number of bytes streamed. |
| 486 | pub fn streamReadDelimiter(br: *BufferedReader, bw: *std.io.BufferedWriter, delimiter: u8) anyerror!usize { | |
| 417 | pub fn streamReadDelimiter(br: *BufferedReader, bw: *BufferedWriter, delimiter: u8) Reader.Error!usize { | |
| 487 | 418 | _ = br; |
| 488 | 419 | _ = bw; |
| 489 | 420 | _ = delimiter; |
| ... | ... | @@ -495,29 +426,35 @@ pub fn streamReadDelimiter(br: *BufferedReader, bw: *std.io.BufferedWriter, deli |
| 495 | 426 | /// |
| 496 | 427 | /// Succeeds if stream ends before delimiter found. |
| 497 | 428 | /// |
| 498 | /// Returns number of bytes streamed as well as whether the input reached the end. | |
| 499 | /// The end is not signaled to the writer. | |
| 429 | /// Returns number of bytes streamed. The end is not signaled to the writer. | |
| 500 | 430 | pub fn streamReadDelimiterExclusive( |
| 501 | 431 | br: *BufferedReader, |
| 502 | bw: *std.io.BufferedWriter, | |
| 432 | bw: *BufferedWriter, | |
| 503 | 433 | delimiter: u8, |
| 504 | ) anyerror!Reader.Status { | |
| 434 | ) Reader.ShortError!usize { | |
| 505 | 435 | _ = br; |
| 506 | 436 | _ = bw; |
| 507 | 437 | _ = delimiter; |
| 508 | 438 | @panic("TODO"); |
| 509 | 439 | } |
| 510 | 440 | |
| 441 | pub const StreamDelimiterLimitedError = Reader.ShortError || error{ | |
| 442 | /// Stream ended before the delimiter was found. | |
| 443 | EndOfStream, | |
| 444 | /// The delimiter was not found within the limit. | |
| 445 | StreamTooLong, | |
| 446 | }; | |
| 447 | ||
| 511 | 448 | /// Appends to `bw` contents by reading from the stream until `delimiter` is found. |
| 512 | 449 | /// Does not write the delimiter itself. |
| 513 | /// | |
| 514 | /// If `limit` is exceeded, returns `error.StreamTooLong`. | |
| 450 | // | |
| 451 | /// Returns number of bytes streamed. | |
| 515 | 452 | pub fn streamReadDelimiterLimited( |
| 516 | 453 | br: *BufferedReader, |
| 517 | 454 | bw: *BufferedWriter, |
| 518 | 455 | delimiter: u8, |
| 519 | limit: usize, | |
| 520 | ) anyerror!void { | |
| 456 | limit: Reader.Limit, | |
| 457 | ) StreamDelimiterLimitedError!usize { | |
| 521 | 458 | _ = br; |
| 522 | 459 | _ = bw; |
| 523 | 460 | _ = delimiter; |
| ... | ... | @@ -529,7 +466,7 @@ pub fn streamReadDelimiterLimited( |
| 529 | 466 | /// including the delimiter. |
| 530 | 467 | /// |
| 531 | 468 | /// If end of stream is found, this function succeeds. |
| 532 | pub fn discardDelimiterExclusive(br: *BufferedReader, delimiter: u8) anyerror!void { | |
| 469 | pub fn discardDelimiterInclusive(br: *BufferedReader, delimiter: u8) Reader.Error!void { | |
| 533 | 470 | _ = br; |
| 534 | 471 | _ = delimiter; |
| 535 | 472 | @panic("TODO"); |
| ... | ... | @@ -538,8 +475,8 @@ pub fn discardDelimiterExclusive(br: *BufferedReader, delimiter: u8) anyerror!vo |
| 538 | 475 | /// Reads from the stream until specified byte is found, discarding all data, |
| 539 | 476 | /// excluding the delimiter. |
| 540 | 477 | /// |
| 541 | /// If end of stream is found, `error.EndOfStream` is returned. | |
| 542 | pub fn discardDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror!void { | |
| 478 | /// Succeeds if stream ends before delimiter found. | |
| 479 | pub fn discardDelimiterExclusive(br: *BufferedReader, delimiter: u8) Reader.ShortError!void { | |
| 543 | 480 | _ = br; |
| 544 | 481 | _ = delimiter; |
| 545 | 482 | @panic("TODO"); |
| ... | ... | @@ -548,69 +485,75 @@ pub fn discardDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror!vo |
| 548 | 485 | /// Fills the buffer such that it contains at least `n` bytes, without |
| 549 | 486 | /// advancing the seek position. |
| 550 | 487 | /// |
| 551 | /// Returns `false` if and only if there are fewer than `n` bytes remaining. | |
| 488 | /// Returns `error.EndOfStream` if and only if there are fewer than `n` bytes | |
| 489 | /// remaining. | |
| 552 | 490 | /// |
| 553 | 491 | /// Asserts buffer capacity is at least `n`. |
| 554 | pub fn fill(br: *BufferedReader, n: usize) anyerror!bool { | |
| 492 | pub fn fill(br: *BufferedReader, n: usize) Reader.Error!void { | |
| 555 | 493 | const storage = &br.storage; |
| 556 | 494 | assert(n <= storage.buffer.len); |
| 557 | 495 | const buffer = storage.buffer[0..storage.end]; |
| 558 | 496 | const seek = br.seek; |
| 559 | 497 | if (seek + n <= buffer.len) { |
| 560 | 498 | @branchHint(.likely); |
| 561 | return true; | |
| 499 | return; | |
| 562 | 500 | } |
| 563 | 501 | const remainder = buffer[seek..]; |
| 564 | 502 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); |
| 565 | 503 | storage.end = remainder.len; |
| 566 | 504 | br.seek = 0; |
| 567 | 505 | 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; | |
| 571 | 512 | } |
| 572 | 513 | } |
| 573 | 514 | |
| 574 | 515 | /// Reads 1 byte from the stream or returns `error.EndOfStream`. |
| 575 | pub fn takeByte(br: *BufferedReader) anyerror!u8 { | |
| 516 | pub fn takeByte(br: *BufferedReader) Reader.Error!u8 { | |
| 576 | 517 | const storage = &br.storage; |
| 577 | 518 | const buffer = storage.buffer[0..storage.end]; |
| 578 | 519 | const seek = br.seek; |
| 579 | 520 | if (seek >= buffer.len) { |
| 580 | 521 | @branchHint(.unlikely); |
| 581 | const filled = try fill(br, 1); | |
| 582 | if (!filled) return error.EndOfStream; | |
| 522 | try fill(br, 1); | |
| 583 | 523 | } |
| 584 | 524 | br.seek = seek + 1; |
| 585 | 525 | return buffer[seek]; |
| 586 | 526 | } |
| 587 | 527 | |
| 588 | 528 | /// Same as `takeByte` except the returned byte is signed. |
| 589 | pub fn takeByteSigned(br: *BufferedReader) anyerror!i8 { | |
| 529 | pub fn takeByteSigned(br: *BufferedReader) Reader.Error!i8 { | |
| 590 | 530 | return @bitCast(try br.takeByte()); |
| 591 | 531 | } |
| 592 | 532 | |
| 593 | 533 | /// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`. |
| 594 | pub inline fn takeInt(br: *BufferedReader, comptime T: type, endian: std.builtin.Endian) anyerror!T { | |
| 534 | pub inline fn takeInt(br: *BufferedReader, comptime T: type, endian: std.builtin.Endian) Reader.Error!T { | |
| 595 | 535 | const n = @divExact(@typeInfo(T).int.bits, 8); |
| 596 | 536 | return std.mem.readInt(T, try br.takeArray(n), endian); |
| 597 | 537 | } |
| 598 | 538 | |
| 599 | 539 | /// Asserts the buffer was initialized with a capacity at least `n`. |
| 600 | pub fn takeVarInt(br: *BufferedReader, comptime Int: type, endian: std.builtin.Endian, n: usize) anyerror!Int { | |
| 540 | pub fn takeVarInt(br: *BufferedReader, comptime Int: type, endian: std.builtin.Endian, n: usize) Reader.Error!Int { | |
| 601 | 541 | assert(n <= @sizeOf(Int)); |
| 602 | 542 | return std.mem.readVarInt(Int, try br.take(n), endian); |
| 603 | 543 | } |
| 604 | 544 | |
| 605 | 545 | /// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`. |
| 606 | pub fn takeStruct(br: *BufferedReader, comptime T: type) anyerror!*align(1) T { | |
| 546 | pub fn takeStruct(br: *BufferedReader, comptime T: type) Reader.Error!*align(1) T { | |
| 607 | 547 | // Only extern and packed structs have defined in-memory layout. |
| 608 | 548 | comptime assert(@typeInfo(T).@"struct".layout != .auto); |
| 609 | 549 | return @ptrCast(try br.takeArray(@sizeOf(T))); |
| 610 | 550 | } |
| 611 | 551 | |
| 612 | 552 | /// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`. |
| 613 | pub 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. | |
| 556 | pub inline fn takeStructEndian(br: *BufferedReader, comptime T: type, endian: std.builtin.Endian) Reader.Error!T { | |
| 614 | 557 | var res = (try br.takeStruct(T)).*; |
| 615 | 558 | if (native_endian != endian) std.mem.byteSwapAllFields(T, &res); |
| 616 | 559 | return res; |
| ... | ... | @@ -621,14 +564,16 @@ pub fn takeStructEndian(br: *BufferedReader, comptime T: type, endian: std.built |
| 621 | 564 | /// it. Otherwise, returns `error.InvalidEnumTag`. |
| 622 | 565 | /// |
| 623 | 566 | /// Asserts the buffer was initialized with a capacity at least `@sizeOf(Enum)`. |
| 624 | pub fn takeEnum(br: *BufferedReader, comptime Enum: type, endian: std.builtin.Endian) anyerror!Enum { | |
| 567 | pub fn takeEnum(br: *BufferedReader, comptime Enum: type, endian: std.builtin.Endian) Reader.Error!Enum { | |
| 625 | 568 | const Tag = @typeInfo(Enum).@"enum".tag_type; |
| 626 | 569 | const int = try br.takeInt(Tag, endian); |
| 627 | 570 | return std.meta.intToEnum(Enum, int); |
| 628 | 571 | } |
| 629 | 572 | |
| 573 | pub const TakeLeb128Error = Reader.Error || error{Overflow}; | |
| 574 | ||
| 630 | 575 | /// Read a single LEB128 value as type T, or `error.Overflow` if the value cannot fit. |
| 631 | pub fn takeLeb128(br: *BufferedReader, comptime Result: type) anyerror!Result { | |
| 576 | pub fn takeLeb128(br: *BufferedReader, comptime Result: type) TakeLeb128Error!Result { | |
| 632 | 577 | const result_info = @typeInfo(Result).int; |
| 633 | 578 | return std.math.cast(Result, try br.takeMultipleOf7Leb128(@Type(.{ .int = .{ |
| 634 | 579 | .signedness = result_info.signedness, |
| ... | ... | @@ -636,7 +581,7 @@ pub fn takeLeb128(br: *BufferedReader, comptime Result: type) anyerror!Result { |
| 636 | 581 | } }))) orelse error.Overflow; |
| 637 | 582 | } |
| 638 | 583 | |
| 639 | fn takeMultipleOf7Leb128(br: *BufferedReader, comptime Result: type) anyerror!Result { | |
| 584 | fn takeMultipleOf7Leb128(br: *BufferedReader, comptime Result: type) TakeLeb128Error!Result { | |
| 640 | 585 | const result_info = @typeInfo(Result).int; |
| 641 | 586 | comptime assert(result_info.bits % 7 == 0); |
| 642 | 587 | var remaining_bits: std.math.Log2IntCeil(Result) = result_info.bits; |
| ... | ... | @@ -708,7 +653,7 @@ test discard { |
| 708 | 653 | try testing.expectError(error.EndOfStream, br.discard(1)); |
| 709 | 654 | } |
| 710 | 655 | |
| 711 | test discardUntilEnd { | |
| 656 | test discardRemaining { | |
| 712 | 657 | return error.Unimplemented; |
| 713 | 658 | } |
| 714 | 659 | |
| ... | ... | @@ -795,3 +740,7 @@ test takeEnum { |
| 795 | 740 | test takeLeb128 { |
| 796 | 741 | return error.Unimplemented; |
| 797 | 742 | } |
| 743 | ||
| 744 | test readShort { | |
| 745 | return error.Unimplemented; | |
| 746 | } |
lib/std/io/BufferedWriter.zig+93-70| ... | ... | @@ -35,19 +35,19 @@ pub fn writer(bw: *BufferedWriter) Writer { |
| 35 | 35 | return .{ |
| 36 | 36 | .context = bw, |
| 37 | 37 | .vtable = &.{ |
| 38 | .writeSplat = passthru_writeSplat, | |
| 39 | .writeFile = passthru_writeFile, | |
| 38 | .writeSplat = passthruWriteSplat, | |
| 39 | .writeFile = passthruWriteFile, | |
| 40 | 40 | }, |
| 41 | 41 | }; |
| 42 | 42 | } |
| 43 | 43 | |
| 44 | 44 | const fixed_vtable: Writer.VTable = .{ |
| 45 | .writeSplat = fixed_writeSplat, | |
| 46 | .writeFile = Writer.unimplemented_writeFile, | |
| 45 | .writeSplat = fixedWriteSplat, | |
| 46 | .writeFile = Writer.failingWriteFile, | |
| 47 | 47 | }; |
| 48 | 48 | |
| 49 | 49 | /// 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 | |
| 51 | 51 | /// equal. |
| 52 | 52 | pub fn initFixed(bw: *BufferedWriter, buffer: []u8) void { |
| 53 | 53 | bw.* = .{ |
| ... | ... | @@ -72,10 +72,10 @@ pub fn reset(bw: *BufferedWriter) void { |
| 72 | 72 | bw.count = 0; |
| 73 | 73 | } |
| 74 | 74 | |
| 75 | pub fn flush(bw: *BufferedWriter) anyerror!void { | |
| 75 | pub fn flush(bw: *BufferedWriter) Writer.Error!void { | |
| 76 | 76 | const send_buffer = bw.buffer[0..bw.end]; |
| 77 | 77 | var index: usize = 0; |
| 78 | while (index < send_buffer.len) index += try bw.unbuffered_writer.writev(&.{send_buffer[index..]}); | |
| 78 | while (index < send_buffer.len) index += try bw.unbuffered_writer.writeVec(&.{send_buffer[index..]}); | |
| 79 | 79 | bw.end = 0; |
| 80 | 80 | } |
| 81 | 81 | |
| ... | ... | @@ -84,7 +84,7 @@ pub fn unusedCapacitySlice(bw: *const BufferedWriter) []u8 { |
| 84 | 84 | } |
| 85 | 85 | |
| 86 | 86 | /// Asserts the provided buffer has total capacity enough for `minimum_length`. |
| 87 | pub fn writableSlice(bw: *BufferedWriter, minimum_length: usize) anyerror![]u8 { | |
| 87 | pub fn writableSlice(bw: *BufferedWriter, minimum_length: usize) Writer.Error![]u8 { | |
| 88 | 88 | assert(bw.buffer.len >= minimum_length); |
| 89 | 89 | const cap_slice = bw.buffer[bw.end..]; |
| 90 | 90 | if (cap_slice.len >= minimum_length) { |
| ... | ... | @@ -92,7 +92,7 @@ pub fn writableSlice(bw: *BufferedWriter, minimum_length: usize) anyerror![]u8 { |
| 92 | 92 | return cap_slice; |
| 93 | 93 | } |
| 94 | 94 | const buffer = bw.buffer[0..bw.end]; |
| 95 | const n = try bw.unbuffered_writer.writev(&.{buffer}); | |
| 95 | const n = try bw.unbuffered_writer.writeVec(&.{buffer}); | |
| 96 | 96 | if (n == buffer.len) { |
| 97 | 97 | @branchHint(.likely); |
| 98 | 98 | bw.end = 0; |
| ... | ... | @@ -115,11 +115,11 @@ pub fn advance(bw: *BufferedWriter, n: usize) void { |
| 115 | 115 | } |
| 116 | 116 | |
| 117 | 117 | /// The `data` parameter is mutable because this function needs to mutate the |
| 118 | /// fields in order to handle partial writes from `Writer.VTable.writev`. | |
| 119 | pub fn writevAll(bw: *BufferedWriter, data: [][]const u8) anyerror!void { | |
| 118 | /// fields in order to handle partial writes from `Writer.VTable.writeVec`. | |
| 119 | pub fn writeVecAll(bw: *BufferedWriter, data: [][]const u8) Writer.Error!void { | |
| 120 | 120 | var i: usize = 0; |
| 121 | 121 | while (true) { |
| 122 | var n = try passthru_writeSplat(bw, data[i..], 1); | |
| 122 | var n = try passthruWriteSplat(bw, data[i..], 1); | |
| 123 | 123 | const len = data[i].len; |
| 124 | 124 | while (n >= len) { |
| 125 | 125 | n -= len; |
| ... | ... | @@ -130,15 +130,15 @@ pub fn writevAll(bw: *BufferedWriter, data: [][]const u8) anyerror!void { |
| 130 | 130 | } |
| 131 | 131 | } |
| 132 | 132 | |
| 133 | pub fn writeSplat(bw: *BufferedWriter, data: []const []const u8, splat: usize) anyerror!usize { | |
| 134 | return passthru_writeSplat(bw, data, splat); | |
| 133 | pub fn writeSplat(bw: *BufferedWriter, data: []const []const u8, splat: usize) Writer.Error!usize { | |
| 134 | return passthruWriteSplat(bw, data, splat); | |
| 135 | 135 | } |
| 136 | 136 | |
| 137 | pub fn writev(bw: *BufferedWriter, data: []const []const u8) anyerror!usize { | |
| 138 | return passthru_writeSplat(bw, data, 1); | |
| 137 | pub fn writeVec(bw: *BufferedWriter, data: []const []const u8) Writer.Error!usize { | |
| 138 | return passthruWriteSplat(bw, data, 1); | |
| 139 | 139 | } |
| 140 | 140 | |
| 141 | fn passthru_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 141 | fn passthruWriteSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) Writer.Error!usize { | |
| 142 | 142 | const bw: *BufferedWriter = @alignCast(@ptrCast(context)); |
| 143 | 143 | const buffer = bw.buffer; |
| 144 | 144 | const start_end = bw.end; |
| ... | ... | @@ -258,11 +258,11 @@ fn track(count: *usize, n: usize) usize { |
| 258 | 258 | /// When this function is called it means the buffer got full, so it's time |
| 259 | 259 | /// to return an error. However, we still need to make sure all of the |
| 260 | 260 | /// available buffer has been filled. |
| 261 | fn fixed_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 261 | fn fixedWriteSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) Writer.Error!usize { | |
| 262 | 262 | const bw: *BufferedWriter = @alignCast(@ptrCast(context)); |
| 263 | 263 | for (data) |bytes| { |
| 264 | 264 | const dest = bw.buffer[bw.end..]; |
| 265 | if (dest.len == 0) return error.NoSpaceLeft; | |
| 265 | if (dest.len == 0) return error.WriteFailed; | |
| 266 | 266 | const len = @min(bytes.len, dest.len); |
| 267 | 267 | @memcpy(dest[0..len], bytes[0..len]); |
| 268 | 268 | bw.end += len; |
| ... | ... | @@ -277,16 +277,16 @@ fn fixed_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize |
| 277 | 277 | } |
| 278 | 278 | bw.end = bw.buffer.len; |
| 279 | 279 | bw.count = bw.end; |
| 280 | return error.NoSpaceLeft; | |
| 280 | return error.WriteFailed; | |
| 281 | 281 | } |
| 282 | 282 | |
| 283 | pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize { | |
| 283 | pub fn write(bw: *BufferedWriter, bytes: []const u8) Writer.Error!usize { | |
| 284 | 284 | const buffer = bw.buffer; |
| 285 | 285 | const end = bw.end; |
| 286 | 286 | const new_end = end + bytes.len; |
| 287 | 287 | if (new_end > buffer.len) { |
| 288 | 288 | var data: [2][]const u8 = .{ buffer[0..end], bytes }; |
| 289 | const n = try bw.unbuffered_writer.writev(&data); | |
| 289 | const n = try bw.unbuffered_writer.writeVec(&data); | |
| 290 | 290 | if (n < end) { |
| 291 | 291 | @branchHint(.unlikely); |
| 292 | 292 | const remainder = buffer[n..end]; |
| ... | ... | @@ -304,16 +304,16 @@ pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize { |
| 304 | 304 | |
| 305 | 305 | /// Calls `write` as many times as necessary such that all of `bytes` are |
| 306 | 306 | /// transferred. |
| 307 | pub fn writeAll(bw: *BufferedWriter, bytes: []const u8) anyerror!void { | |
| 307 | pub fn writeAll(bw: *BufferedWriter, bytes: []const u8) Writer.Error!void { | |
| 308 | 308 | var index: usize = 0; |
| 309 | 309 | while (index < bytes.len) index += try bw.write(bytes[index..]); |
| 310 | 310 | } |
| 311 | 311 | |
| 312 | pub fn print(bw: *BufferedWriter, comptime format: []const u8, args: anytype) anyerror!void { | |
| 312 | pub fn print(bw: *BufferedWriter, comptime format: []const u8, args: anytype) Writer.Error!void { | |
| 313 | 313 | try std.fmt.format(bw, format, args); |
| 314 | 314 | } |
| 315 | 315 | |
| 316 | pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void { | |
| 316 | pub fn writeByte(bw: *BufferedWriter, byte: u8) Writer.Error!void { | |
| 317 | 317 | const buffer = bw.buffer[0..bw.end]; |
| 318 | 318 | if (buffer.len < bw.buffer.len) { |
| 319 | 319 | @branchHint(.likely); |
| ... | ... | @@ -324,7 +324,7 @@ pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void { |
| 324 | 324 | } |
| 325 | 325 | var buffers: [2][]const u8 = .{ buffer, &.{byte} }; |
| 326 | 326 | while (true) { |
| 327 | const n = try bw.unbuffered_writer.writev(&buffers); | |
| 327 | const n = try bw.unbuffered_writer.writeVec(&buffers); | |
| 328 | 328 | if (n == 0) { |
| 329 | 329 | @branchHint(.unlikely); |
| 330 | 330 | continue; |
| ... | ... | @@ -352,7 +352,7 @@ pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void { |
| 352 | 352 | |
| 353 | 353 | /// Writes the same byte many times, performing the underlying write call as |
| 354 | 354 | /// many times as necessary. |
| 355 | pub fn splatByteAll(bw: *BufferedWriter, byte: u8, n: usize) anyerror!void { | |
| 355 | pub fn splatByteAll(bw: *BufferedWriter, byte: u8, n: usize) Writer.Error!void { | |
| 356 | 356 | var remaining: usize = n; |
| 357 | 357 | while (remaining > 0) remaining -= try bw.splatByte(byte, remaining); |
| 358 | 358 | } |
| ... | ... | @@ -360,13 +360,13 @@ pub fn splatByteAll(bw: *BufferedWriter, byte: u8, n: usize) anyerror!void { |
| 360 | 360 | /// Writes the same byte many times, allowing short writes. |
| 361 | 361 | /// |
| 362 | 362 | /// Does maximum of one underlying `Writer.VTable.writeSplat`. |
| 363 | pub fn splatByte(bw: *BufferedWriter, byte: u8, n: usize) anyerror!usize { | |
| 364 | return passthru_writeSplat(bw, &.{&.{byte}}, n); | |
| 363 | pub fn splatByte(bw: *BufferedWriter, byte: u8, n: usize) Writer.Error!usize { | |
| 364 | return passthruWriteSplat(bw, &.{&.{byte}}, n); | |
| 365 | 365 | } |
| 366 | 366 | |
| 367 | 367 | /// Writes the same slice many times, performing the underlying write call as |
| 368 | 368 | /// many times as necessary. |
| 369 | pub fn splatBytesAll(bw: *BufferedWriter, bytes: []const u8, splat: usize) anyerror!void { | |
| 369 | pub fn splatBytesAll(bw: *BufferedWriter, bytes: []const u8, splat: usize) Writer.Error!void { | |
| 370 | 370 | var remaining_bytes: usize = bytes.len * splat; |
| 371 | 371 | remaining_bytes -= try bw.splatBytes(bytes, splat); |
| 372 | 372 | while (remaining_bytes > 0) { |
| ... | ... | @@ -378,26 +378,28 @@ pub fn splatBytesAll(bw: *BufferedWriter, bytes: []const u8, splat: usize) anyer |
| 378 | 378 | |
| 379 | 379 | /// Writes the same slice many times, allowing short writes. |
| 380 | 380 | /// |
| 381 | /// Does maximum of one underlying `Writer.VTable.writev`. | |
| 382 | pub 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`. | |
| 382 | pub fn splatBytes(bw: *BufferedWriter, bytes: []const u8, n: usize) Writer.Error!usize { | |
| 383 | return passthruWriteSplat(bw, &.{bytes}, n); | |
| 384 | 384 | } |
| 385 | 385 | |
| 386 | 386 | /// Asserts the `buffer` was initialized with a capacity of at least `@sizeOf(T)` bytes. |
| 387 | pub inline fn writeInt(bw: *BufferedWriter, comptime T: type, value: T, endian: std.builtin.Endian) anyerror!void { | |
| 387 | pub inline fn writeInt(bw: *BufferedWriter, comptime T: type, value: T, endian: std.builtin.Endian) Writer.Error!void { | |
| 388 | 388 | var bytes: [@divExact(@typeInfo(T).int.bits, 8)]u8 = undefined; |
| 389 | 389 | std.mem.writeInt(std.math.ByteAlignedInt(@TypeOf(value)), &bytes, value, endian); |
| 390 | 390 | return bw.writeAll(&bytes); |
| 391 | 391 | } |
| 392 | 392 | |
| 393 | pub fn writeStruct(bw: *BufferedWriter, value: anytype) anyerror!void { | |
| 393 | pub fn writeStruct(bw: *BufferedWriter, value: anytype) Writer.Error!void { | |
| 394 | 394 | // Only extern and packed structs have defined in-memory layout. |
| 395 | 395 | comptime assert(@typeInfo(@TypeOf(value)).@"struct".layout != .auto); |
| 396 | 396 | return bw.writeAll(std.mem.asBytes(&value)); |
| 397 | 397 | } |
| 398 | 398 | |
| 399 | pub 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 | |
| 402 | pub inline fn writeStructEndian(bw: *BufferedWriter, value: anytype, endian: std.builtin.Endian) Writer.Error!void { | |
| 401 | 403 | if (native_endian == endian) { |
| 402 | 404 | return bw.writeStruct(value); |
| 403 | 405 | } else { |
| ... | ... | @@ -407,6 +409,27 @@ pub fn writeStructEndian(bw: *BufferedWriter, value: anytype, endian: std.builti |
| 407 | 409 | } |
| 408 | 410 | } |
| 409 | 411 | |
| 412 | pub 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)` | |
| 426 | pub 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 | ||
| 410 | 433 | pub fn writeFile( |
| 411 | 434 | bw: *BufferedWriter, |
| 412 | 435 | file: std.fs.File, |
| ... | ... | @@ -414,18 +437,18 @@ pub fn writeFile( |
| 414 | 437 | limit: Writer.Limit, |
| 415 | 438 | headers_and_trailers: []const []const u8, |
| 416 | 439 | 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); | |
| 419 | 442 | } |
| 420 | 443 | |
| 421 | fn passthru_writeFile( | |
| 444 | fn passthruWriteFile( | |
| 422 | 445 | context: ?*anyopaque, |
| 423 | 446 | file: std.fs.File, |
| 424 | 447 | offset: Writer.Offset, |
| 425 | 448 | limit: Writer.Limit, |
| 426 | 449 | headers_and_trailers: []const []const u8, |
| 427 | 450 | headers_len: usize, |
| 428 | ) anyerror!usize { | |
| 451 | ) Writer.FileError!usize { | |
| 429 | 452 | const bw: *BufferedWriter = @alignCast(@ptrCast(context)); |
| 430 | 453 | const buffer = bw.buffer; |
| 431 | 454 | if (buffer.len == 0) return track( |
| ... | ... | @@ -468,8 +491,8 @@ fn passthru_writeFile( |
| 468 | 491 | bw.end = 0; |
| 469 | 492 | return track(&bw.count, n - start_end); |
| 470 | 493 | } |
| 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]); | |
| 473 | 496 | if (n < end) { |
| 474 | 497 | @branchHint(.unlikely); |
| 475 | 498 | const remainder = buffer[n..end]; |
| ... | ... | @@ -505,7 +528,7 @@ pub const WriteFileOptions = struct { |
| 505 | 528 | /// size here will save one syscall. |
| 506 | 529 | limit: Writer.Limit = .unlimited, |
| 507 | 530 | /// Headers and trailers must be passed together so that in case `len` is |
| 508 | /// zero, they can be forwarded directly to `Writer.VTable.writev`. | |
| 531 | /// zero, they can be forwarded directly to `Writer.VTable.writeVec`. | |
| 509 | 532 | /// |
| 510 | 533 | /// The parameter is mutable because this function needs to mutate the |
| 511 | 534 | /// fields in order to handle partial writes from `Writer.VTable.writeFile`. |
| ... | ... | @@ -515,11 +538,11 @@ pub const WriteFileOptions = struct { |
| 515 | 538 | headers_len: usize = 0, |
| 516 | 539 | }; |
| 517 | 540 | |
| 518 | pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOptions) anyerror!void { | |
| 541 | pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOptions) Writer.FileError!void { | |
| 519 | 542 | const headers_and_trailers = options.headers_and_trailers; |
| 520 | 543 | const headers = headers_and_trailers[0..options.headers_len]; |
| 521 | 544 | switch (options.limit) { |
| 522 | .nothing => return bw.writevAll(headers_and_trailers), | |
| 545 | .nothing => return bw.writeVecAll(headers_and_trailers), | |
| 523 | 546 | .unlimited => { |
| 524 | 547 | // When reading the whole file, we cannot include the trailers in the |
| 525 | 548 | // call that reads from the file handle, because we have no way to |
| ... | ... | @@ -564,7 +587,7 @@ pub fn writeFileAll(bw: *BufferedWriter, file: std.fs.File, options: WriteFileOp |
| 564 | 587 | if (i >= headers_and_trailers.len) return; |
| 565 | 588 | } |
| 566 | 589 | headers_and_trailers[i] = headers_and_trailers[i][n..]; |
| 567 | return bw.writevAll(headers_and_trailers[i..]); | |
| 590 | return bw.writeVecAll(headers_and_trailers[i..]); | |
| 568 | 591 | } |
| 569 | 592 | offset = offset.advance(n); |
| 570 | 593 | len -= n; |
| ... | ... | @@ -579,7 +602,7 @@ pub fn alignBuffer( |
| 579 | 602 | width: usize, |
| 580 | 603 | alignment: std.fmt.Alignment, |
| 581 | 604 | fill: u8, |
| 582 | ) anyerror!void { | |
| 605 | ) Writer.Error!void { | |
| 583 | 606 | const padding = if (buffer.len < width) width - buffer.len else 0; |
| 584 | 607 | if (padding == 0) { |
| 585 | 608 | @branchHint(.likely); |
| ... | ... | @@ -604,11 +627,11 @@ pub fn alignBuffer( |
| 604 | 627 | } |
| 605 | 628 | } |
| 606 | 629 | |
| 607 | pub fn alignBufferOptions(bw: *BufferedWriter, buffer: []const u8, options: std.fmt.Options) anyerror!void { | |
| 630 | pub fn alignBufferOptions(bw: *BufferedWriter, buffer: []const u8, options: std.fmt.Options) Writer.Error!void { | |
| 608 | 631 | return bw.alignBuffer(buffer, options.width orelse buffer.len, options.alignment, options.fill); |
| 609 | 632 | } |
| 610 | 633 | |
| 611 | pub fn printAddress(bw: *BufferedWriter, value: anytype) anyerror!void { | |
| 634 | pub fn printAddress(bw: *BufferedWriter, value: anytype) Writer.Error!void { | |
| 612 | 635 | const T = @TypeOf(value); |
| 613 | 636 | switch (@typeInfo(T)) { |
| 614 | 637 | .pointer => |info| { |
| ... | ... | @@ -638,7 +661,7 @@ pub fn printValue( |
| 638 | 661 | options: std.fmt.Options, |
| 639 | 662 | value: anytype, |
| 640 | 663 | max_depth: usize, |
| 641 | ) anyerror!void { | |
| 664 | ) Writer.Error!void { | |
| 642 | 665 | const T = @TypeOf(value); |
| 643 | 666 | const actual_fmt = comptime if (std.mem.eql(u8, fmt, ANY)) |
| 644 | 667 | defaultFormatString(T) |
| ... | ... | @@ -791,7 +814,7 @@ pub fn printValue( |
| 791 | 814 | }, |
| 792 | 815 | else => { |
| 793 | 816 | var buffers: [2][]const u8 = .{ @typeName(ptr_info.child), "@" }; |
| 794 | try bw.writevAll(&buffers); | |
| 817 | try bw.writeVecAll(&buffers); | |
| 795 | 818 | try bw.printIntOptions(@intFromPtr(value), 16, .lower, options); |
| 796 | 819 | return; |
| 797 | 820 | }, |
| ... | ... | @@ -896,7 +919,7 @@ pub fn printInt( |
| 896 | 919 | comptime fmt: []const u8, |
| 897 | 920 | options: std.fmt.Options, |
| 898 | 921 | value: anytype, |
| 899 | ) anyerror!void { | |
| 922 | ) Writer.Error!void { | |
| 900 | 923 | const int_value = if (@TypeOf(value) == comptime_int) blk: { |
| 901 | 924 | const Int = std.math.IntFittingRange(value, value); |
| 902 | 925 | break :blk @as(Int, value); |
| ... | ... | @@ -940,15 +963,15 @@ pub fn printInt( |
| 940 | 963 | comptime unreachable; |
| 941 | 964 | } |
| 942 | 965 | |
| 943 | pub fn printAsciiChar(bw: *BufferedWriter, c: u8, options: std.fmt.Options) anyerror!void { | |
| 966 | pub fn printAsciiChar(bw: *BufferedWriter, c: u8, options: std.fmt.Options) Writer.Error!void { | |
| 944 | 967 | return bw.alignBufferOptions(@as(*const [1]u8, &c), options); |
| 945 | 968 | } |
| 946 | 969 | |
| 947 | pub fn printAscii(bw: *BufferedWriter, bytes: []const u8, options: std.fmt.Options) anyerror!void { | |
| 970 | pub fn printAscii(bw: *BufferedWriter, bytes: []const u8, options: std.fmt.Options) Writer.Error!void { | |
| 948 | 971 | return bw.alignBufferOptions(bytes, options); |
| 949 | 972 | } |
| 950 | 973 | |
| 951 | pub fn printUnicodeCodepoint(bw: *BufferedWriter, c: u21, options: std.fmt.Options) anyerror!void { | |
| 974 | pub fn printUnicodeCodepoint(bw: *BufferedWriter, c: u21, options: std.fmt.Options) Writer.Error!void { | |
| 952 | 975 | var buf: [4]u8 = undefined; |
| 953 | 976 | const len = try std.unicode.utf8Encode(c, &buf); |
| 954 | 977 | return bw.alignBufferOptions(buf[0..len], options); |
| ... | ... | @@ -960,7 +983,7 @@ pub fn printIntOptions( |
| 960 | 983 | base: u8, |
| 961 | 984 | case: std.fmt.Case, |
| 962 | 985 | options: std.fmt.Options, |
| 963 | ) anyerror!void { | |
| 986 | ) Writer.Error!void { | |
| 964 | 987 | assert(base >= 2); |
| 965 | 988 | |
| 966 | 989 | const int_value = if (@TypeOf(value) == comptime_int) blk: { |
| ... | ... | @@ -1027,7 +1050,7 @@ pub fn printFloat( |
| 1027 | 1050 | comptime fmt: []const u8, |
| 1028 | 1051 | options: std.fmt.Options, |
| 1029 | 1052 | value: anytype, |
| 1030 | ) anyerror!void { | |
| 1053 | ) Writer.Error!void { | |
| 1031 | 1054 | var buf: [std.fmt.float.bufferSize(.decimal, f64)]u8 = undefined; |
| 1032 | 1055 | |
| 1033 | 1056 | if (fmt.len > 1) invalidFmtError(fmt, value); |
| ... | ... | @@ -1054,7 +1077,7 @@ pub fn printFloat( |
| 1054 | 1077 | } |
| 1055 | 1078 | } |
| 1056 | 1079 | |
| 1057 | pub fn printFloatHexadecimal(bw: *BufferedWriter, value: anytype, opt_precision: ?usize) anyerror!void { | |
| 1080 | pub fn printFloatHexadecimal(bw: *BufferedWriter, value: anytype, opt_precision: ?usize) Writer.Error!void { | |
| 1058 | 1081 | if (std.math.signbit(value)) try bw.writeByte('-'); |
| 1059 | 1082 | if (std.math.isNan(value)) return bw.writeAll("nan"); |
| 1060 | 1083 | if (std.math.isInf(value)) return bw.writeAll("inf"); |
| ... | ... | @@ -1168,7 +1191,7 @@ pub fn printByteSize( |
| 1168 | 1191 | value: u64, |
| 1169 | 1192 | comptime units: ByteSizeUnits, |
| 1170 | 1193 | options: std.fmt.Options, |
| 1171 | ) anyerror!void { | |
| 1194 | ) Writer.Error!void { | |
| 1172 | 1195 | if (value == 0) return bw.alignBufferOptions("0B", options); |
| 1173 | 1196 | // The worst case in terms of space needed is 32 bytes + 3 for the suffix. |
| 1174 | 1197 | var buf: [std.fmt.float.min_buffer_size + 3]u8 = undefined; |
| ... | ... | @@ -1248,12 +1271,12 @@ pub fn invalidFmtError(comptime fmt: []const u8, value: anytype) noreturn { |
| 1248 | 1271 | @compileError("invalid format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'"); |
| 1249 | 1272 | } |
| 1250 | 1273 | |
| 1251 | pub fn printDurationSigned(bw: *BufferedWriter, ns: i64) anyerror!void { | |
| 1274 | pub fn printDurationSigned(bw: *BufferedWriter, ns: i64) Writer.Error!void { | |
| 1252 | 1275 | if (ns < 0) try bw.writeByte('-'); |
| 1253 | 1276 | return bw.printDurationUnsigned(@abs(ns)); |
| 1254 | 1277 | } |
| 1255 | 1278 | |
| 1256 | pub fn printDurationUnsigned(bw: *BufferedWriter, ns: u64) anyerror!void { | |
| 1279 | pub fn printDurationUnsigned(bw: *BufferedWriter, ns: u64) Writer.Error!void { | |
| 1257 | 1280 | var ns_remaining = ns; |
| 1258 | 1281 | inline for (.{ |
| 1259 | 1282 | .{ .ns = 365 * std.time.ns_per_day, .sep = 'y' }, |
| ... | ... | @@ -1303,7 +1326,7 @@ pub fn printDurationUnsigned(bw: *BufferedWriter, ns: u64) anyerror!void { |
| 1303 | 1326 | /// Writes number of nanoseconds according to its signed magnitude: |
| 1304 | 1327 | /// `[#y][#w][#d][#h][#m]#[.###][n|u|m]s` |
| 1305 | 1328 | /// `nanoseconds` must be an integer that coerces into `u64` or `i64`. |
| 1306 | pub fn printDuration(bw: *BufferedWriter, nanoseconds: anytype, options: std.fmt.Options) anyerror!void { | |
| 1329 | pub fn printDuration(bw: *BufferedWriter, nanoseconds: anytype, options: std.fmt.Options) Writer.Error!void { | |
| 1307 | 1330 | // worst case: "-XXXyXXwXXdXXhXXmXX.XXXs".len = 24 |
| 1308 | 1331 | var buf: [24]u8 = undefined; |
| 1309 | 1332 | var sub_bw: BufferedWriter = undefined; |
| ... | ... | @@ -1315,7 +1338,7 @@ pub fn printDuration(bw: *BufferedWriter, nanoseconds: anytype, options: std.fmt |
| 1315 | 1338 | return bw.alignBufferOptions(sub_bw.getWritten(), options); |
| 1316 | 1339 | } |
| 1317 | 1340 | |
| 1318 | pub fn printHex(bw: *BufferedWriter, bytes: []const u8, case: std.fmt.Case) anyerror!void { | |
| 1341 | pub fn printHex(bw: *BufferedWriter, bytes: []const u8, case: std.fmt.Case) Writer.Error!void { | |
| 1319 | 1342 | const charset = switch (case) { |
| 1320 | 1343 | .upper => "0123456789ABCDEF", |
| 1321 | 1344 | .lower => "0123456789abcdef", |
| ... | ... | @@ -1326,7 +1349,7 @@ pub fn printHex(bw: *BufferedWriter, bytes: []const u8, case: std.fmt.Case) anye |
| 1326 | 1349 | } |
| 1327 | 1350 | } |
| 1328 | 1351 | |
| 1329 | pub fn printBase64(bw: *BufferedWriter, bytes: []const u8) anyerror!void { | |
| 1352 | pub fn printBase64(bw: *BufferedWriter, bytes: []const u8) Writer.Error!void { | |
| 1330 | 1353 | var chunker = std.mem.window(u8, bytes, 3, 3); |
| 1331 | 1354 | var temp: [5]u8 = undefined; |
| 1332 | 1355 | while (chunker.next()) |chunk| { |
| ... | ... | @@ -1335,7 +1358,7 @@ pub fn printBase64(bw: *BufferedWriter, bytes: []const u8) anyerror!void { |
| 1335 | 1358 | } |
| 1336 | 1359 | |
| 1337 | 1360 | /// Write a single unsigned integer as LEB128 to the given writer. |
| 1338 | pub fn writeUleb128(bw: *BufferedWriter, value: anytype) anyerror!void { | |
| 1361 | pub fn writeUleb128(bw: *BufferedWriter, value: anytype) Writer.Error!void { | |
| 1339 | 1362 | try bw.writeLeb128(switch (@typeInfo(@TypeOf(value))) { |
| 1340 | 1363 | .comptime_int => @as(std.math.IntFittingRange(0, @abs(value)), value), |
| 1341 | 1364 | .int => |value_info| switch (value_info.signedness) { |
| ... | ... | @@ -1347,7 +1370,7 @@ pub fn writeUleb128(bw: *BufferedWriter, value: anytype) anyerror!void { |
| 1347 | 1370 | } |
| 1348 | 1371 | |
| 1349 | 1372 | /// Write a single signed integer as LEB128 to the given writer. |
| 1350 | pub fn writeSleb128(bw: *BufferedWriter, value: anytype) anyerror!void { | |
| 1373 | pub fn writeSleb128(bw: *BufferedWriter, value: anytype) Writer.Error!void { | |
| 1351 | 1374 | try bw.writeLeb128(switch (@typeInfo(@TypeOf(value))) { |
| 1352 | 1375 | .comptime_int => @as(std.math.IntFittingRange(@min(value, -1), @max(0, value)), value), |
| 1353 | 1376 | .int => |value_info| switch (value_info.signedness) { |
| ... | ... | @@ -1359,7 +1382,7 @@ pub fn writeSleb128(bw: *BufferedWriter, value: anytype) anyerror!void { |
| 1359 | 1382 | } |
| 1360 | 1383 | |
| 1361 | 1384 | /// Write a single integer as LEB128 to the given writer. |
| 1362 | pub fn writeLeb128(bw: *BufferedWriter, value: anytype) anyerror!void { | |
| 1385 | pub fn writeLeb128(bw: *BufferedWriter, value: anytype) Writer.Error!void { | |
| 1363 | 1386 | const value_info = @typeInfo(@TypeOf(value)).int; |
| 1364 | 1387 | try bw.writeMultipleOf7Leb128(@as(@Type(.{ .int = .{ |
| 1365 | 1388 | .signedness = value_info.signedness, |
| ... | ... | @@ -1367,7 +1390,7 @@ pub fn writeLeb128(bw: *BufferedWriter, value: anytype) anyerror!void { |
| 1367 | 1390 | } }), value)); |
| 1368 | 1391 | } |
| 1369 | 1392 | |
| 1370 | fn writeMultipleOf7Leb128(bw: *BufferedWriter, value: anytype) anyerror!void { | |
| 1393 | fn writeMultipleOf7Leb128(bw: *BufferedWriter, value: anytype) Writer.Error!void { | |
| 1371 | 1394 | const value_info = @typeInfo(@TypeOf(value)).int; |
| 1372 | 1395 | comptime assert(value_info.bits % 7 == 0); |
| 1373 | 1396 | var remaining = value; |
| ... | ... | @@ -1409,7 +1432,7 @@ test "formatValue max_depth" { |
| 1409 | 1432 | comptime fmt: []const u8, |
| 1410 | 1433 | options: std.fmt.Options, |
| 1411 | 1434 | bw: *BufferedWriter, |
| 1412 | ) anyerror!void { | |
| 1435 | ) Writer.Error!void { | |
| 1413 | 1436 | _ = options; |
| 1414 | 1437 | if (fmt.len == 0) { |
| 1415 | 1438 | return bw.print("({d:.3},{d:.3})", .{ self.x, self.y }); |
lib/std/io/PositionalReader.zig deleted-64| ... | ... | @@ -1,64 +0,0 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const PositionalReader = @This(); | |
| 3 | const assert = std.debug.assert; | |
| 4 | ||
| 5 | context: ?*anyopaque, | |
| 6 | vtable: *const VTable, | |
| 7 | ||
| 8 | pub 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 | ||
| 40 | pub const Len = std.io.Reader.Len; | |
| 41 | pub const Status = std.io.Reader.Status; | |
| 42 | pub const Limit = std.io.Reader.Limit; | |
| 43 | ||
| 44 | pub 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 | ||
| 48 | pub 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. | |
| 56 | pub fn readAll(pr: PositionalReader, w: *std.io.BufferedWriter, start_offset: u64) anyerror!usize { | |
| 57 | const readFn = pr.vtable.read; | |
| 58 | var offset: u64 = start_offset; | |
| 59 | while (true) { | |
| 60 | const status = try readFn(pr.context, w, .none, offset); | |
| 61 | offset += status.len; | |
| 62 | if (status.end) return @intCast(offset - start_offset); | |
| 63 | } | |
| 64 | } |
lib/std/io/Reader.zig+128-36| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | const std = @import("../std.zig"); |
| 2 | 2 | const Reader = @This(); |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | const BufferedWriter = std.io.BufferedWriter; | |
| 4 | 5 | |
| 5 | 6 | context: ?*anyopaque, |
| 6 | 7 | vtable: *const VTable, |
| ... | ... | @@ -16,35 +17,54 @@ pub const VTable = struct { |
| 16 | 17 | /// accordance with the number of bytes return from this function. |
| 17 | 18 | /// |
| 18 | 19 | /// 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, | |
| 23 | 21 | |
| 24 | 22 | /// Writes bytes from the internally tracked stream position to `data`. |
| 25 | 23 | /// |
| 26 | /// Returns the number of bytes written, which will be at minimum `0` and 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. | |
| 29 | 27 | /// |
| 30 | 28 | /// If the reader has an internal seek position, it moves forward in |
| 31 | 29 | /// accordance with the number of bytes return from this function. |
| 32 | 30 | /// |
| 33 | 31 | /// The implementation should do a maximum of one underlying read call. |
| 32 | readVec: *const fn (context: ?*anyopaque, data: []const []u8) Error!usize, | |
| 33 | ||
| 34 | /// Consumes bytes from the internally tracked stream position without | |
| 35 | /// providing access to them. | |
| 34 | 36 | /// |
| 35 | /// If `error.Unstreamable` is returned, the resource cannot be used via 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, | |
| 38 | 46 | }; |
| 39 | 47 | |
| 40 | pub const Len = @Type(.{ .int = .{ .signedness = .unsigned, .bits = @bitSizeOf(usize) - 1 } }); | |
| 48 | pub const RwError = RwAllError || error{ | |
| 49 | /// End of stream indicated from the `Reader`. This error cannot originate | |
| 50 | /// from the `Writer`. | |
| 51 | EndOfStream, | |
| 52 | }; | |
| 53 | ||
| 54 | pub const Error = ShortError || error{ | |
| 55 | EndOfStream, | |
| 56 | }; | |
| 57 | ||
| 58 | /// For functions that handle end of stream as a success case. | |
| 59 | pub const RwAllError = ShortError || error{ | |
| 60 | /// See the `Writer` implementation for detailed diagnostics. | |
| 61 | WriteFailed, | |
| 62 | }; | |
| 41 | 63 | |
| 42 | pub 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`. | |
| 65 | pub const ShortError = error{ | |
| 66 | /// See the `Reader` implementation for detailed diagnostics. | |
| 67 | ReadFailed, | |
| 48 | 68 | }; |
| 49 | 69 | |
| 50 | 70 | pub const Limit = enum(usize) { |
| ... | ... | @@ -93,50 +113,122 @@ pub const Limit = enum(usize) { |
| 93 | 113 | } |
| 94 | 114 | }; |
| 95 | 115 | |
| 96 | pub fn read(r: Reader, w: *std.io.BufferedWriter, limit: Limit) anyerror!Status { | |
| 97 | return r.vtable.read(r.context, w, limit); | |
| 116 | pub fn read(r: Reader, bw: *BufferedWriter, limit: Limit) RwError!usize { | |
| 117 | return r.vtable.read(r.context, bw, limit); | |
| 98 | 118 | } |
| 99 | 119 | |
| 100 | pub fn readv(r: Reader, data: []const []u8) anyerror!Status { | |
| 101 | return r.vtable.readv(r.context, data); | |
| 120 | pub fn readVec(r: Reader, data: []const []u8) Error!usize { | |
| 121 | return r.vtable.readVec(r.context, data); | |
| 102 | 122 | } |
| 103 | 123 | |
| 104 | /// Returns total number of bytes written to `w`. | |
| 105 | pub fn readAll(r: Reader, w: *std.io.BufferedWriter) anyerror!usize { | |
| 124 | pub 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`. | |
| 129 | pub fn readAll(r: Reader, bw: *BufferedWriter) RwAllError!usize { | |
| 106 | 130 | const readFn = r.vtable.read; |
| 107 | 131 | var offset: usize = 0; |
| 108 | 132 | 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 | }; | |
| 112 | 137 | } |
| 113 | 138 | } |
| 114 | 139 | |
| 140 | /// Consumes the stream until the end, ignoring all the data, returning the | |
| 141 | /// number of bytes discarded. | |
| 142 | pub 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 | ||
| 153 | pub const ReadAllocError = std.mem.Allocator.Error || ShortError; | |
| 154 | ||
| 115 | 155 | /// Allocates enough memory to hold all the contents of the stream. If the allocated |
| 116 | 156 | /// memory would be greater than `max_size`, returns `error.StreamTooLong`. |
| 117 | 157 | /// |
| 118 | 158 | /// Caller owns returned memory. |
| 119 | 159 | /// |
| 120 | 160 | /// If this function returns an error, the contents from the stream read so far are lost. |
| 121 | pub fn readAlloc(r: Reader, gpa: std.mem.Allocator, max_size: usize) anyerror![]u8 { | |
| 161 | pub fn readAlloc(r: Reader, gpa: std.mem.Allocator, max_size: usize) ReadAllocError![]u8 { | |
| 122 | 162 | const readFn = r.vtable.read; |
| 123 | 163 | var aw: std.io.AllocatingWriter = undefined; |
| 124 | 164 | errdefer aw.deinit(); |
| 125 | 165 | aw.init(gpa); |
| 126 | 166 | var remaining = max_size; |
| 127 | 167 | 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; | |
| 131 | 174 | } |
| 132 | 175 | return aw.toOwnedSlice(); |
| 133 | 176 | } |
| 134 | 177 | |
| 135 | /// Reads the stream until the end, ignoring all the data. | |
| 136 | /// Returns the number of bytes discarded. | |
| 137 | pub fn discardUntilEnd(r: Reader) anyerror!usize { | |
| 138 | var bw = std.io.Writer.null.unbuffered(); | |
| 139 | return r.readAll(&bw); | |
| 178 | pub const failing: Reader = .{ | |
| 179 | .context = undefined, | |
| 180 | .vtable = &.{ | |
| 181 | .read = failingRead, | |
| 182 | .readVec = failingReadVec, | |
| 183 | .discard = failingDiscard, | |
| 184 | }, | |
| 185 | }; | |
| 186 | ||
| 187 | pub const ending: Reader = .{ | |
| 188 | .context = undefined, | |
| 189 | .vtable = &.{ | |
| 190 | .read = endingRead, | |
| 191 | .readVec = endingReadVec, | |
| 192 | .discard = endingDiscard, | |
| 193 | }, | |
| 194 | }; | |
| 195 | ||
| 196 | fn endingRead(context: ?*anyopaque, bw: *BufferedWriter, limit: Limit) RwError!usize { | |
| 197 | _ = context; | |
| 198 | _ = bw; | |
| 199 | _ = limit; | |
| 200 | return error.EndOfStream; | |
| 201 | } | |
| 202 | ||
| 203 | fn endingReadVec(context: ?*anyopaque, data: []const []u8) Error!usize { | |
| 204 | _ = context; | |
| 205 | _ = data; | |
| 206 | return error.EndOfStream; | |
| 207 | } | |
| 208 | ||
| 209 | fn endingDiscard(context: ?*anyopaque, limit: Limit) Error!usize { | |
| 210 | _ = context; | |
| 211 | _ = limit; | |
| 212 | return error.EndOfStream; | |
| 213 | } | |
| 214 | ||
| 215 | fn failingRead(context: ?*anyopaque, bw: *BufferedWriter, limit: Limit) RwError!usize { | |
| 216 | _ = context; | |
| 217 | _ = bw; | |
| 218 | _ = limit; | |
| 219 | return error.ReadFailed; | |
| 220 | } | |
| 221 | ||
| 222 | fn failingReadVec(context: ?*anyopaque, data: []const []u8) Error!usize { | |
| 223 | _ = context; | |
| 224 | _ = data; | |
| 225 | return error.ReadFailed; | |
| 226 | } | |
| 227 | ||
| 228 | fn failingDiscard(context: ?*anyopaque, limit: Limit) Error!usize { | |
| 229 | _ = context; | |
| 230 | _ = limit; | |
| 231 | return error.ReadFailed; | |
| 140 | 232 | } |
| 141 | 233 | |
| 142 | 234 | test "readAlloc when the backing reader provides one byte at a time" { |
| ... | ... | @@ -144,7 +236,7 @@ test "readAlloc when the backing reader provides one byte at a time" { |
| 144 | 236 | str: []const u8, |
| 145 | 237 | curr: usize, |
| 146 | 238 | |
| 147 | fn read(self: *@This(), dest: []u8) anyerror!usize { | |
| 239 | fn read(self: *@This(), dest: []u8) usize { | |
| 148 | 240 | if (self.str.len <= self.curr or dest.len == 0) |
| 149 | 241 | return 0; |
| 150 | 242 |
lib/std/io/Writer.zig+41-63| ... | ... | @@ -2,6 +2,8 @@ const std = @import("../std.zig"); |
| 2 | 2 | const assert = std.debug.assert; |
| 3 | 3 | const Writer = @This(); |
| 4 | 4 | |
| 5 | pub const Null = @import("Writer/Null.zig"); | |
| 6 | ||
| 5 | 7 | context: ?*anyopaque, |
| 6 | 8 | vtable: *const VTable, |
| 7 | 9 | |
| ... | ... | @@ -16,8 +18,8 @@ pub const VTable = struct { |
| 16 | 18 | /// |
| 17 | 19 | /// Number of bytes returned may be zero, which does not mean |
| 18 | 20 | /// 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, | |
| 21 | 23 | |
| 22 | 24 | /// Writes contents from an open file. `headers` are written first, then `len` |
| 23 | 25 | /// bytes of `file` starting from `offset`, then `trailers`. |
| ... | ... | @@ -27,7 +29,7 @@ pub const VTable = struct { |
| 27 | 29 | /// |
| 28 | 30 | /// Number of bytes returned may be zero, which does not mean |
| 29 | 31 | /// end-of-stream. A subsequent call may return nonzero, or may signal end |
| 30 | /// of stream via an error. | |
| 32 | /// of stream via `error.WriteFailed`. | |
| 31 | 33 | writeFile: *const fn ( |
| 32 | 34 | ctx: ?*anyopaque, |
| 33 | 35 | file: std.fs.File, |
| ... | ... | @@ -37,12 +39,19 @@ pub const VTable = struct { |
| 37 | 39 | /// Maximum amount of bytes to read from the file. |
| 38 | 40 | limit: Limit, |
| 39 | 41 | /// Headers and trailers must be passed together so that in case `len` is |
| 40 | /// zero, they can be forwarded directly to `VTable.writev`. | |
| 42 | /// zero, they can be forwarded directly to `VTable.writeVec`. | |
| 41 | 43 | headers_and_trailers: []const []const u8, |
| 42 | 44 | headers_len: usize, |
| 43 | ) anyerror!usize, | |
| 45 | ) FileError!usize, | |
| 46 | }; | |
| 47 | ||
| 48 | pub const Error = error{ | |
| 49 | /// See the `Writer` implementation for detailed diagnostics. | |
| 50 | WriteFailed, | |
| 44 | 51 | }; |
| 45 | 52 | |
| 53 | pub const FileError = Error || std.fs.File.PReadError; | |
| 54 | ||
| 46 | 55 | pub const Limit = std.io.Reader.Limit; |
| 47 | 56 | |
| 48 | 57 | pub const Offset = enum(u64) { |
| ... | ... | @@ -69,11 +78,11 @@ pub const Offset = enum(u64) { |
| 69 | 78 | } |
| 70 | 79 | }; |
| 71 | 80 | |
| 72 | pub fn writev(w: Writer, data: []const []const u8) anyerror!usize { | |
| 81 | pub fn writeVec(w: Writer, data: []const []const u8) Error!usize { | |
| 73 | 82 | return w.vtable.writeSplat(w.context, data, 1); |
| 74 | 83 | } |
| 75 | 84 | |
| 76 | pub fn writeSplat(w: Writer, data: []const []const u8, splat: usize) anyerror!usize { | |
| 85 | pub fn writeSplat(w: Writer, data: []const []const u8, splat: usize) Error!usize { | |
| 77 | 86 | return w.vtable.writeSplat(w.context, data, splat); |
| 78 | 87 | } |
| 79 | 88 | |
| ... | ... | @@ -84,27 +93,10 @@ pub fn writeFile( |
| 84 | 93 | limit: Limit, |
| 85 | 94 | headers_and_trailers: []const []const u8, |
| 86 | 95 | headers_len: usize, |
| 87 | ) anyerror!usize { | |
| 96 | ) FileError!usize { | |
| 88 | 97 | return w.vtable.writeFile(w.context, file, offset, limit, headers_and_trailers, headers_len); |
| 89 | 98 | } |
| 90 | 99 | |
| 91 | pub 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 | ||
| 108 | 100 | pub fn buffered(w: Writer, buffer: []u8) std.io.BufferedWriter { |
| 109 | 101 | return .{ |
| 110 | 102 | .buffer = buffer, |
| ... | ... | @@ -116,52 +108,38 @@ pub fn unbuffered(w: Writer) std.io.BufferedWriter { |
| 116 | 108 | return w.buffered(&.{}); |
| 117 | 109 | } |
| 118 | 110 | |
| 119 | /// A `Writer` that discards all data. | |
| 120 | pub const @"null": Writer = .{ | |
| 121 | .context = undefined, | |
| 122 | .vtable = &.{ | |
| 123 | .writeSplat = null_writeSplat, | |
| 124 | .writeFile = null_writeFile, | |
| 125 | }, | |
| 126 | }; | |
| 127 | ||
| 128 | fn null_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 111 | pub fn failingWriteSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) Error!usize { | |
| 129 | 112 | _ = 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; | |
| 135 | 116 | } |
| 136 | 117 | |
| 137 | fn null_writeFile( | |
| 118 | pub fn failingWriteFile( | |
| 138 | 119 | context: ?*anyopaque, |
| 139 | 120 | file: std.fs.File, |
| 140 | offset: Offset, | |
| 141 | limit: Limit, | |
| 121 | offset: std.io.Writer.Offset, | |
| 122 | limit: std.io.Writer.Limit, | |
| 142 | 123 | headers_and_trailers: []const []const u8, |
| 143 | 124 | headers_len: usize, |
| 144 | ) anyerror!usize { | |
| 125 | ) Error!usize { | |
| 145 | 126 | _ = 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; | |
| 163 | 133 | } |
| 164 | 134 | |
| 165 | test @"null" { | |
| 166 | try @"null".writeAll("yay"); | |
| 135 | pub const failing: Writer = .{ | |
| 136 | .context = undefined, | |
| 137 | .vtable = &.{ | |
| 138 | .writeSplat = failingWriteSplat, | |
| 139 | .writeFile = failingWriteFile, | |
| 140 | }, | |
| 141 | }; | |
| 142 | ||
| 143 | test { | |
| 144 | _ = Null; | |
| 167 | 145 | } |
lib/std/io/Writer/Null.zig created+66| ... | ... | @@ -0,0 +1,66 @@ |
| 1 | //! A `Writer` that discards all data. | |
| 2 | ||
| 3 | const std = @import("../../std.zig"); | |
| 4 | const Writer = std.io.Writer; | |
| 5 | ||
| 6 | const NullWriter = @This(); | |
| 7 | ||
| 8 | err: Error, | |
| 9 | ||
| 10 | pub const Error = std.fs.File.StatError; | |
| 11 | ||
| 12 | pub fn writer(nw: *NullWriter) Writer { | |
| 13 | return .{ | |
| 14 | .context = nw, | |
| 15 | .vtable = &.{ | |
| 16 | .writeSplat = writeSplat, | |
| 17 | .writeFile = writeFile, | |
| 18 | }, | |
| 19 | }; | |
| 20 | } | |
| 21 | ||
| 22 | fn 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 | ||
| 31 | fn 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 | ||
| 62 | test "writing a small string" { | |
| 63 | var nw: NullWriter = undefined; | |
| 64 | var bw = nw.writer().unbuffered(); | |
| 65 | try bw.writeAll("yay"); | |
| 66 | } |
lib/std/io/tty.zig+3-1| ... | ... | @@ -71,7 +71,9 @@ pub const Config = union(enum) { |
| 71 | 71 | reset_attributes: u16, |
| 72 | 72 | }; |
| 73 | 73 | |
| 74 | pub fn setColor(conf: Config, bw: *std.io.BufferedWriter, color: Color) anyerror!void { | |
| 74 | pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || std.io.Writer.Error; | |
| 75 | ||
| 76 | pub fn setColor(conf: Config, bw: *std.io.BufferedWriter, color: Color) SetColorError!void { | |
| 75 | 77 | nosuspend switch (conf) { |
| 76 | 78 | .no_color => return, |
| 77 | 79 | .escape_codes => { |
lib/std/json/Stringify.zig+16-14| ... | ... | @@ -77,7 +77,9 @@ const safety_checks: @TypeOf(safety_checks_hint) = if (build_mode_has_safety) |
| 77 | 77 | else |
| 78 | 78 | .assumed_correct; |
| 79 | 79 | |
| 80 | pub fn beginArray(self: *Stringify) anyerror!void { | |
| 80 | pub const Error = std.io.Writer.Error; | |
| 81 | ||
| 82 | pub fn beginArray(self: *Stringify) Error!void { | |
| 81 | 83 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); |
| 82 | 84 | try self.valueStart(); |
| 83 | 85 | try self.writer.writeByte('['); |
| ... | ... | @@ -85,7 +87,7 @@ pub fn beginArray(self: *Stringify) anyerror!void { |
| 85 | 87 | self.next_punctuation = .none; |
| 86 | 88 | } |
| 87 | 89 | |
| 88 | pub fn beginObject(self: *Stringify) anyerror!void { | |
| 90 | pub fn beginObject(self: *Stringify) Error!void { | |
| 89 | 91 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); |
| 90 | 92 | try self.valueStart(); |
| 91 | 93 | try self.writer.writeByte('{'); |
| ... | ... | @@ -93,7 +95,7 @@ pub fn beginObject(self: *Stringify) anyerror!void { |
| 93 | 95 | self.next_punctuation = .none; |
| 94 | 96 | } |
| 95 | 97 | |
| 96 | pub fn endArray(self: *Stringify) anyerror!void { | |
| 98 | pub fn endArray(self: *Stringify) Error!void { | |
| 97 | 99 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); |
| 98 | 100 | self.popIndentation(.array); |
| 99 | 101 | switch (self.next_punctuation) { |
| ... | ... | @@ -107,7 +109,7 @@ pub fn endArray(self: *Stringify) anyerror!void { |
| 107 | 109 | self.valueDone(); |
| 108 | 110 | } |
| 109 | 111 | |
| 110 | pub fn endObject(self: *Stringify) anyerror!void { | |
| 112 | pub fn endObject(self: *Stringify) Error!void { | |
| 111 | 113 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); |
| 112 | 114 | self.popIndentation(.object); |
| 113 | 115 | switch (self.next_punctuation) { |
| ... | ... | @@ -213,7 +215,7 @@ fn isComplete(self: *const Stringify) bool { |
| 213 | 215 | /// assuming the resulting formatted string represents a single complete value; |
| 214 | 216 | /// e.g. `"1"`, `"[]"`, `"[1,2]"`, not `"1,2"`. |
| 215 | 217 | /// This function may be useful for doing your own number formatting. |
| 216 | pub fn print(self: *Stringify, comptime fmt: []const u8, args: anytype) anyerror!void { | |
| 218 | pub fn print(self: *Stringify, comptime fmt: []const u8, args: anytype) Error!void { | |
| 217 | 219 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); |
| 218 | 220 | try self.valueStart(); |
| 219 | 221 | try self.writer.print(fmt, args); |
| ... | ... | @@ -274,7 +276,7 @@ pub fn endWriteRaw(self: *Stringify) void { |
| 274 | 276 | /// `key` is the string content of the property name. |
| 275 | 277 | /// Surrounding quotes will be added and any special characters will be escaped. |
| 276 | 278 | /// See also `objectFieldRaw`. |
| 277 | pub fn objectField(self: *Stringify, key: []const u8) anyerror!void { | |
| 279 | pub fn objectField(self: *Stringify, key: []const u8) Error!void { | |
| 278 | 280 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); |
| 279 | 281 | try self.objectFieldStart(); |
| 280 | 282 | try encodeJsonString(key, self.options, self.writer); |
| ... | ... | @@ -284,7 +286,7 @@ pub fn objectField(self: *Stringify, key: []const u8) anyerror!void { |
| 284 | 286 | /// `quoted_key` is the complete bytes of the key including quotes and any necessary escape sequences. |
| 285 | 287 | /// A few assertions are performed on the given value to ensure that the caller of this function understands the API contract. |
| 286 | 288 | /// See also `objectField`. |
| 287 | pub fn objectFieldRaw(self: *Stringify, quoted_key: []const u8) anyerror!void { | |
| 289 | pub fn objectFieldRaw(self: *Stringify, quoted_key: []const u8) Error!void { | |
| 288 | 290 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); |
| 289 | 291 | assert(quoted_key.len >= 2 and quoted_key[0] == '"' and quoted_key[quoted_key.len - 1] == '"'); // quoted_key should be "quoted". |
| 290 | 292 | try self.objectFieldStart(); |
| ... | ... | @@ -343,7 +345,7 @@ pub fn endObjectFieldRaw(self: *Stringify) void { |
| 343 | 345 | /// |
| 344 | 346 | /// See also alternative functions `print` and `beginWriteRaw`. |
| 345 | 347 | /// For writing object field names, use `objectField` instead. |
| 346 | pub fn write(self: *Stringify, v: anytype) anyerror!void { | |
| 348 | pub fn write(self: *Stringify, v: anytype) Error!void { | |
| 347 | 349 | if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); |
| 348 | 350 | const T = @TypeOf(v); |
| 349 | 351 | switch (@typeInfo(T)) { |
| ... | ... | @@ -568,7 +570,7 @@ pub const Options = struct { |
| 568 | 570 | /// Writes the given value to the `std.io.Writer` writer. |
| 569 | 571 | /// See `Stringify` for how the given value is serialized into JSON. |
| 570 | 572 | /// The maximum nesting depth of the output JSON document is 256. |
| 571 | pub fn value(v: anytype, options: Options, writer: *std.io.BufferedWriter) anyerror!void { | |
| 573 | pub fn value(v: anytype, options: Options, writer: *std.io.BufferedWriter) Error!void { | |
| 572 | 574 | var s: Stringify = .{ .writer = writer, .options = options }; |
| 573 | 575 | try s.write(v); |
| 574 | 576 | } |
| ... | ... | @@ -632,7 +634,7 @@ test valueAlloc { |
| 632 | 634 | try std.testing.expectEqualStrings(expected, actual); |
| 633 | 635 | } |
| 634 | 636 | |
| 635 | fn outputUnicodeEscape(codepoint: u21, bw: *std.io.BufferedWriter) anyerror!void { | |
| 637 | fn outputUnicodeEscape(codepoint: u21, bw: *std.io.BufferedWriter) Error!void { | |
| 636 | 638 | if (codepoint <= 0xFFFF) { |
| 637 | 639 | // If the character is in the Basic Multilingual Plane (U+0000 through U+FFFF), |
| 638 | 640 | // then it may be represented as a six-character sequence: a reverse solidus, followed |
| ... | ... | @@ -652,7 +654,7 @@ fn outputUnicodeEscape(codepoint: u21, bw: *std.io.BufferedWriter) anyerror!void |
| 652 | 654 | } |
| 653 | 655 | } |
| 654 | 656 | |
| 655 | fn outputSpecialEscape(c: u8, writer: *std.io.BufferedWriter) anyerror!void { | |
| 657 | fn outputSpecialEscape(c: u8, writer: *std.io.BufferedWriter) Error!void { | |
| 656 | 658 | switch (c) { |
| 657 | 659 | '\\' => try writer.writeAll("\\\\"), |
| 658 | 660 | '\"' => try writer.writeAll("\\\""), |
| ... | ... | @@ -666,14 +668,14 @@ fn outputSpecialEscape(c: u8, writer: *std.io.BufferedWriter) anyerror!void { |
| 666 | 668 | } |
| 667 | 669 | |
| 668 | 670 | /// Write `string` to `writer` as a JSON encoded string. |
| 669 | pub fn encodeJsonString(string: []const u8, options: Options, writer: *std.io.BufferedWriter) anyerror!void { | |
| 671 | pub fn encodeJsonString(string: []const u8, options: Options, writer: *std.io.BufferedWriter) Error!void { | |
| 670 | 672 | try writer.writeByte('\"'); |
| 671 | 673 | try encodeJsonStringChars(string, options, writer); |
| 672 | 674 | try writer.writeByte('\"'); |
| 673 | 675 | } |
| 674 | 676 | |
| 675 | 677 | /// Write `chars` to `writer` as JSON encoded string characters. |
| 676 | pub fn encodeJsonStringChars(chars: []const u8, options: Options, writer: *std.io.BufferedWriter) anyerror!void { | |
| 678 | pub fn encodeJsonStringChars(chars: []const u8, options: Options, writer: *std.io.BufferedWriter) Error!void { | |
| 677 | 679 | var write_cursor: usize = 0; |
| 678 | 680 | var i: usize = 0; |
| 679 | 681 | if (options.escape_unicode) { |
| ... | ... | @@ -722,7 +724,7 @@ test "json write stream" { |
| 722 | 724 | try testBasicWriteStream(&w); |
| 723 | 725 | } |
| 724 | 726 | |
| 725 | fn testBasicWriteStream(w: *Stringify) anyerror!void { | |
| 727 | fn testBasicWriteStream(w: *Stringify) Error!void { | |
| 726 | 728 | w.writer.reset(); |
| 727 | 729 | |
| 728 | 730 | try w.beginObject(); |
lib/std/json/dynamic.zig+3-3| ... | ... | @@ -51,10 +51,10 @@ pub const Value = union(enum) { |
| 51 | 51 | } |
| 52 | 52 | |
| 53 | 53 | 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(); | |
| 56 | 56 | |
| 57 | json.Stringify.value(v, .{}, &bw) catch return; | |
| 57 | json.Stringify.value(v, .{}, bw) catch return; | |
| 58 | 58 | } |
| 59 | 59 | |
| 60 | 60 | pub fn jsonStringify(value: @This(), jws: anytype) !void { |
lib/std/log.zig+3-6| ... | ... | @@ -149,12 +149,9 @@ pub fn defaultLog( |
| 149 | 149 | const level_txt = comptime message_level.asText(); |
| 150 | 150 | const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; |
| 151 | 151 | 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; | |
| 158 | 155 | } |
| 159 | 156 | |
| 160 | 157 | /// Returns a scoped logging namespace that logs all messages using the scope |
lib/std/math/big/int.zig+1-1| ... | ... | @@ -2322,7 +2322,7 @@ pub const Const = struct { |
| 2322 | 2322 | /// this function will fail to print the string, printing "(BigInt)" instead of a number. |
| 2323 | 2323 | /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory. |
| 2324 | 2324 | /// See `toString` and `toStringAlloc` for a way to print big integers without failure. |
| 2325 | pub fn format(self: Const, bw: *std.io.BufferedWriter, comptime fmt: []const u8) anyerror!void { | |
| 2325 | pub fn format(self: Const, bw: *std.io.BufferedWriter, comptime fmt: []const u8) !void { | |
| 2326 | 2326 | comptime var base = 10; |
| 2327 | 2327 | comptime var case: std.fmt.Case = .lower; |
| 2328 | 2328 |
lib/std/net.zig+7-7| ... | ... | @@ -850,8 +850,8 @@ pub fn tcpConnectToAddress(address: Address) TcpConnectToAddressError!Stream { |
| 850 | 850 | } |
| 851 | 851 | |
| 852 | 852 | // 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. | |
| 854 | const 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. | |
| 854 | const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || posix.SocketError || posix.BindError || posix.SetSockOptError || error{ | |
| 855 | 855 | TemporaryNameServerFailure, |
| 856 | 856 | NameServerFailure, |
| 857 | 857 | AddressFamilyNotSupported, |
| ... | ... | @@ -1873,14 +1873,14 @@ pub const Stream = struct { |
| 1873 | 1873 | context: ?*anyopaque, |
| 1874 | 1874 | bw: *std.io.BufferedWriter, |
| 1875 | 1875 | limit: std.io.Reader.Limit, |
| 1876 | ) anyerror!std.io.Reader.Status { | |
| 1876 | ) std.io.Reader.Error!usize { | |
| 1877 | 1877 | const buf = limit.slice(try bw.writableSlice(1)); |
| 1878 | 1878 | const status = try windows_readv(context, &.{buf}); |
| 1879 | 1879 | bw.advance(status.len); |
| 1880 | 1880 | return status; |
| 1881 | 1881 | } |
| 1882 | 1882 | |
| 1883 | fn windows_readv(context: ?*anyopaque, data: []const []u8) anyerror!std.io.Reader.Status { | |
| 1883 | fn windows_readv(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | |
| 1884 | 1884 | var iovecs: [max_buffers_len]windows.WSABUF = undefined; |
| 1885 | 1885 | var iovecs_i: usize = 0; |
| 1886 | 1886 | for (data) |d| { |
| ... | ... | @@ -1915,7 +1915,7 @@ pub const Stream = struct { |
| 1915 | 1915 | return .{ .len = n, .end = n == 0 }; |
| 1916 | 1916 | } |
| 1917 | 1917 | |
| 1918 | fn windows_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 1918 | fn windows_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { | |
| 1919 | 1919 | comptime assert(native_os == .windows); |
| 1920 | 1920 | if (data.len == 1 and splat == 0) return 0; |
| 1921 | 1921 | var splat_buffer: [256]u8 = undefined; |
| ... | ... | @@ -1974,7 +1974,7 @@ pub const Stream = struct { |
| 1974 | 1974 | return n; |
| 1975 | 1975 | } |
| 1976 | 1976 | |
| 1977 | fn posix_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) anyerror!usize { | |
| 1977 | fn posix_writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { | |
| 1978 | 1978 | const sock_fd = opaqueToHandle(context); |
| 1979 | 1979 | comptime assert(native_os != .windows); |
| 1980 | 1980 | var splat_buffer: [256]u8 = undefined; |
| ... | ... | @@ -2028,7 +2028,7 @@ pub const Stream = struct { |
| 2028 | 2028 | in_len: std.io.Writer.FileLen, |
| 2029 | 2029 | headers_and_trailers: []const []const u8, |
| 2030 | 2030 | headers_len: usize, |
| 2031 | ) anyerror!usize { | |
| 2031 | ) std.io.Writer.FileError!usize { | |
| 2032 | 2032 | const len_int = switch (in_len) { |
| 2033 | 2033 | .zero => return windows_writeSplat(context, headers_and_trailers, 1), |
| 2034 | 2034 | .entire_file => std.math.maxInt(usize), |
lib/std/tar.zig+1-1| ... | ... | @@ -603,7 +603,7 @@ fn PaxIterator(comptime ReaderType: type) type { |
| 603 | 603 | return null; |
| 604 | 604 | } |
| 605 | 605 | |
| 606 | fn readUntil(self: *Self, delimiter: u8) anyerror![]const u8 { | |
| 606 | fn readUntil(self: *Self, delimiter: u8) ![]const u8 { | |
| 607 | 607 | var fbs: std.io.BufferedWriter = undefined; |
| 608 | 608 | fbs.initFixed(&self.scratch); |
| 609 | 609 | try self.reader.streamUntilDelimiter(&fbs, delimiter, null); |
lib/std/testing.zig+4-4| ... | ... | @@ -390,8 +390,8 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const |
| 390 | 390 | const actual_window = actual[window_start..@min(actual.len, window_start + max_window_size)]; |
| 391 | 391 | const actual_truncated = window_start + actual_window.len < actual.len; |
| 392 | 392 | |
| 393 | var bw = std.debug.lockStdErr2(&.{}); | |
| 394 | defer std.debug.unlockStdErr(); | |
| 393 | const bw = std.debug.lockStderrWriter(&.{}); | |
| 394 | defer std.debug.unlockStderrWriter(); | |
| 395 | 395 | const ttyconf = std.io.tty.detectConfig(.stderr()); |
| 396 | 396 | var differ = if (T == u8) BytesDiffer{ |
| 397 | 397 | .expected = expected_window, |
| ... | ... | @@ -416,7 +416,7 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const |
| 416 | 416 | print("... truncated ...\n", .{}); |
| 417 | 417 | } |
| 418 | 418 | } |
| 419 | differ.write(&bw) catch {}; | |
| 419 | differ.write(bw) catch {}; | |
| 420 | 420 | if (expected_truncated) { |
| 421 | 421 | const end_offset = window_start + expected_window.len; |
| 422 | 422 | const num_missing_items = expected.len - (window_start + expected_window.len); |
| ... | ... | @@ -438,7 +438,7 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const |
| 438 | 438 | print("... truncated ...\n", .{}); |
| 439 | 439 | } |
| 440 | 440 | } |
| 441 | differ.write(&bw) catch {}; | |
| 441 | differ.write(bw) catch {}; | |
| 442 | 442 | if (actual_truncated) { |
| 443 | 443 | const end_offset = window_start + actual_window.len; |
| 444 | 444 | const num_missing_items = actual.len - (window_start + actual_window.len); |
lib/std/zig/Ast.zig+2-2| ... | ... | @@ -207,7 +207,7 @@ pub fn renderAlloc(tree: Ast, gpa: Allocator) RenderError![]u8 { |
| 207 | 207 | return aw.toOwnedSlice(); |
| 208 | 208 | } |
| 209 | 209 | |
| 210 | pub fn render(tree: Ast, gpa: Allocator, bw: *std.io.BufferedWriter, fixups: Fixups) anyerror!void { | |
| 210 | pub fn render(tree: Ast, gpa: Allocator, bw: *std.io.BufferedWriter, fixups: Fixups) RenderError!void { | |
| 211 | 211 | return @import("./render.zig").renderTree(gpa, bw, tree, fixups); |
| 212 | 212 | } |
| 213 | 213 | |
| ... | ... | @@ -315,7 +315,7 @@ pub fn rootDecls(tree: Ast) []const Node.Index { |
| 315 | 315 | } |
| 316 | 316 | } |
| 317 | 317 | |
| 318 | pub fn renderError(tree: Ast, parse_error: Error, bw: *std.io.BufferedWriter) anyerror!void { | |
| 318 | pub fn renderError(tree: Ast, parse_error: Error, bw: *std.io.BufferedWriter) std.io.Writer.Error!void { | |
| 319 | 319 | switch (parse_error.tag) { |
| 320 | 320 | .asterisk_after_ptr_deref => { |
| 321 | 321 | // Note that the token will point at the `.*` but ideally the source |
lib/std/zig/ErrorBundle.zig+5-6| ... | ... | @@ -158,13 +158,12 @@ pub const RenderOptions = struct { |
| 158 | 158 | |
| 159 | 159 | pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void { |
| 160 | 160 | 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; | |
| 165 | 164 | } |
| 166 | 165 | |
| 167 | pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, bw: *std.io.BufferedWriter) anyerror!void { | |
| 166 | pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, bw: *std.io.BufferedWriter) std.io.Writer.Error!void { | |
| 168 | 167 | if (eb.extra.len == 0) return; |
| 169 | 168 | for (eb.getMessages()) |err_msg| { |
| 170 | 169 | try renderErrorMessageToWriter(eb, options, err_msg, bw, "error", .red, 0); |
| ... | ... | @@ -187,7 +186,7 @@ fn renderErrorMessageToWriter( |
| 187 | 186 | kind: []const u8, |
| 188 | 187 | color: std.io.tty.Color, |
| 189 | 188 | indent: usize, |
| 190 | ) anyerror!void { | |
| 189 | ) std.io.Writer.Error!void { | |
| 191 | 190 | const ttyconf = options.ttyconf; |
| 192 | 191 | const err_msg = eb.getErrorMessage(err_msg_index); |
| 193 | 192 | const prefix_start = bw.count; |
lib/std/zig/Server.zig+42-145| ... | ... | @@ -1,6 +1,5 @@ |
| 1 | in: std.fs.File, | |
| 2 | out: std.fs.File, | |
| 3 | receive_fifo: std.fifo.LinearFifo(u8, .Dynamic), | |
| 1 | in: *std.io.BufferedReader, | |
| 2 | out: *std.io.BufferedWriter, | |
| 4 | 3 | |
| 5 | 4 | pub const Message = struct { |
| 6 | 5 | pub const Header = extern struct { |
| ... | ... | @@ -94,9 +93,8 @@ pub const Message = struct { |
| 94 | 93 | }; |
| 95 | 94 | |
| 96 | 95 | pub 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, | |
| 100 | 98 | zig_version: []const u8, |
| 101 | 99 | }; |
| 102 | 100 | |
| ... | ... | @@ -104,96 +102,40 @@ pub fn init(options: Options) !Server { |
| 104 | 102 | var s: Server = .{ |
| 105 | 103 | .in = options.in, |
| 106 | 104 | .out = options.out, |
| 107 | .receive_fifo = std.fifo.LinearFifo(u8, .Dynamic).init(options.gpa), | |
| 108 | 105 | }; |
| 109 | 106 | try s.serveStringMessage(.zig_version, options.zig_version); |
| 110 | 107 | return s; |
| 111 | 108 | } |
| 112 | 109 | |
| 113 | pub fn deinit(s: *Server) void { | |
| 114 | s.receive_fifo.deinit(); | |
| 115 | s.* = undefined; | |
| 116 | } | |
| 117 | ||
| 118 | 110 | pub 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); | |
| 154 | 112 | } |
| 155 | 113 | |
| 156 | 114 | pub 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); | |
| 162 | 116 | } |
| 163 | 117 | |
| 164 | 118 | pub fn serveStringMessage(s: *Server, tag: OutMessage.Tag, msg: []const u8) !void { |
| 165 | return s.serveMessage(.{ | |
| 119 | try s.serveMessageHeader(.{ | |
| 166 | 120 | .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(); | |
| 169 | 125 | } |
| 170 | 126 | |
| 171 | pub 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! | |
| 128 | pub fn serveMessageHeader(s: *const Server, header: OutMessage.Header) !void { | |
| 129 | try s.out.writeStructEndian(header, .little); | |
| 189 | 130 | } |
| 190 | 131 | |
| 191 | pub fn serveU64Message(s: *Server, tag: OutMessage.Tag, int: u64) !void { | |
| 192 | const msg_le = bswap(int); | |
| 193 | return s.serveMessage(.{ | |
| 132 | pub fn serveU64Message(s: *const Server, tag: OutMessage.Tag, int: u64) !void { | |
| 133 | try serveMessageHeader(s, .{ | |
| 194 | 134 | .tag = tag, |
| 195 | 135 | .bytes_len = @sizeOf(u64), |
| 196 | }, &.{std.mem.asBytes(&msg_le)}); | |
| 136 | }); | |
| 137 | try s.out.writeInt(u64, int, .little); | |
| 138 | try s.out.flush(); | |
| 197 | 139 | } |
| 198 | 140 | |
| 199 | 141 | pub fn serveEmitDigest( |
| ... | ... | @@ -201,26 +143,22 @@ pub fn serveEmitDigest( |
| 201 | 143 | digest: *const [Cache.bin_digest_len]u8, |
| 202 | 144 | header: OutMessage.EmitDigest, |
| 203 | 145 | ) !void { |
| 204 | try s.serveMessage(.{ | |
| 146 | try s.serveMessageHeader(.{ | |
| 205 | 147 | .tag = .emit_digest, |
| 206 | 148 | .bytes_len = @intCast(digest.len + @sizeOf(OutMessage.EmitDigest)), |
| 207 | }, &.{ | |
| 208 | std.mem.asBytes(&header), | |
| 209 | digest, | |
| 210 | 149 | }); |
| 150 | try s.out.writeStructEndian(header, .little); | |
| 151 | try s.out.writeAll(digest); | |
| 152 | try s.out.flush(); | |
| 211 | 153 | } |
| 212 | 154 | |
| 213 | pub fn serveTestResults( | |
| 214 | s: *Server, | |
| 215 | msg: OutMessage.TestResults, | |
| 216 | ) !void { | |
| 217 | const msg_le = bswap(msg); | |
| 218 | try s.serveMessage(.{ | |
| 155 | pub fn serveTestResults(s: *Server, msg: OutMessage.TestResults) !void { | |
| 156 | try s.serveMessageHeader(.{ | |
| 219 | 157 | .tag = .test_results, |
| 220 | 158 | .bytes_len = @intCast(@sizeOf(OutMessage.TestResults)), |
| 221 | }, &.{ | |
| 222 | std.mem.asBytes(&msg_le), | |
| 223 | 159 | }); |
| 160 | try s.out.writeStructEndian(msg, .little); | |
| 161 | try s.out.flush(); | |
| 224 | 162 | } |
| 225 | 163 | |
| 226 | 164 | pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void { |
| ... | ... | @@ -230,81 +168,40 @@ pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void { |
| 230 | 168 | }; |
| 231 | 169 | const bytes_len = @sizeOf(OutMessage.ErrorBundle) + |
| 232 | 170 | 4 * error_bundle.extra.len + error_bundle.string_bytes.len; |
| 233 | try s.serveMessage(.{ | |
| 171 | try s.serveMessageHeader(.{ | |
| 234 | 172 | .tag = .error_bundle, |
| 235 | 173 | .bytes_len = @intCast(bytes_len), |
| 236 | }, &.{ | |
| 237 | std.mem.asBytes(&eb_hdr), | |
| 238 | // TODO: implement @ptrCast between slices changing the length | |
| 239 | std.mem.sliceAsBytes(error_bundle.extra), | |
| 240 | error_bundle.string_bytes, | |
| 241 | 174 | }); |
| 175 | try s.out.writeStructEndian(eb_hdr, .little); | |
| 176 | try s.out.writeArrayEndian(u32, error_bundle.extra, .little); | |
| 177 | try s.out.writeAll(error_bundle.string_bytes); | |
| 178 | try s.out.flush(); | |
| 242 | 179 | } |
| 243 | 180 | |
| 244 | 181 | pub const TestMetadata = struct { |
| 245 | names: []u32, | |
| 246 | expected_panic_msgs: []u32, | |
| 182 | names: []const u32, | |
| 183 | expected_panic_msgs: []const u32, | |
| 247 | 184 | string_bytes: []const u8, |
| 248 | 185 | }; |
| 249 | 186 | |
| 250 | 187 | pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void { |
| 251 | 188 | 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)), | |
| 254 | 191 | }; |
| 255 | 192 | const trailing = 2; |
| 256 | 193 | const bytes_len = @sizeOf(OutMessage.TestMetadata) + |
| 257 | 194 | trailing * @sizeOf(u32) * test_metadata.names.len + test_metadata.string_bytes.len; |
| 258 | 195 | |
| 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(.{ | |
| 269 | 197 | .tag = .test_metadata, |
| 270 | 198 | .bytes_len = @intCast(bytes_len), |
| 271 | }, &.{ | |
| 272 | std.mem.asBytes(&header), | |
| 273 | // TODO: implement @ptrCast between slices changing the length | |
| 274 | std.mem.sliceAsBytes(test_metadata.names), | |
| 275 | std.mem.sliceAsBytes(test_metadata.expected_panic_msgs), | |
| 276 | test_metadata.string_bytes, | |
| 277 | 199 | }); |
| 278 | } | |
| 279 | ||
| 280 | fn 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 | ||
| 305 | fn 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(); | |
| 308 | 205 | } |
| 309 | 206 | |
| 310 | 207 | const OutMessage = std.zig.Server.Message; |
lib/std/zig/ZonGen.zig+1-1| ... | ... | @@ -520,7 +520,7 @@ pub fn parseStrLit( |
| 520 | 520 | tree: Ast, |
| 521 | 521 | node: Ast.Node.Index, |
| 522 | 522 | writer: *std.io.BufferedWriter, |
| 523 | ) anyerror!std.zig.string_literal.Result { | |
| 523 | ) error{OutOfMemory}!std.zig.string_literal.Result { | |
| 524 | 524 | switch (tree.nodeTag(node)) { |
| 525 | 525 | .string_literal => { |
| 526 | 526 | const token = tree.nodeMainToken(node); |
lib/std/zig/llvm/BitcodeReader.zig+3-3| ... | ... | @@ -170,7 +170,7 @@ pub fn next(bc: *BitcodeReader) !?Item { |
| 170 | 170 | } |
| 171 | 171 | } |
| 172 | 172 | |
| 173 | pub fn skipBlock(bc: *BitcodeReader, block: Block) anyerror!void { | |
| 173 | pub fn skipBlock(bc: *BitcodeReader, block: Block) !void { | |
| 174 | 174 | assert(bc.bit_offset == 0); |
| 175 | 175 | try bc.br.discard(4 * @as(u34, block.len)); |
| 176 | 176 | try bc.endBlock(); |
| ... | ... | @@ -369,12 +369,12 @@ fn align32Bits(bc: *BitcodeReader) void { |
| 369 | 369 | bc.bit_offset = 0; |
| 370 | 370 | } |
| 371 | 371 | |
| 372 | fn read32Bits(bc: *BitcodeReader) anyerror!u32 { | |
| 372 | fn read32Bits(bc: *BitcodeReader) !u32 { | |
| 373 | 373 | assert(bc.bit_offset == 0); |
| 374 | 374 | return bc.br.takeInt(u32, .little); |
| 375 | 375 | } |
| 376 | 376 | |
| 377 | fn readBytes(bc: *BitcodeReader, bytes: []u8) anyerror!void { | |
| 377 | fn readBytes(bc: *BitcodeReader, bytes: []u8) !void { | |
| 378 | 378 | assert(bc.bit_offset == 0); |
| 379 | 379 | try bc.br.read(bytes); |
| 380 | 380 |
lib/std/zig/llvm/Builder.zig+32-32| ... | ... | @@ -91,7 +91,7 @@ pub const String = enum(u32) { |
| 91 | 91 | string: String, |
| 92 | 92 | builder: *const Builder, |
| 93 | 93 | }; |
| 94 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) anyerror!void { | |
| 94 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) std.io.Writer.Error!void { | |
| 95 | 95 | if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_| |
| 96 | 96 | @compileError("invalid format string: '" ++ fmt_str ++ "'"); |
| 97 | 97 | assert(data.string != .none); |
| ... | ... | @@ -649,7 +649,7 @@ pub const Type = enum(u32) { |
| 649 | 649 | type: Type, |
| 650 | 650 | builder: *const Builder, |
| 651 | 651 | }; |
| 652 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) anyerror!void { | |
| 652 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) std.io.Writer.Error!void { | |
| 653 | 653 | assert(data.type != .none); |
| 654 | 654 | if (comptime std.mem.eql(u8, fmt_str, "m")) { |
| 655 | 655 | const item = data.builder.type_items.items[@intFromEnum(data.type)]; |
| ... | ... | @@ -1129,7 +1129,7 @@ pub const Attribute = union(Kind) { |
| 1129 | 1129 | attribute_index: Index, |
| 1130 | 1130 | builder: *const Builder, |
| 1131 | 1131 | }; |
| 1132 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) anyerror!void { | |
| 1132 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) std.io.Writer.Error!void { | |
| 1133 | 1133 | if (comptime std.mem.indexOfNone(u8, fmt_str, "\"#")) |_| |
| 1134 | 1134 | @compileError("invalid format string: '" ++ fmt_str ++ "'"); |
| 1135 | 1135 | const attribute = data.attribute_index.toAttribute(data.builder); |
| ... | ... | @@ -1568,7 +1568,7 @@ pub const Attributes = enum(u32) { |
| 1568 | 1568 | attributes: Attributes, |
| 1569 | 1569 | builder: *const Builder, |
| 1570 | 1570 | }; |
| 1571 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) anyerror!void { | |
| 1571 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) std.io.Writer.Error!void { | |
| 1572 | 1572 | for (data.attributes.slice(data.builder)) |attribute_index| try Attribute.Index.format(.{ |
| 1573 | 1573 | .attribute_index = attribute_index, |
| 1574 | 1574 | .builder = data.builder, |
| ... | ... | @@ -1761,11 +1761,11 @@ pub const Linkage = enum(u4) { |
| 1761 | 1761 | extern_weak = 7, |
| 1762 | 1762 | external = 0, |
| 1763 | 1763 | |
| 1764 | pub fn format(self: Linkage, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void { | |
| 1764 | pub fn format(self: Linkage, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void { | |
| 1765 | 1765 | if (self != .external) try bw.print(" {s}", .{@tagName(self)}); |
| 1766 | 1766 | } |
| 1767 | 1767 | |
| 1768 | fn formatOptional(data: ?Linkage, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void { | |
| 1768 | fn formatOptional(data: ?Linkage, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void { | |
| 1769 | 1769 | if (data) |linkage| try bw.print(" {s}", .{@tagName(linkage)}); |
| 1770 | 1770 | } |
| 1771 | 1771 | pub fn fmtOptional(self: ?Linkage) std.fmt.Formatter(formatOptional) { |
| ... | ... | @@ -1778,7 +1778,7 @@ pub const Preemption = enum { |
| 1778 | 1778 | dso_local, |
| 1779 | 1779 | implicit_dso_local, |
| 1780 | 1780 | |
| 1781 | pub fn format(self: Preemption, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void { | |
| 1781 | pub fn format(self: Preemption, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void { | |
| 1782 | 1782 | if (self == .dso_local) try bw.print(" {s}", .{@tagName(self)}); |
| 1783 | 1783 | } |
| 1784 | 1784 | }; |
| ... | ... | @@ -1799,8 +1799,8 @@ pub const Visibility = enum(u2) { |
| 1799 | 1799 | pub fn format( |
| 1800 | 1800 | self: Visibility, |
| 1801 | 1801 | comptime format_string: []const u8, |
| 1802 | writer: anytype, | |
| 1803 | ) @TypeOf(writer).Error!void { | |
| 1802 | writer: *std.io.BufferedWriter, | |
| 1803 | ) std.io.Writer.Error!void { | |
| 1804 | 1804 | comptime assert(format_string.len == 0); |
| 1805 | 1805 | if (self != .default) try writer.print(" {s}", .{@tagName(self)}); |
| 1806 | 1806 | } |
| ... | ... | @@ -1811,7 +1811,7 @@ pub const DllStorageClass = enum(u2) { |
| 1811 | 1811 | dllimport = 1, |
| 1812 | 1812 | dllexport = 2, |
| 1813 | 1813 | |
| 1814 | pub fn format(self: DllStorageClass, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void { | |
| 1814 | pub fn format(self: DllStorageClass, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void { | |
| 1815 | 1815 | if (self != .default) try bw.print(" {s}", .{@tagName(self)}); |
| 1816 | 1816 | } |
| 1817 | 1817 | }; |
| ... | ... | @@ -1823,7 +1823,7 @@ pub const ThreadLocal = enum(u3) { |
| 1823 | 1823 | initialexec = 3, |
| 1824 | 1824 | localexec = 4, |
| 1825 | 1825 | |
| 1826 | pub fn format(self: ThreadLocal, bw: *std.io.BufferedWriter, comptime prefix: []const u8) anyerror!void { | |
| 1826 | pub fn format(self: ThreadLocal, bw: *std.io.BufferedWriter, comptime prefix: []const u8) std.io.Writer.Error!void { | |
| 1827 | 1827 | if (self == .default) return; |
| 1828 | 1828 | try bw.print("{s}thread_local", .{prefix}); |
| 1829 | 1829 | if (self != .generaldynamic) try bw.print("({s})", .{@tagName(self)}); |
| ... | ... | @@ -1837,7 +1837,7 @@ pub const UnnamedAddr = enum(u2) { |
| 1837 | 1837 | unnamed_addr = 1, |
| 1838 | 1838 | local_unnamed_addr = 2, |
| 1839 | 1839 | |
| 1840 | pub fn format(self: UnnamedAddr, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void { | |
| 1840 | pub fn format(self: UnnamedAddr, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void { | |
| 1841 | 1841 | if (self != .default) try bw.print(" {s}", .{@tagName(self)}); |
| 1842 | 1842 | } |
| 1843 | 1843 | }; |
| ... | ... | @@ -1931,7 +1931,7 @@ pub const AddrSpace = enum(u24) { |
| 1931 | 1931 | pub const funcref: AddrSpace = @enumFromInt(20); |
| 1932 | 1932 | }; |
| 1933 | 1933 | |
| 1934 | pub fn format(self: AddrSpace, bw: *std.io.BufferedWriter, comptime prefix: []const u8) anyerror!void { | |
| 1934 | pub fn format(self: AddrSpace, bw: *std.io.BufferedWriter, comptime prefix: []const u8) std.io.Writer.Error!void { | |
| 1935 | 1935 | if (self != .default) try bw.print("{s}addrspace({d})", .{ prefix, @intFromEnum(self) }); |
| 1936 | 1936 | } |
| 1937 | 1937 | }; |
| ... | ... | @@ -1940,7 +1940,7 @@ pub const ExternallyInitialized = enum { |
| 1940 | 1940 | default, |
| 1941 | 1941 | externally_initialized, |
| 1942 | 1942 | |
| 1943 | pub fn format(self: ExternallyInitialized, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void { | |
| 1943 | pub fn format(self: ExternallyInitialized, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void { | |
| 1944 | 1944 | if (self != .default) try bw.print(" {s}", .{@tagName(self)}); |
| 1945 | 1945 | } |
| 1946 | 1946 | }; |
| ... | ... | @@ -1964,7 +1964,7 @@ pub const Alignment = enum(u6) { |
| 1964 | 1964 | return if (self == .default) 0 else (@intFromEnum(self) + 1); |
| 1965 | 1965 | } |
| 1966 | 1966 | |
| 1967 | pub fn format(self: Alignment, bw: *std.io.BufferedWriter, comptime prefix: []const u8) anyerror!void { | |
| 1967 | pub fn format(self: Alignment, bw: *std.io.BufferedWriter, comptime prefix: []const u8) std.io.Writer.Error!void { | |
| 1968 | 1968 | try bw.print("{s}align {d}", .{ prefix, self.toByteUnits() orelse return }); |
| 1969 | 1969 | } |
| 1970 | 1970 | }; |
| ... | ... | @@ -2038,7 +2038,7 @@ pub const CallConv = enum(u10) { |
| 2038 | 2038 | |
| 2039 | 2039 | pub const default = CallConv.ccc; |
| 2040 | 2040 | |
| 2041 | pub fn format(self: CallConv, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void { | |
| 2041 | pub fn format(self: CallConv, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void { | |
| 2042 | 2042 | switch (self) { |
| 2043 | 2043 | default => {}, |
| 2044 | 2044 | .fastcc, |
| ... | ... | @@ -2119,7 +2119,7 @@ pub const StrtabString = enum(u32) { |
| 2119 | 2119 | string: StrtabString, |
| 2120 | 2120 | builder: *const Builder, |
| 2121 | 2121 | }; |
| 2122 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) anyerror!void { | |
| 2122 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) std.io.Writer.Error!void { | |
| 2123 | 2123 | if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_| |
| 2124 | 2124 | @compileError("invalid format string: '" ++ fmt_str ++ "'"); |
| 2125 | 2125 | assert(data.string != .none); |
| ... | ... | @@ -2306,7 +2306,7 @@ pub const Global = struct { |
| 2306 | 2306 | global: Index, |
| 2307 | 2307 | builder: *const Builder, |
| 2308 | 2308 | }; |
| 2309 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void { | |
| 2309 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void { | |
| 2310 | 2310 | try bw.print("@{f}", .{ |
| 2311 | 2311 | data.global.unwrap(data.builder).name(data.builder).fmt(data.builder), |
| 2312 | 2312 | }); |
| ... | ... | @@ -4752,7 +4752,7 @@ pub const Function = struct { |
| 4752 | 4752 | function: Function.Index, |
| 4753 | 4753 | builder: *Builder, |
| 4754 | 4754 | }; |
| 4755 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) anyerror!void { | |
| 4755 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) std.io.Writer.Error!void { | |
| 4756 | 4756 | if (comptime std.mem.indexOfNone(u8, fmt_str, ", %")) |_| |
| 4757 | 4757 | @compileError("invalid format string: '" ++ fmt_str ++ "'"); |
| 4758 | 4758 | if (comptime std.mem.indexOfScalar(u8, fmt_str, ',') != null) { |
| ... | ... | @@ -6944,7 +6944,7 @@ pub const MemoryAccessKind = enum(u1) { |
| 6944 | 6944 | normal, |
| 6945 | 6945 | @"volatile", |
| 6946 | 6946 | |
| 6947 | pub fn format(self: MemoryAccessKind, bw: *std.io.BufferedWriter, comptime prefix: []const u8) anyerror!void { | |
| 6947 | pub fn format(self: MemoryAccessKind, bw: *std.io.BufferedWriter, comptime prefix: []const u8) std.io.Writer.Error!void { | |
| 6948 | 6948 | if (self != .normal) try bw.print("{s}{s}", .{ prefix, @tagName(self) }); |
| 6949 | 6949 | } |
| 6950 | 6950 | }; |
| ... | ... | @@ -6953,7 +6953,7 @@ pub const SyncScope = enum(u1) { |
| 6953 | 6953 | singlethread, |
| 6954 | 6954 | system, |
| 6955 | 6955 | |
| 6956 | pub fn format(self: SyncScope, bw: *std.io.BufferedWriter, comptime prefix: []const u8) anyerror!void { | |
| 6956 | pub fn format(self: SyncScope, bw: *std.io.BufferedWriter, comptime prefix: []const u8) std.io.Writer.Error!void { | |
| 6957 | 6957 | if (self != .system) try bw.print( |
| 6958 | 6958 | \\{s}syncscope("{s}") |
| 6959 | 6959 | , .{ prefix, @tagName(self) }); |
| ... | ... | @@ -6969,7 +6969,7 @@ pub const AtomicOrdering = enum(u3) { |
| 6969 | 6969 | acq_rel = 5, |
| 6970 | 6970 | seq_cst = 6, |
| 6971 | 6971 | |
| 6972 | pub fn format(self: AtomicOrdering, bw: *std.io.BufferedWriter, comptime prefix: []const u8) anyerror!void { | |
| 6972 | pub fn format(self: AtomicOrdering, bw: *std.io.BufferedWriter, comptime prefix: []const u8) std.io.Writer.Error!void { | |
| 6973 | 6973 | if (self != .none) try bw.print("{s}{s}", .{ prefix, @tagName(self) }); |
| 6974 | 6974 | } |
| 6975 | 6975 | }; |
| ... | ... | @@ -7385,7 +7385,7 @@ pub const Constant = enum(u32) { |
| 7385 | 7385 | constant: Constant, |
| 7386 | 7386 | builder: *Builder, |
| 7387 | 7387 | }; |
| 7388 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) anyerror!void { | |
| 7388 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) std.io.Writer.Error!void { | |
| 7389 | 7389 | if (comptime std.mem.indexOfNone(u8, fmt_str, ", %")) |_| |
| 7390 | 7390 | @compileError("invalid format string: '" ++ fmt_str ++ "'"); |
| 7391 | 7391 | if (comptime std.mem.indexOfScalar(u8, fmt_str, ',') != null) { |
| ... | ... | @@ -7712,7 +7712,7 @@ pub const Value = enum(u32) { |
| 7712 | 7712 | function: Function.Index, |
| 7713 | 7713 | builder: *Builder, |
| 7714 | 7714 | }; |
| 7715 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) anyerror!void { | |
| 7715 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) std.io.Writer.Error!void { | |
| 7716 | 7716 | switch (data.value.unwrap()) { |
| 7717 | 7717 | .instruction => |instruction| try Function.Instruction.Index.format(.{ |
| 7718 | 7718 | .instruction = instruction, |
| ... | ... | @@ -7757,7 +7757,7 @@ pub const MetadataString = enum(u32) { |
| 7757 | 7757 | metadata_string: MetadataString, |
| 7758 | 7758 | builder: *const Builder, |
| 7759 | 7759 | }; |
| 7760 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void { | |
| 7760 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void { | |
| 7761 | 7761 | try printEscapedString(data.metadata_string.slice(data.builder), .always_quote, bw); |
| 7762 | 7762 | } |
| 7763 | 7763 | fn fmt(self: MetadataString, builder: *const Builder) std.fmt.Formatter(format) { |
| ... | ... | @@ -7922,7 +7922,7 @@ pub const Metadata = enum(u32) { |
| 7922 | 7922 | AllCallsDescribed: bool = false, |
| 7923 | 7923 | Unused: u2 = 0, |
| 7924 | 7924 | |
| 7925 | pub fn format(self: DIFlags, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void { | |
| 7925 | pub fn format(self: DIFlags, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void { | |
| 7926 | 7926 | var need_pipe = false; |
| 7927 | 7927 | inline for (@typeInfo(DIFlags).@"struct".fields) |field| { |
| 7928 | 7928 | switch (@typeInfo(field.type)) { |
| ... | ... | @@ -7979,7 +7979,7 @@ pub const Metadata = enum(u32) { |
| 7979 | 7979 | ObjCDirect: bool = false, |
| 7980 | 7980 | Unused: u20 = 0, |
| 7981 | 7981 | |
| 7982 | pub fn format(self: DISPFlags, bw: *std.io.BufferedWriter, comptime _: []const u8) anyerror!void { | |
| 7982 | pub fn format(self: DISPFlags, bw: *std.io.BufferedWriter, comptime _: []const u8) std.io.Writer.Error!void { | |
| 7983 | 7983 | var need_pipe = false; |
| 7984 | 7984 | inline for (@typeInfo(DISPFlags).@"struct".fields) |field| { |
| 7985 | 7985 | switch (@typeInfo(field.type)) { |
| ... | ... | @@ -8196,7 +8196,7 @@ pub const Metadata = enum(u32) { |
| 8196 | 8196 | }; |
| 8197 | 8197 | }; |
| 8198 | 8198 | }; |
| 8199 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) anyerror!void { | |
| 8199 | fn format(data: FormatData, bw: *std.io.BufferedWriter, comptime fmt_str: []const u8) std.io.Writer.Error!void { | |
| 8200 | 8200 | if (data.node == .none) return; |
| 8201 | 8201 | |
| 8202 | 8202 | const is_specialized = fmt_str.len > 0 and fmt_str[0] == 'S'; |
| ... | ... | @@ -8371,7 +8371,7 @@ pub const Metadata = enum(u32) { |
| 8371 | 8371 | }, |
| 8372 | 8372 | nodes: anytype, |
| 8373 | 8373 | bw: *std.io.BufferedWriter, |
| 8374 | ) anyerror!void { | |
| 8374 | ) !void { | |
| 8375 | 8375 | comptime var fmt_str: []const u8 = ""; |
| 8376 | 8376 | const names = comptime std.meta.fieldNames(@TypeOf(nodes)); |
| 8377 | 8377 | comptime var fields: [2 + names.len]std.builtin.Type.StructField = undefined; |
| ... | ... | @@ -9379,14 +9379,14 @@ pub fn printToFile(self: *Builder, path: []const u8) bool { |
| 9379 | 9379 | return true; |
| 9380 | 9380 | } |
| 9381 | 9381 | |
| 9382 | pub fn printBuffered(self: *Builder, writer: std.io.Writer) anyerror!void { | |
| 9382 | pub fn printBuffered(self: *Builder, writer: std.io.Writer) std.io.Writer.Error!void { | |
| 9383 | 9383 | var buffer: [4096]u8 = undefined; |
| 9384 | 9384 | var bw = writer.buffered(&buffer); |
| 9385 | 9385 | try self.print(&bw); |
| 9386 | 9386 | try bw.flush(); |
| 9387 | 9387 | } |
| 9388 | 9388 | |
| 9389 | pub fn print(self: *Builder, bw: *std.io.BufferedWriter) anyerror!void { | |
| 9389 | pub fn print(self: *Builder, bw: *std.io.BufferedWriter) std.io.Writer.Error!void { | |
| 9390 | 9390 | var need_newline = false; |
| 9391 | 9391 | var metadata_formatter: Metadata.Formatter = .{ .builder = self, .need_comma = undefined }; |
| 9392 | 9392 | defer metadata_formatter.map.deinit(self.gpa); |
| ... | ... | @@ -10458,7 +10458,7 @@ fn isValidIdentifier(id: []const u8) bool { |
| 10458 | 10458 | } |
| 10459 | 10459 | |
| 10460 | 10460 | const QuoteBehavior = enum { always_quote, quote_unless_valid_identifier }; |
| 10461 | fn printEscapedString(slice: []const u8, quotes: QuoteBehavior, bw: *std.io.BufferedWriter) anyerror!void { | |
| 10461 | fn printEscapedString(slice: []const u8, quotes: QuoteBehavior, bw: *std.io.BufferedWriter) std.io.Writer.Error!void { | |
| 10462 | 10462 | const need_quotes = switch (quotes) { |
| 10463 | 10463 | .always_quote => true, |
| 10464 | 10464 | .quote_unless_valid_identifier => !isValidIdentifier(slice), |
lib/std/zig/render.zig+53-51| ... | ... | @@ -10,6 +10,8 @@ const primitives = std.zig.primitives; |
| 10 | 10 | const indent_delta = 4; |
| 11 | 11 | const asm_indent_delta = 2; |
| 12 | 12 | |
| 13 | pub const Error = Ast.RenderError; | |
| 14 | ||
| 13 | 15 | pub const Fixups = struct { |
| 14 | 16 | /// The key is the mut token (`var`/`const`) of the variable declaration |
| 15 | 17 | /// that should have a `_ = foo;` inserted afterwards. |
| ... | ... | @@ -75,7 +77,7 @@ const Render = struct { |
| 75 | 77 | fixups: Fixups, |
| 76 | 78 | }; |
| 77 | 79 | |
| 78 | pub fn renderTree(gpa: Allocator, bw: *std.io.BufferedWriter, tree: Ast, fixups: Fixups) anyerror!void { | |
| 80 | pub fn renderTree(gpa: Allocator, bw: *std.io.BufferedWriter, tree: Ast, fixups: Fixups) Error!void { | |
| 79 | 81 | assert(tree.errors.len == 0); // Cannot render an invalid tree. |
| 80 | 82 | var auto_indenting_stream: AutoIndentingStream = .init(gpa, bw, indent_delta); |
| 81 | 83 | defer auto_indenting_stream.deinit(); |
| ... | ... | @@ -111,7 +113,7 @@ pub fn renderTree(gpa: Allocator, bw: *std.io.BufferedWriter, tree: Ast, fixups: |
| 111 | 113 | } |
| 112 | 114 | |
| 113 | 115 | /// Render all members in the given slice, keeping empty lines where appropriate |
| 114 | fn renderMembers(r: *Render, members: []const Ast.Node.Index) anyerror!void { | |
| 116 | fn renderMembers(r: *Render, members: []const Ast.Node.Index) Error!void { | |
| 115 | 117 | const tree = r.tree; |
| 116 | 118 | if (members.len == 0) return; |
| 117 | 119 | const container: Container = for (members) |member| { |
| ... | ... | @@ -135,7 +137,7 @@ fn renderMember( |
| 135 | 137 | container: Container, |
| 136 | 138 | decl: Ast.Node.Index, |
| 137 | 139 | space: Space, |
| 138 | ) anyerror!void { | |
| 140 | ) Error!void { | |
| 139 | 141 | const tree = r.tree; |
| 140 | 142 | const ais = r.ais; |
| 141 | 143 | if (r.fixups.omit_nodes.contains(decl)) return; |
| ... | ... | @@ -305,7 +307,7 @@ fn renderMember( |
| 305 | 307 | } |
| 306 | 308 | |
| 307 | 309 | /// Render all expressions in the slice, keeping empty lines where appropriate |
| 308 | fn renderExpressions(r: *Render, expressions: []const Ast.Node.Index, space: Space) anyerror!void { | |
| 310 | fn renderExpressions(r: *Render, expressions: []const Ast.Node.Index, space: Space) Error!void { | |
| 309 | 311 | if (expressions.len == 0) return; |
| 310 | 312 | try renderExpression(r, expressions[0], space); |
| 311 | 313 | for (expressions[1..]) |expression| { |
| ... | ... | @@ -314,7 +316,7 @@ fn renderExpressions(r: *Render, expressions: []const Ast.Node.Index, space: Spa |
| 314 | 316 | } |
| 315 | 317 | } |
| 316 | 318 | |
| 317 | fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) anyerror!void { | |
| 319 | fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { | |
| 318 | 320 | const tree = r.tree; |
| 319 | 321 | const ais = r.ais; |
| 320 | 322 | if (r.fixups.replace_nodes_with_string.get(node)) |replacement| { |
| ... | ... | @@ -886,7 +888,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) anyerror!voi |
| 886 | 888 | |
| 887 | 889 | /// Same as `renderExpression`, but afterwards looks for any |
| 888 | 890 | /// append_string_after_node fixups to apply |
| 889 | fn renderExpressionFixup(r: *Render, node: Ast.Node.Index, space: Space) anyerror!void { | |
| 891 | fn renderExpressionFixup(r: *Render, node: Ast.Node.Index, space: Space) Error!void { | |
| 890 | 892 | const ais = r.ais; |
| 891 | 893 | try renderExpression(r, node, space); |
| 892 | 894 | if (r.fixups.append_string_after_node.get(node)) |bytes| { |
| ... | ... | @@ -898,7 +900,7 @@ fn renderArrayType( |
| 898 | 900 | r: *Render, |
| 899 | 901 | array_type: Ast.full.ArrayType, |
| 900 | 902 | space: Space, |
| 901 | ) anyerror!void { | |
| 903 | ) Error!void { | |
| 902 | 904 | const tree = r.tree; |
| 903 | 905 | const ais = r.ais; |
| 904 | 906 | const rbracket = tree.firstToken(array_type.ast.elem_type) - 1; |
| ... | ... | @@ -916,7 +918,7 @@ fn renderArrayType( |
| 916 | 918 | return renderExpression(r, array_type.ast.elem_type, space); |
| 917 | 919 | } |
| 918 | 920 | |
| 919 | fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) anyerror!void { | |
| 921 | fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!void { | |
| 920 | 922 | const tree = r.tree; |
| 921 | 923 | const main_token = ptr_type.ast.main_token; |
| 922 | 924 | switch (ptr_type.size) { |
| ... | ... | @@ -1010,7 +1012,7 @@ fn renderSlice( |
| 1010 | 1012 | slice_node: Ast.Node.Index, |
| 1011 | 1013 | slice: Ast.full.Slice, |
| 1012 | 1014 | space: Space, |
| 1013 | ) anyerror!void { | |
| 1015 | ) Error!void { | |
| 1014 | 1016 | const tree = r.tree; |
| 1015 | 1017 | const after_start_space_bool = nodeCausesSliceOpSpace(tree.nodeTag(slice.ast.start)) or |
| 1016 | 1018 | if (slice.ast.end.unwrap()) |end| nodeCausesSliceOpSpace(tree.nodeTag(end)) else false; |
| ... | ... | @@ -1043,7 +1045,7 @@ fn renderAsmOutput( |
| 1043 | 1045 | r: *Render, |
| 1044 | 1046 | asm_output: Ast.Node.Index, |
| 1045 | 1047 | space: Space, |
| 1046 | ) anyerror!void { | |
| 1048 | ) Error!void { | |
| 1047 | 1049 | const tree = r.tree; |
| 1048 | 1050 | assert(tree.nodeTag(asm_output) == .asm_output); |
| 1049 | 1051 | const symbolic_name = tree.nodeMainToken(asm_output); |
| ... | ... | @@ -1069,7 +1071,7 @@ fn renderAsmInput( |
| 1069 | 1071 | r: *Render, |
| 1070 | 1072 | asm_input: Ast.Node.Index, |
| 1071 | 1073 | space: Space, |
| 1072 | ) anyerror!void { | |
| 1074 | ) Error!void { | |
| 1073 | 1075 | const tree = r.tree; |
| 1074 | 1076 | assert(tree.nodeTag(asm_input) == .asm_input); |
| 1075 | 1077 | const symbolic_name = tree.nodeMainToken(asm_input); |
| ... | ... | @@ -1091,7 +1093,7 @@ fn renderVarDecl( |
| 1091 | 1093 | ignore_comptime_token: bool, |
| 1092 | 1094 | /// `comma_space` and `space` are used for destructure LHS decls. |
| 1093 | 1095 | space: Space, |
| 1094 | ) anyerror!void { | |
| 1096 | ) Error!void { | |
| 1095 | 1097 | try renderVarDeclWithoutFixups(r, var_decl, ignore_comptime_token, space); |
| 1096 | 1098 | if (r.fixups.unused_var_decls.contains(var_decl.ast.mut_token + 1)) { |
| 1097 | 1099 | // Discard the variable like this: `_ = foo;` |
| ... | ... | @@ -1109,7 +1111,7 @@ fn renderVarDeclWithoutFixups( |
| 1109 | 1111 | ignore_comptime_token: bool, |
| 1110 | 1112 | /// `comma_space` and `space` are used for destructure LHS decls. |
| 1111 | 1113 | space: Space, |
| 1112 | ) anyerror!void { | |
| 1114 | ) Error!void { | |
| 1113 | 1115 | const tree = r.tree; |
| 1114 | 1116 | const ais = r.ais; |
| 1115 | 1117 | |
| ... | ... | @@ -1221,7 +1223,7 @@ fn renderVarDeclWithoutFixups( |
| 1221 | 1223 | ais.popIndent(); |
| 1222 | 1224 | } |
| 1223 | 1225 | |
| 1224 | fn renderIf(r: *Render, if_node: Ast.full.If, space: Space) anyerror!void { | |
| 1226 | fn renderIf(r: *Render, if_node: Ast.full.If, space: Space) Error!void { | |
| 1225 | 1227 | return renderWhile(r, .{ |
| 1226 | 1228 | .ast = .{ |
| 1227 | 1229 | .while_token = if_node.ast.if_token, |
| ... | ... | @@ -1240,7 +1242,7 @@ fn renderIf(r: *Render, if_node: Ast.full.If, space: Space) anyerror!void { |
| 1240 | 1242 | |
| 1241 | 1243 | /// Note that this function is additionally used to render if expressions, with |
| 1242 | 1244 | /// respective values set to null. |
| 1243 | fn renderWhile(r: *Render, while_node: Ast.full.While, space: Space) anyerror!void { | |
| 1245 | fn renderWhile(r: *Render, while_node: Ast.full.While, space: Space) Error!void { | |
| 1244 | 1246 | const tree = r.tree; |
| 1245 | 1247 | |
| 1246 | 1248 | if (while_node.label_token) |label| { |
| ... | ... | @@ -1310,7 +1312,7 @@ fn renderThenElse( |
| 1310 | 1312 | maybe_error_token: ?Ast.TokenIndex, |
| 1311 | 1313 | opt_else_expr: Ast.Node.OptionalIndex, |
| 1312 | 1314 | space: Space, |
| 1313 | ) anyerror!void { | |
| 1315 | ) Error!void { | |
| 1314 | 1316 | const tree = r.tree; |
| 1315 | 1317 | const ais = r.ais; |
| 1316 | 1318 | const then_expr_is_block = nodeIsBlock(tree.nodeTag(then_expr)); |
| ... | ... | @@ -1365,7 +1367,7 @@ fn renderThenElse( |
| 1365 | 1367 | } |
| 1366 | 1368 | } |
| 1367 | 1369 | |
| 1368 | fn renderFor(r: *Render, for_node: Ast.full.For, space: Space) anyerror!void { | |
| 1370 | fn renderFor(r: *Render, for_node: Ast.full.For, space: Space) Error!void { | |
| 1369 | 1371 | const tree = r.tree; |
| 1370 | 1372 | const ais = r.ais; |
| 1371 | 1373 | const token_tags = tree.tokens.items(.tag); |
| ... | ... | @@ -1440,7 +1442,7 @@ fn renderContainerField( |
| 1440 | 1442 | container: Container, |
| 1441 | 1443 | field_param: Ast.full.ContainerField, |
| 1442 | 1444 | space: Space, |
| 1443 | ) anyerror!void { | |
| 1445 | ) Error!void { | |
| 1444 | 1446 | const tree = r.tree; |
| 1445 | 1447 | const ais = r.ais; |
| 1446 | 1448 | var field = field_param; |
| ... | ... | @@ -1549,7 +1551,7 @@ fn renderBuiltinCall( |
| 1549 | 1551 | builtin_token: Ast.TokenIndex, |
| 1550 | 1552 | params: []const Ast.Node.Index, |
| 1551 | 1553 | space: Space, |
| 1552 | ) anyerror!void { | |
| 1554 | ) Error!void { | |
| 1553 | 1555 | const tree = r.tree; |
| 1554 | 1556 | const ais = r.ais; |
| 1555 | 1557 | |
| ... | ... | @@ -1622,7 +1624,7 @@ fn renderBuiltinCall( |
| 1622 | 1624 | } |
| 1623 | 1625 | } |
| 1624 | 1626 | |
| 1625 | fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) anyerror!void { | |
| 1627 | fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!void { | |
| 1626 | 1628 | const tree = r.tree; |
| 1627 | 1629 | const ais = r.ais; |
| 1628 | 1630 | |
| ... | ... | @@ -1847,7 +1849,7 @@ fn renderSwitchCase( |
| 1847 | 1849 | r: *Render, |
| 1848 | 1850 | switch_case: Ast.full.SwitchCase, |
| 1849 | 1851 | space: Space, |
| 1850 | ) anyerror!void { | |
| 1852 | ) Error!void { | |
| 1851 | 1853 | const ais = r.ais; |
| 1852 | 1854 | const tree = r.tree; |
| 1853 | 1855 | const trailing_comma = tree.tokenTag(switch_case.ast.arrow_token - 1) == .comma; |
| ... | ... | @@ -1909,7 +1911,7 @@ fn renderBlock( |
| 1909 | 1911 | block_node: Ast.Node.Index, |
| 1910 | 1912 | statements: []const Ast.Node.Index, |
| 1911 | 1913 | space: Space, |
| 1912 | ) anyerror!void { | |
| 1914 | ) Error!void { | |
| 1913 | 1915 | const tree = r.tree; |
| 1914 | 1916 | const ais = r.ais; |
| 1915 | 1917 | const lbrace = tree.nodeMainToken(block_node); |
| ... | ... | @@ -1934,7 +1936,7 @@ fn finishRenderBlock( |
| 1934 | 1936 | block_node: Ast.Node.Index, |
| 1935 | 1937 | statements: []const Ast.Node.Index, |
| 1936 | 1938 | space: Space, |
| 1937 | ) anyerror!void { | |
| 1939 | ) Error!void { | |
| 1938 | 1940 | const tree = r.tree; |
| 1939 | 1941 | const ais = r.ais; |
| 1940 | 1942 | for (statements, 0..) |stmt, i| { |
| ... | ... | @@ -1962,7 +1964,7 @@ fn renderStructInit( |
| 1962 | 1964 | struct_node: Ast.Node.Index, |
| 1963 | 1965 | struct_init: Ast.full.StructInit, |
| 1964 | 1966 | space: Space, |
| 1965 | ) anyerror!void { | |
| 1967 | ) Error!void { | |
| 1966 | 1968 | const tree = r.tree; |
| 1967 | 1969 | const ais = r.ais; |
| 1968 | 1970 | |
| ... | ... | @@ -2033,7 +2035,7 @@ fn renderArrayInit( |
| 2033 | 2035 | r: *Render, |
| 2034 | 2036 | array_init: Ast.full.ArrayInit, |
| 2035 | 2037 | space: Space, |
| 2036 | ) anyerror!void { | |
| 2038 | ) Error!void { | |
| 2037 | 2039 | const tree = r.tree; |
| 2038 | 2040 | const ais = r.ais; |
| 2039 | 2041 | const gpa = r.gpa; |
| ... | ... | @@ -2263,7 +2265,7 @@ fn renderContainerDecl( |
| 2263 | 2265 | container_decl_node: Ast.Node.Index, |
| 2264 | 2266 | container_decl: Ast.full.ContainerDecl, |
| 2265 | 2267 | space: Space, |
| 2266 | ) anyerror!void { | |
| 2268 | ) Error!void { | |
| 2267 | 2269 | const tree = r.tree; |
| 2268 | 2270 | const ais = r.ais; |
| 2269 | 2271 | |
| ... | ... | @@ -2382,7 +2384,7 @@ fn renderAsm( |
| 2382 | 2384 | r: *Render, |
| 2383 | 2385 | asm_node: Ast.full.Asm, |
| 2384 | 2386 | space: Space, |
| 2385 | ) anyerror!void { | |
| 2387 | ) Error!void { | |
| 2386 | 2388 | const tree = r.tree; |
| 2387 | 2389 | const ais = r.ais; |
| 2388 | 2390 | |
| ... | ... | @@ -2548,7 +2550,7 @@ fn renderCall( |
| 2548 | 2550 | r: *Render, |
| 2549 | 2551 | call: Ast.full.Call, |
| 2550 | 2552 | space: Space, |
| 2551 | ) anyerror!void { | |
| 2553 | ) Error!void { | |
| 2552 | 2554 | if (call.async_token) |async_token| { |
| 2553 | 2555 | try renderToken(r, async_token, .space); |
| 2554 | 2556 | } |
| ... | ... | @@ -2561,7 +2563,7 @@ fn renderParamList( |
| 2561 | 2563 | lparen: Ast.TokenIndex, |
| 2562 | 2564 | params: []const Ast.Node.Index, |
| 2563 | 2565 | space: Space, |
| 2564 | ) anyerror!void { | |
| 2566 | ) Error!void { | |
| 2565 | 2567 | const tree = r.tree; |
| 2566 | 2568 | const ais = r.ais; |
| 2567 | 2569 | |
| ... | ... | @@ -2614,7 +2616,7 @@ fn renderParamList( |
| 2614 | 2616 | |
| 2615 | 2617 | /// Render an expression, and the comma that follows it, if it is present in the source. |
| 2616 | 2618 | /// If a comma is present, and `space` is `Space.comma`, render only a single comma. |
| 2617 | fn renderExpressionComma(r: *Render, node: Ast.Node.Index, space: Space) anyerror!void { | |
| 2619 | fn renderExpressionComma(r: *Render, node: Ast.Node.Index, space: Space) Error!void { | |
| 2618 | 2620 | const tree = r.tree; |
| 2619 | 2621 | const maybe_comma = tree.lastToken(node) + 1; |
| 2620 | 2622 | if (tree.tokenTag(maybe_comma) == .comma and space != .comma) { |
| ... | ... | @@ -2627,7 +2629,7 @@ fn renderExpressionComma(r: *Render, node: Ast.Node.Index, space: Space) anyerro |
| 2627 | 2629 | |
| 2628 | 2630 | /// Render a token, and the comma that follows it, if it is present in the source. |
| 2629 | 2631 | /// If a comma is present, and `space` is `Space.comma`, render only a single comma. |
| 2630 | fn renderTokenComma(r: *Render, token: Ast.TokenIndex, space: Space) anyerror!void { | |
| 2632 | fn renderTokenComma(r: *Render, token: Ast.TokenIndex, space: Space) Error!void { | |
| 2631 | 2633 | const tree = r.tree; |
| 2632 | 2634 | const maybe_comma = token + 1; |
| 2633 | 2635 | if (tree.tokenTag(maybe_comma) == .comma and space != .comma) { |
| ... | ... | @@ -2640,7 +2642,7 @@ fn renderTokenComma(r: *Render, token: Ast.TokenIndex, space: Space) anyerror!vo |
| 2640 | 2642 | |
| 2641 | 2643 | /// Render an identifier, and the comma that follows it, if it is present in the source. |
| 2642 | 2644 | /// If a comma is present, and `space` is `Space.comma`, render only a single comma. |
| 2643 | fn renderIdentifierComma(r: *Render, token: Ast.TokenIndex, space: Space, quote: QuoteBehavior) anyerror!void { | |
| 2645 | fn renderIdentifierComma(r: *Render, token: Ast.TokenIndex, space: Space, quote: QuoteBehavior) Error!void { | |
| 2644 | 2646 | const tree = r.tree; |
| 2645 | 2647 | const maybe_comma = token + 1; |
| 2646 | 2648 | if (tree.tokenTag(maybe_comma) == .comma and space != .comma) { |
| ... | ... | @@ -2672,7 +2674,7 @@ const Space = enum { |
| 2672 | 2674 | skip, |
| 2673 | 2675 | }; |
| 2674 | 2676 | |
| 2675 | fn renderToken(r: *Render, token_index: Ast.TokenIndex, space: Space) anyerror!void { | |
| 2677 | fn renderToken(r: *Render, token_index: Ast.TokenIndex, space: Space) Error!void { | |
| 2676 | 2678 | const tree = r.tree; |
| 2677 | 2679 | const ais = r.ais; |
| 2678 | 2680 | const lexeme = tokenSliceForRender(tree, token_index); |
| ... | ... | @@ -2680,7 +2682,7 @@ fn renderToken(r: *Render, token_index: Ast.TokenIndex, space: Space) anyerror!v |
| 2680 | 2682 | try renderSpace(r, token_index, lexeme.len, space); |
| 2681 | 2683 | } |
| 2682 | 2684 | |
| 2683 | fn renderTokenOverrideSpaceMode(r: *Render, token_index: Ast.TokenIndex, space: Space, override_space: Space) anyerror!void { | |
| 2685 | fn renderTokenOverrideSpaceMode(r: *Render, token_index: Ast.TokenIndex, space: Space, override_space: Space) Error!void { | |
| 2684 | 2686 | const tree = r.tree; |
| 2685 | 2687 | const ais = r.ais; |
| 2686 | 2688 | const lexeme = tokenSliceForRender(tree, token_index); |
| ... | ... | @@ -2690,7 +2692,7 @@ fn renderTokenOverrideSpaceMode(r: *Render, token_index: Ast.TokenIndex, space: |
| 2690 | 2692 | try renderSpace(r, token_index, lexeme.len, space); |
| 2691 | 2693 | } |
| 2692 | 2694 | |
| 2693 | fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space: Space) anyerror!void { | |
| 2695 | fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space: Space) Error!void { | |
| 2694 | 2696 | const tree = r.tree; |
| 2695 | 2697 | const ais = r.ais; |
| 2696 | 2698 | |
| ... | ... | @@ -2735,7 +2737,7 @@ fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space |
| 2735 | 2737 | } |
| 2736 | 2738 | } |
| 2737 | 2739 | |
| 2738 | fn renderOnlySpace(r: *Render, space: Space) anyerror!void { | |
| 2740 | fn renderOnlySpace(r: *Render, space: Space) Error!void { | |
| 2739 | 2741 | const ais = r.ais; |
| 2740 | 2742 | switch (space) { |
| 2741 | 2743 | .none => {}, |
| ... | ... | @@ -2754,7 +2756,7 @@ const QuoteBehavior = enum { |
| 2754 | 2756 | eagerly_unquote_except_underscore, |
| 2755 | 2757 | }; |
| 2756 | 2758 | |
| 2757 | fn renderIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space, quote: QuoteBehavior) anyerror!void { | |
| 2759 | fn renderIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space, quote: QuoteBehavior) Error!void { | |
| 2758 | 2760 | const tree = r.tree; |
| 2759 | 2761 | assert(tree.tokenTag(token_index) == .identifier); |
| 2760 | 2762 | const lexeme = tokenSliceForRender(tree, token_index); |
| ... | ... | @@ -2940,7 +2942,7 @@ fn hasMultilineString(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.Tok |
| 2940 | 2942 | |
| 2941 | 2943 | /// Assumes that start is the first byte past the previous token and |
| 2942 | 2944 | /// that end is the last byte before the next token. |
| 2943 | fn renderComments(r: *Render, start: usize, end: usize) anyerror!bool { | |
| 2945 | fn renderComments(r: *Render, start: usize, end: usize) Error!bool { | |
| 2944 | 2946 | const tree = r.tree; |
| 2945 | 2947 | const ais = r.ais; |
| 2946 | 2948 | |
| ... | ... | @@ -3003,12 +3005,12 @@ fn renderComments(r: *Render, start: usize, end: usize) anyerror!bool { |
| 3003 | 3005 | return index != start; |
| 3004 | 3006 | } |
| 3005 | 3007 | |
| 3006 | fn renderExtraNewline(r: *Render, node: Ast.Node.Index) anyerror!void { | |
| 3008 | fn renderExtraNewline(r: *Render, node: Ast.Node.Index) Error!void { | |
| 3007 | 3009 | return renderExtraNewlineToken(r, r.tree.firstToken(node)); |
| 3008 | 3010 | } |
| 3009 | 3011 | |
| 3010 | 3012 | /// Check if there is an empty line immediately before the given token. If so, render it. |
| 3011 | fn renderExtraNewlineToken(r: *Render, token_index: Ast.TokenIndex) anyerror!void { | |
| 3013 | fn renderExtraNewlineToken(r: *Render, token_index: Ast.TokenIndex) Error!void { | |
| 3012 | 3014 | const tree = r.tree; |
| 3013 | 3015 | const ais = r.ais; |
| 3014 | 3016 | const token_start = tree.tokenStart(token_index); |
| ... | ... | @@ -3036,7 +3038,7 @@ fn renderExtraNewlineToken(r: *Render, token_index: Ast.TokenIndex) anyerror!voi |
| 3036 | 3038 | |
| 3037 | 3039 | /// end_token is the token one past the last doc comment token. This function |
| 3038 | 3040 | /// searches backwards from there. |
| 3039 | fn renderDocComments(r: *Render, end_token: Ast.TokenIndex) anyerror!void { | |
| 3041 | fn renderDocComments(r: *Render, end_token: Ast.TokenIndex) Error!void { | |
| 3040 | 3042 | const tree = r.tree; |
| 3041 | 3043 | // Search backwards for the first doc comment. |
| 3042 | 3044 | if (end_token == 0) return; |
| ... | ... | @@ -3067,7 +3069,7 @@ fn renderDocComments(r: *Render, end_token: Ast.TokenIndex) anyerror!void { |
| 3067 | 3069 | } |
| 3068 | 3070 | |
| 3069 | 3071 | /// start_token is first container doc comment token. |
| 3070 | fn renderContainerDocComments(r: *Render, start_token: Ast.TokenIndex) anyerror!void { | |
| 3072 | fn renderContainerDocComments(r: *Render, start_token: Ast.TokenIndex) Error!void { | |
| 3071 | 3073 | const tree = r.tree; |
| 3072 | 3074 | var tok = start_token; |
| 3073 | 3075 | while (tree.tokenTag(tok) == .container_doc_comment) : (tok += 1) { |
| ... | ... | @@ -3081,7 +3083,7 @@ fn renderContainerDocComments(r: *Render, start_token: Ast.TokenIndex) anyerror! |
| 3081 | 3083 | } |
| 3082 | 3084 | } |
| 3083 | 3085 | |
| 3084 | fn discardAllParams(r: *Render, fn_proto_node: Ast.Node.Index) anyerror!void { | |
| 3086 | fn discardAllParams(r: *Render, fn_proto_node: Ast.Node.Index) Error!void { | |
| 3085 | 3087 | const tree = &r.tree; |
| 3086 | 3088 | const ais = r.ais; |
| 3087 | 3089 | var buf: [1]Ast.Node.Index = undefined; |
| ... | ... | @@ -3129,7 +3131,7 @@ fn anythingBetween(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenI |
| 3129 | 3131 | return false; |
| 3130 | 3132 | } |
| 3131 | 3133 | |
| 3132 | fn writeFixingWhitespace(bw: *std.io.BufferedWriter, slice: []const u8) anyerror!void { | |
| 3134 | fn writeFixingWhitespace(bw: *std.io.BufferedWriter, slice: []const u8) Error!void { | |
| 3133 | 3135 | for (slice) |byte| switch (byte) { |
| 3134 | 3136 | '\t' => try bw.splatByteAll(' ', indent_delta), |
| 3135 | 3137 | '\r' => {}, |
| ... | ... | @@ -3308,7 +3310,7 @@ const AutoIndentingStream = struct { |
| 3308 | 3310 | self.space_stack.deinit(); |
| 3309 | 3311 | } |
| 3310 | 3312 | |
| 3311 | pub fn writeAll(ais: *AutoIndentingStream, bytes: []const u8) anyerror!void { | |
| 3313 | pub fn writeAll(ais: *AutoIndentingStream, bytes: []const u8) Error!void { | |
| 3312 | 3314 | if (bytes.len == 0) return; |
| 3313 | 3315 | try ais.applyIndent(); |
| 3314 | 3316 | if (ais.disabled_offset == null) try ais.underlying_writer.writeAll(bytes); |
| ... | ... | @@ -3317,19 +3319,19 @@ const AutoIndentingStream = struct { |
| 3317 | 3319 | |
| 3318 | 3320 | /// Assumes that if the printed data ends with a newline, it is directly |
| 3319 | 3321 | /// contained in the format string. |
| 3320 | pub fn print(ais: *AutoIndentingStream, comptime format: []const u8, args: anytype) anyerror!void { | |
| 3322 | pub fn print(ais: *AutoIndentingStream, comptime format: []const u8, args: anytype) Error!void { | |
| 3321 | 3323 | try ais.applyIndent(); |
| 3322 | 3324 | if (ais.disabled_offset == null) try ais.underlying_writer.print(format, args); |
| 3323 | 3325 | if (format[format.len - 1] == '\n') ais.resetLine(); |
| 3324 | 3326 | } |
| 3325 | 3327 | |
| 3326 | pub fn writeByte(ais: *AutoIndentingStream, byte: u8) anyerror!void { | |
| 3328 | pub fn writeByte(ais: *AutoIndentingStream, byte: u8) Error!void { | |
| 3327 | 3329 | try ais.applyIndent(); |
| 3328 | 3330 | if (ais.disabled_offset == null) try ais.underlying_writer.writeByte(byte); |
| 3329 | 3331 | assert(byte != '\n'); |
| 3330 | 3332 | } |
| 3331 | 3333 | |
| 3332 | pub fn splatByteAll(ais: *AutoIndentingStream, byte: u8, n: usize) anyerror!void { | |
| 3334 | pub fn splatByteAll(ais: *AutoIndentingStream, byte: u8, n: usize) Error!void { | |
| 3333 | 3335 | assert(byte != '\n'); |
| 3334 | 3336 | try ais.applyIndent(); |
| 3335 | 3337 | if (ais.disabled_offset == null) try ais.underlying_writer.splatByteAll(byte, n); |
| ... | ... | @@ -3350,13 +3352,13 @@ const AutoIndentingStream = struct { |
| 3350 | 3352 | ais.indent_delta = new_indent_delta; |
| 3351 | 3353 | } |
| 3352 | 3354 | |
| 3353 | pub fn insertNewline(ais: *AutoIndentingStream) anyerror!void { | |
| 3355 | pub fn insertNewline(ais: *AutoIndentingStream) Error!void { | |
| 3354 | 3356 | if (ais.disabled_offset == null) try ais.underlying_writer.writeByte('\n'); |
| 3355 | 3357 | ais.resetLine(); |
| 3356 | 3358 | } |
| 3357 | 3359 | |
| 3358 | 3360 | /// Insert a newline unless the current line is blank |
| 3359 | pub fn maybeInsertNewline(ais: *AutoIndentingStream) anyerror!void { | |
| 3361 | pub fn maybeInsertNewline(ais: *AutoIndentingStream) Error!void { | |
| 3360 | 3362 | if (!ais.current_line_empty) |
| 3361 | 3363 | try ais.insertNewline(); |
| 3362 | 3364 | } |
| ... | ... | @@ -3483,7 +3485,7 @@ const AutoIndentingStream = struct { |
| 3483 | 3485 | } |
| 3484 | 3486 | |
| 3485 | 3487 | /// Writes ' ' bytes if the current line is empty |
| 3486 | fn applyIndent(ais: *AutoIndentingStream) anyerror!void { | |
| 3488 | fn applyIndent(ais: *AutoIndentingStream) Error!void { | |
| 3487 | 3489 | const current_indent = ais.currentIndent(); |
| 3488 | 3490 | if (ais.current_line_empty and current_indent > 0) { |
| 3489 | 3491 | if (ais.disabled_offset == null) { |
lib/ubsan_rt.zig+1-1| ... | ... | @@ -119,7 +119,7 @@ const Value = extern struct { |
| 119 | 119 | } |
| 120 | 120 | } |
| 121 | 121 | |
| 122 | pub fn format(value: Value, bw: *std.io.BufferedWriter, comptime fmt: []const u8) anyerror!void { | |
| 122 | pub fn format(value: Value, bw: *std.io.BufferedWriter, comptime fmt: []const u8) !void { | |
| 123 | 123 | comptime assert(fmt.len == 0); |
| 124 | 124 | |
| 125 | 125 | // Work around x86_64 backend limitation. |