///|
fn read_fixed_array_chunks(
  bytes : Bytes,
  layout : DataLayout,
  dataspace : Dataspace,
  datatype : Datatype,
  offset_size : Int32,
  length_size : Int32,
  base_address : UInt64,
  filters : Array[Filter],
  output : FixedArray[Byte],
  undefined : UInt64,
  page_bits : Int32,
  selection : Array[UInt64]?,
) -> Unit raise Hdf5Error {
  guard page_bits > 0 && page_bits <= 31 else {
    raise InvalidField("invalid fixed array page bits")
  }
  let rank = dataspace.dimensions.length()
  guard dataspace.maximum_dimensions.length() == rank else {
    raise InvalidField("fixed array requires maximum dimensions")
  }
  let current_grid : Array[Int64] = []
  let maximum_grid : Array[Int64] = []
  for dimension in 0.. 0 else {
      raise InvalidField("invalid fixed array dimensions")
    }
    current_grid.push((current.reinterpret_as_int64() + width - 1) / width)
    maximum_grid.push((maximum.reinterpret_as_int64() + width - 1) / width)
  }
  let maximum_total = product(maximum_grid)
  let chunk_size = chunk_byte_size(layout.chunk_dimensions, datatype.size)
  let header_offset = address_to_offset(layout.data_address, base_address)
  let header = Reader::at(bytes, header_offset)
  guard header.read_bytes(4) == b"FAHD" && header.read_u8() == b'\x00' else {
    raise InvalidField("invalid fixed array header")
  }
  let client = header.read_u8().to_int()
  let entry_size = header.read_u8().to_int()
  let size_width = entry_size - offset_size - 4
  guard header.read_u8().to_int() == page_bits &&
    (
      (client == 0 && filters.length() == 0 && entry_size == offset_size) ||
      (
        client == 1 &&
        filters.length() > 0 &&
        size_width >= 1 &&
        size_width <= 8
      )
    ) else {
    raise InvalidField("invalid fixed array entry encoding")
  }
  guard header.read_uint_le(length_size) ==
    maximum_total.reinterpret_as_uint64() else {
    raise InvalidField(
      "fixed array entry count does not match maximum dimensions",
    )
  }
  let block_address = header.read_uint_le(offset_size)
  verify_metadata_checksum(
    bytes,
    header_offset,
    header.position(),
    "fixed array header",
  )
  header.skip(4)
  if block_address == undefined {
    return
  }
  let block_offset = address_to_offset(block_address, base_address)
  let block = Reader::at(bytes, block_offset)
  guard block.read_bytes(4) == b"FADB" &&
    block.read_u8() == b'\x00' &&
    block.read_u8().to_int() == client &&
    block.read_uint_le(offset_size) == layout.data_address else {
    raise InvalidField("invalid fixed array data block")
  }
  let page_entries = 1L << page_bits
  let paged = maximum_total > page_entries
  let page_count = (maximum_total + page_entries - 1) / page_entries
  let bitmap = if paged {
    let bitmap = block.read_bytes(((page_count + 7) / 8).to_int())
    verify_metadata_checksum(
      bytes,
      block_offset,
      block.position(),
      "fixed array data block",
    )
    block.skip(4)
    bitmap
  } else {
    b""
  }
  let entries_start = block.position().to_int64()
  if !paged {
    verify_metadata_checksum(
      bytes,
      block_offset,
      (entries_start + maximum_total * entry_size.to_int64()).to_int(),
      "fixed array data block",
    )
  }
  let storage_size = maximum_total * entry_size.to_int64() +
    (if paged { page_count } else { 1L }) * 4
  guard storage_size <= bytes.length().to_int64() - entries_start else {
    raise InvalidField("fixed array entries extend beyond input")
  }
  if output.length() == 0 {
    return
  }
  let total = product(current_grid)
  for linear in 0.. 0 {
      dimension = dimension - 1
      coordinates[dimension] = remaining % current_grid[dimension]
      remaining = remaining / current_grid[dimension]
      offsets[dimension] = (coordinates[dimension] *
      layout.chunk_dimensions[dimension].to_int64()).reinterpret_as_uint64()
    }
    let mut stored_index = 0L
    for dimension in 0..> (page.to_int() % 8))) == 0 {
      continue
    }
    if paged {
      let page_elements = if page + 1 == page_count {
        maximum_total - page * page_entries
      } else {
        page_entries
      }
      let page_offset = entries_start +
        page * page_entries * entry_size.to_int64() +
        page * 4L
      verify_metadata_checksum(
        bytes,
        page_offset.to_int(),
        (page_offset + page_elements * entry_size.to_int64()).to_int(),
        "fixed array data block page",
      )
    }
    let position = entries_start +
      stored_index * entry_size.to_int64() +
      (if paged { page * 4 } else { 0L })
    let entry = Reader::at(bytes, position.to_int())
    let address = entry.read_uint_le(offset_size)
    if address == undefined {
      continue
    }
    let mut stored_size = chunk_size.to_uint64()
    let mut mask = 0U
    if client == 1 {
      stored_size = entry.read_var_uint_le(size_width)
      mask = entry.read_u32_le()
    }
    if !chunk_contains_selection(
        offsets,
        layout.chunk_dimensions,
        dataspace.dimensions,
        selection,
      ) {
      continue
    }
    let decoded = read_filtered_chunk(
      bytes,
      address,
      base_address,
      stored_size,
      filters,
      mask,
      datatype.size,
      chunk_size,
    )
    copy_chunk(
      decoded,
      decoded.length(),
      offsets,
      layout.chunk_dimensions,
      dataspace.dimensions,
      datatype.size,
      output,
      selection,
    )
  }
}

///|
fn read_source_fixed_array_chunks(
  source : &Hdf5Source,
  layout : DataLayout,
  dataspace : Dataspace,
  datatype : Datatype,
  offset_size : Int32,
  length_size : Int32,
  base_address : UInt64,
  filters : Array[Filter],
  output : FixedArray[Byte],
  undefined : UInt64,
  page_bits : Int32,
  selection : Array[UInt64]?,
) -> Unit raise Hdf5Error {
  guard page_bits > 0 && page_bits <= 31 else {
    raise InvalidField("invalid fixed array page bits")
  }
  let rank = dataspace.dimensions.length()
  guard dataspace.maximum_dimensions.length() == rank else {
    raise InvalidField("fixed array requires maximum dimensions")
  }
  let current_grid : Array[Int64] = []
  let maximum_grid : Array[Int64] = []
  for dimension in 0.. 0 else {
      raise InvalidField("invalid fixed array dimensions")
    }
    current_grid.push((current.reinterpret_as_int64() + width - 1) / width)
    maximum_grid.push((maximum.reinterpret_as_int64() + width - 1) / width)
  }
  let maximum_total = product(maximum_grid)
  let chunk_size = chunk_byte_size(layout.chunk_dimensions, datatype.size)
  let header_offset = source_address_to_offset(
    layout.data_address,
    base_address,
  )
  let header_content = 8 + length_size + offset_size
  let header = Reader::new(
    source_read_checksummed(
      source, header_offset, header_content, "fixed array header",
    ),
  )
  guard header.read_bytes(4) == b"FAHD" && header.read_u8() == b'\x00' else {
    raise InvalidField("invalid fixed array header")
  }
  let client = header.read_u8().to_int()
  let entry_size = header.read_u8().to_int()
  let size_width = entry_size - offset_size - 4
  guard header.read_u8().to_int() == page_bits &&
    (
      (client == 0 && filters.length() == 0 && entry_size == offset_size) ||
      (
        client == 1 &&
        filters.length() > 0 &&
        size_width >= 1 &&
        size_width <= 8
      )
    ) else {
    raise InvalidField("invalid fixed array entry encoding")
  }
  guard header.read_uint_le(length_size) ==
    maximum_total.reinterpret_as_uint64() else {
    raise InvalidField(
      "fixed array entry count does not match maximum dimensions",
    )
  }
  let block_address = header.read_uint_le(offset_size)
  if block_address == undefined || output.length() == 0 {
    return
  }
  let block_offset = source_address_to_offset(block_address, base_address)
  let block_prefix_size = 6 + offset_size
  let prefix = Reader::new(source.read_at(block_offset, block_prefix_size))
  guard prefix.read_bytes(4) == b"FADB" &&
    prefix.read_u8() == b'\x00' &&
    prefix.read_u8().to_int() == client &&
    prefix.read_uint_le(offset_size) == layout.data_address else {
    raise InvalidField("invalid fixed array data block")
  }
  let page_entries = 1L << page_bits
  let paged = maximum_total > page_entries
  let page_count = (maximum_total + page_entries - 1) / page_entries
  let bitmap_size = if paged { ((page_count + 7) / 8).to_int() } else { 0 }
  let block_header = if paged {
    source_read_checksummed(
      source,
      block_offset,
      block_prefix_size + bitmap_size,
      "fixed array data block",
    )
  } else {
    b""
  }
  let bitmap = if paged {
    block_header
    .get_view(start=block_prefix_size, end=block_prefix_size + bitmap_size)
    .unwrap()
    .to_owned()
  } else {
    b""
  }
  let entries_start = block_offset +
    block_prefix_size.to_uint64() +
    (if paged { bitmap_size + 4 } else { 0 }).to_uint64()
  let unpaged = if paged {
    b""
  } else {
    guard maximum_total <= 0x7FFFFFFFL / entry_size.to_int64() else {
      raise InvalidField("fixed array entries extend beyond input")
    }
    source_read_checksummed(
      source,
      block_offset,
      block_prefix_size + maximum_total.to_int() * entry_size,
      "fixed array data block",
    )
  }
  let total = product(current_grid)
  let mut cached_page = -1L
  let mut cached_entries = b""
  for linear in 0.. 0 {
      dimension = dimension - 1
      coordinates[dimension] = remaining % current_grid[dimension]
      remaining = remaining / current_grid[dimension]
      offsets[dimension] = (coordinates[dimension] *
      layout.chunk_dimensions[dimension].to_int64()).reinterpret_as_uint64()
    }
    let mut stored_index = 0L
    for dimension in 0..> (page.to_int() % 8))) == 0 {
      continue
    }
    if !chunk_contains_selection(
        offsets,
        layout.chunk_dimensions,
        dataspace.dimensions,
        selection,
      ) {
      continue
    }
    let entry = if paged {
      if cached_page != page {
        let page_elements = if page + 1 == page_count {
          maximum_total - page * page_entries
        } else {
          page_entries
        }
        guard page_elements <= 0x7FFFFFFFL / entry_size.to_int64() else {
          raise InvalidField("fixed array page is too large")
        }
        let page_offset = entries_start +
          page.reinterpret_as_uint64() *
          (page_entries * entry_size.to_int64() + 4L).reinterpret_as_uint64()
        cached_entries = source_read_checksummed(
          source,
          page_offset,
          page_elements.to_int() * entry_size,
          "fixed array data block page",
        )
        cached_page = page
      }
      Reader::at(
        cached_entries,
        ((stored_index - page * page_entries) * entry_size.to_int64()).to_int(),
      )
    } else {
      Reader::at(
        unpaged,
        block_prefix_size + stored_index.to_int() * entry_size,
      )
    }
    let address = entry.read_uint_le(offset_size)
    if address == undefined {
      continue
    }
    let stored_size = if client == 1 {
      entry.read_var_uint_le(size_width)
    } else {
      chunk_size.to_uint64()
    }
    let mask = if client == 1 { entry.read_u32_le() } else { 0U }
    let decoded = read_source_filtered_chunk(
      source,
      address,
      base_address,
      stored_size,
      filters,
      mask,
      datatype.size,
      chunk_size,
    )
    copy_chunk(
      decoded,
      decoded.length(),
      offsets,
      layout.chunk_dimensions,
      dataspace.dimensions,
      datatype.size,
      output,
      selection,
    )
  }
}