authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-04-30 14:18:45-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:28-07:00
log25ac70f973c84bb26ebb1b69eda30d2c6207c9b0
tree1a91822da4b33bf25e14bddc65c3ef51b984e69b
parentc7040171fb06bc4300547a9f4550346b847fc406

std: WIP update more to new reader/writer

delete some bad readers/writers add limited reader update TLS about to do something drastic to compress

18 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.
22
3const std = @import("std.zig");
4
5pub const flate = @import("compress/flate.zig");3pub const flate = @import("compress/flate.zig");
6pub const gzip = @import("compress/gzip.zig");4pub const gzip = @import("compress/gzip.zig");
7pub const zlib = @import("compress/zlib.zig");
8pub const lzma = @import("compress/lzma.zig");5pub const lzma = @import("compress/lzma.zig");
9pub const lzma2 = @import("compress/lzma2.zig");6pub const lzma2 = @import("compress/lzma2.zig");
10pub const xz = @import("compress/xz.zig");7pub const xz = @import("compress/xz.zig");
8pub const zlib = @import("compress/zlib.zig");
11pub const zstd = @import("compress/zstandard.zig");9pub const zstd = @import("compress/zstandard.zig");
1210
13pub 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
33pub fn hashedReader(
34 reader: anytype,
35 hasher: anytype,
36) HashedReader(@TypeOf(reader), @TypeOf(hasher)) {
37 return .{ .child_reader = reader, .hasher = hasher };
38}
39
40pub 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
60pub fn hashedWriter(
61 writer: anytype,
62 hasher: anytype,
63) HashedWriter(@TypeOf(writer), @TypeOf(hasher)) {
64 return .{ .child_writer = writer, .hasher = hasher };
65}
66
67test {11test {
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 }
827827
lib/std/compress/zstandard.zig+110-76
...@@ -1,7 +1,12 @@...@@ -1,7 +1,12 @@
1const std = @import("std");1const std = @import("../std.zig");
2const RingBuffer = std.RingBuffer;2const RingBuffer = std.RingBuffer;
33
4const types = @import("zstandard/types.zig");4const types = @import("zstandard/types.zig");
5
6/// Recommended amount by the standard. Lower than this may result in inability
7/// to decompress common streams.
8pub const default_window_len = 8 * 1024 * 1024;
9
5pub const frame = types.frame;10pub const frame = types.frame;
6pub const compressed_block = types.compressed_block;11pub const compressed_block = types.compressed_block;
712
...@@ -10,7 +15,8 @@ pub const decompress = @import("zstandard/decompress.zig");...@@ -10,7 +15,8 @@ pub const decompress = @import("zstandard/decompress.zig");
10pub const Decompressor = struct {15pub 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;
1217
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,
2633
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 };
3539
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 };
4954
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 }
6773
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 );
8188
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 );
8794
88 self.decode_state = decode_state;95 d.decode_state = decode_state;
89 self.frame_context = frame_context;96 d.frame_context = frame_context;
9097
91 self.checksum = null;98 d.checksum = null;
92 self.current_frame_decompressed_size = 0;99 d.current_frame_decompressed_size = 0;
93100
94 self.state = .InFrame;101 d.state = .InFrame;
95 },102 },
96 }103 }
97 }104 }
98105
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 }
102116
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));
105119 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 0127 return readVec(context, &.{buf});
114 else128 }
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 }
123157
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);
126160
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 }
136170
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) catch173 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);
142176
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 ) catch186 ) catch return error.MalformedBlock;
152 return error.MalformedBlock;187
153188 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 }
157191
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;
160194
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) catch205 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.
808pub fn decodeBlockReader(808pub 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);
840844
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};
5252
53pub const HeaderError = error{ BadMagic, EndOfStream, ReservedBitSet };53pub const HeaderError = error{ ReadFailed, BadMagic, EndOfStream, ReservedBitSet };
5454
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 bytes61/// - `error.EndOfStream` if `source` contains fewer than 4 bytes
62/// - `error.ReservedBitSet` if the frame is a Zstandard frame and any of the62/// - `error.ReservedBitSet` if the frame is a Zstandard frame and any of the
63/// reserved bits are set63/// reserved bits are set
64pub fn decodeFrameHeader(source: anytype) (@TypeOf(source).Error || HeaderError)!FrameHeader {64pub 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};
5050
51pub const close_notify_alert = [_]u8{51pub 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};
5555
56pub const ProtocolVersion = enum(u16) {56pub 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};
140140
141pub const AlertLevel = enum(u8) {141pub const Alert = struct {
142 warning = 1,142 level: Level,
143 fatal = 2,143 description: Description,
144 _,
145};
146144
147pub 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 };
176150
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 };
205180
206 pub fn toError(alert: AlertDescription) Error!void {181 close_notify = 0,
207 switch (alert) {182 unexpected_message = 10,
208 .close_notify => {}, // not an error183 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 error202 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};
239244
240pub const SignatureScheme = enum(u16) {245pub 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`.
41reader: std.io.BufferedReader,41reader: std.io.BufferedReader,
42/// Populated under various error conditions.42/// Populated when `error.TlsAlert` is returned.
43diagnostics: Diagnostics,43alert: ?tls.Alert,
44read_err: ?ReadError,
4445
45tls_version: tls.ProtocolVersion,46tls_version: tls.ProtocolVersion,
46read_seq: u64,47read_seq: u64,
...@@ -69,15 +70,16 @@ application_cipher: tls.ApplicationCipher,...@@ -69,15 +70,16 @@ application_cipher: tls.ApplicationCipher,
69/// this connection.70/// this connection.
70ssl_key_log: ?*SslKeyLog,71ssl_key_log: ?*SslKeyLog,
7172
72pub const Diagnostics = union(enum) {73pub 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 makes79 TlsDecodeError,
79 /// no sense in a handshake.80 TlsRecordOverflow,
80 alert: tls.AlertDescription,81 TlsUnexpectedMessage,
82 TlsIllegalParameter,
81};83};
8284
83pub const SslKeyLog = struct {85pub const SslKeyLog = struct {
...@@ -128,14 +130,13 @@ pub const Options = struct {...@@ -128,14 +130,13 @@ pub const Options = struct {
128};130};
129131
130const InitError = error{132const 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 {
924fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {925fn 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 truncation943/// distinguish between a properly finished TLS session, or a truncation
943/// attack.944/// attack.
944pub fn end(c: *Client) std.io.Writer.Error!void {945pub 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}
10711072
1072fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {1073fn 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;
10751076
1076 var vp: VecPut = .{ .iovecs = data };1077 var vp: VecPut = .{ .iovecs = data };
10771078
...@@ -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 }
11031104
...@@ -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 }
11551156
...@@ -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);
11921193
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) catch1258 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) catch1290 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 closures1312 // 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}
14251424
1425fn failRead(c: *Client, err: ReadError) error{ReadFailed} {
1426 c.read_err = err;
1427 return error.ReadFailed;
1428}
1429
1426fn logSecrets(key_log_file: std.fs.File, context: anytype, secrets: anytype) void {1430fn 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 stream28/// 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 all29/// 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`.
31ssl_key_logger: ?*std.io.BufferedWriter = null,31ssl_key_log: ?*std.io.BufferedWriter = null,
3232
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 contain347 // 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 orelse1673 .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 @@
1const std = @import("std.zig");
2const builtin = @import("builtin");1const builtin = @import("builtin");
3const root = @import("root");
4const c = std.c;
5const is_windows = builtin.os.tag == .windows;2const is_windows = builtin.os.tag == .windows;
3
4const std = @import("std.zig");
6const windows = std.os.windows;5const windows = std.os.windows;
7const posix = std.posix;6const posix = std.posix;
8const math = std.math;7const math = std.math;
9const assert = std.debug.assert;8const assert = std.debug.assert;
10const fs = std.fs;
11const mem = std.mem;
12const meta = std.meta;
13const File = std.fs.File;
14const Allocator = std.mem.Allocator;9const Allocator = std.mem.Allocator;
15const Alignment = std.mem.Alignment;10const Alignment = std.mem.Alignment;
1611
...@@ -21,12 +16,6 @@ pub const BufferedReader = @import("io/BufferedReader.zig");...@@ -21,12 +16,6 @@ pub const BufferedReader = @import("io/BufferedReader.zig");
21pub const BufferedWriter = @import("io/BufferedWriter.zig");16pub const BufferedWriter = @import("io/BufferedWriter.zig");
22pub const AllocatingWriter = @import("io/AllocatingWriter.zig");17pub const AllocatingWriter = @import("io/AllocatingWriter.zig");
2318
24pub const CWriter = @import("io/c_writer.zig").CWriter;
25pub const cWriter = @import("io/c_writer.zig").cWriter;
26
27pub const LimitedReader = @import("io/limited_reader.zig").LimitedReader;
28pub const limitedReader = @import("io/limited_reader.zig").limitedReader;
29
30pub const MultiWriter = @import("io/multi_writer.zig").MultiWriter;19pub const MultiWriter = @import("io/multi_writer.zig").MultiWriter;
31pub const multiWriter = @import("io/multi_writer.zig").multiWriter;20pub const multiWriter = @import("io/multi_writer.zig").multiWriter;
3221
...@@ -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;
38pub const ChangeDetectionStream = @import("io/change_detection_stream.zig").ChangeDetectionStream;27pub const ChangeDetectionStream = @import("io/change_detection_stream.zig").ChangeDetectionStream;
39pub const changeDetectionStream = @import("io/change_detection_stream.zig").changeDetectionStream;28pub const changeDetectionStream = @import("io/change_detection_stream.zig").changeDetectionStream;
4029
41pub const FindByteWriter = @import("io/find_byte_writer.zig").FindByteWriter;
42pub const findByteWriter = @import("io/find_byte_writer.zig").findByteWriter;
43
44pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAtomicFile;30pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAtomicFile;
4531
46pub const tty = @import("io/tty.zig");32pub 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;
6const Allocator = std.mem.Allocator;6const Allocator = std.mem.Allocator;
7const ArrayList = std.ArrayListUnmanaged;7const ArrayList = std.ArrayListUnmanaged;
88
9pub const Limited = @import("Reader/Limited.zig");
10
9context: ?*anyopaque,11context: ?*anyopaque,
10vtable: *const VTable,12vtable: *const VTable,
1113
...@@ -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}
254256
257pub fn limited(r: Reader, limit: Limit) Limited {
258 return .{
259 .unlimited_reader = r,
260 .remaining = limit,
261 };
262}
263
255fn endingRead(context: ?*anyopaque, bw: *BufferedWriter, limit: Limit) RwError!usize {264fn 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 @@
1const Limited = @This();
2
3const std = @import("../../std.zig");
4const Reader = std.io.Reader;
5const BufferedWriter = std.io.BufferedWriter;
6
7unlimited_reader: Reader,
8remaining: Reader.Limit,
9
10pub 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
21fn 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
29fn 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
37fn 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};
9696
97pub fn writeVec(w: Writer, data: []const []const u8) Error!usize {97pub 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}
100101
101pub fn writeSplat(w: Writer, data: []const []const u8, splat: usize) Error!usize {102pub 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}
104106
lib/std/io/c_writer.zig deleted-44
...@@ -1,44 +0,0 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const io = std.io;
4const testing = std.testing;
5
6pub const CWriter = io.Writer(*std.c.FILE, std.fs.File.WriteError, cWriterWrite);
7
8pub fn cWriter(c_file: *std.c.FILE) CWriter {
9 return .{ .context = c_file };
10}
11
12fn 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
32test 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 @@
1const std = @import("../std.zig");
2const io = std.io;
3const assert = std.debug.assert;
4const testing = std.testing;
5
6pub 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
31pub fn limitedReader(inner_reader: anytype, bytes_left: u64) LimitedReader(@TypeOf(inner_reader)) {
32 return .{ .inner_reader = inner_reader, .bytes_left = bytes_left };
33}
34
35test "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 }
19261926
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 {
154pub fn decompress(154pub 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 in1491/// 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).
1493fn expandDelta(base_object: anytype, delta_reader: anytype, writer: anytype) !void {1493fn 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 }