tablestg 0.3.0

Storage for database tables
Documentation
use crate::*;

/// A Store stores up to 255 records in a Page.
//
/// Has an array of up to 255 16-bit values, which reference data stored later in the page.
/// The page header has numbers such as current number of occupied slots, first free slot, data allocation offset, free space total,
/// Extension length and page number.
///
/// A reference to a stored record consists of a Page number and an index 1..32.
pub struct Store<'a> {
    pub data: Data,
    pub ps: &'a mut PageSet, // Used to access extension pages etc.
}

#[derive(Debug)]
pub enum AllocErr {
    Id,
    Data
}

/// 16-bit value is interpreted 3 bits tag, then 13 bit offset as follows:
///
/// 13-bit offset to data ( which consists of 1 byte length then inline data )
///
/// 13-bit offset to data ( which consists of 1 byte length and 2 byte offset into extension data )
///
/// 13-bit offset to data ( which consists of 2 byte length and 2 byte offset into extension data )
///
/// 13-bit offset to data ( which consists of 2 byte length and 3 byte offset into extension data )
///
/// 13-bit offset to data ( which consists of 3 byte length and 6 byte page number )
///
/// 13-bit offset to data ( which consists of 4 byte length and 6 byte page number )
///
/// 13-bit offset to data ( which consists of 6 byte length and 6 byte page number )
///
/// Some records with size < 256 bytes may be stored in extension data, to keep the Store page becoming too big.
///
/// Basic worst-case page size is 255 x 12 = 3060 bytes (plus some header bytes), with no inline data.

impl<'a> Store<'a> {
    pub fn new(ps: &'a mut PageSet) -> Self
    {
       let mut md = Vec::new();
       let noff = 255;
       md.resize( DATA + 2 * noff, 0 );
       md[NOFF] = noff as u8;
       
       let data = Arc::new(md);
       Self{ ps, data }
    }

    /// Store user data, result is local id of data.
    pub fn write(&mut self, user_data: &[u8]) -> Result<u8,AllocErr> {
        let md = &mut Arc::make_mut(&mut self.data);
        let id = alloc_id(md)?;
        let len = user_data.len();
 
        // At this point depending on len and space available, may alloc as extension data or new TreeVec.
        // For initial test assume all records are small.
        
        let off = alloc_inline(md, len+1)?;
        md[off] = len as u8;
        let loc = &mut md[off+1..off+1+len];
        loc.copy_from_slice(user_data);
        set_off(md, id, off);

        Ok(id)
    }

    /// Get the size of the data associated with local id
    pub fn size(&self, id: u8) -> usize {
        let off = self.get_off(id);
        // Should check top 3 bits of off to determinte how data is stored.
        let len = self.data[off] as usize;
        len
    }

    /// Read data. Could have an optimised version to avoid copying if possible.
    pub fn read(&self, id: u8) -> Vec<u8> {
        let off = self.get_off(id);
        let len = self.data[off] as usize;
        let loc = &self.data[off+1..off+1+len];
        let mut result = Vec::new();
        result.extend_from_slice(loc);
        result
    }
    
    /// ToDo
    pub fn update(&mut self, _id: u8, _data: &[u8]) {
        todo!()
    }

    /// Delete record.
    pub fn delete(&mut self, id: u8) {
        let md = &mut Arc::make_mut(&mut self.data);
        let x = md[FFID];
        md[FFID] = id;
        set_off( md, id, x as usize);
        // Should also track free space here.
    }
    
    pub fn space(&self) -> usize
    {
        todo!()
    }
    
    pub fn pack(&mut self)
    {
        todo!()
    }

    fn get_off(&self, id:u8) -> usize
    {
       let off = DATA + ( (id-1) as usize ) * 2;
       self.data[off] as usize + 256 * ( self.data[off+1] as usize )
    }

    /// Read ids.
    pub fn ids(&self) -> Vec<u8> // Could return a bitmap instead.
    {
        // Need to avoid ids in free list.
        // So start by creating bitmap of ids in free list.
        let mut bitmap = [0u8; 16];
        let mut fid = self.data[FFID];
        while fid != 0
        {
            let i = ( fid - 1 ) as usize;
            bitmap[ i / 8 ] |= 1 << ( i % 8 );
            fid = self.get_off(fid) as u8;
        }
        let mut result = Vec::new();
        let n = self.data[IDNX];
        for id in 1..=n { 
            let i = ( id - 1 ) as usize;
            if bitmap[ i / 8 ] & ( 1 << ( i % 8 ) ) == 0
            {
                result.push(id); 
            }
        }
        result
    }
}

// Offsets.
const NOFF : usize = 0; // Size of offset table ( 1 byte )
const IDNX : usize = 1; // Next Id
const FFID : usize = 2; // First Free Id ( 2 bytes )
const _DTOT : usize = 3; // Data allocated ( 2 bytes )
const _FTOT : usize = 5; // Total free space due to deletions ( 2 bytes )
const DATA : usize = 7; // Start of data area ( offset table then data ).

fn alloc_id(md: &mut [u8]) -> Result<u8,AllocErr>
{
    let mut id : u8 = md[FFID];
    if id == 0 
    {
        id = md[IDNX];
        if id == md[NOFF] { return Err(AllocErr::Id); } // Offset table is full
        md[IDNX] = id + 1;
    }
    else
    {
        md[FFID] = get_off(md,id) as u8;
    }
    Ok(id+1)
}

fn alloc_inline(v: &mut Vec<u8>, len: usize) -> Result<usize,AllocErr>
{
    let result = v.len();
    v.resize( result + len, 0);
    Ok(result)
}

fn get_off(md: &mut [u8], id: u8) -> usize
{
    let off = DATA + ( (id-1) as usize ) * 2;
    md[off] as usize + 256 * ( md[off+1] as usize )
}

fn set_off(md: &mut [u8], id: u8, val: usize)
{
    let off = DATA + ( (id-1) as usize ) * 2;
    md[off] = ( val % 256 ) as u8;
    md[off+1] = ( val / 256 ) as u8;
}

#[cfg(test)]
pub fn test_store(ps: &mut PageSet)
{
   let td1 = b"Hello George";
   let td2 = b"Marilyn";
   
   let mut s = Store::new(ps);
   
   let id1 = s.write( td1 ).unwrap();
   let id2 = s.write( td2 ).unwrap();

   let x1 = s.read(id1);
   let x2 = s.read(id2);

   assert_eq!( x1, td1 );
   assert_eq!( x2, td2 );

   s.delete( id1 );

   let ids = s.ids();
   println!("ids={:?}", ids);

   let root = s.ps.new_page();
   s.ps.note( root, s.data, true );
}