tablestg 0.2.0

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

/// Value for Hash Map lookup.
pub trait SmallFixed {
    fn size() -> usize;
    fn load(bytes: &[u8]) -> Self;
    fn save(&self, bytes: &mut [u8]);
}

/// Key for Hash Map lookup.
pub trait Key<T>
where
    T: SmallFixed,
{
    fn hash(&self) -> u64;
    fn equal(&self, v: &T, ps: &mut PageSet) -> bool;
}

/// [HashMap] - maps keys to 64 bit ids using 64 bit hash.
pub mod hashmap;
pub use hashmap::*;

/// [PageSet] - keeps track of changed pages that need saving.
pub mod pageset;
pub use pageset::*;

/// [Table] -- record storage. Divided into pages, each page stores 64 records.
pub mod table;
pub use table::*;

/// [VarVal] -- storage of variable length values.
pub mod varval;
pub use varval::*;

// Private modules.

/// [PageTree] - list of pages implemented as tree.
mod pagetree;
use pagetree::*;

/// [TreeVec] -- a variable length list of bytes. Used to implement [Table].
mod treevec;
use treevec::*;

/// Hash Map Bucket - maps up to 127 keys to 64 bit ids using 64 bit hash.
mod bucket;

const PAGE_SIZE: u64 = 4612;
// pub const PAGE_SIZE: u64 =  4612;

#[test]
fn test() {
    use page_store::*;

    let limits = Limits::default();
    /*
    let _dt = DataType::String{};
    let _dt = DataType::Binary{};
    let _dt = DataType::Int(4);
    let _dt = DataType::Array( 5, Box::new(DataType::String) );
    let _dt = DataType::Map( Box::new(DataType::String), Box::new(DataType::String) );
    let _dt = DataType::Struct( Vec::new() );
    */

    // Construct BlockPageStg.
    let file = atom_file::MultiFileStorage::new("test.db");
    let upd = atom_file::FastFileStorage::new("test.upd");
    let af = atom_file::AtomicFile::new_with_limits(file, upd, &limits.af_lim);
    let ps = BlockPageStg::new(af, &limits);
    let is_new = ps.is_new();

    let spd = SharedPagedData::new_from_ps(ps);

    println!("max page size={}", spd.psi.max_size_page());

    let mut ps = PageSet::new(spd.new_writer());

    if false {
        let (root, len) = if is_new { (ps.new_page(), 0) } else { (3, 410) };
        test_tv(root, len, &mut ps);
    }

    if true {
        println!("Calling test_hash");
        hashmap::test_hash(&mut ps);
    }

    if false {
        test_table(&mut ps);
    }

    if false {
        test_varval(&mut ps);
    }

    // ps.save();

    spd.shutdown();
}

#[cfg(test)]
fn tos(s: &[u8]) -> &str {
    str::from_utf8(s).unwrap()
}

/*
   For a table with very fragmented ids, you can make a new table with new compact ids, and map the old ids to the new ones using a PagedHashMap.

   Access is slightly slower, due to the extra Hash lookup.
*/

#[derive(serde::Serialize, serde::Deserialize, Clone, Hash, PartialEq, Eq, Debug)]
pub enum DataType {
    /// e.g. date
    Named(u64),

    /// e.g. `struct{ name: string, email: string, created: date }`
    Struct(Vec<(String, DataType)>),

    /// e.g. `( string, string, int )`
    Tuple(Vec<DataType>),

    /// e.g. `enum{ leaf: int, node: [int] }`
    Enum(Vec<(String, DataType)>),

    /// e.g. `string`
    String(u8),

    /// e.g. `binary(50)` - 50 is the number of bytes stored inline.
    Binary(u8),

    /// e.g. `int`
    Int(u8),

    /// e.g. `[string; 5]`
    Array(usize, Box<DataType>),

    /// e.g. `[string]`
    List(Box<DataType>),

    /// e.g. `[string->int]`
    Map(Box<DataType>, Box<DataType>),
}

impl DataType {
    pub fn to_bytes(&self) -> Vec<u8> {
        postcard::to_stdvec(self).unwrap()
    }
    pub fn from_bytes(b: &[u8]) -> Self {
        postcard::from_bytes(b).unwrap()
    }
}

use pstd::localalloc::Local;
pub type LString = pstd::StringA<Local>;
pub type LVec<T> = pstd::VecA<T, Local>;

#[derive(serde::Serialize, serde::Deserialize, Clone, Hash, PartialEq, Eq, Debug)]
pub enum Value {
    String(LString),
    Int(i64),
    Binary(LVec<u8>),
    List(LVec<Value>),
    VarVal(u64, u64),
    IList(LVec<i64>),
}

impl Value {
    pub fn to_bytes(&self) -> Vec<u8> {
        postcard::to_stdvec(self).unwrap()
    }
    pub fn from_bytes(b: &[u8]) -> Self {
        postcard::from_bytes(b).unwrap()
    }
}

#[test]
fn test_value() {
    use pstd::veca;
    let v1 = Value::List(veca![
        Value::String(LString::from("George Barwood")),
        Value::String(LString::from("george.barwood@gmail.com")),
        Value::String(LString::from("33 Sandpipe Close, GL2 4LZ")),
        Value::Int(-47),
        Value::Int(99),
        Value::VarVal(100, 20),
        Value::IList(veca![1, 2, 3, 4]),
    ]);
    let bytes = v1.to_bytes();
    println!("len={} bytes={:?}", bytes.len(), bytes);

    let v2 = Value::from_bytes(&bytes);
    println!("v2={:?}", v2);
}

#[test]
fn test_datatype() {
    let t1 = DataType::String(20);
    let t2 = DataType::Int(3);
    let t3 = DataType::Struct(vec![
        ("name".to_string(), t1.clone()),
        ("email".to_string(), t1.clone()),
    ]);
    let t4 = DataType::Tuple(vec![t1, t2, t3]);

    let bytes = t4.to_bytes();
    // println!("bytes={:?}", bytes);

    let t5 = DataType::from_bytes(&bytes);
    // println!("t5={:?}", t5);
    assert_eq!(t5, t4);
}

/* System tables.

   VarVal tables with diffent chunk sizes.
   Schema table
     Name : string
   Table table
     Schema, Name, DataType (varval), root/len, next_id

   DataType table
     Schema, Name, DataType (varval)

   Function table
     Schema, Name, Definition (varval)
*/