use std::fs::File;
use std::io::{BufWriter, Write};
use std::path::Path;
use crate::error::{Result, RingDbError};
fn write_le_file<T: Copy, const N: usize>(
path: &Path,
data: &[T],
to_le: fn(T) -> [u8; N],
) -> Result<()> {
let mut w = BufWriter::with_capacity(1 << 20, File::create(path)?);
for &x in data {
w.write_all(&to_le(x))?;
}
Ok(())
}
fn read_le_file<T, const N: usize>(path: &Path, from_le: fn([u8; N]) -> T) -> Result<Vec<T>> {
let bytes = std::fs::read(path)?;
if bytes.len() % N != 0 {
return Err(RingDbError::Corrupt(format!(
"file '{}' has {} bytes (not a multiple of {})",
path.display(),
bytes.len(),
N,
)));
}
Ok(bytes
.chunks_exact(N)
.map(|b| from_le(b.try_into().unwrap()))
.collect())
}
pub fn write_meta(path: &Path, dims: usize, n_vectors: usize) -> Result<()> {
let mut f = File::create(path)?;
f.write_all(&(dims as u64).to_le_bytes())?;
f.write_all(&(n_vectors as u64).to_le_bytes())?;
Ok(())
}
pub fn read_meta(path: &Path) -> Result<(usize, usize)> {
let bytes = std::fs::read(path)?;
if bytes.len() < 16 {
return Err(RingDbError::Corrupt(format!(
"meta file '{}' is too short ({} bytes, expected ≥ 16)",
path.display(),
bytes.len()
)));
}
let dims = u64::from_le_bytes(bytes[0..8].try_into().unwrap()) as usize;
let n_vectors = u64::from_le_bytes(bytes[8..16].try_into().unwrap()) as usize;
Ok((dims, n_vectors))
}
pub fn write_f32_file(path: &Path, data: &[f32]) -> Result<()> {
write_le_file(path, data, f32::to_le_bytes)
}
pub fn read_f32_file(path: &Path) -> Result<Vec<f32>> {
read_le_file(path, f32::from_le_bytes)
}
pub fn write_u64_file(path: &Path, data: &[u64]) -> Result<()> {
write_le_file(path, data, u64::to_le_bytes)
}
pub fn read_u64_file(path: &Path) -> Result<Vec<u64>> {
read_le_file(path, u64::from_le_bytes)
}
pub fn move_file(src: &Path, dst: &Path) -> Result<()> {
match std::fs::rename(src, dst) {
Ok(()) => Ok(()),
Err(_) => {
std::fs::copy(src, dst)?;
std::fs::remove_file(src)?;
Ok(())
}
}
}