use crate::{
block::{BlockStg, RSVD_SIZE},
util, Arc, Data, Storage,
};
use std::cmp::min;
pub struct DividedStg {
pub bs: BlockStg,
base: u64,
}
pub const FD_SIZE: usize = 8 + 8;
pub struct FD {
root: u64,
size: u64,
blocks: u64,
level: u8,
pub changed: bool,
}
impl FD {
pub fn size(&self) -> u64 {
self.size
}
fn set_size(&mut self, size: u64, blocks: u64) {
self.changed = true;
self.size = size;
self.blocks = blocks;
}
}
impl DividedStg {
pub fn new(stg: Box<dyn Storage>, blk_cap: u64) -> Self {
let bs = BlockStg::new(stg, blk_cap);
let base = bs.blk_cap() / bs.nsz() as u64;
Self { bs, base }
}
pub fn blk_cap(&self) -> u64 {
self.bs.blk_cap()
}
pub fn new_file(&mut self) -> FD {
FD {
root: self.bs.new_block(),
level: 0,
size: 0,
blocks: 1,
changed: true,
}
}
pub fn drop_file(&mut self, f: &mut FD) {
self.truncate(f, 0);
self.bs.drop_block(f.root);
}
pub fn truncate(&mut self, f: &mut FD, size: u64) {
if size < f.size {
let reqd = self.blocks(size);
if reqd < f.blocks {
let levels = self.levels(reqd);
let mut new_root = f.root;
for _ in levels..f.level {
new_root = self.get_num(new_root, 0);
}
let mut level = f.level;
let mut old = f.blocks;
let mut new = reqd;
while level > 0 && old != new {
self.reduce_blocks(f, level, old, new);
new = (new + self.base - 1) / self.base;
old = (old + self.base - 1) / self.base;
level -= 1;
}
if levels < f.level {
self.bs.drop_block(f.root);
f.root = new_root;
f.level = levels;
}
}
f.set_size(size, reqd);
}
}
pub fn write(&mut self, f: &mut FD, offset: u64, data: &[u8]) {
let n = data.len();
let data = Arc::new(data.to_vec());
self.write_data(f, offset, data, n);
}
pub fn write_data(&mut self, f: &mut FD, offset: u64, data: Data, n: usize) {
self.allocate(f, offset + n as u64);
if f.blocks == 1 {
self.bs.write_data(f.root, offset, data, 0, n);
} else {
self.write_blocks(f, offset, data, n);
}
}
pub fn read(&self, f: &FD, offset: u64, data: &mut [u8]) {
if f.blocks == 1 {
self.bs.read(f.root, offset, data);
} else {
self.read_blocks(f, offset, data);
}
}
pub fn save_fd(&self, fd: &FD, buf: &mut [u8]) {
debug_assert!(fd.level == self.levels(fd.blocks));
debug_assert!(fd.blocks == self.blocks(fd.size));
util::setu64(&mut buf[0..8], fd.root);
util::setu64(&mut buf[8..16], fd.size);
}
pub fn load_fd(&self, buf: &[u8]) -> FD {
let root = util::getu64(buf, 0);
let size = util::getu64(buf, 8);
let blocks = self.blocks(size);
let level = self.levels(blocks);
FD {
root,
size,
blocks,
level,
changed: false,
}
}
pub fn set_root(&mut self, fd: &FD) {
let mut rsvd = [0; RSVD_SIZE];
self.save_fd(fd, &mut rsvd);
self.bs.set_rsvd(rsvd);
}
pub fn get_root(&self) -> FD {
let rsvd = self.bs.get_rsvd();
self.load_fd(&rsvd)
}
pub fn save(&mut self) {
self.bs.save();
}
pub fn wait_complete(&self) {
self.bs.wait_complete();
}
fn allocate(&mut self, f: &mut FD, size: u64) {
if size > f.size {
let reqd = self.blocks(size);
if reqd > f.blocks {
let new_level = self.levels(reqd);
while f.level < new_level {
let blk = self.bs.new_block();
self.set_num(blk, 0, f.root);
f.root = blk;
f.level += 1;
}
self.add_blocks(f, reqd);
}
f.set_size(size, reqd);
}
}
fn write_blocks(&mut self, f: &FD, offset: u64, data: Data, n: usize) {
let mut done = 0;
while done < n {
let off = offset + done as u64;
let (blk, off) = (off / self.blk_cap(), off % self.blk_cap());
let a = min(n - done, (self.blk_cap() - off) as usize);
let blk = self.get_block(f.root, f.level, blk);
self.bs.write_data(blk, off, data.clone(), done, a);
done += a;
}
}
fn read_blocks(&self, f: &FD, offset: u64, data: &mut [u8]) {
let (mut done, len) = (0, data.len());
while done < len {
let off = offset + done as u64;
let (blk, off) = (off / self.blk_cap(), off % self.blk_cap());
let a = min(len - done, (self.blk_cap() - off) as usize);
if blk < f.blocks {
let blk = self.get_block(f.root, f.level, blk);
self.bs.read(blk, off, &mut data[done..done + a]);
}
done += a;
}
}
fn add_blocks(&mut self, f: &mut FD, new: u64) {
for ix in f.blocks..new {
let nb = self.bs.new_block();
self.set_block(f.root, f.level, ix, nb);
}
}
fn reduce_blocks(&mut self, f: &mut FD, level: u8, old: u64, new: u64) {
for ix in new..old {
let blk = self.get_block(f.root, level, ix);
self.bs.drop_block(blk);
}
}
fn blocks(&self, size: u64) -> u64 {
if size == 0 {
return 1;
}
(size + self.blk_cap() - 1) / self.blk_cap()
}
fn levels(&self, blocks: u64) -> u8 {
if blocks <= 1 {
0
} else {
(blocks - 1).ilog(self.base) as u8 + 1
}
}
fn nsz(&self) -> usize {
self.bs.nsz()
}
fn set_block(&mut self, mut blk: u64, level: u8, mut ix: u64, value: u64) {
if level > 1 {
let x = ix / self.base;
ix %= self.base;
blk = if ix == 0 {
let nb = self.bs.new_block();
self.set_block(blk, level - 1, x, nb);
nb
} else {
self.get_block(blk, level - 1, x)
};
}
self.set_num(blk, ix * self.nsz() as u64, value);
}
fn get_block(&self, mut blk: u64, level: u8, mut ix: u64) -> u64 {
if level > 1 {
let x = ix / self.base;
ix %= self.base;
blk = self.get_block(blk, level - 1, x);
}
self.get_num(blk, ix * self.nsz() as u64)
}
fn get_num(&self, blk: u64, off: u64) -> u64 {
let mut bytes = [0; 8];
self.bs.read(blk, off, &mut bytes[0..self.nsz()]);
u64::from_le_bytes(bytes)
}
fn set_num(&mut self, blk: u64, off: u64, v: u64) {
self.bs.write(blk, off, &v.to_le_bytes()[0..self.nsz()]);
}
}
#[test]
fn divided_stg_test() {
let blk_cap = 10000;
let stg = crate::MemFile::new();
let mut ds = DividedStg::new(stg.clone(), blk_cap);
let mut f = ds.new_file();
let data = b"hello george";
ds.write(&mut f, 0, data);
let test_off = 200 * blk_cap;
ds.write(&mut f, test_off, data);
ds.save();
let mut ds = DividedStg::new(stg.clone(), blk_cap);
let mut buf = vec![0; data.len()];
ds.read(&f, 0, &mut buf);
assert!(&buf == data);
let mut buf = vec![0; data.len()];
ds.read(&f, test_off, &mut buf);
assert!(&buf == data);
ds.truncate(&mut f, 10 * blk_cap);
ds.drop_file(&mut f);
ds.save();
}