Type Function Decompressor [src]

Prototype

pub fn Decompressor(comptime ReaderType: type) type

Parameters

ReaderType: type

Source

pub fn Decompressor(comptime ReaderType: type) type { return struct { const Self = @This(); const table_size_max = types.compressed_block.table_size_max; source: std.io.CountingReader(ReaderType), state: enum { NewFrame, InFrame, LastBlock }, decode_state: decompress.block.DecodeState, frame_context: decompress.FrameContext, buffer: WindowBuffer, literal_fse_buffer: [table_size_max.literal]types.compressed_block.Table.Fse, match_fse_buffer: [table_size_max.match]types.compressed_block.Table.Fse, offset_fse_buffer: [table_size_max.offset]types.compressed_block.Table.Fse, literals_buffer: [types.block_size_max]u8, sequence_buffer: [types.block_size_max]u8, verify_checksum: bool, checksum: ?u32, current_frame_decompressed_size: usize, const WindowBuffer = struct { data: []u8 = undefined, read_index: usize = 0, write_index: usize = 0, }; pub const Error = ReaderType.Error || error{ ChecksumFailure, DictionaryIdFlagUnsupported, MalformedBlock, MalformedFrame, OutOfMemory, }; pub const Reader = std.io.Reader(*Self, Error, read); pub fn init(source: ReaderType, options: DecompressorOptions) Self { return .{ .source = std.io.countingReader(source), .state = .NewFrame, .decode_state = undefined, .frame_context = undefined, .buffer = .{ .data = options.window_buffer }, .literal_fse_buffer = undefined, .match_fse_buffer = undefined, .offset_fse_buffer = undefined, .literals_buffer = undefined, .sequence_buffer = undefined, .verify_checksum = options.verify_checksum, .checksum = undefined, .current_frame_decompressed_size = undefined, }; } fn frameInit(self: *Self) !void { const source_reader = self.source.reader(); switch (try decompress.decodeFrameHeader(source_reader)) { .skippable => |header| { try source_reader.skipBytes(header.frame_size, .{}); self.state = .NewFrame; }, .zstandard => |header| { const frame_context = try decompress.FrameContext.init( header, self.buffer.data.len, self.verify_checksum, ); const decode_state = decompress.block.DecodeState.init( &self.literal_fse_buffer, &self.match_fse_buffer, &self.offset_fse_buffer, ); self.decode_state = decode_state; self.frame_context = frame_context; self.checksum = null; self.current_frame_decompressed_size = 0; self.state = .InFrame; }, } } pub fn reader(self: *Self) Reader { return .{ .context = self }; } pub fn read(self: *Self, buffer: []u8) Error!usize { if (buffer.len == 0) return 0; var size: usize = 0; while (size == 0) { while (self.state == .NewFrame) { const initial_count = self.source.bytes_read; self.frameInit() catch |err| switch (err) { error.DictionaryIdFlagUnsupported => return error.DictionaryIdFlagUnsupported, error.EndOfStream => return if (self.source.bytes_read == initial_count) 0 else error.MalformedFrame, else => return error.MalformedFrame, }; } size = try self.readInner(buffer); } return size; } fn readInner(self: *Self, buffer: []u8) Error!usize { std.debug.assert(self.state != .NewFrame); var ring_buffer = RingBuffer{ .data = self.buffer.data, .read_index = self.buffer.read_index, .write_index = self.buffer.write_index, }; defer { self.buffer.read_index = ring_buffer.read_index; self.buffer.write_index = ring_buffer.write_index; } const source_reader = self.source.reader(); while (ring_buffer.isEmpty() and self.state != .LastBlock) { const header_bytes = source_reader.readBytesNoEof(3) catch return error.MalformedFrame; const block_header = decompress.block.decodeBlockHeader(&header_bytes); decompress.block.decodeBlockReader( &ring_buffer, source_reader, block_header, &self.decode_state, self.frame_context.block_size_max, &self.literals_buffer, &self.sequence_buffer, ) catch return error.MalformedBlock; if (self.frame_context.content_size) |size| { if (self.current_frame_decompressed_size > size) return error.MalformedFrame; } const size = ring_buffer.len(); self.current_frame_decompressed_size += size; if (self.frame_context.hasher_opt) |*hasher| { if (size > 0) { const written_slice = ring_buffer.sliceLast(size); hasher.update(written_slice.first); hasher.update(written_slice.second); } } if (block_header.last_block) { self.state = .LastBlock; if (self.frame_context.has_checksum) { const checksum = source_reader.readInt(u32, .little) catch return error.MalformedFrame; if (self.verify_checksum) { if (self.frame_context.hasher_opt) |*hasher| { if (checksum != decompress.computeChecksum(hasher)) return error.ChecksumFailure; } } } if (self.frame_context.content_size) |content_size| { if (content_size != self.current_frame_decompressed_size) { return error.MalformedFrame; } } } } const size = @min(ring_buffer.len(), buffer.len); if (size > 0) { ring_buffer.readFirstAssumeLength(buffer, size); } if (self.state == .LastBlock and ring_buffer.len() == 0) { self.state = .NewFrame; } return size; } }; }