feature. See also
. The project being documented here (as the example) is the Zig library itself.
Decompress.streamInner
fn streamInner(d: *Decompress, w: *Writer, limit: std.Io.Limit) (Error || Reader.StreamError)!usize
File
Code
fn streamInner(d: *Decompress, w: *Writer, limit: std.Io.Limit) (Error || Reader.StreamError)!usize {
var remaining = @backingInt(limit);
const in = d.input;
sw: switch (d.state) {
.protocol_header => switch (d.container_metadata.container()) {
.gzip => {
const Header = extern struct {
magic: u16 align(1),
method: u8,
flags: packed struct(u8) {
text: bool,
hcrc: bool,
extra: bool,
name: bool,
comment: bool,
reserved: u3,
},
mtime: u32 align(1),
xfl: u8,
os: u8,
};
const header = try in.takeStruct(Header, .little);
if (header.magic != 0x8b1f or header.method != 0x08)
return error.BadGzipHeader;
if (header.flags.extra) {
const extra_len = try in.takeInt(u16, .little);
try in.discardAll(extra_len);
}
if (header.flags.name) {
_ = try in.discardDelimiterInclusive(0);
}
if (header.flags.comment) {
_ = try in.discardDelimiterInclusive(0);
}
if (header.flags.hcrc) {
try in.discardAll(2);
}
continue :sw .block_header;
},
.zlib => {
const header = try in.takeArray(2);
const cmf: packed struct(u8) { cm: u4, cinfo: u4 } = @bitCast(header[0]);
if (cmf.cm != 8 or cmf.cinfo > 7) return error.BadZlibHeader;
continue :sw .block_header;
},
.raw => continue :sw .block_header,
},
.block_header => {
d.final_block = (try d.takeIntBits(u1)) != 0;
const block_type: BlockType = @fromBackingInt(@intCast(try d.takeIntBits(u2)));
switch (block_type) {
.stored => {
d.alignBitsForward();
const len = try in.takeInt(u16, .little);
const nlen = try in.takeInt(u16, .little);
if (len != ~nlen) return error.WrongStoredBlockNlen;
continue :sw .{ .stored_block = len };
},
.fixed => continue :sw .fixed_block,
.dynamic => {
const hlit: u16 = @as(u16, try d.takeIntBits(u5)) + 257;
const hdist: u16 = @as(u16, try d.takeIntBits(u5)) + 1;
const hclen: u8 = @as(u8, try d.takeIntBits(u4)) + 4;
if (hlit > 286 or hdist > 30)
return error.InvalidDynamicBlockHeader;
var cl_lens: [19]u4 = @splat(0);
for (token.codegen_order[0..hclen]) |i| {
cl_lens[i] = try d.takeIntBits(u3);
}
var cl_dec: CodegenDecoder = .{};
try cl_dec.generate(&cl_lens);
var dec_lens: [286 + 30]u4 = @splat(0);
var pos: usize = 0;
while (pos < hlit + hdist) {
const peeked = try d.peekIntBitsShort(u7);
const sym = try cl_dec.find(peeked);
try d.tossBitsShort(sym.code_bits);
pos += try d.dynamicCodeLength(sym.value, &dec_lens, pos);
}
if (pos > hlit + hdist) {
return error.InvalidDynamicBlockHeader;
}
try d.lit_dec.generate(dec_lens[0..hlit]);
try d.dst_dec.generate(dec_lens[hlit..][0..hdist]);
continue :sw .dynamic_block;
},
.invalid => return error.InvalidBlockType,
}
},
.stored_block => |remaining_len| {
const out: []u8 = if (remaining != 0)
try w.writableSliceGreedyPreserve(flate.history_len, 1)
else
&.{};
var limited_out: [1][]u8 = .{limit.min(.limited(remaining_len)).slice(out)};
const n = try in.readVec(&limited_out);
if (remaining_len - n == 0) {
d.state = if (d.final_block) .protocol_footer else .block_header;
} else {
d.state = .{ .stored_block = @intCast(remaining_len - n) };
}
w.advance(n);
return @backingInt(limit) - remaining + n;
},
.fixed_block => while (true) {
const sym = try d.readFixedCode();
if (sym >= 256) {
@branchHint(.unlikely);
if (sym == 256) {
@branchHint(.unlikely);
d.state = if (d.final_block) .protocol_footer else .block_header;
continue :sw d.state;
}
const length = try d.decodeLength(@intCast(sym - 257));
continue :sw .{ .fixed_block_match = length };
}
const byte: u8 = @intCast(sym);
if (remaining != 0) {
@branchHint(.likely);
remaining -= 1;
try w.writeBytePreserve(flate.history_len, byte);
} else {
d.state = .{ .fixed_block_literal = byte };
return @backingInt(limit) - remaining;
}
},
.fixed_block_literal => |symbol| {
assert(remaining != 0);
remaining -= 1;
try w.writeBytePreserve(flate.history_len, symbol);
continue :sw .fixed_block;
},
.fixed_block_match => |length| {
if (remaining >= length) {
@branchHint(.likely);
const distance = try d.decodeDistance(@bitReverse(try d.takeIntBits(u5)));
try writeMatch(w, length, distance);
remaining -= length;
continue :sw .fixed_block;
} else {
d.state = .{ .fixed_block_match = length };
return @backingInt(limit) - remaining;
}
},
// decompression performance depends on this logic.
.dynamic_block => while (true) {
const sym = try d.decodeSymbol(&d.lit_dec);
if (sym >= 256) {
@branchHint(.unlikely);
if (sym == 256) {
@branchHint(.unlikely);
d.state = if (d.final_block) .protocol_footer else .block_header;
continue :sw d.state;
}
const length = try d.decodeLength(@intCast(sym - 257));
continue :sw .{ .dynamic_block_match = length };
}
const byte: u8 = @intCast(sym);
if (remaining != 0) {
@branchHint(.likely);
remaining -= 1;
try w.writeBytePreserve(flate.history_len, byte);
} else {
d.state = .{ .dynamic_block_literal = byte };
return @backingInt(limit) - remaining;
}
},
.dynamic_block_literal => |symbol| {
assert(remaining != 0);
remaining -= 1;
try w.writeBytePreserve(flate.history_len, symbol);
continue :sw .dynamic_block;
},
.dynamic_block_match => |length| {
if (remaining >= length) {
@branchHint(.likely);
remaining -= length;
const dsm = try d.decodeSymbol(&d.dst_dec);
const distance = try d.decodeDistance(@intCast(dsm));
try writeMatch(w, length, distance);
continue :sw .dynamic_block;
} else {
d.state = .{ .dynamic_block_match = length };
return @backingInt(limit) - remaining;
}
},
.protocol_footer => {
d.alignBitsForward();
switch (d.container_metadata) {
.gzip => |*gzip| {
gzip.crc = try in.takeInt(u32, .little);
gzip.count = try in.takeInt(u32, .little);
},
.zlib => |*zlib| {
zlib.adler = try in.takeInt(u32, .big);
},
.raw => {},
}
d.state = .end;
return @backingInt(limit) - remaining;
},
.end => return error.EndOfStream,
}
}