feature. See also
. The project being documented here (as the example) is the Zig library itself.
Decompress.readInFrame
fn readInFrame(d: *Decompress, w: *Writer, limit: Limit, state: *State.InFrame) !usize
File
Code
fn readInFrame(d: *Decompress, w: *Writer, limit: Limit, state: *State.InFrame) !usize {
const in = d.input;
const window_len = d.window_len;
const block_header = try in.takeStruct(Frame.Zstandard.Block.Header, .little);
const block_size = block_header.size;
const frame_block_size_max = state.frame.block_size_max;
if (frame_block_size_max < block_size) return error.BlockOversize;
if (@backingInt(limit) < block_size) return error.OutputBufferUndersize;
var bytes_written: usize = 0;
switch (block_header.type) {
.raw => {
try in.streamExactPreserve(w, window_len, block_size);
bytes_written = block_size;
},
.rle => {
const byte = try in.takeByte();
try w.splatBytePreserve(window_len, byte, block_size);
bytes_written = block_size;
},
.compressed => {
var literals_buffer: [zstd.block_size_max]u8 = undefined;
var sequence_buffer: [zstd.block_size_max]u8 = undefined;
var remaining: Limit = .limited(block_size);
const literals = try LiteralsSection.decode(in, &remaining, &literals_buffer);
const sequences_header = try SequencesSection.Header.decode(in, &remaining);
const decode = &state.decode;
try decode.prepare(in, &remaining, literals, sequences_header);
{
if (sequence_buffer.len < @backingInt(remaining))
return error.SequenceBufferUndersize;
const seq_slice = remaining.slice(&sequence_buffer);
try in.readSliceAll(seq_slice);
var bit_stream = try ReverseBitReader.init(seq_slice);
if (sequences_header.sequence_count > 0) {
try decode.readInitialFseState(&bit_stream);
const dest = (try w.writableSliceGreedyPreserve(window_len, frame_block_size_max))[0..frame_block_size_max];
const write_pos = dest.ptr - w.buffer.ptr;
for (0..sequences_header.sequence_count - 1) |_| {
bytes_written += try decode.decodeSequence(w.buffer, write_pos + bytes_written, &bit_stream);
try decode.updateState(.literal, &bit_stream);
try decode.updateState(.match, &bit_stream);
try decode.updateState(.offset, &bit_stream);
}
bytes_written += try decode.decodeSequence(w.buffer, write_pos + bytes_written, &bit_stream);
if (bytes_written > dest.len) return error.MalformedSequence;
w.advance(bytes_written);
}
if (!bit_stream.isEmpty()) {
return error.MalformedCompressedBlock;
}
}
if (decode.literal_written_count < literals.header.regenerated_size) {
const len = literals.header.regenerated_size - decode.literal_written_count;
try decode.decodeLiterals(w, len);
decode.literal_written_count += len;
bytes_written += len;
}
switch (decode.literal_header.block_type) {
.treeless, .compressed => {
if (!decode.isLiteralStreamEmpty()) return error.MalformedCompressedBlock;
},
.raw, .rle => {},
}
if (bytes_written > frame_block_size_max) return error.BlockOversize;
},
.reserved => return error.ReservedBlock,
}
if (state.frame.hasher_opt) |*hasher| {
if (bytes_written > 0) {
_ = hasher;
@panic("TODO all those bytes written needed to go through the hasher too");
}
}
state.decompressed_size += bytes_written;
if (block_header.last) {
if (state.frame.has_checksum) {
const expected_checksum = try in.takeInt(u32, .little);
if (state.frame.hasher_opt) |*hasher| {
const actual_checksum: u32 = @truncate(hasher.final());
if (expected_checksum != actual_checksum) return error.ChecksumFailure;
}
}
if (state.frame.content_size) |content_size| {
if (content_size != state.decompressed_size) {
return error.MalformedFrame;
}
}
d.state = .new_frame;
} else if (state.frame.content_size) |content_size| {
if (state.decompressed_size > content_size) return error.MalformedFrame;
}
return bytes_written;
}