// Block planning for the streaming encoder. Input lives in a fixed mirrored
// ring: the first half is indexed by absolute position modulo 64 KiB and the
// second half mirrors it for branch-free SIMD comparisons across the wrap.
// The parser and its 32 KiB matchfinder survive every block boundary.
///|
/// Bytes of unconsumed lookahead reserved beyond a non-final block boundary so
/// matches there see their full extent.
pub const MIN_LOOKAHEAD : Int = 258
///|
fn Deflater::pending_bytes(self : Deflater) -> Int {
self.pending_end - self.parse_pos
}
///|
fn Deflater::push_pending_byte(self : Deflater, byte : Byte) -> Unit {
let offset = self.pending_end & STREAM_RING_MASK
self.pending[offset] = byte
self.pending[offset + STREAM_RING_SIZE] = byte
self.pending_end = self.pending_end + 1
}
///|
fn Deflater::write_stored_pending(self : Deflater, is_final~ : Bool) -> Unit {
write_stored_window(
self.w,
self.pending,
self.parse_pos,
self.pending_end,
is_final~,
)
self.parse_pos = self.pending_end
}
///|
// The final two bytes of an earlier accepted chunk could not yet form a
// 3-byte hash. Insert them once new lookahead makes them hashable; all older
// history is already present in the persistent chain.
fn Deflater::chain_ready_history(self : Deflater) -> Unit {
let ready_end = self.parse_pos.min((self.pending_end - 2).max(0))
while self.hashed_through < ready_end {
insert_window_pos(
self.pending,
self.finder,
self.hashed_through,
self.pending_end,
)
self.hashed_through = self.hashed_through + 1
}
}
///|
fn Deflater::record_chained_positions(self : Deflater, processed : Int) -> Unit {
let chained_end = processed.min((self.pending_end - 2).max(0))
if chained_end > self.hashed_through {
self.hashed_through = chained_end
}
}
///|
/// Emit one non-final parsed block. `pending` already includes the lookahead
/// tail, and `tokenize_window` only commits `DEFLATE_BLOCK_SIZE` bytes except
/// for an ordinary 258-byte match overhang.
fn Deflater::emit_capped(self : Deflater) -> Unit {
let target = (self.parse_pos + DEFLATE_BLOCK_SIZE).min(self.pending_end)
let tokens : Array[Int] = []
let ll_freq = FixedArray::make(286, 0)
let d_freq = FixedArray::make(30, 0)
self.chain_ready_history()
let processed = tokenize_window(
self.pending,
self.finder,
self.parse_pos,
target,
self.pending_end,
self.cfg,
tokens,
ll_freq,
d_freq,
)
emit_window_block(
self.w,
self.pending,
self.parse_pos,
processed,
tokens[:],
ll_freq,
d_freq,
is_final=false,
)
self.parse_pos = processed
self.record_chained_positions(processed)
self.maybe_rebase()
}
///|
/// Level 0 bypasses parsing entirely and writes buffered bytes straight out of
/// the ring. With no matches, neither matchfinder nor history needs updates.
fn Deflater::emit_stored_all(self : Deflater, is_final~ : Bool) -> Unit {
self.write_stored_pending(is_final~)
self.parse_pos = 0
self.pending_end = 0
}
///|
fn Deflater::emit_one(self : Deflater) -> Unit {
guard self.level != 0 else {
self.emit_stored_all(is_final=false)
return
}
self.emit_capped()
}
///|
/// Tokenize all remaining input into one trailing block. The end bound removes
/// the non-final lookahead restriction while preserving the same parse policy.
fn Deflater::emit_trailing_block(self : Deflater, is_final~ : Bool) -> Int {
let tokens : Array[Int] = []
let ll_freq = FixedArray::make(286, 0)
let d_freq = FixedArray::make(30, 0)
self.chain_ready_history()
let processed = tokenize_window(
self.pending,
self.finder,
self.parse_pos,
self.pending_end,
self.pending_end,
self.cfg,
tokens,
ll_freq,
d_freq,
)
emit_window_block(
self.w,
self.pending,
self.parse_pos,
processed,
tokens[:],
ll_freq,
d_freq,
is_final~,
)
self.parse_pos = processed
self.record_chained_positions(processed)
self.maybe_rebase()
processed
}
///|
/// Keep absolute positions comfortably below backend integer limits. This is
/// amortized over 1 GiB of source and preserves every live 32 KiB chain link.
fn Deflater::maybe_rebase(self : Deflater) -> Unit {
let rebase_at = 1 << 30
guard self.parse_pos >= rebase_at else { return }
let offset = (self.parse_pos / STREAM_RING_SIZE - 1) * STREAM_RING_SIZE
self.finder.rebase(offset)
self.parse_pos = self.parse_pos - offset
self.pending_end = self.pending_end - offset
self.hashed_through = self.hashed_through - offset
}
///|
fn Deflater::emit_final(self : Deflater) -> Unit {
if self.level == 0 {
self.emit_stored_all(is_final=true)
} else {
while self.pending_bytes() > DEFLATE_BLOCK_SIZE {
self.emit_capped()
}
ignore(self.emit_trailing_block(is_final=true))
}
self.w.flush()
self.emitted_final = true
}
///|
fn Deflater::flush_stored(self : Deflater) -> Unit {
if self.pending_bytes() > 0 {
self.emit_stored_all(is_final=false)
}
}
///|
fn Deflater::flush_capped(self : Deflater) -> Unit {
while self.pending_bytes() > DEFLATE_BLOCK_SIZE {
self.emit_capped()
}
if self.pending_bytes() > 0 {
ignore(self.emit_trailing_block(is_final=false))
}
}
///|
/// Sync flush (zlib `Z_SYNC_FLUSH`): drain all buffered bytes, then append an
/// empty byte-aligned stored block. The persistent matchfinder retains the
/// preceding window for the stream's next input.
fn Deflater::emit_flush(self : Deflater) -> Unit {
if self.level == 0 {
self.flush_stored()
} else {
self.flush_capped()
}
write_stored(self.w, b"", 0, 0, is_final=false)
}