| author | |
| committer | |
| log | 25ac70f973c84bb26ebb1b69eda30d2c6207c9b0 |
| tree | 1a91822da4b33bf25e14bddc65c3ef51b984e69b |
| parent | c7040171fb06bc4300547a9f4550346b847fc406 |
delete some bad readers/writers
add limited reader
update TLS
about to do something drastic to compress18 files changed, 390 insertions(+), 439 deletions(-)
lib/std/compress.zig+4-60| ... | @@ -1,75 +1,19 @@ | ... | @@ -1,75 +1,19 @@ |
| 1 | //! Compression algorithms. | 1 | //! Compression algorithms. |
| 2 | 2 | ||
| 3 | const std = @import("std.zig"); | ||
| 4 | |||
| 5 | pub const flate = @import("compress/flate.zig"); | 3 | pub const flate = @import("compress/flate.zig"); |
| 6 | pub const gzip = @import("compress/gzip.zig"); | 4 | pub const gzip = @import("compress/gzip.zig"); |
| 7 | pub const zlib = @import("compress/zlib.zig"); | ||
| 8 | pub const lzma = @import("compress/lzma.zig"); | 5 | pub const lzma = @import("compress/lzma.zig"); |
| 9 | pub const lzma2 = @import("compress/lzma2.zig"); | 6 | pub const lzma2 = @import("compress/lzma2.zig"); |
| 10 | pub const xz = @import("compress/xz.zig"); | 7 | pub const xz = @import("compress/xz.zig"); |
| 8 | pub const zlib = @import("compress/zlib.zig"); | ||
| 11 | pub const zstd = @import("compress/zstandard.zig"); | 9 | pub const zstd = @import("compress/zstandard.zig"); |
| 12 | 10 | ||
| 13 | pub fn HashedReader(ReaderType: type, HasherType: type) type { | ||
| 14 | return struct { | ||
| 15 | child_reader: ReaderType, | ||
| 16 | hasher: HasherType, | ||
| 17 | |||
| 18 | pub const Error = ReaderType.Error; | ||
| 19 | pub const Reader = std.io.Reader(*@This(), Error, read); | ||
| 20 | |||
| 21 | pub fn read(self: *@This(), buf: []u8) Error!usize { | ||
| 22 | const amt = try self.child_reader.read(buf); | ||
| 23 | self.hasher.update(buf[0..amt]); | ||
| 24 | return amt; | ||
| 25 | } | ||
| 26 | |||
| 27 | pub fn reader(self: *@This()) Reader { | ||
| 28 | return .{ .context = self }; | ||
| 29 | } | ||
| 30 | }; | ||
| 31 | } | ||
| 32 | |||
| 33 | pub fn hashedReader( | ||
| 34 | reader: anytype, | ||
| 35 | hasher: anytype, | ||
| 36 | ) HashedReader(@TypeOf(reader), @TypeOf(hasher)) { | ||
| 37 | return .{ .child_reader = reader, .hasher = hasher }; | ||
| 38 | } | ||
| 39 | |||
| 40 | pub fn HashedWriter(WriterType: type, HasherType: type) type { | ||
| 41 | return struct { | ||
| 42 | child_writer: WriterType, | ||
| 43 | hasher: HasherType, | ||
| 44 | |||
| 45 | pub const Error = WriterType.Error; | ||
| 46 | pub const Writer = std.io.Writer(*@This(), Error, write); | ||
| 47 | |||
| 48 | pub fn write(self: *@This(), buf: []const u8) Error!usize { | ||
| 49 | const amt = try self.child_writer.write(buf); | ||
| 50 | self.hasher.update(buf[0..amt]); | ||
| 51 | return amt; | ||
| 52 | } | ||
| 53 | |||
| 54 | pub fn writer(self: *@This()) Writer { | ||
| 55 | return .{ .context = self }; | ||
| 56 | } | ||
| 57 | }; | ||
| 58 | } | ||
| 59 | |||
| 60 | pub fn hashedWriter( | ||
| 61 | writer: anytype, | ||
| 62 | hasher: anytype, | ||
| 63 | ) HashedWriter(@TypeOf(writer), @TypeOf(hasher)) { | ||
| 64 | return .{ .child_writer = writer, .hasher = hasher }; | ||
| 65 | } | ||
| 66 | |||
| 67 | test { | 11 | test { |
| 12 | _ = flate; | ||
| 13 | _ = gzip; | ||
| 68 | _ = lzma; | 14 | _ = lzma; |
| 69 | _ = lzma2; | 15 | _ = lzma2; |
| 70 | _ = xz; | 16 | _ = xz; |
| 71 | _ = zstd; | ||
| 72 | _ = flate; | ||
| 73 | _ = gzip; | ||
| 74 | _ = zlib; | 17 | _ = zlib; |
| 18 | _ = zstd; | ||
| 75 | } | 19 | } |
lib/std/compress/flate/inflate.zig+1-1| ... | @@ -821,7 +821,7 @@ pub fn BitReader(comptime T: type) type { | ... | @@ -821,7 +821,7 @@ pub fn BitReader(comptime T: type) type { |
| 821 | /// Skip zero terminated string. | 821 | /// Skip zero terminated string. |
| 822 | pub fn skipStringZ(self: *Self) !void { | 822 | pub fn skipStringZ(self: *Self) !void { |
| 823 | while (true) { | 823 | while (true) { |
| 824 | if (try self.readF(u8, 0) == 0) break; | 824 | if (try self.readF(u8, .{}) == 0) break; |
| 825 | } | 825 | } |
| 826 | } | 826 | } |
| 827 | 827 |
lib/std/compress/zstandard.zig+110-76| ... | @@ -1,7 +1,12 @@ | ... | @@ -1,7 +1,12 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("../std.zig"); |
| 2 | const RingBuffer = std.RingBuffer; | 2 | const RingBuffer = std.RingBuffer; |
| 3 | 3 | ||
| 4 | const types = @import("zstandard/types.zig"); | 4 | const types = @import("zstandard/types.zig"); |
| 5 | |||
| 6 | /// Recommended amount by the standard. Lower than this may result in inability | ||
| 7 | /// to decompress common streams. | ||
| 8 | pub const default_window_len = 8 * 1024 * 1024; | ||
| 9 | |||
| 5 | pub const frame = types.frame; | 10 | pub const frame = types.frame; |
| 6 | pub const compressed_block = types.compressed_block; | 11 | pub const compressed_block = types.compressed_block; |
| 7 | 12 | ||
| ... | @@ -10,7 +15,8 @@ pub const decompress = @import("zstandard/decompress.zig"); | ... | @@ -10,7 +15,8 @@ pub const decompress = @import("zstandard/decompress.zig"); |
| 10 | pub const Decompressor = struct { | 15 | pub const Decompressor = struct { |
| 11 | const table_size_max = types.compressed_block.table_size_max; | 16 | const table_size_max = types.compressed_block.table_size_max; |
| 12 | 17 | ||
| 13 | source: *std.io.BufferedReader, | 18 | input: *std.io.BufferedReader, |
| 19 | bytes_read: usize, | ||
| 14 | state: enum { NewFrame, InFrame, LastBlock }, | 20 | state: enum { NewFrame, InFrame, LastBlock }, |
| 15 | decode_state: decompress.block.DecodeState, | 21 | decode_state: decompress.block.DecodeState, |
| 16 | frame_context: decompress.FrameContext, | 22 | frame_context: decompress.FrameContext, |
| ... | @@ -23,14 +29,12 @@ pub const Decompressor = struct { | ... | @@ -23,14 +29,12 @@ pub const Decompressor = struct { |
| 23 | verify_checksum: bool, | 29 | verify_checksum: bool, |
| 24 | checksum: ?u32, | 30 | checksum: ?u32, |
| 25 | current_frame_decompressed_size: usize, | 31 | current_frame_decompressed_size: usize, |
| 32 | err: ?Error = null, | ||
| 26 | 33 | ||
| 27 | pub const Options = struct { | 34 | pub const Options = struct { |
| 28 | verify_checksum: bool = true, | 35 | verify_checksum: bool = true, |
| 36 | /// See `default_window_len`. | ||
| 29 | window_buffer: []u8, | 37 | window_buffer: []u8, |
| 30 | |||
| 31 | /// Recommended amount by the standard. Lower than this may result | ||
| 32 | /// in inability to decompress common streams. | ||
| 33 | pub const default_window_buffer_len = 8 * 1024 * 1024; | ||
| 34 | }; | 38 | }; |
| 35 | 39 | ||
| 36 | const WindowBuffer = struct { | 40 | const WindowBuffer = struct { |
| ... | @@ -45,11 +49,13 @@ pub const Decompressor = struct { | ... | @@ -45,11 +49,13 @@ pub const Decompressor = struct { |
| 45 | MalformedBlock, | 49 | MalformedBlock, |
| 46 | MalformedFrame, | 50 | MalformedFrame, |
| 47 | OutOfMemory, | 51 | OutOfMemory, |
| 52 | EndOfStream, | ||
| 48 | }; | 53 | }; |
| 49 | 54 | ||
| 50 | pub fn init(source: *std.io.BufferedReader, options: Options) Decompressor { | 55 | pub fn init(input: *std.io.BufferedReader, options: Options) Decompressor { |
| 51 | return .{ | 56 | return .{ |
| 52 | .source = source, | 57 | .input = input, |
| 58 | .bytes_read = 0, | ||
| 53 | .state = .NewFrame, | 59 | .state = .NewFrame, |
| 54 | .decode_state = undefined, | 60 | .decode_state = undefined, |
| 55 | .frame_context = undefined, | 61 | .frame_context = undefined, |
| ... | @@ -65,100 +71,128 @@ pub const Decompressor = struct { | ... | @@ -65,100 +71,128 @@ pub const Decompressor = struct { |
| 65 | }; | 71 | }; |
| 66 | } | 72 | } |
| 67 | 73 | ||
| 68 | fn frameInit(self: *Decompressor) !void { | 74 | fn frameInit(d: *Decompressor) !void { |
| 69 | const source_reader = self.source; | 75 | const in = d.input; |
| 70 | switch (try decompress.decodeFrameHeader(source_reader)) { | 76 | switch (try decompress.decodeFrameHeader(in, &d.bytes_read)) { |
| 71 | .skippable => |header| { | 77 | .skippable => |header| { |
| 72 | try source_reader.skipBytes(header.frame_size, .{}); | 78 | try in.discardAll(header.frame_size); |
| 73 | self.state = .NewFrame; | 79 | d.bytes_read += header.frame_size; |
| 80 | d.state = .NewFrame; | ||
| 74 | }, | 81 | }, |
| 75 | .zstandard => |header| { | 82 | .zstandard => |header| { |
| 76 | const frame_context = try decompress.FrameContext.init( | 83 | const frame_context = try decompress.FrameContext.init( |
| 77 | header, | 84 | header, |
| 78 | self.buffer.data.len, | 85 | d.buffer.data.len, |
| 79 | self.verify_checksum, | 86 | d.verify_checksum, |
| 80 | ); | 87 | ); |
| 81 | 88 | ||
| 82 | const decode_state = decompress.block.DecodeState.init( | 89 | const decode_state = decompress.block.DecodeState.init( |
| 83 | &self.literal_fse_buffer, | 90 | &d.literal_fse_buffer, |
| 84 | &self.match_fse_buffer, | 91 | &d.match_fse_buffer, |
| 85 | &self.offset_fse_buffer, | 92 | &d.offset_fse_buffer, |
| 86 | ); | 93 | ); |
| 87 | 94 | ||
| 88 | self.decode_state = decode_state; | 95 | d.decode_state = decode_state; |
| 89 | self.frame_context = frame_context; | 96 | d.frame_context = frame_context; |
| 90 | 97 | ||
| 91 | self.checksum = null; | 98 | d.checksum = null; |
| 92 | self.current_frame_decompressed_size = 0; | 99 | d.current_frame_decompressed_size = 0; |
| 93 | 100 | ||
| 94 | self.state = .InFrame; | 101 | d.state = .InFrame; |
| 95 | }, | 102 | }, |
| 96 | } | 103 | } |
| 97 | } | 104 | } |
| 98 | 105 | ||
| 99 | pub fn reader(self: *Decompressor) std.io.Reader { | 106 | pub fn reader(self: *Decompressor) std.io.Reader { |
| 100 | return .{ .context = self }; | 107 | return .{ |
| 108 | .context = self, | ||
| 109 | .vtable = &.{ | ||
| 110 | .read = read, | ||
| 111 | .readVec = readVec, | ||
| 112 | .discard = discard, | ||
| 113 | }, | ||
| 114 | }; | ||
| 101 | } | 115 | } |
| 102 | 116 | ||
| 103 | pub fn read(self: *Decompressor, buffer: []u8) Error!usize { | 117 | fn read(context: ?*anyopaque, bw: *std.io.BufferedWriter, limit: std.io.Reader.Limit) std.io.Reader.RwError!usize { |
| 104 | if (buffer.len == 0) return 0; | 118 | const buf = limit.slice(try bw.writableSliceGreedy(1)); |
| 105 | 119 | const n = try readVec(context, &.{buf}); | |
| 106 | var size: usize = 0; | 120 | bw.advance(n); |
| 107 | while (size == 0) { | 121 | return n; |
| 108 | while (self.state == .NewFrame) { | 122 | } |
| 109 | const initial_count = self.source.bytes_read; | 123 | |
| 110 | self.frameInit() catch |err| switch (err) { | 124 | fn discard(context: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize { |
| 111 | error.DictionaryIdFlagUnsupported => return error.DictionaryIdFlagUnsupported, | 125 | var trash: [128]u8 = undefined; |
| 112 | error.EndOfStream => return if (self.source.bytes_read == initial_count) | 126 | const buf = limit.slice(&trash); |
| 113 | 0 | 127 | return readVec(context, &.{buf}); |
| 114 | else | 128 | } |
| 115 | error.MalformedFrame, | 129 | |
| 116 | else => return error.MalformedFrame, | 130 | fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { |
| 117 | }; | 131 | const d: *Decompressor = @ptrCast(@alignCast(context)); |
| 118 | } | 132 | if (data.len == 0) return 0; |
| 119 | size = try self.readInner(buffer); | 133 | const buffer = data[0]; |
| 134 | while (d.state == .NewFrame) { | ||
| 135 | const initial_count = d.bytes_read; | ||
| 136 | d.frameInit() catch |err| switch (err) { | ||
| 137 | error.DictionaryIdFlagUnsupported => { | ||
| 138 | d.err = error.DictionaryIdFlagUnsupported; | ||
| 139 | return error.ReadFailed; | ||
| 140 | }, | ||
| 141 | error.EndOfStream => { | ||
| 142 | if (d.bytes_read == initial_count) return error.EndOfStream; | ||
| 143 | d.err = error.MalformedFrame; | ||
| 144 | return error.ReadFailed; | ||
| 145 | }, | ||
| 146 | else => { | ||
| 147 | d.err = error.MalformedFrame; | ||
| 148 | return error.ReadFailed; | ||
| 149 | }, | ||
| 150 | }; | ||
| 120 | } | 151 | } |
| 121 | return size; | 152 | return d.readInner(buffer) catch |err| { |
| 153 | d.err = err; | ||
| 154 | return error.ReadFailed; | ||
| 155 | }; | ||
| 122 | } | 156 | } |
| 123 | 157 | ||
| 124 | fn readInner(self: *Decompressor, buffer: []u8) Error!usize { | 158 | fn readInner(d: *Decompressor, buffer: []u8) Error!usize { |
| 125 | std.debug.assert(self.state != .NewFrame); | 159 | std.debug.assert(d.state != .NewFrame); |
| 126 | 160 | ||
| 127 | var ring_buffer = RingBuffer{ | 161 | var ring_buffer = RingBuffer{ |
| 128 | .data = self.buffer.data, | 162 | .data = d.buffer.data, |
| 129 | .read_index = self.buffer.read_index, | 163 | .read_index = d.buffer.read_index, |
| 130 | .write_index = self.buffer.write_index, | 164 | .write_index = d.buffer.write_index, |
| 131 | }; | 165 | }; |
| 132 | defer { | 166 | defer { |
| 133 | self.buffer.read_index = ring_buffer.read_index; | 167 | d.buffer.read_index = ring_buffer.read_index; |
| 134 | self.buffer.write_index = ring_buffer.write_index; | 168 | d.buffer.write_index = ring_buffer.write_index; |
| 135 | } | 169 | } |
| 136 | 170 | ||
| 137 | const source_reader = self.source; | 171 | const in = d.input; |
| 138 | while (ring_buffer.isEmpty() and self.state != .LastBlock) { | 172 | while (ring_buffer.isEmpty() and d.state != .LastBlock) { |
| 139 | const header_bytes = source_reader.readBytesNoEof(3) catch | 173 | const header_bytes = try in.takeArray(3); |
| 140 | return error.MalformedFrame; | 174 | d.bytes_read += header_bytes.len; |
| 141 | const block_header = decompress.block.decodeBlockHeader(&header_bytes); | 175 | const block_header = decompress.block.decodeBlockHeader(header_bytes); |
| 142 | 176 | ||
| 143 | decompress.block.decodeBlockReader( | 177 | decompress.block.decodeBlockReader( |
| 144 | &ring_buffer, | 178 | &ring_buffer, |
| 145 | source_reader, | 179 | in, |
| 180 | &d.bytes_read, | ||
| 146 | block_header, | 181 | block_header, |
| 147 | &self.decode_state, | 182 | &d.decode_state, |
| 148 | self.frame_context.block_size_max, | 183 | d.frame_context.block_size_max, |
| 149 | &self.literals_buffer, | 184 | &d.literals_buffer, |
| 150 | &self.sequence_buffer, | 185 | &d.sequence_buffer, |
| 151 | ) catch | 186 | ) catch return error.MalformedBlock; |
| 152 | return error.MalformedBlock; | 187 | |
| 153 | 188 | if (d.frame_context.content_size) |size| { | |
| 154 | if (self.frame_context.content_size) |size| { | 189 | if (d.current_frame_decompressed_size > size) return error.MalformedFrame; |
| 155 | if (self.current_frame_decompressed_size > size) return error.MalformedFrame; | ||
| 156 | } | 190 | } |
| 157 | 191 | ||
| 158 | const size = ring_buffer.len(); | 192 | const size = ring_buffer.len(); |
| 159 | self.current_frame_decompressed_size += size; | 193 | d.current_frame_decompressed_size += size; |
| 160 | 194 | ||
| 161 | if (self.frame_context.hasher_opt) |*hasher| { | 195 | if (d.frame_context.hasher_opt) |*hasher| { |
| 162 | if (size > 0) { | 196 | if (size > 0) { |
| 163 | const written_slice = ring_buffer.sliceLast(size); | 197 | const written_slice = ring_buffer.sliceLast(size); |
| 164 | hasher.update(written_slice.first); | 198 | hasher.update(written_slice.first); |
| ... | @@ -166,19 +200,19 @@ pub const Decompressor = struct { | ... | @@ -166,19 +200,19 @@ pub const Decompressor = struct { |
| 166 | } | 200 | } |
| 167 | } | 201 | } |
| 168 | if (block_header.last_block) { | 202 | if (block_header.last_block) { |
| 169 | self.state = .LastBlock; | 203 | d.state = .LastBlock; |
| 170 | if (self.frame_context.has_checksum) { | 204 | if (d.frame_context.has_checksum) { |
| 171 | const checksum = source_reader.readInt(u32, .little) catch | 205 | const checksum = in.readInt(u32, .little) catch return error.MalformedFrame; |
| 172 | return error.MalformedFrame; | 206 | d.bytes_read += 4; |
| 173 | if (self.verify_checksum) { | 207 | if (d.verify_checksum) { |
| 174 | if (self.frame_context.hasher_opt) |*hasher| { | 208 | if (d.frame_context.hasher_opt) |*hasher| { |
| 175 | if (checksum != decompress.computeChecksum(hasher)) | 209 | if (checksum != decompress.computeChecksum(hasher)) |
| 176 | return error.ChecksumFailure; | 210 | return error.ChecksumFailure; |
| 177 | } | 211 | } |
| 178 | } | 212 | } |
| 179 | } | 213 | } |
| 180 | if (self.frame_context.content_size) |content_size| { | 214 | if (d.frame_context.content_size) |content_size| { |
| 181 | if (content_size != self.current_frame_decompressed_size) { | 215 | if (content_size != d.current_frame_decompressed_size) { |
| 182 | return error.MalformedFrame; | 216 | return error.MalformedFrame; |
| 183 | } | 217 | } |
| 184 | } | 218 | } |
| ... | @@ -189,8 +223,8 @@ pub const Decompressor = struct { | ... | @@ -189,8 +223,8 @@ pub const Decompressor = struct { |
| 189 | if (size > 0) { | 223 | if (size > 0) { |
| 190 | ring_buffer.readFirstAssumeLength(buffer, size); | 224 | ring_buffer.readFirstAssumeLength(buffer, size); |
| 191 | } | 225 | } |
| 192 | if (self.state == .LastBlock and ring_buffer.len() == 0) { | 226 | if (d.state == .LastBlock and ring_buffer.len() == 0) { |
| 193 | self.state = .NewFrame; | 227 | d.state = .NewFrame; |
| 194 | } | 228 | } |
| 195 | return size; | 229 | return size; |
| 196 | } | 230 | } |
lib/std/compress/zstandard/decode/block.zig+10-6| ... | @@ -807,7 +807,8 @@ pub fn decodeBlockRingBuffer( | ... | @@ -807,7 +807,8 @@ pub fn decodeBlockRingBuffer( |
| 807 | /// contain enough bytes. | 807 | /// contain enough bytes. |
| 808 | pub fn decodeBlockReader( | 808 | pub fn decodeBlockReader( |
| 809 | dest: *RingBuffer, | 809 | dest: *RingBuffer, |
| 810 | source: anytype, | 810 | in: *std.io.BufferedReader, |
| 811 | bytes_read: *usize, | ||
| 811 | block_header: frame.Zstandard.Block.Header, | 812 | block_header: frame.Zstandard.Block.Header, |
| 812 | decode_state: *DecodeState, | 813 | decode_state: *DecodeState, |
| 813 | block_size_max: usize, | 814 | block_size_max: usize, |
| ... | @@ -815,26 +816,29 @@ pub fn decodeBlockReader( | ... | @@ -815,26 +816,29 @@ pub fn decodeBlockReader( |
| 815 | sequence_buffer: []u8, | 816 | sequence_buffer: []u8, |
| 816 | ) !void { | 817 | ) !void { |
| 817 | const block_size = block_header.block_size; | 818 | const block_size = block_header.block_size; |
| 818 | var block_reader_limited = std.io.limitedReader(source, block_size); | ||
| 819 | const block_reader = block_reader_limited.reader(); | ||
| 820 | if (block_size_max < block_size) return error.BlockSizeOverMaximum; | 819 | if (block_size_max < block_size) return error.BlockSizeOverMaximum; |
| 821 | switch (block_header.block_type) { | 820 | switch (block_header.block_type) { |
| 822 | .raw => { | 821 | .raw => { |
| 823 | if (block_size == 0) return; | 822 | if (block_size == 0) return; |
| 824 | const slice = dest.sliceAt(dest.write_index, block_size); | 823 | const slice = dest.sliceAt(dest.write_index, block_size); |
| 825 | try source.readNoEof(slice.first); | 824 | var vecs: [2][]u8 = &.{slice.first, slice.second }; |
| 826 | try source.readNoEof(slice.second); | 825 | try in.readVecAll(&vecs); |
| 826 | assert(slice.first.len + slice.second.len == block_size); | ||
| 827 | bytes_read.* += block_size; | ||
| 827 | dest.write_index = dest.mask2(dest.write_index + block_size); | 828 | dest.write_index = dest.mask2(dest.write_index + block_size); |
| 828 | decode_state.written_count += block_size; | 829 | decode_state.written_count += block_size; |
| 829 | }, | 830 | }, |
| 830 | .rle => { | 831 | .rle => { |
| 831 | const byte = try source.readByte(); | 832 | const byte = try in.takeByte(); |
| 833 | bytes_read.* += 1; | ||
| 832 | for (0..block_size) |_| { | 834 | for (0..block_size) |_| { |
| 833 | dest.writeAssumeCapacity(byte); | 835 | dest.writeAssumeCapacity(byte); |
| 834 | } | 836 | } |
| 835 | decode_state.written_count += block_size; | 837 | decode_state.written_count += block_size; |
| 836 | }, | 838 | }, |
| 837 | .compressed => { | 839 | .compressed => { |
| 840 | var block_reader_limited = std.io.limitedReader(source, block_size); | ||
| 841 | const block_reader = block_reader_limited.reader(); | ||
| 838 | const literals = try decodeLiteralsSection(block_reader, literals_buffer); | 842 | const literals = try decodeLiteralsSection(block_reader, literals_buffer); |
| 839 | const sequences_header = try decodeSequencesHeader(block_reader); | 843 | const sequences_header = try decodeSequencesHeader(block_reader); |
| 840 | 844 |
lib/std/compress/zstandard/decompress.zig+14-9| ... | @@ -50,7 +50,7 @@ pub const FrameHeader = union(enum) { | ... | @@ -50,7 +50,7 @@ pub const FrameHeader = union(enum) { |
| 50 | skippable: SkippableHeader, | 50 | skippable: SkippableHeader, |
| 51 | }; | 51 | }; |
| 52 | 52 | ||
| 53 | pub const HeaderError = error{ BadMagic, EndOfStream, ReservedBitSet }; | 53 | pub const HeaderError = error{ ReadFailed, BadMagic, EndOfStream, ReservedBitSet }; |
| 54 | 54 | ||
| 55 | /// Returns the header of the frame at the beginning of `source`. | 55 | /// Returns the header of the frame at the beginning of `source`. |
| 56 | /// | 56 | /// |
| ... | @@ -61,16 +61,21 @@ pub const HeaderError = error{ BadMagic, EndOfStream, ReservedBitSet }; | ... | @@ -61,16 +61,21 @@ pub const HeaderError = error{ BadMagic, EndOfStream, ReservedBitSet }; |
| 61 | /// - `error.EndOfStream` if `source` contains fewer than 4 bytes | 61 | /// - `error.EndOfStream` if `source` contains fewer than 4 bytes |
| 62 | /// - `error.ReservedBitSet` if the frame is a Zstandard frame and any of the | 62 | /// - `error.ReservedBitSet` if the frame is a Zstandard frame and any of the |
| 63 | /// reserved bits are set | 63 | /// reserved bits are set |
| 64 | pub fn decodeFrameHeader(source: anytype) (@TypeOf(source).Error || HeaderError)!FrameHeader { | 64 | pub fn decodeFrameHeader(br: *std.io.BufferedReader, bytes_read: *usize) HeaderError!FrameHeader { |
| 65 | const magic = try source.readInt(u32, .little); | 65 | const magic = try br.readInt(u32, .little); |
| 66 | bytes_read.* += 4; | ||
| 66 | const frame_type = try frameType(magic); | 67 | const frame_type = try frameType(magic); |
| 67 | switch (frame_type) { | 68 | switch (frame_type) { |
| 68 | .zstandard => return FrameHeader{ .zstandard = try decodeZstandardHeader(source) }, | 69 | .zstandard => return .{ .zstandard = try decodeZstandardHeader(br, bytes_read) }, |
| 69 | .skippable => return FrameHeader{ | 70 | .skippable => { |
| 70 | .skippable = .{ | 71 | const result: FrameHeader = .{ |
| 71 | .magic_number = magic, | 72 | .skippable = .{ |
| 72 | .frame_size = try source.readInt(u32, .little), | 73 | .magic_number = magic, |
| 73 | }, | 74 | .frame_size = try br.readInt(u32, .little), |
| 75 | }, | ||
| 76 | }; | ||
| 77 | bytes_read.* += 4; | ||
| 78 | return result; | ||
| 74 | }, | 79 | }, |
| 75 | } | 80 | } |
| 76 | } | 81 | } |
lib/std/crypto/tls.zig+99-94| ... | @@ -49,8 +49,8 @@ pub const hello_retry_request_sequence = [32]u8{ | ... | @@ -49,8 +49,8 @@ pub const hello_retry_request_sequence = [32]u8{ |
| 49 | }; | 49 | }; |
| 50 | 50 | ||
| 51 | pub const close_notify_alert = [_]u8{ | 51 | pub const close_notify_alert = [_]u8{ |
| 52 | @intFromEnum(AlertLevel.warning), | 52 | @intFromEnum(Alert.Level.warning), |
| 53 | @intFromEnum(AlertDescription.close_notify), | 53 | @intFromEnum(Alert.Description.close_notify), |
| 54 | }; | 54 | }; |
| 55 | 55 | ||
| 56 | pub const ProtocolVersion = enum(u16) { | 56 | pub const ProtocolVersion = enum(u16) { |
| ... | @@ -138,103 +138,108 @@ pub const ExtensionType = enum(u16) { | ... | @@ -138,103 +138,108 @@ pub const ExtensionType = enum(u16) { |
| 138 | _, | 138 | _, |
| 139 | }; | 139 | }; |
| 140 | 140 | ||
| 141 | pub const AlertLevel = enum(u8) { | 141 | pub const Alert = struct { |
| 142 | warning = 1, | 142 | level: Level, |
| 143 | fatal = 2, | 143 | description: Description, |
| 144 | _, | ||
| 145 | }; | ||
| 146 | 144 | ||
| 147 | pub const AlertDescription = enum(u8) { | 145 | pub const Level = enum(u8) { |
| 148 | pub const Error = error{ | 146 | warning = 1, |
| 149 | TlsAlertUnexpectedMessage, | 147 | fatal = 2, |
| 150 | TlsAlertBadRecordMac, | 148 | _, |
| 151 | TlsAlertRecordOverflow, | ||
| 152 | TlsAlertHandshakeFailure, | ||
| 153 | TlsAlertBadCertificate, | ||
| 154 | TlsAlertUnsupportedCertificate, | ||
| 155 | TlsAlertCertificateRevoked, | ||
| 156 | TlsAlertCertificateExpired, | ||
| 157 | TlsAlertCertificateUnknown, | ||
| 158 | TlsAlertIllegalParameter, | ||
| 159 | TlsAlertUnknownCa, | ||
| 160 | TlsAlertAccessDenied, | ||
| 161 | TlsAlertDecodeError, | ||
| 162 | TlsAlertDecryptError, | ||
| 163 | TlsAlertProtocolVersion, | ||
| 164 | TlsAlertInsufficientSecurity, | ||
| 165 | TlsAlertInternalError, | ||
| 166 | TlsAlertInappropriateFallback, | ||
| 167 | TlsAlertMissingExtension, | ||
| 168 | TlsAlertUnsupportedExtension, | ||
| 169 | TlsAlertUnrecognizedName, | ||
| 170 | TlsAlertBadCertificateStatusResponse, | ||
| 171 | TlsAlertUnknownPskIdentity, | ||
| 172 | TlsAlertCertificateRequired, | ||
| 173 | TlsAlertNoApplicationProtocol, | ||
| 174 | TlsAlertUnknown, | ||
| 175 | }; | 149 | }; |
| 176 | 150 | ||
| 177 | close_notify = 0, | 151 | pub const Description = enum(u8) { |
| 178 | unexpected_message = 10, | 152 | pub const Error = error{ |
| 179 | bad_record_mac = 20, | 153 | TlsAlertUnexpectedMessage, |
| 180 | record_overflow = 22, | 154 | TlsAlertBadRecordMac, |
| 181 | handshake_failure = 40, | 155 | TlsAlertRecordOverflow, |
| 182 | bad_certificate = 42, | 156 | TlsAlertHandshakeFailure, |
| 183 | unsupported_certificate = 43, | 157 | TlsAlertBadCertificate, |
| 184 | certificate_revoked = 44, | 158 | TlsAlertUnsupportedCertificate, |
| 185 | certificate_expired = 45, | 159 | TlsAlertCertificateRevoked, |
| 186 | certificate_unknown = 46, | 160 | TlsAlertCertificateExpired, |
| 187 | illegal_parameter = 47, | 161 | TlsAlertCertificateUnknown, |
| 188 | unknown_ca = 48, | 162 | TlsAlertIllegalParameter, |
| 189 | access_denied = 49, | 163 | TlsAlertUnknownCa, |
| 190 | decode_error = 50, | 164 | TlsAlertAccessDenied, |
| 191 | decrypt_error = 51, | 165 | TlsAlertDecodeError, |
| 192 | protocol_version = 70, | 166 | TlsAlertDecryptError, |
| 193 | insufficient_security = 71, | 167 | TlsAlertProtocolVersion, |
| 194 | internal_error = 80, | 168 | TlsAlertInsufficientSecurity, |
| 195 | inappropriate_fallback = 86, | 169 | TlsAlertInternalError, |
| 196 | user_canceled = 90, | 170 | TlsAlertInappropriateFallback, |
| 197 | missing_extension = 109, | 171 | TlsAlertMissingExtension, |
| 198 | unsupported_extension = 110, | 172 | TlsAlertUnsupportedExtension, |
| 199 | unrecognized_name = 112, | 173 | TlsAlertUnrecognizedName, |
| 200 | bad_certificate_status_response = 113, | 174 | TlsAlertBadCertificateStatusResponse, |
| 201 | unknown_psk_identity = 115, | 175 | TlsAlertUnknownPskIdentity, |
| 202 | certificate_required = 116, | 176 | TlsAlertCertificateRequired, |
| 203 | no_application_protocol = 120, | 177 | TlsAlertNoApplicationProtocol, |
| 204 | _, | 178 | TlsAlertUnknown, |
| 179 | }; | ||
| 205 | 180 | ||
| 206 | pub fn toError(alert: AlertDescription) Error!void { | 181 | close_notify = 0, |
| 207 | switch (alert) { | 182 | unexpected_message = 10, |
| 208 | .close_notify => {}, // not an error | 183 | bad_record_mac = 20, |
| 209 | .unexpected_message => return error.TlsAlertUnexpectedMessage, | 184 | record_overflow = 22, |
| 210 | .bad_record_mac => return error.TlsAlertBadRecordMac, | 185 | handshake_failure = 40, |
| 211 | .record_overflow => return error.TlsAlertRecordOverflow, | 186 | bad_certificate = 42, |
| 212 | .handshake_failure => return error.TlsAlertHandshakeFailure, | 187 | unsupported_certificate = 43, |
| 213 | .bad_certificate => return error.TlsAlertBadCertificate, | 188 | certificate_revoked = 44, |
| 214 | .unsupported_certificate => return error.TlsAlertUnsupportedCertificate, | 189 | certificate_expired = 45, |
| 215 | .certificate_revoked => return error.TlsAlertCertificateRevoked, | 190 | certificate_unknown = 46, |
| 216 | .certificate_expired => return error.TlsAlertCertificateExpired, | 191 | illegal_parameter = 47, |
| 217 | .certificate_unknown => return error.TlsAlertCertificateUnknown, | 192 | unknown_ca = 48, |
| 218 | .illegal_parameter => return error.TlsAlertIllegalParameter, | 193 | access_denied = 49, |
| 219 | .unknown_ca => return error.TlsAlertUnknownCa, | 194 | decode_error = 50, |
| 220 | .access_denied => return error.TlsAlertAccessDenied, | 195 | decrypt_error = 51, |
| 221 | .decode_error => return error.TlsAlertDecodeError, | 196 | protocol_version = 70, |
| 222 | .decrypt_error => return error.TlsAlertDecryptError, | 197 | insufficient_security = 71, |
| 223 | .protocol_version => return error.TlsAlertProtocolVersion, | 198 | internal_error = 80, |
| 224 | .insufficient_security => return error.TlsAlertInsufficientSecurity, | 199 | inappropriate_fallback = 86, |
| 225 | .internal_error => return error.TlsAlertInternalError, | 200 | user_canceled = 90, |
| 226 | .inappropriate_fallback => return error.TlsAlertInappropriateFallback, | 201 | missing_extension = 109, |
| 227 | .user_canceled => {}, // not an error | 202 | unsupported_extension = 110, |
| 228 | .missing_extension => return error.TlsAlertMissingExtension, | 203 | unrecognized_name = 112, |
| 229 | .unsupported_extension => return error.TlsAlertUnsupportedExtension, | 204 | bad_certificate_status_response = 113, |
| 230 | .unrecognized_name => return error.TlsAlertUnrecognizedName, | 205 | unknown_psk_identity = 115, |
| 231 | .bad_certificate_status_response => return error.TlsAlertBadCertificateStatusResponse, | 206 | certificate_required = 116, |
| 232 | .unknown_psk_identity => return error.TlsAlertUnknownPskIdentity, | 207 | no_application_protocol = 120, |
| 233 | .certificate_required => return error.TlsAlertCertificateRequired, | 208 | _, |
| 234 | .no_application_protocol => return error.TlsAlertNoApplicationProtocol, | 209 | |
| 235 | _ => return error.TlsAlertUnknown, | 210 | pub fn toError(description: Description) Error!void { |
| 211 | switch (description) { | ||
| 212 | .close_notify => {}, // not an error | ||
| 213 | .unexpected_message => return error.TlsAlertUnexpectedMessage, | ||
| 214 | .bad_record_mac => return error.TlsAlertBadRecordMac, | ||
| 215 | .record_overflow => return error.TlsAlertRecordOverflow, | ||
| 216 | .handshake_failure => return error.TlsAlertHandshakeFailure, | ||
| 217 | .bad_certificate => return error.TlsAlertBadCertificate, | ||
| 218 | .unsupported_certificate => return error.TlsAlertUnsupportedCertificate, | ||
| 219 | .certificate_revoked => return error.TlsAlertCertificateRevoked, | ||
| 220 | .certificate_expired => return error.TlsAlertCertificateExpired, | ||
| 221 | .certificate_unknown => return error.TlsAlertCertificateUnknown, | ||
| 222 | .illegal_parameter => return error.TlsAlertIllegalParameter, | ||
| 223 | .unknown_ca => return error.TlsAlertUnknownCa, | ||
| 224 | .access_denied => return error.TlsAlertAccessDenied, | ||
| 225 | .decode_error => return error.TlsAlertDecodeError, | ||
| 226 | .decrypt_error => return error.TlsAlertDecryptError, | ||
| 227 | .protocol_version => return error.TlsAlertProtocolVersion, | ||
| 228 | .insufficient_security => return error.TlsAlertInsufficientSecurity, | ||
| 229 | .internal_error => return error.TlsAlertInternalError, | ||
| 230 | .inappropriate_fallback => return error.TlsAlertInappropriateFallback, | ||
| 231 | .user_canceled => {}, // not an error | ||
| 232 | .missing_extension => return error.TlsAlertMissingExtension, | ||
| 233 | .unsupported_extension => return error.TlsAlertUnsupportedExtension, | ||
| 234 | .unrecognized_name => return error.TlsAlertUnrecognizedName, | ||
| 235 | .bad_certificate_status_response => return error.TlsAlertBadCertificateStatusResponse, | ||
| 236 | .unknown_psk_identity => return error.TlsAlertUnknownPskIdentity, | ||
| 237 | .certificate_required => return error.TlsAlertCertificateRequired, | ||
| 238 | .no_application_protocol => return error.TlsAlertNoApplicationProtocol, | ||
| 239 | _ => return error.TlsAlertUnknown, | ||
| 240 | } | ||
| 236 | } | 241 | } |
| 237 | } | 242 | }; |
| 238 | }; | 243 | }; |
| 239 | 244 | ||
| 240 | pub const SignatureScheme = enum(u16) { | 245 | pub const SignatureScheme = enum(u16) { |
lib/std/crypto/tls/Client.zig+55-51| ... | @@ -39,8 +39,9 @@ output: *std.io.BufferedWriter, | ... | @@ -39,8 +39,9 @@ output: *std.io.BufferedWriter, |
| 39 | /// | 39 | /// |
| 40 | /// Its buffer aliases the buffer of `input`. | 40 | /// Its buffer aliases the buffer of `input`. |
| 41 | reader: std.io.BufferedReader, | 41 | reader: std.io.BufferedReader, |
| 42 | /// Populated under various error conditions. | 42 | /// Populated when `error.TlsAlert` is returned. |
| 43 | diagnostics: Diagnostics, | 43 | alert: ?tls.Alert, |
| 44 | read_err: ?ReadError, | ||
| 44 | 45 | ||
| 45 | tls_version: tls.ProtocolVersion, | 46 | tls_version: tls.ProtocolVersion, |
| 46 | read_seq: u64, | 47 | read_seq: u64, |
| ... | @@ -69,15 +70,16 @@ application_cipher: tls.ApplicationCipher, | ... | @@ -69,15 +70,16 @@ application_cipher: tls.ApplicationCipher, |
| 69 | /// this connection. | 70 | /// this connection. |
| 70 | ssl_key_log: ?*SslKeyLog, | 71 | ssl_key_log: ?*SslKeyLog, |
| 71 | 72 | ||
| 72 | pub const Diagnostics = union(enum) { | 73 | pub const ReadError = error{ |
| 73 | /// Any `ReadFailure` and `WriteFailure` was due to `input` or `output` | 74 | /// The alert description will be stored in `alert`. |
| 74 | /// returning the error, respectively. | 75 | TlsAlert, |
| 75 | transitive, | 76 | TlsBadLength, |
| 76 | /// Populated on `error.TlsAlert`. | 77 | TlsBadRecordMac, |
| 77 | /// | 78 | TlsConnectionTruncated, |
| 78 | /// If this isn't a error alert, then it's a closure alert, which makes | 79 | TlsDecodeError, |
| 79 | /// no sense in a handshake. | 80 | TlsRecordOverflow, |
| 80 | alert: tls.AlertDescription, | 81 | TlsUnexpectedMessage, |
| 82 | TlsIllegalParameter, | ||
| 81 | }; | 83 | }; |
| 82 | 84 | ||
| 83 | pub const SslKeyLog = struct { | 85 | pub const SslKeyLog = struct { |
| ... | @@ -128,14 +130,13 @@ pub const Options = struct { | ... | @@ -128,14 +130,13 @@ pub const Options = struct { |
| 128 | }; | 130 | }; |
| 129 | 131 | ||
| 130 | const InitError = error{ | 132 | const InitError = error{ |
| 131 | //OutOfMemory, | 133 | WriteFailed, |
| 132 | WriteFailure, | 134 | ReadFailed, |
| 133 | ReadFailure, | ||
| 134 | InsufficientEntropy, | 135 | InsufficientEntropy, |
| 135 | DiskQuota, | 136 | DiskQuota, |
| 136 | LockViolation, | 137 | LockViolation, |
| 137 | NotOpenForWriting, | 138 | NotOpenForWriting, |
| 138 | /// The alert description will be stored in `Options.Diagnostics.alert`. | 139 | /// The alert description will be stored in `alert`. |
| 139 | TlsAlert, | 140 | TlsAlert, |
| 140 | TlsUnexpectedMessage, | 141 | TlsUnexpectedMessage, |
| 141 | TlsIllegalParameter, | 142 | TlsIllegalParameter, |
| ... | @@ -192,7 +193,7 @@ pub fn init( | ... | @@ -192,7 +193,7 @@ pub fn init( |
| 192 | ) InitError!void { | 193 | ) InitError!void { |
| 193 | assert(input.storage.buffer.len >= min_buffer_len); | 194 | assert(input.storage.buffer.len >= min_buffer_len); |
| 194 | assert(output.buffer.len >= min_buffer_len); | 195 | assert(output.buffer.len >= min_buffer_len); |
| 195 | client.diagnostics = .transient; | 196 | client.alert = null; |
| 196 | const host = switch (options.host) { | 197 | const host = switch (options.host) { |
| 197 | .no_verification => "", | 198 | .no_verification => "", |
| 198 | .explicit => |host| host, | 199 | .explicit => |host| host, |
| ... | @@ -417,10 +418,10 @@ pub fn init( | ... | @@ -417,10 +418,10 @@ pub fn init( |
| 417 | switch (ct) { | 418 | switch (ct) { |
| 418 | .alert => { | 419 | .alert => { |
| 419 | ctd.ensure(2) catch continue :fragment; | 420 | ctd.ensure(2) catch continue :fragment; |
| 420 | const level = ctd.decode(tls.AlertLevel); | 421 | client.alert = .{ |
| 421 | const desc = ctd.decode(tls.AlertDescription); | 422 | .level = ctd.decode(tls.Alert.Level), |
| 422 | _ = level; | 423 | .description = ctd.decode(tls.Alert.Description), |
| 423 | client.diagnostics = .{ .alert = desc }; | 424 | }; |
| 424 | return error.TlsAlert; | 425 | return error.TlsAlert; |
| 425 | }, | 426 | }, |
| 426 | .change_cipher_spec => { | 427 | .change_cipher_spec => { |
| ... | @@ -924,7 +925,7 @@ pub fn writer(c: *Client) std.io.Writer { | ... | @@ -924,7 +925,7 @@ pub fn writer(c: *Client) std.io.Writer { |
| 924 | fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { | 925 | fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize { |
| 925 | const c: *Client = @alignCast(@ptrCast(context)); | 926 | const c: *Client = @alignCast(@ptrCast(context)); |
| 926 | const sliced_data = if (splat == 0) data[0..data.len -| 1] else data; | 927 | const sliced_data = if (splat == 0) data[0..data.len -| 1] else data; |
| 927 | const output = &c.output; | 928 | const output = c.output; |
| 928 | const ciphertext_buf = try output.writableSliceGreedy(min_buffer_len); | 929 | const ciphertext_buf = try output.writableSliceGreedy(min_buffer_len); |
| 929 | var total_clear: usize = 0; | 930 | var total_clear: usize = 0; |
| 930 | var ciphertext_end: usize = 0; | 931 | var ciphertext_end: usize = 0; |
| ... | @@ -942,7 +943,7 @@ fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) std.i | ... | @@ -942,7 +943,7 @@ fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) std.i |
| 942 | /// distinguish between a properly finished TLS session, or a truncation | 943 | /// distinguish between a properly finished TLS session, or a truncation |
| 943 | /// attack. | 944 | /// attack. |
| 944 | pub fn end(c: *Client) std.io.Writer.Error!void { | 945 | pub fn end(c: *Client) std.io.Writer.Error!void { |
| 945 | const output = &c.output; | 946 | const output = c.output; |
| 946 | const ciphertext_buf = try output.writableSliceGreedy(min_buffer_len); | 947 | const ciphertext_buf = try output.writableSliceGreedy(min_buffer_len); |
| 947 | const prepared = prepareCiphertextRecord(c, ciphertext_buf, &tls.close_notify_alert, .alert); | 948 | const prepared = prepareCiphertextRecord(c, ciphertext_buf, &tls.close_notify_alert, .alert); |
| 948 | output.advance(prepared.cleartext_len); | 949 | output.advance(prepared.cleartext_len); |
| ... | @@ -1062,16 +1063,16 @@ fn read( | ... | @@ -1062,16 +1063,16 @@ fn read( |
| 1062 | context: ?*anyopaque, | 1063 | context: ?*anyopaque, |
| 1063 | bw: *std.io.BufferedWriter, | 1064 | bw: *std.io.BufferedWriter, |
| 1064 | limit: std.io.Reader.Limit, | 1065 | limit: std.io.Reader.Limit, |
| 1065 | ) std.io.Reader.RwError!std.io.Reader.Status { | 1066 | ) std.io.Reader.RwError!usize { |
| 1066 | const buf = limit.slice(try bw.writableSliceGreedy(1)); | 1067 | const buf = limit.slice(try bw.writableSliceGreedy(1)); |
| 1067 | const status = try readVec(context, &.{buf}); | 1068 | const n = try readVec(context, &.{buf}); |
| 1068 | bw.advance(status.len); | 1069 | bw.advance(n); |
| 1069 | return status; | 1070 | return n; |
| 1070 | } | 1071 | } |
| 1071 | 1072 | ||
| 1072 | fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | 1073 | fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { |
| 1073 | const c: *Client = @ptrCast(@alignCast(context)); | 1074 | const c: *Client = @ptrCast(@alignCast(context)); |
| 1074 | if (c.eof()) return .{ .end = true }; | 1075 | if (c.eof()) return error.EndOfStream; |
| 1075 | 1076 | ||
| 1076 | var vp: VecPut = .{ .iovecs = data }; | 1077 | var vp: VecPut = .{ .iovecs = data }; |
| 1077 | 1078 | ||
| ... | @@ -1093,11 +1094,11 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | ... | @@ -1093,11 +1094,11 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { |
| 1093 | if (c.received_close_notify) { | 1094 | if (c.received_close_notify) { |
| 1094 | c.partial_ciphertext_end = 0; | 1095 | c.partial_ciphertext_end = 0; |
| 1095 | assert(vp.total == amt); | 1096 | assert(vp.total == amt); |
| 1096 | return .{ .len = amt, .end = c.eof() }; | 1097 | return amt; |
| 1097 | } else if (amt > 0) { | 1098 | } else if (amt > 0) { |
| 1098 | // We don't need more data, so don't call read. | 1099 | // We don't need more data, so don't call read. |
| 1099 | assert(vp.total == amt); | 1100 | assert(vp.total == amt); |
| 1100 | return .{ .len = amt, .end = c.eof() }; | 1101 | return amt; |
| 1101 | } | 1102 | } |
| 1102 | } | 1103 | } |
| 1103 | 1104 | ||
| ... | @@ -1149,7 +1150,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | ... | @@ -1149,7 +1150,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { |
| 1149 | if (c.allow_truncation_attacks) { | 1150 | if (c.allow_truncation_attacks) { |
| 1150 | c.received_close_notify = true; | 1151 | c.received_close_notify = true; |
| 1151 | } else { | 1152 | } else { |
| 1152 | return error.TlsConnectionTruncated; | 1153 | return failRead(c, error.TlsConnectionTruncated); |
| 1153 | } | 1154 | } |
| 1154 | } | 1155 | } |
| 1155 | 1156 | ||
| ... | @@ -1168,7 +1169,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | ... | @@ -1168,7 +1169,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { |
| 1168 | // Perfect split. | 1169 | // Perfect split. |
| 1169 | if (frag.ptr == frag1.ptr) { | 1170 | if (frag.ptr == frag1.ptr) { |
| 1170 | c.partial_ciphertext_end = c.partial_ciphertext_idx; | 1171 | c.partial_ciphertext_end = c.partial_ciphertext_idx; |
| 1171 | return .{ .len = vp.total, .end = c.eof() }; | 1172 | return vp.total; |
| 1172 | } | 1173 | } |
| 1173 | frag = frag1; | 1174 | frag = frag1; |
| 1174 | in = 0; | 1175 | in = 0; |
| ... | @@ -1188,7 +1189,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | ... | @@ -1188,7 +1189,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { |
| 1188 | const record_len_byte_0: u16 = straddleByte(frag, frag1, in + 3); | 1189 | const record_len_byte_0: u16 = straddleByte(frag, frag1, in + 3); |
| 1189 | const record_len_byte_1: u16 = straddleByte(frag, frag1, in + 4); | 1190 | const record_len_byte_1: u16 = straddleByte(frag, frag1, in + 4); |
| 1190 | const record_len = (record_len_byte_0 << 8) | record_len_byte_1; | 1191 | const record_len = (record_len_byte_0 << 8) | record_len_byte_1; |
| 1191 | if (record_len > max_ciphertext_len) return error.TlsRecordOverflow; | 1192 | if (record_len > max_ciphertext_len) return failRead(c, error.TlsRecordOverflow); |
| 1192 | 1193 | ||
| 1193 | const full_record_len = record_len + tls.record_header_len; | 1194 | const full_record_len = record_len + tls.record_header_len; |
| 1194 | const second_len = full_record_len - first.len; | 1195 | const second_len = full_record_len - first.len; |
| ... | @@ -1208,7 +1209,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | ... | @@ -1208,7 +1209,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { |
| 1208 | in += 2; | 1209 | in += 2; |
| 1209 | _ = legacy_version; | 1210 | _ = legacy_version; |
| 1210 | const record_len = mem.readInt(u16, frag[in..][0..2], .big); | 1211 | const record_len = mem.readInt(u16, frag[in..][0..2], .big); |
| 1211 | if (record_len > max_ciphertext_len) return error.TlsRecordOverflow; | 1212 | if (record_len > max_ciphertext_len) return failRead(c, error.TlsRecordOverflow); |
| 1212 | in += 2; | 1213 | in += 2; |
| 1213 | const the_end = in + record_len; | 1214 | const the_end = in + record_len; |
| 1214 | if (the_end > frag.len) { | 1215 | if (the_end > frag.len) { |
| ... | @@ -1255,7 +1256,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | ... | @@ -1255,7 +1256,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { |
| 1255 | &cleartext_stack_buffer; | 1256 | &cleartext_stack_buffer; |
| 1256 | const cleartext = cleartext_buf[0..ciphertext.len]; | 1257 | const cleartext = cleartext_buf[0..ciphertext.len]; |
| 1257 | P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, pv.server_key) catch | 1258 | P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, pv.server_key) catch |
| 1258 | return error.TlsBadRecordMac; | 1259 | return failRead(c, error.TlsBadRecordMac); |
| 1259 | const msg = mem.trimEnd(u8, cleartext, "\x00"); | 1260 | const msg = mem.trimEnd(u8, cleartext, "\x00"); |
| 1260 | break :cleartext .{ msg[0 .. msg.len - 1], @enumFromInt(msg[msg.len - 1]) }; | 1261 | break :cleartext .{ msg[0 .. msg.len - 1], @enumFromInt(msg[msg.len - 1]) }; |
| 1261 | }, | 1262 | }, |
| ... | @@ -1287,7 +1288,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | ... | @@ -1287,7 +1288,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { |
| 1287 | &cleartext_stack_buffer; | 1288 | &cleartext_stack_buffer; |
| 1288 | const cleartext = cleartext_buf[0..ciphertext.len]; | 1289 | const cleartext = cleartext_buf[0..ciphertext.len]; |
| 1289 | P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, pv.server_write_key) catch | 1290 | P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, pv.server_write_key) catch |
| 1290 | return error.TlsBadRecordMac; | 1291 | return failRead(c, error.TlsBadRecordMac); |
| 1291 | break :cleartext .{ cleartext, ct }; | 1292 | break :cleartext .{ cleartext, ct }; |
| 1292 | }, | 1293 | }, |
| 1293 | else => unreachable, | 1294 | else => unreachable, |
| ... | @@ -1296,23 +1297,24 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | ... | @@ -1296,23 +1297,24 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { |
| 1296 | c.read_seq = try std.math.add(u64, c.read_seq, 1); | 1297 | c.read_seq = try std.math.add(u64, c.read_seq, 1); |
| 1297 | switch (inner_ct) { | 1298 | switch (inner_ct) { |
| 1298 | .alert => { | 1299 | .alert => { |
| 1299 | if (cleartext.len != 2) return error.TlsDecodeError; | 1300 | if (cleartext.len != 2) return failRead(c, error.TlsDecodeError); |
| 1300 | const level: tls.AlertLevel = @enumFromInt(cleartext[0]); | 1301 | const alert: tls.Alert = .{ |
| 1301 | _ = level; | 1302 | .level = @enumFromInt(cleartext[0]), |
| 1302 | const desc: tls.AlertDescription = @enumFromInt(cleartext[1]); | 1303 | .description = @enumFromInt(cleartext[1]), |
| 1303 | switch (desc) { | 1304 | }; |
| 1305 | switch (alert.description) { | ||
| 1304 | .close_notify => { | 1306 | .close_notify => { |
| 1305 | c.received_close_notify = true; | 1307 | c.received_close_notify = true; |
| 1306 | c.partial_ciphertext_end = c.partial_ciphertext_idx; | 1308 | c.partial_ciphertext_end = c.partial_ciphertext_idx; |
| 1307 | return .{ .len = vp.total, .end = c.eof() }; | 1309 | return vp.total; |
| 1308 | }, | 1310 | }, |
| 1309 | .user_canceled => { | 1311 | .user_canceled => { |
| 1310 | // TODO: handle server-side closures | 1312 | // TODO: handle server-side closures |
| 1311 | return error.TlsUnexpectedMessage; | 1313 | return failRead(c, error.TlsUnexpectedMessage); |
| 1312 | }, | 1314 | }, |
| 1313 | else => { | 1315 | else => { |
| 1314 | c.diagnostics = .{ .alert = desc }; | 1316 | c.alert = alert; |
| 1315 | return error.TlsAlert; | 1317 | return failRead(c, error.TlsAlert); |
| 1316 | }, | 1318 | }, |
| 1317 | } | 1319 | } |
| 1318 | }, | 1320 | }, |
| ... | @@ -1324,8 +1326,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | ... | @@ -1324,8 +1326,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { |
| 1324 | const handshake_len = mem.readInt(u24, cleartext[ct_i..][0..3], .big); | 1326 | const handshake_len = mem.readInt(u24, cleartext[ct_i..][0..3], .big); |
| 1325 | ct_i += 3; | 1327 | ct_i += 3; |
| 1326 | const next_handshake_i = ct_i + handshake_len; | 1328 | const next_handshake_i = ct_i + handshake_len; |
| 1327 | if (next_handshake_i > cleartext.len) | 1329 | if (next_handshake_i > cleartext.len) return failRead(c, error.TlsBadLength); |
| 1328 | return error.TlsBadLength; | ||
| 1329 | const handshake = cleartext[ct_i..next_handshake_i]; | 1330 | const handshake = cleartext[ct_i..next_handshake_i]; |
| 1330 | switch (handshake_type) { | 1331 | switch (handshake_type) { |
| 1331 | .new_session_ticket => { | 1332 | .new_session_ticket => { |
| ... | @@ -1371,12 +1372,10 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | ... | @@ -1371,12 +1372,10 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { |
| 1371 | c.write_seq = 0; | 1372 | c.write_seq = 0; |
| 1372 | }, | 1373 | }, |
| 1373 | .update_not_requested => {}, | 1374 | .update_not_requested => {}, |
| 1374 | _ => return error.TlsIllegalParameter, | 1375 | _ => return failRead(c, error.TlsIllegalParameter), |
| 1375 | } | 1376 | } |
| 1376 | }, | 1377 | }, |
| 1377 | else => { | 1378 | else => return failRead(c, error.TlsUnexpectedMessage), |
| 1378 | return error.TlsUnexpectedMessage; | ||
| 1379 | }, | ||
| 1380 | } | 1379 | } |
| 1381 | ct_i = next_handshake_i; | 1380 | ct_i = next_handshake_i; |
| 1382 | if (ct_i >= cleartext.len) break; | 1381 | if (ct_i >= cleartext.len) break; |
| ... | @@ -1411,7 +1410,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | ... | @@ -1411,7 +1410,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { |
| 1411 | vp.next(cleartext.len); | 1410 | vp.next(cleartext.len); |
| 1412 | } | 1411 | } |
| 1413 | }, | 1412 | }, |
| 1414 | else => return error.TlsUnexpectedMessage, | 1413 | else => return failRead(c, error.TlsUnexpectedMessage), |
| 1415 | } | 1414 | } |
| 1416 | in = end; | 1415 | in = end; |
| 1417 | } | 1416 | } |
| ... | @@ -1423,6 +1422,11 @@ fn discard(context: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error | ... | @@ -1423,6 +1422,11 @@ fn discard(context: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error |
| 1423 | @panic("TODO"); | 1422 | @panic("TODO"); |
| 1424 | } | 1423 | } |
| 1425 | 1424 | ||
| 1425 | fn failRead(c: *Client, err: ReadError) error{ReadFailed} { | ||
| 1426 | c.read_err = err; | ||
| 1427 | return error.ReadFailed; | ||
| 1428 | } | ||
| 1429 | |||
| 1426 | fn logSecrets(key_log_file: std.fs.File, context: anytype, secrets: anytype) void { | 1430 | fn logSecrets(key_log_file: std.fs.File, context: anytype, secrets: anytype) void { |
| 1427 | const locked = if (key_log_file.lock(.exclusive)) |_| true else |_| false; | 1431 | const locked = if (key_log_file.lock(.exclusive)) |_| true else |_| false; |
| 1428 | defer if (locked) key_log_file.unlock(); | 1432 | defer if (locked) key_log_file.unlock(); |
lib/std/http/Client.zig+3-3| ... | @@ -28,7 +28,7 @@ tls_buffer_size: if (disable_tls) u0 else usize = if (disable_tls) 0 else std.cr | ... | @@ -28,7 +28,7 @@ tls_buffer_size: if (disable_tls) u0 else usize = if (disable_tls) 0 else std.cr |
| 28 | /// If non-null, ssl secrets are logged to a stream. Creating such a stream | 28 | /// If non-null, ssl secrets are logged to a stream. Creating such a stream |
| 29 | /// allows other processes with access to that stream to decrypt all | 29 | /// allows other processes with access to that stream to decrypt all |
| 30 | /// traffic over connections created with this `Client`. | 30 | /// traffic over connections created with this `Client`. |
| 31 | ssl_key_logger: ?*std.io.BufferedWriter = null, | 31 | ssl_key_log: ?*std.io.BufferedWriter = null, |
| 32 | 32 | ||
| 33 | /// When this is `true`, the next time this client performs an HTTPS request, | 33 | /// When this is `true`, the next time this client performs an HTTPS request, |
| 34 | /// it will first rescan the system for root certificates. | 34 | /// it will first rescan the system for root certificates. |
| ... | @@ -342,7 +342,7 @@ pub const Connection = struct { | ... | @@ -342,7 +342,7 @@ pub const Connection = struct { |
| 342 | tls.client.init(&tls.reader, &tls.writer, .{ | 342 | tls.client.init(&tls.reader, &tls.writer, .{ |
| 343 | .host = .{ .explicit = remote_host }, | 343 | .host = .{ .explicit = remote_host }, |
| 344 | .ca = .{ .bundle = client.ca_bundle }, | 344 | .ca = .{ .bundle = client.ca_bundle }, |
| 345 | .ssl_key_logger = client.ssl_key_logger, | 345 | .ssl_key_log = client.ssl_key_log, |
| 346 | }) catch return error.TlsInitializationFailed; | 346 | }) catch return error.TlsInitializationFailed; |
| 347 | // This is appropriate for HTTPS because the HTTP headers contain | 347 | // This is appropriate for HTTPS because the HTTP headers contain |
| 348 | // the content length which is used to detect truncation attacks. | 348 | // the content length which is used to detect truncation attacks. |
| ... | @@ -1671,7 +1671,7 @@ pub fn fetch(client: *Client, options: FetchOptions) FetchError!FetchResult { | ... | @@ -1671,7 +1671,7 @@ pub fn fetch(client: *Client, options: FetchOptions) FetchError!FetchResult { |
| 1671 | const decompress_buffer: []u8 = switch (response.head.content_encoding) { | 1671 | const decompress_buffer: []u8 = switch (response.head.content_encoding) { |
| 1672 | .identity => &.{}, | 1672 | .identity => &.{}, |
| 1673 | .zstd => options.decompress_buffer orelse | 1673 | .zstd => options.decompress_buffer orelse |
| 1674 | try client.allocator.alloc(u8, std.compress.zstd.Decompressor.Options.default_window_buffer_len * 2), | 1674 | try client.allocator.alloc(u8, std.compress.zstd.default_window_len * 2), |
| 1675 | else => options.decompress_buffer orelse try client.allocator.alloc(u8, 8 * 1024), | 1675 | else => options.decompress_buffer orelse try client.allocator.alloc(u8, 8 * 1024), |
| 1676 | }; | 1676 | }; |
| 1677 | defer if (options.decompress_buffer == null) client.allocator.free(decompress_buffer); | 1677 | defer if (options.decompress_buffer == null) client.allocator.free(decompress_buffer); |
lib/std/io.zig+5-20| ... | @@ -1,16 +1,11 @@ | ... | @@ -1,16 +1,11 @@ |
| 1 | const std = @import("std.zig"); | ||
| 2 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 3 | const root = @import("root"); | ||
| 4 | const c = std.c; | ||
| 5 | const is_windows = builtin.os.tag == .windows; | 2 | const is_windows = builtin.os.tag == .windows; |
| 3 | |||
| 4 | const std = @import("std.zig"); | ||
| 6 | const windows = std.os.windows; | 5 | const windows = std.os.windows; |
| 7 | const posix = std.posix; | 6 | const posix = std.posix; |
| 8 | const math = std.math; | 7 | const math = std.math; |
| 9 | const assert = std.debug.assert; | 8 | const assert = std.debug.assert; |
| 10 | const fs = std.fs; | ||
| 11 | const mem = std.mem; | ||
| 12 | const meta = std.meta; | ||
| 13 | const File = std.fs.File; | ||
| 14 | const Allocator = std.mem.Allocator; | 9 | const Allocator = std.mem.Allocator; |
| 15 | const Alignment = std.mem.Alignment; | 10 | const Alignment = std.mem.Alignment; |
| 16 | 11 | ||
| ... | @@ -21,12 +16,6 @@ pub const BufferedReader = @import("io/BufferedReader.zig"); | ... | @@ -21,12 +16,6 @@ pub const BufferedReader = @import("io/BufferedReader.zig"); |
| 21 | pub const BufferedWriter = @import("io/BufferedWriter.zig"); | 16 | pub const BufferedWriter = @import("io/BufferedWriter.zig"); |
| 22 | pub const AllocatingWriter = @import("io/AllocatingWriter.zig"); | 17 | pub const AllocatingWriter = @import("io/AllocatingWriter.zig"); |
| 23 | 18 | ||
| 24 | pub const CWriter = @import("io/c_writer.zig").CWriter; | ||
| 25 | pub const cWriter = @import("io/c_writer.zig").cWriter; | ||
| 26 | |||
| 27 | pub const LimitedReader = @import("io/limited_reader.zig").LimitedReader; | ||
| 28 | pub const limitedReader = @import("io/limited_reader.zig").limitedReader; | ||
| 29 | |||
| 30 | pub const MultiWriter = @import("io/multi_writer.zig").MultiWriter; | 19 | pub const MultiWriter = @import("io/multi_writer.zig").MultiWriter; |
| 31 | pub const multiWriter = @import("io/multi_writer.zig").multiWriter; | 20 | pub const multiWriter = @import("io/multi_writer.zig").multiWriter; |
| 32 | 21 | ||
| ... | @@ -38,9 +27,6 @@ pub const bitWriter = @import("io/bit_writer.zig").bitWriter; | ... | @@ -38,9 +27,6 @@ pub const bitWriter = @import("io/bit_writer.zig").bitWriter; |
| 38 | pub const ChangeDetectionStream = @import("io/change_detection_stream.zig").ChangeDetectionStream; | 27 | pub const ChangeDetectionStream = @import("io/change_detection_stream.zig").ChangeDetectionStream; |
| 39 | pub const changeDetectionStream = @import("io/change_detection_stream.zig").changeDetectionStream; | 28 | pub const changeDetectionStream = @import("io/change_detection_stream.zig").changeDetectionStream; |
| 40 | 29 | ||
| 41 | pub const FindByteWriter = @import("io/find_byte_writer.zig").FindByteWriter; | ||
| 42 | pub const findByteWriter = @import("io/find_byte_writer.zig").findByteWriter; | ||
| 43 | |||
| 44 | pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAtomicFile; | 30 | pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAtomicFile; |
| 45 | 31 | ||
| 46 | pub const tty = @import("io/tty.zig"); | 32 | pub const tty = @import("io/tty.zig"); |
| ... | @@ -63,7 +49,7 @@ pub fn poll( | ... | @@ -63,7 +49,7 @@ pub fn poll( |
| 63 | .windows = if (is_windows) .{ | 49 | .windows = if (is_windows) .{ |
| 64 | .first_read_done = false, | 50 | .first_read_done = false, |
| 65 | .overlapped = [1]windows.OVERLAPPED{ | 51 | .overlapped = [1]windows.OVERLAPPED{ |
| 66 | mem.zeroes(windows.OVERLAPPED), | 52 | std.mem.zeroes(windows.OVERLAPPED), |
| 67 | } ** enum_fields.len, | 53 | } ** enum_fields.len, |
| 68 | .small_bufs = undefined, | 54 | .small_bufs = undefined, |
| 69 | .active = .{ | 55 | .active = .{ |
| ... | @@ -436,10 +422,10 @@ pub fn PollFiles(comptime StreamEnum: type) type { | ... | @@ -436,10 +422,10 @@ pub fn PollFiles(comptime StreamEnum: type) type { |
| 436 | for (&struct_fields, enum_fields) |*struct_field, enum_field| { | 422 | for (&struct_fields, enum_fields) |*struct_field, enum_field| { |
| 437 | struct_field.* = .{ | 423 | struct_field.* = .{ |
| 438 | .name = enum_field.name, | 424 | .name = enum_field.name, |
| 439 | .type = fs.File, | 425 | .type = std.fs.File, |
| 440 | .default_value_ptr = null, | 426 | .default_value_ptr = null, |
| 441 | .is_comptime = false, | 427 | .is_comptime = false, |
| 442 | .alignment = @alignOf(fs.File), | 428 | .alignment = @alignOf(std.fs.File), |
| 443 | }; | 429 | }; |
| 444 | } | 430 | } |
| 445 | return @Type(.{ .@"struct" = .{ | 431 | return @Type(.{ .@"struct" = .{ |
| ... | @@ -459,6 +445,5 @@ test { | ... | @@ -459,6 +445,5 @@ test { |
| 459 | _ = @import("io/bit_reader.zig"); | 445 | _ = @import("io/bit_reader.zig"); |
| 460 | _ = @import("io/bit_writer.zig"); | 446 | _ = @import("io/bit_writer.zig"); |
| 461 | _ = @import("io/buffered_atomic_file.zig"); | 447 | _ = @import("io/buffered_atomic_file.zig"); |
| 462 | _ = @import("io/c_writer.zig"); | ||
| 463 | _ = @import("io/test.zig"); | 448 | _ = @import("io/test.zig"); |
| 464 | } | 449 | } |
lib/std/io/Reader.zig+9| ... | @@ -6,6 +6,8 @@ const BufferedReader = std.io.BufferedReader; | ... | @@ -6,6 +6,8 @@ const BufferedReader = std.io.BufferedReader; |
| 6 | const Allocator = std.mem.Allocator; | 6 | const Allocator = std.mem.Allocator; |
| 7 | const ArrayList = std.ArrayListUnmanaged; | 7 | const ArrayList = std.ArrayListUnmanaged; |
| 8 | 8 | ||
| 9 | pub const Limited = @import("Reader/Limited.zig"); | ||
| 10 | |||
| 9 | context: ?*anyopaque, | 11 | context: ?*anyopaque, |
| 10 | vtable: *const VTable, | 12 | vtable: *const VTable, |
| 11 | 13 | ||
| ... | @@ -252,6 +254,13 @@ pub fn buffered(r: Reader, buffer: []u8) BufferedReader { | ... | @@ -252,6 +254,13 @@ pub fn buffered(r: Reader, buffer: []u8) BufferedReader { |
| 252 | }; | 254 | }; |
| 253 | } | 255 | } |
| 254 | 256 | ||
| 257 | pub fn limited(r: Reader, limit: Limit) Limited { | ||
| 258 | return .{ | ||
| 259 | .unlimited_reader = r, | ||
| 260 | .remaining = limit, | ||
| 261 | }; | ||
| 262 | } | ||
| 263 | |||
| 255 | fn endingRead(context: ?*anyopaque, bw: *BufferedWriter, limit: Limit) RwError!usize { | 264 | fn endingRead(context: ?*anyopaque, bw: *BufferedWriter, limit: Limit) RwError!usize { |
| 256 | _ = context; | 265 | _ = context; |
| 257 | _ = bw; | 266 | _ = bw; |
lib/std/io/Reader/Limited.zig created+55| ... | @@ -0,0 +1,55 @@ | ||
| 1 | const Limited = @This(); | ||
| 2 | |||
| 3 | const std = @import("../../std.zig"); | ||
| 4 | const Reader = std.io.Reader; | ||
| 5 | const BufferedWriter = std.io.BufferedWriter; | ||
| 6 | |||
| 7 | unlimited_reader: Reader, | ||
| 8 | remaining: Reader.Limit, | ||
| 9 | |||
| 10 | pub fn reader(l: *Limited) Reader { | ||
| 11 | return .{ | ||
| 12 | .context = l, | ||
| 13 | .vtable = &.{ | ||
| 14 | .read = passthruRead, | ||
| 15 | .readVec = passthruReadVec, | ||
| 16 | .discard = passthruDiscard, | ||
| 17 | }, | ||
| 18 | }; | ||
| 19 | } | ||
| 20 | |||
| 21 | fn passthruRead(context: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) Reader.RwError!usize { | ||
| 22 | const l: *Limited = @alignCast(@ptrCast(context)); | ||
| 23 | const combined_limit = limit.min(l.remaining); | ||
| 24 | const n = try l.unlimited_reader.read(bw, combined_limit); | ||
| 25 | l.remaining.subtract(n); | ||
| 26 | return n; | ||
| 27 | } | ||
| 28 | |||
| 29 | fn passthruDiscard(context: ?*anyopaque, limit: Reader.Limit) Reader.Error!usize { | ||
| 30 | const l: *Limited = @alignCast(@ptrCast(context)); | ||
| 31 | const combined_limit = limit.min(l.remaining); | ||
| 32 | const n = try l.unlimited_reader.discard(combined_limit); | ||
| 33 | l.remaining.subtract(n); | ||
| 34 | return n; | ||
| 35 | } | ||
| 36 | |||
| 37 | fn passthruReadVec(context: ?*anyopaque, data: []const []u8) Reader.Error!usize { | ||
| 38 | const l: *Limited = @alignCast(@ptrCast(context)); | ||
| 39 | if (data.len == 0) return 0; | ||
| 40 | if (data[0].len >= @intFromEnum(l.limit)) { | ||
| 41 | const n = try l.unlimited_reader.readVec(&.{l.limit.slice(data[0])}); | ||
| 42 | l.remaining.subtract(n); | ||
| 43 | return n; | ||
| 44 | } | ||
| 45 | var total: usize = 0; | ||
| 46 | for (data, 0..) |buf, i| { | ||
| 47 | total += buf.len; | ||
| 48 | if (total > @intFromEnum(l.limit)) { | ||
| 49 | const n = try l.unlimited_reader.readVec(data[0..i]); | ||
| 50 | l.remaining.subtract(n); | ||
| 51 | return n; | ||
| 52 | } | ||
| 53 | } | ||
| 54 | return 0; | ||
| 55 | } | ||
lib/std/io/Writer.zig+2| ... | @@ -95,10 +95,12 @@ pub const Offset = enum(u64) { | ... | @@ -95,10 +95,12 @@ pub const Offset = enum(u64) { |
| 95 | }; | 95 | }; |
| 96 | 96 | ||
| 97 | pub fn writeVec(w: Writer, data: []const []const u8) Error!usize { | 97 | pub fn writeVec(w: Writer, data: []const []const u8) Error!usize { |
| 98 | assert(data.len > 0); | ||
| 98 | return w.vtable.writeSplat(w.context, data, 1); | 99 | return w.vtable.writeSplat(w.context, data, 1); |
| 99 | } | 100 | } |
| 100 | 101 | ||
| 101 | pub fn writeSplat(w: Writer, data: []const []const u8, splat: usize) Error!usize { | 102 | pub fn writeSplat(w: Writer, data: []const []const u8, splat: usize) Error!usize { |
| 103 | assert(data.len > 0); | ||
| 102 | return w.vtable.writeSplat(w.context, data, splat); | 104 | return w.vtable.writeSplat(w.context, data, splat); |
| 103 | } | 105 | } |
| 104 | 106 |
lib/std/io/c_writer.zig deleted-44| ... | @@ -1,44 +0,0 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const io = std.io; | ||
| 4 | const testing = std.testing; | ||
| 5 | |||
| 6 | pub const CWriter = io.Writer(*std.c.FILE, std.fs.File.WriteError, cWriterWrite); | ||
| 7 | |||
| 8 | pub fn cWriter(c_file: *std.c.FILE) CWriter { | ||
| 9 | return .{ .context = c_file }; | ||
| 10 | } | ||
| 11 | |||
| 12 | fn cWriterWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!usize { | ||
| 13 | const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, c_file); | ||
| 14 | if (amt_written >= 0) return amt_written; | ||
| 15 | switch (@as(std.c.E, @enumFromInt(std.c._errno().*))) { | ||
| 16 | .SUCCESS => unreachable, | ||
| 17 | .INVAL => unreachable, | ||
| 18 | .FAULT => unreachable, | ||
| 19 | .AGAIN => unreachable, // this is a blocking API | ||
| 20 | .BADF => unreachable, // always a race condition | ||
| 21 | .DESTADDRREQ => unreachable, // connect was never called | ||
| 22 | .DQUOT => return error.DiskQuota, | ||
| 23 | .FBIG => return error.FileTooBig, | ||
| 24 | .IO => return error.InputOutput, | ||
| 25 | .NOSPC => return error.NoSpaceLeft, | ||
| 26 | .PERM => return error.PermissionDenied, | ||
| 27 | .PIPE => return error.BrokenPipe, | ||
| 28 | else => |err| return std.posix.unexpectedErrno(err), | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | test cWriter { | ||
| 33 | if (!builtin.link_libc or builtin.os.tag == .wasi) return error.SkipZigTest; | ||
| 34 | |||
| 35 | const filename = "tmp_io_test_file.txt"; | ||
| 36 | const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile; | ||
| 37 | defer { | ||
| 38 | _ = std.c.fclose(out_file); | ||
| 39 | std.fs.cwd().deleteFileZ(filename) catch {}; | ||
| 40 | } | ||
| 41 | |||
| 42 | const writer = cWriter(out_file); | ||
| 43 | try writer.print("hi: {}\n", .{@as(i32, 123)}); | ||
| 44 | } | ||
lib/std/io/limited_reader.zig deleted-45| ... | @@ -1,45 +0,0 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const io = std.io; | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | const testing = std.testing; | ||
| 5 | |||
| 6 | pub fn LimitedReader(comptime ReaderType: type) type { | ||
| 7 | return struct { | ||
| 8 | inner_reader: ReaderType, | ||
| 9 | bytes_left: u64, | ||
| 10 | |||
| 11 | pub const Error = ReaderType.Error; | ||
| 12 | pub const Reader = io.Reader(*Self, Error, read); | ||
| 13 | |||
| 14 | const Self = @This(); | ||
| 15 | |||
| 16 | pub fn read(self: *Self, dest: []u8) Error!usize { | ||
| 17 | const max_read = @min(self.bytes_left, dest.len); | ||
| 18 | const n = try self.inner_reader.read(dest[0..max_read]); | ||
| 19 | self.bytes_left -= n; | ||
| 20 | return n; | ||
| 21 | } | ||
| 22 | |||
| 23 | pub fn reader(self: *Self) Reader { | ||
| 24 | return .{ .context = self }; | ||
| 25 | } | ||
| 26 | }; | ||
| 27 | } | ||
| 28 | |||
| 29 | /// Returns an initialised `LimitedReader`. | ||
| 30 | /// `bytes_left` is a `u64` to be able to take 64 bit file offsets | ||
| 31 | pub fn limitedReader(inner_reader: anytype, bytes_left: u64) LimitedReader(@TypeOf(inner_reader)) { | ||
| 32 | return .{ .inner_reader = inner_reader, .bytes_left = bytes_left }; | ||
| 33 | } | ||
| 34 | |||
| 35 | test "basic usage" { | ||
| 36 | const data = "hello world"; | ||
| 37 | var fbs = std.io.fixedBufferStream(data); | ||
| 38 | var early_stream = limitedReader(fbs.reader(), 3); | ||
| 39 | |||
| 40 | var buf: [5]u8 = undefined; | ||
| 41 | try testing.expectEqual(@as(usize, 3), try early_stream.reader().read(&buf)); | ||
| 42 | try testing.expectEqualSlices(u8, data[0..3], buf[0..3]); | ||
| 43 | try testing.expectEqual(@as(usize, 0), try early_stream.reader().read(&buf)); | ||
| 44 | try testing.expectError(error.EndOfStream, early_stream.reader().skipBytes(10, .{})); | ||
| 45 | } | ||
lib/std/net.zig+3-3| ... | @@ -1919,9 +1919,9 @@ pub const Stream = struct { | ... | @@ -1919,9 +1919,9 @@ pub const Stream = struct { |
| 1919 | limit: std.io.Reader.Limit, | 1919 | limit: std.io.Reader.Limit, |
| 1920 | ) std.io.Reader.Error!usize { | 1920 | ) std.io.Reader.Error!usize { |
| 1921 | const buf = limit.slice(try bw.writableSliceGreedy(1)); | 1921 | const buf = limit.slice(try bw.writableSliceGreedy(1)); |
| 1922 | const status = try readVec(context, &.{buf}); | 1922 | const n = try readVec(context, &.{buf}); |
| 1923 | bw.advance(status.len); | 1923 | bw.advance(n); |
| 1924 | return status; | 1924 | return n; |
| 1925 | } | 1925 | } |
| 1926 | 1926 | ||
| 1927 | fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { | 1927 | fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize { |
lib/std/zip.zig+14-19| ... | @@ -154,35 +154,30 @@ pub fn findEndRecord(seekable_stream: anytype, stream_len: u64) !EndRecord { | ... | @@ -154,35 +154,30 @@ pub fn findEndRecord(seekable_stream: anytype, stream_len: u64) !EndRecord { |
| 154 | pub fn decompress( | 154 | pub fn decompress( |
| 155 | method: CompressionMethod, | 155 | method: CompressionMethod, |
| 156 | uncompressed_size: u64, | 156 | uncompressed_size: u64, |
| 157 | reader: anytype, | 157 | reader: *std.io.BufferedReader, |
| 158 | writer: anytype, | 158 | writer: *std.io.BufferedWriter, |
| 159 | compressed_remaining: *u64, | ||
| 159 | ) !u32 { | 160 | ) !u32 { |
| 160 | var hash = std.hash.Crc32.init(); | 161 | var hash = std.hash.Crc32.init(); |
| 161 | |||
| 162 | var total_uncompressed: u64 = 0; | 162 | var total_uncompressed: u64 = 0; |
| 163 | switch (method) { | 163 | switch (method) { |
| 164 | .store => { | 164 | .store => { |
| 165 | var buf: [4096]u8 = undefined; | 165 | reader.writeAll(writer, .limited(compressed_remaining.*)) catch |err| switch (err) { |
| 166 | while (true) { | 166 | error.EndOfStream => return error.ZipDecompressTruncated, |
| 167 | const len = try reader.read(&buf); | 167 | else => |e| return e, |
| 168 | if (len == 0) break; | 168 | }; |
| 169 | try writer.writeAll(buf[0..len]); | 169 | total_uncompressed += compressed_remaining.*; |
| 170 | hash.update(buf[0..len]); | ||
| 171 | total_uncompressed += @intCast(len); | ||
| 172 | } | ||
| 173 | }, | 170 | }, |
| 174 | .deflate => { | 171 | .deflate => { |
| 175 | var br = std.io.bufferedReader(reader); | 172 | var decompressor: std.compress.flate.Decompressor = .init(reader); |
| 176 | var decompressor = std.compress.flate.decompressor(br.reader()); | ||
| 177 | while (try decompressor.next()) |chunk| { | 173 | while (try decompressor.next()) |chunk| { |
| 178 | try writer.writeAll(chunk); | 174 | try writer.writeAll(chunk); |
| 179 | hash.update(chunk); | 175 | hash.update(chunk); |
| 180 | total_uncompressed += @intCast(chunk.len); | 176 | total_uncompressed += @intCast(chunk.len); |
| 181 | if (total_uncompressed > uncompressed_size) | 177 | if (total_uncompressed > uncompressed_size) |
| 182 | return error.ZipUncompressSizeTooSmall; | 178 | return error.ZipUncompressSizeTooSmall; |
| 179 | compressed_remaining.* -= chunk.len; | ||
| 183 | } | 180 | } |
| 184 | if (br.end != br.start) | ||
| 185 | return error.ZipDeflateTruncated; | ||
| 186 | }, | 181 | }, |
| 187 | _ => return error.UnsupportedCompressionMethod, | 182 | _ => return error.UnsupportedCompressionMethod, |
| 188 | } | 183 | } |
| ... | @@ -552,15 +547,15 @@ pub fn Iterator(comptime SeekableStream: type) type { | ... | @@ -552,15 +547,15 @@ pub fn Iterator(comptime SeekableStream: type) type { |
| 552 | @as(u64, @sizeOf(LocalFileHeader)) + | 547 | @as(u64, @sizeOf(LocalFileHeader)) + |
| 553 | local_data_header_offset; | 548 | local_data_header_offset; |
| 554 | try stream.seekTo(local_data_file_offset); | 549 | try stream.seekTo(local_data_file_offset); |
| 555 | var limited_reader = std.io.limitedReader(stream.context.reader(), self.compressed_size); | 550 | var compressed_remaining: u64 = self.compressed_size; |
| 556 | const crc = try decompress( | 551 | const crc = try decompress( |
| 557 | self.compression_method, | 552 | self.compression_method, |
| 558 | self.uncompressed_size, | 553 | self.uncompressed_size, |
| 559 | limited_reader.reader(), | 554 | stream.context.reader(), |
| 560 | out_file.writer(), | 555 | out_file.writer(), |
| 556 | &compressed_remaining, | ||
| 561 | ); | 557 | ); |
| 562 | if (limited_reader.bytes_left != 0) | 558 | if (compressed_remaining != 0) return error.ZipDecompressTruncated; |
| 563 | return error.ZipDecompressTruncated; | ||
| 564 | return crc; | 559 | return crc; |
| 565 | } | 560 | } |
| 566 | }; | 561 | }; |
src/Package/Fetch.zig+1-1| ... | @@ -1199,7 +1199,7 @@ fn unpackResource( | ... | @@ -1199,7 +1199,7 @@ fn unpackResource( |
| 1199 | return try unpackTarball(f, tmp_directory.handle, dcp.reader()); | 1199 | return try unpackTarball(f, tmp_directory.handle, dcp.reader()); |
| 1200 | }, | 1200 | }, |
| 1201 | .@"tar.zst" => { | 1201 | .@"tar.zst" => { |
| 1202 | const window_size = std.compress.zstd.DecompressorOptions.default_window_buffer_len; | 1202 | const window_size = std.compress.zstd.default_window_len; |
| 1203 | const window_buffer = try f.arena.allocator().create([window_size]u8); | 1203 | const window_buffer = try f.arena.allocator().create([window_size]u8); |
| 1204 | const reader = resource.reader(); | 1204 | const reader = resource.reader(); |
| 1205 | var br = std.io.bufferedReaderSize(std.crypto.tls.max_ciphertext_record_len, reader); | 1205 | var br = std.io.bufferedReaderSize(std.crypto.tls.max_ciphertext_record_len, reader); |
src/Package/Fetch/git.zig+5-7| ... | @@ -1490,7 +1490,7 @@ fn readObjectRaw(allocator: Allocator, reader: anytype, size: u64) ![]u8 { | ... | @@ -1490,7 +1490,7 @@ fn readObjectRaw(allocator: Allocator, reader: anytype, size: u64) ![]u8 { |
| 1490 | /// | 1490 | /// |
| 1491 | /// The format of the delta data is documented in | 1491 | /// The format of the delta data is documented in |
| 1492 | /// [pack-format](https://git-scm.com/docs/pack-format). | 1492 | /// [pack-format](https://git-scm.com/docs/pack-format). |
| 1493 | fn expandDelta(base_object: anytype, delta_reader: anytype, writer: anytype) !void { | 1493 | fn expandDelta(base_object: anytype, delta_reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter) !void { |
| 1494 | while (true) { | 1494 | while (true) { |
| 1495 | const inst: packed struct { value: u7, copy: bool } = @bitCast(delta_reader.readByte() catch |e| switch (e) { | 1495 | const inst: packed struct { value: u7, copy: bool } = @bitCast(delta_reader.readByte() catch |e| switch (e) { |
| 1496 | error.EndOfStream => return, | 1496 | error.EndOfStream => return, |
| ... | @@ -1521,13 +1521,11 @@ fn expandDelta(base_object: anytype, delta_reader: anytype, writer: anytype) !vo | ... | @@ -1521,13 +1521,11 @@ fn expandDelta(base_object: anytype, delta_reader: anytype, writer: anytype) !vo |
| 1521 | var size: u24 = @bitCast(size_parts); | 1521 | var size: u24 = @bitCast(size_parts); |
| 1522 | if (size == 0) size = 0x10000; | 1522 | if (size == 0) size = 0x10000; |
| 1523 | try base_object.seekTo(offset); | 1523 | try base_object.seekTo(offset); |
| 1524 | var copy_reader = std.io.limitedReader(base_object.reader(), size); | 1524 | |
| 1525 | var fifo = std.fifo.LinearFifo(u8, .{ .Static = 4096 }).init(); | 1525 | var base_object_br = base_object.reader(); |
| 1526 | try fifo.pump(copy_reader.reader(), writer); | 1526 | try base_object_br.readAll(writer, .limited(size)); |
| 1527 | } else if (inst.value != 0) { | 1527 | } else if (inst.value != 0) { |
| 1528 | var data_reader = std.io.limitedReader(delta_reader, inst.value); | 1528 | try delta_reader.readAll(writer, .limited(inst.value)); |
| 1529 | var fifo = std.fifo.LinearFifo(u8, .{ .Static = 4096 }).init(); | ||
| 1530 | try fifo.pump(data_reader.reader(), writer); | ||
| 1531 | } else { | 1529 | } else { |
| 1532 | return error.InvalidDeltaInstruction; | 1530 | return error.InvalidDeltaInstruction; |
| 1533 | } | 1531 | } |