// The streaming decoder.
//
// The decoding is `@inflate.Inflater`'s, which zlib and gzip hold too: it
// stops right after the final block, consuming nothing past it, and hands
// out every byte before an error before it raises. This type adds where a
// raw DEFLATE stream ends: under `EndOfInput`, `Done` waits for `end`, and a
// byte after the final block raises `TrailingData` (raw DEFLATE cannot be
// concatenated); under `EndOfMember`, `Done` comes right after the final
// block, and what follows is `input[last_consumed():]`.
///|
priv enum State {
/// The final block has not ended.
Running
/// The final block has ended; under `EndOfInput`, `Done` waits for `end`.
Ended
/// `Done` was answered.
Done
}
///|
/// The history window of RFC 1951: distances reach at most 32 KiB back.
const WINDOW_SIZE : Int32 = 32768
///|
/// A streaming raw DEFLATE decoder (RFC 1951). Feed it input in chunks of any
/// size with `step` and collect output in slices of any size; it holds the
/// 32 KiB window and the Huffman tables of the current block, never the whole
/// input or output.
pub struct Decoder {
priv inflater : @inflate.Inflater
// The last 32 KiB of the preset dictionary, all the window can use: a view
// of the caller's bytes, which are immutable, for `reset`.
priv dictionary : BytesView?
priv until : @codec_bytes.Until
priv mut state : State
priv mut failure : @codec.CodecError?
priv mut step_consumed : Int32
priv mut step_produced : Int32
}
///|
/// A decoder at the start of a stream.
///
/// `dictionary` is a preset dictionary: the stream's matches may reach back
/// into its last 32 KiB as if it had been decoded just before the stream. A
/// zlib stream names the one it needs; raw DEFLATE leaves it to the caller.
/// The decoder keeps a view of the dictionary's last 32 KiB, not a copy, so
/// the bytes the view belongs to stay alive as long as the decoder.
///
/// `until` says where the stream ends. `EndOfInput`, the default, decodes the
/// stream up to the end of the input, and bytes after its final block raise
/// `TrailingData`. `EndOfMember` stops right after the final block and
/// consumes nothing past it, so that `input[last_consumed():]` is what follows:
/// a format that embeds DEFLATE (zlib, gzip) reads its trailer from there.
pub fn Decoder::Decoder(
dictionary? : BytesView,
until~ : @codec_bytes.Until = EndOfInput,
) -> Decoder {
let dictionary = dictionary.map(d => d[(d.length() - WINDOW_SIZE).max(0):])
let inflater = @inflate.Inflater::new()
inflater.reset(dictionary?)
{
inflater,
dictionary,
until,
state: Running,
failure: None,
step_consumed: 0,
step_produced: 0,
}
}
///|
/// Starts a new stream with the same options: the same dictionary, and the
/// same `until`.
pub fn Decoder::reset(self : Decoder) -> Unit {
self.inflater.reset(dictionary?=self.dictionary)
self.state = Running
self.failure = None
self.step_consumed = 0
self.step_produced = 0
}
///|
/// Bytes held for the stream: the 32 KiB window, the Huffman tables of a
/// block and the scratch space they are built with, and the bytes kept aside
/// for a unit cut short. All of it is allocated when the decoder is made, so
/// this is the same from the first step to the last, with a dictionary or
/// without (see RESOURCE_BOUNDS.md). A dictionary is the caller's bytes, kept
/// by reference, and not counted.
pub fn Decoder::memory_usage(self : Decoder) -> Int32 {
self.inflater.memory_usage()
}
///|
/// Whether the stream has ended: its final block is decoded and all its
/// output has been handed out. Under `EndOfInput` this can be true before
/// `Done`, which waits for `end`.
pub fn Decoder::is_finished(self : Decoder) -> Bool {
!(self.state is Running)
}
///|
/// Input bytes the latest `step` accepted: all of them on `NeedMoreInput`.
/// A step that raised accepted the bytes up to the error; for
/// `TrailingData`, exactly up to the end of the stream, or none if the
/// stream ended before this step's input.
pub fn Decoder::last_consumed(self : Decoder) -> Int32 {
self.step_consumed
}
///|
/// Output bytes the latest `step` wrote, a step that raised included: the
/// output before the error.
pub fn Decoder::last_produced(self : Decoder) -> Int32 {
self.step_produced
}
///|
/// Decodes from `input` into `output`; see `last_consumed` and
/// `last_produced` for how much of each it used.
///
/// Pass `end=true` once `input` holds the last bytes and `end=false` before:
/// a stream cut short then raises `Truncated`. Under `until=EndOfInput` a
/// stream whose final block has ended answers `NeedMoreInput` until `end`,
/// and bytes after it raise `TrailingData`; under `until=EndOfMember` it
/// answers `Done` right after the final block, whatever `end` says. After an
/// error the decoder raises it again, consuming and writing nothing, until
/// `reset`.
pub fn Decoder::step(
self : Decoder,
input : BytesView,
output : MutArrayView[Byte],
end~ : Bool,
) -> Status raise @codec.CodecError {
self.step_with(input, output, None, end~)
}
///|
/// `step` into a whole `FixedArray`, with bulk copies.
pub fn Decoder::step_into(
self : Decoder,
input : BytesView,
output : FixedArray[Byte],
end~ : Bool,
) -> Status raise @codec.CodecError {
self.step_with(input, output.mut_view(), Some(output), end~)
}
///|
fn Decoder::step_with(
self : Decoder,
input : BytesView,
output : MutArrayView[Byte],
fixed_output : FixedArray[Byte]?,
end~ : Bool,
) -> Status raise @codec.CodecError {
self.step_consumed = 0
self.step_produced = 0
if self.failure is Some(err) {
raise err
}
match self.state {
Running => {
let status = try {
match fixed_output {
Some(out) => self.inflater.step_into(input, out, end~)
None => self.inflater.step(input, output, end~)
}
} catch {
err =>
self.fail(
err,
self.inflater.last_consumed(),
self.inflater.last_produced(),
)
}
let consumed = self.inflater.last_consumed()
let produced = self.inflater.last_produced()
match status {
Done => {
self.state = Ended
self.ended(input, consumed, produced, end~)
}
NeedMoreInput | NeedMoreOutput =>
self.finish(status, consumed, produced)
}
}
Ended => self.ended(input, 0, 0, end~)
Done => {
if self.until is EndOfInput && input.length() > 0 {
self.fail(
CodecError(TrailingData, "data after the end of the stream"),
0,
0,
)
}
self.finish(Done, 0, 0)
}
}
}
///|
/// The answer of a step in which the final block has ended, `consumed` bytes
/// into `input`.
fn Decoder::ended(
self : Decoder,
input : BytesView,
consumed : Int32,
produced : Int32,
end~ : Bool,
) -> Status raise @codec.CodecError {
match self.until {
EndOfMember => {
self.state = Done
self.finish(Done, consumed, produced)
}
EndOfInput =>
if consumed < input.length() {
self.fail(
CodecError(TrailingData, "data after the final block"),
consumed,
produced,
)
} else if end {
self.state = Done
self.finish(Done, consumed, produced)
} else {
self.finish(NeedMoreInput, consumed, produced)
}
}
}
///|
fn Decoder::finish(
self : Decoder,
status : Status,
consumed : Int32,
produced : Int32,
) -> Status {
self.step_consumed = consumed
self.step_produced = produced
status
}
///|
/// Records a terminal error and raises it, from a step that consumed
/// `consumed` bytes and wrote `produced` before it.
fn[T] Decoder::fail(
self : Decoder,
err : @codec.CodecError,
consumed : Int32,
produced : Int32,
) -> T raise @codec.CodecError {
self.failure = Some(err)
self.step_consumed = consumed
self.step_produced = produced
raise err
}
///|
pub impl @codec_bytes.Decode for Decoder with fn step(self, input, output, end~) {
Decoder::step(self, input, output, end~)
}
///|
pub impl @codec_bytes.Decode for Decoder with fn last_consumed(self) {
Decoder::last_consumed(self)
}
///|
pub impl @codec_bytes.Decode for Decoder with fn last_produced(self) {
Decoder::last_produced(self)
}
///|
pub impl @codec_bytes.Decode for Decoder with fn reset(self) {
Decoder::reset(self)
}
///|
pub impl @codec_bytes.Decode for Decoder with fn memory_usage(self) {
Decoder::memory_usage(self)
}