// Frames and blocks (RFC 8878 sections 3.1 and 3.1.1.2): the units of the
// state machine outside compressed blocks.
///|
/// The magic number that starts every frame, or a skippable one.
fn Decoder::magic(self : Decoder) -> Unit raise Stop {
self.fill(4)
let magic = self.hdr_le(0, 4).to_uint()
if magic == FRAME_MAGIC {
self.hdr_len = 0
self.state = FrameHeader
} else if (magic & 0xFFFFFFF0) == 0x184D2A50 {
self.state = SkippableSize
} else if self.frames == 0 {
raise corrupt("not a zstd stream")
} else {
// The stream ends where these four bytes begin, if in this step's input.
self.in_pos = (self.in_pos - self.hdr_len).max(0)
raise Fail(TrailingData, "data after the last frame that starts no frame")
}
}
///|
fn Decoder::skippable_size(self : Decoder) -> Unit raise Stop {
self.fill(8)
self.remaining = self.hdr_le(4, 4).reinterpret_as_int64()
self.hdr_len = 0
self.state = SkippableData
}
///|
/// Skips a skippable frame's data without keeping it.
fn Decoder::skippable_data(self : Decoder) -> Unit raise Stop {
let take = self.remaining.min((self.view.length() - self.in_pos).to_int64())
self.in_pos += take.to_int()
self.remaining -= take
if self.remaining > 0 {
raise NeedInput
}
self.end_member()
}
///|
/// The frame header: what the frame holds and the window it needs.
fn Decoder::frame_header(self : Decoder) -> Unit raise Stop {
self.fill(1)
let len = checked(@header.frame_header_len(self.hdr[0].to_int()))
self.fill(len)
let header = checked(@header.parse_frame_header(self.hdr[0:len]))
if header.dictionary_id != 0 {
let id = header.dictionary_id
raise Fail(DictionaryRequired(Some(id)), "the frame needs dictionary \{id}")
}
self.content_size = header.content_size
// Computed in 64 bits: a descriptor may ask for up to 2^41 + 7 * 2^38.
let window = header.window_size
let limit = 1UL << self.max_window_bits
if window > limit {
raise Fail(
LimitExceeded({ resource: Memory, needed: Some(window), limit, }),
"the frame needs a \{window}-byte window, over the \{limit}-byte limit",
)
}
self.window = window.to_int()
self.block_max = self.window.min(MAX_BLOCK_SIZE)
self.has_checksum = header.has_checksum
self.frame_out = 0
self.xxh.reset()
self.huffman_valid = false
self.ll_table = None
self.of_table = None
self.ml_table = None
self.rep[0] = 1
self.rep[1] = 4
self.rep[2] = 8
self.new_frame = true
self.hdr_len = 0
self.state = BlockHeader
}
///|
/// Sets the window up for a new frame once the last frame's output is all
/// handed out; false until then.
fn Decoder::start_window(self : Decoder) -> Bool {
if self.pending > 0 {
self.drain()
if self.pending > 0 {
return false
}
}
if self.ring.length() > self.window {
self.ring = FixedArray::make(0, b'\x00')
}
self.ring_max = self.window
self.wpos = 0
self.filled = 0
self.new_frame = false
true
}
///|
fn Decoder::block_header(self : Decoder) -> Status? raise Stop {
if self.new_frame && !self.start_window() {
return Some(NeedMoreOutput)
}
self.fill(3)
let value = self.hdr_le(0, 3).to_int()
self.hdr_len = 0
let header = checked(
@header.parse_block_header(value).bind(h => h.check(self.block_max)),
)
let size = header.size
self.last_block = header.last
self.block_size = size
self.block_out = 0
match header.block_type {
Raw => {
self.remaining = size.to_int64()
self.state = RawBlock
}
Rle => self.state = RleByte
Compressed => {
if self.block.length() < size {
self.block = FixedArray::make(size, b'\x00')
}
self.block_len = 0
self.state = CompressedBlock
}
}
None
}
///|
fn Decoder::raw_block(self : Decoder) -> Status? raise Stop {
while self.remaining > 0 {
let available = self.view.length() - self.in_pos
if available == 0 {
raise NeedInput
}
let n = self.room(self.remaining.to_int().min(available))
if n == 0 {
return Some(NeedMoreOutput)
}
self.copy_input(n)
self.remaining -= n.to_int64()
}
self.end_block()
None
}
///|
fn Decoder::rle_byte(self : Decoder) -> Unit raise Stop {
self.fill(1)
self.rle_byte = self.hdr[0]
self.hdr_len = 0
self.remaining = self.block_size.to_int64()
self.state = RleRun
}
///|
fn Decoder::rle_run(self : Decoder) -> Status? raise Stop {
while self.remaining > 0 {
let n = self.room(self.remaining.to_int())
if n == 0 {
return Some(NeedMoreOutput)
}
self.put_run(self.rle_byte, n)
self.remaining -= n.to_int64()
}
self.end_block()
None
}
///|
/// Gathers a compressed block, then decodes it once earlier output is all
/// handed out.
fn Decoder::compressed_block(self : Decoder) -> Status? raise Stop {
let take = (self.block_size - self.block_len).min(
self.view.length() - self.in_pos,
)
if take > 0 {
self.block.blit_from_bytesview(
self.block_len,
self.view[self.in_pos:self.in_pos + take],
)
self.block_len += take
self.in_pos += take
}
if self.block_len < self.block_size {
raise NeedInput
}
if self.pending > 0 {
self.drain()
if self.pending > 0 {
return Some(NeedMoreOutput)
}
}
self.decode_block()
self.end_block()
None
}
///|
fn Decoder::end_block(self : Decoder) -> Unit raise Stop {
if self.content_size is Some(size) && self.frame_out > size {
raise corrupt("the frame holds more than its declared content size")
}
if !self.last_block {
self.state = BlockHeader
} else if self.has_checksum {
self.state = Checksum
} else {
self.end_frame()
}
}
///|
fn Decoder::checksum(self : Decoder) -> Unit raise Stop {
self.fill(4)
let stored = self.hdr_le(0, 4).to_uint()
self.hdr_len = 0
if stored != self.xxh.digest().to_uint() {
raise Fail(ChecksumMismatch, "the frame's content checksum does not match")
}
self.end_frame()
}
///|
fn Decoder::end_frame(self : Decoder) -> Unit raise Stop {
if self.content_size is Some(size) && self.frame_out != size {
raise corrupt("the frame holds less than its declared content size")
}
self.end_member()
}
///|
/// Counts a whole frame, skippable or not. Under `EndOfMember` the stream
/// ends here; otherwise another frame may follow.
fn Decoder::end_member(self : Decoder) -> Unit {
self.frames += 1
self.state = if self.until is EndOfMember { Done } else { Magic }
}