///|
priv struct Snapshot {
  data : @fs.File
  index : @fs.File
  size : Int64
  records : Int64
  mut page_start : Int64
  mut page : Bytes
  mut data_start : Int64
  mut data_page : Bytes
}

///|
async fn snapshot(
  reader : &@io.Reader,
  data : @fs.File,
  index : @fs.File,
) -> Snapshot {
  let offsets = @io.BufferedWriter::new(index, size=65536)
  let number = FixedArray::make(8, b'\x00')
  let mut entries = 0L
  let append = async fn(position : Int64) {
    guard entries < 1152921504606846975L else {
      fail("line index exceeds signed 64-bit file offsets")
    }
    for byte in 0..<8 {
      number[byte] = ((position >> (byte * 8)) & 255L).to_byte()
    }
    offsets.write(number.unsafe_reinterpret_as_bytes())
    entries += 1
  }
  append(0)
  let mut size = 0L
  let mut last = 0L
  while reader.read_some(max_len=65536) is Some(bytes) {
    guard size <= 9223372036854775807L - bytes.length().to_int64() else {
      fail("input exceeds signed 64-bit file offsets")
    }
    data.write(bytes)
    for position in 0.. Int64 {
  let first = line / 8192 * 8192
  if first != self.page_start {
    let count = (self.records + 1 - first).min(8192).to_int()
    self.page = self.index.read_exactly_at(count * 8, position=first * 8)
    self.page_start = first
  }
  let start = (line - first).to_int() * 8
  let mut result = 0L
  for byte in 0..<8 {
    result = result | (self.page[start + byte].to_int64() << (byte * 8))
  }
  result
}

///|
async fn Snapshot::matches(
  self : Snapshot,
  line : Int64,
  pattern : @bytere.Pattern,
) -> Bool {
  let start = self.offset(line)
  let length = self.offset(line + 1) - start
  guard length <= 2147483647L else {
    fail("regular-expression line exceeds the supported record length")
  }
  let text = StringBuilder()
  let end = start + length
  let mut position = start
  while position < end {
    let first = position / 65536 * 65536
    if first != self.data_start {
      self.data_page = self.data.read_exactly_at(
        (self.size - first).min(65536).to_int(),
        position=first,
      )
      self.data_start = first
    }
    let begin = (position - first).to_int()
    let take = (end - position)
      .min((self.data_page.length() - begin).to_int64())
      .to_int()
    for byte in begin..<(begin + take) {
      if position + (byte - begin).to_int64() + 1 < end ||
        self.data_page[byte] != b'\n' {
        text.write_char(self.data_page[byte].to_int().unsafe_to_char())
      }
    }
    position += take.to_int64()
  }
  pattern.is_match(text.to_string())
}

///|
async fn Snapshot::copy(
  self : Snapshot,
  first : Int64,
  end : Int64,
  writer : &@io.Writer,
) -> Unit {
  let mut position = first
  while position < end {
    let length = (end - position).min(65536).to_int()
    let bytes = self.data.read_exactly_at(length, position~)
    writer.write(bytes)
    position += length.to_int64()
  }
}