use crate::{DbError, DbResult};
use crate::commutative::{CHDR, CMAG};
use memmap2::{Mmap, MmapOptions};
use std::fs::{self, OpenOptions};
use std::path::Path;
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
pub struct SegmentMeta {
pub id: u32,
pub path: String,
pub sealed: bool,
pub used: u64,
pub min_height: u64,
pub max_height: u64,
}
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, Default)]
pub struct Manifest {
pub segments: Vec<SegmentMeta>,
pub next_id: u32,
}
impl Manifest {
pub fn load(path: &Path) -> DbResult<Self> {
if !path.exists() {
return Ok(Self::default());
}
let raw = fs::read_to_string(path).map_err(DbError::Io)?;
serde_json::from_str(&raw).map_err(|e| {
DbError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidData,
e.to_string(),
))
})
}
pub fn save(&self, path: &Path) -> DbResult<()> {
let tmp = path.with_extension("tmp");
fs::write(&tmp, serde_json::to_string_pretty(self).unwrap())
.map_err(DbError::Io)?;
fs::rename(&tmp, path).map_err(DbError::Io)
}
}
pub struct SealedSegment {
pub meta: SegmentMeta,
mmap: Mmap,
}
impl SealedSegment {
pub fn open(meta: SegmentMeta) -> DbResult<Self> {
let f = OpenOptions::new()
.read(true)
.open(&meta.path)
.map_err(DbError::Io)?;
let mmap = unsafe { MmapOptions::new().map(&f).map_err(DbError::Io)? };
Ok(Self { meta, mmap })
}
pub fn get_at(&self, key: &[u8; 32], height: u64) -> Option<Vec<u8>> {
let m = &self.mmap;
let used = self.meta.used as usize;
if height < self.meta.min_height || height > self.meta.max_height {
return None;
}
let mut best: Option<Vec<u8>> = None;
let mut cur = 8usize;
while cur + CHDR + 32 <= used {
let magic = u32::from_le_bytes(m[cur..cur + 4].try_into().unwrap());
if magic != CMAG {
break; }
let vlen = u32::from_le_bytes(m[cur + 4..cur + 8].try_into().unwrap()) as usize;
let total = CHDR + 32 + vlen;
if cur + total > used || vlen > used {
break; }
let rec_key: [u8; 32] = m[cur + CHDR..cur + CHDR + 32].try_into().unwrap();
if rec_key == *key {
best = Some(m[cur + CHDR + 32..cur + total].to_vec());
}
cur += total;
}
best
}
}