///|
/// Decodes a whole raw DEFLATE stream held in memory. `max_output` caps the
/// decoded size (unlimited by default) and raises
/// `LimitExceeded({ resource: Output, .. })` past it; bytes after the final
/// block raise `TrailingData`.
pub fn decompress(
  input : BytesView,
  max_output? : Int32,
) -> Bytes raise @codec.CodecError {
  let decoder = Decoder()
  let chunk = FixedArray::make(1 << 16, b'\x00')
  let out = Buffer()
  let mut rest = input
  for ;; {
    let status = decoder.step_into(rest, chunk, end=true)
    rest = rest[decoder.last_consumed():]
    let produced = decoder.last_produced()
    // `out` holds at most `limit` bytes, so this cannot overflow.
    if max_output is Some(limit) && produced > limit - out.length() {
      raise CodecError(
        LimitExceeded({
          resource: Output,
          needed: None,
          limit: limit.max(0).to_uint64(),
        }),
        "the output exceeds \{limit} bytes",
      )
    }
    out.write_bytesview(Bytes::from_array(chunk[:produced])[:])
    if status is Done {
      return out.to_bytes()
    }
  }
}

///|
/// Compresses `input` into a whole raw DEFLATE stream, at `level` (0 to 9,
/// clamped; 6 by default) and with the preset `dictionary` if given, as
/// `Encoder` does.
pub fn compress(
  input : BytesView,
  level~ : Int32 = 6,
  dictionary? : BytesView,
) -> Bytes {
  let encoder = Encoder(level~, dictionary?)
  let chunk = FixedArray::make(1 << 16, b'\x00')
  let out = Buffer()
  let mut rest = input
  for ;; {
    let status = encoder.step_into(rest, chunk, action=Finish)
    rest = rest[encoder.last_consumed():]
    out.write_bytesview(Bytes::from_array(chunk[:encoder.last_produced()])[:])
    if status is Done {
      return out.to_bytes()
    }
  }
}