use std::collections::HashSet;
use crate::pager::Pager;
use crate::pager::format::data_page::body_capacity;
use crate::pager::format::page_kind::PageKind;
use crate::vfs::Vfs;
use crate::{PagedbError, RealmId, Result};
use super::layout::{ENTRY_LEN, PAGE_HEADER_LEN, chain_capacity};
pub async fn rewrite_chain<V: Vfs + Clone>(
pager: &Pager<V>,
realm_id: RealmId,
page_size: usize,
mut entries: Vec<(u64, u64)>,
host_candidates: Vec<u64>,
next_page: u64,
tail: u64,
) -> Result<(u64, u64)> {
let cap = chain_capacity(page_size);
let total = entries.len();
let mut next = next_page;
let mut carved: HashSet<u64> = HashSet::new();
let mut chain_pages: Vec<u64> = Vec::new();
let mut hosts = host_candidates.into_iter();
loop {
let remaining = total - carved.len();
let need = if remaining == 0 {
0
} else {
remaining.div_ceil(cap)
};
if chain_pages.len() >= need {
break;
}
let host = if remaining > 1 { hosts.next() } else { None };
if let Some(h) = host {
carved.insert(h);
chain_pages.push(h);
} else {
chain_pages.push(next);
next += 1;
}
}
entries.retain(|(_, pid)| !carved.contains(pid));
let head = write_chain(pager, realm_id, page_size, &chain_pages, &entries, tail).await?;
Ok((head, next))
}
pub async fn write_chain<V: Vfs + Clone>(
pager: &Pager<V>,
realm_id: RealmId,
page_size: usize,
chain_pages: &[u64],
entries: &[(u64, u64)],
tail: u64,
) -> Result<u64> {
if entries.is_empty() {
return Ok(tail);
}
let cap = chain_capacity(page_size);
let body_len = body_capacity(page_size);
debug_assert!(chain_pages.len() * cap >= entries.len());
let mut written = 0usize;
for (i, &page_id) in chain_pages.iter().enumerate() {
let chunk = &entries[written..(written + cap).min(entries.len())];
let next = chain_pages.get(i + 1).copied().unwrap_or(tail);
let mut body = vec![0u8; body_len];
body[0..8].copy_from_slice(&next.to_le_bytes());
let chunk_len = u32::try_from(chunk.len())
.map_err(|_| PagedbError::Io(std::io::Error::other("free-list chunk_len overflow")))?;
body[8..12].copy_from_slice(&chunk_len.to_le_bytes());
for (j, (cid, pid)) in chunk.iter().enumerate() {
let off = PAGE_HEADER_LEN + j * ENTRY_LEN;
body[off..off + 8].copy_from_slice(&cid.to_le_bytes());
body[off + 8..off + 16].copy_from_slice(&pid.to_le_bytes());
}
pager
.write_main_page(page_id, realm_id, PageKind::Free, &body)
.await?;
written += chunk.len();
}
let head = chain_pages.first().copied().unwrap_or(tail);
Ok(head)
}