///|
/// This file is based on the Go implementation found here:
/// https://cs.opensource.google/go/go/+/refs/tags/go1.23.3:src/compress/zlib/reader.go
/// which has the copyright notice:
/// Copyright 2009 The Go Authors. All rights reserved.
/// Use of this source code is governed by a BSD-style
/// license that can be found in the LICENSE file.
///
/// Package zlib implements reading and writing of zlib format compressed data,
/// as specified in RFC 1950.
///
/// The implementation provides filters that uncompress during reading
/// and compress during writing. For example, to write compressed data
/// to a buffer:
///
/// var b bytes.Buffer
/// w := zlib.NewWriter(&b)
/// w.Write(Slice[Byte]("hello, world\n"))
/// w.close()
///
/// and to read that data back:
///
/// r, err := zlib.Reader::new(&b)
/// io.Copy(os.Stdout, r)
/// r.close()
const ZLIB_DEFLATE = b'\x08'
///|
const ZLIB_MAX_WINDOW = b'\x07'
///|
using @io {type Slice}
///|
using @io {type IOError}
///|
/// err_checksum is returned when reading ZLIB data that has an invalid checksum.
pub let err_checksum : IOError = IOError("zlib: invalid checksum")
///|
/// err_dictionary is returned when reading ZLIB data that has an invalid dictionary.
pub let err_dictionary : IOError = IOError("zlib: invalid dictionary")
///|
/// err_header is returned when reading ZLIB data that has an invalid header.
pub let err_header : IOError = IOError("zlib: invalid header")
///|
struct Reader {
mut r : &@flate.Reader
mut decompressor : &@io.ReadCloser
mut digest : &@hash.Hash32
mut err : IOError?
mut scratch : Slice[Byte] // [4]byte
}
///|
/// Resetter resets a ReadCloser returned by [Reader::new] or [Reader::new_dict]
/// to switch to a new underlying Reader. This permits reusing a ReadCloser
/// instead of allocating a new one.
pub(open) trait Resetter {
/// reset discards any buffered data and resets the Resetter as if it was
/// newly initialized with the given reader.
reset(Self, &@flate.Reader, Slice[Byte]) -> IOError?
}
///|
/// Reader::new creates a new ReadCloser.
/// Reads from the returned ReadCloser read and decompress data from r.
/// If r does not implement [io.ByteReader], the decompressor may read more
/// data than necessary from r.
/// It is the caller's responsibility to call close on the ReadCloser when done.
///
/// The [io.ReadCloser] returned by Reader::new also implements [Resetter].
pub fn Reader::new(r : &@flate.Reader) -> (&@io.ReadCloser, IOError?) {
Reader::new_dict(r, Slice::new([]))
}
///|
/// Reader::new_dict is like [Reader::new] but uses a preset dictionary.
/// Reader::new_dict ignores the dictionary if the compressed data does not refer to it.
/// If the compressed data refers to a different dictionary, Reader::new_dict returns [err_dictionary].
///
/// The ReadCloser returned by Reader::new_dict also implements [Resetter].
pub fn Reader::new_dict(
r : &@flate.Reader,
dict : Slice[Byte],
) -> (&@io.ReadCloser, IOError?) {
let z : Reader = {
r,
decompressor: &@flate.Reader::new(r),
digest: @adler32.new(),
err: None,
scratch: Slice::new([0, 0, 0, 0]),
}
let err = z.reset(r, dict)
(z, err)
}
///|
pub impl @io.Reader for Reader with read(self, p) {
if self.err != None {
return (0, self.err)
}
let (n, err) = self.decompressor.read(p)
self.err = err
guard self.digest.write(p[0:n]) is (_, None)
if self.err != Some(@io.eof) {
// In the normal case we return here.
return (n, self.err)
}
// Finished file; check checksum.
let (_, err) = @io.read_full(self.r, self.scratch)
if err != None {
let mut err = err
if err == Some(@io.eof) {
err = Some(@io.err_unexpected_eof)
}
self.err = err
return (n, self.err)
}
// ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952).
let checksum = be_uint32(self.scratch)
if checksum != self.digest.sum32() {
self.err = Some(err_checksum)
return (n, self.err)
}
(n, Some(@io.eof))
}
///|
fn be_uint16(b : Slice[Byte]) -> UInt {
(b[0].to_uint() << 8) | b[1].to_uint()
}
///|
fn be_uint32(b : Slice[Byte]) -> UInt {
(b[0].to_uint() << 24) |
(b[1].to_uint() << 16) |
(b[2].to_uint() << 8) |
b[3].to_uint()
}
///|
/// Calling close does not close the wrapped [io.Reader] originally passed to [Reader::new].
/// In order for the ZLIB checksum to be verified, the reader must be
/// fully consumed until the [io.eof].
pub impl @io.Closer for Reader with close(self) {
if self.err != None && self.err != Some(@io.eof) {
return self.err
}
self.err = self.decompressor.close()
self.err
}
///|
pub impl @io.ReadCloser for Reader
///|
pub impl Resetter for Reader with reset(self, r, dict) {
self.r = r
self.digest = @adler32.new()
self.err = None
self.scratch = Slice::new([0, 0, 0, 0])
// if fr, ok := r.(flate.Reader); ok {
// self.r = fr
// } else {
// self.r = bufio.Reader::new(r)
// }
// Read the header (RFC 1950 section 2.2.).
let (_, err) = @io.read_full(self.r, self.scratch[0:2])
self.err = err
if self.err != None {
if self.err == Some(@io.eof) {
self.err = Some(@io.err_unexpected_eof)
}
return self.err
}
let h = be_uint16(self.scratch[:2])
if (self.scratch[0] & 0x0f) != ZLIB_DEFLATE ||
self.scratch[0] >> 4 > ZLIB_MAX_WINDOW ||
h % 31 != 0 {
self.err = Some(err_header)
return self.err
}
let have_dict = (self.scratch[1] & 0x20) != 0
if have_dict {
let (_, err) = @io.read_full(self.r, self.scratch)
self.err = err
if self.err != None {
if self.err == Some(@io.eof) {
self.err = Some(@io.err_unexpected_eof)
}
return self.err
}
let checksum = be_uint32(self.scratch)
if checksum != @adler32.checksum(dict) {
self.err = Some(err_dictionary)
return self.err
}
}
//
// if self.decompressor == None {
if have_dict {
self.decompressor = &@flate.Reader::new_dict(self.r, dict)
} else {
self.decompressor = &@flate.Reader::new(self.r)
}
// } else {
// self.decompressor.(flate.Resetter).reset(self.r, dict)
// }
None
}