///|
/// Validate the one's-complement length pair carried by a stored block.
#inline
fn validate_stored_length(len : Int, nlen : Int) -> Unit raise InflateError {
  if len != (nlen ^ 0xFFFF) {
    raise InflateError(Corrupt, "stored block length mismatch")
  }
}

///|
/// Interpret HLIT while preserving its reserved-value diagnostic before any
/// later dynamic-header fields are read.
#inline
fn dynamic_literal_count(bits : Int) -> Int raise InflateError {
  let count = bits + 257
  if count > max_num_lit {
    raise InflateError(Corrupt, "corrupt: too many literal codes")
  }
  count
}

///|
/// Interpret HDIST and reject the two reserved five-bit encodings.
#inline
fn dynamic_distance_count(bits : Int) -> Int raise InflateError {
  let count = bits + 1
  if count > max_num_dist {
    raise InflateError(Corrupt, "corrupt: too many distance codes")
  }
  count
}

///|
/// Check that a back-reference reaches a byte in the decoder's available
/// history. The caller supplies the history size appropriate to its execution
/// strategy (one-shot output or the streaming circular window), which is
/// always non-negative. Subtracting one as `UInt` turns the inclusive valid
/// interval `1..=available_history` into one upper-bound comparison; zero and
/// negative distances wrap above the bound and are rejected as well.
#inline
fn back_reference_distance_is_valid(
  distance : Int,
  available_history : Int,
) -> Bool {
  distance.reinterpret_as_uint() - 1U < available_history.reinterpret_as_uint()
}

///|
/// Static interpretation of one dynamic-header repeat symbol. Keeping this
/// independent of either bit reader makes the streaming and in-memory decoders
/// agree on the symbol's base count, extra-bit width, and repeated value.
priv struct CodeLengthRepeat {
  base : Int
  extra_bits : Int
  value : Int
}

///|
fn interpret_code_length_repeat(
  symbol : Int,
  previous : Int?,
) -> CodeLengthRepeat raise InflateError {
  match symbol {
    16 => {
      guard previous is Some(value) else {
        raise InflateError(Corrupt, "corrupt: repeat with no previous length")
      }
      { base: 3, extra_bits: 2, value, }
    }
    17 => { base: 3, extra_bits: 3, value: 0, }
    18 => { base: 11, extra_bits: 7, value: 0, }
    _ => raise InflateError(Corrupt, "corrupt: bad code-length symbol")
  }
}

///|
/// Interpret a literal/length code's base length and extra-bit width
/// (RFC 1951 §3.2.5). The caller adds `read_bits(extra)` to the base.
/// Split into two scalar helpers rather than returning a tuple so the per-token
/// decode path allocates nothing.
#inline
fn length_code_base(code : Int) -> Int {
  length_code_info_table[code - 257] >> 3
}

///|
#inline
fn length_code_extra(code : Int) -> Int {
  length_code_info_table[code - 257] & 0x7
}

///|
/// Interpret a distance code's base distance and extra-bit width
/// (RFC 1951 §3.2.5). The caller adds `read_bits(extra)` to the base.
#inline
fn distance_code_base(code : Int) -> Int {
  distance_code_info_table[code] >> 4
}

///|
#inline
fn distance_code_extra(code : Int) -> Int {
  distance_code_info_table[code] & 0xF
}

///|
/// The fixed-Huffman distance symbol carried by a raw five-bit code: the code
/// is stored bit-reversed in the lowest five bits (RFC 1951 §3.2.6).
#inline
fn fixed_distance_symbol(bits5 : Int) -> Int {
  reverse8(((bits5 << 3) & 0xFF).to_byte()).to_int()
}

///|
/// Decode one dynamic-Huffman block header, shared by the streaming `Inflater`
/// and the in-memory `MemDecoder` so both interpret HLIT/HDIST/HCLEN and the
/// code-length run-length encoding identically. `read_bits` and `huff_sym`
/// come from the caller's bit reader and raise only `InflateError`; the
/// streaming decoder's input-suspension signal is converted to `Truncated` at
/// its call site. `huff_sym` reads from the code-length tree, which this
/// function has just initialized inside `dyn_litlen`. `codebits` must have
/// `num_codes` slots and `clbits` room for `nlit + ndist`. Returns the
/// literal/length and distance code counts.
fn decode_dynamic_header(
  read_bits : (Int) -> Int raise InflateError,
  huff_sym : () -> Int raise InflateError,
  dyn_litlen : HuffmanDecoder,
  codebits : Array[Int],
  clbits : Array[Int],
) -> (Int, Int) raise InflateError {
  let nlit = dynamic_literal_count(read_bits(5))
  let ndist = dynamic_distance_count(read_bits(5))
  let nclen = read_bits(4) + 4
  for i in 0.. n {
        raise InflateError(Corrupt, "corrupt: code-length repeat overflow")
      }
      for _j in 0..