| author | |
| committer | |
| log | 1bb75f9d6276ddf6b69717d9b1c2f68140727425 |
| tree | 62ec5d7fef20f0047c5ff253db2ae80f06451e0b |
| parent | a90f07b5374723ff76ed2ea888e5515326650e64 |
8 files changed, 178 insertions(+), 102 deletions(-)
lib/std/Build.zig+4-4| ... | ... | @@ -179,7 +179,8 @@ const InitializedDepContext = struct { |
| 179 | 179 | }; |
| 180 | 180 | |
| 181 | 181 | pub const RunError = error{ |
| 182 | ReadFailure, | |
| 182 | ReadFailed, | |
| 183 | StreamTooLong, | |
| 183 | 184 | ExitCodeFailure, |
| 184 | 185 | ProcessTerminated, |
| 185 | 186 | ExecNotSupported, |
| ... | ... | @@ -2059,9 +2060,8 @@ pub fn runAllowFail( |
| 2059 | 2060 | try Step.handleVerbose2(b, null, child.env_map, argv); |
| 2060 | 2061 | try child.spawn(); |
| 2061 | 2062 | |
| 2062 | const stdout = child.stdout.?.readToEndAlloc(b.allocator, .limited(max_output_size)) catch { | |
| 2063 | return error.ReadFailure; | |
| 2064 | }; | |
| 2063 | var file_reader = child.stdout.?.readerStreaming(); | |
| 2064 | const stdout = try file_reader.interface().readRemainingAlloc(b.allocator, .limited(max_output_size)); | |
| 2065 | 2065 | errdefer b.allocator.free(stdout); |
| 2066 | 2066 | |
| 2067 | 2067 | const term = try child.wait(); |
lib/std/Build/Step/Run.zig+12-2| ... | ... | @@ -1791,10 +1791,20 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !StdIoResult { |
| 1791 | 1791 | stdout_bytes = poller.reader(.stdout).bufferContents(); |
| 1792 | 1792 | stderr_bytes = poller.reader(.stderr).bufferContents(); |
| 1793 | 1793 | } else { |
| 1794 | stdout_bytes = try stdout.readToEndAlloc(arena, run.stdio_limit); | |
| 1794 | var fr = stdout.readerStreaming(); | |
| 1795 | stdout_bytes = fr.interface().readRemainingAlloc(arena, run.stdio_limit) catch |err| switch (err) { | |
| 1796 | error.OutOfMemory => return error.OutOfMemory, | |
| 1797 | error.ReadFailed => return fr.err.?, | |
| 1798 | error.StreamTooLong => return error.StdoutStreamTooLong, | |
| 1799 | }; | |
| 1795 | 1800 | } |
| 1796 | 1801 | } else if (child.stderr) |stderr| { |
| 1797 | stderr_bytes = try stderr.readToEndAlloc(arena, run.stdio_limit); | |
| 1802 | var fr = stderr.readerStreaming(); | |
| 1803 | stderr_bytes = fr.interface().readRemainingAlloc(arena, run.stdio_limit) catch |err| switch (err) { | |
| 1804 | error.OutOfMemory => return error.OutOfMemory, | |
| 1805 | error.ReadFailed => return fr.err.?, | |
| 1806 | error.StreamTooLong => return error.StderrStreamTooLong, | |
| 1807 | }; | |
| 1798 | 1808 | } |
| 1799 | 1809 | |
| 1800 | 1810 | if (stderr_bytes) |bytes| if (bytes.len > 0) { |
lib/std/debug/Dwarf.zig+1-1| ... | ... | @@ -2243,7 +2243,7 @@ pub const ElfModule = struct { |
| 2243 | 2243 | |
| 2244 | 2244 | var zlib_stream: std.compress.zlib.Decompressor = .init(&section_reader); |
| 2245 | 2245 | |
| 2246 | const decompressed_section = zlib_stream.reader().readAlloc(gpa, ch_size) catch continue; | |
| 2246 | const decompressed_section = zlib_stream.reader().readRemainingAlloc(gpa, .limited(ch_size)) catch continue; | |
| 2247 | 2247 | if (decompressed_section.len != ch_size) { |
| 2248 | 2248 | gpa.free(decompressed_section); |
| 2249 | 2249 | continue; |
lib/std/fs/Dir.zig+17-10| ... | ... | @@ -1963,6 +1963,8 @@ pub fn readFile(self: Dir, file_path: []const u8, buffer: []u8) ![]u8 { |
| 1963 | 1963 | return buffer[0..end_index]; |
| 1964 | 1964 | } |
| 1965 | 1965 | |
| 1966 | pub const ReadFileAllocError = File.OpenError || File.ReadError || Allocator.Error || error{StreamTooLong}; | |
| 1967 | ||
| 1966 | 1968 | /// Reads all the bytes from the named file. On success, caller owns returned |
| 1967 | 1969 | /// buffer. |
| 1968 | 1970 | pub fn readFileAlloc( |
| ... | ... | @@ -1972,13 +1974,13 @@ pub fn readFileAlloc( |
| 1972 | 1974 | /// On other platforms, an opaque sequence of bytes with no particular encoding. |
| 1973 | 1975 | file_path: []const u8, |
| 1974 | 1976 | /// Used to allocate the result. |
| 1975 | gpa: mem.Allocator, | |
| 1977 | gpa: Allocator, | |
| 1976 | 1978 | /// If exceeded: |
| 1977 | 1979 | /// * The array list's length is increased by exactly one byte past `limit`. |
| 1978 | 1980 | /// * The file seek position is advanced by exactly one byte past `limit`. |
| 1979 | /// * `error.FileTooBig` is returned. | |
| 1981 | /// * `error.StreamTooLong` is returned. | |
| 1980 | 1982 | limit: std.io.Reader.Limit, |
| 1981 | ) (File.OpenError || File.ReadAllocError)![]u8 { | |
| 1983 | ) ReadFileAllocError![]u8 { | |
| 1982 | 1984 | return dir.readFileAllocOptions(file_path, gpa, limit, null, .of(u8), null); |
| 1983 | 1985 | } |
| 1984 | 1986 | |
| ... | ... | @@ -1991,18 +1993,18 @@ pub fn readFileAllocOptions( |
| 1991 | 1993 | /// On other platforms, an opaque sequence of bytes with no particular encoding. |
| 1992 | 1994 | file_path: []const u8, |
| 1993 | 1995 | /// Used to allocate the result. |
| 1994 | gpa: mem.Allocator, | |
| 1996 | gpa: Allocator, | |
| 1995 | 1997 | /// If exceeded: |
| 1996 | 1998 | /// * The array list's length is increased by exactly one byte past `limit`. |
| 1997 | 1999 | /// * The file seek position is advanced by exactly one byte past `limit`. |
| 1998 | /// * `error.FileTooBig` is returned. | |
| 2000 | /// * `error.StreamTooLong` is returned. | |
| 1999 | 2001 | limit: std.io.Reader.Limit, |
| 2000 | 2002 | /// If specified, the initial buffer size is calculated using this value, |
| 2001 | 2003 | /// otherwise the effective file size is used instead. |
| 2002 | 2004 | size_hint: ?usize, |
| 2003 | 2005 | comptime alignment: std.mem.Alignment, |
| 2004 | 2006 | comptime optional_sentinel: ?u8, |
| 2005 | ) (File.OpenError || File.ReadAllocError)!(if (optional_sentinel) |s| [:s]align(alignment.toByteUnits()) u8 else []align(alignment.toByteUnits()) u8) { | |
| 2007 | ) ReadFileAllocError!(if (optional_sentinel) |s| [:s]align(alignment.toByteUnits()) u8 else []align(alignment.toByteUnits()) u8) { | |
| 2006 | 2008 | var buffer: std.ArrayListAlignedUnmanaged(u8, alignment) = .empty; |
| 2007 | 2009 | defer buffer.deinit(gpa); |
| 2008 | 2010 | try readFileIntoArrayList( |
| ... | ... | @@ -2026,7 +2028,7 @@ pub fn readFileAllocOptions( |
| 2026 | 2028 | /// If `limit` is exceeded: |
| 2027 | 2029 | /// * The array list's length is increased by exactly one byte past `limit`. |
| 2028 | 2030 | /// * The file seek position is advanced by exactly one byte past `limit`. |
| 2029 | /// * `error.FileTooBig` is returned. | |
| 2031 | /// * `error.StreamTooLong` is returned. | |
| 2030 | 2032 | pub fn readFileIntoArrayList( |
| 2031 | 2033 | dir: Dir, |
| 2032 | 2034 | /// On Windows, should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). |
| ... | ... | @@ -2040,7 +2042,7 @@ pub fn readFileIntoArrayList( |
| 2040 | 2042 | size_hint: ?usize, |
| 2041 | 2043 | comptime alignment: ?std.mem.Alignment, |
| 2042 | 2044 | list: *std.ArrayListAlignedUnmanaged(u8, alignment), |
| 2043 | ) (File.OpenError || File.ReadAllocError)!void { | |
| 2045 | ) ReadFileAllocError!void { | |
| 2044 | 2046 | var file = try dir.openFile(file_path, .{}); |
| 2045 | 2047 | defer file.close(); |
| 2046 | 2048 | |
| ... | ... | @@ -2049,14 +2051,19 @@ pub fn readFileIntoArrayList( |
| 2049 | 2051 | try list.ensureUnusedCapacity(gpa, size); |
| 2050 | 2052 | } else if (file.getEndPos()) |size| { |
| 2051 | 2053 | // If the file size doesn't fit a usize it'll be certainly exceed the limit. |
| 2052 | try list.ensureUnusedCapacity(gpa, std.math.cast(usize, size) orelse return error.FileTooBig); | |
| 2054 | try list.ensureUnusedCapacity(gpa, std.math.cast(usize, size) orelse return error.StreamTooLong); | |
| 2053 | 2055 | } else |err| switch (err) { |
| 2054 | 2056 | // Ignore most errors; size hint is only an optimization. |
| 2055 | 2057 | error.Unexpected, error.AccessDenied, error.PermissionDenied => {}, |
| 2056 | 2058 | else => |e| return e, |
| 2057 | 2059 | } |
| 2058 | 2060 | |
| 2059 | try file.readIntoArrayList(gpa, limit, alignment, list); | |
| 2061 | var file_reader = file.reader(); | |
| 2062 | file_reader.interface().readRemainingArrayList(gpa, alignment, list, limit) catch |err| switch (err) { | |
| 2063 | error.OutOfMemory => return error.OutOfMemory, | |
| 2064 | error.StreamTooLong => return error.StreamTooLong, | |
| 2065 | error.ReadFailed => return file_reader.err.?, | |
| 2066 | }; | |
| 2060 | 2067 | } |
| 2061 | 2068 | |
| 2062 | 2069 | pub const DeleteTreeError = error{ |
lib/std/fs/File.zig-40| ... | ... | @@ -788,46 +788,6 @@ pub fn updateTimes( |
| 788 | 788 | try posix.futimens(self.handle, &times); |
| 789 | 789 | } |
| 790 | 790 | |
| 791 | pub const ReadAllocError = ReadError || Allocator.Error || error{FileTooBig}; | |
| 792 | ||
| 793 | /// Reads all the bytes from the current position to the end of the file. | |
| 794 | /// | |
| 795 | /// On success, caller owns returned buffer. | |
| 796 | /// | |
| 797 | /// If `limit` is exceeded, returns `error.FileTooBig`. | |
| 798 | pub fn readToEndAlloc(file: File, gpa: Allocator, limit: std.io.Reader.Limit) ReadAllocError![]u8 { | |
| 799 | var buffer: std.ArrayListUnmanaged(u8) = .empty; | |
| 800 | defer buffer.deinit(gpa); | |
| 801 | try buffer.ensureUnusedCapacity(gpa, std.heap.page_size_min); | |
| 802 | try readIntoArrayList(file, gpa, limit, null, &buffer); | |
| 803 | return buffer.toOwnedSlice(gpa); | |
| 804 | } | |
| 805 | ||
| 806 | /// Reads all the bytes from the current position to the end of the file, | |
| 807 | /// appending them into the provided array list. | |
| 808 | /// | |
| 809 | /// If `limit` is exceeded: | |
| 810 | /// * The array list's length is increased by exactly one byte past `limit`. | |
| 811 | /// * The file seek position is advanced by exactly one byte past `limit`. | |
| 812 | /// * `error.FileTooBig` is returned. | |
| 813 | pub fn readIntoArrayList( | |
| 814 | file: File, | |
| 815 | gpa: Allocator, | |
| 816 | limit: std.io.Reader.Limit, | |
| 817 | comptime alignment: ?std.mem.Alignment, | |
| 818 | list: *std.ArrayListAlignedUnmanaged(u8, alignment), | |
| 819 | ) ReadAllocError!void { | |
| 820 | var remaining = limit; | |
| 821 | while (true) { | |
| 822 | try list.ensureUnusedCapacity(gpa, 1); | |
| 823 | const buffer = remaining.slice1(list.unusedCapacitySlice()); | |
| 824 | const n = try read(file, buffer); | |
| 825 | if (n == 0) return; | |
| 826 | list.items.len += n; | |
| 827 | remaining = remaining.subtract(n) orelse return error.FileTooBig; | |
| 828 | } | |
| 829 | } | |
| 830 | ||
| 831 | 791 | pub const ReadError = posix.ReadError; |
| 832 | 792 | pub const PReadError = posix.PReadError; |
| 833 | 793 |
lib/std/http/test.zig+23-23| ... | ... | @@ -70,7 +70,7 @@ test "trailers" { |
| 70 | 70 | try req.send(); |
| 71 | 71 | try req.wait(); |
| 72 | 72 | |
| 73 | const body = try req.reader().readAllAlloc(gpa, 8192); | |
| 73 | const body = try req.reader().readRemainingAlloc(gpa, .limited(8192)); | |
| 74 | 74 | defer gpa.free(body); |
| 75 | 75 | |
| 76 | 76 | try expectEqualStrings("Hello, World!\n", body); |
| ... | ... | @@ -153,7 +153,7 @@ test "HTTP server handles a chunked transfer coding request" { |
| 153 | 153 | "content-type: text/plain\r\n" ++ |
| 154 | 154 | "\r\n" ++ |
| 155 | 155 | "message from server!\n"; |
| 156 | const response = try stream.reader().readAllAlloc(gpa, expected_response.len); | |
| 156 | const response = try stream.reader().readRemainingAlloc(gpa, expected_response.len); | |
| 157 | 157 | defer gpa.free(response); |
| 158 | 158 | try expectEqualStrings(expected_response, response); |
| 159 | 159 | } |
| ... | ... | @@ -206,7 +206,7 @@ test "echo content server" { |
| 206 | 206 | // request.head.target, |
| 207 | 207 | //}); |
| 208 | 208 | |
| 209 | const body = try (try request.reader()).readAllAlloc(std.testing.allocator, 8192); | |
| 209 | const body = try (try request.reader()).readRemainingAlloc(std.testing.allocator, 8192); | |
| 210 | 210 | defer std.testing.allocator.free(body); |
| 211 | 211 | |
| 212 | 212 | try expect(mem.startsWith(u8, request.head.target, "/echo-content")); |
| ... | ... | @@ -291,7 +291,7 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" { |
| 291 | 291 | var writer = stream.writer().unbuffered(); |
| 292 | 292 | try writer.writeAll(request_bytes); |
| 293 | 293 | |
| 294 | const response = try stream.reader().readAllAlloc(gpa, 8192); | |
| 294 | const response = try stream.reader().readRemainingAlloc(gpa, 8192); | |
| 295 | 295 | defer gpa.free(response); |
| 296 | 296 | |
| 297 | 297 | var expected_response = std.ArrayList(u8).init(gpa); |
| ... | ... | @@ -362,7 +362,7 @@ test "receiving arbitrary http headers from the client" { |
| 362 | 362 | var writer = stream_writer.interface().unbuffered(); |
| 363 | 363 | try writer.writeAll(request_bytes); |
| 364 | 364 | |
| 365 | const response = try stream.reader().readAllAlloc(gpa, 8192); | |
| 365 | const response = try stream.reader().readRemainingAlloc(gpa, .limited(8192)); | |
| 366 | 366 | defer gpa.free(response); |
| 367 | 367 | |
| 368 | 368 | var expected_response = std.ArrayList(u8).init(gpa); |
| ... | ... | @@ -419,7 +419,7 @@ test "general client/server API coverage" { |
| 419 | 419 | }); |
| 420 | 420 | |
| 421 | 421 | const gpa = std.testing.allocator; |
| 422 | const body = try (try request.reader()).readAllAlloc(gpa, 8192); | |
| 422 | const body = try (try request.reader()).readRemainingAlloc(gpa, .limited(8192)); | |
| 423 | 423 | defer gpa.free(body); |
| 424 | 424 | |
| 425 | 425 | if (mem.startsWith(u8, request.head.target, "/get")) { |
| ... | ... | @@ -568,7 +568,7 @@ test "general client/server API coverage" { |
| 568 | 568 | try req.send(); |
| 569 | 569 | try req.wait(); |
| 570 | 570 | |
| 571 | const body = try req.reader().readAllAlloc(gpa, 8192); | |
| 571 | const body = try req.reader().readRemainingAlloc(gpa, .limited(8192)); | |
| 572 | 572 | defer gpa.free(body); |
| 573 | 573 | |
| 574 | 574 | try expectEqualStrings("Hello, World!\n", body); |
| ... | ... | @@ -593,7 +593,7 @@ test "general client/server API coverage" { |
| 593 | 593 | try req.send(); |
| 594 | 594 | try req.wait(); |
| 595 | 595 | |
| 596 | const body = try req.reader().readAllAlloc(gpa, 8192 * 1024); | |
| 596 | const body = try req.reader().readRemainingAlloc(gpa, .limited(8192 * 1024)); | |
| 597 | 597 | defer gpa.free(body); |
| 598 | 598 | |
| 599 | 599 | try expectEqual(@as(usize, 14 * 1024 + 14 * 10), body.len); |
| ... | ... | @@ -617,7 +617,7 @@ test "general client/server API coverage" { |
| 617 | 617 | try req.send(); |
| 618 | 618 | try req.wait(); |
| 619 | 619 | |
| 620 | const body = try req.reader().readAllAlloc(gpa, 8192); | |
| 620 | const body = try req.reader().readRemainingAlloc(gpa, .limited(8192)); | |
| 621 | 621 | defer gpa.free(body); |
| 622 | 622 | |
| 623 | 623 | try expectEqualStrings("", body); |
| ... | ... | @@ -643,7 +643,7 @@ test "general client/server API coverage" { |
| 643 | 643 | try req.send(); |
| 644 | 644 | try req.wait(); |
| 645 | 645 | |
| 646 | const body = try req.reader().readAllAlloc(gpa, 8192); | |
| 646 | const body = try req.reader().readRemainingAlloc(gpa, .limited(8192)); | |
| 647 | 647 | defer gpa.free(body); |
| 648 | 648 | |
| 649 | 649 | try expectEqualStrings("Hello, World!\n", body); |
| ... | ... | @@ -668,7 +668,7 @@ test "general client/server API coverage" { |
| 668 | 668 | try req.send(); |
| 669 | 669 | try req.wait(); |
| 670 | 670 | |
| 671 | const body = try req.reader().readAllAlloc(gpa, 8192); | |
| 671 | const body = try req.reader().readRemainingAlloc(gpa, .limited(8192)); | |
| 672 | 672 | defer gpa.free(body); |
| 673 | 673 | |
| 674 | 674 | try expectEqualStrings("", body); |
| ... | ... | @@ -695,7 +695,7 @@ test "general client/server API coverage" { |
| 695 | 695 | try req.send(); |
| 696 | 696 | try req.wait(); |
| 697 | 697 | |
| 698 | const body = try req.reader().readAllAlloc(gpa, 8192); | |
| 698 | const body = try req.reader().readRemainingAlloc(gpa, .limited(8192)); | |
| 699 | 699 | defer gpa.free(body); |
| 700 | 700 | |
| 701 | 701 | try expectEqualStrings("Hello, World!\n", body); |
| ... | ... | @@ -725,7 +725,7 @@ test "general client/server API coverage" { |
| 725 | 725 | |
| 726 | 726 | try std.testing.expectEqual(.ok, req.response.status); |
| 727 | 727 | |
| 728 | const body = try req.reader().readAllAlloc(gpa, 8192); | |
| 728 | const body = try req.reader().readRemainingAlloc(gpa, .limited(8192)); | |
| 729 | 729 | defer gpa.free(body); |
| 730 | 730 | |
| 731 | 731 | try expectEqualStrings("", body); |
| ... | ... | @@ -764,7 +764,7 @@ test "general client/server API coverage" { |
| 764 | 764 | try req.send(); |
| 765 | 765 | try req.wait(); |
| 766 | 766 | |
| 767 | const body = try req.reader().readAllAlloc(gpa, 8192); | |
| 767 | const body = try req.reader().readRemainingAlloc(gpa, .limited(8192)); | |
| 768 | 768 | defer gpa.free(body); |
| 769 | 769 | |
| 770 | 770 | try expectEqualStrings("Hello, World!\n", body); |
| ... | ... | @@ -788,7 +788,7 @@ test "general client/server API coverage" { |
| 788 | 788 | try req.send(); |
| 789 | 789 | try req.wait(); |
| 790 | 790 | |
| 791 | const body = try req.reader().readAllAlloc(gpa, 8192); | |
| 791 | const body = try req.reader().readRemainingAlloc(gpa, .limited(8192)); | |
| 792 | 792 | defer gpa.free(body); |
| 793 | 793 | |
| 794 | 794 | try expectEqualStrings("Hello, World!\n", body); |
| ... | ... | @@ -812,7 +812,7 @@ test "general client/server API coverage" { |
| 812 | 812 | try req.send(); |
| 813 | 813 | try req.wait(); |
| 814 | 814 | |
| 815 | const body = try req.reader().readAllAlloc(gpa, 8192); | |
| 815 | const body = try req.reader().readRemainingAlloc(gpa, .limited(8192)); | |
| 816 | 816 | defer gpa.free(body); |
| 817 | 817 | |
| 818 | 818 | try expectEqualStrings("Hello, World!\n", body); |
| ... | ... | @@ -855,7 +855,7 @@ test "general client/server API coverage" { |
| 855 | 855 | try req.send(); |
| 856 | 856 | try req.wait(); |
| 857 | 857 | |
| 858 | const body = try req.reader().readAllAlloc(gpa, 8192); | |
| 858 | const body = try req.reader().readRemainingAlloc(gpa, .limited(8192)); | |
| 859 | 859 | defer gpa.free(body); |
| 860 | 860 | |
| 861 | 861 | try expectEqualStrings("Encoded redirect successful!\n", body); |
| ... | ... | @@ -946,7 +946,7 @@ test "Server streams both reading and writing" { |
| 946 | 946 | var server = http.Server.init(&connection_br, &connection_bw); |
| 947 | 947 | var request = try server.receiveHead(); |
| 948 | 948 | var read_buffer: [100]u8 = undefined; |
| 949 | var br = try request.reader().buffered(&read_buffer); | |
| 949 | var br = (try request.reader()).buffered(&read_buffer); | |
| 950 | 950 | var response = try request.respondStreaming(.{ |
| 951 | 951 | .respond_options = .{ |
| 952 | 952 | .transfer_encoding = .none, // Causes keep_alive=false |
| ... | ... | @@ -993,7 +993,7 @@ test "Server streams both reading and writing" { |
| 993 | 993 | |
| 994 | 994 | try req.finish(); |
| 995 | 995 | |
| 996 | const body = try req.reader().readAllAlloc(std.testing.allocator, 8192); | |
| 996 | const body = try req.reader().readRemainingAlloc(std.testing.allocator, .limited(8192)); | |
| 997 | 997 | defer std.testing.allocator.free(body); |
| 998 | 998 | |
| 999 | 999 | try expectEqualStrings("ONE FISH", body); |
| ... | ... | @@ -1027,7 +1027,7 @@ fn echoTests(client: *http.Client, port: u16) !void { |
| 1027 | 1027 | |
| 1028 | 1028 | try req.wait(); |
| 1029 | 1029 | |
| 1030 | const body = try req.reader().readAllAlloc(gpa, 8192); | |
| 1030 | const body = try req.reader().readRemainingAlloc(gpa, .limited(8192)); | |
| 1031 | 1031 | defer gpa.free(body); |
| 1032 | 1032 | |
| 1033 | 1033 | try expectEqualStrings("Hello, World!\n", body); |
| ... | ... | @@ -1062,7 +1062,7 @@ fn echoTests(client: *http.Client, port: u16) !void { |
| 1062 | 1062 | |
| 1063 | 1063 | try req.wait(); |
| 1064 | 1064 | |
| 1065 | const body = try req.reader().readAllAlloc(gpa, 8192); | |
| 1065 | const body = try req.reader().readRemainingAlloc(gpa, .limited(8192)); | |
| 1066 | 1066 | defer gpa.free(body); |
| 1067 | 1067 | |
| 1068 | 1068 | try expectEqualStrings("Hello, World!\n", body); |
| ... | ... | @@ -1118,7 +1118,7 @@ fn echoTests(client: *http.Client, port: u16) !void { |
| 1118 | 1118 | try req.wait(); |
| 1119 | 1119 | try expectEqual(.ok, req.response.status); |
| 1120 | 1120 | |
| 1121 | const body = try req.reader().readAllAlloc(gpa, 8192); | |
| 1121 | const body = try req.reader().readRemainingAlloc(gpa, .limited(8192)); | |
| 1122 | 1122 | defer gpa.free(body); |
| 1123 | 1123 | |
| 1124 | 1124 | try expectEqualStrings("Hello, World!\n", body); |
| ... | ... | @@ -1258,7 +1258,7 @@ test "redirect to different connection" { |
| 1258 | 1258 | try req.send(); |
| 1259 | 1259 | try req.wait(); |
| 1260 | 1260 | |
| 1261 | const body = try req.reader().readAllAlloc(gpa, 8192); | |
| 1261 | const body = try req.reader().readRemainingAlloc(gpa, .limited(8192)); | |
| 1262 | 1262 | defer gpa.free(body); |
| 1263 | 1263 | |
| 1264 | 1264 | try expectEqualStrings("good job, you pass", body); |
lib/std/io/BufferedReader.zig+72-3| ... | ... | @@ -7,6 +7,7 @@ const testing = std.testing; |
| 7 | 7 | const BufferedWriter = std.io.BufferedWriter; |
| 8 | 8 | const Reader = std.io.Reader; |
| 9 | 9 | const Allocator = std.mem.Allocator; |
| 10 | const ArrayList = std.ArrayListUnmanaged; | |
| 10 | 11 | |
| 11 | 12 | const BufferedReader = @This(); |
| 12 | 13 | |
| ... | ... | @@ -373,6 +374,8 @@ pub fn readShort(br: *BufferedReader, buffer: []u8) Reader.ShortError!usize { |
| 373 | 374 | @panic("TODO"); |
| 374 | 375 | } |
| 375 | 376 | |
| 377 | pub const ReadAllocError = Reader.Error || Allocator.Error; | |
| 378 | ||
| 376 | 379 | /// The function is inline to avoid the dead code in case `endian` is |
| 377 | 380 | /// comptime-known and matches host endianness. |
| 378 | 381 | pub inline fn readSliceEndianAlloc( |
| ... | ... | @@ -389,8 +392,6 @@ pub inline fn readSliceEndianAlloc( |
| 389 | 392 | return dest; |
| 390 | 393 | } |
| 391 | 394 | |
| 392 | pub const ReadAllocError = Reader.Error || Allocator.Error; | |
| 393 | ||
| 394 | 395 | pub fn readSliceAlloc(br: *BufferedReader, allocator: Allocator, len: usize) ReadAllocError![]u8 { |
| 395 | 396 | const dest = try allocator.alloc(u8, len); |
| 396 | 397 | errdefer allocator.free(dest); |
| ... | ... | @@ -398,6 +399,74 @@ pub fn readSliceAlloc(br: *BufferedReader, allocator: Allocator, len: usize) Rea |
| 398 | 399 | return dest; |
| 399 | 400 | } |
| 400 | 401 | |
| 402 | /// Transfers all bytes from the current position to the end of the stream, up | |
| 403 | /// to `limit`, returning them as a caller-owned allocated slice. | |
| 404 | /// | |
| 405 | /// If `limit` is exceeded, returns `error.StreamTooLong`. In such case, the | |
| 406 | /// stream is advanced an unspecified amount, and the consumed data is | |
| 407 | /// unrecoverable. The other function listed below does not have this caveat. | |
| 408 | /// | |
| 409 | /// Asserts `br` was initialized with at least one byte of storage capacity. | |
| 410 | /// | |
| 411 | /// See also: | |
| 412 | /// * `readRemainingArrayList` | |
| 413 | pub fn readRemainingAlloc(r: Reader, gpa: Allocator, limit: Reader.Limit) Reader.LimitedAllocError![]u8 { | |
| 414 | var buffer: ArrayList(u8) = .empty; | |
| 415 | defer buffer.deinit(gpa); | |
| 416 | try readRemainingArrayList(r, gpa, null, &buffer, limit); | |
| 417 | return buffer.toOwnedSlice(gpa); | |
| 418 | } | |
| 419 | ||
| 420 | /// Transfers all bytes from the current position to the end of the stream, up | |
| 421 | /// to `limit`, appending them to `list`. | |
| 422 | /// | |
| 423 | /// If `limit` would be exceeded, `error.StreamTooLong` is returned instead. In | |
| 424 | /// such case, the stream is in a well-defined state. The next byte that would | |
| 425 | /// be read will be the first one to exceed `limit`, and all preceeding bytes | |
| 426 | /// have been appended to `list`. | |
| 427 | /// | |
| 428 | /// Asserts `br` was initialized with at least one byte of storage capacity. | |
| 429 | /// | |
| 430 | /// See also: | |
| 431 | /// * `readRemainingAlloc` | |
| 432 | pub fn readRemainingArrayList( | |
| 433 | br: *BufferedReader, | |
| 434 | gpa: Allocator, | |
| 435 | comptime alignment: ?std.mem.Alignment, | |
| 436 | list: *std.ArrayListAlignedUnmanaged(u8, alignment), | |
| 437 | limit: Reader.Limit, | |
| 438 | ) Reader.LimitedAllocError!void { | |
| 439 | const buffer = br.buffer; | |
| 440 | const buffered = buffer[br.seek..br.end]; | |
| 441 | const copy_len = limit.minInt(buffered.len); | |
| 442 | try list.ensureUnusedCapacity(gpa, copy_len); | |
| 443 | @memcpy(list.unusedCapacitySlice()[0..copy_len], buffer[0..copy_len]); | |
| 444 | list.items.len += copy_len; | |
| 445 | br.seek += copy_len; | |
| 446 | if (copy_len == buffered.len) { | |
| 447 | br.seek = 0; | |
| 448 | br.end = 0; | |
| 449 | } | |
| 450 | var remaining = limit.subtract(copy_len).?; | |
| 451 | while (true) { | |
| 452 | try list.ensureUnusedCapacity(gpa, 1); | |
| 453 | const dest = remaining.slice(list.unusedCapacitySlice()); | |
| 454 | const additional_buffer = if (@intFromEnum(remaining) == dest.len) buffer else &.{}; | |
| 455 | const n = br.unbuffered_reader.readVec(&.{ dest, additional_buffer }) catch |err| switch (err) { | |
| 456 | error.EndOfStream => break, | |
| 457 | error.ReadFailed => return error.ReadFailed, | |
| 458 | }; | |
| 459 | if (n >= dest.len) { | |
| 460 | br.end = n - dest.len; | |
| 461 | list.items.len += dest.len; | |
| 462 | if (n == dest.len) return; | |
| 463 | return error.StreamTooLong; | |
| 464 | } | |
| 465 | list.items.len += n; | |
| 466 | remaining = remaining.subtract(n).?; | |
| 467 | } | |
| 468 | } | |
| 469 | ||
| 401 | 470 | pub const DelimiterInclusiveError = error{ |
| 402 | 471 | /// See the `Reader` implementation for detailed diagnostics. |
| 403 | 472 | ReadFailed, |
| ... | ... | @@ -775,7 +844,7 @@ pub fn writableSliceGreedyAlloc( |
| 775 | 844 | br.seek = 0; |
| 776 | 845 | } |
| 777 | 846 | { |
| 778 | var list: std.ArrayListUnmanaged(u8) = .{ | |
| 847 | var list: ArrayList(u8) = .{ | |
| 779 | 848 | .items = br.buffer[0..br.end], |
| 780 | 849 | .capacity = br.buffer.len, |
| 781 | 850 | }; |
lib/std/io/Reader.zig+49-19| ... | ... | @@ -2,6 +2,9 @@ const std = @import("../std.zig"); |
| 2 | 2 | const Reader = @This(); |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | 4 | const BufferedWriter = std.io.BufferedWriter; |
| 5 | const BufferedReader = std.io.BufferedReader; | |
| 6 | const Allocator = std.mem.Allocator; | |
| 7 | const ArrayList = std.ArrayListUnmanaged; | |
| 5 | 8 | |
| 6 | 9 | context: ?*anyopaque, |
| 7 | 10 | vtable: *const VTable, |
| ... | ... | @@ -166,29 +169,56 @@ pub fn discardRemaining(r: Reader) ShortError!usize { |
| 166 | 169 | } |
| 167 | 170 | } |
| 168 | 171 | |
| 169 | pub const ReadAllocError = std.mem.Allocator.Error || ShortError; | |
| 172 | pub const LimitedAllocError = Allocator.Error || ShortError || error{StreamTooLong}; | |
| 170 | 173 | |
| 171 | /// Allocates enough memory to hold all the contents of the stream. If the allocated | |
| 172 | /// memory would be greater than `max_size`, returns `error.StreamTooLong`. | |
| 174 | /// Transfers all bytes from the current position to the end of the stream, up | |
| 175 | /// to `limit`, returning them as a caller-owned allocated slice. | |
| 173 | 176 | /// |
| 174 | /// Caller owns returned memory. | |
| 177 | /// If `limit` is exceeded, returns `error.StreamTooLong`. In such case, the | |
| 178 | /// stream is advanced one byte beyond the limit, and the consumed data is | |
| 179 | /// unrecoverable. Other functions listed below do not have this caveat. | |
| 175 | 180 | /// |
| 176 | /// If this function returns an error, the contents from the stream read so far are lost. | |
| 177 | pub fn readAlloc(r: Reader, gpa: std.mem.Allocator, max_size: usize) ReadAllocError![]u8 { | |
| 178 | const readFn = r.vtable.read; | |
| 179 | var aw: std.io.AllocatingWriter = undefined; | |
| 180 | aw.init(gpa); | |
| 181 | errdefer aw.deinit(); | |
| 182 | var remaining = max_size; | |
| 183 | while (remaining > 0) { | |
| 184 | const n = readFn(r.context, &aw.buffered_writer, .limited(remaining)) catch |err| switch (err) { | |
| 185 | error.WriteFailed => return error.OutOfMemory, | |
| 186 | error.EndOfStream => break, | |
| 181 | /// See also: | |
| 182 | /// * `readRemainingArrayList` | |
| 183 | /// * `BufferedReader.readRemainingArrayList` | |
| 184 | pub fn readRemainingAlloc(r: Reader, gpa: Allocator, limit: Reader.Limit) LimitedAllocError![]u8 { | |
| 185 | var buffer: ArrayList(u8) = .empty; | |
| 186 | defer buffer.deinit(gpa); | |
| 187 | try readRemainingArrayList(r, gpa, null, &buffer, limit); | |
| 188 | return buffer.toOwnedSlice(gpa); | |
| 189 | } | |
| 190 | ||
| 191 | /// Transfers all bytes from the current position to the end of the stream, up | |
| 192 | /// to `limit`, appending them to `list`. | |
| 193 | /// | |
| 194 | /// If `limit` is exceeded: | |
| 195 | /// * The array list's length is increased by exactly one byte past `limit`. | |
| 196 | /// * The stream seek position is advanced by exactly one byte past `limit`. | |
| 197 | /// * `error.StreamTooLong` is returned. | |
| 198 | /// | |
| 199 | /// The other function listed below has different semantics for an exceeded | |
| 200 | /// limit. | |
| 201 | /// | |
| 202 | /// See also: | |
| 203 | /// * `BufferedReader.readRemainingArrayList` | |
| 204 | pub fn readRemainingArrayList( | |
| 205 | r: Reader, | |
| 206 | gpa: Allocator, | |
| 207 | comptime alignment: ?std.mem.Alignment, | |
| 208 | list: *std.ArrayListAlignedUnmanaged(u8, alignment), | |
| 209 | limit: Limit, | |
| 210 | ) LimitedAllocError!void { | |
| 211 | var remaining = limit; | |
| 212 | while (true) { | |
| 213 | try list.ensureUnusedCapacity(gpa, 1); | |
| 214 | const buffer = remaining.slice1(list.unusedCapacitySlice()); | |
| 215 | const n = r.vtable.readVec(r.context, &.{buffer}) catch |err| switch (err) { | |
| 216 | error.EndOfStream => return, | |
| 187 | 217 | error.ReadFailed => return error.ReadFailed, |
| 188 | 218 | }; |
| 189 | remaining -= n; | |
| 219 | list.items.len += n; | |
| 220 | remaining = remaining.subtract(n) orelse return error.StreamTooLong; | |
| 190 | 221 | } |
| 191 | return aw.toOwnedSlice(); | |
| 192 | 222 | } |
| 193 | 223 | |
| 194 | 224 | pub const failing: Reader = .{ |
| ... | ... | @@ -209,11 +239,11 @@ pub const ending: Reader = .{ |
| 209 | 239 | }, |
| 210 | 240 | }; |
| 211 | 241 | |
| 212 | pub fn unbuffered(r: Reader) std.io.BufferedReader { | |
| 242 | pub fn unbuffered(r: Reader) BufferedReader { | |
| 213 | 243 | return buffered(r, &.{}); |
| 214 | 244 | } |
| 215 | 245 | |
| 216 | pub fn buffered(r: Reader, buffer: []u8) std.io.BufferedReader { | |
| 246 | pub fn buffered(r: Reader, buffer: []u8) BufferedReader { | |
| 217 | 247 | return .{ |
| 218 | 248 | .unbuffered_reader = r, |
| 219 | 249 | .seek = 0, |