use crate::pager::format::data_page::body_capacity;
use crate::{PagedbError, Result};
pub(crate) const ENTRY_LEN: usize = 16;
pub(crate) const PAGE_HEADER_LEN: usize = 12;
pub const CHAIN_METADATA_CID: u64 = 0;
#[must_use]
pub const fn chain_capacity(page_size: usize) -> usize {
(body_capacity(page_size) - PAGE_HEADER_LEN) / ENTRY_LEN
}
pub(crate) fn decode_chain_header(body: &[u8]) -> Result<(u64, usize)> {
if body.len() < PAGE_HEADER_LEN {
return Err(PagedbError::corruption(
crate::errors::CorruptionDetail::CatalogRowInvalid {
field: "freelist chain page shorter than its header",
},
));
}
let mut next_b = [0u8; 8];
next_b.copy_from_slice(&body[0..8]);
let next = u64::from_le_bytes(next_b);
let mut cnt_b = [0u8; 4];
cnt_b.copy_from_slice(&body[8..12]);
let count = u32::from_le_bytes(cnt_b) as usize;
if count > (body.len() - PAGE_HEADER_LEN) / ENTRY_LEN {
return Err(PagedbError::corruption(
crate::errors::CorruptionDetail::CatalogRowInvalid {
field: "freelist chain page entry count exceeds capacity",
},
));
}
Ok((next, count))
}
pub(crate) fn decode_entry(body: &[u8], index: usize) -> Result<(u64, u64)> {
let offset = PAGE_HEADER_LEN + index * ENTRY_LEN;
let slot = body.get(offset..offset + ENTRY_LEN).ok_or_else(|| {
PagedbError::corruption(crate::errors::CorruptionDetail::CatalogRowInvalid {
field: "freelist chain page entry runs past the page body",
})
})?;
let mut cid_b = [0u8; 8];
cid_b.copy_from_slice(&slot[0..8]);
let mut pid_b = [0u8; 8];
pid_b.copy_from_slice(&slot[8..16]);
Ok((u64::from_le_bytes(cid_b), u64::from_le_bytes(pid_b)))
}