blob: 8e4121f669a46b44926d6a001185911d5a38af82 [file]
use crate::config::FullTextIndexMetadata;
use crate::error::{FtIndexError, Result};
use crate::io::{ReadRequest, SeekRead, SeekWrite};
use serde::{Deserialize, Serialize};
pub const FORMAT_MAGIC: &[u8; 8] = b"PFTIDX01";
pub const FORMAT_VERSION: u32 = 1;
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct ArchiveFileEntry {
pub name: String,
pub offset: u64,
pub length: u64,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct IndexHeader {
pub metadata: FullTextIndexMetadata,
pub files: Vec<ArchiveFileEntry>,
}
pub fn write_envelope<W: SeekWrite>(
output: &mut W,
header: &IndexHeader,
files: &[(String, Vec<u8>)],
) -> Result<()> {
let header_json = serde_json::to_vec(header)?;
output.write_all(FORMAT_MAGIC)?;
output.write_all(&FORMAT_VERSION.to_be_bytes())?;
output.write_all(
&u32::try_from(header_json.len())
.map_err(|_| FtIndexError::InvalidStorage("header too large".to_string()))?
.to_be_bytes(),
)?;
output.write_all(&header_json)?;
for (_, data) in files {
output.write_all(data)?;
}
output.flush()?;
Ok(())
}
pub fn read_header<R: SeekRead>(input: &mut R) -> Result<(IndexHeader, u64)> {
let mut fixed = [0u8; 16];
read_exact_at(input, 0, &mut fixed)?;
if &fixed[0..8] != FORMAT_MAGIC {
return Err(FtIndexError::InvalidStorage("bad magic".to_string()));
}
let version = u32::from_be_bytes(fixed[8..12].try_into().expect("slice length"));
if version != FORMAT_VERSION {
return Err(FtIndexError::InvalidStorage(format!(
"unsupported format version {version}"
)));
}
let header_len = u32::from_be_bytes(fixed[12..16].try_into().expect("slice length")) as usize;
let mut header_json = vec![0u8; header_len];
read_exact_at(input, 16, &mut header_json)?;
let header = serde_json::from_slice(&header_json)?;
Ok((header, 16 + header_len as u64))
}
pub fn read_exact_at<R: SeekRead>(input: &mut R, pos: u64, buf: &mut [u8]) -> Result<()> {
let mut request = [ReadRequest { pos, buf }];
input.pread(&mut request)?;
Ok(())
}