use std::path::Path;
use crate::eliasfano::EliasFano;
use crate::error::{Error, Result};
use crate::util::mmap_file;
const ANCHOR_LEN: usize = 16;
const META_LEN: usize = 24;
const FOOTER_MAGIC: [u8; 8] = *b"erigon\x00\x00";
const FIRST_BYTE_FOOTER: u8 = 0x01;
pub struct BtreeIndex {
ef: Option<EliasFano>,
m: Option<u64>,
}
impl BtreeIndex {
pub fn open(path: impl AsRef<Path>) -> Result<BtreeIndex> {
let path = path.as_ref();
let mmap = mmap_file(path)?;
let len = mmap.len();
if len == 0 {
return Ok(BtreeIndex { ef: None, m: None });
}
if len >= ANCHOR_LEN && mmap[len - 8..len] == FOOTER_MAGIC {
let anchor = &mmap[len - ANCHOR_LEN..];
let footer_len = u32::from_be_bytes(anchor[0..4].try_into().unwrap()) as usize;
if footer_len < META_LEN || ANCHOR_LEN + footer_len > len {
return Err(Error::format(format!(
"{}: corrupt .bt footer (footer_len={footer_len}, file={len})",
path.display()
)));
}
let footer_start = len - ANCHOR_LEN - footer_len;
let payload = &mmap[footer_start..len - ANCHOR_LEN];
let keys_count = u64::from_be_bytes(payload[0..8].try_into().unwrap());
let m = u64::from_be_bytes(payload[8..16].try_into().unwrap());
let ef_offset = u64::from_be_bytes(payload[16..24].try_into().unwrap()) as usize;
if ef_offset >= footer_start {
return Err(Error::format(format!(
"{}: corrupt .bt footer (ef_offset={ef_offset} >= body={footer_start})",
path.display()
)));
}
let ef = EliasFano::open(mmap, ef_offset)?;
if ef.len() != keys_count {
return Err(Error::format(format!(
"{}: .bt EF has {} keys, footer says {keys_count}",
path.display(),
ef.len()
)));
}
return Ok(BtreeIndex {
ef: Some(ef),
m: Some(m),
});
}
if mmap[0] == FIRST_BYTE_FOOTER {
return Err(Error::format(format!(
"{}: .bt looks like footer layout but the trailing magic is missing (truncated?)",
path.display()
)));
}
let ef = EliasFano::open(mmap, 0)?;
Ok(BtreeIndex {
ef: Some(ef),
m: None,
})
}
pub fn key_count(&self) -> u64 {
self.ef.as_ref().map_or(0, EliasFano::len)
}
pub fn key_offset(&self, i: u64) -> Option<u64> {
let ef = self.ef.as_ref()?;
(i < ef.len()).then(|| ef.get(i))
}
pub fn m(&self) -> Option<u64> {
self.m
}
pub fn elias_fano(&self) -> Option<&EliasFano> {
self.ef.as_ref()
}
}