tablestg 0.4.5

Storage for database tables
Documentation
use atom_file::Data;
use page_store::*;
use std::sync::Arc;

/// returned by [PageSet::load].
pub struct PData {
    pub data: Data,
    pub pnum: u64,
    pub changed: bool,
    /// Causes error if Pageset::note is not called.
    can_drop: bool,
}

impl PData {
    pub fn new(pnum: u64, data: Data, changed: bool) -> Self {
        Self {
            pnum,
            data,
            changed,
            can_drop: false,
        }
    }
    pub fn take_data(&mut self) -> Data {
        std::mem::take(&mut self.data)
    }
    pub fn make_mut(&mut self) -> &mut Vec<u8> {
        Arc::make_mut(&mut self.data)
    }
}

impl Drop for PData {
    fn drop(&mut self) {
        if !self.can_drop {
            panic!();
        }
    }
}

/// Set of working pages, clean and changed.
pub struct PageSet {
    wapd: AccessPagedData,
    clean: HashMap<u64, Data>,
    changed: HashMap<u64, Data>,

    /// Variable length value storage data.
    pub vvs: (u64, u64, u64),

    /// System map table. Records are fixed size, (u64,u64), root and buckets.
    pub sys_map: (u64, u64, u64),

    /// Temporary kludge, should be some kind of map that holds this.
    pub cust_hashmap: (u64, u64),

    /// Temporary kludge, should be some kind of map that holds this.
    pub cust_by_email: (u64, u64),
}

impl PageSet {
    pub fn new(wapd: AccessPagedData) -> Self {
        // If not a new database, root1 and root2 need to be read from page 0.
        let root1 = wapd.alloc_page() + 1;
        let root2 = wapd.alloc_page() + 1;
        Self {
            wapd,
            clean: HashMap::default(),
            changed: HashMap::default(),
            vvs: (0, root1, 0),
            sys_map: (0, root2, 0),
            cust_hashmap: (0, 0),
            cust_by_email: (0, 0),
        }
    }

    pub fn new_page(&self) -> u64 {
        self.wapd.alloc_page() + 1 // Add 1 so that zero can be used as null page.
    }

    pub fn free_page(&mut self, pnum: u64) {
        self.clean.remove(&pnum);
        self.changed.remove(&pnum);
        self.wapd.free_page(pnum - 1);
    }

    pub fn load(&mut self, pnum: u64) -> PData {
        if let Some(data) = self.clean.remove(&pnum) {
            PData::new(pnum, data, false)
        } else if let Some(data) = self.changed.remove(&pnum) {
            PData::new(pnum, data, true)
        } else {
            let data = self.wapd.get_data(pnum - 1);
            PData::new(pnum, data, false)
        }
    }

    pub fn note(&mut self, mut pd: PData) {
        // println!("note pnum={} data={:?}", pnum, data);
        if pd.changed {
            self.changed.insert(pd.pnum, pd.take_data());
        } else {
            self.clean.insert(pd.pnum, pd.take_data());
        }
        pd.can_drop = true;
    }

    pub fn save(&mut self) {
        for (pnum, data) in self.changed.drain() {
            println!("Saving data pnum={} len={}", pnum, data.len());
            self.wapd.set_data(pnum - 1, data);
        }
        self.wapd.save(SaveOp::Save);
    }
}