///|
/// 源必须在 Database 的使用期间保持同一份静态快照;不提供并发锁或事务语义。
pub(open) trait PageSource {
  fn byte_length(Self) -> Int
  fn read_range(Self, Int, Int) -> Bytes raise SqliteError
}

///|
pub struct BytesSource {
  data : Bytes
}

///|
pub fn BytesSource::new(data : Bytes) -> BytesSource {
  { data, }
}

///|
pub extend BytesSource with PageSource::{byte_length, read_range}

///|
pub impl PageSource for BytesSource with fn byte_length(self) {
  self.data.length()
}

///|
pub impl PageSource for BytesSource with fn read_range(self, offset, count) {
  require_range(self.data, offset, count)
  self.data[offset:offset + count].to_owned()
}

///|
fn source_read(
  source : &PageSource,
  offset : Int,
  count : Int,
) -> Bytes raise SqliteError {
  let size = source.byte_length()
  if offset < 0 || count < 0 || offset > size || count > size - offset {
    raise Invalid("数据源读取范围越界")
  }
  let bytes = source.read_range(offset, count)
  if bytes.length() != count {
    raise Invalid("数据源未返回请求的完整字节范围")
  }
  bytes
}

///|
pub fn open_source(
  source : &PageSource,
  limits? : Limits = Limits::default(),
) -> Database raise SqliteError {
  if limits.max_rows < 1 ||
    limits.max_payload_bytes < 1 ||
    limits.max_pages < 1 ||
    limits.max_depth < 1 {
    raise Invalid("所有资源限制必须为正数")
  }
  let size = source.byte_length()
  let header = parse_header(source_read(source, 0, 100))
  if size % header.page_size != 0 || size < header.page_size {
    raise Invalid("数据库文件不包含完整页面")
  }
  let physical_pages = size / header.page_size
  let page_count = if header.declared_pages != 0U &&
    header.change_counter == header.version_valid_for {
    let declared = bounded_int(
      header.declared_pages.to_uint64(),
      "database pages",
    )
    if declared > physical_pages {
      raise Invalid(
        "数据库声明页数超过文件长度;请使用 checkpoint 后的副本",
      )
    }
    declared
  } else {
    physical_pages
  }
  if header.freelist_pages.to_uint64() > page_count.to_uint64() {
    raise Invalid("freelist 页数超过数据库页数")
  }
  { source, header, page_count, limits, }
}