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 @@
11//! Compression algorithms.
22
3const std = @import("std.zig");
4
53pub const flate = @import("compress/flate.zig");
64pub const gzip = @import("compress/gzip.zig");
7pub const zlib = @import("compress/zlib.zig");
85pub const lzma = @import("compress/lzma.zig");
96pub const lzma2 = @import("compress/lzma2.zig");
107pub const xz = @import("compress/xz.zig");
8pub const zlib = @import("compress/zlib.zig");
119pub 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
6711test {
12 _ = flate;
13 _ = gzip;
6814 _ = lzma;
6915 _ = lzma2;
7016 _ = xz;
71 _ = zstd;
72 _ = flate;
73 _ = gzip;
7417 _ = zlib;
18 _ = zstd;
7519}
lib/std/compress/flate/inflate.zig+1-1
......@@ -821,7 +821,7 @@ pub fn BitReader(comptime T: type) type {
821821 /// Skip zero terminated string.
822822 pub fn skipStringZ(self: *Self) !void {
823823 while (true) {
824 if (try self.readF(u8, 0) == 0) break;
824 if (try self.readF(u8, .{}) == 0) break;
825825 }
826826 }
827827
lib/std/compress/zstandard.zig+110-76
......@@ -1,7 +1,12 @@
1const std = @import("std");
1const std = @import("../std.zig");
22const RingBuffer = std.RingBuffer;
33
44const 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
510pub const frame = types.frame;
611pub const compressed_block = types.compressed_block;
712
......@@ -10,7 +15,8 @@ pub const decompress = @import("zstandard/decompress.zig");
1015pub const Decompressor = struct {
1116 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,
1420 state: enum { NewFrame, InFrame, LastBlock },
1521 decode_state: decompress.block.DecodeState,
1622 frame_context: decompress.FrameContext,
......@@ -23,14 +29,12 @@ pub const Decompressor = struct {
2329 verify_checksum: bool,
2430 checksum: ?u32,
2531 current_frame_decompressed_size: usize,
32 err: ?Error = null,
2633
2734 pub const Options = struct {
2835 verify_checksum: bool = true,
36 /// See `default_window_len`.
2937 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;
3438 };
3539
3640 const WindowBuffer = struct {
......@@ -45,11 +49,13 @@ pub const Decompressor = struct {
4549 MalformedBlock,
4650 MalformedFrame,
4751 OutOfMemory,
52 EndOfStream,
4853 };
4954
50 pub fn init(source: *std.io.BufferedReader, options: Options) Decompressor {
55 pub fn init(input: *std.io.BufferedReader, options: Options) Decompressor {
5156 return .{
52 .source = source,
57 .input = input,
58 .bytes_read = 0,
5359 .state = .NewFrame,
5460 .decode_state = undefined,
5561 .frame_context = undefined,
......@@ -65,100 +71,128 @@ pub const Decompressor = struct {
6571 };
6672 }
6773
68 fn frameInit(self: *Decompressor) !void {
69 const source_reader = self.source;
70 switch (try decompress.decodeFrameHeader(source_reader)) {
74 fn frameInit(d: *Decompressor) !void {
75 const in = d.input;
76 switch (try decompress.decodeFrameHeader(in, &d.bytes_read)) {
7177 .skippable => |header| {
72 try source_reader.skipBytes(header.frame_size, .{});
73 self.state = .NewFrame;
78 try in.discardAll(header.frame_size);
79 d.bytes_read += header.frame_size;
80 d.state = .NewFrame;
7481 },
7582 .zstandard => |header| {
7683 const frame_context = try decompress.FrameContext.init(
7784 header,
78 self.buffer.data.len,
79 self.verify_checksum,
85 d.buffer.data.len,
86 d.verify_checksum,
8087 );
8188
8289 const decode_state = decompress.block.DecodeState.init(
83 &self.literal_fse_buffer,
84 &self.match_fse_buffer,
85 &self.offset_fse_buffer,
90 &d.literal_fse_buffer,
91 &d.match_fse_buffer,
92 &d.offset_fse_buffer,
8693 );
8794
88 self.decode_state = decode_state;
89 self.frame_context = frame_context;
95 d.decode_state = decode_state;
96 d.frame_context = frame_context;
9097
91 self.checksum = null;
92 self.current_frame_decompressed_size = 0;
98 d.checksum = null;
99 d.current_frame_decompressed_size = 0;
93100
94 self.state = .InFrame;
101 d.state = .InFrame;
95102 },
96103 }
97104 }
98105
99106 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 };
101115 }
102116
103 pub fn read(self: *Decompressor, buffer: []u8) Error!usize {
104 if (buffer.len == 0) return 0;
105
106 var size: usize = 0;
107 while (size == 0) {
108 while (self.state == .NewFrame) {
109 const initial_count = self.source.bytes_read;
110 self.frameInit() catch |err| switch (err) {
111 error.DictionaryIdFlagUnsupported => return error.DictionaryIdFlagUnsupported,
112 error.EndOfStream => return if (self.source.bytes_read == initial_count)
113 0
114 else
115 error.MalformedFrame,
116 else => return error.MalformedFrame,
117 };
118 }
119 size = try self.readInner(buffer);
117 fn read(context: ?*anyopaque, bw: *std.io.BufferedWriter, limit: std.io.Reader.Limit) std.io.Reader.RwError!usize {
118 const buf = limit.slice(try bw.writableSliceGreedy(1));
119 const n = try readVec(context, &.{buf});
120 bw.advance(n);
121 return n;
122 }
123
124 fn discard(context: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize {
125 var trash: [128]u8 = undefined;
126 const buf = limit.slice(&trash);
127 return readVec(context, &.{buf});
128 }
129
130 fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
131 const d: *Decompressor = @ptrCast(@alignCast(context));
132 if (data.len == 0) return 0;
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 };
120151 }
121 return size;
152 return d.readInner(buffer) catch |err| {
153 d.err = err;
154 return error.ReadFailed;
155 };
122156 }
123157
124 fn readInner(self: *Decompressor, buffer: []u8) Error!usize {
125 std.debug.assert(self.state != .NewFrame);
158 fn readInner(d: *Decompressor, buffer: []u8) Error!usize {
159 std.debug.assert(d.state != .NewFrame);
126160
127161 var ring_buffer = RingBuffer{
128 .data = self.buffer.data,
129 .read_index = self.buffer.read_index,
130 .write_index = self.buffer.write_index,
162 .data = d.buffer.data,
163 .read_index = d.buffer.read_index,
164 .write_index = d.buffer.write_index,
131165 };
132166 defer {
133 self.buffer.read_index = ring_buffer.read_index;
134 self.buffer.write_index = ring_buffer.write_index;
167 d.buffer.read_index = ring_buffer.read_index;
168 d.buffer.write_index = ring_buffer.write_index;
135169 }
136170
137 const source_reader = self.source;
138 while (ring_buffer.isEmpty() and self.state != .LastBlock) {
139 const header_bytes = source_reader.readBytesNoEof(3) catch
140 return error.MalformedFrame;
141 const block_header = decompress.block.decodeBlockHeader(&header_bytes);
171 const in = d.input;
172 while (ring_buffer.isEmpty() and d.state != .LastBlock) {
173 const header_bytes = try in.takeArray(3);
174 d.bytes_read += header_bytes.len;
175 const block_header = decompress.block.decodeBlockHeader(header_bytes);
142176
143177 decompress.block.decodeBlockReader(
144178 &ring_buffer,
145 source_reader,
179 in,
180 &d.bytes_read,
146181 block_header,
147 &self.decode_state,
148 self.frame_context.block_size_max,
149 &self.literals_buffer,
150 &self.sequence_buffer,
151 ) catch
152 return error.MalformedBlock;
153
154 if (self.frame_context.content_size) |size| {
155 if (self.current_frame_decompressed_size > size) return error.MalformedFrame;
182 &d.decode_state,
183 d.frame_context.block_size_max,
184 &d.literals_buffer,
185 &d.sequence_buffer,
186 ) catch return error.MalformedBlock;
187
188 if (d.frame_context.content_size) |size| {
189 if (d.current_frame_decompressed_size > size) return error.MalformedFrame;
156190 }
157191
158192 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| {
162196 if (size > 0) {
163197 const written_slice = ring_buffer.sliceLast(size);
164198 hasher.update(written_slice.first);
......@@ -166,19 +200,19 @@ pub const Decompressor = struct {
166200 }
167201 }
168202 if (block_header.last_block) {
169 self.state = .LastBlock;
170 if (self.frame_context.has_checksum) {
171 const checksum = source_reader.readInt(u32, .little) catch
172 return error.MalformedFrame;
173 if (self.verify_checksum) {
174 if (self.frame_context.hasher_opt) |*hasher| {
203 d.state = .LastBlock;
204 if (d.frame_context.has_checksum) {
205 const checksum = in.readInt(u32, .little) catch return error.MalformedFrame;
206 d.bytes_read += 4;
207 if (d.verify_checksum) {
208 if (d.frame_context.hasher_opt) |*hasher| {
175209 if (checksum != decompress.computeChecksum(hasher))
176210 return error.ChecksumFailure;
177211 }
178212 }
179213 }
180 if (self.frame_context.content_size) |content_size| {
181 if (content_size != self.current_frame_decompressed_size) {
214 if (d.frame_context.content_size) |content_size| {
215 if (content_size != d.current_frame_decompressed_size) {
182216 return error.MalformedFrame;
183217 }
184218 }
......@@ -189,8 +223,8 @@ pub const Decompressor = struct {
189223 if (size > 0) {
190224 ring_buffer.readFirstAssumeLength(buffer, size);
191225 }
192 if (self.state == .LastBlock and ring_buffer.len() == 0) {
193 self.state = .NewFrame;
226 if (d.state == .LastBlock and ring_buffer.len() == 0) {
227 d.state = .NewFrame;
194228 }
195229 return size;
196230 }
lib/std/compress/zstandard/decode/block.zig+10-6
......@@ -807,7 +807,8 @@ pub fn decodeBlockRingBuffer(
807807/// contain enough bytes.
808808pub fn decodeBlockReader(
809809 dest: *RingBuffer,
810 source: anytype,
810 in: *std.io.BufferedReader,
811 bytes_read: *usize,
811812 block_header: frame.Zstandard.Block.Header,
812813 decode_state: *DecodeState,
813814 block_size_max: usize,
......@@ -815,26 +816,29 @@ pub fn decodeBlockReader(
815816 sequence_buffer: []u8,
816817) !void {
817818 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();
820819 if (block_size_max < block_size) return error.BlockSizeOverMaximum;
821820 switch (block_header.block_type) {
822821 .raw => {
823822 if (block_size == 0) return;
824823 const slice = dest.sliceAt(dest.write_index, block_size);
825 try source.readNoEof(slice.first);
826 try source.readNoEof(slice.second);
824 var vecs: [2][]u8 = &.{slice.first, slice.second };
825 try in.readVecAll(&vecs);
826 assert(slice.first.len + slice.second.len == block_size);
827 bytes_read.* += block_size;
827828 dest.write_index = dest.mask2(dest.write_index + block_size);
828829 decode_state.written_count += block_size;
829830 },
830831 .rle => {
831 const byte = try source.readByte();
832 const byte = try in.takeByte();
833 bytes_read.* += 1;
832834 for (0..block_size) |_| {
833835 dest.writeAssumeCapacity(byte);
834836 }
835837 decode_state.written_count += block_size;
836838 },
837839 .compressed => {
840 var block_reader_limited = std.io.limitedReader(source, block_size);
841 const block_reader = block_reader_limited.reader();
838842 const literals = try decodeLiteralsSection(block_reader, literals_buffer);
839843 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) {
5050 skippable: SkippableHeader,
5151};
5252
53pub const HeaderError = error{ BadMagic, EndOfStream, ReservedBitSet };
53pub const HeaderError = error{ ReadFailed, BadMagic, EndOfStream, ReservedBitSet };
5454
5555/// Returns the header of the frame at the beginning of `source`.
5656///
......@@ -61,16 +61,21 @@ pub const HeaderError = error{ BadMagic, EndOfStream, ReservedBitSet };
6161/// - `error.EndOfStream` if `source` contains fewer than 4 bytes
6262/// - `error.ReservedBitSet` if the frame is a Zstandard frame and any of the
6363/// reserved bits are set
64pub fn decodeFrameHeader(source: anytype) (@TypeOf(source).Error || HeaderError)!FrameHeader {
65 const magic = try source.readInt(u32, .little);
64pub fn decodeFrameHeader(br: *std.io.BufferedReader, bytes_read: *usize) HeaderError!FrameHeader {
65 const magic = try br.readInt(u32, .little);
66 bytes_read.* += 4;
6667 const frame_type = try frameType(magic);
6768 switch (frame_type) {
68 .zstandard => return FrameHeader{ .zstandard = try decodeZstandardHeader(source) },
69 .skippable => return FrameHeader{
70 .skippable = .{
71 .magic_number = magic,
72 .frame_size = try source.readInt(u32, .little),
73 },
69 .zstandard => return .{ .zstandard = try decodeZstandardHeader(br, bytes_read) },
70 .skippable => {
71 const result: FrameHeader = .{
72 .skippable = .{
73 .magic_number = magic,
74 .frame_size = try br.readInt(u32, .little),
75 },
76 };
77 bytes_read.* += 4;
78 return result;
7479 },
7580 }
7681}
lib/std/crypto/tls.zig+99-94
......@@ -49,8 +49,8 @@ pub const hello_retry_request_sequence = [32]u8{
4949};
5050
5151pub const close_notify_alert = [_]u8{
52 @intFromEnum(AlertLevel.warning),
53 @intFromEnum(AlertDescription.close_notify),
52 @intFromEnum(Alert.Level.warning),
53 @intFromEnum(Alert.Description.close_notify),
5454};
5555
5656pub const ProtocolVersion = enum(u16) {
......@@ -138,103 +138,108 @@ pub const ExtensionType = enum(u16) {
138138 _,
139139};
140140
141pub const AlertLevel = enum(u8) {
142 warning = 1,
143 fatal = 2,
144 _,
145};
141pub const Alert = struct {
142 level: Level,
143 description: Description,
146144
147pub const AlertDescription = enum(u8) {
148 pub const Error = error{
149 TlsAlertUnexpectedMessage,
150 TlsAlertBadRecordMac,
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,
145 pub const Level = enum(u8) {
146 warning = 1,
147 fatal = 2,
148 _,
175149 };
176150
177 close_notify = 0,
178 unexpected_message = 10,
179 bad_record_mac = 20,
180 record_overflow = 22,
181 handshake_failure = 40,
182 bad_certificate = 42,
183 unsupported_certificate = 43,
184 certificate_revoked = 44,
185 certificate_expired = 45,
186 certificate_unknown = 46,
187 illegal_parameter = 47,
188 unknown_ca = 48,
189 access_denied = 49,
190 decode_error = 50,
191 decrypt_error = 51,
192 protocol_version = 70,
193 insufficient_security = 71,
194 internal_error = 80,
195 inappropriate_fallback = 86,
196 user_canceled = 90,
197 missing_extension = 109,
198 unsupported_extension = 110,
199 unrecognized_name = 112,
200 bad_certificate_status_response = 113,
201 unknown_psk_identity = 115,
202 certificate_required = 116,
203 no_application_protocol = 120,
204 _,
151 pub const Description = enum(u8) {
152 pub const Error = error{
153 TlsAlertUnexpectedMessage,
154 TlsAlertBadRecordMac,
155 TlsAlertRecordOverflow,
156 TlsAlertHandshakeFailure,
157 TlsAlertBadCertificate,
158 TlsAlertUnsupportedCertificate,
159 TlsAlertCertificateRevoked,
160 TlsAlertCertificateExpired,
161 TlsAlertCertificateUnknown,
162 TlsAlertIllegalParameter,
163 TlsAlertUnknownCa,
164 TlsAlertAccessDenied,
165 TlsAlertDecodeError,
166 TlsAlertDecryptError,
167 TlsAlertProtocolVersion,
168 TlsAlertInsufficientSecurity,
169 TlsAlertInternalError,
170 TlsAlertInappropriateFallback,
171 TlsAlertMissingExtension,
172 TlsAlertUnsupportedExtension,
173 TlsAlertUnrecognizedName,
174 TlsAlertBadCertificateStatusResponse,
175 TlsAlertUnknownPskIdentity,
176 TlsAlertCertificateRequired,
177 TlsAlertNoApplicationProtocol,
178 TlsAlertUnknown,
179 };
205180
206 pub fn toError(alert: AlertDescription) Error!void {
207 switch (alert) {
208 .close_notify => {}, // not an error
209 .unexpected_message => return error.TlsAlertUnexpectedMessage,
210 .bad_record_mac => return error.TlsAlertBadRecordMac,
211 .record_overflow => return error.TlsAlertRecordOverflow,
212 .handshake_failure => return error.TlsAlertHandshakeFailure,
213 .bad_certificate => return error.TlsAlertBadCertificate,
214 .unsupported_certificate => return error.TlsAlertUnsupportedCertificate,
215 .certificate_revoked => return error.TlsAlertCertificateRevoked,
216 .certificate_expired => return error.TlsAlertCertificateExpired,
217 .certificate_unknown => return error.TlsAlertCertificateUnknown,
218 .illegal_parameter => return error.TlsAlertIllegalParameter,
219 .unknown_ca => return error.TlsAlertUnknownCa,
220 .access_denied => return error.TlsAlertAccessDenied,
221 .decode_error => return error.TlsAlertDecodeError,
222 .decrypt_error => return error.TlsAlertDecryptError,
223 .protocol_version => return error.TlsAlertProtocolVersion,
224 .insufficient_security => return error.TlsAlertInsufficientSecurity,
225 .internal_error => return error.TlsAlertInternalError,
226 .inappropriate_fallback => return error.TlsAlertInappropriateFallback,
227 .user_canceled => {}, // not an error
228 .missing_extension => return error.TlsAlertMissingExtension,
229 .unsupported_extension => return error.TlsAlertUnsupportedExtension,
230 .unrecognized_name => return error.TlsAlertUnrecognizedName,
231 .bad_certificate_status_response => return error.TlsAlertBadCertificateStatusResponse,
232 .unknown_psk_identity => return error.TlsAlertUnknownPskIdentity,
233 .certificate_required => return error.TlsAlertCertificateRequired,
234 .no_application_protocol => return error.TlsAlertNoApplicationProtocol,
235 _ => return error.TlsAlertUnknown,
181 close_notify = 0,
182 unexpected_message = 10,
183 bad_record_mac = 20,
184 record_overflow = 22,
185 handshake_failure = 40,
186 bad_certificate = 42,
187 unsupported_certificate = 43,
188 certificate_revoked = 44,
189 certificate_expired = 45,
190 certificate_unknown = 46,
191 illegal_parameter = 47,
192 unknown_ca = 48,
193 access_denied = 49,
194 decode_error = 50,
195 decrypt_error = 51,
196 protocol_version = 70,
197 insufficient_security = 71,
198 internal_error = 80,
199 inappropriate_fallback = 86,
200 user_canceled = 90,
201 missing_extension = 109,
202 unsupported_extension = 110,
203 unrecognized_name = 112,
204 bad_certificate_status_response = 113,
205 unknown_psk_identity = 115,
206 certificate_required = 116,
207 no_application_protocol = 120,
208 _,
209
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 }
236241 }
237 }
242 };
238243};
239244
240245pub const SignatureScheme = enum(u16) {
lib/std/crypto/tls/Client.zig+55-51
......@@ -39,8 +39,9 @@ output: *std.io.BufferedWriter,
3939///
4040/// Its buffer aliases the buffer of `input`.
4141reader: std.io.BufferedReader,
42/// Populated under various error conditions.
43diagnostics: Diagnostics,
42/// Populated when `error.TlsAlert` is returned.
43alert: ?tls.Alert,
44read_err: ?ReadError,
4445
4546tls_version: tls.ProtocolVersion,
4647read_seq: u64,
......@@ -69,15 +70,16 @@ application_cipher: tls.ApplicationCipher,
6970/// this connection.
7071ssl_key_log: ?*SslKeyLog,
7172
72pub const Diagnostics = union(enum) {
73 /// Any `ReadFailure` and `WriteFailure` was due to `input` or `output`
74 /// returning the error, respectively.
75 transitive,
76 /// Populated on `error.TlsAlert`.
77 ///
78 /// If this isn't a error alert, then it's a closure alert, which makes
79 /// no sense in a handshake.
80 alert: tls.AlertDescription,
73pub const ReadError = error{
74 /// The alert description will be stored in `alert`.
75 TlsAlert,
76 TlsBadLength,
77 TlsBadRecordMac,
78 TlsConnectionTruncated,
79 TlsDecodeError,
80 TlsRecordOverflow,
81 TlsUnexpectedMessage,
82 TlsIllegalParameter,
8183};
8284
8385pub const SslKeyLog = struct {
......@@ -128,14 +130,13 @@ pub const Options = struct {
128130};
129131
130132const InitError = error{
131 //OutOfMemory,
132 WriteFailure,
133 ReadFailure,
133 WriteFailed,
134 ReadFailed,
134135 InsufficientEntropy,
135136 DiskQuota,
136137 LockViolation,
137138 NotOpenForWriting,
138 /// The alert description will be stored in `Options.Diagnostics.alert`.
139 /// The alert description will be stored in `alert`.
139140 TlsAlert,
140141 TlsUnexpectedMessage,
141142 TlsIllegalParameter,
......@@ -192,7 +193,7 @@ pub fn init(
192193) InitError!void {
193194 assert(input.storage.buffer.len >= min_buffer_len);
194195 assert(output.buffer.len >= min_buffer_len);
195 client.diagnostics = .transient;
196 client.alert = null;
196197 const host = switch (options.host) {
197198 .no_verification => "",
198199 .explicit => |host| host,
......@@ -417,10 +418,10 @@ pub fn init(
417418 switch (ct) {
418419 .alert => {
419420 ctd.ensure(2) catch continue :fragment;
420 const level = ctd.decode(tls.AlertLevel);
421 const desc = ctd.decode(tls.AlertDescription);
422 _ = level;
423 client.diagnostics = .{ .alert = desc };
421 client.alert = .{
422 .level = ctd.decode(tls.Alert.Level),
423 .description = ctd.decode(tls.Alert.Description),
424 };
424425 return error.TlsAlert;
425426 },
426427 .change_cipher_spec => {
......@@ -924,7 +925,7 @@ pub fn writer(c: *Client) std.io.Writer {
924925fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
925926 const c: *Client = @alignCast(@ptrCast(context));
926927 const sliced_data = if (splat == 0) data[0..data.len -| 1] else data;
927 const output = &c.output;
928 const output = c.output;
928929 const ciphertext_buf = try output.writableSliceGreedy(min_buffer_len);
929930 var total_clear: usize = 0;
930931 var ciphertext_end: usize = 0;
......@@ -942,7 +943,7 @@ fn writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) std.i
942943/// distinguish between a properly finished TLS session, or a truncation
943944/// attack.
944945pub fn end(c: *Client) std.io.Writer.Error!void {
945 const output = &c.output;
946 const output = c.output;
946947 const ciphertext_buf = try output.writableSliceGreedy(min_buffer_len);
947948 const prepared = prepareCiphertextRecord(c, ciphertext_buf, &tls.close_notify_alert, .alert);
948949 output.advance(prepared.cleartext_len);
......@@ -1062,16 +1063,16 @@ fn read(
10621063 context: ?*anyopaque,
10631064 bw: *std.io.BufferedWriter,
10641065 limit: std.io.Reader.Limit,
1065) std.io.Reader.RwError!std.io.Reader.Status {
1066) std.io.Reader.RwError!usize {
10661067 const buf = limit.slice(try bw.writableSliceGreedy(1));
1067 const status = try readVec(context, &.{buf});
1068 bw.advance(status.len);
1069 return status;
1068 const n = try readVec(context, &.{buf});
1069 bw.advance(n);
1070 return n;
10701071}
10711072
10721073fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
10731074 const c: *Client = @ptrCast(@alignCast(context));
1074 if (c.eof()) return .{ .end = true };
1075 if (c.eof()) return error.EndOfStream;
10751076
10761077 var vp: VecPut = .{ .iovecs = data };
10771078
......@@ -1093,11 +1094,11 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
10931094 if (c.received_close_notify) {
10941095 c.partial_ciphertext_end = 0;
10951096 assert(vp.total == amt);
1096 return .{ .len = amt, .end = c.eof() };
1097 return amt;
10971098 } else if (amt > 0) {
10981099 // We don't need more data, so don't call read.
10991100 assert(vp.total == amt);
1100 return .{ .len = amt, .end = c.eof() };
1101 return amt;
11011102 }
11021103 }
11031104
......@@ -1149,7 +1150,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
11491150 if (c.allow_truncation_attacks) {
11501151 c.received_close_notify = true;
11511152 } else {
1152 return error.TlsConnectionTruncated;
1153 return failRead(c, error.TlsConnectionTruncated);
11531154 }
11541155 }
11551156
......@@ -1168,7 +1169,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
11681169 // Perfect split.
11691170 if (frag.ptr == frag1.ptr) {
11701171 c.partial_ciphertext_end = c.partial_ciphertext_idx;
1171 return .{ .len = vp.total, .end = c.eof() };
1172 return vp.total;
11721173 }
11731174 frag = frag1;
11741175 in = 0;
......@@ -1188,7 +1189,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
11881189 const record_len_byte_0: u16 = straddleByte(frag, frag1, in + 3);
11891190 const record_len_byte_1: u16 = straddleByte(frag, frag1, in + 4);
11901191 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
11931194 const full_record_len = record_len + tls.record_header_len;
11941195 const second_len = full_record_len - first.len;
......@@ -1208,7 +1209,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
12081209 in += 2;
12091210 _ = legacy_version;
12101211 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);
12121213 in += 2;
12131214 const the_end = in + record_len;
12141215 if (the_end > frag.len) {
......@@ -1255,7 +1256,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
12551256 &cleartext_stack_buffer;
12561257 const cleartext = cleartext_buf[0..ciphertext.len];
12571258 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, pv.server_key) catch
1258 return error.TlsBadRecordMac;
1259 return failRead(c, error.TlsBadRecordMac);
12591260 const msg = mem.trimEnd(u8, cleartext, "\x00");
12601261 break :cleartext .{ msg[0 .. msg.len - 1], @enumFromInt(msg[msg.len - 1]) };
12611262 },
......@@ -1287,7 +1288,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
12871288 &cleartext_stack_buffer;
12881289 const cleartext = cleartext_buf[0..ciphertext.len];
12891290 P.AEAD.decrypt(cleartext, ciphertext, auth_tag, ad, nonce, pv.server_write_key) catch
1290 return error.TlsBadRecordMac;
1291 return failRead(c, error.TlsBadRecordMac);
12911292 break :cleartext .{ cleartext, ct };
12921293 },
12931294 else => unreachable,
......@@ -1296,23 +1297,24 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
12961297 c.read_seq = try std.math.add(u64, c.read_seq, 1);
12971298 switch (inner_ct) {
12981299 .alert => {
1299 if (cleartext.len != 2) return error.TlsDecodeError;
1300 const level: tls.AlertLevel = @enumFromInt(cleartext[0]);
1301 _ = level;
1302 const desc: tls.AlertDescription = @enumFromInt(cleartext[1]);
1303 switch (desc) {
1300 if (cleartext.len != 2) return failRead(c, error.TlsDecodeError);
1301 const alert: tls.Alert = .{
1302 .level = @enumFromInt(cleartext[0]),
1303 .description = @enumFromInt(cleartext[1]),
1304 };
1305 switch (alert.description) {
13041306 .close_notify => {
13051307 c.received_close_notify = true;
13061308 c.partial_ciphertext_end = c.partial_ciphertext_idx;
1307 return .{ .len = vp.total, .end = c.eof() };
1309 return vp.total;
13081310 },
13091311 .user_canceled => {
13101312 // TODO: handle server-side closures
1311 return error.TlsUnexpectedMessage;
1313 return failRead(c, error.TlsUnexpectedMessage);
13121314 },
13131315 else => {
1314 c.diagnostics = .{ .alert = desc };
1315 return error.TlsAlert;
1316 c.alert = alert;
1317 return failRead(c, error.TlsAlert);
13161318 },
13171319 }
13181320 },
......@@ -1324,8 +1326,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
13241326 const handshake_len = mem.readInt(u24, cleartext[ct_i..][0..3], .big);
13251327 ct_i += 3;
13261328 const next_handshake_i = ct_i + handshake_len;
1327 if (next_handshake_i > cleartext.len)
1328 return error.TlsBadLength;
1329 if (next_handshake_i > cleartext.len) return failRead(c, error.TlsBadLength);
13291330 const handshake = cleartext[ct_i..next_handshake_i];
13301331 switch (handshake_type) {
13311332 .new_session_ticket => {
......@@ -1371,12 +1372,10 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
13711372 c.write_seq = 0;
13721373 },
13731374 .update_not_requested => {},
1374 _ => return error.TlsIllegalParameter,
1375 _ => return failRead(c, error.TlsIllegalParameter),
13751376 }
13761377 },
1377 else => {
1378 return error.TlsUnexpectedMessage;
1379 },
1378 else => return failRead(c, error.TlsUnexpectedMessage),
13801379 }
13811380 ct_i = next_handshake_i;
13821381 if (ct_i >= cleartext.len) break;
......@@ -1411,7 +1410,7 @@ fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
14111410 vp.next(cleartext.len);
14121411 }
14131412 },
1414 else => return error.TlsUnexpectedMessage,
1413 else => return failRead(c, error.TlsUnexpectedMessage),
14151414 }
14161415 in = end;
14171416 }
......@@ -1423,6 +1422,11 @@ fn discard(context: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error
14231422 @panic("TODO");
14241423}
14251424
1425fn failRead(c: *Client, err: ReadError) error{ReadFailed} {
1426 c.read_err = err;
1427 return error.ReadFailed;
1428}
1429
14261430fn logSecrets(key_log_file: std.fs.File, context: anytype, secrets: anytype) void {
14271431 const locked = if (key_log_file.lock(.exclusive)) |_| true else |_| false;
14281432 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
2828/// If non-null, ssl secrets are logged to a stream. Creating such a stream
2929/// allows other processes with access to that stream to decrypt all
3030/// traffic over connections created with this `Client`.
31ssl_key_logger: ?*std.io.BufferedWriter = null,
31ssl_key_log: ?*std.io.BufferedWriter = null,
3232
3333/// When this is `true`, the next time this client performs an HTTPS request,
3434/// it will first rescan the system for root certificates.
......@@ -342,7 +342,7 @@ pub const Connection = struct {
342342 tls.client.init(&tls.reader, &tls.writer, .{
343343 .host = .{ .explicit = remote_host },
344344 .ca = .{ .bundle = client.ca_bundle },
345 .ssl_key_logger = client.ssl_key_logger,
345 .ssl_key_log = client.ssl_key_log,
346346 }) catch return error.TlsInitializationFailed;
347347 // This is appropriate for HTTPS because the HTTP headers contain
348348 // the content length which is used to detect truncation attacks.
......@@ -1671,7 +1671,7 @@ pub fn fetch(client: *Client, options: FetchOptions) FetchError!FetchResult {
16711671 const decompress_buffer: []u8 = switch (response.head.content_encoding) {
16721672 .identity => &.{},
16731673 .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),
16751675 else => options.decompress_buffer orelse try client.allocator.alloc(u8, 8 * 1024),
16761676 };
16771677 defer if (options.decompress_buffer == null) client.allocator.free(decompress_buffer);
lib/std/io.zig+5-20
......@@ -1,16 +1,11 @@
1const std = @import("std.zig");
21const builtin = @import("builtin");
3const root = @import("root");
4const c = std.c;
52const is_windows = builtin.os.tag == .windows;
3
4const std = @import("std.zig");
65const windows = std.os.windows;
76const posix = std.posix;
87const math = std.math;
98const assert = std.debug.assert;
10const fs = std.fs;
11const mem = std.mem;
12const meta = std.meta;
13const File = std.fs.File;
149const Allocator = std.mem.Allocator;
1510const Alignment = std.mem.Alignment;
1611
......@@ -21,12 +16,6 @@ pub const BufferedReader = @import("io/BufferedReader.zig");
2116pub const BufferedWriter = @import("io/BufferedWriter.zig");
2217pub 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
3019pub const MultiWriter = @import("io/multi_writer.zig").MultiWriter;
3120pub const multiWriter = @import("io/multi_writer.zig").multiWriter;
3221
......@@ -38,9 +27,6 @@ pub const bitWriter = @import("io/bit_writer.zig").bitWriter;
3827pub const ChangeDetectionStream = @import("io/change_detection_stream.zig").ChangeDetectionStream;
3928pub 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
4430pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAtomicFile;
4531
4632pub const tty = @import("io/tty.zig");
......@@ -63,7 +49,7 @@ pub fn poll(
6349 .windows = if (is_windows) .{
6450 .first_read_done = false,
6551 .overlapped = [1]windows.OVERLAPPED{
66 mem.zeroes(windows.OVERLAPPED),
52 std.mem.zeroes(windows.OVERLAPPED),
6753 } ** enum_fields.len,
6854 .small_bufs = undefined,
6955 .active = .{
......@@ -436,10 +422,10 @@ pub fn PollFiles(comptime StreamEnum: type) type {
436422 for (&struct_fields, enum_fields) |*struct_field, enum_field| {
437423 struct_field.* = .{
438424 .name = enum_field.name,
439 .type = fs.File,
425 .type = std.fs.File,
440426 .default_value_ptr = null,
441427 .is_comptime = false,
442 .alignment = @alignOf(fs.File),
428 .alignment = @alignOf(std.fs.File),
443429 };
444430 }
445431 return @Type(.{ .@"struct" = .{
......@@ -459,6 +445,5 @@ test {
459445 _ = @import("io/bit_reader.zig");
460446 _ = @import("io/bit_writer.zig");
461447 _ = @import("io/buffered_atomic_file.zig");
462 _ = @import("io/c_writer.zig");
463448 _ = @import("io/test.zig");
464449}
lib/std/io/Reader.zig+9
......@@ -6,6 +6,8 @@ const BufferedReader = std.io.BufferedReader;
66const Allocator = std.mem.Allocator;
77const ArrayList = std.ArrayListUnmanaged;
88
9pub const Limited = @import("Reader/Limited.zig");
10
911context: ?*anyopaque,
1012vtable: *const VTable,
1113
......@@ -252,6 +254,13 @@ pub fn buffered(r: Reader, buffer: []u8) BufferedReader {
252254 };
253255}
254256
257pub fn limited(r: Reader, limit: Limit) Limited {
258 return .{
259 .unlimited_reader = r,
260 .remaining = limit,
261 };
262}
263
255264fn endingRead(context: ?*anyopaque, bw: *BufferedWriter, limit: Limit) RwError!usize {
256265 _ = context;
257266 _ = 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) {
9595};
9696
9797pub fn writeVec(w: Writer, data: []const []const u8) Error!usize {
98 assert(data.len > 0);
9899 return w.vtable.writeSplat(w.context, data, 1);
99100}
100101
101102pub fn writeSplat(w: Writer, data: []const []const u8, splat: usize) Error!usize {
103 assert(data.len > 0);
102104 return w.vtable.writeSplat(w.context, data, splat);
103105}
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 {
19191919 limit: std.io.Reader.Limit,
19201920 ) std.io.Reader.Error!usize {
19211921 const buf = limit.slice(try bw.writableSliceGreedy(1));
1922 const status = try readVec(context, &.{buf});
1923 bw.advance(status.len);
1924 return status;
1922 const n = try readVec(context, &.{buf});
1923 bw.advance(n);
1924 return n;
19251925 }
19261926
19271927 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 {
154154pub fn decompress(
155155 method: CompressionMethod,
156156 uncompressed_size: u64,
157 reader: anytype,
158 writer: anytype,
157 reader: *std.io.BufferedReader,
158 writer: *std.io.BufferedWriter,
159 compressed_remaining: *u64,
159160) !u32 {
160161 var hash = std.hash.Crc32.init();
161
162162 var total_uncompressed: u64 = 0;
163163 switch (method) {
164164 .store => {
165 var buf: [4096]u8 = undefined;
166 while (true) {
167 const len = try reader.read(&buf);
168 if (len == 0) break;
169 try writer.writeAll(buf[0..len]);
170 hash.update(buf[0..len]);
171 total_uncompressed += @intCast(len);
172 }
165 reader.writeAll(writer, .limited(compressed_remaining.*)) catch |err| switch (err) {
166 error.EndOfStream => return error.ZipDecompressTruncated,
167 else => |e| return e,
168 };
169 total_uncompressed += compressed_remaining.*;
173170 },
174171 .deflate => {
175 var br = std.io.bufferedReader(reader);
176 var decompressor = std.compress.flate.decompressor(br.reader());
172 var decompressor: std.compress.flate.Decompressor = .init(reader);
177173 while (try decompressor.next()) |chunk| {
178174 try writer.writeAll(chunk);
179175 hash.update(chunk);
180176 total_uncompressed += @intCast(chunk.len);
181177 if (total_uncompressed > uncompressed_size)
182178 return error.ZipUncompressSizeTooSmall;
179 compressed_remaining.* -= chunk.len;
183180 }
184 if (br.end != br.start)
185 return error.ZipDeflateTruncated;
186181 },
187182 _ => return error.UnsupportedCompressionMethod,
188183 }
......@@ -552,15 +547,15 @@ pub fn Iterator(comptime SeekableStream: type) type {
552547 @as(u64, @sizeOf(LocalFileHeader)) +
553548 local_data_header_offset;
554549 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;
556551 const crc = try decompress(
557552 self.compression_method,
558553 self.uncompressed_size,
559 limited_reader.reader(),
554 stream.context.reader(),
560555 out_file.writer(),
556 &compressed_remaining,
561557 );
562 if (limited_reader.bytes_left != 0)
563 return error.ZipDecompressTruncated;
558 if (compressed_remaining != 0) return error.ZipDecompressTruncated;
564559 return crc;
565560 }
566561 };
src/Package/Fetch.zig+1-1
......@@ -1199,7 +1199,7 @@ fn unpackResource(
11991199 return try unpackTarball(f, tmp_directory.handle, dcp.reader());
12001200 },
12011201 .@"tar.zst" => {
1202 const window_size = std.compress.zstd.DecompressorOptions.default_window_buffer_len;
1202 const window_size = std.compress.zstd.default_window_len;
12031203 const window_buffer = try f.arena.allocator().create([window_size]u8);
12041204 const reader = resource.reader();
12051205 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 {
14901490///
14911491/// The format of the delta data is documented in
14921492/// [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 {
14941494 while (true) {
14951495 const inst: packed struct { value: u7, copy: bool } = @bitCast(delta_reader.readByte() catch |e| switch (e) {
14961496 error.EndOfStream => return,
......@@ -1521,13 +1521,11 @@ fn expandDelta(base_object: anytype, delta_reader: anytype, writer: anytype) !vo
15211521 var size: u24 = @bitCast(size_parts);
15221522 if (size == 0) size = 0x10000;
15231523 try base_object.seekTo(offset);
1524 var copy_reader = std.io.limitedReader(base_object.reader(), size);
1525 var fifo = std.fifo.LinearFifo(u8, .{ .Static = 4096 }).init();
1526 try fifo.pump(copy_reader.reader(), writer);
1524
1525 var base_object_br = base_object.reader();
1526 try base_object_br.readAll(writer, .limited(size));
15271527 } else if (inst.value != 0) {
1528 var data_reader = std.io.limitedReader(delta_reader, inst.value);
1529 var fifo = std.fifo.LinearFifo(u8, .{ .Static = 4096 }).init();
1530 try fifo.pump(data_reader.reader(), writer);
1528 try delta_reader.readAll(writer, .limited(inst.value));
15311529 } else {
15321530 return error.InvalidDeltaInstruction;
15331531 }