use crate::stg::Storage;
use crate::util;
use std::cmp::min;
use std::collections::BTreeSet;
pub struct CompactFile {
pub stg: Box<dyn Storage>,
sp_size: usize,
ep_size: usize,
ep_resvd: u64,
ep_count: u64,
ep_free: BTreeSet<u64>,
lp_alloc: u64,
lp_first: u64,
lp_alloc_dirty: bool,
lp_free: BTreeSet<u64>,
is_new: bool,
}
impl CompactFile {
const HSIZE: u64 = 28;
pub fn new(mut stg: Box<dyn Storage>, sp_size: usize, ep_size: usize) -> Self {
let fsize = stg.size();
let is_new = fsize == 0;
let mut x = Self {
sp_size,
ep_size,
stg,
ep_resvd: 12,
ep_count: 12,
ep_free: BTreeSet::new(),
lp_alloc: 0,
lp_first: u64::MAX,
lp_alloc_dirty: false,
lp_free: BTreeSet::new(),
is_new,
};
if is_new {
x.writeu64(0, x.ep_resvd);
x.writeu16(24, x.sp_size as u16);
x.writeu16(26, x.ep_size as u16);
x.lp_alloc_dirty = true;
} else {
x.ep_resvd = x.readu64(0);
x.lp_alloc = x.readu64(8);
x.lp_first = x.readu64(16);
x.sp_size = x.readu16(24) as usize;
x.ep_size = x.readu16(26) as usize;
}
x.ep_count = (fsize + (x.ep_size as u64) - 1) / (x.ep_size as u64);
if x.ep_count < x.ep_resvd {
x.ep_count = x.ep_resvd;
}
if is_new {
x.save();
}
x
}
pub fn set_page(&mut self, lpnum: u64, data: &[u8], size: usize) {
self.extend_starter_pages(lpnum);
let ext = self.ext(size);
let off = Self::HSIZE + (self.sp_size as u64) * lpnum;
let mut starter = vec![0_u8; self.sp_size];
self.read(off, &mut starter);
let old_size = util::get(&starter, 0, 2) as usize;
let mut old_ext = self.ext(old_size);
util::set(&mut starter, 0, size as u64, 2);
if ext != old_ext {
while old_ext > ext {
old_ext -= 1;
let fp = util::getu64(&starter, 2 + old_ext * 8);
self.ep_free.insert(fp);
}
while old_ext < ext {
let np = self.ep_alloc();
util::setu64(&mut starter[2 + old_ext * 8..], np);
old_ext += 1;
}
}
let off = 2 + ext * 8;
let mut done = min(self.sp_size - off, size);
starter[off..off + done].copy_from_slice(&data[0..done]);
let woff = Self::HSIZE + (self.sp_size as u64) * lpnum;
self.write(woff, &starter[0..off + done]);
for i in 0..ext {
let amount = min(size - done, self.ep_size - 8);
let page = util::getu64(&starter, 2 + i * 8) as u64;
let woff = page * (self.ep_size as u64);
self.writeu64(woff, lpnum);
self.write(woff + 8, &data[done..done + amount]);
done += amount;
}
debug_assert!(done == size);
}
pub fn page_size(&mut self, lpnum: u64) -> usize {
if self.lp_valid(lpnum) {
self.readu16(Self::HSIZE + (self.sp_size as u64) * lpnum)
} else {
0
}
}
pub fn get_page(&mut self, lpnum: u64, data: &mut [u8]) -> usize {
if !self.lp_valid(lpnum) {
return 0;
}
let off = Self::HSIZE + (self.sp_size as u64) * lpnum;
let mut starter = vec![0_u8; self.sp_size];
self.read(off, &mut starter);
let size = util::get(&starter, 0, 2) as usize; let ext = self.ext(size);
let off = 2 + ext * 8;
let mut done = min(size, self.sp_size - off);
data[0..done].copy_from_slice(&starter[off..off + done]);
for i in 0..ext {
let amount = min(size - done, self.ep_size - 8);
let page = util::getu64(&starter, 2 + i * 8);
let roff = page * (self.ep_size as u64);
debug_assert!(self.readu64(roff) == lpnum);
self.read(roff + 8, &mut data[done..done + amount]);
done += amount;
}
debug_assert!(done == size);
size
}
pub fn alloc_page(&mut self) -> u64 {
if let Some(p) = self.lp_free.iter().next() {
*p
} else {
self.lp_alloc_dirty = true;
if self.lp_first != u64::MAX {
let p = self.lp_first;
self.lp_first = self.readu64(Self::HSIZE + p * self.sp_size as u64 + 2);
p
} else {
let p = self.lp_alloc;
self.lp_alloc += 1;
p
}
}
}
pub fn free_page(&mut self, pnum: u64) {
self.lp_free.insert(pnum);
}
pub fn is_new(&self) -> bool {
self.is_new
}
pub fn rollback(&mut self) {
self.lp_free.clear();
if self.lp_alloc_dirty {
self.lp_alloc_dirty = false;
self.lp_alloc = self.readu64(8);
self.lp_first = self.readu64(16);
}
}
pub fn save(&mut self) {
for p in &std::mem::take(&mut self.lp_free) {
let p = *p;
self.set_page(p, &[], 0);
self.writeu64(Self::HSIZE + p * self.sp_size as u64 + 2, self.lp_first);
self.lp_first = p;
self.lp_alloc_dirty = true;
}
while !self.ep_free.is_empty() {
self.ep_count -= 1;
let from = self.ep_count;
if !self.ep_free.remove(&from) {
let to = self.ep_alloc();
self.relocate(from, to);
}
}
if self.lp_alloc_dirty {
self.lp_alloc_dirty = false;
self.writeu64(8, self.lp_alloc);
self.writeu64(16, self.lp_first);
}
self.stg.commit(self.ep_count * self.ep_size as u64);
}
fn readu64(&mut self, offset: u64) -> u64 {
let mut bytes = [0; 8];
self.read(offset, &mut bytes);
u64::from_le_bytes(bytes)
}
fn readu16(&mut self, offset: u64) -> usize {
let mut bytes = [0; 2];
self.read(offset, &mut bytes);
u16::from_le_bytes(bytes) as usize
}
fn writeu64(&mut self, offset: u64, x: u64) {
self.write(offset, &x.to_le_bytes());
}
fn writeu16(&mut self, offset: u64, x: u16) {
self.write(offset, &x.to_le_bytes());
}
fn read(&mut self, off: u64, bytes: &mut [u8]) {
self.stg.read(off, bytes);
}
fn write(&mut self, off: u64, bytes: &[u8]) {
self.stg.write(off, bytes);
}
fn relocate(&mut self, from: u64, to: u64) {
if from == to {
return;
}
let mut buffer = vec![0; self.ep_size];
self.read(from * self.ep_size as u64, &mut buffer);
self.write(to * self.ep_size as u64, &buffer);
let lpnum = util::getu64(&buffer, 0);
let mut off = Self::HSIZE + lpnum * self.sp_size as u64;
let size = self.readu16(off);
let mut ext = self.ext(size);
off += 2;
loop {
debug_assert!(ext != 0);
let x = self.readu64(off);
if x == from {
self.writeu64(off, to);
break;
}
off += 8;
ext -= 1;
}
}
fn ep_clear(&mut self, epnum: u64) {
let buf = vec![0; self.ep_size];
self.write(epnum * self.ep_size as u64, &buf);
}
fn lp_valid(&mut self, lpnum: u64) -> bool {
Self::HSIZE + (lpnum + 1) * (self.sp_size as u64) <= self.ep_resvd * (self.ep_size as u64)
}
fn extend_starter_pages(&mut self, lpnum: u64) {
let mut save = false;
while !self.lp_valid(lpnum) {
self.relocate(self.ep_resvd, self.ep_count);
self.ep_clear(self.ep_resvd);
self.ep_resvd += 1;
self.ep_count += 1;
save = true;
}
if save {
self.writeu64(0, self.ep_resvd);
}
}
fn ep_alloc(&mut self) -> u64 {
if let Some(pp) = self.ep_free.iter().next() {
let p = *pp;
self.ep_free.remove(&p);
p
} else {
let p = self.ep_count;
self.ep_count += 1;
p
}
}
fn ext(&self, size: usize) -> usize {
let mut n = 0;
if size > (self.sp_size - 2) {
n = ((size - (self.sp_size - 2)) + (self.ep_size - 16 - 1)) / (self.ep_size - 16);
}
debug_assert!(2 + 16 * n + size <= self.sp_size + n * self.ep_size);
n
}
pub fn compress(&self, size: usize, saving: usize) -> bool {
self.ext(size - saving) < self.ext(size)
}
}