authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-05-26 20:31:35-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:29-07:00
logda303bdaf1ae8717df2d4ede9e7dfb215636ae33
tree29c02e46d0b180e595cdf636c6e0ebc44a3c1271
parentfd4fb10722beea90ee3a1c9314a10eec36a44346

std: fix a bunch of compilation errors


25 files changed, 1011 insertions(+), 840 deletions(-)

lib/std/coff.zig+12-14
......@@ -1087,16 +1087,14 @@ pub const Coff = struct {
10871087 const pe_pointer_offset = 0x3C;
10881088 const pe_magic = "PE\x00\x00";
10891089
1090 var stream = std.io.fixedBufferStream(data);
1091 const reader = stream.reader();
1092 try stream.seekTo(pe_pointer_offset);
1090 var reader: std.io.BufferedReader = undefined;
1091 reader.initFixed(data[pe_pointer_offset..]);
10931092 const coff_header_offset = try reader.readInt(u32, .little);
1094 try stream.seekTo(coff_header_offset);
1095 var buf: [4]u8 = undefined;
1096 try reader.readNoEof(&buf);
1097 const is_image = mem.eql(u8, pe_magic, &buf);
1093 reader.initFixed(data[coff_header_offset..]);
1094 const magic = try reader.peek(4);
1095 const is_image = mem.eql(u8, pe_magic, magic);
10981096
1099 var coff = @This(){
1097 var coff: Coff = .{
11001098 .data = data,
11011099 .is_image = is_image,
11021100 .is_loaded = is_loaded,
......@@ -1123,16 +1121,16 @@ pub const Coff = struct {
11231121 if (@intFromEnum(DirectoryEntry.DEBUG) >= data_dirs.len) return null;
11241122
11251123 const debug_dir = data_dirs[@intFromEnum(DirectoryEntry.DEBUG)];
1126 var stream = std.io.fixedBufferStream(self.data);
1127 const reader = stream.reader();
1124 var reader: std.io.BufferedReader = undefined;
1125 reader.initFixed(self.data);
11281126
11291127 if (self.is_loaded) {
1130 try stream.seekTo(debug_dir.virtual_address);
1128 reader.initFixed(self.data[debug_dir.virtual_address..]);
11311129 } else {
11321130 // Find what section the debug_dir is in, in order to convert the RVA to a file offset
11331131 for (self.getSectionHeaders()) |*sect| {
11341132 if (debug_dir.virtual_address >= sect.virtual_address and debug_dir.virtual_address < sect.virtual_address + sect.virtual_size) {
1135 try stream.seekTo(sect.pointer_to_raw_data + (debug_dir.virtual_address - sect.virtual_address));
1133 reader.initFixed(self.data[sect.pointer_to_raw_data + (debug_dir.virtual_address - sect.virtual_address) ..]);
11361134 break;
11371135 }
11381136 } else return error.InvalidDebugDirectory;
......@@ -1143,10 +1141,10 @@ pub const Coff = struct {
11431141 const debug_dir_entry_count = debug_dir.size / @sizeOf(DebugDirectoryEntry);
11441142 var i: u32 = 0;
11451143 while (i < debug_dir_entry_count) : (i += 1) {
1146 const debug_dir_entry = try reader.readStruct(DebugDirectoryEntry);
1144 const debug_dir_entry = try reader.takeStruct(DebugDirectoryEntry);
11471145 if (debug_dir_entry.type == .CODEVIEW) {
11481146 const dir_offset = if (self.is_loaded) debug_dir_entry.address_of_raw_data else debug_dir_entry.pointer_to_raw_data;
1149 try stream.seekTo(dir_offset);
1147 reader.initFixed(self.data[dir_offset..]);
11501148 break;
11511149 }
11521150 } else return null;
lib/std/compress/flate.zig+11-67
......@@ -68,81 +68,23 @@ pub const Container = enum {
6868 //
6969 // CINFO = 7, CM = 8, FLEVEL = 0b10, FDICT = 0, FCHECK = 0b11100
7070 .zlib => &[_]u8{ 0x78, 0b10_0_11100 },
71 .raw => &{},
71 .raw => &.{},
7272 };
7373 }
7474
75 pub fn parseHeader(comptime wrap: Container, reader: *std.io.BufferedReader) !void {
76 switch (wrap) {
77 .gzip => try parseGzipHeader(reader),
78 .zlib => try parseZlibHeader(reader),
79 .raw => {},
80 }
81 }
82
83 fn parseGzipHeader(reader: *std.io.BufferedReader) !void {
84 const magic1 = try reader.read(u8);
85 const magic2 = try reader.read(u8);
86 const method = try reader.read(u8);
87 const flags = try reader.read(u8);
88 try reader.skipBytes(6); // mtime(4), xflags, os
89 if (magic1 != 0x1f or magic2 != 0x8b or method != 0x08)
90 return error.BadGzipHeader;
91 // Flags description: https://www.rfc-editor.org/rfc/rfc1952.html#page-5
92 if (flags != 0) {
93 if (flags & 0b0000_0100 != 0) { // FEXTRA
94 const extra_len = try reader.read(u16);
95 try reader.skipBytes(extra_len);
96 }
97 if (flags & 0b0000_1000 != 0) { // FNAME
98 try reader.skipStringZ();
99 }
100 if (flags & 0b0001_0000 != 0) { // FCOMMENT
101 try reader.skipStringZ();
102 }
103 if (flags & 0b0000_0010 != 0) { // FHCRC
104 try reader.skipBytes(2);
105 }
106 }
107 }
108
109 fn parseZlibHeader(reader: *std.io.BufferedReader) !void {
110 const cm = try reader.read(u4);
111 const cinfo = try reader.read(u4);
112 _ = try reader.read(u8);
113 if (cm != 8 or cinfo > 7) {
114 return error.BadZlibHeader;
115 }
116 }
117
118 pub fn parseFooter(comptime wrap: Container, hasher: *Hasher(wrap), reader: *std.io.BufferedReader) !void {
119 switch (wrap) {
120 .gzip => {
121 try reader.fill(0);
122 if (try reader.read(u32) != hasher.chksum()) return error.WrongGzipChecksum;
123 if (try reader.read(u32) != hasher.bytesRead()) return error.WrongGzipSize;
124 },
125 .zlib => {
126 const chksum: u32 = @byteSwap(hasher.chksum());
127 if (try reader.read(u32) != chksum) return error.WrongZlibChecksum;
128 },
129 .raw => {},
130 }
131 }
132
13375 pub const Hasher = union(Container) {
76 raw: void,
13477 gzip: struct {
13578 crc: std.hash.Crc32 = .init(),
13679 count: usize = 0,
13780 },
13881 zlib: std.hash.Adler32,
139 raw: void,
14082
14183 pub fn init(containter: Container) Hasher {
14284 return switch (containter) {
14385 .gzip => .{ .gzip = .{} },
14486 .zlib => .{ .zlib = .init() },
145 .raw => {},
87 .raw => .raw,
14688 };
14789 }
14890
......@@ -288,15 +230,18 @@ test "compress/decompress" {
288230 // compress original stream to compressed stream
289231 {
290232 var original: std.io.BufferedReader = undefined;
291 original.initFixed(data);
233 original.initFixed(@constCast(data));
292234 var compressed: std.io.BufferedWriter = undefined;
293235 compressed.initFixed(&cmp_buf);
294 try Compress.pump(container, original.reader(), &compressed, .{ .level = level });
236 var compress: Compress = .init(&original, .raw);
237 var compress_br = compress.readable(&.{});
238 const n = try compress_br.readRemaining(&compressed, .{ .level = level });
295239 if (compressed_size == 0) {
296240 if (container == .gzip)
297241 print("case {d} gzip level {} compressed size: {d}\n", .{ case_no, level, compressed.pos });
298242 compressed_size = compressed.pos;
299243 }
244 try testing.expectEqual(compressed_size, n);
300245 try testing.expectEqual(compressed_size, compressed.pos);
301246 }
302247 // decompress compressed stream to decompressed stream
......@@ -688,9 +633,7 @@ pub const match = struct {
688633 pub const max_distance = 32768;
689634};
690635
691pub const history = struct {
692 pub const len = match.max_distance;
693};
636pub const history_len = match.max_distance;
694637
695638pub const lookup = struct {
696639 pub const bits = 15;
......@@ -707,7 +650,8 @@ test "zlib should not overshoot" {
707650 0x03, 0x00, 0x8b, 0x61, 0x0f, 0xa4, 0x52, 0x5a, 0x94, 0x12,
708651 };
709652
710 var stream = std.io.fixedBufferStream(data[0..]);
653 var stream: std.io.BufferedReader = undefined;
654 stream.initFixed(&data);
711655 const reader = stream.reader();
712656
713657 var dcp = Decompress.init(reader);
lib/std/compress/flate/Compress.zig+203-130
......@@ -51,9 +51,37 @@ const math = std.math;
5151const Compress = @This();
5252const Token = @import("Token.zig");
5353const BlockWriter = @import("BlockWriter.zig");
54const Container = std.compress.flate.Container;
54const flate = @import("../flate.zig");
55const Container = flate.Container;
5556const Lookup = @import("Lookup.zig");
56const huffman = std.compress.flate.huffman;
57const huffman = flate.huffman;
58
59lookup: Lookup = .{},
60tokens: Tokens = .{},
61/// Asserted to have a buffer capacity of at least `flate.max_window_len`.
62input: *std.io.BufferedReader,
63block_writer: BlockWriter,
64level: LevelArgs,
65hasher: Container.Hasher,
66
67// Match and literal at the previous position.
68// Used for lazy match finding in processWindow.
69prev_match: ?Token = null,
70prev_literal: ?u8 = null,
71
72pub fn readable(c: *Compress, buffer: []u8) std.io.BufferedReader {
73 return .{
74 .unbuffered_reader = .{
75 .context = c,
76 .vtable = .{
77 .read = read,
78 .readVec = readVec,
79 .discard = discard,
80 },
81 },
82 .buffer = buffer,
83 };
84}
5785
5886pub const Options = struct {
5987 level: Level = .default,
......@@ -77,10 +105,10 @@ pub const Level = enum(u4) {
77105 best = 0xd,
78106};
79107
80// Number of tokens to accumulate in deflate before starting block encoding.
81//
82// In zlib this depends on memlevel: 6 + memlevel, where default memlevel is
83// 8 and max 9 that gives 14 or 15 bits.
108/// Number of tokens to accumulate in deflate before starting block encoding.
109///
110/// In zlib this depends on memlevel: 6 + memlevel, where default memlevel is
111/// 8 and max 9 that gives 14 or 15 bits.
84112pub const n_tokens = 1 << 15;
85113
86114/// Algorithm knobs for each level.
......@@ -102,85 +130,60 @@ const LevelArgs = struct {
102130 }
103131};
104132
105lookup: Lookup = .{},
106tokens: Tokens = .{},
107output: *std.io.BufferedWriter,
108block_writer: BlockWriter,
109level: LevelArgs,
110hasher: Container.Hasher,
111
112// Match and literal at the previous position.
113// Used for lazy match finding in processWindow.
114prev_match: ?Token = null,
115prev_literal: ?u8 = null,
116
117pub fn init(output: *std.io.BufferedWriter, options: Options) std.io.Writer.Error!Compress {
118 try output.writeAll(options.container.header(output));
133pub fn init(input: *std.io.BufferedReader, options: Options) Compress {
119134 return .{
120 .output = output,
121 .block_writer = .init(output),
135 .input = input,
136 .block_writer = undefined,
122137 .level = .get(options.level),
123138 .hasher = .init(options.container),
139 .state = .header,
124140 };
125141}
126142
127143const FlushOption = enum { none, flush, final };
128144
129// Process data in window and create tokens. If token buffer is full
130// flush tokens to the token writer. In the case of `flush` or `final`
131// option it will process all data from the window. In the `none` case
132// it will preserve some data for the next match.
133fn tokenize(self: *Compress, flush_opt: FlushOption) !void {
134 // flush - process all data from window
135 const should_flush = (flush_opt != .none);
136
137 // While there is data in active lookahead buffer.
138 while (self.win.activeLookahead(should_flush)) |lh| {
139 var step: u16 = 1; // 1 in the case of literal, match length otherwise
140 const pos: u16 = self.win.pos();
141 const literal = lh[0]; // literal at current position
142 const min_len: u16 = if (self.prev_match) |m| m.length() else 0;
143
144 // Try to find match at least min_len long.
145 if (self.findMatch(pos, lh, min_len)) |match| {
146 // Found better match than previous.
147 try self.addPrevLiteral();
148
149 // Is found match length good enough?
150 if (match.length() >= self.level.lazy) {
151 // Don't try to lazy find better match, use this.
152 step = try self.addMatch(match);
153 } else {
154 // Store this match.
155 self.prev_literal = literal;
156 self.prev_match = match;
157 }
145/// Process data in window and create tokens. If token buffer is full
146/// flush tokens to the token writer.
147///
148/// Returns number of bytes consumed from `lh`.
149fn tokenizeSlice(c: *Compress, bw: *std.io.BufferedWriter, limit: std.io.Limit, lh: []const u8) !usize {
150 _ = bw;
151 _ = limit;
152 if (true) @panic("TODO");
153 var step: u16 = 1; // 1 in the case of literal, match length otherwise
154 const pos: u16 = c.win.pos();
155 const literal = lh[0]; // literal at current position
156 const min_len: u16 = if (c.prev_match) |m| m.length() else 0;
157
158 // Try to find match at least min_len long.
159 if (c.findMatch(pos, lh, min_len)) |match| {
160 // Found better match than previous.
161 try c.addPrevLiteral();
162
163 // Is found match length good enough?
164 if (match.length() >= c.level.lazy) {
165 // Don't try to lazy find better match, use this.
166 step = try c.addMatch(match);
158167 } else {
159 // There is no better match at current pos then it was previous.
160 // Write previous match or literal.
161 if (self.prev_match) |m| {
162 // Write match from previous position.
163 step = try self.addMatch(m) - 1; // we already advanced 1 from previous position
164 } else {
165 // No match at previous position.
166 // Write previous literal if any, and remember this literal.
167 try self.addPrevLiteral();
168 self.prev_literal = literal;
169 }
168 // Store this match.
169 c.prev_literal = literal;
170 c.prev_match = match;
171 }
172 } else {
173 // There is no better match at current pos then it was previous.
174 // Write previous match or literal.
175 if (c.prev_match) |m| {
176 // Write match from previous position.
177 step = try c.addMatch(m) - 1; // we already advanced 1 from previous position
178 } else {
179 // No match at previous position.
180 // Write previous literal if any, and remember this literal.
181 try c.addPrevLiteral();
182 c.prev_literal = literal;
170183 }
171 // Advance window and add hashes.
172 self.windowAdvance(step, lh, pos);
173 }
174
175 if (should_flush) {
176 // In the case of flushing, last few lookahead buffers were smaller then min match len.
177 // So only last literal can be unwritten.
178 assert(self.prev_match == null);
179 try self.addPrevLiteral();
180 self.prev_literal = null;
181
182 try self.flushTokens(flush_opt);
183184 }
185 // Advance window and add hashes.
186 c.windowAdvance(step, lh, pos);
184187}
185188
186189fn windowAdvance(self: *Compress, step: u16, lh: []const u8, pos: u16) void {
......@@ -226,7 +229,7 @@ fn findMatch(self: *Compress, pos: u16, lh: []const u8, min_len: u16) ?Token {
226229 // Hot path loop!
227230 while (prev_pos > 0 and chain > 0) : (chain -= 1) {
228231 const distance = pos - prev_pos;
229 if (distance > std.compress.flate.match.max_distance)
232 if (distance > flate.match.max_distance)
230233 break;
231234
232235 const new_len = self.win.match(prev_pos, pos, len);
......@@ -272,33 +275,6 @@ fn slide(self: *Compress) void {
272275 self.lookup.slide(n);
273276}
274277
275/// Compresses as much data as possible, stops when the reader becomes
276/// empty. It will introduce some output latency (reading input without
277/// producing all output) because some data are still in internal
278/// buffers.
279///
280/// It is up to the caller to call flush (if needed) or finish (required)
281/// when is need to output any pending data or complete stream.
282///
283pub fn compress(self: *Compress, reader: anytype) !void {
284 while (true) {
285 // Fill window from reader
286 const buf = self.win.writable();
287 if (buf.len == 0) {
288 try self.tokenize(.none);
289 self.slide();
290 continue;
291 }
292 const n = try reader.readAll(buf);
293 self.hasher.update(buf[0..n]);
294 self.win.written(n);
295 // Process window
296 try self.tokenize(.none);
297 // Exit when no more data in reader
298 if (n < buf.len) break;
299 }
300}
301
302278/// Flushes internal buffers to the output writer. Outputs empty stored
303279/// block to sync bit stream to the byte boundary, so that the
304280/// decompressor can get all input data available so far.
......@@ -311,8 +287,8 @@ pub fn compress(self: *Compress, reader: anytype) !void {
311287/// stored block that is three zero bits plus filler bits to the next
312288/// byte, followed by four bytes (00 00 ff ff).
313289///
314pub fn flush(self: *Compress) !void {
315 try self.tokenize(.flush);
290pub fn flush(c: *Compress) !void {
291 try c.tokenize(.flush);
316292}
317293
318294/// Completes deflate bit stream by writing any pending data as deflate
......@@ -320,9 +296,9 @@ pub fn flush(self: *Compress) !void {
320296/// the compressor as a signal that next block has to have final bit
321297/// set.
322298///
323pub fn finish(self: *Compress) !void {
324 try self.tokenize(.final);
325 try self.hasher.writeFooter(self.output);
299pub fn finish(c: *Compress) !void {
300 _ = c;
301 @panic("TODO");
326302}
327303
328304/// Use another writer while preserving history. Most probably flush
......@@ -437,24 +413,6 @@ fn SimpleCompressor(
437413 }
438414 self.wp = 0;
439415 }
440
441 // Writes all data from the input reader of uncompressed data.
442 // It is up to the caller to call flush or finish if there is need to
443 // output compressed blocks.
444 pub fn compress(self: *Self, reader: anytype) !void {
445 while (true) {
446 // read from rdr into buffer
447 const buf = self.buffer[self.wp..];
448 if (buf.len == 0) {
449 try self.flushBuffer(false);
450 continue;
451 }
452 const n = try reader.readAll(buf);
453 self.hasher.update(buf[0..n]);
454 self.wp += n;
455 if (n < buf.len) break; // no more data in reader
456 }
457 }
458416 };
459417}
460418
......@@ -811,6 +769,119 @@ fn byFreq(context: void, a: LiteralNode, b: LiteralNode) bool {
811769 return a.freq < b.freq;
812770}
813771
772fn read(
773 context: ?*anyopaque,
774 bw: *std.io.BufferedWriter,
775 limit: std.io.Reader.Limit,
776) std.io.Reader.RwError!usize {
777 const c: *Compress = @ptrCast(@alignCast(context));
778 switch (c.state) {
779 .header => |i| {
780 const header = c.hasher.container().header();
781 const n = try bw.write(header[i..]);
782 if (header.len - i - n == 0) {
783 c.state = .middle;
784 } else {
785 c.state.header += n;
786 }
787 return n;
788 },
789 .middle => {
790 c.input.fillMore() catch |err| switch (err) {
791 error.EndOfStream => {
792 c.state = .final;
793 return 0;
794 },
795 else => |e| return e,
796 };
797 const buffer_contents = c.input.bufferContents();
798 const min_lookahead = flate.match.min_length + flate.match.max_length;
799 const history_plus_lookahead_len = flate.history_len + min_lookahead;
800 if (buffer_contents.len < history_plus_lookahead_len) return 0;
801 const lookahead = buffer_contents[flate.history_len..];
802 const start = bw.count;
803 const n = try c.tokenizeSlice(bw, limit, lookahead) catch |err| switch (err) {
804 error.WriteFailed => return error.WriteFailed,
805 };
806 c.hasher.update(lookahead[0..n]);
807 c.input.toss(n);
808 return bw.count - start;
809 },
810 .final => {
811 const buffer_contents = c.input.bufferContents();
812 const start = bw.count;
813 const n = c.tokenizeSlice(bw, limit, buffer_contents) catch |err| switch (err) {
814 error.WriteFailed => return error.WriteFailed,
815 };
816 if (buffer_contents.len - n == 0) {
817 c.hasher.update(buffer_contents);
818 c.input.tossAll();
819 {
820 // In the case of flushing, last few lookahead buffers were
821 // smaller than min match len, so only last literal can be
822 // unwritten.
823 assert(c.prev_match == null);
824 try c.addPrevLiteral();
825 c.prev_literal = null;
826
827 try c.flushTokens(.final);
828 }
829 switch (c.hasher) {
830 .gzip => |*gzip| {
831 // GZIP 8 bytes footer
832 // - 4 bytes, CRC32 (CRC-32)
833 // - 4 bytes, ISIZE (Input SIZE) - size of the original (uncompressed) input data modulo 2^32
834 comptime assert(c.footer_buffer.len == 8);
835 std.mem.writeInt(u32, c.footer_buffer[0..4], gzip.final(), .little);
836 std.mem.writeInt(u32, c.footer_buffer[4..8], gzip.bytes_read, .little);
837 c.state = .{ .footer = 0 };
838 },
839 .zlib => |*zlib| {
840 // ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952).
841 // 4 bytes of ADLER32 (Adler-32 checksum)
842 // Checksum value of the uncompressed data (excluding any
843 // dictionary data) computed according to Adler-32
844 // algorithm.
845 comptime assert(c.footer_buffer.len == 8);
846 std.mem.writeInt(u32, c.footer_buffer[4..8], zlib.final, .big);
847 c.state = .{ .footer = 4 };
848 },
849 .raw => {
850 c.state = .ended;
851 },
852 }
853 }
854 return bw.count - start;
855 },
856 .ended => return error.EndOfStream,
857 .footer => |i| {
858 const remaining = c.footer_buffer[i..];
859 const n = try bw.write(limit.slice(remaining));
860 c.state = if (n == remaining) .ended else .{ .footer = i - n };
861 return n;
862 },
863 }
864}
865
866fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
867 var bw: std.io.BufferedWriter = undefined;
868 bw.initVec(data);
869 return read(context, &bw, .countVec(data)) catch |err| switch (err) {
870 error.WriteFailed => unreachable, // Prevented by the limit.
871 else => |e| return e,
872 };
873}
874
875fn discard(context: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize {
876 var trash_buffer: [64]u8 = undefined;
877 var null_writer: std.io.Writer.Null = undefined;
878 var bw = null_writer.writer().buffered(&trash_buffer);
879 return read(context, &bw, limit) catch |err| switch (err) {
880 error.WriteFailed => unreachable,
881 else => |e| return e,
882 };
883}
884
814885test "generate a Huffman code from an array of frequencies" {
815886 var freqs: [19]u16 = [_]u16{
816887 8, // 0
......@@ -1099,7 +1170,8 @@ test "file tokenization" {
10991170 const data = case.data;
11001171
11011172 for (levels, 0..) |level, i| { // for each compression level
1102 var original = io.fixedBufferStream(data);
1173 var original: std.io.BufferedReader = undefined;
1174 original.initFixed(data);
11031175
11041176 // buffer for decompressed data
11051177 var al = std.ArrayList(u8).init(testing.allocator);
......@@ -1173,21 +1245,22 @@ test "store simple compressor" {
11731245 //0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21,
11741246 };
11751247
1176 var fbs = std.io.fixedBufferStream(data);
1248 var fbs: std.io.BufferedReader = undefined;
1249 fbs.initFixed(data);
11771250 var al = std.ArrayList(u8).init(testing.allocator);
11781251 defer al.deinit();
11791252
11801253 var cmp = try store.compressor(.raw, al.writer());
1181 try cmp.compress(fbs.reader());
1254 try cmp.compress(&fbs);
11821255 try cmp.finish();
11831256 try testing.expectEqualSlices(u8, &expected, al.items);
11841257
1185 fbs.reset();
1258 fbs.initFixed(data);
11861259 try al.resize(0);
11871260
11881261 // huffman only compresoor will also emit store block for this small sample
11891262 var hc = try huffman.compressor(.raw, al.writer());
1190 try hc.compress(fbs.reader());
1263 try hc.compress(&fbs);
11911264 try hc.finish();
11921265 try testing.expectEqualSlices(u8, &expected, al.items);
11931266}
lib/std/compress/flate/Decompress.zig+329-273
......@@ -22,32 +22,38 @@ const flate = std.compress.flate;
2222const Container = flate.Container;
2323const Token = @import("Token.zig");
2424const testing = std.testing;
25const Decompress = @This();
2526
2627input: *std.io.BufferedReader,
2728// Hashes, produces checksum, of uncompressed data for gzip/zlib footer.
28hasher: Container.Hasher(),
29hasher: Container.Hasher,
2930
3031// dynamic block huffman code decoders
3132lit_dec: LiteralDecoder,
3233dst_dec: DistanceDecoder,
3334
3435// current read state
35bfinal: u1,
36block_type: u2,
37state: ReadState,
36final_block: bool,
37state: State,
38
39read_err: ?Error,
3840
39read_err: Error!void,
41const BlockType = enum(u2) {
42 stored = 0,
43 fixed = 1,
44 dynamic = 2,
45};
4046
41const ReadState = enum {
47const State = union(enum) {
4248 protocol_header,
4349 block_header,
44 block,
50 stored_block: u16,
51 fixed_block,
52 dynamic_block,
4553 protocol_footer,
4654 end,
4755};
4856
49const Decompress = @This();
50
5157pub const Error = Container.Error || error{
5258 InvalidCode,
5359 InvalidMatch,
......@@ -61,71 +67,25 @@ pub const Error = Container.Error || error{
6167 MissingEndOfBlockCode,
6268};
6369
64pub fn init(input: *std.io.BufferedReader) Decompress {
70pub fn init(input: *std.io.BufferedReader, container: Container) Decompress {
6571 return .{
6672 .input = input,
67 .hasher = .{},
73 .hasher = .init(container),
6874 .lit_dec = .{},
6975 .dst_dec = .{},
70 .bfinal = 0,
71 .block_type = 0b11,
76 .final_block = false,
7277 .state = .protocol_header,
73 .read_err = {},
78 .read_err = null,
7479 };
7580}
7681
77fn blockHeader(self: *Decompress) Error!void {
78 self.bfinal = try self.bits.read(u1);
79 self.block_type = try self.bits.read(u2);
80}
81
82fn storedBlock(self: *Decompress) !bool {
83 self.bits.alignToByte(); // skip padding until byte boundary
84 // everything after this is byte aligned in stored block
85 var len = try self.bits.read(u16);
86 const nlen = try self.bits.read(u16);
87 if (len != ~nlen) return error.WrongStoredBlockNlen;
88
89 while (len > 0) {
90 const buf = self.hist.getWritable(len);
91 try self.bits.readAll(buf);
92 len -= @intCast(buf.len);
93 }
94 return true;
95}
96
97fn fixedBlock(self: *Decompress) !bool {
98 while (!self.hist.full()) {
99 const code = try self.bits.readFixedCode();
100 switch (code) {
101 0...255 => self.hist.write(@intCast(code)),
102 256 => return true, // end of block
103 257...285 => try self.fixedDistanceCode(@intCast(code - 257)),
104 else => return error.InvalidCode,
105 }
106 }
107 return false;
108}
109
110// Handles fixed block non literal (length) code.
111// Length code is followed by 5 bits of distance code.
112fn fixedDistanceCode(self: *Decompress, code: u8) !void {
113 try self.bits.fill(5 + 5 + 13);
114 const length = try self.decodeLength(code);
115 const distance = try self.decodeDistance(try self.bits.readF(u5, .{
116 .buffered = true,
117 .reverse = true,
118 }));
119 try self.hist.writeMatch(length, distance);
120}
121
12282fn decodeLength(self: *Decompress, code: u8) !u16 {
12383 if (code > 28) return error.InvalidCode;
12484 const ml = Token.matchLength(code);
12585 return if (ml.extra_bits == 0) // 0 - 5 extra bits
12686 ml.base
12787 else
128 ml.base + try self.bits.readN(ml.extra_bits, .{ .buffered = true });
88 ml.base + try self.takeNBitsBuffered(ml.extra_bits);
12989}
13090
13191fn decodeDistance(self: *Decompress, code: u8) !u16 {
......@@ -134,42 +94,7 @@ fn decodeDistance(self: *Decompress, code: u8) !u16 {
13494 return if (md.extra_bits == 0) // 0 - 13 extra bits
13595 md.base
13696 else
137 md.base + try self.bits.readN(md.extra_bits, .{ .buffered = true });
138}
139
140fn dynamicBlockHeader(self: *Decompress) !void {
141 const hlit: u16 = @as(u16, try self.bits.read(u5)) + 257; // number of ll code entries present - 257
142 const hdist: u16 = @as(u16, try self.bits.read(u5)) + 1; // number of distance code entries - 1
143 const hclen: u8 = @as(u8, try self.bits.read(u4)) + 4; // hclen + 4 code lengths are encoded
144
145 if (hlit > 286 or hdist > 30)
146 return error.InvalidDynamicBlockHeader;
147
148 // lengths for code lengths
149 var cl_lens = [_]u4{0} ** 19;
150 for (0..hclen) |i| {
151 cl_lens[flate.huffman.codegen_order[i]] = try self.bits.read(u3);
152 }
153 var cl_dec: CodegenDecoder = .{};
154 try cl_dec.generate(&cl_lens);
155
156 // decoded code lengths
157 var dec_lens = [_]u4{0} ** (286 + 30);
158 var pos: usize = 0;
159 while (pos < hlit + hdist) {
160 const sym = try cl_dec.find(try self.bits.peekF(u7, .{ .reverse = true }));
161 try self.bits.shift(sym.code_bits);
162 pos += try self.dynamicCodeLength(sym.symbol, &dec_lens, pos);
163 }
164 if (pos > hlit + hdist) {
165 return error.InvalidDynamicBlockHeader;
166 }
167
168 // literal code lengths to literal decoder
169 try self.lit_dec.generate(dec_lens[0..hlit]);
170
171 // distance code lengths to distance decoder
172 try self.dst_dec.generate(dec_lens[hlit .. hlit + hdist]);
97 md.base + try self.takeNBitsBuffered(md.extra_bits);
17398}
17499
175100// Decode code length symbol to code length. Writes decoded length into
......@@ -188,7 +113,7 @@ fn dynamicCodeLength(self: *Decompress, code: u16, lens: []u4, pos: usize) !usiz
188113 16 => {
189114 // Copy the previous code length 3 - 6 times.
190115 // The next 2 bits indicate repeat length
191 const n: u8 = @as(u8, try self.bits.read(u2)) + 3;
116 const n: u8 = @as(u8, try self.takeBits(u2)) + 3;
192117 if (pos == 0 or pos + n > lens.len)
193118 return error.InvalidDynamicBlockHeader;
194119 for (0..n) |i| {
......@@ -197,194 +122,301 @@ fn dynamicCodeLength(self: *Decompress, code: u16, lens: []u4, pos: usize) !usiz
197122 return n;
198123 },
199124 // Repeat a code length of 0 for 3 - 10 times. (3 bits of length)
200 17 => return @as(u8, try self.bits.read(u3)) + 3,
125 17 => return @as(u8, try self.takeBits(u3)) + 3,
201126 // Repeat a code length of 0 for 11 - 138 times (7 bits of length)
202 18 => return @as(u8, try self.bits.read(u7)) + 11,
127 18 => return @as(u8, try self.takeBits(u7)) + 11,
203128 else => return error.InvalidDynamicBlockHeader,
204129 }
205130}
206131
207// In larger archives most blocks are usually dynamic, so decompression
208// performance depends on this function.
209fn dynamicBlock(self: *Decompress) !bool {
210 // Hot path loop!
211 while (!self.hist.full()) {
212 // optimization so other bit reads can be buffered (avoiding one `if` in hot path)
213 try self.bits.fill(15);
214 const sym = try self.decodeSymbol(&self.lit_dec);
215
216 switch (sym.kind) {
217 .literal => self.hist.write(sym.symbol),
218 .match => {
219 // Decode match backreference <length, distance>
220 try self.bits.fill(5 + 15 + 13);
221 const length = try self.decodeLength(sym.symbol);
222 const dsm = try self.decodeSymbol(&self.dst_dec);
223 const distance = try self.decodeDistance(dsm.symbol);
224 try self.hist.writeMatch(length, distance);
225 },
226 .end_of_block => return true,
227 }
228 }
229 return false;
230}
231
232132// Peek 15 bits from bits reader (maximum code len is 15 bits). Use
233133// decoder to find symbol for that code. We then know how many bits is
234134// used. Shift bit reader for that much bits, those bits are used. And
235135// return symbol.
236136fn decodeSymbol(self: *Decompress, decoder: anytype) !Symbol {
237 const sym = try decoder.find(try self.bits.peekF(u15, .{ .buffered = true, .reverse = true }));
238 try self.bits.shift(sym.code_bits);
137 const sym = try decoder.find(try self.peekBitsReverseBuffered(u15));
138 try self.shiftBits(sym.code_bits);
239139 return sym;
240140}
241141
242fn step(self: *Decompress) !void {
243 switch (self.state) {
244 .protocol_header => {
245 try self.hasher.container().parseHeader(&self.bits);
246 self.state = .block_header;
142pub fn read(
143 context: ?*anyopaque,
144 bw: *std.io.BufferedWriter,
145 limit: std.io.Reader.Limit,
146) std.io.Reader.RwError!usize {
147 const d: *Decompress = @alignCast(@ptrCast(context));
148 return readInner(d, bw, limit) catch |err| switch (err) {
149 error.EndOfStream => return error.EndOfStream,
150 error.WriteFailed => return error.WriteFailed,
151 else => |e| {
152 // In the event of an error, state is unmodified so that it can be
153 // better used to diagnose the failure.
154 d.read_err = e;
155 return error.ReadFailed;
156 },
157 };
158}
159
160fn readInner(
161 d: *Decompress,
162 bw: *std.io.BufferedWriter,
163 limit: std.io.Reader.Limit,
164) (Error || error{ WriteFailed, EndOfStream })!usize {
165 const in = d.input;
166 sw: switch (d.state) {
167 .protocol_header => switch (d.hasher.container()) {
168 .gzip => {
169 const Header = extern struct {
170 magic: u16 align(1),
171 method: u8,
172 flags: packed struct(u8) {
173 text: bool,
174 hcrc: bool,
175 extra: bool,
176 name: bool,
177 comment: bool,
178 reserved: u3,
179 },
180 mtime: u32 align(1),
181 xfl: u8,
182 os: u8,
183 };
184 const header = try in.takeStructEndian(Header, .little);
185 if (header.magic != 0x8b1f or header.method != 0x08)
186 return error.BadGzipHeader;
187 if (header.flags.extra) {
188 const extra_len = try in.takeInt(u16, .little);
189 try in.discardAll(extra_len);
190 }
191 if (header.flags.name) {
192 try in.discardDelimiterInclusive(0);
193 }
194 if (header.flags.comment) {
195 try in.discardDelimiterInclusive(0);
196 }
197 if (header.flags.hcrc) {
198 try in.discardAll(2);
199 }
200 continue :sw .block_header;
201 },
202 .zlib => {
203 const Header = extern struct {
204 cmf: packed struct(u8) {
205 cm: u4,
206 cinfo: u4,
207 },
208 flg: u8,
209 };
210 const header = try in.takeStruct(Header);
211 if (header.cmf.cm != 8 or header.cmf.cinfo > 7) return error.BadZlibHeader;
212 continue :sw .block_header;
213 },
214 .raw => continue :sw .block_header,
247215 },
248216 .block_header => {
249 try self.blockHeader();
250 self.state = .block;
251 if (self.block_type == 2) try self.dynamicBlockHeader();
217 d.final_block = (try d.takeBits(u1)) != 0;
218 const block_type = try d.takeBits(BlockType);
219 switch (block_type) {
220 .stored => {
221 d.alignBitsToByte(); // skip padding until byte boundary
222 // everything after this is byte aligned in stored block
223 const len = try in.takeInt(u16, .little);
224 const nlen = try in.takeInt(u16, .little);
225 if (len != ~nlen) return error.WrongStoredBlockNlen;
226 continue :sw .{ .stored_block = len };
227 },
228 .fixed => continue :sw .fixed_block,
229 .dynamic => {
230 const hlit: u16 = @as(u16, try d.takeBits(u5)) + 257; // number of ll code entries present - 257
231 const hdist: u16 = @as(u16, try d.takeBits(u5)) + 1; // number of distance code entries - 1
232 const hclen: u8 = @as(u8, try d.takeBits(u4)) + 4; // hclen + 4 code lengths are encoded
233
234 if (hlit > 286 or hdist > 30)
235 return error.InvalidDynamicBlockHeader;
236
237 // lengths for code lengths
238 var cl_lens = [_]u4{0} ** 19;
239 for (0..hclen) |i| {
240 cl_lens[flate.huffman.codegen_order[i]] = try d.takeBits(u3);
241 }
242 var cl_dec: CodegenDecoder = .{};
243 try cl_dec.generate(&cl_lens);
244
245 // decoded code lengths
246 var dec_lens = [_]u4{0} ** (286 + 30);
247 var pos: usize = 0;
248 while (pos < hlit + hdist) {
249 const sym = try cl_dec.find(try d.peekBitsReverse(u7));
250 try d.shiftBits(sym.code_bits);
251 pos += try d.dynamicCodeLength(sym.symbol, &dec_lens, pos);
252 }
253 if (pos > hlit + hdist) {
254 return error.InvalidDynamicBlockHeader;
255 }
256
257 // literal code lengths to literal decoder
258 try d.lit_dec.generate(dec_lens[0..hlit]);
259
260 // distance code lengths to distance decoder
261 try d.dst_dec.generate(dec_lens[hlit .. hlit + hdist]);
262
263 continue :sw .dynamic_block;
264 },
265 }
252266 },
253 .block => {
254 const done = switch (self.block_type) {
255 0 => try self.storedBlock(),
256 1 => try self.fixedBlock(),
257 2 => try self.dynamicBlock(),
258 else => return error.InvalidBlockType,
259 };
260 if (done) {
261 self.state = if (self.bfinal == 1) .protocol_footer else .block_header;
267 .stored_block => |remaining_len| {
268 const out = try bw.writableSliceGreedyPreserving(flate.history_len, 1);
269 const limited_out = limit.min(.limited(remaining_len)).slice(out);
270 const n = try d.input.readVec(bw, &.{limited_out});
271 if (remaining_len - n == 0) {
272 d.state = if (d.final_block) .protocol_footer else .block_header;
273 } else {
274 d.state = .{ .stored_block = remaining_len - n };
275 }
276 bw.advance(n);
277 return n;
278 },
279 .fixed_block => {
280 const start = bw.count;
281 while (@intFromEnum(limit) > bw.count - start) {
282 const code = try d.readFixedCode();
283 switch (code) {
284 0...255 => try bw.writeBytePreserving(flate.history_len, @intCast(code)),
285 256 => {
286 d.state = if (d.final_block) .protocol_footer else .block_header;
287 return bw.count - start;
288 },
289 257...285 => {
290 // Handles fixed block non literal (length) code.
291 // Length code is followed by 5 bits of distance code.
292 const rebased_code = code - 257;
293 const length = try d.decodeLength(rebased_code);
294 const distance = try d.decodeDistance(try d.takeBitsReverseBuffered(u5));
295 try writeMatch(bw, length, distance);
296 },
297 else => return error.InvalidCode,
298 }
299 }
300 d.state = .fixed_block;
301 return bw.count - start;
302 },
303 .dynamic_block => {
304 // In larger archives most blocks are usually dynamic, so decompression
305 // performance depends on this logic.
306 const start = bw.count;
307 while (@intFromEnum(limit) > bw.count - start) {
308 const sym = try d.decodeSymbol(&d.lit_dec);
309
310 switch (sym.kind) {
311 .literal => d.hist.write(sym.symbol),
312 .match => {
313 // Decode match backreference <length, distance>
314 const length = try d.decodeLength(sym.symbol);
315 const dsm = try d.decodeSymbol(&d.dst_dec);
316 const distance = try d.decodeDistance(dsm.symbol);
317 try writeMatch(bw, length, distance);
318 },
319 .end_of_block => {
320 d.state = if (d.final_block) .protocol_footer else .block_header;
321 return bw.count - start;
322 },
323 }
262324 }
325 d.state = .dynamic_block;
326 return bw.count - start;
263327 },
264328 .protocol_footer => {
265 self.bits.alignToByte();
266 try self.hasher.container().parseFooter(&self.hasher, &self.bits);
267 self.state = .end;
329 d.alignBitsToByte();
330 switch (d.hasher.container()) {
331 .gzip => |*gzip| {
332 if (try reader.read(u32) != gzip.final()) return error.WrongGzipChecksum;
333 if (try reader.read(u32) != gzip.count) return error.WrongGzipSize;
334 },
335 .zlib => |*zlib| {
336 const chksum: u32 = @byteSwap(zlib.final());
337 if (try reader.read(u32) != chksum) return error.WrongZlibChecksum;
338 },
339 .raw => {},
340 }
341 d.state = .end;
342 return 0;
268343 },
269 .end => {},
344 .end => return error.EndOfStream,
270345 }
271346}
272347
273/// Replaces the inner reader with new reader.
274pub fn setReader(self: *Decompress, new_reader: *std.io.BufferedReader) void {
275 self.bits.forward_reader = new_reader;
276 if (self.state == .end or self.state == .protocol_footer) {
277 self.state = .protocol_header;
278 }
348fn readVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
349 _ = context;
350 _ = data;
351 @panic("TODO remove readVec primitive");
279352}
280353
281// Reads all compressed data from the internal reader and outputs plain
282// (uncompressed) data to the provided writer.
283pub fn decompress(self: *Decompress, writer: *std.io.BufferedWriter) !void {
284 while (try self.next()) |buf| {
285 try writer.writeAll(buf);
286 }
354fn discard(context: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize {
355 _ = context;
356 _ = limit;
357 // Problem here is we still need access to the output ring buffer.
358 @panic("TODO allow discard to be null");
287359}
288360
289/// Returns the number of bytes that have been read from the internal
290/// reader but not yet consumed by the decompressor.
291pub fn unreadBytes(self: Decompress) usize {
292 // There can be no error here: the denominator is not zero, and
293 // overflow is not possible since the type is unsigned.
294 return std.math.divCeil(usize, self.bits.nbits, 8) catch unreachable;
361/// Write match (back-reference to the same data slice) starting at `distance`
362/// back from current write position, and `length` of bytes.
363fn writeMatch(bw: *std.io.BufferedWriter, length: u16, distance: u16) !void {
364 _ = bw;
365 _ = length;
366 _ = distance;
367 @panic("TODO");
295368}
296369
297// Iterator interface
370pub fn reader(self: *Decompress) std.io.Reader {
371 return .{
372 .context = self,
373 .vtable = &.{
374 .read = read,
375 .readVec = readVec,
376 .discard = discard,
377 },
378 };
379}
298380
299/// Can be used in iterator like loop without memcpy to another buffer:
300/// while (try inflate.next()) |buf| { ... }
301pub fn next(self: *Decompress) Error!?[]const u8 {
302 const out = try self.get(0);
303 if (out.len == 0) return null;
304 return out;
381pub fn readable(self: *Decompress, buffer: []u8) std.io.BufferedReader {
382 return reader(self).buffered(buffer);
305383}
306384
307/// Returns decompressed data from internal sliding window buffer.
308/// Returned buffer can be any length between 0 and `limit` bytes. 0
309/// returned bytes means end of stream reached. With limit=0 returns as
310/// much data it can. It newer will be more than 65536 bytes, which is
311/// size of internal buffer.
312/// TODO merge this logic into readerRead and readerReadVec
313pub fn get(self: *Decompress, limit: usize) Error![]const u8 {
314 while (true) {
315 const out = self.hist.readAtMost(limit);
316 if (out.len > 0) {
317 self.hasher.update(out);
318 return out;
319 }
320 if (self.state == .end) return out;
321 try self.step();
322 }
385fn takeBits(d: *Decompress, comptime T: type) !T {
386 _ = d;
387 @panic("TODO");
323388}
324389
325fn readerRead(
326 context: ?*anyopaque,
327 bw: *std.io.BufferedWriter,
328 limit: std.io.Reader.Limit,
329) std.io.Reader.RwError!usize {
330 const self: *Decompress = @alignCast(@ptrCast(context));
331 const out = try bw.writableSliceGreedy(1);
332 const in = self.get(limit.minInt(out.len)) catch |err| switch (err) {
333 error.EndOfStream => return error.EndOfStream,
334 error.ReadFailed => return error.ReadFailed,
335 else => |e| {
336 self.read_err = e;
337 return error.ReadFailed;
338 },
339 };
340 if (in.len == 0) return error.EndOfStream;
341 @memcpy(out[0..in.len], in);
342 bw.advance(in.len);
343 return in.len;
390fn takeNBitsBuffered(d: *Decompress, n: u4) !u16 {
391 _ = d;
392 _ = n;
393 @panic("TODO");
344394}
345395
346fn readerReadVec(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
347 const self: *Decompress = @alignCast(@ptrCast(context));
348 return readVec(self, data) catch |err| switch (err) {
349 error.EndOfStream => return error.EndOfStream,
350 error.ReadFailed => return error.ReadFailed,
351 else => |e| {
352 self.read_err = e;
353 return error.ReadFailed;
354 },
355 };
396fn peekBitsReverse(d: *Decompress, comptime T: type) !T {
397 _ = d;
398 @panic("TODO");
356399}
357400
358fn readerDiscard(context: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize {
359 _ = context;
360 _ = limit;
401fn peekBitsReverseBuffered(d: *Decompress, comptime T: type) !T {
402 _ = d;
361403 @panic("TODO");
362404}
363405
364pub fn readVec(self: *Decompress, data: []const []u8) Error!usize {
365 for (data) |out| {
366 if (out.len == 0) continue;
367 const in = try self.get(out.len);
368 @memcpy(out[0..in.len], in);
369 if (in.len == 0) return error.EndOfStream;
370 return in.len;
371 }
372 return 0;
406fn alignBitsToByte(d: *Decompress) void {
407 _ = d;
408 @panic("TODO");
373409}
374410
375pub fn reader(self: *Decompress) std.io.Reader {
376 return .{
377 .context = self,
378 .vtable = &.{
379 .read = readerRead,
380 .readVec = readerReadVec,
381 .discard = readerDiscard,
382 },
383 };
411fn shiftBits(d: *Decompress, n: u6) !void {
412 _ = d;
413 _ = n;
414 @panic("TODO");
384415}
385416
386pub fn readable(self: *Decompress, buffer: []u8) std.io.BufferedReader {
387 return reader(self).buffered(buffer);
417fn readFixedCode(d: *Decompress) !u16 {
418 _ = d;
419 @panic("TODO");
388420}
389421
390422pub const Symbol = packed struct {
......@@ -712,12 +744,16 @@ test "decompress" {
712744 },
713745 };
714746 for (cases) |c| {
715 var fb = std.io.fixedBufferStream(c.in);
716 var al = std.ArrayList(u8).init(testing.allocator);
717 defer al.deinit();
718
719 try decompress(.raw, fb.reader(), al.writer());
720 try testing.expectEqualStrings(c.out, al.items);
747 var fb: std.io.BufferedReader = undefined;
748 fb.initFixed(@constCast(c.in));
749 var aw: std.io.AllocatingWriter = undefined;
750 aw.init(testing.allocator);
751 defer aw.deinit();
752
753 var decompress: Decompress = .init(&fb, .raw);
754 var decompress_br = decompress.readable(&.{});
755 _ = try decompress_br.readRemaining(&aw.buffered_writer);
756 try testing.expectEqualStrings(c.out, aw.getWritten());
721757 }
722758}
723759
......@@ -769,12 +805,16 @@ test "gzip decompress" {
769805 },
770806 };
771807 for (cases) |c| {
772 var fb = std.io.fixedBufferStream(c.in);
773 var al = std.ArrayList(u8).init(testing.allocator);
774 defer al.deinit();
775
776 try decompress(.gzip, fb.reader(), al.writer());
777 try testing.expectEqualStrings(c.out, al.items);
808 var fb: std.io.BufferedReader = undefined;
809 fb.initFixed(@constCast(c.in));
810 var aw: std.io.AllocatingWriter = undefined;
811 aw.init(testing.allocator);
812 defer aw.deinit();
813
814 var decompress: Decompress = .init(&fb, .gzip);
815 var decompress_br = decompress.readable(&.{});
816 _ = try decompress_br.readRemaining(&aw.buffered_writer);
817 try testing.expectEqualStrings(c.out, aw.getWritten());
778818 }
779819}
780820
......@@ -795,12 +835,16 @@ test "zlib decompress" {
795835 },
796836 };
797837 for (cases) |c| {
798 var fb = std.io.fixedBufferStream(c.in);
799 var al = std.ArrayList(u8).init(testing.allocator);
800 defer al.deinit();
801
802 try decompress(.zlib, fb.reader(), al.writer());
803 try testing.expectEqualStrings(c.out, al.items);
838 var fb: std.io.BufferedReader = undefined;
839 fb.initFixed(@constCast(c.in));
840 var aw: std.io.AllocatingWriter = undefined;
841 aw.init(testing.allocator);
842 defer aw.deinit();
843
844 var decompress: Decompress = .init(&fb, .zlib);
845 var decompress_br = decompress.readable(&.{});
846 _ = try decompress_br.readRemaining(&aw.buffered_writer);
847 try testing.expectEqualStrings(c.out, aw.getWritten());
804848 }
805849}
806850
......@@ -853,16 +897,21 @@ test "fuzzing tests" {
853897 };
854898
855899 inline for (cases, 0..) |c, case_no| {
856 var in = std.io.fixedBufferStream(@embedFile("testdata/fuzz/" ++ c.input ++ ".input"));
857 var out = std.ArrayList(u8).init(testing.allocator);
858 defer out.deinit();
900 var in: std.io.BufferedReader = undefined;
901 in.initFixed(@constCast(@embedFile("testdata/fuzz/" ++ c.input ++ ".input")));
902 var aw: std.io.AllocatingWriter = undefined;
903 aw.init(testing.allocator);
904 defer aw.deinit();
859905 errdefer std.debug.print("test case failed {}\n", .{case_no});
860906
907 var decompress: Decompress = .init(&in, .raw);
908 var decompress_br = decompress.readable(&.{});
861909 if (c.err) |expected_err| {
862 try testing.expectError(expected_err, decompress(.raw, in.reader(), out.writer()));
910 try testing.expectError(error.ReadFailed, decompress_br.readRemaining(&aw.buffered_writer));
911 try testing.expectError(expected_err, decompress.read_err.?);
863912 } else {
864 try decompress(.raw, in.reader(), out.writer());
865 try testing.expectEqualStrings(c.out, out.items);
913 _ = try decompress_br.readRemaining(&aw.buffered_writer);
914 try testing.expectEqualStrings(c.out, aw.getWritten());
866915 }
867916 }
868917}
......@@ -871,21 +920,28 @@ test "bug 18966" {
871920 const input = @embedFile("testdata/fuzz/bug_18966.input");
872921 const expect = @embedFile("testdata/fuzz/bug_18966.expect");
873922
874 var in = std.io.fixedBufferStream(input);
875 var out = std.ArrayList(u8).init(testing.allocator);
876 defer out.deinit();
923 var in: std.io.BufferedReader = undefined;
924 in.initFixed(@constCast(input));
925 var aw: std.io.AllocatingWriter = undefined;
926 aw.init(testing.allocator);
927 defer aw.deinit();
877928
878 try decompress(.gzip, in.reader(), out.writer());
879 try testing.expectEqualStrings(expect, out.items);
929 var decompress: Decompress = .init(&in, .gzip);
930 var decompress_br = decompress.readable(&.{});
931 _ = try decompress_br.readRemaining(&aw.buffered_writer);
932 try testing.expectEqualStrings(expect, aw.getWritten());
880933}
881934
882test "bug 19895" {
935test "reading into empty buffer" {
936 // Inspired by https://github.com/ziglang/zig/issues/19895
883937 const input = &[_]u8{
884938 0b0000_0001, 0b0000_1100, 0x00, 0b1111_0011, 0xff, // deflate fixed buffer header len, nlen
885939 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 0x0a, // non compressed data
886940 };
887 var in = std.io.fixedBufferStream(input);
888 var decomp = Decompress.init(.raw, in.reader());
941 var in: std.io.BufferedReader = undefined;
942 in.initFixed(@constCast(input));
943 var decomp: Decompress = .init(&in, .raw);
944 var decompress_br = decomp.readable(&.{});
889945 var buf: [0]u8 = undefined;
890 try testing.expectEqual(0, try decomp.read(&buf));
946 try testing.expectEqual(0, try decompress_br.readVec(&.{&buf}));
891947}
lib/std/compress/flate/Lookup.zig+1-1
......@@ -10,7 +10,7 @@ const flate = @import("../flate.zig");
1010const Lookup = @This();
1111
1212const prime4 = 0x9E3779B1; // 4 bytes prime number 2654435761
13const chain_len = 2 * flate.history.len;
13const chain_len = 2 * flate.history_len;
1414
1515// Maps hash => first position
1616head: [flate.lookup.len]u16 = [_]u16{0} ** flate.lookup.len,
lib/std/compress/xz/test.zig+3-2
......@@ -3,9 +3,10 @@ const testing = std.testing;
33const xz = std.compress.xz;
44
55fn decompress(data: []const u8) ![]u8 {
6 var in_stream = std.io.fixedBufferStream(data);
6 var in_stream: std.io.BufferedReader = undefined;
7 in_stream.initFixed(data);
78
8 var xz_stream = try xz.decompress(testing.allocator, in_stream.reader());
9 var xz_stream = try xz.decompress(testing.allocator, &in_stream);
910 defer xz_stream.deinit();
1011
1112 return xz_stream.reader().readAllAlloc(testing.allocator, std.math.maxInt(usize));
lib/std/compress/zstd/Decompress.zig+2-2
......@@ -267,8 +267,8 @@ fn discard(context: ?*anyopaque, limit: Reader.Limit) Reader.Error!usize {
267267
268268fn readVec(context: ?*anyopaque, data: []const []u8) Reader.Error!usize {
269269 var bw: BufferedWriter = undefined;
270 bw.initFixed(data[0]);
271 return read(context, &bw, .limited(data[0].len)) catch |err| switch (err) {
270 bw.initVec(data);
271 return read(context, &bw, .countVec(data)) catch |err| switch (err) {
272272 error.WriteFailed => unreachable,
273273 else => |e| return e,
274274 };
lib/std/crypto/codecs/asn1.zig+4-3
......@@ -154,7 +154,8 @@ pub const Tag = struct {
154154
155155test Tag {
156156 const buf = [_]u8{0xa3};
157 var stream = std.io.fixedBufferStream(&buf);
157 var stream: std.io.BufferedReader = undefined;
158 stream.initFixed(&buf);
158159 const t = Tag.decode(stream.reader());
159160 try std.testing.expectEqual(Tag.init(@enumFromInt(3), true, .context_specific), t);
160161}
......@@ -184,8 +185,8 @@ pub const Element = struct {
184185 /// - Ensures length is within `bytes`
185186 /// - Ensures length is less than `std.math.maxInt(Index)`
186187 pub fn decode(bytes: []const u8, index: Index) DecodeError!Element {
187 var stream = std.io.fixedBufferStream(bytes[index..]);
188 var reader = stream.reader();
188 var reader: std.io.BufferedReader = undefined;
189 reader.initFixed(bytes[index..]);
189190
190191 const tag = try Tag.decode(reader);
191192 const size_or_len_size = try reader.readByte();
lib/std/crypto/tls/Client.zig+2-2
......@@ -1227,8 +1227,8 @@ fn read(context: ?*anyopaque, bw: *std.io.BufferedWriter, limit: Reader.Limit) R
12271227
12281228fn readVec(context: ?*anyopaque, data: []const []u8) Reader.Error!usize {
12291229 var bw: std.io.BufferedWriter = undefined;
1230 bw.initFixed(data[0]);
1231 return read(context, &bw, .limited(data[0].len)) catch |err| switch (err) {
1230 bw.initVec(data);
1231 return read(context, &bw, .countVec(data)) catch |err| switch (err) {
12321232 error.WriteFailed => unreachable,
12331233 else => |e| return e,
12341234 };
lib/std/debug/Dwarf.zig+1-1
......@@ -2241,7 +2241,7 @@ pub const ElfModule = struct {
22412241 if (chdr.ch_type != .ZLIB) continue;
22422242 const ch_size = chdr.ch_size;
22432243
2244 var zlib_stream: std.compress.zlib.Decompressor = .init(&section_reader);
2244 var zlib_stream: std.compress.flate.Decompress = .init(&section_reader, .zlib);
22452245
22462246 const decompressed_section = zlib_stream.reader().readRemainingAlloc(gpa, .limited(ch_size)) catch continue;
22472247 if (decompressed_section.len != ch_size) {
lib/std/debug/Pdb.zig+5-5
......@@ -67,7 +67,7 @@ pub fn parseDbiStream(self: *Pdb) !void {
6767 return error.InvalidDebugInfo;
6868 const reader = stream.reader();
6969
70 const header = try reader.readStruct(std.pdb.DbiStreamHeader);
70 const header = try reader.takeStruct(std.pdb.DbiStreamHeader);
7171 if (header.version_header != 19990903) // V70, only value observed by LLVM team
7272 return error.UnknownPDBVersion;
7373 // if (header.Age != age)
......@@ -82,7 +82,7 @@ pub fn parseDbiStream(self: *Pdb) !void {
8282 // Module Info Substream
8383 var mod_info_offset: usize = 0;
8484 while (mod_info_offset != mod_info_size) {
85 const mod_info = try reader.readStruct(pdb.ModInfo);
85 const mod_info = try reader.takeStruct(pdb.ModInfo);
8686 var this_record_len: usize = @sizeOf(pdb.ModInfo);
8787
8888 const module_name = try reader.readUntilDelimiterAlloc(self.allocator, 0, 1024);
......@@ -131,7 +131,7 @@ pub fn parseDbiStream(self: *Pdb) !void {
131131 }
132132 while (sect_cont_offset != section_contrib_size) {
133133 const entry = try sect_contribs.addOne();
134 entry.* = try reader.readStruct(pdb.SectionContribEntry);
134 entry.* = try reader.takeStruct(pdb.SectionContribEntry);
135135 sect_cont_offset += @sizeOf(pdb.SectionContribEntry);
136136
137137 if (sect_cont_offset > section_contrib_size)
......@@ -175,7 +175,7 @@ pub fn parseInfoStream(self: *Pdb) !void {
175175 return cap * 2 / 3 + 1;
176176 }
177177 };
178 const hash_tbl_hdr = try reader.readStruct(HashTableHeader);
178 const hash_tbl_hdr = try reader.takeStruct(HashTableHeader);
179179 if (hash_tbl_hdr.capacity == 0)
180180 return error.InvalidDebugInfo;
181181
......@@ -397,7 +397,7 @@ const Msf = struct {
397397 fn init(allocator: Allocator, file: File) !Msf {
398398 const in = file.reader();
399399
400 const superblock = try in.readStruct(pdb.SuperBlock);
400 const superblock = try in.takeStruct(pdb.SuperBlock);
401401
402402 // Sanity checks
403403 if (!std.mem.eql(u8, &superblock.file_magic, pdb.SuperBlock.expect_magic))
lib/std/elf.zig+106-117
......@@ -494,30 +494,39 @@ pub const Header = struct {
494494 shnum: u16,
495495 shstrndx: u16,
496496
497 pub fn program_header_iterator(self: Header, parse_source: anytype) ProgramHeaderIterator(@TypeOf(parse_source)) {
498 return ProgramHeaderIterator(@TypeOf(parse_source)){
499 .elf_header = self,
500 .parse_source = parse_source,
497 pub fn iterateProgramHeaders(h: Header, file_reader: *std.fs.File.Reader) ProgramHeaderIterator {
498 return .{
499 .elf_header = h,
500 .file_reader = file_reader,
501501 };
502502 }
503503
504 pub fn section_header_iterator(self: Header, parse_source: anytype) SectionHeaderIterator(@TypeOf(parse_source)) {
505 return SectionHeaderIterator(@TypeOf(parse_source)){
506 .elf_header = self,
507 .parse_source = parse_source,
504 pub fn iterateSectionHeaders(h: Header, file_reader: *std.fs.File.Reader) SectionHeaderIterator {
505 return .{
506 .elf_header = h,
507 .file_reader = file_reader,
508508 };
509509 }
510510
511 pub fn read(parse_source: anytype) !Header {
512 var hdr_buf: [@sizeOf(Elf64_Ehdr)]u8 align(@alignOf(Elf64_Ehdr)) = undefined;
513 try parse_source.seekableStream().seekTo(0);
514 try parse_source.reader().readNoEof(&hdr_buf);
515 return Header.parse(&hdr_buf);
511 pub const ReadError = std.io.Reader.Error || ParseError;
512
513 pub fn read(br: *std.io.BufferedReader) ReadError!Header {
514 const buf = try br.peek(@sizeOf(Elf64_Ehdr));
515 const result = try parse(@ptrCast(buf));
516 br.toss(if (result.is_64) @sizeOf(Elf64_Ehdr) else @sizeOf(Elf32_Ehdr));
517 return result;
516518 }
517519
518 pub fn parse(hdr_buf: *align(@alignOf(Elf64_Ehdr)) const [@sizeOf(Elf64_Ehdr)]u8) !Header {
519 const hdr32 = @as(*const Elf32_Ehdr, @ptrCast(hdr_buf));
520 const hdr64 = @as(*const Elf64_Ehdr, @ptrCast(hdr_buf));
520 pub const ParseError = error{
521 InvalidElfMagic,
522 InvalidElfVersion,
523 InvalidElfClass,
524 InvalidElfEndian,
525 };
526
527 pub fn parse(hdr_buf: *align(@alignOf(Elf64_Ehdr)) const [@sizeOf(Elf64_Ehdr)]u8) ParseError!Header {
528 const hdr32: *const Elf32_Ehdr = @ptrCast(hdr_buf);
529 const hdr64: *const Elf64_Ehdr = @ptrCast(hdr_buf);
521530 if (!mem.eql(u8, hdr32.e_ident[0..4], MAGIC)) return error.InvalidElfMagic;
522531 if (hdr32.e_ident[EI_VERSION] != 1) return error.InvalidElfVersion;
523532
......@@ -541,19 +550,19 @@ pub const Header = struct {
541550 // The meaning of this value depends on `os_abi` so just make it available as `u8`.
542551 const abi_version = hdr32.e_ident[EI_ABIVERSION];
543552
544 const @"type" = if (need_bswap) blk: {
553 const @"type": ET = if (need_bswap) blk: {
545554 comptime assert(!@typeInfo(ET).@"enum".is_exhaustive);
546555 const value = @intFromEnum(hdr32.e_type);
547 break :blk @as(ET, @enumFromInt(@byteSwap(value)));
556 break :blk @enumFromInt(@byteSwap(value));
548557 } else hdr32.e_type;
549558
550 const machine = if (need_bswap) blk: {
559 const machine: EM = if (need_bswap) blk: {
551560 comptime assert(!@typeInfo(EM).@"enum".is_exhaustive);
552561 const value = @intFromEnum(hdr32.e_machine);
553 break :blk @as(EM, @enumFromInt(@byteSwap(value)));
562 break :blk @enumFromInt(@byteSwap(value));
554563 } else hdr32.e_machine;
555564
556 return @as(Header, .{
565 return .{
557566 .is_64 = is_64,
558567 .endian = endian,
559568 .os_abi = os_abi,
......@@ -568,111 +577,91 @@ pub const Header = struct {
568577 .shentsize = int(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize),
569578 .shnum = int(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum),
570579 .shstrndx = int(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx),
571 });
580 };
572581 }
573582};
574583
575pub fn ProgramHeaderIterator(comptime ParseSource: anytype) type {
576 return struct {
577 elf_header: Header,
578 parse_source: ParseSource,
579 index: usize = 0,
580
581 pub fn next(self: *@This()) !?Elf64_Phdr {
582 if (self.index >= self.elf_header.phnum) return null;
583 defer self.index += 1;
584
585 if (self.elf_header.is_64) {
586 var phdr: Elf64_Phdr = undefined;
587 const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index;
588 try self.parse_source.seekableStream().seekTo(offset);
589 try self.parse_source.reader().readNoEof(mem.asBytes(&phdr));
590
591 // ELF endianness matches native endianness.
592 if (self.elf_header.endian == native_endian) return phdr;
593
594 // Convert fields to native endianness.
584pub const ProgramHeaderIterator = struct {
585 elf_header: Header,
586 file_reader: *std.fs.File.Reader,
587 index: usize = 0,
588
589 pub fn next(it: *ProgramHeaderIterator) !?Elf64_Phdr {
590 if (it.index >= it.elf_header.phnum) return null;
591 defer it.index += 1;
592
593 if (it.elf_header.is_64) {
594 var phdr: Elf64_Phdr = undefined;
595 const offset = it.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * it.index;
596 try it.file_reader.seekTo(offset);
597 var br = it.file_reader.readable(&.{});
598 try br.readSlice(@ptrCast(&phdr));
599 if (it.elf_header.endian != native_endian)
595600 mem.byteSwapAllFields(Elf64_Phdr, &phdr);
596 return phdr;
597 }
598
599 var phdr: Elf32_Phdr = undefined;
600 const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index;
601 try self.parse_source.seekableStream().seekTo(offset);
602 try self.parse_source.reader().readNoEof(mem.asBytes(&phdr));
603
604 // ELF endianness does NOT match native endianness.
605 if (self.elf_header.endian != native_endian) {
606 // Convert fields to native endianness.
607 mem.byteSwapAllFields(Elf32_Phdr, &phdr);
608 }
609
610 // Convert 32-bit header to 64-bit.
611 return Elf64_Phdr{
612 .p_type = phdr.p_type,
613 .p_offset = phdr.p_offset,
614 .p_vaddr = phdr.p_vaddr,
615 .p_paddr = phdr.p_paddr,
616 .p_filesz = phdr.p_filesz,
617 .p_memsz = phdr.p_memsz,
618 .p_flags = phdr.p_flags,
619 .p_align = phdr.p_align,
620 };
601 return phdr;
621602 }
622 };
623}
624
625pub fn SectionHeaderIterator(comptime ParseSource: anytype) type {
626 return struct {
627 elf_header: Header,
628 parse_source: ParseSource,
629 index: usize = 0,
630603
631 pub fn next(self: *@This()) !?Elf64_Shdr {
632 if (self.index >= self.elf_header.shnum) return null;
633 defer self.index += 1;
634
635 if (self.elf_header.is_64) {
636 var shdr: Elf64_Shdr = undefined;
637 const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index;
638 try self.parse_source.seekableStream().seekTo(offset);
639 try self.parse_source.reader().readNoEof(mem.asBytes(&shdr));
640
641 // ELF endianness matches native endianness.
642 if (self.elf_header.endian == native_endian) return shdr;
604 var phdr: Elf32_Phdr = undefined;
605 const offset = it.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * it.index;
606 try it.file_reader.seekTo(offset);
607 var br = it.file_reader.readable(&.{});
608 try br.readSlice(@ptrCast(&phdr));
609 if (it.elf_header.endian != native_endian)
610 mem.byteSwapAllFields(Elf32_Phdr, &phdr);
611 return .{
612 .p_type = phdr.p_type,
613 .p_offset = phdr.p_offset,
614 .p_vaddr = phdr.p_vaddr,
615 .p_paddr = phdr.p_paddr,
616 .p_filesz = phdr.p_filesz,
617 .p_memsz = phdr.p_memsz,
618 .p_flags = phdr.p_flags,
619 .p_align = phdr.p_align,
620 };
621 }
622};
643623
644 // Convert fields to native endianness.
624pub const SectionHeaderIterator = struct {
625 elf_header: Header,
626 file_reader: *std.fs.File.Reader,
627 index: usize = 0,
628
629 pub fn next(it: *SectionHeaderIterator) !?Elf64_Shdr {
630 if (it.index >= it.elf_header.shnum) return null;
631 defer it.index += 1;
632
633 if (it.elf_header.is_64) {
634 var shdr: Elf64_Shdr = undefined;
635 const offset = it.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * it.index;
636 try it.file_reader.seekTo(offset);
637 var br = it.file_reader.readable(&.{});
638 try br.readSlice(@ptrCast(&shdr));
639 if (it.elf_header.endian != native_endian)
645640 mem.byteSwapAllFields(Elf64_Shdr, &shdr);
646 return shdr;
647 }
648
649 var shdr: Elf32_Shdr = undefined;
650 const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index;
651 try self.parse_source.seekableStream().seekTo(offset);
652 try self.parse_source.reader().readNoEof(mem.asBytes(&shdr));
653
654 // ELF endianness does NOT match native endianness.
655 if (self.elf_header.endian != native_endian) {
656 // Convert fields to native endianness.
657 mem.byteSwapAllFields(Elf32_Shdr, &shdr);
658 }
659
660 // Convert 32-bit header to 64-bit.
661 return Elf64_Shdr{
662 .sh_name = shdr.sh_name,
663 .sh_type = shdr.sh_type,
664 .sh_flags = shdr.sh_flags,
665 .sh_addr = shdr.sh_addr,
666 .sh_offset = shdr.sh_offset,
667 .sh_size = shdr.sh_size,
668 .sh_link = shdr.sh_link,
669 .sh_info = shdr.sh_info,
670 .sh_addralign = shdr.sh_addralign,
671 .sh_entsize = shdr.sh_entsize,
672 };
641 return shdr;
673642 }
674 };
675}
643
644 var shdr: Elf32_Shdr = undefined;
645 const offset = it.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * it.index;
646 try it.file_reader.seekTo(offset);
647 var br = it.file_reader.readable(&.{});
648 try br.readSlice(@ptrCast(&shdr));
649 if (it.elf_header.endian != native_endian)
650 mem.byteSwapAllFields(Elf32_Shdr, &shdr);
651 return .{
652 .sh_name = shdr.sh_name,
653 .sh_type = shdr.sh_type,
654 .sh_flags = shdr.sh_flags,
655 .sh_addr = shdr.sh_addr,
656 .sh_offset = shdr.sh_offset,
657 .sh_size = shdr.sh_size,
658 .sh_link = shdr.sh_link,
659 .sh_info = shdr.sh_info,
660 .sh_addralign = shdr.sh_addralign,
661 .sh_entsize = shdr.sh_entsize,
662 };
663 }
664};
676665
677666fn int(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) {
678667 if (is_64) {
lib/std/fmt.zig+8-9
......@@ -828,8 +828,7 @@ pub const BufPrintError = error{
828828 NoSpaceLeft,
829829};
830830
831/// Print a Formatter string into `buf`. Actually just a thin wrapper around `format` and `fixedBufferStream`.
832/// Returns a slice of the bytes printed to.
831/// Print a Formatter string into `buf`. Returns a slice of the bytes printed.
833832pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintError![]u8 {
834833 var bw: std.io.BufferedWriter = undefined;
835834 bw.initFixed(buf);
......@@ -1015,18 +1014,18 @@ test "int.padded" {
10151014test "buffer" {
10161015 {
10171016 var buf1: [32]u8 = undefined;
1018 var fbs = std.io.fixedBufferStream(&buf1);
1019 var bw = fbs.writer();
1017 var bw: std.io.BufferedWriter = undefined;
1018 bw.initFixed(&buf1);
10201019 try bw.printValue("", .{}, 1234, std.options.fmt_max_depth);
1021 try std.testing.expectEqualStrings("1234", fbs.getWritten());
1020 try std.testing.expectEqualStrings("1234", bw.getWritten());
10221021
1023 fbs.reset();
1022 bw.initFixed(&buf1);
10241023 try bw.printValue("c", .{}, 'a', std.options.fmt_max_depth);
1025 try std.testing.expectEqualStrings("a", fbs.getWritten());
1024 try std.testing.expectEqualStrings("a", bw.getWritten());
10261025
1027 fbs.reset();
1026 bw.initFixed(&buf1);
10281027 try bw.printValue("b", .{}, 0b1100, std.options.fmt_max_depth);
1029 try std.testing.expectEqualStrings("1100", fbs.getWritten());
1028 try std.testing.expectEqualStrings("1100", bw.getWritten());
10301029 }
10311030}
10321031
lib/std/io.zig+60
......@@ -9,6 +9,66 @@ const assert = std.debug.assert;
99const Allocator = std.mem.Allocator;
1010const Alignment = std.mem.Alignment;
1111
12pub const Limit = enum(usize) {
13 nothing = 0,
14 unlimited = std.math.maxInt(usize),
15 _,
16
17 /// `std.math.maxInt(usize)` is interpreted to mean `.unlimited`.
18 pub fn limited(n: usize) Limit {
19 return @enumFromInt(n);
20 }
21
22 pub fn countVec(data: []const []const u8) Limit {
23 var total: usize = 0;
24 for (data) |d| total += d.len;
25 return .limited(total);
26 }
27
28 pub fn min(a: Limit, b: Limit) Limit {
29 return @enumFromInt(@min(@intFromEnum(a), @intFromEnum(b)));
30 }
31
32 pub fn minInt(l: Limit, n: usize) usize {
33 return @min(n, @intFromEnum(l));
34 }
35
36 pub fn slice(l: Limit, s: []u8) []u8 {
37 return s[0..l.minInt(s.len)];
38 }
39
40 pub fn sliceConst(l: Limit, s: []const u8) []const u8 {
41 return s[0..l.minInt(s.len)];
42 }
43
44 pub fn toInt(l: Limit) ?usize {
45 return switch (l) {
46 else => @intFromEnum(l),
47 .unlimited => null,
48 };
49 }
50
51 /// Reduces a slice to account for the limit, leaving room for one extra
52 /// byte above the limit, allowing for the use case of differentiating
53 /// between end-of-stream and reaching the limit.
54 pub fn slice1(l: Limit, non_empty_buffer: []u8) []u8 {
55 assert(non_empty_buffer.len >= 1);
56 return non_empty_buffer[0..@min(@intFromEnum(l) +| 1, non_empty_buffer.len)];
57 }
58
59 pub fn nonzero(l: Limit) bool {
60 return @intFromEnum(l) > 0;
61 }
62
63 /// Return a new limit reduced by `amount` or return `null` indicating
64 /// limit would be exceeded.
65 pub fn subtract(l: Limit, amount: usize) ?Limit {
66 if (l == .unlimited) return .unlimited;
67 if (amount > @intFromEnum(l)) return null;
68 return @enumFromInt(@intFromEnum(l) - amount);
69 }
70};
71
1272pub const Reader = @import("io/Reader.zig");
1373pub const Writer = @import("io/Writer.zig");
1474
lib/std/io/AllocatingWriter.zig+1-1
......@@ -130,7 +130,7 @@ pub fn clearRetainingCapacity(aw: *AllocatingWriter) void {
130130}
131131
132132fn writeSplat(context: ?*anyopaque, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {
133 if (data.len == 0 and splat == 0) return 0;
133 assert(data.len != 0);
134134 const aw: *AllocatingWriter = @alignCast(@ptrCast(context));
135135 const start_len = aw.written.len;
136136 const bw = &aw.buffered_writer;
lib/std/io/BufferedReader.zig+9-2
......@@ -253,6 +253,12 @@ pub fn toss(br: *BufferedReader, n: usize) void {
253253 assert(br.seek <= br.end);
254254}
255255
256/// Equivalent to `toss(br.bufferedLen())`.
257pub fn tossAll(br: *BufferedReader) void {
258 br.seek = 0;
259 br.end = 0;
260}
261
256262/// Equivalent to `peek` followed by `toss`.
257263///
258264/// The data returned is invalidated by the next call to `take`, `peek`,
......@@ -791,8 +797,9 @@ pub fn fill(br: *BufferedReader, n: usize) Reader.Error!void {
791797 }
792798}
793799
794/// Fills the buffer with at least one more byte of data, without advancing the
795/// seek position, doing exactly one underlying read.
800/// Without advancing the seek position, does exactly one underlying read, filling the buffer as
801/// much as possible. This may result in zero bytes added to the buffer, which is not an end of
802/// stream condition. End of stream is communicated via returning `error.EndOfStream`.
796803///
797804/// Asserts buffer capacity is at least 1.
798805pub fn fillMore(br: *BufferedReader) Reader.Error!void {
lib/std/io/Reader.zig+21-65
......@@ -14,14 +14,17 @@ vtable: *const VTable,
1414pub const VTable = struct {
1515 /// Writes bytes from the internally tracked stream position to `bw`.
1616 ///
17 /// Returns the number of bytes written, which will be at minimum `0` and at
18 /// most `limit`. The number of bytes read, including zero, does not
19 /// indicate end of stream.
17 /// Returns the number of bytes written, which will be at minimum `0` and
18 /// at most `limit`. The number returned, including zero, does not indicate
19 /// end of stream. `limit` is guaranteed to be at least as large as the
20 /// buffer capacity of `bw`.
2021 ///
21 /// If the reader has an internal seek position, it moves forward in
22 /// accordance with the number of bytes return from this function.
22 /// The reader's internal logical seek position moves forward in accordance
23 /// with the number of bytes returned from this function.
2324 ///
24 /// The implementation should do a maximum of one underlying read call.
25 /// Implementations are encouraged to utilize mandatory minimum buffer
26 /// sizes combined with short reads (returning a value less than `limit`)
27 /// in order to minimize complexity.
2528 read: *const fn (context: ?*anyopaque, bw: *BufferedWriter, limit: Limit) RwError!usize,
2629
2730 /// Writes bytes from the internally tracked stream position to `data`.
......@@ -30,10 +33,12 @@ pub const VTable = struct {
3033 /// at most the sum of each data slice length. The number of bytes read,
3134 /// including zero, does not indicate end of stream.
3235 ///
33 /// If the reader has an internal seek position, it moves forward in
34 /// accordance with the number of bytes return from this function.
36 /// The reader's internal logical seek position moves forward in accordance
37 /// with the number of bytes returned from this function.
3538 ///
36 /// The implementation should do a maximum of one underlying read call.
39 /// Implementations are encouraged to utilize mandatory minimum buffer
40 /// sizes combined with short reads (returning a value less than the total
41 /// buffer capacity inside `data`) in order to minimize complexity.
3742 readVec: *const fn (context: ?*anyopaque, data: []const []u8) Error!usize,
3843
3944 /// Consumes bytes from the internally tracked stream position without
......@@ -43,10 +48,12 @@ pub const VTable = struct {
4348 /// at most `limit`. The number of bytes returned, including zero, does not
4449 /// indicate end of stream.
4550 ///
46 /// If the reader has an internal seek position, it moves forward in
47 /// accordance with the number of bytes return from this function.
51 /// The reader's internal logical seek position moves forward in accordance
52 /// with the number of bytes returned from this function.
4853 ///
49 /// The implementation should do a maximum of one underlying read call.
54 /// Implementations are encouraged to utilize mandatory minimum buffer
55 /// sizes combined with short reads (returning a value less than `limit`)
56 /// in order to minimize complexity.
5057 discard: *const fn (context: ?*anyopaque, limit: Limit) Error!usize,
5158};
5259
......@@ -78,59 +85,8 @@ pub const ShortError = error{
7885 ReadFailed,
7986};
8087
81pub const Limit = enum(usize) {
82 nothing = 0,
83 unlimited = std.math.maxInt(usize),
84 _,
85
86 /// `std.math.maxInt(usize)` is interpreted to mean `.unlimited`.
87 pub fn limited(n: usize) Limit {
88 return @enumFromInt(n);
89 }
90
91 pub fn min(a: Limit, b: Limit) Limit {
92 return @enumFromInt(@min(@intFromEnum(a), @intFromEnum(b)));
93 }
94
95 pub fn minInt(l: Limit, n: usize) usize {
96 return @min(n, @intFromEnum(l));
97 }
98
99 pub fn slice(l: Limit, s: []u8) []u8 {
100 return s[0..l.minInt(s.len)];
101 }
102
103 pub fn sliceConst(l: Limit, s: []const u8) []const u8 {
104 return s[0..l.minInt(s.len)];
105 }
106
107 pub fn toInt(l: Limit) ?usize {
108 return switch (l) {
109 else => @intFromEnum(l),
110 .unlimited => null,
111 };
112 }
113
114 /// Reduces a slice to account for the limit, leaving room for one extra
115 /// byte above the limit, allowing for the use case of differentiating
116 /// between end-of-stream and reaching the limit.
117 pub fn slice1(l: Limit, non_empty_buffer: []u8) []u8 {
118 assert(non_empty_buffer.len >= 1);
119 return non_empty_buffer[0..@min(@intFromEnum(l) +| 1, non_empty_buffer.len)];
120 }
121
122 pub fn nonzero(l: Limit) bool {
123 return @intFromEnum(l) > 0;
124 }
125
126 /// Return a new limit reduced by `amount` or return `null` indicating
127 /// limit would be exceeded.
128 pub fn subtract(l: Limit, amount: usize) ?Limit {
129 if (l == .unlimited) return .unlimited;
130 if (amount > @intFromEnum(l)) return null;
131 return @enumFromInt(@intFromEnum(l) - amount);
132 }
133};
88/// TODO: no pub
89pub const Limit = std.io.Limit;
13490
13591pub fn read(r: Reader, bw: *BufferedWriter, limit: Limit) RwError!usize {
13692 const before = bw.count;
lib/std/io/Reader/Limited.zig+7-7
......@@ -22,7 +22,7 @@ fn passthruRead(context: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit)
2222 const l: *Limited = @alignCast(@ptrCast(context));
2323 const combined_limit = limit.min(l.remaining);
2424 const n = try l.unlimited_reader.read(bw, combined_limit);
25 l.remaining.subtract(n);
25 l.remaining = l.remaining.subtract(n).?;
2626 return n;
2727}
2828
......@@ -30,24 +30,24 @@ fn passthruDiscard(context: ?*anyopaque, limit: Reader.Limit) Reader.Error!usize
3030 const l: *Limited = @alignCast(@ptrCast(context));
3131 const combined_limit = limit.min(l.remaining);
3232 const n = try l.unlimited_reader.discard(combined_limit);
33 l.remaining.subtract(n);
33 l.remaining = l.remaining.subtract(n).?;
3434 return n;
3535}
3636
3737fn passthruReadVec(context: ?*anyopaque, data: []const []u8) Reader.Error!usize {
3838 const l: *Limited = @alignCast(@ptrCast(context));
3939 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);
40 if (data[0].len >= @intFromEnum(l.remaining)) {
41 const n = try l.unlimited_reader.readVec(&.{l.remaining.slice(data[0])});
42 l.remaining = l.remaining.subtract(n).?;
4343 return n;
4444 }
4545 var total: usize = 0;
4646 for (data, 0..) |buf, i| {
4747 total += buf.len;
48 if (total > @intFromEnum(l.limit)) {
48 if (total > @intFromEnum(l.remaining)) {
4949 const n = try l.unlimited_reader.readVec(data[0..i]);
50 l.remaining.subtract(n);
50 l.remaining = l.remaining.subtract(n).?;
5151 return n;
5252 }
5353 }
lib/std/io/Writer.zig+2-1
......@@ -68,7 +68,8 @@ pub const FileError = std.fs.File.PReadError || error{
6868 Unimplemented,
6969};
7070
71pub const Limit = std.io.Reader.Limit;
71/// TODO: no pub
72pub const Limit = std.io.Limit;
7273
7374pub const Offset = enum(u64) {
7475 zero = 0,
lib/std/os/uefi/protocol/file.zig-24
......@@ -79,30 +79,6 @@ pub const File = extern struct {
7979 VolumeFull,
8080 };
8181
82 pub const SeekableStream = io.SeekableStream(
83 *File,
84 SeekError,
85 SeekError,
86 setPosition,
87 seekBy,
88 getPosition,
89 getEndPos,
90 );
91 pub const Reader = io.Reader(*File, ReadError, read);
92 pub const Writer = io.Writer(*File, WriteError, write);
93
94 pub fn seekableStream(self: *File) SeekableStream {
95 return .{ .context = self };
96 }
97
98 pub fn reader(self: *File) Reader {
99 return .{ .context = self };
100 }
101
102 pub fn writer(self: *File) Writer {
103 return .{ .context = self };
104 }
105
10682 pub fn open(
10783 self: *const File,
10884 file_name: [*:0]const u16,
lib/std/testing.zig+5
......@@ -609,6 +609,11 @@ pub const TmpDir = struct {
609609 self.parent_dir.close();
610610 self.* = undefined;
611611 }
612
613 pub fn createFile(td: *TmpDir) std.fs.File {
614 _ = td;
615 @panic("TODO");
616 }
612617};
613618
614619pub fn tmpDir(opts: std.fs.Dir.OpenOptions) TmpDir {
lib/std/tz.zig+12-9
......@@ -54,8 +54,8 @@ pub const Tz = struct {
5454 },
5555 };
5656
57 pub fn parse(allocator: std.mem.Allocator, reader: anytype) !Tz {
58 var legacy_header = try reader.readStruct(Header);
57 pub fn parse(allocator: std.mem.Allocator, reader: *std.io.BufferedReader) !Tz {
58 var legacy_header = try reader.takeStruct(Header);
5959 if (!std.mem.eql(u8, &legacy_header.magic, "TZif")) return error.BadHeader;
6060 if (legacy_header.version != 0 and legacy_header.version != '2' and legacy_header.version != '3') return error.BadVersion;
6161
......@@ -70,7 +70,7 @@ pub const Tz = struct {
7070 const skipv = legacy_header.counts.timecnt * 5 + legacy_header.counts.typecnt * 6 + legacy_header.counts.charcnt + legacy_header.counts.leapcnt * 8 + legacy_header.counts.isstdcnt + legacy_header.counts.isutcnt;
7171 try reader.skipBytes(skipv, .{});
7272
73 var header = try reader.readStruct(Header);
73 var header = try reader.takeStruct(Header);
7474 if (!std.mem.eql(u8, &header.magic, "TZif")) return error.BadHeader;
7575 if (header.version != '2' and header.version != '3') return error.BadVersion;
7676 if (builtin.target.cpu.arch.endian() != std.builtin.Endian.big) {
......@@ -215,9 +215,10 @@ pub const Tz = struct {
215215
216216test "slim" {
217217 const data = @embedFile("tz/asia_tokyo.tzif");
218 var in_stream = std.io.fixedBufferStream(data);
218 var in_stream: std.io.BufferedReader = undefined;
219 in_stream.initFixed(data);
219220
220 var tz = try std.Tz.parse(std.testing.allocator, in_stream.reader());
221 var tz = try std.Tz.parse(std.testing.allocator, &in_stream);
221222 defer tz.deinit();
222223
223224 try std.testing.expectEqual(tz.transitions.len, 9);
......@@ -228,9 +229,10 @@ test "slim" {
228229
229230test "fat" {
230231 const data = @embedFile("tz/antarctica_davis.tzif");
231 var in_stream = std.io.fixedBufferStream(data);
232 var in_stream: std.io.BufferedReader = undefined;
233 in_stream.initFixed(data);
232234
233 var tz = try std.Tz.parse(std.testing.allocator, in_stream.reader());
235 var tz = try std.Tz.parse(std.testing.allocator, &in_stream);
234236 defer tz.deinit();
235237
236238 try std.testing.expectEqual(tz.transitions.len, 8);
......@@ -241,9 +243,10 @@ test "fat" {
241243test "legacy" {
242244 // Taken from Slackware 8.0, from 2001
243245 const data = @embedFile("tz/europe_vatican.tzif");
244 var in_stream = std.io.fixedBufferStream(data);
246 var in_stream: std.io.BufferedReader = undefined;
247 in_stream.initFixed(data);
245248
246 var tz = try std.Tz.parse(std.testing.allocator, in_stream.reader());
249 var tz = try std.Tz.parse(std.testing.allocator, &in_stream);
247250 defer tz.deinit();
248251
249252 try std.testing.expectEqual(tz.transitions.len, 170);
lib/std/zip.zig+116-61
......@@ -112,7 +112,7 @@ pub const EndRecord = extern struct {
112112 return record;
113113 }
114114
115 pub const FindFileError = File.GetEndPosError || File.SeekError || error{
115 pub const FindFileError = File.GetEndPosError || File.SeekError || File.ReadError || error{
116116 ZipNoEndRecord,
117117 EndOfStream,
118118 };
......@@ -138,6 +138,7 @@ pub const EndRecord = extern struct {
138138 var br = fr.interface().unbuffered();
139139 br.readSlice(read_buf) catch |err| switch (err) {
140140 error.ReadFailed => return fr.err.?,
141 error.EndOfStream => return error.EndOfStream,
141142 };
142143 loaded_len = new_loaded_len;
143144 }
......@@ -158,45 +159,83 @@ pub const EndRecord = extern struct {
158159 }
159160};
160161
161/// Decompresses the given data from `reader` into `writer`. Stops early if more
162/// than `uncompressed_size` bytes are processed and verifies that exactly that
163/// number of bytes are decompressed. Returns the CRC-32 of the uncompressed data.
164/// `writer` can be anything with a `writeAll(self: *Self, chunk: []const u8) anyerror!void` method.
165pub fn decompress(
166 method: CompressionMethod,
167 uncompressed_size: u64,
168 reader: *std.io.BufferedReader,
169 writer: *std.io.BufferedWriter,
170 compressed_remaining: *u64,
171) !u32 {
172 var hash = std.hash.Crc32.init();
173 var total_uncompressed: u64 = 0;
174 switch (method) {
175 .store => {
176 reader.writeAll(writer, .limited(compressed_remaining.*)) catch |err| switch (err) {
177 error.EndOfStream => return error.ZipDecompressTruncated,
178 else => |e| return e,
179 };
180 total_uncompressed += compressed_remaining.*;
181 },
182 .deflate => {
183 var decompressor: std.compress.flate.Decompressor = .init(reader);
184 while (try decompressor.next()) |chunk| {
185 try writer.writeAll(chunk);
186 hash.update(chunk);
187 total_uncompressed += @intCast(chunk.len);
188 if (total_uncompressed > uncompressed_size)
189 return error.ZipUncompressSizeTooSmall;
190 compressed_remaining.* -= chunk.len;
191 }
192 },
193 _ => return error.UnsupportedCompressionMethod,
162pub const Decompress = union {
163 inflate: std.compress.flate.Decompress,
164 store: *std.io.BufferedReader,
165
166 fn readable(
167 d: *Decompress,
168 reader: *std.io.BufferedReader,
169 method: CompressionMethod,
170 buffer: []u8,
171 ) std.io.BufferedReader {
172 switch (method) {
173 .store => {
174 d.* = .{ .store = reader };
175 return .{
176 .unbuffered_reader = .{
177 .context = d,
178 .vtable = &.{
179 .read = readStore,
180 .readVec = readVecUnimplemented,
181 .discard = discardUnimplemented,
182 },
183 },
184 .buffer = buffer,
185 .end = 0,
186 .seek = 0,
187 };
188 },
189 .deflate => {
190 d.* = .{ .inflate = .init(reader, .raw) };
191 return .{
192 .unbuffered_reader = .{
193 .context = d,
194 .vtable = &.{
195 .read = readDeflate,
196 .readVec = readVecUnimplemented,
197 .discard = discardUnimplemented,
198 },
199 },
200 .buffer = buffer,
201 .end = 0,
202 .seek = 0,
203 };
204 },
205 else => unreachable,
206 }
194207 }
195 if (total_uncompressed != uncompressed_size)
196 return error.ZipUncompressSizeMismatch;
197208
198 return hash.final();
199}
209 fn readStore(
210 context: ?*anyopaque,
211 writer: *std.io.BufferedWriter,
212 limit: std.io.Limit,
213 ) std.io.Reader.RwError!usize {
214 const d: *Decompress = @ptrCast(@alignCast(context));
215 return d.store.read(writer, limit);
216 }
217
218 fn readDeflate(
219 context: ?*anyopaque,
220 writer: *std.io.BufferedWriter,
221 limit: std.io.Limit,
222 ) std.io.Reader.RwError!usize {
223 const d: *Decompress = @ptrCast(@alignCast(context));
224 return std.compress.flate.Decompress.read(&d.inflate, writer, limit);
225 }
226
227 fn readVecUnimplemented(context: ?*anyopaque, data: []const []u8) std.io.Reader.Error!usize {
228 _ = context;
229 _ = data;
230 @panic("TODO remove readVec primitive");
231 }
232
233 fn discardUnimplemented(context: ?*anyopaque, limit: std.io.Reader.Limit) std.io.Reader.Error!usize {
234 _ = context;
235 _ = limit;
236 @panic("TODO allow discard to be null");
237 }
238};
200239
201240fn isBadFilename(filename: []const u8) bool {
202241 if (filename.len == 0 or filename[0] == '/')
......@@ -299,8 +338,9 @@ pub const Iterator = struct {
299338 return error.ZipTruncated;
300339 try input.seekTo(stream_len - locator_end_offset);
301340 var br = input.interface().unbuffered();
302 const locator = br.readStructEndian(EndLocator64, .little) catch |err| switch (err) {
341 const locator = br.takeStructEndian(EndLocator64, .little) catch |err| switch (err) {
303342 error.ReadFailed => return input.err.?,
343 error.EndOfStream => return error.EndOfStream,
304344 };
305345 if (!std.mem.eql(u8, &locator.signature, &end_locator64_sig))
306346 return error.ZipBadLocatorSig;
......@@ -311,8 +351,9 @@ pub const Iterator = struct {
311351
312352 try input.seekTo(locator.record_file_offset);
313353
314 const record64 = br.readStructEndian(EndRecord64, .little) catch |err| switch (err) {
354 const record64 = br.takeStructEndian(EndRecord64, .little) catch |err| switch (err) {
315355 error.ReadFailed => return input.err.?,
356 error.EndOfStream => return error.EndOfStream,
316357 };
317358
318359 if (!std.mem.eql(u8, &record64.signature, &end_record64_sig))
......@@ -367,8 +408,9 @@ pub const Iterator = struct {
367408 const input = self.input;
368409 try input.seekTo(header_zip_offset);
369410 var br = input.interface().unbuffered();
370 const header = br.readStructEndian(CentralDirectoryFileHeader, .little) catch |err| switch (err) {
411 const header = br.takeStructEndian(CentralDirectoryFileHeader, .little) catch |err| switch (err) {
371412 error.ReadFailed => return input.err.?,
413 error.EndOfStream => return error.EndOfStream,
372414 };
373415 if (!std.mem.eql(u8, &header.signature, &central_file_header_sig))
374416 return error.ZipBadCdOffset;
......@@ -399,6 +441,7 @@ pub const Iterator = struct {
399441 try input.seekTo(header_zip_offset + @sizeOf(CentralDirectoryFileHeader) + header.filename_len);
400442 br.readSlice(extra) catch |err| switch (err) {
401443 error.ReadFailed => return input.err.?,
444 error.EndOfStream => return error.EndOfStream,
402445 };
403446
404447 var extra_offset: usize = 0;
......@@ -454,20 +497,23 @@ pub const Iterator = struct {
454497 ) !u32 {
455498 if (filename_buf.len < self.filename_len)
456499 return error.ZipInsufficientBuffer;
500 switch (self.compression_method) {
501 .store, .deflate => {},
502 else => return error.UnsupportedCompressionMethod,
503 }
457504 const filename = filename_buf[0..self.filename_len];
458
459 try stream.seekTo(self.header_zip_offset + @sizeOf(CentralDirectoryFileHeader));
460
461505 {
462 const len = try stream.context.reader().readAll(filename);
463 if (len != filename.len)
464 return error.ZipBadFileOffset;
506 try stream.seekTo(self.header_zip_offset + @sizeOf(CentralDirectoryFileHeader));
507 var stream_br = stream.readable(&.{});
508 try stream_br.readSlice(filename);
465509 }
466510
467511 const local_data_header_offset: u64 = local_data_header_offset: {
468512 const local_header = blk: {
469513 try stream.seekTo(self.file_offset);
470 break :blk try stream.context.reader().readStructEndian(LocalFileHeader, .little);
514 var read_buffer: [@sizeOf(LocalFileHeader)]u8 = undefined;
515 var stream_br = stream.readable(&read_buffer);
516 break :blk try stream_br.takeStructEndian(LocalFileHeader, .little);
471517 };
472518 if (!std.mem.eql(u8, &local_header.signature, &local_file_header_sig))
473519 return error.ZipBadFileOffset;
......@@ -493,9 +539,8 @@ pub const Iterator = struct {
493539
494540 {
495541 try stream.seekTo(self.file_offset + @sizeOf(LocalFileHeader) + local_header.filename_len);
496 const len = try stream.context.reader().readAll(extra);
497 if (len != extra.len)
498 return error.ZipTruncated;
542 var stream_br = stream.readable(&.{});
543 try stream_br.readSlice(extra);
499544 }
500545
501546 var extra_offset: usize = 0;
......@@ -557,21 +602,31 @@ pub const Iterator = struct {
557602 break :blk try dest.createFile(filename, .{ .exclusive = true });
558603 };
559604 defer out_file.close();
605 var file_writer = out_file.writer();
606 var file_bw = file_writer.writable(&.{});
560607 const local_data_file_offset: u64 =
561608 @as(u64, self.file_offset) +
562609 @as(u64, @sizeOf(LocalFileHeader)) +
563610 local_data_header_offset;
564611 try stream.seekTo(local_data_file_offset);
565 var compressed_remaining: u64 = self.compressed_size;
566 const crc = try decompress(
567 self.compression_method,
568 self.uncompressed_size,
569 stream.context.reader(),
570 out_file.writer(),
571 &compressed_remaining,
572 );
573 if (compressed_remaining != 0) return error.ZipDecompressTruncated;
574 return crc;
612 var limited_file_reader = stream.interface().limited(.limited(self.compressed_size));
613 var file_read_buffer: [1000]u8 = undefined;
614 var decompress_read_buffer: [1000]u8 = undefined;
615 var limited_br = limited_file_reader.reader().buffered(&file_read_buffer);
616 var decompress: Decompress = undefined;
617 var decompress_br = decompress.readable(&limited_br, self.compression_method, &decompress_read_buffer);
618 const start_out = file_bw.count;
619 var hash_writer = file_bw.hashed(std.hash.Crc32.init());
620 var hash_bw = hash_writer.writable(&.{});
621 decompress_br.readAll(&hash_bw, .limited(self.uncompressed_size)) catch |err| switch (err) {
622 error.ReadFailed => return stream.err.?,
623 error.WriteFailed => return file_writer.err.?,
624 error.EndOfStream => return error.ZipDecompressTruncated,
625 };
626 if (limited_file_reader.remaining.nonzero()) return error.ZipDecompressTruncated;
627 const written = file_bw.count - start_out;
628 if (written != self.uncompressed_size) return error.ZipUncompressSizeMismatch;
629 return hash_writer.hasher.final();
575630 }
576631 };
577632};
lib/std/zip/test.zig+90-42
......@@ -33,8 +33,10 @@ fn expectFiles(
3333 std.mem.replaceScalar(u8, normalized_sub_path, '\\', '/');
3434 var file = try dir.openFile(normalized_sub_path, .{});
3535 defer file.close();
36 var file_reader = file.reader();
37 var file_br = file_reader.readable(&.{});
3638 var content_buf: [4096]u8 = undefined;
37 const n = try file.reader().readAll(&content_buf);
39 const n = try file_br.readSliceShort(&content_buf);
3840 try testing.expectEqualStrings(test_file.content, content_buf[0..n]);
3941 }
4042}
......@@ -49,24 +51,21 @@ const FileStore = struct {
4951 uncompressed_size: usize,
5052};
5153
52fn makeZip(buf: []u8, files: []const File, options: WriteZipOptions) !std.io.BufferedReader {
54fn makeZip(file_writer: *std.fs.File.Writer, files: []const File, options: WriteZipOptions) !std.io.BufferedReader {
5355 const store = try std.testing.allocator.alloc(FileStore, files.len);
5456 defer std.testing.allocator.free(store);
55 return makeZipWithStore(buf, files, options, store);
57 return makeZipWithStore(file_writer, files, options, store);
5658}
5759
5860fn makeZipWithStore(
59 buf: []u8,
61 file_writer: *std.fs.File.Writer,
6062 files: []const File,
6163 options: WriteZipOptions,
6264 store: []FileStore,
63) !std.io.BufferedReader {
64 var out: std.io.BufferedWriter = undefined;
65 out.initFixed(buf);
66 try writeZip(&out, files, store, options);
67 var result: std.io.BufferedReader = undefined;
68 result.initFixed(buf[0..out.end]);
69 return result;
65) !void {
66 var buffer: [200]u8 = undefined;
67 var bw = file_writer.writable(&buffer);
68 try writeZip(&bw, files, store, options);
7069}
7170
7271const WriteZipOptions = struct {
......@@ -201,9 +200,12 @@ const Zipper = struct {
201200 const offset = writer.count;
202201 var br: std.io.BufferedReader = undefined;
203202 br.initFixed(@constCast(opt.content));
204 try std.compress.flate.deflate.compress(.raw, &br, writer, .{});
203 var compress: std.compress.flate.Compress = .init(&br, .{});
204 var compress_br = compress.readable(&.{});
205 const n = try compress_br.readRemaining(writer);
205206 assert(br.seek == opt.content.len);
206 compressed_size = @intCast(writer.count - offset);
207 try testing.expectEqual(n, writer.count - offset);
208 compressed_size = @intCast(n);
207209 },
208210 else => unreachable,
209211 }
......@@ -306,21 +308,27 @@ fn testZipWithStore(
306308 write_opt: WriteZipOptions,
307309 store: []FileStore,
308310) !void {
309 var zip_buf: [4096]u8 = undefined;
310 var fbs = try makeZipWithStore(&zip_buf, test_files, write_opt, store);
311
312311 var tmp = testing.tmpDir(.{ .no_follow = true });
313312 defer tmp.cleanup();
314 try zip.extract(tmp.dir, fbs.seekableStream(), options);
313
314 var file = tmp.createFile();
315 defer file.close();
316 var file_writer = file.writer();
317 try makeZipWithStore(&file_writer, test_files, write_opt, store);
318 var file_reader = file_writer.moveToReader();
319 try zip.extract(tmp.dir, &file_reader, options);
315320 try expectFiles(test_files, tmp.dir, .{});
316321}
317322fn testZipError(expected_error: anyerror, file: File, options: zip.ExtractOptions) !void {
318 var zip_buf: [4096]u8 = undefined;
319 var store: [1]FileStore = undefined;
320 var fbs = try makeZipWithStore(&zip_buf, &[_]File{file}, .{}, &store);
321323 var tmp = testing.tmpDir(.{ .no_follow = true });
322324 defer tmp.cleanup();
323 try testing.expectError(expected_error, zip.extract(tmp.dir, fbs.seekableStream(), options));
325 const tmp_file = tmp.createFile();
326 defer tmp_file.close();
327 var file_writer = tmp_file.writer();
328 var store: [1]FileStore = undefined;
329 try makeZipWithStore(&file_writer, &[_]File{file}, .{}, &store);
330 var file_reader = file_writer.moveToReader();
331 try testing.expectError(expected_error, zip.extract(tmp.dir, &file_reader, options));
324332}
325333
326334test "zip one file" {
......@@ -416,53 +424,93 @@ test "zip64" {
416424test "bad zip files" {
417425 var tmp = testing.tmpDir(.{ .no_follow = true });
418426 defer tmp.cleanup();
419 var zip_buf: [4096]u8 = undefined;
427 var buffer: [4096]u8 = undefined;
420428
421429 const file_a = [_]File{.{ .name = "a", .content = "", .compression = .store }};
422430
423431 {
424 var fbs = try makeZip(&zip_buf, &.{}, .{ .end = .{ .sig = [_]u8{ 1, 2, 3, 4 } } });
425 try testing.expectError(error.ZipNoEndRecord, zip.extract(tmp.dir, fbs.seekableStream(), .{}));
432 const tmp_file = tmp.createFile();
433 defer tmp_file.close();
434 var file_writer = tmp_file.writable(&buffer);
435 try makeZip(&file_writer, &.{}, .{ .end = .{ .sig = [_]u8{ 1, 2, 3, 4 } } });
436 var file_reader = file_writer.moveToReader();
437 try testing.expectError(error.ZipNoEndRecord, zip.extract(tmp.dir, &file_reader, .{}));
426438 }
427439 {
428 var fbs = try makeZip(&zip_buf, &.{}, .{ .end = .{ .comment_len = 1 } });
429 try testing.expectError(error.ZipNoEndRecord, zip.extract(tmp.dir, fbs.seekableStream(), .{}));
440 const tmp_file = tmp.createFile();
441 defer tmp_file.close();
442 var file_writer = tmp_file.writable(&buffer);
443 try makeZip(&file_writer, &.{}, .{ .end = .{ .comment_len = 1 } });
444 var file_reader = file_writer.moveToReader();
445 try testing.expectError(error.ZipNoEndRecord, zip.extract(tmp.dir, &file_reader, .{}));
430446 }
431447 {
432 var fbs = try makeZip(&zip_buf, &.{}, .{ .end = .{ .comment = "a", .comment_len = 0 } });
433 try testing.expectError(error.ZipNoEndRecord, zip.extract(tmp.dir, fbs.seekableStream(), .{}));
448 const tmp_file = tmp.createFile();
449 defer tmp_file.close();
450 var file_writer = tmp_file.writable(&buffer);
451 try makeZip(&file_writer, &.{}, .{ .end = .{ .comment = "a", .comment_len = 0 } });
452 var file_reader = file_writer.moveToReader();
453 try testing.expectError(error.ZipNoEndRecord, zip.extract(tmp.dir, &file_reader, .{}));
434454 }
435455 {
436 var fbs = try makeZip(&zip_buf, &.{}, .{ .end = .{ .disk_number = 1 } });
437 try testing.expectError(error.ZipMultiDiskUnsupported, zip.extract(tmp.dir, fbs.seekableStream(), .{}));
456 const tmp_file = tmp.createFile();
457 defer tmp_file.close();
458 var file_writer = tmp_file.writable(&buffer);
459 try makeZip(&file_writer, &.{}, .{ .end = .{ .disk_number = 1 } });
460 var file_reader = file_writer.moveToReader();
461 try testing.expectError(error.ZipMultiDiskUnsupported, zip.extract(tmp.dir, &file_reader, .{}));
438462 }
439463 {
440 var fbs = try makeZip(&zip_buf, &.{}, .{ .end = .{ .central_directory_disk_number = 1 } });
441 try testing.expectError(error.ZipMultiDiskUnsupported, zip.extract(tmp.dir, fbs.seekableStream(), .{}));
464 const tmp_file = tmp.createFile();
465 defer tmp_file.close();
466 var file_writer = tmp_file.writable(&buffer);
467 try makeZip(&file_writer, &.{}, .{ .end = .{ .central_directory_disk_number = 1 } });
468 var file_reader = file_writer.moveToReader();
469 try testing.expectError(error.ZipMultiDiskUnsupported, zip.extract(tmp.dir, &file_reader, .{}));
442470 }
443471 {
444 var fbs = try makeZip(&zip_buf, &.{}, .{ .end = .{ .record_count_disk = 1 } });
445 try testing.expectError(error.ZipDiskRecordCountTooLarge, zip.extract(tmp.dir, fbs.seekableStream(), .{}));
472 const tmp_file = tmp.createFile();
473 defer tmp_file.close();
474 var file_writer = tmp_file.writable(&buffer);
475 try makeZip(&file_writer, &.{}, .{ .end = .{ .record_count_disk = 1 } });
476 var file_reader = file_writer.moveToReader();
477 try testing.expectError(error.ZipDiskRecordCountTooLarge, zip.extract(tmp.dir, &file_reader, .{}));
446478 }
447479 {
448 var fbs = try makeZip(&zip_buf, &.{}, .{ .end = .{ .central_directory_size = 1 } });
449 try testing.expectError(error.ZipCdOversized, zip.extract(tmp.dir, fbs.seekableStream(), .{}));
480 const tmp_file = tmp.createFile();
481 defer tmp_file.close();
482 var file_writer = tmp_file.writable(&buffer);
483 try makeZip(&file_writer, &.{}, .{ .end = .{ .central_directory_size = 1 } });
484 var file_reader = file_writer.moveToReader();
485 try testing.expectError(error.ZipCdOversized, zip.extract(tmp.dir, &file_reader, .{}));
450486 }
451487 {
452 var fbs = try makeZip(&zip_buf, &file_a, .{ .end = .{ .central_directory_size = 0 } });
453 try testing.expectError(error.ZipCdUndersized, zip.extract(tmp.dir, fbs.seekableStream(), .{}));
488 const tmp_file = tmp.createFile();
489 defer tmp_file.close();
490 var file_writer = tmp_file.writable(&buffer);
491 try makeZip(&file_writer, &file_a, .{ .end = .{ .central_directory_size = 0 } });
492 var file_reader = file_writer.moveToReader();
493 try testing.expectError(error.ZipCdUndersized, zip.extract(tmp.dir, &file_reader, .{}));
454494 }
455495 {
456 var fbs = try makeZip(&zip_buf, &file_a, .{ .end = .{ .central_directory_offset = 0 } });
457 try testing.expectError(error.ZipBadCdOffset, zip.extract(tmp.dir, fbs.seekableStream(), .{}));
496 const tmp_file = tmp.createFile();
497 defer tmp_file.close();
498 var file_writer = tmp_file.writable(&buffer);
499 try makeZip(&file_writer, &file_a, .{ .end = .{ .central_directory_offset = 0 } });
500 var file_reader = file_writer.moveToReader();
501 try testing.expectError(error.ZipBadCdOffset, zip.extract(tmp.dir, &file_reader, .{}));
458502 }
459503 {
460 var fbs = try makeZip(&zip_buf, &file_a, .{
504 const tmp_file = tmp.createFile();
505 defer tmp_file.close();
506 var file_writer = tmp_file.writable(&buffer);
507 try makeZip(&file_writer, &file_a, .{
461508 .end = .{
462509 .zip64 = .{ .locator_sig = [_]u8{ 1, 2, 3, 4 } },
463510 .central_directory_size = std.math.maxInt(u32), // trigger 64
464511 },
465512 });
466 try testing.expectError(error.ZipBadLocatorSig, zip.extract(tmp.dir, fbs.seekableStream(), .{}));
513 var file_reader = file_writer.moveToReader();
514 try testing.expectError(error.ZipBadLocatorSig, zip.extract(tmp.dir, &file_reader, .{}));
467515 }
468516}
src/Package/Fetch.zig+1-2
......@@ -1357,8 +1357,7 @@ fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource.Git) anyerror!U
13571357 defer pack_dir.close();
13581358 var pack_file = try pack_dir.createFile("pkg.pack", .{ .read = true });
13591359 defer pack_file.close();
1360 var fifo = std.fifo.LinearFifo(u8, .{ .Static = 4096 }).init();
1361 try fifo.pump(resource.fetch_stream.reader(), pack_file.writer());
1360 _ = try resource.fetch_stream.reader().readRemaining(pack_file.writer());
13621361 try pack_file.sync();
13631362
13641363 var index_file = try pack_dir.createFile("pkg.idx", .{ .read = true });