///|
/// data block 的编码与解码:前缀压缩 + restart point。
///
/// 磁盘布局见 `docs/design/format.md` §3.2:
/// ```
/// entry: [type:1][seq:varint64][shared:v32][non_shared:v32][value_len:v32][key_delta][value]
/// 尾部:  [restart_offset:u32 LE] * n  [num_restarts:u32 LE]  [crc32c:u32 LE]
/// ```
///
/// 末尾 4 字节 CRC 是**格式版本 2** 引入的(ADR-0010)。版本 1 的 block
/// 没有这个字段,靠 `decode_block_v1` 读 —— 升级不能炸掉老库。
///
/// **为什么值得做前缀压缩**:SST 里的 key 通常高度相似(`user:1000`、
/// `user:1001`……),前缀压缩能把 key 部分压掉一半以上。而 restart point
/// 是为了让「二分查找」成为可能 —— 压缩后第 i 条不能独立解码,
/// 必须先解第 i-1 条;每 16 条插一个「完整 key」的锚点,
/// 查找时最多只解码 16 条。这是空间与查找速度的经典折中。

///|
/// 每 16 条 entry 一个 restart point(格式规范 §3.2,与 LevelDB 一致)。
const RESTART_INTERVAL : Int32 = 16

///|
/// data block 的目标大小。超过它就切下一块(切点必须落在 entry 边界)。
/// 4 KiB 是 LevelDB / RocksDB 的默认值,也是「一次读放大」与
/// 「索引条目数」之间的平衡点。
pub const BLOCK_TARGET_SIZE : Int32 = 4096

///|
/// block 尾部 CRC 的长度(v2 起)。校验范围是**它之前的所有字节** ——
/// entry 区、restart 数组、`num_restarts` 全在里面。
///
/// 只保护 entry 区是不够的:`num_restarts` 被改成一个仍然能通过边界检查的值时,
/// 读端会把 entry 区当 restart 数组读,得到一个「结构合法但内容错乱」的 block。
pub const BLOCK_CRC_SIZE : Int32 = 4

///|
/// 解码过程中间态:比 `Entry` 多一个 `next`,
/// 因为解码是流式的,必须知道下一条从哪开始。
/// 不直接复用 `Entry` 是因为 `Entry` 是对外契约,不该混进游标信息。
priv struct DecodedEntry {
  key : Bytes
  value : Bytes
  seq : UInt64
  deleted : Bool
  next : Int32
}

///|
/// 两个 Bytes 的公共前缀长度。
fn common_prefix_len(a : Bytes, b : Bytes) -> Int32 {
  let n = if a.length() < b.length() { a.length() } else { b.length() }
  let mut i = 0
  while i < n && a[i] == b[i] {
    i = i + 1
  }
  i
}

///|
/// 按 key 升序逐条追加,最后 `finish()` 出完整 block 字节。
///
/// 使用契约:`add` 的 key 必须**严格升序**。违反会直接抛 `Corruption`,
/// 而不是静默写出一个坏 block —— 排序错了说明上层归并逻辑有 bug,
/// 让它早点炸比让它写进磁盘好。
pub struct BlockBuilder {
  mut buf : Buffer
  mut restarts : Array[Int32]
  mut counter : Int32
  mut last_key : Bytes
  mut count : Int32
}

///|
/// 新建一个空 block。初始就带一个 restart 点 0,
/// 保证第一个 entry 一定可以独立解码。
pub fn BlockBuilder::new() -> BlockBuilder {
  { buf: Buffer::Buffer(), restarts: [0], counter: 0, last_key: b"", count: 0, }
}

///|
/// 已追加的条目数。
pub fn BlockBuilder::entry_count(self : BlockBuilder) -> Int32 {
  self.count
}

///|
/// 最后一条的 key(也就是这个 block 的最大 key)。
/// SST 写索引时需要它作为 `last_key`;空 block 返回空 key。
pub fn BlockBuilder::last_key(self : BlockBuilder) -> Bytes {
  self.last_key
}

///|
/// 是否一条都没写。
pub fn BlockBuilder::is_empty(self : BlockBuilder) -> Bool {
  self.count == 0
}

///|
/// 估算编码后的字节数(含 restart 数组、计数与 CRC),用于判断是否该切块。
/// 估算而不是精确值:restart 数组长度依赖 counter,这里按当前状态算,
/// 差几个字节不影响「是否超过 4 KiB」的判断。
pub fn BlockBuilder::estimated_size(self : BlockBuilder) -> Int32 {
  self.buf.length() + self.restarts.length() * 4 + 8
}

///|
/// 是否已经达到切块阈值。
pub fn BlockBuilder::should_finish(self : BlockBuilder) -> Bool {
  self.estimated_size() >= BLOCK_TARGET_SIZE
}

///|
/// 追加一条。key 必须严格大于上一条。
pub fn BlockBuilder::add(
  self : BlockBuilder,
  entry : Entry,
) -> Unit raise LsmError {
  if self.count > 0 && compare_bytes(self.last_key, entry.key) >= 0 {
    raise LsmError::corruption(
      "block: key 必须严格升序,上一条不小于本条(说明上层排序有 bug)",
    )
  }
  let shared = if self.counter < RESTART_INTERVAL {
    common_prefix_len(self.last_key, entry.key)
  } else {
    // 达到间隔:这条成为新的 restart 点,前缀必须从零开始
    self.restarts.push(self.buf.length())
    self.counter = 0
    0
  }
  self.counter = self.counter + 1
  self.buf.write_byte((if entry.deleted { 1 } else { 0 }).to_byte())
  write_varint64(self.buf, entry.seq)
  write_varint32(self.buf, shared)
  write_varint32(self.buf, entry.key.length() - shared)
  write_varint32(self.buf, entry.value.length())
  self.buf.write_bytes(entry.key[shared:])
  self.buf.write_bytes(entry.value[:])
  self.last_key = entry.key
  self.count = self.count + 1
}

///|
/// 产出完整 block 字节(entry 区 + restart 数组 + 计数 + CRC)。
///
/// 每次调用都基于当前状态重新拼装,**不修改 builder 自身**,
/// 所以可以在不 `reset` 的情况下多次调用(拿到的内容相同)。
pub fn BlockBuilder::finish(self : BlockBuilder) -> Bytes {
  self.assemble(true)
}

///|
/// 产出**格式版本 1** 的 block 字节(没有尾部 CRC)。
///
/// 存在的唯一理由是兼容性测试:要证明「读到 v1 的老文件仍然能正确打开」,
/// 就必须能造出 v1 的字节。生产路径永远不会调用它 ——
/// `SstWriter` 写的一律是 v2。
fn BlockBuilder::finish_v1(self : BlockBuilder) -> Bytes {
  self.assemble(false)
}

///|
/// 拼装 block。`with_crc` 决定尾部是否带 CRC。
///
/// CRC 覆盖「拼装到 CRC 之前」的全部字节,而不是只覆盖 entry 区:
/// restart 数组与 `num_restarts` 同样是读端解析的依据,
/// 它们坏掉一样会让解码器读到错位置。
fn BlockBuilder::assemble(self : BlockBuilder, with_crc : Bool) -> Bytes {
  let body = Buffer::Buffer()
  body.write_bytes(self.buf.to_bytes()[:])
  for i in 0.. Unit {
  self.buf = Buffer::Buffer()
  self.restarts = [0]
  self.counter = 0
  self.last_key = b""
  self.count = 0
}

///|
/// 一个已编码的 block。持有原始字节,按需解码。
pub struct Block {
  data : Bytes
  restarts : Array[Int32]
  /// entry 区的结束位置,也就是 restart 数组的起点。
  /// 所有解码都必须以它为硬边界,否则会把 restart 数组当 entry 读。
  limit : Int32
}

///|
/// 解析一个**格式版本 2** 的 block:先校验尾部 CRC,再解析结构。
///
/// 校验顺序是「CRC 在前,结构在后」,因为 CRC 是唯一的**内容**校验。
/// 先跑结构检查会让「结构碰巧仍然合法、内容已经错了」的损坏漏过去 ——
/// 那正是没有 CRC 时 58% 的静默接受是怎么来的。
///
/// 返回 `None` 的两种情形(CRC 不符 / 结构不合法)在读端是同一个后果:
/// 这个 block 不可信。调用方一律当损坏处理,不去猜。
pub fn decode_block(raw : Bytes) -> Block? {
  let n = raw.length()
  if n < BLOCK_CRC_SIZE + 4 {
    return None
  }
  let body_len = n - BLOCK_CRC_SIZE
  let want = match read_fixed32u(raw, body_len) {
    Some(v) => v
    None => return None
  }
  if crc32c(raw[:body_len].to_owned()) != want {
    return None
  }
  parse_block(raw, body_len)
}

///|
/// 解析一个**格式版本 1** 的 block(尾部没有 CRC,只有 `num_restarts`)。
///
/// 只在读 v1 文件时走这条路(ADR-0010)。它**不做任何内容校验** ——
/// 这正是 v1 的已知短板,保留它是为了能读老库,不是为了继续用。
pub fn decode_block_v1(raw : Bytes) -> Block? {
  parse_block(raw, raw.length())
}

///|
/// 结构解析:`end` 指向「`num_restarts` 字段结束」的位置。
/// v2 传 `n - 4`(跳过 CRC),v1 传 `n`。
///
/// 注意这里**只校验结构,不校验内容** —— entry 区的内容是惰性解码的,
/// 只有真正访问到坏 entry 时才会暴露。这是有意的:
/// 全量预解码会让「读一个 key」退化成「扫整个 block」,
/// 而 LSM 的读路径最忌讳这个。
/// v2 之所以还敢这么懒,是因为 CRC 已经在入口处把「内容被改坏」拦掉了。
fn parse_block(raw : Bytes, end : Int32) -> Block? {
  if end < 4 {
    return None
  }
  let num_restarts = match read_fixed32(raw, end - 4) {
    Some(v) => v
    None => return None
  }
  // 至少要有 1 个 restart,且 restart 数组不能吃掉整个 block
  if num_restarts < 1 {
    return None
  }
  let restarts_start = end - 4 - num_restarts * 4
  if restarts_start < 0 {
    return None
  }
  let restarts : Array[Int32] = []
  for i in 0.. v
      None => return None
    }
    if off < 0 || off > restarts_start {
      return None
    }
    restarts.push(off)
  }
  Some({ data: raw, restarts, limit: restarts_start, })
}

///|
/// restart 点个数。
pub fn Block::num_restarts(self : Block) -> Int32 {
  self.restarts.length()
}

///|
/// 从 `pos` 解一条 entry。`prev_key` 是上一条的完整 key(restart 点传 `b""`)。
///
/// 所有长度字段都做上界检查后再切片,避免越界 panic ——
/// 这条路径上输入来自磁盘,必须假设它是恶意的或损坏的。
fn Block::read_entry_at(
  self : Block,
  pos : Int32,
  prev_key : Bytes,
) -> DecodedEntry? {
  if pos < 0 || pos >= self.limit {
    return None
  }
  let ty = self.data[pos].to_int()
  if ty > 1 {
    return None
  }
  let mut p = pos + 1
  let seq = match read_varint64(self.data, p, self.limit) {
    Some((v, np)) => {
      p = np
      v
    }
    None => return None
  }
  let shared = match read_varint32(self.data, p, self.limit) {
    Some((v, np)) => {
      p = np
      v
    }
    None => return None
  }
  let non_shared = match read_varint32(self.data, p, self.limit) {
    Some((v, np)) => {
      p = np
      v
    }
    None => return None
  }
  let value_len = match read_varint32(self.data, p, self.limit) {
    Some((v, np)) => {
      p = np
      v
    }
    None => return None
  }
  // shared 不能超过上一个 key 的长度:超过了说明「要复制的前缀比源还长」
  if shared > prev_key.length() {
    return None
  }
  // 用「剩余长度」做减法而不是相加后比较,避免 Int 溢出
  let rest = self.limit - p
  if non_shared > rest || value_len > rest - non_shared {
    return None
  }
  let end = p + non_shared + value_len
  let key = if shared == 0 {
    self.data[p:p + non_shared].to_owned()
  } else {
    // 只复制 prev_key 的**前 shared 个字节**。写成 prev_key[:] 是这里
    // 最容易犯的错 —— 那样会把整个上一条 key 当公共前缀,解出来的 key 变长。
    let k = Buffer::Buffer()
    k.write_bytes(prev_key[:shared])
    k.write_bytes(self.data[p:p + non_shared])
    k.to_bytes()
  }
  Some({
    key,
    value: self.data[p + non_shared:end].to_owned(),
    seq,
    deleted: ty == 1,
    next: end,
  })
}

///|
/// 解出全部条目(按 key 升序)。用于全扫描、校验和测试。
///
/// 生产读路径**不该**走这里 —— 它会把整个 block 解完。
/// 单 key 查询请用 `get`。
pub fn Block::entries(self : Block) -> Array[Entry] raise LsmError {
  let out : Array[Entry] = []
  let mut pos = 0
  let mut prev : Bytes = b""
  while pos < self.limit {
    let e = match self.read_entry_at(pos, prev) {
      Some(e) => e
      None =>
        raise LsmError::corruption("block: entry 解码失败,偏移 \{pos}")
    }
    out.push({ key: e.key, value: e.value, seq: e.seq, deleted: e.deleted, })
    prev = e.key
    pos = e.next
  }
  out
}

///|
/// 取第 `idx` 个 restart 点的首 key。
/// restart 点的 `shared` 恒为 0,所以可以用空 key 作为 `prev_key`。
fn Block::restart_first_key(self : Block, idx : Int32) -> Bytes? {
  if idx < 0 || idx >= self.restarts.length() {
    return None
  }
  match self.read_entry_at(self.restarts[idx], b"") {
    Some(e) => Some(e.key)
    None => None
  }
}

///|
/// 二分定位:返回「最后一个首 key ≤ target」的 restart 点下标。
///
/// `get` 与 `seek` 共用这一段。**故意抽出来**:这是整个 block 里
/// 唯一一处二分逻辑,写两份的话,某天只改了其中一份就会得到
/// 「精确查找对、下界查找错」这种极难发现的偏差。
fn Block::restart_index_for(self : Block, target : Bytes) -> Int32? {
  let n = self.restarts.length()
  if n == 0 {
    return None
  }
  let mut lo = 0
  let mut hi = n - 1
  while lo < hi {
    let mid = (lo + hi + 1) / 2
    let mid_key = match self.restart_first_key(mid) {
      Some(k) => k
      None => return None
    }
    if compare_bytes(mid_key, target) <= 0 {
      lo = mid
    } else {
      hi = mid - 1
    }
  }
  Some(lo)
}

///|
/// 按 key 精确查找。
///
/// 两步:先在 restart 数组上二分(找最后一个首 key ≤ 目标的 restart 点),
/// 再从那里线性扫到下一个 restart 点。最坏解 16 条 entry。
///
/// 返回墓碑条目(`deleted = true`)时**不要当命中处理**——
/// 调用方必须看 `deleted` 字段,否则删除会失效。
pub fn Block::get(self : Block, key : Bytes) -> Entry? {
  let start = match self.restart_index_for(key) {
    Some(i) => i
    None => return None
  }
  let mut pos = self.restarts[start]
  let mut prev : Bytes = b""
  while pos < self.limit {
    let e = match self.read_entry_at(pos, prev) {
      Some(e) => e
      None => return None
    }
    let c = compare_bytes(e.key, key)
    if c == 0 {
      return Some({
        key: e.key,
        value: e.value,
        seq: e.seq,
        deleted: e.deleted,
      })
    }
    if c > 0 {
      // key 已经越过目标,后面只会更大
      return None
    }
    prev = e.key
    pos = e.next
  }
  None
}

///|
/// 下界查找:返回第一个 `key >= target` 的条目。
///
/// 这是 index block 需要的语义 —— 索引里存的是每个 data block 的
/// **最大 key**,找「第一个 ≥ 目标的 last_key」就等于找到「可能包含目标的块」。
/// 也用于 scan 的起始定位。
///
/// 与 `get` 的区别只在终点判断:`get` 遇到更大的 key 就返回 `None`,
/// `seek` 则把它返回出去。
pub fn Block::seek(self : Block, target : Bytes) -> Entry? {
  let start = match self.restart_index_for(target) {
    Some(i) => i
    None => return None
  }
  let mut pos = self.restarts[start]
  let mut prev : Bytes = b""
  while pos < self.limit {
    let e = match self.read_entry_at(pos, prev) {
      Some(e) => e
      None => return None
    }
    if compare_bytes(e.key, target) >= 0 {
      return Some({
        key: e.key,
        value: e.value,
        seq: e.seq,
        deleted: e.deleted,
      })
    }
    prev = e.key
    pos = e.next
  }
  None
}

///|
/// 最后一条 entry。空 block 返回 `None`。
///
/// 用于「文件的最大 key」这个边界:index 里存的是每块的 `last_key`,
/// 所以最后一个 index 条目的 key 就是整个文件的最大 key,
/// 但要拿到它就得能取 block 的最后一条 —— `seek` 做不到这件事
/// (key 是任意二进制,没有「比所有 key 都大」的哨兵值可传)。
pub fn Block::last(self : Block) -> Entry? {
  let n = self.restarts.length()
  if n == 0 {
    return None
  }
  // 从最后一个 restart 点开始走到底:restart 点的 entry 是完整 key,
  // 之后各条是前缀压缩的,必须逐条解出 prev_key。
  let mut pos = self.restarts[n - 1]
  let mut prev : Bytes = b""
  let mut found : Entry? = None
  while pos < self.limit {
    let e = match self.read_entry_at(pos, prev) {
      Some(e) => e
      None => return found
    }
    found = Some({ key: e.key, value: e.value, seq: e.seq, deleted: e.deleted, })
    prev = e.key
    pos = e.next
  }
  found
}

///|
/// 第一条 entry。空 block 返回 `None`。
///
/// 与 `seek(b"")` 的区别:`seek` 要跑一次 `restart_index_for` 的二分,
/// 而「第一条」根本不需要比较 —— 直接从第一个 restart 点解即可。
pub fn Block::first(self : Block) -> Entry? {
  if self.restarts.length() == 0 {
    return None
  }
  match self.read_entry_at(self.restarts[0], b"") {
    Some(e) =>
      Some({ key: e.key, value: e.value, seq: e.seq, deleted: e.deleted, })
    None => None
  }
}