///|
/// Write Zlib header
fn zlh(c : FixedArray[Byte], o : ZlibOptions) -> Unit {
let lv = o.level
let fl_val = if lv == 0 {
0
} else if lv < 6 {
1
} else if lv == 9 {
3
} else {
2
}
c[0] = b'\x78' // CMF: CM=8, CINFO=7
let has_dict = match o.dictionary {
Some(_) => 32
None => 0
}
c[1] = ((fl_val << 6) | has_dict).to_byte()
// fix check bits
let check = ((c[0].to_int() << 8) | c[1].to_int()) % 31
c[1] = (c[1].to_int() | (31 - check)).to_byte()
match o.dictionary {
Some(dict) => {
let h = AdlerState::new()
h.push(dict)
let dv = h.digest().reinterpret_as_int()
wbytes(c, 2, dv)
}
None => ()
}
}
///|
/// Parse Zlib header and return data start offset
fn zls(d : FixedArray[Byte], has_dict : Bool) -> Int raise FzipError {
if d.length() < 2 {
raise fzip_err(InvalidHeader, msg="invalid zlib data")
}
if (d[0].to_int() & 15) != 8 ||
d[0].to_int() >> 4 > 7 ||
((d[0].to_int() << 8) | d[1].to_int()) % 31 != 0 {
raise fzip_err(InvalidHeader, msg="invalid zlib data")
}
let dict_flag = (d[1].to_int() >> 5) & 1
if dict_flag == 1 && !has_dict {
raise fzip_err(InvalidHeader, msg="invalid zlib data: need dictionary")
}
if dict_flag == 0 && has_dict {
raise fzip_err(
InvalidHeader,
msg="invalid zlib data: unexpected dictionary",
)
}
// header size: 2 bytes + optional 4 bytes for dict checksum
let extra = if ((d[1].to_int() >> 3) & 4) != 0 { 4 } else { 0 }
extra + 2
}
///|
/// Compress data into a Zlib stream.
///
/// The output contains a Zlib header, a raw DEFLATE payload, and an Adler-32
/// checksum footer. If `ZlibOptions.dictionary` is set, the dictionary checksum
/// is written into the header and callers must provide the same dictionary when
/// decompressing.
pub fn zlib_sync(
data : FixedArray[Byte],
opts? : ZlibOptions = ZlibOptions::default(),
) -> FixedArray[Byte] {
let a = AdlerState::new()
let pre = match opts.dictionary {
Some(_) => 6
None => 2
}
let (d, len) = dopt(
data,
{ level: opts.level, mem: opts.mem, dictionary: opts.dictionary },
pre,
4,
None,
adler_state=Some(a),
)
zlh(d, opts)
wbytes(d, len - 4, a.digest().reinterpret_as_int())
trim_buf(d, len)
}
///|
/// Decompress a Zlib stream.
///
/// The Zlib header is validated before inflating the inner DEFLATE payload. The
/// Adler-32 footer is verified by default and can be disabled with
/// `UnzlibOptions.verify_checksum` for trusted inputs. For dictionary streams,
/// fzip checks that a dictionary is expected but does not compare the header's
/// dictionary checksum with the provided dictionary.
pub fn unzlib_sync(
data : FixedArray[Byte],
opts? : UnzlibOptions = UnzlibOptions::default(),
) -> FixedArray[Byte] raise FzipError {
let has_dict = match opts.dictionary {
Some(_) => true
None => false
}
let st = zls(data, has_dict)
let end = data.length() - 4
if end < st {
raise fzip_err(InvalidHeader, msg="invalid zlib data")
}
let adler_st : AdlerState? = if opts.verify_checksum {
Some(AdlerState::new())
} else {
None
}
let (buf, len) = inflt(
data,
InflateState::new(2),
opts.out,
opts.dictionary,
opts.max_input_size,
opts.max_output_size,
dat_off=st,
dat_end=end,
adler_state=adler_st,
)
match adler_st {
Some(a_s) => {
let expected_adler = b4(data, end)
if a_s.digest() != expected_adler {
raise fzip_err(InvalidChecksum, msg="zlib Adler-32 mismatch")
}
}
None => ()
}
trim_buf(buf, len)
}