///|
/// Output storage for the one-shot decoder. Unlike the general-purpose core
/// `Buffer`, this exposes the fixed backing array needed for DEFLATE's
/// overlapping back-reference copies.
priv struct DecodeOutput {
  mut storage : FixedArray[Byte]
  mut length : Int
  fixed_capacity : Bool
}

///|
fn DecodeOutput::DecodeOutput(size_hint : Int) -> DecodeOutput {
  {
    storage: FixedArray::make(size_hint.max(1), b'\x00'),
    length: 0,
    fixed_capacity: false,
  }
}

///|
/// Use caller-owned storage for a one-shot decode. Capacity exhaustion becomes
/// the existing public `OutputLimitExceeded` failure instead of reallocating.
fn DecodeOutput::into(storage : FixedArray[Byte]) -> DecodeOutput {
  { storage, length: 0, fixed_capacity: true, }
}

///|
/// Byte counts use signed 32-bit Int on every backend.
const MAX_DECODE_OUTPUT_SIZE : Int = 0x7FFFFFFF

///|
/// Both counts are non-negative. Check the remaining range before adding so
/// overflow cannot bypass the capacity check or reach an array write.
fn checked_decode_output_length(
  length : Int,
  additional : Int,
) -> Int raise InflateError {
  guard additional <= MAX_DECODE_OUTPUT_SIZE - length else {
    raise InflateError(OutputLimitExceeded, "DEFLATE output size overflow")
  }
  length + additional
}

///|
/// Retain geometric growth when doubling fits; otherwise allocate only the
/// required size. Keeping this arithmetic separate permits allocation-free
/// tests at the integer limit.
fn grown_decode_output_capacity(current : Int, required : Int) -> Int {
  if current <= MAX_DECODE_OUTPUT_SIZE / 2 {
    (current * 2).max(required)
  } else {
    required
  }
}

///|
fn DecodeOutput::ensure_capacity(
  self : DecodeOutput,
  additional : Int,
) -> Unit raise InflateError {
  let required = checked_decode_output_length(self.length, additional)
  guard required > self.storage.length() else { return }
  guard !self.fixed_capacity else {
    raise InflateError(
      OutputLimitExceeded,
      "decoded output exceeds output buffer",
    )
  }
  let capacity = grown_decode_output_capacity(self.storage.length(), required)
  self.storage = FixedArray::make_and_blit(
    self.storage,
    allocate_len=capacity,
    init=b'\x00',
    len=self.length,
  )
}

///|
fn DecodeOutput::append_byte(
  self : DecodeOutput,
  byte : Byte,
) -> Unit raise InflateError {
  self.ensure_capacity(1)
  self.storage[self.length] = byte
  self.length = self.length + 1
}

///|
fn DecodeOutput::append_bytes(
  self : DecodeOutput,
  source : Bytes,
  source_offset : Int,
  length : Int,
) -> Unit raise InflateError {
  guard length > 0 else { return }
  self.ensure_capacity(length)
  self.storage.blit_from_bytes(self.length, source, source_offset, length)
  self.length = self.length + length
}

///|
/// Check capacity, then append a match without writing beyond its exact length.
/// The one-shot driver splits matches at cancellation boundaries.
fn DecodeOutput::append_repeated_suffix(
  self : DecodeOutput,
  distance : Int,
  length : Int,
) -> Unit raise InflateError {
  guard length > 0 else { return }
  self.ensure_capacity(length)
  let start = self.length
  let storage = self.storage
  if length <= 8 {
    // Keep the common short-text match in the caller to avoid an extra
    // helper call. Forward byte copies also handle overlapping history.
    for i in 0..= 16 keeps them disjoint.
/// The final vector may rewrite the preceding output bytes, but ends exactly
/// at the match boundary and only reads history that has already been produced.
#cfg(any(target="native", target="wasm"))
fn copy_match_bytes(
  storage : FixedArray[Byte],
  start : Int,
  distance : Int,
  length : Int,
) -> Unit {
  if distance >= 16 && length >= 16 {
    for offset = 0; offset + 16 <= length; offset = offset + 16 {
      @v128.v128_store(
        storage,
        start + offset,
        @v128.v128_load(storage, start + offset - distance),
      )
    }
    if (length & 15) != 0 {
      let offset = length - 16
      @v128.v128_store(
        storage,
        start + offset,
        @v128.v128_load(storage, start + offset - distance),
      )
    }
  } else {
    copy_match_bytes_scalar(storage, start, distance, length)
  }
}

///|
#cfg(not(any(target="native", target="wasm")))
fn copy_match_bytes(
  storage : FixedArray[Byte],
  start : Int,
  distance : Int,
  length : Int,
) -> Unit {
  copy_match_bytes_scalar(storage, start, distance, length)
}

///|
/// Longer overlapping matches double their generated prefix. This is also
/// the fallback on targets without native SIMD; the caller handles short matches.
fn copy_match_bytes_scalar(
  storage : FixedArray[Byte],
  start : Int,
  distance : Int,
  length : Int,
) -> Unit {
  let first = distance.min(length)
  FixedArray::unsafe_blit(storage, start, storage, start - distance, first)
  let mut copied = first
  while copied < length {
    let chunk = copied.min(length - copied)
    FixedArray::unsafe_blit(storage, start + copied, storage, start, chunk)
    copied = copied + chunk
  }
}

///|
fn DecodeOutput::to_bytes(self : DecodeOutput) -> Bytes {
  Bytes::from_array(self.storage[:self.length])
}

///|
/// In-memory DEFLATE decoder. Reads from a `Bytes` and appends to `out`.
priv struct MemDecoder {
  mut input : Bytes
  mut pos : Int // next input byte
  mut bitbuf : UInt // LSB-first bit accumulator
  mut bit_count : Int // valid bits in `bitbuf`
  mut out : DecodeOutput
  mut cancelled : () -> Bool // polled every `CANCEL_GRANULARITY` produced bytes
  mut poll_cancel : Bool
  mut next_cancel : Int // poll `cancelled` once `out` reaches this length
  dyn_litlen : HuffmanDecoder // literal/length (also reused for the code-length tree)
  dyn_dist : HuffmanDecoder // distance
  clbits : FixedArray[Int] // decoded literal+distance code lengths
  codebits : FixedArray[Int] // code-length code lengths
}

///|
const CANCEL_GRANULARITY : Int = 4096

///|
fn no_cancellation() -> Bool {
  false
}

///|
/// A DEFLATE Huffman code is at most 15 bits. The one-shot decoder can keep a
/// 24-bit reservoir because it is allowed to consume trailing bytes after the
/// final block, as documented by `inflate_all`'s prefix semantics.
const HUFFMAN_FAST_BITS : Int = 15

///|
const HUFFMAN_FAST_REFILL_BITS : Int = 24

///|
/// Output chunk size for the streaming one-shot drivers: each `step` call
/// writes into this scratch, so cancellation is polled at this granularity.
const INFLATE_SCRATCH_SIZE : Int = 8192

///|
fn MemDecoder::MemDecoder(input : Bytes, cancelled : () -> Bool) -> MemDecoder {
  {
    input,
    pos: 0,
    bitbuf: 0,
    bit_count: 0,
    out: DecodeOutput(input.length()),
    cancelled,
    poll_cancel: true,
    next_cancel: 0,
    dyn_litlen: HuffmanDecoder(),
    dyn_dist: HuffmanDecoder(),
    clbits: FixedArray::make(MAX_NUM_LIT + MAX_NUM_DIST, 0),
    codebits: FixedArray::make(NUM_CODES, 0),
  }
}

///|
/// Reinitialize an in-memory decoder over a new stream while retaining its
/// Huffman and dynamic-header workspace. `output` becomes the complete
/// back-reference history, so no secondary output allocation is needed.
fn MemDecoder::reset_into(
  self : MemDecoder,
  input : Bytes,
  output : FixedArray[Byte],
  cancelled : (() -> Bool)?,
) -> Unit {
  self.input = input
  self.pos = 0
  self.bitbuf = 0
  self.bit_count = 0
  self.out = DecodeOutput::into(output)
  self.set_cancellation(cancelled)
}

///|
fn MemDecoder::set_cancellation(
  self : MemDecoder,
  cancelled : (() -> Bool)?,
) -> Unit {
  self.cancelled = cancelled.unwrap_or(() => false)
  self.poll_cancel = cancelled is Some(_)
  // Without a callback, capacity/EOF alone delimit the batch. Explicit
  // callbacks still run at entry and every 4096 produced bytes.
  self.next_cancel = if self.poll_cancel { 0 } else { 0x7FFF_FFFF }
}

///|
fn MemDecoder::check_cancel(self : MemDecoder) -> Unit raise InflateError {
  guard self.poll_cancel && self.out.length >= self.next_cancel else { return }
  if (self.cancelled)() {
    raise InflateError(Cancelled, "cancelled by caller")
  }
  self.next_cancel = self.out.length + CANCEL_GRANULARITY
}

///|
#inline
fn MemDecoder::push(self : MemDecoder, b : Byte) -> Unit raise InflateError {
  let out = self.out
  let start = out.length
  // The capacity check also proves start + 1 cannot overflow. Keep growth,
  // output-limit errors and the exact cancellation boundary on the slow path.
  if start < out.storage.length() && start + 1 < self.next_cancel {
    out.storage[start] = b
    out.length = start + 1
  } else {
    out.append_byte(b)
    self.check_cancel()
  }
}

///|
fn MemDecoder::pull_byte(self : MemDecoder) -> Bool {
  if self.pos >= self.input.length() {
    return false
  }
  self.bitbuf = self.bitbuf | (self.input[self.pos].to_uint() << self.bit_count)
  self.bit_count = self.bit_count + 8
  self.pos = self.pos + 1
  true
}

///|
fn MemDecoder::need(self : MemDecoder, n : Int) -> Unit raise InflateError {
  while self.bit_count < n {
    if !self.pull_byte() {
      raise InflateError(Truncated, "unexpected end of input")
    }
  }
}

///|
fn MemDecoder::read_bits(self : MemDecoder, n : Int) -> Int raise InflateError {
  self.need(n)
  let v = (self.bitbuf & ((1U << n) - 1)).reinterpret_as_int()
  self.bitbuf = self.bitbuf >> n
  self.bit_count = self.bit_count - n
  v
}

///|
fn MemDecoder::huff_sym(
  self : MemDecoder,
  h : HuffmanDecoder,
) -> Int raise InflateError {
  let mut n = h.min
  for ;; {
    while self.bit_count < n {
      if !self.pull_byte() {
        raise InflateError(Truncated, "unexpected end of input")
      }
    }
    let mut chunk = h.chunks[(self.bitbuf & 0x1FF).reinterpret_as_int()]
    n = (chunk & 0xF).reinterpret_as_int()
    if n > HUFFMAN_CHUNK_BITS {
      let link = ((self.bitbuf >> HUFFMAN_CHUNK_BITS) & h.link_mask).reinterpret_as_int()
      chunk = h.links[(chunk >> HUFFMAN_VALUE_SHIFT).reinterpret_as_int() + link]
      n = (chunk & 0xF).reinterpret_as_int()
    }
    if n <= self.bit_count {
      if n == 0 {
        raise InflateError(Corrupt, "corrupt: bad Huffman code")
      }
      self.bitbuf = self.bitbuf >> n
      self.bit_count = self.bit_count - n
      return ((chunk >> HUFFMAN_VALUE_SHIFT) & 0x1FF).reinterpret_as_int()
    }
  }
}

///|
/// Refill the one-shot bit reservoir in two-byte groups when possible. This is
/// intentionally private to `MemDecoder`: a streaming inflater must not read
/// past the current symbol because those bytes can be a container trailer.
#inline
fn MemDecoder::refill_huffman_fast(self : MemDecoder) -> Unit {
  while self.bit_count < HUFFMAN_FAST_REFILL_BITS &&
        self.pos < self.input.length() {
    if self.bit_count <= HUFFMAN_FAST_BITS && self.pos + 1 < self.input.length() {
      let pair = self.input[self.pos].to_uint() |
        (self.input[self.pos + 1].to_uint() << 8)
      self.bitbuf = self.bitbuf | (pair << self.bit_count)
      self.bit_count = self.bit_count + 16
      self.pos = self.pos + 2
    } else {
      self.bitbuf = self.bitbuf |
        (self.input[self.pos].to_uint() << self.bit_count)
      self.bit_count = self.bit_count + 8
      self.pos = self.pos + 1
    }
  }
}

///|
/// Decode a data-block symbol using the prefilled fast reservoir when possible.
/// Unlike the dynamic-header path, this loop is the dominant one-shot decode
/// cost, so it keeps a wide reservoir and only falls back near physical EOF.
#inline
fn MemDecoder::huff_sym_data(
  self : MemDecoder,
  h : HuffmanDecoder,
) -> Int raise InflateError {
  // Fifteen buffered bits cover any DEFLATE code. Avoid topping up the
  // reservoir on every symbol when the next lookup can already complete.
  if self.bit_count < HUFFMAN_FAST_BITS {
    self.refill_huffman_fast()
  }
  if self.bit_count < HUFFMAN_FAST_BITS {
    return self.huff_sym(h)
  }
  let mut chunk = h.chunks[(self.bitbuf & 0x1FF).reinterpret_as_int()]
  let mut n = (chunk & 0xF).reinterpret_as_int()
  if n > HUFFMAN_CHUNK_BITS {
    let link = ((self.bitbuf >> HUFFMAN_CHUNK_BITS) & h.link_mask).reinterpret_as_int()
    chunk = h.links[(chunk >> HUFFMAN_VALUE_SHIFT).reinterpret_as_int() + link]
    n = (chunk & 0xF).reinterpret_as_int()
  }
  guard n != 0 else { raise InflateError(Corrupt, "corrupt: bad Huffman code") }
  self.bitbuf = self.bitbuf >> n
  self.bit_count = self.bit_count - n
  ((chunk >> HUFFMAN_VALUE_SHIFT) & 0x1FF).reinterpret_as_int()
}

///|
/// Decode and copy one length/distance pair. Keeping the uncommon match path
/// separate lets `decode_block` keep its literal fast loop branch-predictable.
#inline
fn MemDecoder::decode_match(
  self : MemDecoder,
  code : Int,
  hd : HuffmanDecoder?,
) -> Unit raise InflateError {
  guard code < 286 else {
    raise InflateError(Corrupt, "corrupt: invalid literal/length code")
  }
  let length_info = length_code_info_table[code - 257]
  let length = (length_info >> 3) + self.read_bits(length_info & 0x7)
  let dsym = if hd is Some(h) {
    self.huff_sym_data(h)
  } else {
    fixed_distance_symbol(self.read_bits(5))
  }
  guard dsym.reinterpret_as_uint() < MAX_NUM_DIST.reinterpret_as_uint() else {
    raise InflateError(Corrupt, "corrupt: invalid distance code")
  }
  let distance_info = distance_code_info_table[dsym]
  // `read_bits(0)` reads nothing and returns 0, so the base serves as the
  // distance when the code carries no extra bits.
  let dist = (distance_info >> 4) + self.read_bits(distance_info & 0xF)
  let available_history = self.out.length
  guard available_history >= WINDOW_SIZE ||
    back_reference_distance_is_valid(dist, available_history) else {
    raise InflateError(Corrupt, "corrupt: distance too far back")
  }
  // Copy a complete match here when capacity and the cancellation interval
  // permit it. Subtract before comparing so the output bound cannot overflow.
  // Short text matches use forward byte copies, including overlapping history.
  let out = self.out
  let start = out.length
  if length <= out.storage.length() - start && length < self.next_cancel - start {
    let storage = out.storage
    if length <= 16 {
      // A decoded match is at least three bytes. Keep the mandatory prefix
      // outside the loop; sequential stores preserve distances one and two.
      storage[start] = storage[start - dist]
      storage[start + 1] = storage[start + 1 - dist]
      storage[start + 2] = storage[start + 2 - dist]
      for i in 3.. UInt {
  // HuffmanDecoder always owns 512 primary entries, including empty trees.
  // Masking to nine bits proves this load's range independently of input.
  let chunk = h.chunks.unsafe_get((bits & 0x1FFUL).to_int())
  if (chunk & 15) > HUFFMAN_CHUNK_BITS.reinterpret_as_uint() {
    let link = ((bits >> HUFFMAN_CHUNK_BITS).to_uint() & h.link_mask).reinterpret_as_int()
    h.links[(chunk >> HUFFMAN_VALUE_SHIFT).reinterpret_as_int() + link]
  } else {
    chunk
  }
}

///|
// Low input lanes contain eight history bytes; high lanes hold old output.
// Repeating only the available history handles overlap, while selecting old
// output beyond the match preserves every caller-owned tail byte.
#cfg(target="native")
let short_copy_shuffle : FixedArray[Byte] = FixedArray::makei(1024, index => {
  let lane = index & 15
  let length = index >> 7
  let distance = ((index >> 4) & 7).max(1)
  if lane < length {
    (lane % distance).to_byte()
  } else {
    (8 + (lane & 7)).to_byte()
  }
})

///|
/// Process complete tokens with local state. A token needs at most 48 bits
/// (15 + 5 + 15 + 13) and emits at most 258 bytes. Uncommitted tokens are
/// retried by the checked decoder, including malformed codes and EOF tails.
#cfg(target="native")
fn MemDecoder::decode_batch(
  self : MemDecoder,
  hl : HuffmanDecoder,
  hd : HuffmanDecoder?,
) -> Bool {
  guard hd is Some(distance_tree) else { return false }
  let input = self.input
  let out = self.out
  let storage = out.storage
  let output_end = storage.length().min(self.next_cancel - 1)
  guard input.length() - self.pos >= 8 && output_end - out.length >= 258 else {
    return false
  }
  let mut bits = UInt64::extend_uint(self.bitbuf)
  let mut count = self.bit_count
  let mut pos = self.pos
  let mut written = out.length
  let mut next_primary = 0U
  let ended = for ;; {
    let input_length = input.length()
    let output_length = storage.length()
    // Explicit nonnegative ranges enable native bounds-check elimination for
    // the constant-offset loads and stores below.
    let can_decode_token = input_length >= 8 &&
      pos >= 0 &&
      pos <= input_length - 8 &&
      output_length >= 258 &&
      written >= 0 &&
      written <= output_length - 258 &&
      output_end - written >= 258
    guard can_decode_token else { break false }
    let mut word = bits
    let mut remaining = count
    let mut cursor = pos
    if remaining < 48 {
      // The iteration proves eight readable bytes. Advance only by complete
      // bytes fitting below bit 63; afterwards 56..63 bits are available.
      // Higher loaded bits are lookahead from the same unconsumed input. They
      // agree with the next refill's OR and are discarded on batch exit.
      let loaded = UInt64::extend_uint(input[cursor].to_uint()) |
        (UInt64::extend_uint(input[cursor + 1].to_uint()) << 8) |
        (UInt64::extend_uint(input[cursor + 2].to_uint()) << 16) |
        (UInt64::extend_uint(input[cursor + 3].to_uint()) << 24) |
        (UInt64::extend_uint(input[cursor + 4].to_uint()) << 32) |
        (UInt64::extend_uint(input[cursor + 5].to_uint()) << 40) |
        (UInt64::extend_uint(input[cursor + 6].to_uint()) << 48) |
        (UInt64::extend_uint(input[cursor + 7].to_uint()) << 56)
      let bytes = (63 - remaining) >> 3
      word = word | (loaded << remaining)
      remaining += bytes << 3
      cursor += bytes
    }
    let mut entry = if next_primary != 0U {
      next_primary
    } else {
      hl.chunks.unsafe_get((word & 511UL).to_int())
    }
    if (entry & 15U) > 9U {
      entry = batch_huffman_entry(hl, word)
    }
    let mut used = (entry & 15).reinterpret_as_int()
    guard used != 0 else { break false }
    let mut symbol = ((entry >> HUFFMAN_VALUE_SHIFT) & 0x1FF).reinterpret_as_int()
    if symbol < 256 {
      word = word >> used
      remaining -= used
      // The loop entered with at least 48 bits and a literal uses at most 15,
      // so a complete nine-bit primary prefix remains available.
      next_primary = hl.chunks.unsafe_get((word & 511UL).to_int())
      storage[written] = symbol.to_byte()
      written += 1
      // Preserve this literal even if the following token needs checked
      // fallback. Continue directly only with a full token's reservation.
      bits = word
      count = remaining
      pos = cursor
      guard remaining >= 48 && output_end - written >= 258 else { continue }
      entry = next_primary
      if (entry & 15U) > 9U {
        entry = batch_huffman_entry(hl, word)
      }
      used = (entry & 15).reinterpret_as_int()
      guard used != 0 else { break false }
      symbol = ((entry >> HUFFMAN_VALUE_SHIFT) & 0x1FF).reinterpret_as_int()
    }
    if symbol < 256 {
      word = word >> used
      remaining -= used
      // The paired literal starts only with at least 48 remaining bits.
      next_primary = hl.chunks.unsafe_get((word & 511UL).to_int())
      storage[written] = symbol.to_byte()
      written += 1
    } else if symbol == 256 {
      bits = word >> used
      count = remaining - used
      pos = cursor
      break true
    } else {
      guard symbol < 286 else { break false }
      let info = ((entry >> 13) & 0xFFF).reinterpret_as_int()
      let extra = info & 7
      let length = (info >> 3) +
        ((word >> used) & ((1UL << extra) - 1)).to_int()
      let total = (entry >> 25).reinterpret_as_int()
      word = word >> total
      remaining -= total
      let distance_entry = batch_huffman_entry(distance_tree, word)
      let distance_used = (distance_entry & 15).reinterpret_as_int()
      let distance_info = (distance_entry >> 13).reinterpret_as_int()
      // Native data tables give every valid distance nonzero metadata and
      // leave reserved symbols 30/31 at zero. No separate symbol extraction
      // is needed to reject them before committing the token.
      guard distance_used != 0 && distance_info != 0 else { break false }
      word = word >> distance_used
      remaining -= distance_used
      let distance_extra = distance_info & 15
      let distance = (distance_info >> 4) +
        (word & ((1UL << distance_extra) - 1)).to_int()
      guard distance > 0 && distance <= written else { break false }
      word = word >> distance_extra
      remaining -= distance_extra
      // Only the bounded primary read is speculative. Secondary resolution
      // and invalid-code handling stay after refill on the next iteration.
      next_primary = if remaining >= 9 {
        hl.chunks.unsafe_get((word & 511UL).to_int())
      } else {
        0U
      }
      if length <= 7 {
        // The batch reserves 258 bytes, so these eight-byte reads/writes stay
        // in bounds even for a three-byte match. The shuffle retains the tail.
        let history = @v128.v128_load64_zero(storage, written - distance)
        let combined = @v128.v128_load64_lane(storage, written, history, 1)
        let control = @v128.v128_load(
          short_copy_shuffle,
          (length * 8 + distance.min(7)) * 16,
        )
        @v128.v128_store64_lane(
          storage,
          written,
          @v128.i8x16_swizzle(combined, control),
          0,
        )
      } else if length <= 16 && distance >= 8 {
        @v128.v128_store64_lane(
          storage,
          written,
          @v128.v128_load64_zero(storage, written - distance),
          0,
        )
        if length > 8 {
          let tail = written + length - 8
          @v128.v128_store64_lane(
            storage,
            tail,
            @v128.v128_load64_zero(storage, tail - distance),
            0,
          )
        }
      } else if length <= 16 {
        // Constant offsets reuse the batch's range proof. Each load precedes
        // its store for overlapping history; stop at the exact match length.
        storage[written] = storage[written - distance]
        storage[written + 1] = storage[written + 1 - distance]
        storage[written + 2] = storage[written + 2 - distance]
        if length > 3 {
          storage[written + 3] = storage[written + 3 - distance]
        }
        if length > 4 {
          storage[written + 4] = storage[written + 4 - distance]
        }
        if length > 5 {
          storage[written + 5] = storage[written + 5 - distance]
        }
        if length > 6 {
          storage[written + 6] = storage[written + 6 - distance]
        }
        if length > 7 {
          storage[written + 7] = storage[written + 7 - distance]
        }
        if length > 8 {
          storage[written + 8] = storage[written + 8 - distance]
        }
        if length > 9 {
          storage[written + 9] = storage[written + 9 - distance]
        }
        if length > 10 {
          storage[written + 10] = storage[written + 10 - distance]
        }
        if length > 11 {
          storage[written + 11] = storage[written + 11 - distance]
        }
        if length > 12 {
          storage[written + 12] = storage[written + 12 - distance]
        }
        if length > 13 {
          storage[written + 13] = storage[written + 13 - distance]
        }
        if length > 14 {
          storage[written + 14] = storage[written + 14 - distance]
        }
        if length > 15 {
          storage[written + 15] = storage[written + 15 - distance]
        }
      } else if length <= 48 && distance >= 16 {
        // Length is now 17..48. Two or three forward vectors cover the
        // match without a helper call, and the last store ends exactly.
        @v128.v128_store(
          storage,
          written,
          @v128.v128_load(storage, written - distance),
        )
        if length > 32 {
          @v128.v128_store(
            storage,
            written + 16,
            @v128.v128_load(storage, written + 16 - distance),
          )
        }
        let tail = written + length - 16
        @v128.v128_store(
          storage,
          tail,
          @v128.v128_load(storage, tail - distance),
        )
      } else if distance == 1 {
        storage.fill(storage[written - 1], start=written, end=written + length)
      } else {
        copy_match_bytes(storage, written, distance, length)
      }
      written += length
    }
    bits = word
    count = remaining
    pos = cursor
  }
  // Put unread whole bytes back before narrowing to the original reservoir.
  self.pos = pos - (count >> 3)
  self.bit_count = count & 7
  self.bitbuf = bits.to_uint() & ((1U << self.bit_count) - 1)
  out.length = written
  ended
}

///|
// The wide-reservoir fast path is measured on native. Keep the established
// production loop on other backends.
#cfg(not(target="native"))
#inline
fn MemDecoder::decode_batch(
  _self : MemDecoder,
  _hl : HuffmanDecoder,
  _hd : HuffmanDecoder?,
) -> Bool {
  false
}

///|
fn MemDecoder::decode_block(
  self : MemDecoder,
  hl : HuffmanDecoder,
  hd : HuffmanDecoder?,
) -> Unit raise InflateError {
  for ;; {
    if self.decode_batch(hl, hd) {
      return
    }
    let v = self.huff_sym_data(hl)
    guard v >= 256 else {
      self.push(v.to_byte())
      // Most DEFLATE streams contain literal runs. Decode one extra symbol
      // before returning to the match/EOB dispatch to amortize that branch.
      let next = self.huff_sym_data(hl)
      guard next >= 256 else {
        self.push(next.to_byte())
        continue
      }
      guard next != 256 else { return }
      self.decode_match(next, hd)
      continue
    }
    guard v != 256 else { return }
    self.decode_match(v, hd)
  }
}

///|
// Fixed blocks do not use the dynamic batch. Dispatch once per block instead
// of paying for a failed batch probe on every token of small fixed streams.
fn MemDecoder::decode_fixed_block(self : MemDecoder) -> Unit raise InflateError {
  for ;; {
    let symbol = self.huff_sym_data(fixed_huffman_decoder)
    guard symbol >= 256 else {
      self.push(symbol.to_byte())
      let next = self.huff_sym_data(fixed_huffman_decoder)
      guard next >= 256 else {
        self.push(next.to_byte())
        continue
      }
      guard next != 256 else { return }
      self.decode_match(next, None)
      continue
    }
    guard symbol != 256 else { return }
    self.decode_match(symbol, None)
  }
}

///|
fn MemDecoder::copy_match(
  self : MemDecoder,
  distance : Int,
  length : Int,
) -> Unit raise InflateError {
  let mut remaining = length
  while remaining > 0 {
    // Keep the public cancellation cadence while copying in large chunks.
    let until_cancel = if self.poll_cancel {
      self.next_cancel - self.out.length
    } else {
      remaining
    }
    let chunk = remaining.min(until_cancel)
    self.out.append_repeated_suffix(distance, chunk)
    remaining = remaining - chunk
    self.check_cancel()
  }
}

///|
fn MemDecoder::copy_stored(
  self : MemDecoder,
  length : Int,
) -> Unit raise InflateError {
  let mut remaining = length
  while remaining > 0 {
    // Preserve `inflate_all`'s cancellation cadence while copying each chunk
    // through the core bulk-copy primitive.
    let until_cancel = if self.poll_cancel {
      self.next_cancel - self.out.length
    } else {
      remaining
    }
    let chunk = remaining.min(until_cancel)
    self.out.append_bytes(self.input, self.pos, chunk)
    self.pos = self.pos + chunk
    remaining = remaining - chunk
    self.check_cancel()
  }
}

///|
fn MemDecoder::stored_block(self : MemDecoder) -> Unit raise InflateError {
  // The fast Huffman reader can have prefetched whole bytes beyond the current
  // block header. Byte alignment discards only the remaining partial byte; put
  // those unread whole bytes back so LEN/NLEN starts at the correct input byte.
  self.pos = self.pos - (self.bit_count >> 3)
  self.bitbuf = 0
  self.bit_count = 0
  if self.pos + 4 > self.input.length() {
    raise InflateError(Truncated, "unexpected end of input")
  }
  let len = self.input[self.pos].to_int() |
    (self.input[self.pos + 1].to_int() << 8)
  let nlen = self.input[self.pos + 2].to_int() |
    (self.input[self.pos + 3].to_int() << 8)
  self.pos = self.pos + 4
  validate_stored_length(len, nlen)
  if self.pos + len > self.input.length() {
    raise InflateError(Truncated, "unexpected end of input")
  }
  self.copy_stored(len)
}

///|
fn MemDecoder::read_dynamic(self : MemDecoder) -> Unit raise InflateError {
  let (nlit, ndist) = decode_dynamic_header(
    n => self.read_bits(n),
    () => self.huff_sym(self.dyn_litlen),
    self.dyn_litlen,
    self.codebits,
    self.clbits,
  )
  guard self.dyn_litlen.initialize_litlen(self.clbits[0:nlit]) else {
    raise InflateError(Corrupt, "corrupt: bad literal/length tree")
  }
  guard self.dyn_dist.initialize_distance(self.clbits[nlit:nlit + ndist]) else {
    raise InflateError(Corrupt, "corrupt: bad distance tree")
  }
}

///|
fn MemDecoder::run(self : MemDecoder) -> Unit raise InflateError {
  self.check_cancel()
  for ;; {
    self.need(3)
    let bfinal = (self.bitbuf & 1) == 1
    let btype = ((self.bitbuf >> 1) & 3).reinterpret_as_int()
    self.bitbuf = self.bitbuf >> 3
    self.bit_count = self.bit_count - 3
    match btype {
      0 => self.stored_block()
      1 => self.decode_fixed_block()
      2 => {
        self.read_dynamic()
        self.decode_block(self.dyn_litlen, Some(self.dyn_dist))
      }
      _ => raise InflateError(Corrupt, "corrupt: reserved block type")
    }
    if bfinal {
      break
    }
  }
}

///|
/// Decompress a raw DEFLATE stream held entirely in memory. Bytes after the
/// final block are ignored; use `inflate_exact` when an exact framing boundary
/// is required. The output grows without bound; for untrusted input use
/// `inflate_all_limited` or drive `Inflater` with caller-sized output buffers.
/// If supplied, `cancelled` is polled at entry and then every 4096 produced
/// bytes; returning `true` raises `InflateError(Cancelled, _)`.
pub fn inflate_all(
  input : Bytes,
  cancelled? : () -> Bool,
) -> Bytes raise InflateError {
  let d = MemDecoder(input, no_cancellation)
  d.set_cancellation(cancelled)
  d.run()
  d.out.to_bytes()
}

///|
/// A reusable, whole-buffer raw DEFLATE decompressor. It decodes directly into
/// caller-owned storage while retaining Huffman and dynamic-header workspace
/// across streams.
pub struct Decompressor {
  priv decoder : MemDecoder
}

///|
/// Create a reusable whole-buffer decompressor.
pub fn Decompressor::Decompressor() -> Decompressor {
  { decoder: MemDecoder(b"", () => false), }
}

///|
/// Decompress `input` into `output`, returning the number of bytes written.
/// The complete output must fit in `output`; otherwise this raises
/// `InflateError(OutputLimitExceeded, _)`. As with `inflate_all`, bytes after
/// the final DEFLATE block are ignored. Reuse one `Decompressor` and output
/// buffer to avoid per-stream decoder and result-buffer allocation.
pub fn Decompressor::decompress_into(
  self : Decompressor,
  input : Bytes,
  output : FixedArray[Byte],
  cancelled? : () -> Bool,
) -> Int raise InflateError {
  self.decoder.reset_into(input, output, cancelled)
  self.decoder.run()
  self.decoder.out.length
}

///|
/// One-shot convenience form of `Decompressor::decompress_into`. It creates a
/// decompressor for this call; use `Decompressor` when decoding many streams.
pub fn inflate_into(
  input : Bytes,
  output : FixedArray[Byte],
  cancelled? : () -> Bool,
) -> Int raise InflateError {
  let decoder = Decompressor()
  if cancelled is Some(callback) {
    decoder.decompress_into(input, output, cancelled=callback)
  } else {
    decoder.decompress_into(input, output)
  }
}

///|
/// Drive the streaming inflater over an in-memory input. If `exact` is true,
/// reject bytes after the final DEFLATE block. If `max_output` is present, one
/// fixed scratch buffer may be filled past that boundary internally, but no
/// oversized result is returned. `cancelled` is polled at entry and once per
/// step (every 8192 produced bytes); returning `true` raises
/// `InflateError(Cancelled, _)`.
fn inflate_streaming_all(
  input : Bytes,
  max_output : Int?,
  exact : Bool,
  cancelled : () -> Bool,
) -> Bytes raise InflateError {
  guard !cancelled() else {
    raise InflateError(Cancelled, "cancelled by caller")
  }
  let inflater = Inflater()
  let scratch = FixedArray::make(INFLATE_SCRATCH_SIZE, b'\x00')
  let decoded : Array[Byte] = []
  let mut input_pos = 0
  for ;; {
    let status = inflater.step_into(input[input_pos:], scratch)
    let consumed = inflater.last_consumed()
    let produced = inflater.last_produced()
    input_pos = input_pos + consumed
    guard !cancelled() else {
      raise InflateError(Cancelled, "cancelled by caller")
    }
    guard max_output.map_or(true, limit => produced <= limit - decoded.length()) else {
      raise InflateError(
        OutputLimitExceeded,
        "decoded output exceeds max_output",
      )
    }
    decoded.append(scratch[:produced])
    match status {
      Done => {
        if exact && input_pos != input.length() {
          raise InflateError(TrailingData, "trailing data after DEFLATE stream")
        }
        return Bytes::from_array(decoded)
      }
      NeedMoreOutput => ()
      NeedMoreInput => raise InflateError(Truncated, "unexpected end of input")
    }
  }
}

///|
/// Decompress exactly one raw DEFLATE stream. Unlike `inflate_all`, this rejects
/// any bytes after the final block.
///
/// Optional controls:
///  - `max_output`: decoded output beyond this size raises
///    `InflateError(OutputLimitExceeded, _)`. A negative value is rejected the
///    same way.
///  - `cancelled`: polled at entry and then once per internal step (every 8192
///    produced bytes); returning `true` raises `InflateError(Cancelled, _)`.
///  - `preallocated`: when `true`, decode runs twice — a counting pass that
///    validates the whole stream, then a second pass filling one exactly sized
///    allocation. Peak memory stays at roughly one decoded output, at the cost
///    of roughly double the decode work; the default `false` grows one buffer
///    in a single pass, trading transient memory for one decode pass.
pub fn inflate_exact(
  input : Bytes,
  max_output? : Int? = None,
  cancelled? : () -> Bool = () => false,
  preallocated? : Bool = false,
) -> Bytes raise InflateError {
  let limit = max_output.map_or(max_output, limit => {
    guard limit >= 0 else {
      raise InflateError(OutputLimitExceeded, "max_output must be non-negative")
    }
    max_output
  })
  if preallocated {
    inflate_preallocated(input, limit, cancelled)
  } else {
    inflate_streaming_all(input, limit, true, cancelled)
  }
}

///|
/// Drive one inflater over the whole in-memory input, delivering produced
/// chunks to `emit` (called once per step with the scratch contents). Returns
/// the total number of bytes produced. `max_output` raises once the total
/// exceeds it; `exact` rejects bytes after the final block; `cancelled` is
/// polled once per step and raises `InflateError(Cancelled, _)`.
fn inflate_drive(
  inflater : Inflater,
  input : Bytes,
  max_output : Int?,
  exact : Bool,
  cancelled : () -> Bool,
  emit : (FixedArray[Byte], Int) -> Unit,
) -> Int raise InflateError {
  let scratch = FixedArray::make(INFLATE_SCRATCH_SIZE, b'\x00')
  let mut total = 0
  let mut input_pos = 0
  for ;; {
    let status = inflater.step_into(input[input_pos:], scratch, end=true)
    let consumed = inflater.last_consumed()
    let produced = inflater.last_produced()
    input_pos = input_pos + consumed
    total = total + produced
    guard !cancelled() else {
      raise InflateError(Cancelled, "cancelled by caller")
    }
    guard max_output.map_or(true, limit => total <= limit) else {
      raise InflateError(
        OutputLimitExceeded,
        "decoded output exceeds max_output",
      )
    }
    emit(scratch, produced)
    // `Inflater::step(end=true)` raises `Truncated` on exhausted input, so
    // only `Done` and `NeedMoreOutput` can be returned here.
    guard status is Done else { continue }
    if exact && input_pos != input.length() {
      raise InflateError(TrailingData, "trailing data after DEFLATE stream")
    }
    return total
  }
}

///|
/// The replay pass must produce exactly the size measured by the counting pass.
/// A mismatch indicates an internal decoder invariant failure rather than bad
/// caller input, so this defensive branch is excluded from behavior coverage.
#coverage.skip
fn validate_preallocation_size(
  size : Int,
  filled : Int,
) -> Unit raise InflateError {
  guard filled == size else {
    raise InflateError(Corrupt, "corrupt: preallocation size mismatch")
  }
}

///|
/// `inflate_exact` with `preallocated=true`: validate and count in one pass
/// over an 8 KiB scratch, then replay the deterministic decode into one exact
/// allocation (the same `Inflater` is reused via `reset`).
fn inflate_preallocated(
  input : Bytes,
  max_output : Int?,
  cancelled : () -> Bool,
) -> Bytes raise InflateError {
  guard !cancelled() else {
    raise InflateError(Cancelled, "cancelled by caller")
  }
  let inflater = Inflater()
  let size = inflate_drive(inflater, input, max_output, true, cancelled, (
    _scratch,
    _produced,
  ) => ())
  inflater.reset()
  let buf = FixedArray::make(size, b'\x00')
  let mut filled = 0
  ignore(
    inflate_drive(inflater, input, None, true, cancelled, (scratch, produced) => {
      scratch.blit_to(buf, len=produced, dst_offset=filled)
      filled = filled + produced
    }),
  )
  validate_preallocation_size(size, filled)
  Bytes::from_array(buf)
}

///|
/// Decompress one raw DEFLATE stream without allowing the returned output to
/// exceed `max_output`. Bytes after the final block retain `inflate_all`'s
/// prefix semantics and are ignored. `cancelled` follows `inflate_all`'s
/// polling contract.
pub fn inflate_all_limited(
  input : Bytes,
  max_output~ : Int,
  cancelled? : () -> Bool = () => false,
) -> Bytes raise InflateError {
  guard max_output >= 0 else {
    raise InflateError(OutputLimitExceeded, "max_output must be non-negative")
  }
  inflate_streaming_all(input, Some(max_output), false, cancelled)
}