authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-24 23:31:00-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-25 14:15:33-07:00
logee4f5b3f92988bb57849ea0e8d3d2651cda4b696
tree56d0173a273b0b61ae2d058fc4e45601a76dfedf
parent7f1c04423e5cfd628c58824aee543b4e79525c5f

std.compress.zstd: respect the window length


2 files changed, 29 insertions(+), 19 deletions(-)

lib/std/compress/zstd.zig+2-3
...@@ -1,12 +1,11 @@...@@ -1,12 +1,11 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const assert = std.debug.assert;2const assert = std.debug.assert;
33
4pub const Decompress = @import("zstd/Decompress.zig");
5
4/// Recommended amount by the standard. Lower than this may result in inability6/// Recommended amount by the standard. Lower than this may result in inability
5/// to decompress common streams.7/// to decompress common streams.
6pub const default_window_len = 8 * 1024 * 1024;8pub const default_window_len = 8 * 1024 * 1024;
7
8pub const Decompress = @import("zstd/Decompress.zig");
9
10pub const block_size_max = 1 << 17;9pub const block_size_max = 1 << 17;
1110
12pub const literals_length_default_distribution = [36]i16{11pub const literals_length_default_distribution = [36]i16{
lib/std/compress/zstd/Decompress.zig+27-16
...@@ -10,6 +10,7 @@ input: *Reader,...@@ -10,6 +10,7 @@ input: *Reader,
10reader: Reader,10reader: Reader,
11state: State,11state: State,
12verify_checksum: bool,12verify_checksum: bool,
13window_len: u32,
13err: ?Error = null,14err: ?Error = null,
1415
15const State = union(enum) {16const State = union(enum) {
...@@ -29,6 +30,8 @@ pub const Options = struct {...@@ -29,6 +30,8 @@ pub const Options = struct {
29 /// Verifying checksums is not implemented yet and will cause a panic if30 /// Verifying checksums is not implemented yet and will cause a panic if
30 /// you set this to true.31 /// you set this to true.
31 verify_checksum: bool = false,32 verify_checksum: bool = false,
33 /// Affects the minimum capacity of the provided buffer.
34 window_len: u32 = zstd.default_window_len,
32};35};
3336
34pub const Error = error{37pub const Error = error{
...@@ -65,11 +68,14 @@ pub const Error = error{...@@ -65,11 +68,14 @@ pub const Error = error{
65 WindowSizeUnknown,68 WindowSizeUnknown,
66};69};
6770
71/// If buffer that is written to is not big enough, some streams will fail with
72/// `error.OutputBufferUndersize`. A safe value is `zstd.default_window_len * 2`.
68pub fn init(input: *Reader, buffer: []u8, options: Options) Decompress {73pub fn init(input: *Reader, buffer: []u8, options: Options) Decompress {
69 return .{74 return .{
70 .input = input,75 .input = input,
71 .state = .new_frame,76 .state = .new_frame,
72 .verify_checksum = options.verify_checksum,77 .verify_checksum = options.verify_checksum,
78 .window_len = options.window_len,
73 .reader = .{79 .reader = .{
74 .vtable = &.{ .stream = stream },80 .vtable = &.{ .stream = stream },
75 .buffer = buffer,81 .buffer = buffer,
...@@ -143,6 +149,7 @@ fn initFrame(d: *Decompress, window_size_max: usize, magic: Frame.Magic) !void {...@@ -143,6 +149,7 @@ fn initFrame(d: *Decompress, window_size_max: usize, magic: Frame.Magic) !void {
143149
144fn readInFrame(d: *Decompress, w: *Writer, limit: Limit, state: *State.InFrame) !usize {150fn readInFrame(d: *Decompress, w: *Writer, limit: Limit, state: *State.InFrame) !usize {
145 const in = d.input;151 const in = d.input;
152 const window_len = d.window_len;
146153
147 const header_bytes = try in.takeArray(3);154 const header_bytes = try in.takeArray(3);
148 const block_header: Frame.Zstandard.Block.Header = @bitCast(header_bytes.*);155 const block_header: Frame.Zstandard.Block.Header = @bitCast(header_bytes.*);
...@@ -153,12 +160,12 @@ fn readInFrame(d: *Decompress, w: *Writer, limit: Limit, state: *State.InFrame)...@@ -153,12 +160,12 @@ fn readInFrame(d: *Decompress, w: *Writer, limit: Limit, state: *State.InFrame)
153 var bytes_written: usize = 0;160 var bytes_written: usize = 0;
154 switch (block_header.type) {161 switch (block_header.type) {
155 .raw => {162 .raw => {
156 try in.streamExact(w, block_size);163 try in.streamExactPreserve(w, window_len, block_size);
157 bytes_written = block_size;164 bytes_written = block_size;
158 },165 },
159 .rle => {166 .rle => {
160 const byte = try in.takeByte();167 const byte = try in.takeByte();
161 try w.splatByteAll(byte, block_size);168 try w.splatBytePreserve(window_len, byte, block_size);
162 bytes_written = block_size;169 bytes_written = block_size;
163 },170 },
164 .compressed => {171 .compressed => {
...@@ -167,7 +174,7 @@ fn readInFrame(d: *Decompress, w: *Writer, limit: Limit, state: *State.InFrame)...@@ -167,7 +174,7 @@ fn readInFrame(d: *Decompress, w: *Writer, limit: Limit, state: *State.InFrame)
167 var offset_fse_buffer: [zstd.table_size_max.offset]Table.Fse = undefined;174 var offset_fse_buffer: [zstd.table_size_max.offset]Table.Fse = undefined;
168 var literals_buffer: [zstd.block_size_max]u8 = undefined;175 var literals_buffer: [zstd.block_size_max]u8 = undefined;
169 var sequence_buffer: [zstd.block_size_max]u8 = undefined;176 var sequence_buffer: [zstd.block_size_max]u8 = undefined;
170 var decode: Frame.Zstandard.Decode = .init(&literal_fse_buffer, &match_fse_buffer, &offset_fse_buffer);177 var decode: Frame.Zstandard.Decode = .init(&literal_fse_buffer, &match_fse_buffer, &offset_fse_buffer, window_len);
171 var remaining: Limit = .limited(block_size);178 var remaining: Limit = .limited(block_size);
172 const literals = try LiteralsSection.decode(in, &remaining, &literals_buffer);179 const literals = try LiteralsSection.decode(in, &remaining, &literals_buffer);
173 const sequences_header = try SequencesSection.Header.decode(in, &remaining);180 const sequences_header = try SequencesSection.Header.decode(in, &remaining);
...@@ -185,15 +192,16 @@ fn readInFrame(d: *Decompress, w: *Writer, limit: Limit, state: *State.InFrame)...@@ -185,15 +192,16 @@ fn readInFrame(d: *Decompress, w: *Writer, limit: Limit, state: *State.InFrame)
185 try decode.readInitialFseState(&bit_stream);192 try decode.readInitialFseState(&bit_stream);
186193
187 // Ensures the following calls to `decodeSequence` will not flush.194 // Ensures the following calls to `decodeSequence` will not flush.
188 if (frame_block_size_max > w.buffer.len) return error.OutputBufferUndersize;195 if (window_len + frame_block_size_max > w.buffer.len) return error.OutputBufferUndersize;
189 const dest = (try w.writableSliceGreedy(frame_block_size_max))[0..frame_block_size_max];196 const dest = (try w.writableSliceGreedyPreserve(window_len, frame_block_size_max))[0..frame_block_size_max];
197 const write_pos = dest.ptr - w.buffer.ptr;
190 for (0..sequences_header.sequence_count - 1) |_| {198 for (0..sequences_header.sequence_count - 1) |_| {
191 bytes_written += try decode.decodeSequence(dest, bytes_written, &bit_stream);199 bytes_written += try decode.decodeSequence(w.buffer, write_pos + bytes_written, &bit_stream);
192 try decode.updateState(.literal, &bit_stream);200 try decode.updateState(.literal, &bit_stream);
193 try decode.updateState(.match, &bit_stream);201 try decode.updateState(.match, &bit_stream);
194 try decode.updateState(.offset, &bit_stream);202 try decode.updateState(.offset, &bit_stream);
195 }203 }
196 bytes_written += try decode.decodeSequence(dest, bytes_written, &bit_stream);204 bytes_written += try decode.decodeSequence(w.buffer, write_pos + bytes_written, &bit_stream);
197 if (bytes_written > dest.len) return error.MalformedSequence;205 if (bytes_written > dest.len) return error.MalformedSequence;
198 w.advance(bytes_written);206 w.advance(bytes_written);
199 }207 }
...@@ -363,6 +371,7 @@ pub const Frame = struct {...@@ -363,6 +371,7 @@ pub const Frame = struct {
363 };371 };
364372
365 pub const Decode = struct {373 pub const Decode = struct {
374 window_len: u32,
366 repeat_offsets: [3]u32,375 repeat_offsets: [3]u32,
367376
368 offset: StateData(8),377 offset: StateData(8),
...@@ -397,8 +406,10 @@ pub const Frame = struct {...@@ -397,8 +406,10 @@ pub const Frame = struct {
397 literal_fse_buffer: []Table.Fse,406 literal_fse_buffer: []Table.Fse,
398 match_fse_buffer: []Table.Fse,407 match_fse_buffer: []Table.Fse,
399 offset_fse_buffer: []Table.Fse,408 offset_fse_buffer: []Table.Fse,
409 window_len: u32,
400 ) Decode {410 ) Decode {
401 return .{411 return .{
412 .window_len = window_len,
402 .repeat_offsets = .{413 .repeat_offsets = .{
403 zstd.start_repeated_offset_1,414 zstd.start_repeated_offset_1,
404 zstd.start_repeated_offset_2,415 zstd.start_repeated_offset_2,
...@@ -698,19 +709,19 @@ pub const Frame = struct {...@@ -698,19 +709,19 @@ pub const Frame = struct {
698 };709 };
699 }710 }
700711
701 /// Decode `len` bytes of literals into `dest`.712 /// Decode `len` bytes of literals into `w`.
702 fn decodeLiterals(self: *Decode, dest: *Writer, len: usize) !void {713 fn decodeLiterals(d: *Decode, w: *Writer, len: usize) !void {
703 switch (self.literal_header.block_type) {714 switch (d.literal_header.block_type) {
704 .raw => {715 .raw => {
705 try dest.writeAll(self.literal_streams.one[self.literal_written_count..][0..len]);716 try w.writeAll(d.literal_streams.one[d.literal_written_count..][0..len]);
706 },717 },
707 .rle => {718 .rle => {
708 try dest.splatByteAll(self.literal_streams.one[0], len);719 try w.splatByteAll(d.literal_streams.one[0], len);
709 },720 },
710 .compressed, .treeless => {721 .compressed, .treeless => {
711 if (len > dest.buffer.len) return error.OutputBufferUndersize;722 if (len > w.buffer.len) return error.OutputBufferUndersize;
712 const buf = try dest.writableSlice(len);723 const buf = try w.writableSlice(len);
713 const huffman_tree = self.huffman_tree.?;724 const huffman_tree = d.huffman_tree.?;
714 const max_bit_count = huffman_tree.max_bit_count;725 const max_bit_count = huffman_tree.max_bit_count;
715 const starting_bit_count = LiteralsSection.HuffmanTree.weightToBitCount(726 const starting_bit_count = LiteralsSection.HuffmanTree.weightToBitCount(
716 huffman_tree.nodes[huffman_tree.symbol_count_minus_one].weight,727 huffman_tree.nodes[huffman_tree.symbol_count_minus_one].weight,
...@@ -722,7 +733,7 @@ pub const Frame = struct {...@@ -722,7 +733,7 @@ pub const Frame = struct {
722 for (buf) |*out| {733 for (buf) |*out| {
723 var prefix: u16 = 0;734 var prefix: u16 = 0;
724 while (true) {735 while (true) {
725 const new_bits = try self.readLiteralsBits(bit_count_to_read);736 const new_bits = try d.readLiteralsBits(bit_count_to_read);
726 prefix <<= bit_count_to_read;737 prefix <<= bit_count_to_read;
727 prefix |= new_bits;738 prefix |= new_bits;
728 bits_read += bit_count_to_read;739 bits_read += bit_count_to_read;