use crate::format::free_space::{self, FreeSpaceHeader, FreeSpaceManager};
use crate::format::messages::superblock_ext::FileSpaceInfoMessage;
use crate::format::{FormatContext, UNDEF_ADDR};
use crate::io::allocator::FreeBlock;
use crate::io::file_handle::FileHandle;
use crate::io::{IoError, IoResult};
pub(crate) struct ManagerContents {
pub(crate) blocks: Vec<(u64, u64)>,
pub(crate) sections: Vec<FreeBlock>,
}
pub(crate) fn read_managers(
handle: &mut FileHandle,
ctx: &FormatContext,
info: &FileSpaceInfoMessage,
) -> IoResult<ManagerContents> {
let policy = free_space::SpacePolicy::for_message(info);
let hdr_size = FreeSpaceHeader::encoded_size(ctx);
let mut blocks = Vec::new();
let mut found: Vec<(FreeSpaceManager, Vec<(u64, u64)>)> = FreeSpaceManager::ALL
.iter()
.map(|&manager| (manager, Vec::new()))
.collect();
for (slot, &addr) in info.fs_addr.iter().enumerate() {
if addr == UNDEF_ADDR || addr == 0 {
continue;
}
let Some(manager) = FreeSpaceManager::from_message_slot(slot) else {
return Err(IoError::InvalidState(format!(
"the file names a free-space manager in message slot {slot}, which only a \
driver with a non-contiguous address space fills"
)));
};
if manager == FreeSpaceManager::Large && policy.page().is_none() {
return Err(IoError::InvalidState(
"an unpaged file names the large free-space manager, which only paged \
aggregation has"
.into(),
));
}
let hdr = FreeSpaceHeader::decode(&handle.read_at(addr, hdr_size)?, ctx)?;
blocks.push((addr, hdr_size as u64));
if hdr.sect_addr == UNDEF_ADDR || hdr.sect_size == 0 {
continue;
}
let image = handle.read_at(hdr.sect_addr, hdr.sect_size as usize)?;
let decoded = free_space::decode_sections(&image, &hdr, addr, ctx)?;
blocks.push((hdr.sect_addr, hdr.alloc_sect_size.max(hdr.sect_size)));
let bucket = &mut found.iter_mut().find(|(m, _)| *m == manager).unwrap().1;
bucket.extend(decoded.iter().map(|s| (s.addr, s.len)));
}
let mut sections: Vec<FreeBlock> = Vec::new();
for (manager, blocks) in &found {
sections.extend(blocks.iter().map(|&(addr, len)| FreeBlock {
addr,
len,
manager: *manager,
}));
}
free_space::check_disjoint(§ions.iter().map(|s| (s.addr, s.len)).collect::<Vec<_>>())
.map_err(|why| {
IoError::InvalidState(format!(
"the free-space managers of this file overlap: {why}"
))
})?;
sections.sort_unstable_by_key(|b| b.addr);
Ok(ManagerContents { blocks, sections })
}
pub(crate) fn tracked_free_space(
handle: &mut FileHandle,
ctx: &FormatContext,
info: &FileSpaceInfoMessage,
) -> IoResult<u64> {
Ok(read_managers(handle, ctx, info)?
.sections
.iter()
.map(|s| s.len)
.sum())
}