use super::{BSIZE, MAX_DATABLK};
use crate::fs::FsSizePlan;
const BM_BITS_PER_BLOCK: u64 = (BSIZE as u64 / 4 - 1) * 32;
#[derive(Debug)]
pub struct AffsSizePlan {
ffs: bool,
content_blocks: u64,
}
impl AffsSizePlan {
#[must_use]
pub fn new(ffs: bool) -> Self {
Self {
ffs,
content_blocks: 3,
}
}
fn payload(&self) -> u64 {
if self.ffs {
BSIZE as u64
} else {
BSIZE as u64 - 24
}
}
fn file_blocks(&self, len: u64) -> u64 {
let data = len.div_ceil(self.payload());
let max = MAX_DATABLK as u64;
let ext = if data <= max {
0
} else {
(data - max).div_ceil(max)
};
1 + data + ext
}
}
impl FsSizePlan for AffsSizePlan {
fn add_dir(&mut self, _path: &str) {
self.content_blocks += 1;
}
fn add_file(&mut self, _path: &str, len: u64) {
self.content_blocks += self.file_blocks(len);
}
fn add_symlink(&mut self, _path: &str, _target: &str) {
self.content_blocks += 1;
}
fn add_device(&mut self, _path: &str) {
self.content_blocks += 1;
}
fn total_size(&self) -> u64 {
let mut total = self.content_blocks;
for _ in 0..8 {
let bitmap = total.saturating_sub(2).div_ceil(BM_BITS_PER_BLOCK);
let next = self.content_blocks + bitmap;
if next == total {
break;
}
total = next;
}
total * BSIZE as u64
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_volume_is_boot_root_bitmap() {
let p = AffsSizePlan::new(true);
assert_eq!(p.total_size(), 4 * 512);
}
#[test]
fn ffs_vs_ofs_data_blocks() {
let mut ffs = AffsSizePlan::new(true);
ffs.add_file("/a", 512); let mut ofs = AffsSizePlan::new(false);
ofs.add_file("/a", 512); assert!(ofs.total_size() > ffs.total_size());
}
#[test]
fn extension_blocks_past_72_data_blocks() {
let p = AffsSizePlan::new(true);
assert_eq!(p.file_blocks(73 * 512), 1 + 73 + 1);
assert_eq!(p.file_blocks(72 * 512), 1 + 72);
}
#[test]
fn directories_and_symlinks_are_one_block() {
let mut p = AffsSizePlan::new(true);
let base = p.total_size();
p.add_dir("/d");
p.add_symlink("/l", "target");
assert!(p.total_size() >= base + 2 * 512);
}
}