///|
let local_header_sig : Int = 0x04034b50

///|
let central_header_sig : Int = 0x02014b50

///|
let end_of_central_sig : Int = 0x06054b50

///|
let flag_data_descriptor : Int = 0x0008

///|
let flag_utf8 : Int = 0x0800

///|
let flag_encrypted : Int = 0x0001

///|
let flag_deflate_max : Int = 0x0002

///|
let flag_deflate_fast : Int = 0x0004

///|
let data_descriptor_sig : Int = 0x08074b50

///|
let zip64_eocd_sig : Int = 0x06064b50

///|
let zip64_locator_sig : Int = 0x07064b50

///|
let unicode_path_extra_id : Int = 0x7075

///|
priv struct Reader {
  bytes : BytesView
  mut pos : Int
}

///|
fn Reader::new(bytes : BytesView, pos : Int) -> Reader {
  { bytes, pos }
}

///|
fn Reader::read_u8(self : Reader) -> Int raise ZipError {
  if self.pos >= self.bytes.length() || self.pos < 0 {
    raise OutOfBounds(offset=self.pos)
  }
  let value = self.bytes[self.pos].to_int()
  self.pos = self.pos + 1
  value
}

///|
fn Reader::read_u16(self : Reader) -> Int raise ZipError {
  let b0 = self.read_u8()
  let b1 = self.read_u8()
  b0 | (b1 << 8)
}

///|
fn Reader::read_u32(self : Reader) -> Int raise ZipError {
  let value = self.read_u32_raw()
  u32_to_int(value)
}

///|
fn Reader::read_u32_raw(self : Reader) -> UInt raise ZipError {
  let b0 = self.read_u8()
  let b1 = self.read_u8()
  let b2 = self.read_u8()
  let b3 = self.read_u8()
  let v0 = b0.reinterpret_as_uint()
  let v1 = b1.reinterpret_as_uint()
  let v2 = b2.reinterpret_as_uint()
  let v3 = b3.reinterpret_as_uint()
  v0 | (v1 << 8) | (v2 << 16) | (v3 << 24)
}

///|
fn Reader::read_u64_raw(self : Reader) -> UInt64 raise ZipError {
  let lo = self.read_u32_raw()
  let hi = self.read_u32_raw()
  (UInt64::extend_uint(hi) << 32) | UInt64::extend_uint(lo)
}

///|
fn Reader::read_bytes(self : Reader, len : Int) -> BytesView raise ZipError {
  if len < 0 ||
    self.pos < 0 ||
    self.pos > self.bytes.length() ||
    len > self.bytes.length() - self.pos {
    raise OutOfBounds(offset=self.pos)
  }
  let end = self.pos + len
  let view = self.bytes[self.pos:end]
  self.pos = end
  view
}

///|
fn Reader::skip(self : Reader, len : Int) -> Unit raise ZipError {
  if len < 0 ||
    self.pos < 0 ||
    self.pos > self.bytes.length() ||
    len > self.bytes.length() - self.pos {
    raise OutOfBounds(offset=self.pos)
  }
  let end = self.pos + len
  self.pos = end
}

///|
/// Copies a borrowed ZIP slice into owned storage with bounded cancellation
/// latency. Large stored entries and preservation records must not hide one
/// monolithic runtime copy behind a single preflight check.
fn copy_zip_bytes_cancellable(
  bytes : BytesView,
  cancelled : () -> Bool,
) -> Bytes raise ZipError {
  check_zip_cancelled(cancelled)
  // An initialized FixedArray constructor performs a complete zero-fill before
  // user code can poll cancellation. Allocate uninitialized primitive storage
  // instead, then initialize every byte under explicit checkpoints.
  let output : UninitializedArray[Byte] = UninitializedArray::make(
    bytes.length(),
  )
  for index in 0.. Bytes = "%identity"

///|
fn u32_to_int(value : UInt) -> Int raise ZipError {
  let max_u32_int : UInt = 0x7FFFFFFF
  if value > max_u32_int {
    raise UnsupportedFeature(msg="u32 exceeds Int range")
  }
  value.reinterpret_as_int()
}

///|
fn u64_to_int(value : UInt64) -> Int raise ZipError {
  let max_int : UInt64 = 0x7FFFFFFF
  if value > max_int {
    raise UnsupportedFeature(msg="u64 exceeds Int range")
  }
  value.to_int()
}

///|
fn read_u16_le_at(bytes : BytesView, offset : Int) -> Int raise ZipError {
  if offset < 0 || offset > bytes.length() || 2 > bytes.length() - offset {
    raise OutOfBounds(offset~)
  }
  let b0 = bytes[offset].to_int()
  let b1 = bytes[offset + 1].to_int()
  b0 | (b1 << 8)
}

///|
fn read_u32_le_at_raw(bytes : BytesView, offset : Int) -> UInt raise ZipError {
  if offset < 0 || offset > bytes.length() || 4 > bytes.length() - offset {
    raise OutOfBounds(offset~)
  }
  let b0 = bytes[offset].to_uint()
  let b1 = bytes[offset + 1].to_uint()
  let b2 = bytes[offset + 2].to_uint()
  let b3 = bytes[offset + 3].to_uint()
  b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)
}

///|
fn read_u64_le_at(bytes : BytesView, offset : Int) -> UInt64 raise ZipError {
  if offset < 0 || offset > bytes.length() || 8 > bytes.length() - offset {
    raise OutOfBounds(offset~)
  }
  let mut result : UInt64 = 0
  for i in 0..<8 {
    let b = bytes[offset + i].to_uint()
    result = result | (UInt64::extend_uint(b) << (i * 8))
  }
  result
}

///|
fn find_end_of_central(bytes : BytesView) -> Int? raise ZipError {
  let len = bytes.length()
  if len < 22 {
    return None
  }
  let max_scan = 22 + 0xFFFF
  let min_offset = if len > max_scan { len - max_scan } else { 0 }
  for i in (len - 22)>=..min_offset {
    if read_u32_le_at_raw(bytes, i) == end_of_central_sig.reinterpret_as_uint() &&
      read_u16_le_at(bytes, i + 20) == len - i - 22 {
      return Some(i)
    }
  } nobreak {
    None
  }
}

///|
priv struct EntryMeta {
  name : String
  name_bytes : BytesView
  flags : Int
  compression_method : Int
  local_header_offset : Int
  compressed_size : Int
  uncompressed_size : Int
  crc32 : UInt
  zip64_sizes : Bool
  central_record : Bytes
  central_zip64_uncompressed_position : Int?
  central_zip64_compressed_position : Int?
  zip64_offset_position : Int?
}

///|
priv struct LocalRecord {
  compressed : BytesView
  compression : Compression
  data_start : Int
  end : Int
  local_zip64_uncompressed_position : Int?
  local_zip64_compressed_position : Int?
  descriptor_signed : Bool
  descriptor_zip64 : Bool
}

///|
fn decode_name(bytes : BytesView, offset : Int) -> String raise ZipError {
  @encoding/utf8.decode(bytes) catch {
    _ => raise InvalidUtf8(offset~)
  }
}

///|
fn ensure_flags_supported(flags : Int) -> Unit raise ZipError {
  if (flags & flag_encrypted) != 0 {
    raise UnsupportedFeature(msg="encrypted zip entries not supported")
  }
  let supported = flag_data_descriptor |
    flag_utf8 |
    flag_deflate_max |
    flag_deflate_fast
  let unsupported = flags & supported.lnot()
  if unsupported != 0 {
    raise UnsupportedFeature(
      msg="unsupported zip flags: 0x\{unsupported.to_string(radix=16)}",
    )
  }
}

///|
fn parse_zip64_extra(
  extra : BytesView,
  raw_name : BytesView,
  decoded_name : StringView,
  need_uncomp : Bool,
  need_comp : Bool,
  need_offset : Bool,
  descriptor_sizes : Bool,
) -> (Bool, UInt64?, UInt64?, UInt64?) raise ZipError {
  let mut pos = 0
  let mut found_zip64 = false
  let mut found_unicode_path = false
  let mut uncomp : UInt64? = None
  let mut comp : UInt64? = None
  let mut offset : UInt64? = None
  while pos < extra.length() {
    if pos > extra.length() || 4 > extra.length() - pos {
      raise UnsupportedFeature(msg="truncated zip extra field header")
    }
    let header_id = read_u16_le_at(extra, pos)
    let data_size = read_u16_le_at(extra, pos + 2)
    pos = pos + 4
    if pos > extra.length() || data_size > extra.length() - pos {
      raise OutOfBounds(offset=pos)
    }
    let field = extra[pos:pos + data_size]
    pos = pos + data_size
    if header_id == unicode_path_extra_id {
      if found_unicode_path {
        raise UnsupportedFeature(msg="duplicate unicode path extra field")
      }
      found_unicode_path = true
      if field.length() < 5 || field[0] != 1 {
        raise UnsupportedFeature(msg="invalid unicode path extra field")
      }
      if read_u32_le_at_raw(field, 1) != crc32(raw_name) {
        raise UnsupportedFeature(msg="unicode path extra field CRC mismatch")
      }
      let unicode_name = decode_name(field[5:], 0)
      if unicode_name[:] != decoded_name {
        raise UnsupportedFeature(msg="unicode path extra field name mismatch")
      }
    } else if header_id == 0x0001 {
      if found_zip64 {
        raise UnsupportedFeature(msg="duplicate zip64 extra field")
      }
      found_zip64 = true
      // A streamed ZIP64 local header carries both size slots even when its
      // classic size fields are zero rather than 0xffffffff. Their values may
      // remain zero until the authoritative data descriptor is written.
      let read_uncomp = need_uncomp || descriptor_sizes
      let read_comp = need_comp || descriptor_sizes
      let required_size = (if read_uncomp { 8 } else { 0 }) +
        (if read_comp { 8 } else { 0 }) +
        (if need_offset { 8 } else { 0 })
      if field.length() != required_size {
        raise UnsupportedFeature(msg="zip64 extra field size mismatch")
      }
      let reader = Reader::new(field, 0)
      if read_uncomp {
        uncomp = Some(reader.read_u64_raw())
      }
      if read_comp {
        comp = Some(reader.read_u64_raw())
      }
      if need_offset {
        offset = Some(reader.read_u64_raw())
      }
    }
  }
  (found_zip64, uncomp, comp, offset)
}

///|
fn zip64_value_positions(
  extra : BytesView,
  need_uncomp : Bool,
  need_comp : Bool,
  need_offset : Bool,
  descriptor_sizes : Bool,
) -> (Int?, Int?, Int?) raise ZipError {
  let mut position = 0
  while position < extra.length() {
    if position > extra.length() || 4 > extra.length() - position {
      raise UnsupportedFeature(msg="truncated zip extra field header")
    }
    let header_id = read_u16_le_at(extra, position)
    let data_size = read_u16_le_at(extra, position + 2)
    let data_offset = position + 4
    if data_offset > extra.length() || data_size > extra.length() - data_offset {
      raise OutOfBounds(offset=data_offset)
    }
    if header_id == 0x0001 {
      let read_uncomp = need_uncomp || descriptor_sizes
      let read_comp = need_comp || descriptor_sizes
      let required_size = (if read_uncomp { 8 } else { 0 }) +
        (if read_comp { 8 } else { 0 }) +
        (if need_offset { 8 } else { 0 })
      if data_size != required_size {
        raise UnsupportedFeature(msg="zip64 extra field size mismatch")
      }
      let uncomp_position = if read_uncomp { Some(data_offset) } else { None }
      let comp_position = if read_comp {
        Some(data_offset + (if read_uncomp { 8 } else { 0 }))
      } else {
        None
      }
      let offset_position = if need_offset {
        Some(
          data_offset +
          (if read_uncomp { 8 } else { 0 }) +
          (if read_comp { 8 } else { 0 }),
        )
      } else {
        None
      }
      return (uncomp_position, comp_position, offset_position)
    }
    position = data_offset + data_size
  }
  if need_uncomp || need_comp || need_offset || descriptor_sizes {
    raise UnsupportedFeature(msg="required zip64 extra field is missing")
  }
  (None, None, None)
}

///|
fn read_zip64_eocd(
  bytes : BytesView,
  eocd_offset : Int,
  preserved_source_budget : PreservedSourceBudget,
  cancelled : () -> Bool,
) -> (UInt64, UInt64, UInt64, Int, Zip64TrailerTemplate) raise ZipError {
  let locator_offset = eocd_offset - 20
  if locator_offset < 0 {
    raise UnsupportedFeature(msg="zip64 locator missing")
  }
  let locator_sig = read_u32_le_at_raw(bytes, locator_offset)
  if locator_sig != zip64_locator_sig.reinterpret_as_uint() {
    raise UnsupportedFeature(msg="zip64 locator missing")
  }
  let locator_disk = read_u32_le_at_raw(bytes, locator_offset + 4)
  let zip64_offset_u64 = read_u64_le_at(bytes, locator_offset + 8)
  let locator_disks = read_u32_le_at_raw(bytes, locator_offset + 16)
  if locator_disk != 0 || locator_disks != 1 {
    raise UnsupportedFeature(msg="multi-disk zip not supported")
  }
  let zip64_offset = u64_to_int(zip64_offset_u64)
  if zip64_offset > locator_offset || 12 > locator_offset - zip64_offset {
    raise OutOfBounds(offset=zip64_offset)
  }
  let reader = Reader::new(bytes, zip64_offset)
  let sig = reader.read_u32_raw()
  if sig != zip64_eocd_sig.reinterpret_as_uint() {
    raise InvalidSignature(
      expected=zip64_eocd_sig,
      actual=sig.reinterpret_as_int(),
      offset=zip64_offset,
    )
  }
  let record_size = reader.read_u64_raw()
  let expected_record_size = (locator_offset - zip64_offset - 12).to_uint64()
  if record_size < 44 || record_size != expected_record_size {
    raise UnsupportedFeature(msg="zip64 end record size mismatch")
  }
  let _version_made = reader.read_u16()
  let _version_needed = reader.read_u16()
  let disk_no = reader.read_u32_raw()
  let disk_start = reader.read_u32_raw()
  let entries_on_disk = reader.read_u64_raw()
  let total_entries = reader.read_u64_raw()
  let central_size = reader.read_u64_raw()
  let central_offset = reader.read_u64_raw()
  if disk_no != 0 || disk_start != 0 || entries_on_disk != total_entries {
    raise UnsupportedFeature(msg="multi-disk zip not supported")
  }
  let end_record_length = locator_offset - zip64_offset
  let locator_length = eocd_offset - locator_offset
  let classic_end_record_length = bytes.length() - eocd_offset
  preserved_source_budget.charge(end_record_length)
  preserved_source_budget.charge(locator_length)
  preserved_source_budget.charge(classic_end_record_length)
  (
    total_entries,
    central_size,
    central_offset,
    zip64_offset,
    {
      end_record: copy_zip_bytes_cancellable(
        bytes[zip64_offset:locator_offset],
        cancelled,
      ),
      locator: copy_zip_bytes_cancellable(
        bytes[locator_offset:eocd_offset],
        cancelled,
      ),
      classic_end_record: copy_zip_bytes_cancellable(
        bytes[eocd_offset:],
        cancelled,
      ),
    },
  )
}

///|
fn compression_from_method(method_id : Int) -> Compression raise ZipError {
  match method_id {
    0 => Store
    8 => Deflate
    _ => raise UnsupportedCompression(method_id~)
  }
}

///|
fn resource_limit_actual(limit : Int) -> Int {
  if limit < 0x7FFFFFFF {
    limit + 1
  } else {
    limit
  }
}

///|
/// Bounds exact source-record copies retained for byte-preserving rewrites.
/// Charging happens before each allocation; the source input and inflated
/// payloads have independent limits.
priv struct PreservedSourceBudget {
  limit : Int?
  mut used : Int
}

///|
fn PreservedSourceBudget::new(limit : Int?) -> PreservedSourceBudget {
  { limit, used: 0 }
}

///|
fn PreservedSourceBudget::charge(
  self : PreservedSourceBudget,
  amount : Int,
) -> Unit raise ZipError {
  if amount < 0 {
    raise UnsupportedFeature(msg="invalid preserved ZIP source-record size")
  }
  match self.limit {
    Some(limit) if amount > limit - self.used =>
      raise ResourceLimitExceeded(
        kind="total_preserved_source_bytes",
        limit~,
        actual=resource_actual_with_base(self.used, amount),
      )
    Some(_) => self.used = self.used + amount
    None => ()
  }
}

///|
fn resource_actual_with_base(base : Int, actual : Int) -> Int {
  if base < 0 || actual < 0 || actual > 0x7FFFFFFF - base {
    0x7FFFFFFF
  } else {
    base + actual
  }
}

///|
fn u64_resource_actual(value : UInt64) -> Int {
  let max_int : UInt64 = 0x7FFFFFFF
  if value > max_int {
    0x7FFFFFFF
  } else {
    value.to_int()
  }
}

///|
fn local_size_matches(
  raw : UInt,
  zip64 : UInt64?,
  expected : Int,
  allow_placeholder : Bool,
) -> Bool {
  let expected64 = expected.to_uint64()
  let zip64_matches = match zip64 {
    Some(value) => value == expected64 || (allow_placeholder && value == 0)
    None => true
  }
  if allow_placeholder && raw == 0 {
    return zip64_matches
  }
  let max_u32 : UInt = 0xFFFFFFFF
  if raw == max_u32 {
    return match zip64 {
      Some(_) => zip64_matches
      None => false
    }
  }
  UInt64::extend_uint(raw) == expected64 && zip64_matches
}

///|
fn data_descriptor_record_matches(
  bytes : BytesView,
  offset : Int,
  meta : EntryMeta,
  zip64_sizes : Bool,
  signed : Bool,
) -> Bool {
  try {
    let reader = Reader::new(bytes, offset)
    if signed &&
      reader.read_u32_raw() != data_descriptor_sig.reinterpret_as_uint() {
      return false
    }
    if reader.read_u32_raw() != meta.crc32 {
      return false
    }
    if zip64_sizes {
      reader.read_u64_raw() == meta.compressed_size.to_uint64() &&
      reader.read_u64_raw() == meta.uncompressed_size.to_uint64()
    } else {
      reader.read_u32_raw() == meta.compressed_size.reinterpret_as_uint() &&
      reader.read_u32_raw() == meta.uncompressed_size.reinterpret_as_uint()
    }
  } catch {
    _ => false
  }
}

///|
fn data_descriptor_record_size(
  bytes : BytesView,
  offset : Int,
  meta : EntryMeta,
  zip64_sizes : Bool,
) -> Int raise ZipError {
  let signed_matches = data_descriptor_record_matches(
    bytes, offset, meta, zip64_sizes, true,
  )
  let unsigned_matches = data_descriptor_record_matches(
    bytes, offset, meta, zip64_sizes, false,
  )
  if signed_matches && unsigned_matches {
    raise UnsupportedFeature(msg="ambiguous data descriptor")
  }
  if !signed_matches && !unsigned_matches {
    raise UnsupportedFeature(msg="mismatched data descriptor")
  }
  (if signed_matches { 4 } else { 0 }) + 4 + (if zip64_sizes { 16 } else { 8 })
}

///|
fn read_entry_record(
  bytes : BytesView,
  meta : EntryMeta,
  central_offset : Int,
) -> LocalRecord raise ZipError {
  if meta.local_header_offset < 0 || meta.local_header_offset >= central_offset {
    raise OutOfBounds(offset=meta.local_header_offset)
  }
  let reader = Reader::new(bytes, meta.local_header_offset)
  let sig = reader.read_u32()
  if sig != local_header_sig {
    raise InvalidSignature(
      expected=local_header_sig,
      actual=sig,
      offset=meta.local_header_offset,
    )
  }
  let _version = reader.read_u16()
  let flags = reader.read_u16()
  ensure_flags_supported(flags)
  if flags != meta.flags {
    raise UnsupportedFeature(msg="mismatched local and central flags")
  }
  let has_descriptor = (flags & flag_data_descriptor) != 0
  let method_id = reader.read_u16()
  if method_id != meta.compression_method {
    raise UnsupportedFeature(
      msg="mismatched local and central compression method",
    )
  }
  let compression = compression_from_method(method_id)
  let _mod_time = reader.read_u16()
  let _mod_date = reader.read_u16()
  let local_crc32 = reader.read_u32_raw()
  let comp_size_raw = reader.read_u32_raw()
  let uncomp_size_raw = reader.read_u32_raw()
  let name_len = reader.read_u16()
  let extra_len = reader.read_u16()
  let local_name = reader.read_bytes(name_len)
  let local_extra = reader.read_bytes(extra_len)
  if local_name != meta.name_bytes {
    raise UnsupportedFeature(msg="mismatched local and central entry name")
  }
  let max_u32 : UInt = 0xFFFFFFFF
  let need_local_uncomp = uncomp_size_raw == max_u32
  let need_local_comp = comp_size_raw == max_u32
  let (local_zip64, local_zip_uncomp, local_zip_comp, _) = parse_zip64_extra(
    local_extra,
    local_name,
    meta.name,
    need_local_uncomp,
    need_local_comp,
    false,
    has_descriptor,
  )
  let (local_zip64_uncompressed_position, local_zip64_compressed_position, _) = if local_zip64 {
    let (uncomp, comp, offset) = zip64_value_positions(
      local_extra, need_local_uncomp, need_local_comp, false, has_descriptor,
    )
    (
      uncomp.map(position => 30 + name_len + position),
      comp.map(position => 30 + name_len + position),
      offset,
    )
  } else {
    (None, None, None)
  }
  if has_descriptor {
    if local_crc32 != 0 && local_crc32 != meta.crc32 {
      raise UnsupportedFeature(msg="mismatched local and central CRC-32")
    }
  } else if local_crc32 != meta.crc32 {
    raise UnsupportedFeature(msg="mismatched local and central CRC-32")
  }
  if !local_size_matches(
      comp_size_raw,
      local_zip_comp,
      meta.compressed_size,
      has_descriptor,
    ) {
    raise UnsupportedFeature(msg="mismatched local and central compressed size")
  }
  if !local_size_matches(
      uncomp_size_raw,
      local_zip_uncomp,
      meta.uncompressed_size,
      has_descriptor,
    ) {
    raise UnsupportedFeature(
      msg="mismatched local and central uncompressed size",
    )
  }
  if reader.pos > central_offset ||
    meta.compressed_size > central_offset - reader.pos {
    raise OutOfBounds(offset=reader.pos)
  }
  let data_start = reader.pos
  let compressed = reader.read_bytes(meta.compressed_size)
  let descriptor_zip64 = meta.zip64_sizes || local_zip64
  let mut descriptor_signed = false
  if has_descriptor {
    let descriptor_bytes = bytes[:central_offset]
    descriptor_signed = data_descriptor_record_matches(
      descriptor_bytes,
      reader.pos,
      meta,
      descriptor_zip64,
      true,
    )
    let descriptor_size = data_descriptor_record_size(
      descriptor_bytes,
      reader.pos,
      meta,
      descriptor_zip64,
    )
    reader.skip(descriptor_size)
  }
  {
    compressed,
    compression,
    data_start,
    end: reader.pos,
    local_zip64_uncompressed_position,
    local_zip64_compressed_position,
    descriptor_signed,
    descriptor_zip64,
  }
}

///|
fn materialize_entry_data(
  record : LocalRecord,
  meta : EntryMeta,
  max_uncompressed_bytes : Int?,
  limit_kind : String,
  reported_limit : Int,
  actual_base : Int,
  cancelled : () -> Bool,
) -> Bytes raise ZipError {
  check_zip_cancelled(cancelled)
  let data = match record.compression {
    Store => {
      match max_uncompressed_bytes {
        Some(limit) if record.compressed.length() > limit =>
          raise ResourceLimitExceeded(
            kind=limit_kind,
            limit=reported_limit,
            actual=resource_actual_with_base(
              actual_base,
              record.compressed.length(),
            ),
          )
        _ => ()
      }
      copy_zip_bytes_cancellable(record.compressed, cancelled)
    }
    Deflate => {
      // Allow legitimately large declared entries, but keep a hard ceiling so a
      // lying tiny archive cannot expand without bound (decompression bomb).
      let max_output = match max_uncompressed_bytes {
        Some(limit) => limit
        None =>
          if meta.uncompressed_size > deflate_max_output_default {
            meta.uncompressed_size
          } else {
            deflate_max_output_default
          }
      }
      deflate_decode(record.compressed, max_output~, cancelled~) catch {
        OutputLimitExceeded(limit~) =>
          match max_uncompressed_bytes {
            Some(_) =>
              raise ResourceLimitExceeded(
                kind=limit_kind,
                limit=reported_limit,
                actual=resource_actual_with_base(
                  actual_base,
                  resource_limit_actual(max_output),
                ),
              )
            None => raise OutputLimitExceeded(limit~)
          }
        error => raise error
      }
    }
  }
  if data.length() != meta.uncompressed_size {
    raise UnsupportedFeature(
      msg="decoded data length does not match central size",
    )
  }
  check_zip_cancelled(cancelled)
  data
}

///|
fn read_impl(
  bytes : BytesView,
  max_package_bytes : Int?,
  max_entries : Int?,
  max_entry_uncompressed_bytes : Int?,
  max_total_uncompressed_bytes : Int?,
  max_total_preserved_source_bytes : Int?,
  cancelled : () -> Bool,
) -> Archive raise ZipError {
  check_zip_cancelled(cancelled)
  match max_package_bytes {
    Some(limit) if bytes.length() > limit =>
      raise ResourceLimitExceeded(
        kind="package_bytes",
        limit~,
        actual=bytes.length(),
      )
    _ => ()
  }
  let preserved_source_budget = PreservedSourceBudget::new(
    max_total_preserved_source_bytes,
  )
  let eocd_offset = match find_end_of_central(bytes) {
    Some(offset) => offset
    None => raise MissingEndOfCentral
  }
  let reader = Reader::new(bytes, eocd_offset)
  ignore(reader.read_u32())
  let disk_no = reader.read_u16()
  let disk_start = reader.read_u16()
  let disk_entries = reader.read_u16()
  let total_entries = reader.read_u16()
  let central_size_raw = reader.read_u32_raw()
  let central_offset_raw = reader.read_u32_raw()
  let comment_len = reader.read_u16()
  preserved_source_budget.charge(comment_len)
  let archive_comment = copy_zip_bytes_cancellable(
    reader.read_bytes(comment_len),
    cancelled,
  )
  if disk_no != 0 || disk_start != 0 {
    raise UnsupportedFeature(msg="multi-disk zip not supported")
  }
  let max_u32 : UInt = 0xFFFFFFFF
  let needs_zip64 = disk_entries == 0xFFFF ||
    total_entries == 0xFFFF ||
    central_size_raw == max_u32 ||
    central_offset_raw == max_u32
  let (
    entry_count_raw,
    central_size_raw64,
    central_offset_raw64,
    central_boundary,
    zip64_trailer,
  ) = if needs_zip64 {
    let (zip_entries, zip_size, zip_offset, zip64_offset, zip64_trailer) = read_zip64_eocd(
      bytes, eocd_offset, preserved_source_budget, cancelled,
    )
    if disk_entries != 0xFFFF && disk_entries.to_uint64() != zip_entries {
      raise UnsupportedFeature(msg="classic and zip64 entry count mismatch")
    }
    if total_entries != 0xFFFF && total_entries.to_uint64() != zip_entries {
      raise UnsupportedFeature(msg="classic and zip64 entry count mismatch")
    }
    if central_size_raw != max_u32 &&
      UInt64::extend_uint(central_size_raw) != zip_size {
      raise UnsupportedFeature(msg="classic and zip64 central size mismatch")
    }
    if central_offset_raw != max_u32 &&
      UInt64::extend_uint(central_offset_raw) != zip_offset {
      raise UnsupportedFeature(msg="classic and zip64 central offset mismatch")
    }
    (
      if total_entries == 0xFFFF {
        zip_entries
      } else {
        total_entries.to_uint64()
      },
      if central_size_raw == max_u32 {
        zip_size
      } else {
        UInt64::extend_uint(central_size_raw)
      },
      if central_offset_raw == max_u32 {
        zip_offset
      } else {
        UInt64::extend_uint(central_offset_raw)
      },
      zip64_offset,
      Some(zip64_trailer),
    )
  } else {
    if disk_entries != total_entries {
      raise UnsupportedFeature(msg="multi-disk zip not supported")
    }
    (
      total_entries.to_uint64(),
      u32_to_int(central_size_raw).to_uint64(),
      u32_to_int(central_offset_raw).to_uint64(),
      eocd_offset,
      None,
    )
  }
  match max_entries {
    Some(limit) if entry_count_raw > limit.to_uint64() =>
      raise ResourceLimitExceeded(
        kind="entry_count",
        limit~,
        actual=u64_resource_actual(entry_count_raw),
      )
    _ => ()
  }
  let entry_count = u64_to_int(entry_count_raw)
  let central_size = u64_to_int(central_size_raw64)
  let central_offset = u64_to_int(central_offset_raw64)
  if central_offset > central_boundary ||
    central_boundary > bytes.length() ||
    central_size != central_boundary - central_offset {
    raise OutOfBounds(offset=central_offset)
  }
  // The declared entry count is attacker-controlled (zip64 EOCD): clamp the
  // preallocation against what the central directory could actually hold
  // (its range was validated against the input just above). Each central
  // directory record needs at least 46 bytes, so a lying count still fails
  // during parsing without a huge up-front allocation.
  let max_plausible_entries = central_size / 46
  let metas : Array[EntryMeta] = Array::new(
    capacity=if entry_count < max_plausible_entries {
      entry_count
    } else {
      max_plausible_entries
    },
  )
  let central_bytes = bytes[central_offset:central_boundary]
  let central_reader = Reader::new(central_bytes, 0)
  let mut declared_total = 0
  for entry_index in 0.. {
          match max_entry_uncompressed_bytes {
            Some(limit) if value > limit.to_uint64() =>
              raise ResourceLimitExceeded(
                kind="entry_uncompressed_bytes",
                limit~,
                actual=u64_resource_actual(value),
              )
            _ => ()
          }
          match max_total_uncompressed_bytes {
            Some(limit) if value > limit.to_uint64() =>
              raise ResourceLimitExceeded(
                kind="total_uncompressed_bytes",
                limit~,
                actual=u64_resource_actual(value),
              )
            _ => ()
          }
          u64_to_int(value)
        }
        None => raise UnsupportedFeature(msg="zip64 size missing")
      }
    } else {
      let value = UInt64::extend_uint(uncomp_size_raw)
      match max_entry_uncompressed_bytes {
        Some(limit) if value > limit.to_uint64() =>
          raise ResourceLimitExceeded(
            kind="entry_uncompressed_bytes",
            limit~,
            actual=u64_resource_actual(value),
          )
        _ => ()
      }
      match max_total_uncompressed_bytes {
        Some(limit) if value > limit.to_uint64() =>
          raise ResourceLimitExceeded(
            kind="total_uncompressed_bytes",
            limit~,
            actual=u64_resource_actual(value),
          )
        _ => ()
      }
      u32_to_int(uncomp_size_raw)
    }
    let comp_size = if need_comp {
      match zip_comp {
        Some(value) => u64_to_int(value)
        None => raise UnsupportedFeature(msg="zip64 size missing")
      }
    } else {
      u32_to_int(comp_size_raw)
    }
    let local_offset = if need_offset {
      match zip_offset {
        Some(value) => u64_to_int(value)
        None => raise UnsupportedFeature(msg="zip64 offset missing")
      }
    } else {
      u32_to_int(local_offset_raw)
    }
    let (
      central_zip64_uncompressed_position,
      central_zip64_compressed_position,
      central_zip64_offset_position,
    ) = zip64_value_positions(
      extra_bytes, need_uncomp, need_comp, need_offset, false,
    )
    let central_extra_start = 46 + name_len
    ignore(compression_from_method(method_id))
    match max_entry_uncompressed_bytes {
      Some(limit) if uncomp_size > limit =>
        raise ResourceLimitExceeded(
          kind="entry_uncompressed_bytes",
          limit~,
          actual=uncomp_size,
        )
      _ => ()
    }
    match max_total_uncompressed_bytes {
      Some(limit) if uncomp_size > limit - declared_total =>
        raise ResourceLimitExceeded(
          kind="total_uncompressed_bytes",
          limit~,
          actual=resource_limit_actual(limit),
        )
      _ => declared_total = declared_total + uncomp_size
    }
    metas.push({
      name,
      name_bytes,
      flags,
      compression_method: method_id,
      local_header_offset: local_offset,
      compressed_size: comp_size,
      uncompressed_size: uncomp_size,
      crc32,
      zip64_sizes: need_uncomp || need_comp,
      central_record,
      central_zip64_uncompressed_position: central_zip64_uncompressed_position.map(position => {
          central_extra_start + position
        },
      ),
      central_zip64_compressed_position: central_zip64_compressed_position.map(position => {
        central_extra_start + position
      }),
      zip64_offset_position: central_zip64_offset_position.map(position => {
        central_extra_start + position
      }),
    })
  }
  if central_reader.pos != central_size {
    raise UnsupportedFeature(msg="central directory size/count mismatch")
  }
  let records : Array[LocalRecord] = Array::new(capacity=metas.length())
  let local_intervals : Array[(Int, Int)] = Array::new(capacity=metas.length())
  for meta in metas {
    check_zip_cancelled(cancelled)
    let record = read_entry_record(bytes, meta, central_offset)
    preserved_source_budget.charge(record.end - meta.local_header_offset)
    records.push(record)
    local_intervals.push((meta.local_header_offset, record.end))
  }
  local_intervals.sort_by((left, right) => {
    let by_start = left.0.compare(right.0)
    if by_start != 0 {
      by_start
    } else {
      left.1.compare(right.1)
    }
  })
  let mut covered_until = 0
  for interval in local_intervals {
    check_zip_cancelled(cancelled)
    if interval.0 < covered_until {
      raise UnsupportedFeature(msg="overlapping zip local records")
    }
    if interval.0 != covered_until {
      raise UnsupportedFeature(
        msg="unreferenced data before zip central directory",
      )
    }
    covered_until = interval.1
  }
  if covered_until != central_offset {
    raise UnsupportedFeature(
      msg="unreferenced data before zip central directory",
    )
  }
  let bounded_source = match
    (
      max_package_bytes, max_entries, max_entry_uncompressed_bytes, max_total_uncompressed_bytes,
      max_total_preserved_source_bytes,
    ) {
    (
      Some(_),
      Some(max_entries),
      Some(max_entry),
      Some(max_total),
      Some(max_preserved),
    ) =>
      Some({
        package_bytes: bytes.length(),
        max_entries,
        max_entry_uncompressed_bytes: max_entry,
        max_total_uncompressed_bytes: max_total,
        max_total_preserved_source_bytes: max_preserved,
      })
    _ => None
  }
  let archive = archive_from_source(
    archive_comment, zip64_trailer, bounded_source,
  )
  let mut actual_total = 0
  for index in 0.. limit
      None => 0
    }
    let mut actual_base = 0
    match max_total_uncompressed_bytes {
      Some(total_limit) => {
        let remaining = total_limit - actual_total
        match materialize_limit {
          Some(entry_limit) if entry_limit <= remaining => ()
          _ => {
            materialize_limit = Some(remaining)
            limit_kind = "total_uncompressed_bytes"
            reported_limit = total_limit
            actual_base = actual_total
          }
        }
      }
      None => ()
    }
    let data = materialize_entry_data(
      record, meta, materialize_limit, limit_kind, reported_limit, actual_base, cancelled,
    )
    match max_total_uncompressed_bytes {
      Some(limit) if data.length() > limit - actual_total =>
        raise ResourceLimitExceeded(
          kind="total_uncompressed_bytes",
          limit~,
          actual=resource_limit_actual(limit),
        )
      _ => actual_total = actual_total + data.length()
    }
    // Construct the entry directly so it carries the central directory's
    // STORED crc (Archive::add would recompute from the inflated data,
    // hiding any mismatch from validators).
    archive.entries.push({
      name: meta.name,
      data,
      compression: record.compression,
      data_descriptor: (meta.flags & flag_data_descriptor) != 0,
      crc32: meta.crc32,
      source_local_offset: Some(meta.local_header_offset),
      source: Some({
        local_record: copy_zip_bytes_cancellable(
          bytes[meta.local_header_offset:record.end],
          cancelled,
        ),
        local_header_length: record.data_start - meta.local_header_offset,
        central_record: meta.central_record,
        local_zip64_uncompressed_position: record.local_zip64_uncompressed_position,
        local_zip64_compressed_position: record.local_zip64_compressed_position,
        central_zip64_uncompressed_position: meta.central_zip64_uncompressed_position,
        central_zip64_compressed_position: meta.central_zip64_compressed_position,
        central_zip64_offset_position: meta.zip64_offset_position,
        descriptor_signed: record.descriptor_signed,
        descriptor_zip64: record.descriptor_zip64,
      }),
      source_unchanged: true,
    })
  }
  check_zip_cancelled(cancelled)
  archive
}

///|
/// Reads a ZIP archive using the library's compatibility defaults.
pub fn read(bytes : BytesView) -> Archive raise ZipError {
  read_impl(bytes, None, None, None, None, None, () => false)
}

///|
/// Reads a ZIP archive while enforcing caller-provided limits on source-package
/// bytes, entries, inflated payloads, and retained byte-preservation records.
/// Every bounded allocation is rejected before it is materialized.
pub fn read_limited(
  bytes : BytesView,
  max_package_bytes~ : Int,
  max_entries~ : Int,
  max_entry_uncompressed_bytes~ : Int,
  max_total_uncompressed_bytes~ : Int,
  max_total_preserved_source_bytes~ : Int,
  cancelled? : () -> Bool = () => false,
) -> Archive raise ZipError {
  if max_package_bytes < 0 ||
    max_entries < 0 ||
    max_entry_uncompressed_bytes < 0 ||
    max_total_uncompressed_bytes < 0 ||
    max_total_preserved_source_bytes < 0 {
    raise UnsupportedFeature(msg="ZIP resource limits must be non-negative")
  }
  read_impl(
    bytes,
    Some(max_package_bytes),
    Some(max_entries),
    Some(max_entry_uncompressed_bytes),
    Some(max_total_uncompressed_bytes),
    Some(max_total_preserved_source_bytes),
    cancelled,
  )
}

///|
fn push_u16_le(buf : Array[Byte], value : Int) -> Unit {
  let v = value.reinterpret_as_uint()
  buf.push((v & 0xFF).to_byte())
  buf.push(((v >> 8) & 0xFF).to_byte())
}

///|
fn push_u32_le(buf : Array[Byte], value : UInt) -> Unit {
  buf.push((value & 0xFF).to_byte())
  buf.push(((value >> 8) & 0xFF).to_byte())
  buf.push(((value >> 16) & 0xFF).to_byte())
  buf.push(((value >> 24) & 0xFF).to_byte())
}

///|
fn push_u64_le(buf : Array[Byte], value : UInt64) -> Unit {
  for shift in [0, 8, 16, 24, 32, 40, 48, 56] {
    buf.push(((value >> shift).to_uint() & 0xFF).to_byte())
  }
}

///|
fn push_bytes(buf : Array[Byte], bytes : BytesView) -> Unit {
  for b in bytes {
    buf.push(b)
  }
}

///|
test "ZIP owned copies observe cancellation between chunks" {
  let source = Bytes::make(256 * 1024, b'x')
  let checks = [0]
  try
    copy_zip_bytes_cancellable(source, () => {
      checks[0] += 1
      checks[0] >= 3
    })
  catch {
    ReadCancelled => assert_true(checks[0] >= 3)
    _ => fail("expected ZIP copy cancellation")
  } noraise {
    _ => fail("expected ZIP copy cancellation")
  }
}

///|
test "zip read data descriptor" {
  let name : Bytes = b"a.txt"
  let data : Bytes = b"hi"
  let crc = crc32(data)
  let buf : Array[Byte] = []
  // local header
  push_u32_le(buf, 0x04034B50)
  push_u16_le(buf, 20)
  push_u16_le(buf, 0x0808)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u32_le(buf, 0)
  push_u32_le(buf, 0)
  push_u32_le(buf, 0)
  push_u16_le(buf, name.length())
  push_u16_le(buf, 0)
  push_bytes(buf, name)
  push_bytes(buf, data)
  // data descriptor
  push_u32_le(buf, 0x08074B50)
  push_u32_le(buf, crc)
  push_u32_le(buf, data.length().reinterpret_as_uint())
  push_u32_le(buf, data.length().reinterpret_as_uint())
  // central directory
  let central_offset = buf.length()
  push_u32_le(buf, 0x02014B50)
  push_u16_le(buf, 20)
  push_u16_le(buf, 20)
  push_u16_le(buf, 0x0808)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u32_le(buf, crc)
  push_u32_le(buf, data.length().reinterpret_as_uint())
  push_u32_le(buf, data.length().reinterpret_as_uint())
  push_u16_le(buf, name.length())
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u32_le(buf, 0)
  push_u32_le(buf, 0)
  push_bytes(buf, name)
  // end of central directory
  let central_size = buf.length() - central_offset
  push_u32_le(buf, 0x06054B50)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u16_le(buf, 1)
  push_u16_le(buf, 1)
  push_u32_le(buf, central_size.reinterpret_as_uint())
  push_u32_le(buf, central_offset.reinterpret_as_uint())
  push_u16_le(buf, 0)
  let archive = read(Bytes::from_array(buf))
  inspect(archive.get("a.txt") == Some(b"hi"), content="true")
}

///|
fn make_streamed_zip64_descriptor_archive(
  local_size : UInt,
  zip64_placeholder : UInt64,
) -> Bytes {
  let name : Bytes = b"streamed.txt"
  let data : Bytes = b"zip64-stream"
  let crc = crc32(data)
  let buf : Array[Byte] = []
  // Streamed local header: ZIP64 is selected before the final sizes are
  // known, while the central directory can later use its 32-bit fields.
  push_u32_le(buf, 0x04034B50)
  push_u16_le(buf, 45)
  push_u16_le(buf, 0x0808)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u32_le(buf, 0)
  push_u32_le(buf, local_size)
  push_u32_le(buf, local_size)
  push_u16_le(buf, name.length())
  push_u16_le(buf, 20)
  push_bytes(buf, name)
  push_u16_le(buf, 0x0001)
  push_u16_le(buf, 16)
  push_u64_le(buf, zip64_placeholder)
  push_u64_le(buf, zip64_placeholder)
  push_bytes(buf, data)
  // ZIP64 data descriptor selected by the local ZIP64 extra field.
  push_u32_le(buf, 0x08074B50)
  push_u32_le(buf, crc)
  push_u64_le(buf, data.length().to_uint64())
  push_u64_le(buf, data.length().to_uint64())
  let central_offset = buf.length()
  push_u32_le(buf, 0x02014B50)
  push_u16_le(buf, 45)
  push_u16_le(buf, 45)
  push_u16_le(buf, 0x0808)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u32_le(buf, crc)
  push_u32_le(buf, data.length().reinterpret_as_uint())
  push_u32_le(buf, data.length().reinterpret_as_uint())
  push_u16_le(buf, name.length())
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u32_le(buf, 0)
  push_u32_le(buf, 0)
  push_bytes(buf, name)
  let central_size = buf.length() - central_offset
  push_u32_le(buf, 0x06054B50)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u16_le(buf, 1)
  push_u16_le(buf, 1)
  push_u32_le(buf, central_size.reinterpret_as_uint())
  push_u32_le(buf, central_offset.reinterpret_as_uint())
  push_u16_le(buf, 0)
  Bytes::from_array(buf)
}

///|
test "zip read streamed ZIP64 descriptors with ZIP32 central sizes" {
  for local_size in [(0xFFFFFFFF : UInt), (0 : UInt)] {
    let archive = read(make_streamed_zip64_descriptor_archive(local_size, 0))
    inspect(
      archive.get("streamed.txt") == Some(b"zip64-stream"),
      content="true",
    )
  }
}

///|
test "zip read rejects mismatched streamed ZIP64 placeholders" {
  try read(make_streamed_zip64_descriptor_archive(0xFFFFFFFF, 1)) catch {
    UnsupportedFeature(msg~) =>
      inspect(msg, content="mismatched local and central compressed size")
    _ => fail("expected mismatched ZIP64 placeholder")
  } noraise {
    _ => fail("expected mismatched ZIP64 placeholder")
  }
}

///|
test "zip read zip64 extra sizes" {
  let name : Bytes = b"b.txt"
  let data : Bytes = b"zip64"
  let crc = crc32(data)
  let buf : Array[Byte] = []
  // local header with zip64 extra
  push_u32_le(buf, 0x04034B50)
  push_u16_le(buf, 45)
  push_u16_le(buf, 0x0800)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u32_le(buf, crc)
  push_u32_le(buf, 0xFFFFFFFF)
  push_u32_le(buf, 0xFFFFFFFF)
  push_u16_le(buf, name.length())
  push_u16_le(buf, 20)
  push_bytes(buf, name)
  // zip64 extra (uncomp, comp)
  push_u16_le(buf, 0x0001)
  push_u16_le(buf, 16)
  push_u64_le(buf, UInt64::extend_uint(data.length().reinterpret_as_uint()))
  push_u64_le(buf, UInt64::extend_uint(data.length().reinterpret_as_uint()))
  push_bytes(buf, data)
  // central directory with zip64 extra (uncomp, comp, offset)
  let central_offset = buf.length()
  push_u32_le(buf, 0x02014B50)
  push_u16_le(buf, 45)
  push_u16_le(buf, 45)
  push_u16_le(buf, 0x0800)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u32_le(buf, crc)
  push_u32_le(buf, 0xFFFFFFFF)
  push_u32_le(buf, 0xFFFFFFFF)
  push_u16_le(buf, name.length())
  push_u16_le(buf, 28)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u32_le(buf, 0)
  push_u32_le(buf, 0xFFFFFFFF)
  push_bytes(buf, name)
  push_u16_le(buf, 0x0001)
  push_u16_le(buf, 24)
  let central_uncompressed_offset = buf.length()
  push_u64_le(buf, UInt64::extend_uint(data.length().reinterpret_as_uint()))
  push_u64_le(buf, UInt64::extend_uint(data.length().reinterpret_as_uint()))
  push_u64_le(buf, UInt64::extend_uint(0))
  // end of central directory
  let central_size = buf.length() - central_offset
  push_u32_le(buf, 0x06054B50)
  push_u16_le(buf, 0)
  push_u16_le(buf, 0)
  push_u16_le(buf, 1)
  push_u16_le(buf, 1)
  push_u32_le(buf, central_size.reinterpret_as_uint())
  push_u32_le(buf, central_offset.reinterpret_as_uint())
  push_u16_le(buf, 0)
  let encoded = Bytes::from_array(buf)
  let archive = read(encoded)
  inspect(archive.get("b.txt") == Some(b"zip64"), content="true")
  let oversized = encoded.to_array()
  for index in 0..<8 {
    oversized[central_uncompressed_offset + index] = (0).to_byte()
  }
  oversized[central_uncompressed_offset + 4] = (1).to_byte()
  try
    read_limited(
      Bytes::from_array(oversized),
      max_package_bytes=oversized.length(),
      max_entries=1,
      max_entry_uncompressed_bytes=64,
      max_total_uncompressed_bytes=64,
      max_total_preserved_source_bytes=oversized.length(),
    )
  catch {
    ResourceLimitExceeded(kind~, limit~, actual~) => {
      inspect(kind, content="entry_uncompressed_bytes")
      inspect(limit, content="64")
      inspect(actual, content="2147483647")
    }
    _ => fail("expected ZIP64 resource limit")
  } noraise {
    _ => fail("expected oversized ZIP64 declaration to raise")
  }
}

///|
test "zip64 extra values cannot escape their declared field" {
  let followed_by_other_field : Bytes = [
    // Empty ZIP64 field.
    0x01, 0x00, 0x00, 0x00,
    // Unrelated eight-byte field that must not supply the ZIP64 value.
     0xFE, 0xCA, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  ]
  try
    parse_zip64_extra(
      followed_by_other_field, b"", "", true, false, false, false,
    )
  catch {
    UnsupportedFeature(msg~) =>
      inspect(msg, content="zip64 extra field size mismatch")
    _ => fail("expected bounded ZIP64 field failure")
  } noraise {
    _ => fail("expected bounded ZIP64 field failure")
  }
  let trailing_partial_header : Bytes = [0xFE, 0xCA, 0x00, 0x00, 0x01]
  try
    parse_zip64_extra(
      trailing_partial_header, b"", "", false, false, false, false,
    )
  catch {
    UnsupportedFeature(msg~) =>
      inspect(msg, content="truncated zip extra field header")
    _ => fail("expected truncated extra-field failure")
  } noraise {
    _ => fail("expected truncated extra-field failure")
  }
}

///|
test "zip reader wb: direct guard error branches" {
  let reader = Reader::new(b"", 0)
  try reader.read_bytes(-1) catch {
    e => inspect(e is OutOfBounds(_), content="true")
  } noraise {
    _ => fail("expected read_bytes to raise")
  }
  try reader.skip(-1) catch {
    e => inspect(e is OutOfBounds(_), content="true")
  } noraise {
    _ => fail("expected skip to raise")
  }
  try u64_to_int(0x1_0000_0000) catch {
    e => inspect(e is UnsupportedFeature(_), content="true")
  } noraise {
    _ => fail("expected u64_to_int to raise")
  }
  try read_u16_le_at(b"\x00", 0) catch {
    e => inspect(e is OutOfBounds(_), content="true")
  } noraise {
    _ => fail("expected read_u16_le_at to raise")
  }
  try read_u32_le_at_raw(b"\x00\x01", 0) catch {
    e => inspect(e is OutOfBounds(_), content="true")
  } noraise {
    _ => fail("expected read_u32_le_at_raw to raise")
  }
  try read_u64_le_at(b"\x00\x01\x02", 0) catch {
    e => inspect(e is OutOfBounds(_), content="true")
  } noraise {
    _ => fail("expected read_u64_le_at to raise")
  }
}