///|
/// 极简 Bloom filter:用双哈希模拟 k 个独立哈希。
/// 作用是在查 SST 之前先排除「肯定不在」的文件,把读放大压下来。
///
/// **三个实现选择,都是量过才定的**(见 ADR-0008):
///
/// 1. **位图在内存里也按位打包**(`Array[Byte]`),不是一个 bool 一字节。
///    早先用 `Array[Bool]`,10 bit/key 的 filter 在内存里膨胀 8 倍:
///    100 万条数据的 77 个 SST 要占 10 MiB,打包后 1.25 MiB。
///    顺带 `encode` / `decode` 从「逐位重排」变成「整段拷贝」。
///
/// 2. **一次哈希出两个值**,而不是两个种子各跑一遍 FNV。
///    `h2` 由 `h1` 过一次 splitmix 终混得到 —— 6 条整数指令,
///    而第二遍 FNV 要遍历整条 key。Kirsch–Mitzenmacher 证明这样构造的
///    双哈希与 k 个独立哈希的误判率同阶。
///
/// 3. **取模换成乘法**(fastrange)。`%` 在 `UInt64` 上是真除法,
///    约 20 个周期;一次未命中要探 77 个文件 × 7 个位置 = 539 次除法,
///    约 3.6 µs,而引擎侧点查总共才 10 µs 量级。

///|
/// 每个元素分配多少位。10 bit 配 7 个哈希,理论误判率 0.82%。
/// 实测见 `bloom_wbtest.mbt` 与 `docs/engineering/BENCHMARK.md`。
const BLOOM_BITS_PER_KEY : Int32 = 10

///|
/// 哈希个数。最优值是 `bits_per_key * ln2`,10 bit 对应 6.93,取 7。
const BLOOM_K : Int32 = 7

///|
/// 位图在内存里就是打包好的字节,与磁盘布局一一对应。
/// `num_bits` 单独存一份:末尾那个字节的高位可能没用满,
/// 探测时只能落在 `[0, num_bits)` 内。
pub struct BloomFilter {
  bits : Array[Byte]
  num_bits : Int32
  k : Int32
}

///|
/// FNV-1a 64 位。
fn fnv1a(data : Bytes, seed : UInt64) -> UInt64 {
  let mut h = seed
  for i in 0.. UInt64 {
  let mut h = x
  h = h ^ (h >> 33)
  h = h * 0xFF51AFD7ED558CCDUL
  h = h ^ (h >> 33)
  h = h * 0xC4CEB9FE1A85EC53UL
  h = h ^ (h >> 33)
  h
}

///|
/// 由一条 key 算出双哈希对 `(h1, h2)`。
///
/// **单独暴露出来是为了让调用方复用**:一次点查要探 N 个 SST 的 filter,
/// 而这 N 次用的是同一条 key。把哈希提到循环外面,N 次变 1 次。
///
/// **FNV-1a 出来必须再过一遍 `mix64`。** 这不是保险,是必需的:
/// FNV-1a 最后一步是 `(h ^ byte) * prime`,末字节只影响乘数的低 8 位,
/// 高位几乎不随它变化。实测「10000 条只差末几位的 key」直接取高 32 位,
/// 只落进约 1000 个不同位置(理论值 9516),误判率因此只有理论值的一半。
/// 换成低位也有同样的隐患,正确做法是先做一次完整雪崩。
pub fn bloom_hashes(key : Bytes) -> (UInt64, UInt64) {
  let h = mix64(fnv1a(key, 0xCBF29CE484222325UL))
  (h, mix64(h ^ 0x9E3779B97F4A7C15UL))
}

///|
/// 第 `i` 个探测位置。
///
/// `h1 + i * h2` 是双哈希;`(x & 0xFFFFFFFF) * n >> 32` 是 fastrange ——
/// 把 32 位无符号值均匀映射到 `[0, n)`,等价于 `x * n / 2^32`,
/// 但只用一次乘法而不是一次除法。`n <= 2^24` 时乘积不超过 2^56,不会溢出。
fn probe_index(h1 : UInt64, h2 : UInt64, i : Int32, n : UInt64) -> Int32 {
  let x = h1 + i.to_uint64() * h2
  (((x & 0xFFFFFFFFUL) * n) >> 32).to_int()
}

///|
/// `expected` 是预期元素个数,按默认参数(10 bit / 7 哈希)建 filter。
pub fn BloomFilter::new(expected : Int32) -> BloomFilter {
  BloomFilter::new_with(expected, BLOOM_BITS_PER_KEY, BLOOM_K)
}

///|
/// 显式指定每个元素的位数与哈希个数。给调参和测试用。
///
/// 生产路径只用 `new`;`new_with` 存在的意义是让「为什么是 10 bit」
/// 这个问题能用一组可复现的对照实验回答,而不是引用一句经验值。
pub fn BloomFilter::new_with(
  expected : Int32,
  bits_per_key : Int32,
  k : Int32,
) -> BloomFilter {
  let bpk = if bits_per_key < 1 { 1 } else { bits_per_key }
  let kk = if k < 1 { 1 } else { k }
  // 下限 640 位(80 字节):空文件或极小文件也留一个能用的 filter,
  // 否则 n=0 时取模无意义。
  let raw = if expected < 64 { 640 } else { expected * bpk }
  // 向上取整到整字节,让内存表示与磁盘表示完全一致
  let num_bits = (raw + 7) / 8 * 8
  { bits: Array::make(num_bits / 8, (0 : Byte)), num_bits, k: kk, }
}

///|
fn BloomFilter::set_bit(self : BloomFilter, idx : Int32) -> Unit {
  let byte_idx = idx / 8
  let mask = 1 << (idx % 8)
  self.bits[byte_idx] = (self.bits[byte_idx].to_int() | mask).to_byte()
}

///|
fn BloomFilter::get_bit(self : BloomFilter, idx : Int32) -> Bool {
  let byte_idx = idx / 8
  let mask = 1 << (idx % 8)
  (self.bits[byte_idx].to_int() & mask) != 0
}

///|
pub fn BloomFilter::put(self : BloomFilter, key : Bytes) -> Unit {
  let (h1, h2) = bloom_hashes(key)
  self.put_hashes(h1, h2)
}

///|
/// 用预算好的哈希写入。与 `put` 等价,省一次哈希。
pub fn BloomFilter::put_hashes(
  self : BloomFilter,
  h1 : UInt64,
  h2 : UInt64,
) -> Unit {
  let n = self.num_bits.to_uint64()
  for i in 0.. Bool {
  let (h1, h2) = bloom_hashes(key)
  self.may_contain_hashes(h1, h2)
}

///|
/// 用预算好的哈希查询。一次点查探 N 个 SST 时,哈希只需算一次。
pub fn BloomFilter::may_contain_hashes(
  self : BloomFilter,
  h1 : UInt64,
  h2 : UInt64,
) -> Bool {
  let n = self.num_bits.to_uint64()
  for i in 0.. Int32 {
  self.num_bits
}

///|
/// 哈希个数。
pub fn BloomFilter::k(self : BloomFilter) -> Int32 {
  self.k
}

///|
/// 已置位的 bit 数。只用于测试与统计。
pub fn BloomFilter::popcount(self : BloomFilter) -> Int32 {
  let mut total = 0
  for b in self.bits {
    let v = b.to_int()
    for j in 0..<8 {
      if (v & (1 << j)) != 0 {
        total = total + 1
      }
    }
  }
  total
}

///|
/// 编码成格式规范 §3.3 的字节布局(v2):
/// `k(1B) | num_bits(varint32) | bits[num_bits/8] | crc32c(4B)`
///
/// 因为内存里已经是打包好的位图,这里只做整段拷贝,
/// 不再逐位重排 —— 早先的实现每 8 个 bool 要跑一轮内层循环。
///
/// 尾部 CRC 是 v2 加的(ADR-0010)。理由不是「顺手统一」:
/// filter 没有 CRC 时,一个坏掉的位图会让 `may_contain` 返回 **false**,
/// 于是「数据明明在,却查不到」—— 这是唯一一种会**主动丢数据**的损坏方向,
/// 比报错难查得多。fuzz 也证实了:加 CRC 之前,整个 filter 区域 80 个位置
/// 全部漏检。
pub fn BloomFilter::encode(self : BloomFilter) -> Bytes {
  self.encode_with(true)
}

///|
/// 产出**格式版本 1** 的 filter 字节(没有尾部 CRC)。
///
/// 只被 `SstWriter` 的 v1 写入路径用到 —— 那条路径存在的唯一目的是
/// 让兼容性测试能造出老文件,见 `sstable_wbtest.mbt`。
fn BloomFilter::encode_v1(self : BloomFilter) -> Bytes {
  self.encode_with(false)
}

///|
fn BloomFilter::encode_with(self : BloomFilter, with_crc : Bool) -> Bytes {
  let body = Buffer::Buffer()
  body.write_byte(self.k.to_byte())
  write_varint32(body, self.num_bits)
  body.write_bytes(Bytes::from_array(self.bits[:])[:])
  let raw = body.to_bytes()
  if !with_crc {
    return raw
  }
  let out = Buffer::Buffer()
  out.write_bytes(raw[:])
  write_fixed32u(out, crc32c(raw))
  out.to_bytes()
}

///|
/// 从字节解码 filter(**格式版本 2**,尾部带 CRC)。结构不一致或 CRC 不符时返回 `None`。
///
/// 这里**校验得比较严**(k 的范围、位图长度必须与 num_bits 精确匹配):
/// filter 读错不会直接导致数据错误,但会让「假阴性」出现 ——
/// 而假阴性意味着「数据明明在,却查不到」,这是 LSM 里最不可接受的错误。
/// 宁可判定文件损坏、拒绝打开,也不要拿着一个半信半疑的 filter 去过滤。
pub fn decode_bloom(data : Bytes) -> BloomFilter? {
  if data.length() < 2 + 4 {
    return None
  }
  let body_len = data.length() - 4
  let want = match read_fixed32u(data, body_len) {
    Some(v) => v
    None => return None
  }
  if crc32c(data[:body_len].to_owned()) != want {
    return None
  }
  parse_bloom(data, body_len)
}

///|
/// 解码**格式版本 1** 的 filter(没有尾部 CRC)。只在读 v1 文件时走这里。
pub fn decode_bloom_v1(data : Bytes) -> BloomFilter? {
  parse_bloom(data, data.length())
}

///|
/// `end` 指向 filter 结构结束的位置:v2 传 `n - 4`(跳过 CRC),v1 传 `n`。
fn parse_bloom(data : Bytes, end : Int32) -> BloomFilter? {
  if end < 2 {
    return None
  }
  let k = data[0].to_int()
  if k < 1 || k > 30 {
    return None
  }
  let (nbits, body_start) = match read_varint32(data, 1, end) {
    Some(pair) => pair
    None => return None
  }
  // 上限 1 << 24 位 = 2 MiB 位图,足够 130 万 key,超过这个数说明数据坏了
  if nbits < 1 || nbits > 1 << 24 {
    return None
  }
  let nbytes = (nbits + 7) / 8
  if body_start + nbytes != end {
    return None
  }
  Some({
    bits: data[body_start:body_start + nbytes].to_array(),
    num_bits: nbits,
    k,
  })
}