use crate::error::{Error, Result};
use xxhash_rust::xxh3::{Xxh3, xxh3_64};
pub const MAGIC: [u8; 8] = *b"MINDFMT1";
pub const WAL_MAGIC: [u8; 8] = *b"MINDWAL1";
pub const FORMAT_VERSION: u32 = 8;
pub const DEFAULT_PAGE_SIZE: u32 = 4096;
pub const MIN_PAGE_SIZE: u32 = 512;
pub const MAX_PAGE_SIZE: u32 = 65536;
pub const PAGE_TRAILER_LEN: usize = 8;
pub const MAX_MODEL_ID_LEN: usize = 64;
pub const WAL_HEADER_LEN: usize = 32;
pub const WAL_FRAME_HEADER_LEN: usize = 32;
pub const FLAG_ENCRYPTED: u32 = 1;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum PageType {
BtreeInner = 0x01,
BtreeLeaf = 0x02,
Vector = 0x03,
HnswNode = 0x04,
HnswMeta = 0x05,
Freelist = 0x06,
Overflow = 0x07,
FtsDict = 0x08,
FtsPostings = 0x09,
GraphDict = 0x0A,
GraphOverflow = 0x0B,
FilterMeta = 0x0C,
FilterSymbols = 0x0D,
}
impl PageType {
pub fn from_u8(v: u8) -> Option<PageType> {
match v {
0x01 => Some(PageType::BtreeInner),
0x02 => Some(PageType::BtreeLeaf),
0x03 => Some(PageType::Vector),
0x04 => Some(PageType::HnswNode),
0x05 => Some(PageType::HnswMeta),
0x06 => Some(PageType::Freelist),
0x07 => Some(PageType::Overflow),
0x08 => Some(PageType::FtsDict),
0x09 => Some(PageType::FtsPostings),
0x0A => Some(PageType::GraphDict),
0x0B => Some(PageType::GraphOverflow),
0x0C => Some(PageType::FilterMeta),
0x0D => Some(PageType::FilterSymbols),
_ => None,
}
}
}
pub fn page_size_is_valid(page_size: u32) -> bool {
(MIN_PAGE_SIZE..=MAX_PAGE_SIZE).contains(&page_size) && page_size.is_power_of_two()
}
pub const PAGE_HEADER_LEN: usize = 16;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PageHeader {
pub page_type: PageType,
pub entry_count: u32,
pub next_page: u64,
}
impl PageHeader {
pub fn encode_into(&self, page: &mut [u8]) {
write_bytes(page, 0, &[self.page_type as u8, 0, 0, 0]);
write_u32(page, 4, self.entry_count);
write_u64(page, 8, self.next_page);
}
pub fn decode(page: &[u8]) -> Option<Self> {
if page.len() < PAGE_HEADER_LEN {
return None;
}
Some(PageHeader {
page_type: PageType::from_u8(*page.first()?)?,
entry_count: read_u32(page, 4)?,
next_page: read_u64(page, 8)?,
})
}
}
pub fn page_checksum(page: &[u8]) -> u64 {
let body_len = page.len().saturating_sub(PAGE_TRAILER_LEN);
xxh3_64(page.get(..body_len).unwrap_or_default())
}
pub fn stamp_page_checksum(page: &mut [u8]) {
let sum = page_checksum(page).to_le_bytes();
let len = page.len();
if let Some(trailer) = page.get_mut(len.saturating_sub(PAGE_TRAILER_LEN)..) {
trailer.copy_from_slice(&sum);
}
}
pub fn page_checksum_is_valid(page: &[u8]) -> bool {
if page.len() < PAGE_TRAILER_LEN {
return false;
}
let stored = read_u64(page, page.len() - PAGE_TRAILER_LEN);
stored == Some(page_checksum(page))
}
const OFF_MAGIC: usize = 0;
const OFF_VERSION: usize = 8;
const OFF_PAGE_SIZE: usize = 12;
const OFF_PAGE_COUNT: usize = 16;
const OFF_ROOT_BTREE: usize = 24;
const OFF_FREELIST: usize = 32;
const OFF_HNSW_META: usize = 40;
const OFF_TXN_COUNTER: usize = 48;
const OFF_DIMS: usize = 56;
const OFF_QUANT: usize = 58;
const OFF_MODEL_ID: usize = 60;
const OFF_FLAGS: usize = 128;
const OFF_FTS_ROOT: usize = 156;
const OFF_GRAPH_ROOT: usize = 164;
const OFF_FILTER_META: usize = 172;
const OFF_FILTER_SYMBOLS: usize = 180;
pub const HEADER_PEEK_LEN: usize = 16;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Header {
pub format_version: u32,
pub page_size: u32,
pub page_count: u64,
pub root_btree_page: u64,
pub freelist_page: u64,
pub hnsw_meta_page: u64,
pub fts_root_page: u64,
pub graph_root_page: u64,
pub filter_meta_page: u64,
pub filter_symbols_page: u64,
pub txn_counter: u64,
pub embedding_dims: u16,
pub embedding_quant: u16,
pub embedding_model_id: String,
pub flags: u32,
}
impl Header {
pub fn new(page_size: u32) -> Result<Self> {
if !page_size_is_valid(page_size) {
return Err(Error::InvalidArgument("unsupported page size"));
}
Ok(Header {
format_version: FORMAT_VERSION,
page_size,
page_count: 1,
root_btree_page: 0,
freelist_page: 0,
hnsw_meta_page: 0,
fts_root_page: 0,
graph_root_page: 0,
filter_meta_page: 0,
filter_symbols_page: 0,
txn_counter: 0,
embedding_dims: 0,
embedding_quant: 0,
embedding_model_id: String::new(),
flags: 0,
})
}
pub fn peek_page_size(prefix: &[u8]) -> Result<u32> {
if prefix.get(OFF_MAGIC..OFF_MAGIC + 8) != Some(&MAGIC[..]) {
return Err(Error::BadHeader);
}
let page_size = read_u32(prefix, OFF_PAGE_SIZE).ok_or(Error::BadHeader)?;
if !page_size_is_valid(page_size) {
return Err(Error::BadHeader);
}
Ok(page_size)
}
pub fn encode(&self, page: &mut [u8]) -> Result<()> {
if page.len() != self.page_size as usize || !page_size_is_valid(self.page_size) {
return Err(Error::InvalidArgument("header buffer must be one page"));
}
let id = self.embedding_model_id.as_bytes();
if id.len() > MAX_MODEL_ID_LEN {
return Err(Error::InvalidArgument(
"embedding_model_id exceeds 64 bytes",
));
}
if self.flags & FLAG_ENCRYPTED != 0 {
return Err(Error::InvalidArgument("encrypted flag is reserved in v1"));
}
page.fill(0);
write_bytes(page, OFF_MAGIC, &MAGIC);
write_u32(page, OFF_VERSION, self.format_version);
write_u32(page, OFF_PAGE_SIZE, self.page_size);
write_u64(page, OFF_PAGE_COUNT, self.page_count);
write_u64(page, OFF_ROOT_BTREE, self.root_btree_page);
write_u64(page, OFF_FREELIST, self.freelist_page);
write_u64(page, OFF_HNSW_META, self.hnsw_meta_page);
write_u64(page, OFF_TXN_COUNTER, self.txn_counter);
write_u16(page, OFF_DIMS, self.embedding_dims);
write_u16(page, OFF_QUANT, self.embedding_quant);
write_u32(page, OFF_MODEL_ID, id.len() as u32);
write_bytes(page, OFF_MODEL_ID + 4, id);
write_u32(page, OFF_FLAGS, self.flags);
write_u64(page, OFF_FTS_ROOT, self.fts_root_page);
write_u64(page, OFF_GRAPH_ROOT, self.graph_root_page);
write_u64(page, OFF_FILTER_META, self.filter_meta_page);
write_u64(page, OFF_FILTER_SYMBOLS, self.filter_symbols_page);
stamp_page_checksum(page);
Ok(())
}
pub fn decode(page: &[u8]) -> Result<Self> {
if page.get(OFF_MAGIC..OFF_MAGIC + 8) != Some(&MAGIC[..]) {
return Err(Error::BadHeader);
}
let format_version = read_u32(page, OFF_VERSION).ok_or(Error::BadHeader)?;
if format_version == 0 {
return Err(Error::BadHeader);
}
if format_version > FORMAT_VERSION {
return Err(Error::UnsupportedVersion {
found: format_version,
supported: FORMAT_VERSION,
});
}
let page_size = read_u32(page, OFF_PAGE_SIZE).ok_or(Error::BadHeader)?;
if !page_size_is_valid(page_size) || page.len() != page_size as usize {
return Err(Error::BadHeader);
}
if !page_checksum_is_valid(page) {
return Err(Error::CorruptPage { page_no: 0 });
}
let flags = read_u32(page, OFF_FLAGS).ok_or(Error::BadHeader)?;
if flags & FLAG_ENCRYPTED != 0 {
return Err(Error::Encrypted);
}
let id_len = read_u32(page, OFF_MODEL_ID).ok_or(Error::BadHeader)? as usize;
if id_len > MAX_MODEL_ID_LEN {
return Err(Error::BadHeader);
}
let id_bytes = page
.get(OFF_MODEL_ID + 4..OFF_MODEL_ID + 4 + id_len)
.ok_or(Error::BadHeader)?;
let embedding_model_id =
String::from_utf8(id_bytes.to_vec()).map_err(|_| Error::BadHeader)?;
let page_count = read_u64(page, OFF_PAGE_COUNT).ok_or(Error::BadHeader)?;
if page_count == 0 {
return Err(Error::BadHeader);
}
Ok(Header {
format_version,
page_size,
page_count,
root_btree_page: read_u64(page, OFF_ROOT_BTREE).ok_or(Error::BadHeader)?,
freelist_page: read_u64(page, OFF_FREELIST).ok_or(Error::BadHeader)?,
hnsw_meta_page: read_u64(page, OFF_HNSW_META).ok_or(Error::BadHeader)?,
fts_root_page: read_u64(page, OFF_FTS_ROOT).ok_or(Error::BadHeader)?,
graph_root_page: read_u64(page, OFF_GRAPH_ROOT).ok_or(Error::BadHeader)?,
filter_meta_page: read_u64(page, OFF_FILTER_META).ok_or(Error::BadHeader)?,
filter_symbols_page: read_u64(page, OFF_FILTER_SYMBOLS).ok_or(Error::BadHeader)?,
txn_counter: read_u64(page, OFF_TXN_COUNTER).ok_or(Error::BadHeader)?,
embedding_dims: read_u16(page, OFF_DIMS).ok_or(Error::BadHeader)?,
embedding_quant: read_u16(page, OFF_QUANT).ok_or(Error::BadHeader)?,
embedding_model_id,
flags,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WalHeader {
pub format_version: u32,
pub page_size: u32,
pub salt: u64,
}
impl WalHeader {
pub fn encode(&self) -> [u8; WAL_HEADER_LEN] {
let mut buf = [0u8; WAL_HEADER_LEN];
write_bytes(&mut buf, 0, &WAL_MAGIC);
write_u32(&mut buf, 8, self.format_version);
write_u32(&mut buf, 12, self.page_size);
write_u64(&mut buf, 16, self.salt);
buf
}
pub fn decode(buf: &[u8]) -> Option<Self> {
if buf.get(..8) != Some(&WAL_MAGIC[..]) {
return None;
}
let format_version = read_u32(buf, 8)?;
let page_size = read_u32(buf, 12)?;
if format_version == 0 || format_version > FORMAT_VERSION || !page_size_is_valid(page_size)
{
return None;
}
Some(WalHeader {
format_version,
page_size,
salt: read_u64(buf, 16)?,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WalFrameHeader {
pub page_no: u64,
pub txn_id: u64,
pub commit: bool,
}
impl WalFrameHeader {
pub fn encode(&self, page_image: &[u8], salt: u64) -> [u8; WAL_FRAME_HEADER_LEN] {
let mut buf = [0u8; WAL_FRAME_HEADER_LEN];
write_u64(&mut buf, 0, self.page_no);
write_u64(&mut buf, 8, self.txn_id);
buf[16] = u8::from(self.commit);
let sum = frame_checksum(&buf, page_image, salt);
write_u64(&mut buf, 24, sum);
buf
}
pub fn decode(buf: &[u8], page_image: &[u8], salt: u64) -> Option<Self> {
if buf.len() < WAL_FRAME_HEADER_LEN {
return None;
}
let stored = read_u64(buf, 24)?;
if stored != frame_checksum(buf, page_image, salt) {
return None;
}
let commit_byte = *buf.get(16)?;
if commit_byte > 1 {
return None;
}
Some(WalFrameHeader {
page_no: read_u64(buf, 0)?,
txn_id: read_u64(buf, 8)?,
commit: commit_byte == 1,
})
}
}
pub fn frame_checksum(frame_header: &[u8], page_image: &[u8], salt: u64) -> u64 {
let mut hasher = Xxh3::with_seed(salt);
hasher.update(frame_header.get(..24).unwrap_or_default());
hasher.update(page_image);
hasher.digest()
}
const VECTOR_STRIDE_F32: usize = 4;
pub fn vector_slots_per_page(page_size: u32, dims: u16) -> usize {
let stride = usize::from(dims) * VECTOR_STRIDE_F32;
if stride == 0 {
return 0;
}
(page_size as usize - PAGE_HEADER_LEN - PAGE_TRAILER_LEN) / stride
}
pub fn vector_page_push(page: &mut [u8], dims: u16, vector: &[f32]) -> Result<Option<u16>> {
if vector.len() != usize::from(dims) {
return Err(Error::InvalidArgument("vector length != header dims"));
}
let page_size = page.len() as u32;
let capacity = vector_slots_per_page(page_size, dims);
let header = PageHeader::decode(page).ok_or(Error::InvalidArgument("not a valid page"))?;
if header.page_type != PageType::Vector {
return Err(Error::InvalidArgument("not a VECTOR page"));
}
let used = header.entry_count as usize;
if used >= capacity {
return Ok(None);
}
let stride = usize::from(dims) * VECTOR_STRIDE_F32;
let offset = PAGE_HEADER_LEN + used * stride;
for (i, v) in vector.iter().enumerate() {
write_bytes(page, offset + i * VECTOR_STRIDE_F32, &v.to_le_bytes());
}
PageHeader {
page_type: PageType::Vector,
entry_count: used as u32 + 1,
next_page: header.next_page,
}
.encode_into(page);
Ok(Some(used as u16))
}
pub fn vector_page_get(page: &[u8], dims: u16, slot: u16, page_no: u64) -> Result<Vec<f32>> {
let header = PageHeader::decode(page).ok_or(Error::MalformedPage {
page_no,
what: "page header",
})?;
if header.page_type != PageType::Vector {
return Err(Error::MalformedPage {
page_no,
what: "not a VECTOR page",
});
}
if u32::from(slot) >= header.entry_count {
return Err(Error::MalformedPage {
page_no,
what: "vector slot out of range",
});
}
let stride = usize::from(dims) * VECTOR_STRIDE_F32;
let offset = PAGE_HEADER_LEN + usize::from(slot) * stride;
let bytes = page
.get(offset..offset + stride)
.ok_or(Error::MalformedPage {
page_no,
what: "vector slot bounds",
})?;
Ok(bytes
.chunks_exact(VECTOR_STRIDE_F32)
.map(|c| f32::from_le_bytes([c[0], c[1], c[2], c[3]]))
.collect())
}
pub fn init_vector_page(page: &mut [u8]) {
PageHeader {
page_type: PageType::Vector,
entry_count: 0,
next_page: 0,
}
.encode_into(page);
}
pub const HNSW_DEFAULT_M: u16 = 16;
pub const HNSW_DEFAULT_EF_CONSTRUCTION: u16 = 200;
pub const HNSW_DEFAULT_EF_SEARCH: u16 = 64;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HnswMeta {
pub m: u16,
pub ef_construction: u16,
pub max_layer: u8,
pub entry_point_page: u64,
pub node_count: u64,
}
impl HnswMeta {
pub fn new() -> Self {
HnswMeta {
m: HNSW_DEFAULT_M,
ef_construction: HNSW_DEFAULT_EF_CONSTRUCTION,
max_layer: 0,
entry_point_page: 0,
node_count: 0,
}
}
pub fn encode(&self, page: &mut [u8]) -> Result<()> {
if self.m == 0 {
return Err(Error::InvalidArgument("hnsw m must be >= 1"));
}
if self.node_count > 0 && self.entry_point_page == 0 {
return Err(Error::InvalidArgument(
"non-empty hnsw index requires an entry point",
));
}
page.fill(0);
PageHeader {
page_type: PageType::HnswMeta,
entry_count: 0, next_page: 0,
}
.encode_into(page);
let mut off = PAGE_HEADER_LEN;
write_u16(page, off, self.m);
off += 2;
write_u16(page, off, self.ef_construction);
off += 2;
page[off] = self.max_layer;
off += 1;
write_u64(page, off, self.entry_point_page);
off += 8;
write_u64(page, off, self.node_count);
stamp_page_checksum(page);
Ok(())
}
pub fn decode(page: &[u8], page_no: u64) -> Result<Self> {
let header = PageHeader::decode(page).ok_or(Error::MalformedPage {
page_no,
what: "page header",
})?;
if header.page_type != PageType::HnswMeta {
return Err(Error::MalformedPage {
page_no,
what: "not an HNSW_META page",
});
}
let mut off = PAGE_HEADER_LEN;
let m = read_u16(page, off).ok_or(Error::MalformedPage { page_no, what: "m" })?;
off += 2;
if m == 0 {
return Err(Error::MalformedPage {
page_no,
what: "hnsw m is zero",
});
}
let ef_construction = read_u16(page, off).ok_or(Error::MalformedPage {
page_no,
what: "ef_construction",
})?;
off += 2;
let max_layer = *page.get(off).ok_or(Error::MalformedPage {
page_no,
what: "max_layer",
})?;
off += 1;
let entry_point_page = read_u64(page, off).ok_or(Error::MalformedPage {
page_no,
what: "entry_point_page",
})?;
off += 8;
let node_count = read_u64(page, off).ok_or(Error::MalformedPage {
page_no,
what: "node_count",
})?;
if node_count > 0 && entry_point_page == 0 {
return Err(Error::MalformedPage {
page_no,
what: "hnsw entry point missing",
});
}
Ok(HnswMeta {
m,
ef_construction,
max_layer,
entry_point_page,
node_count,
})
}
}
pub fn max_hnsw_level(page_size: u32, m: u16) -> Option<usize> {
let usable = page_size as usize - PAGE_HEADER_LEN - PAGE_TRAILER_LEN;
let fixed = 16 + 8 + 2 + 1; let layer0 = 2 + usize::from(m) * 2 * 8; let upper = 2 + usize::from(m) * 8; usable
.checked_sub(fixed + layer0)
.map(|rest| (rest / upper).min(31))
}
impl Default for HnswMeta {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HnswNode {
pub record_id: ulid::Ulid,
pub vec_page: u64,
pub vec_slot: u16,
pub layers: Vec<Vec<u64>>,
}
impl HnswNode {
pub fn encode(&self, page_size: u32) -> Option<Vec<u8>> {
if self.layers.len() > u8::MAX as usize {
return None;
}
let mut body = Vec::with_capacity(16 + 8 + 2 + 1 + self.layers.len() * 8);
body.extend_from_slice(&self.record_id.to_bytes());
body.extend_from_slice(&self.vec_page.to_le_bytes());
body.extend_from_slice(&self.vec_slot.to_le_bytes());
body.push(self.layers.len() as u8);
for layer in &self.layers {
let count = u16::try_from(layer.len()).ok()?;
body.extend_from_slice(&count.to_le_bytes());
for &n in layer {
body.extend_from_slice(&n.to_le_bytes());
}
}
let total = PAGE_HEADER_LEN + body.len();
if total > page_size as usize - PAGE_TRAILER_LEN {
return None;
}
let mut page = vec![0u8; page_size as usize];
PageHeader {
page_type: PageType::HnswNode,
entry_count: 0, next_page: 0,
}
.encode_into(&mut page);
page[PAGE_HEADER_LEN..PAGE_HEADER_LEN + body.len()].copy_from_slice(&body);
stamp_page_checksum(&mut page);
Some(page)
}
pub fn decode(page: &[u8], page_no: u64) -> Result<Self> {
let header = PageHeader::decode(page).ok_or(Error::MalformedPage {
page_no,
what: "page header",
})?;
if header.page_type != PageType::HnswNode {
return Err(Error::MalformedPage {
page_no,
what: "not an HNSW_NODE page",
});
}
let mut off = PAGE_HEADER_LEN;
let record_id_bytes: [u8; 16] = page
.get(off..off + 16)
.and_then(|b| b.try_into().ok())
.ok_or(Error::MalformedPage {
page_no,
what: "hnsw record_id",
})?;
off += 16;
let vec_page = read_u64(page, off).ok_or(Error::MalformedPage {
page_no,
what: "hnsw vec_page",
})?;
off += 8;
let vec_slot = read_u16(page, off).ok_or(Error::MalformedPage {
page_no,
what: "hnsw vec_slot",
})?;
off += 2;
let layer_count = *page.get(off).ok_or(Error::MalformedPage {
page_no,
what: "hnsw layer_count",
})? as usize;
off += 1;
let mut layers = Vec::with_capacity(layer_count);
for _ in 0..layer_count {
let count = read_u16(page, off).ok_or(Error::MalformedPage {
page_no,
what: "hnsw neighbor count",
})? as usize;
off += 2;
if count * 8 > page.len().saturating_sub(off) {
return Err(Error::MalformedPage {
page_no,
what: "hnsw neighbor count exceeds page",
});
}
let mut neighbors = Vec::with_capacity(count);
for _ in 0..count {
let n = read_u64(page, off).ok_or(Error::MalformedPage {
page_no,
what: "hnsw neighbor page",
})?;
if n == 0 {
return Err(Error::MalformedPage {
page_no,
what: "hnsw null neighbor page",
});
}
neighbors.push(n);
off += 8;
}
layers.push(neighbors);
}
Ok(HnswNode {
record_id: ulid::Ulid::from_bytes(record_id_bytes),
vec_page,
vec_slot,
layers,
})
}
}
fn read_u16(buf: &[u8], off: usize) -> Option<u16> {
Some(u16::from_le_bytes(buf.get(off..off + 2)?.try_into().ok()?))
}
fn read_u32(buf: &[u8], off: usize) -> Option<u32> {
Some(u32::from_le_bytes(buf.get(off..off + 4)?.try_into().ok()?))
}
fn read_u64(buf: &[u8], off: usize) -> Option<u64> {
Some(u64::from_le_bytes(buf.get(off..off + 8)?.try_into().ok()?))
}
fn write_bytes(buf: &mut [u8], off: usize, val: &[u8]) {
if let Some(dst) = buf.get_mut(off..off + val.len()) {
dst.copy_from_slice(val);
}
}
fn write_u16(buf: &mut [u8], off: usize, val: u16) {
write_bytes(buf, off, &val.to_le_bytes());
}
fn write_u32(buf: &mut [u8], off: usize, val: u32) {
write_bytes(buf, off, &val.to_le_bytes());
}
fn write_u64(buf: &mut [u8], off: usize, val: u64) {
write_bytes(buf, off, &val.to_le_bytes());
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use super::*;
fn sample_header() -> Header {
Header {
format_version: FORMAT_VERSION,
page_size: DEFAULT_PAGE_SIZE,
page_count: 42,
root_btree_page: 3,
freelist_page: 7,
hnsw_meta_page: 9,
fts_root_page: 11,
graph_root_page: 13,
filter_meta_page: 15,
filter_symbols_page: 17,
txn_counter: 1234,
embedding_dims: 384,
embedding_quant: 0,
embedding_model_id: "all-MiniLM-L6-v2-int8".to_owned(),
flags: 0,
}
}
#[test]
fn magic_values_match_spec() {
assert_eq!(&MAGIC, b"MINDFMT1");
assert_eq!(&WAL_MAGIC, b"MINDWAL1");
}
#[test]
fn header_roundtrip() {
let h = sample_header();
let mut page = vec![0u8; DEFAULT_PAGE_SIZE as usize];
h.encode(&mut page).unwrap();
assert_eq!(Header::peek_page_size(&page).unwrap(), DEFAULT_PAGE_SIZE);
assert_eq!(Header::decode(&page).unwrap(), h);
}
#[test]
fn header_roundtrip_min_page_size() {
let mut h = sample_header();
h.page_size = MIN_PAGE_SIZE;
let mut page = vec![0u8; MIN_PAGE_SIZE as usize];
h.encode(&mut page).unwrap();
assert_eq!(Header::decode(&page).unwrap(), h);
}
#[test]
fn header_rejects_bad_magic() {
let mut page = vec![0u8; DEFAULT_PAGE_SIZE as usize];
sample_header().encode(&mut page).unwrap();
page[0] = b'X';
assert!(matches!(Header::decode(&page), Err(Error::BadHeader)));
assert!(matches!(
Header::peek_page_size(&page),
Err(Error::BadHeader)
));
}
#[test]
fn version_1_file_decodes_with_no_fts_index() {
let mut h = sample_header();
h.format_version = 1;
h.fts_root_page = 0;
h.graph_root_page = 0;
h.filter_meta_page = 0;
h.filter_symbols_page = 0;
let mut page = vec![0u8; DEFAULT_PAGE_SIZE as usize];
h.encode(&mut page).unwrap();
assert_eq!(read_u64(&page, OFF_FTS_ROOT), Some(0));
let back = Header::decode(&page).unwrap();
assert_eq!(back.format_version, 1);
assert_eq!(back.fts_root_page, 0);
assert_eq!(back.graph_root_page, 0);
}
#[test]
fn version_2_file_decodes_with_no_graph() {
let mut h = sample_header();
h.format_version = 2;
h.graph_root_page = 0;
h.filter_meta_page = 0;
h.filter_symbols_page = 0;
let mut page = vec![0u8; DEFAULT_PAGE_SIZE as usize];
h.encode(&mut page).unwrap();
assert_eq!(read_u64(&page, OFF_GRAPH_ROOT), Some(0));
let back = Header::decode(&page).unwrap();
assert_eq!(back.format_version, 2);
assert_eq!(back.fts_root_page, 11, "v2 keeps its full-text index");
assert_eq!(back.graph_root_page, 0);
}
#[test]
fn version_6_file_decodes_with_no_filter_meta() {
let mut h = sample_header();
h.format_version = 6;
h.filter_meta_page = 0;
h.filter_symbols_page = 0;
let mut page = vec![0u8; DEFAULT_PAGE_SIZE as usize];
h.encode(&mut page).unwrap();
assert_eq!(read_u64(&page, OFF_FILTER_META), Some(0));
assert_eq!(read_u64(&page, OFF_FILTER_SYMBOLS), Some(0));
let back = Header::decode(&page).unwrap();
assert_eq!(back.format_version, 6);
assert_eq!(back.fts_root_page, 11, "v6 keeps its full-text index");
assert_eq!(back.graph_root_page, 13, "v6 keeps its graph");
assert_eq!(back.filter_meta_page, 0);
assert_eq!(back.filter_symbols_page, 0);
}
#[test]
fn header_refuses_future_version_before_checksum() {
let mut page = vec![0u8; DEFAULT_PAGE_SIZE as usize];
sample_header().encode(&mut page).unwrap();
write_u32(&mut page, OFF_VERSION, FORMAT_VERSION + 1);
assert!(matches!(
Header::decode(&page),
Err(Error::UnsupportedVersion { found, supported })
if found == FORMAT_VERSION + 1 && supported == FORMAT_VERSION
));
}
#[test]
fn header_detects_corruption() {
let mut page = vec![0u8; DEFAULT_PAGE_SIZE as usize];
sample_header().encode(&mut page).unwrap();
page[100] ^= 0xff;
assert!(matches!(
Header::decode(&page),
Err(Error::CorruptPage { page_no: 0 })
));
}
#[test]
fn header_refuses_encrypted_flag() {
let mut page = vec![0u8; DEFAULT_PAGE_SIZE as usize];
sample_header().encode(&mut page).unwrap();
write_u32(&mut page, OFF_FLAGS, FLAG_ENCRYPTED);
stamp_page_checksum(&mut page);
assert!(matches!(Header::decode(&page), Err(Error::Encrypted)));
}
#[test]
fn header_decode_never_panics_on_arbitrary_bytes() {
let mut state = 0x2545F4914F6CDD1Du64;
let mut next = move || {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
state
};
for _ in 0..2000 {
let len = (next() % 8192) as usize;
let mut buf = vec![0u8; len];
for b in &mut buf {
*b = next() as u8;
}
let _ = Header::decode(&buf); let _ = Header::peek_page_size(&buf);
let _ = WalHeader::decode(&buf);
let _ = WalFrameHeader::decode(&buf, &buf, next());
}
}
#[test]
fn page_header_roundtrip_and_rejects() {
let h = PageHeader {
page_type: PageType::BtreeLeaf,
entry_count: 17,
next_page: 99,
};
let mut page = vec![0u8; 64];
h.encode_into(&mut page);
assert_eq!(PageHeader::decode(&page), Some(h));
page[0] = 0xEE; assert_eq!(PageHeader::decode(&page), None);
assert_eq!(PageHeader::decode(&[0u8; 4]), None); }
#[test]
fn page_checksum_roundtrip() {
let mut page = vec![7u8; 1024];
stamp_page_checksum(&mut page);
assert!(page_checksum_is_valid(&page));
page[3] ^= 1;
assert!(!page_checksum_is_valid(&page));
}
#[test]
fn wal_header_roundtrip() {
let h = WalHeader {
format_version: 1,
page_size: 4096,
salt: 0xDEADBEEF,
};
assert_eq!(WalHeader::decode(&h.encode()), Some(h));
assert_eq!(WalHeader::decode(b"NOTAWAL0................"), None);
}
#[test]
fn wal_frame_roundtrip_and_salt_binding() {
let image = vec![9u8; 4096];
let fh = WalFrameHeader {
page_no: 5,
txn_id: 8,
commit: true,
};
let enc = fh.encode(&image, 111);
assert_eq!(WalFrameHeader::decode(&enc, &image, 111), Some(fh));
assert_eq!(WalFrameHeader::decode(&enc, &image, 112), None);
let mut bad = image.clone();
bad[0] ^= 1;
assert_eq!(WalFrameHeader::decode(&enc, &bad, 111), None);
}
#[test]
fn vector_page_push_get_roundtrip_and_fills() {
const DIMS: u16 = 384;
let page_size = DEFAULT_PAGE_SIZE;
let mut page = vec![0u8; page_size as usize];
init_vector_page(&mut page);
let cap = vector_slots_per_page(page_size, DIMS);
assert!(cap > 0);
let v0: Vec<f32> = (0..DIMS).map(|i| i as f32 * 0.001).collect();
let slot0 = vector_page_push(&mut page, DIMS, &v0).unwrap().unwrap();
assert_eq!(slot0, 0);
assert_eq!(vector_page_get(&page, DIMS, slot0, 1).unwrap(), v0);
let v1: Vec<f32> = (0..DIMS).map(|i| -(i as f32)).collect();
let slot1 = vector_page_push(&mut page, DIMS, &v1).unwrap().unwrap();
assert_eq!(slot1, 1);
assert_eq!(vector_page_get(&page, DIMS, slot1, 1).unwrap(), v1);
assert_eq!(vector_page_get(&page, DIMS, slot0, 1).unwrap(), v0);
let mut fresh = vec![0u8; page_size as usize];
init_vector_page(&mut fresh);
let filler = vec![1.0f32; DIMS as usize];
for _ in 0..cap {
assert!(
vector_page_push(&mut fresh, DIMS, &filler)
.unwrap()
.is_some()
);
}
assert_eq!(vector_page_push(&mut fresh, DIMS, &filler).unwrap(), None);
}
#[test]
fn vector_page_rejects_wrong_dims_and_bad_page() {
const DIMS: u16 = 8;
let mut page = vec![0u8; DEFAULT_PAGE_SIZE as usize];
init_vector_page(&mut page);
assert!(matches!(
vector_page_push(&mut page, DIMS, &[0.0; 4]),
Err(Error::InvalidArgument(_))
));
let mut not_vector = vec![0u8; DEFAULT_PAGE_SIZE as usize];
PageHeader {
page_type: PageType::BtreeLeaf,
entry_count: 0,
next_page: 0,
}
.encode_into(&mut not_vector);
assert!(matches!(
vector_page_push(&mut not_vector, DIMS, &[0.0; 8]),
Err(Error::InvalidArgument(_))
));
assert!(matches!(
vector_page_get(¬_vector, DIMS, 0, 9),
Err(Error::MalformedPage { page_no: 9, .. })
));
}
#[test]
fn hnsw_meta_roundtrip() {
let meta = HnswMeta {
m: 16,
ef_construction: 200,
max_layer: 3,
entry_point_page: 42,
node_count: 100_000,
};
let mut page = vec![0u8; DEFAULT_PAGE_SIZE as usize];
meta.encode(&mut page).unwrap();
assert_eq!(HnswMeta::decode(&page, 7).unwrap(), meta);
}
#[test]
fn hnsw_meta_empty_roundtrip() {
let meta = HnswMeta::new();
let mut page = vec![0u8; DEFAULT_PAGE_SIZE as usize];
meta.encode(&mut page).unwrap();
assert_eq!(HnswMeta::decode(&page, 0).unwrap(), meta);
}
#[test]
fn hnsw_meta_rejects_inconsistent_state() {
let mut meta = HnswMeta::new();
meta.node_count = 5;
let mut page = vec![0u8; DEFAULT_PAGE_SIZE as usize];
assert!(matches!(
meta.encode(&mut page),
Err(Error::InvalidArgument(_))
));
let valid = HnswMeta {
m: 16,
ef_construction: 200,
max_layer: 0,
entry_point_page: 9,
node_count: 1,
};
valid.encode(&mut page).unwrap();
write_u64(&mut page, PAGE_HEADER_LEN + 5, 0); stamp_page_checksum(&mut page);
assert!(matches!(
HnswMeta::decode(&page, 3),
Err(Error::MalformedPage { page_no: 3, .. })
));
}
#[test]
fn hnsw_node_roundtrip_multi_layer() {
let node = HnswNode {
record_id: ulid::Ulid::from_parts(1_700_000_000_000, 7),
vec_page: 5,
vec_slot: 3,
layers: vec![vec![1, 2, 3, 4], vec![5, 6], vec![7]],
};
let page = node.encode(DEFAULT_PAGE_SIZE).unwrap();
assert_eq!(HnswNode::decode(&page, 1).unwrap(), node);
}
#[test]
fn hnsw_node_roundtrip_no_neighbors() {
let node = HnswNode {
record_id: ulid::Ulid::from_parts(0, 0),
vec_page: 1,
vec_slot: 0,
layers: vec![vec![]],
};
let page = node.encode(DEFAULT_PAGE_SIZE).unwrap();
assert_eq!(HnswNode::decode(&page, 1).unwrap(), node);
}
#[test]
fn hnsw_node_rejects_when_too_large_for_page() {
let node = HnswNode {
record_id: ulid::Ulid::from_parts(0, 0),
vec_page: 1,
vec_slot: 0,
layers: vec![(1..=2000u64).collect()],
};
assert_eq!(node.encode(MIN_PAGE_SIZE), None);
}
#[test]
fn max_hnsw_level_guarantees_full_nodes_fit() {
for page_size in [MIN_PAGE_SIZE, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE] {
for m in [4u16, 16, 48] {
let Some(level) = max_hnsw_level(page_size, m) else {
assert_eq!((page_size, m), (MIN_PAGE_SIZE, 48));
continue;
};
let mut layers: Vec<Vec<u64>> = vec![(1..=u64::from(m) * 2).collect()];
for _ in 0..level {
layers.push((1..=u64::from(m)).collect());
}
let node = HnswNode {
record_id: ulid::Ulid::from_parts(1, 1),
vec_page: 1,
vec_slot: 0,
layers,
};
assert!(
node.encode(page_size).is_some(),
"full node at page_size {page_size}, m {m}, level {level} must fit"
);
}
}
assert!(max_hnsw_level(DEFAULT_PAGE_SIZE, HNSW_DEFAULT_M).unwrap() >= 16);
}
#[test]
fn hnsw_page_decode_never_panics_on_arbitrary_bytes() {
let mut state = 0xA5A5A5A5A5A5A5A5u64;
let mut next = move || {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
state
};
for _ in 0..2000 {
let len = [64usize, 512, 4096][(next() % 3) as usize];
let mut buf = vec![0u8; len];
for b in &mut buf {
*b = next() as u8;
}
let _ = HnswMeta::decode(&buf, 1);
let _ = HnswNode::decode(&buf, 1);
let _ = vector_page_get(&buf, 384, (next() % 8) as u16, 1);
}
}
}