boltdb 0.0.1

[WIP] A boltdb implementation written in Rust.
Documentation
#![allow(dead_code)]

use std::rc::Rc;

pub struct DatabaseBuilder {}

impl DatabaseBuilder {
    pub fn new() -> Self {
        DatabaseBuilder {}
    }

    pub fn build(&self) -> Database {
        Database::new()
    }
}

pub struct Database {}

impl Database {
    pub fn new() -> Self {
        Database {}
    }

    pub fn begin(&mut self) -> Transaction {
        Transaction::new()
    }
}

type TransactionId = u64;

pub struct Transaction {}

impl Transaction {
    pub fn new() -> Self {
        Transaction {}
    }

    pub fn commit(&mut self) {}

    pub fn rollback(&mut self) {}
}

pub struct TransactionStats {}

#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct BucketInner {
    root: PageId,
    sequence: u64,
}

#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct Meta {
    magic: u32,
    version: u32,
    page_size: u32,
    flags: u32,
    root: BucketInner,
    freelist: PageId,
    page_id: PageId,
    tx_id: TransactionId,
    checksum: u64,
}

impl Meta {
    fn validate(&self) {}
}

/// Page type flags.
const PAGE_FLAG_BRANCH: u16 = 0x01;
const PAGE_FLAG_LEAF: u16 = 0x02;
const PAGE_FLAG_META: u16 = 0x04;
const PAGE_FLAG_FREELIST: u16 = 0x10;

/// Page header size.
const PAGE_HEADER_SIZE: usize = std::mem::size_of::<Page>();

type PageId = u64;

#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct Page {
    id: PageId,
    flags: u16,
    count: u16,
    overflow: u32,
}

impl Page {
    /// Returns a typed PageType.
    fn page_type(&self) -> PageType {
        if (self.flags & PAGE_FLAG_BRANCH) != 0 {
            PageType::Branch
        } else if (self.flags & PAGE_FLAG_LEAF) != 0 {
            PageType::Leaf
        } else if (self.flags & PAGE_FLAG_META) != 0 {
            PageType::Meta
        } else if (self.flags & PAGE_FLAG_FREELIST) != 0 {
            PageType::Freelist
        } else {
            PageType::Unknown(self.flags)
        }
    }

    /// Returns a reference to the metadata section of the page.
    unsafe fn meta(&self) -> &Meta {
        let ptr: *const u8 = std::mem::transmute(self);
        let offset = PAGE_HEADER_SIZE as isize;
        std::mem::transmute(ptr.offset(offset))
    }

    /// Retrieves the element by index.
    unsafe fn element<T>(&self, index: usize) -> &T {
        let ptr: *const u8 = std::mem::transmute(self);
        let offset = (PAGE_HEADER_SIZE + std::mem::size_of::<T>() * index) as isize;
        std::mem::transmute(ptr.offset(offset))
    }

    /// Retrieves a vec of elements.
    unsafe fn elements<T>(&self) -> Option<Vec<T>> where T: Clone {
        let length = self.count as usize;
        if length == 0 {
            return None;
        }
        let ptr: *const u8 = std::mem::transmute(self);
        let offset = PAGE_HEADER_SIZE as isize;
        let ptr: *const T = std::mem::transmute(ptr.offset(offset));
        Some(std::slice::from_raw_parts(ptr, length).to_vec())
    }
}

#[derive(Debug, Copy, Clone)]
enum PageType {
    Branch,
    Leaf,
    Meta,
    Freelist,
    Unknown(u16),
}

impl std::fmt::Display for PageType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            PageType::Branch => write!(f, "branch"),
            PageType::Leaf => write!(f, "leaf"),
            PageType::Meta => write!(f, "meta"),
            PageType::Freelist => write!(f, "freelist"),
            PageType::Unknown(flags) => write!(f, "unknown<{}>", flags)
        }
    }
}

/// Represents human readable information about a page.
#[derive(Debug, Copy, Clone)]
struct PageInfo {
    id: i32,
    page_type: PageType,
    count: i32,
    overflow_count: i32,
}

/// Represents a node on a branch page.
#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct BranchPageElement {
    position: u32,
    k_size: u32,
    page_id: PageId,
}

impl BranchPageElement {
    /// Returns a byte slice of the node key.
    unsafe fn key(&self) -> Vec<u8> {
        let ptr: *const u8 = std::mem::transmute(self);
        std::slice::from_raw_parts(ptr.offset(self.position as isize), self.k_size as usize).to_vec()
    }
}

/// Represents a node on a leaf page.
#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct LeafPageElement {
    flags: u32,
    position: u32,
    k_size: u32,
    v_size: u32,
}

impl LeafPageElement {
    /// Returns a byte slice of the node key.
    unsafe fn key(&self) -> Vec<u8> {
        let ptr: *const u8 = std::mem::transmute(self);
        std::slice::from_raw_parts(ptr.offset(self.position as isize), self.k_size as usize).to_vec()
    }

    /// Returns a byte slice of the node value.
    unsafe fn value(&self) -> Vec<u8> {
        let ptr: *const u8 = std::mem::transmute(self);
        std::slice::from_raw_parts(ptr.offset((self.position + self.k_size) as isize), self.k_size as usize).to_vec()
    }
}

// /// Minimal keys per page.
// const MIN_KEYS_PER_PAGE: usize = 2;
//
// /// Flags of i-node type.
// const INODE_FLAG_COMMON_LEAF: usize = 0x00;
// const INODE_FLAG_BUCKET_LEAF: usize = 0x01;

/// Represents an in-memory, deserialized page.
#[derive(Debug)]
struct Node {
    is_leaf: bool,
    unbalanced: bool,
    spilled: bool,
    key: Vec<u8>,
    page_id: PageId,
    parent: Option<Rc<Box<Node>>>,
    children: Vec<Rc<Box<Node>>>,
    inodes: Vec<INode>,
}

impl Node {
    /// Returns the top-level node this node is attached to.
    fn root(node: Rc<Box<Node>>) -> Rc<Box<Node>> {
        match node.parent.clone() {
            Some(p) => Node::root(p),
            None => node,
        }
    }

    fn min_keys(&self) -> i32 {
        if self.is_leaf { 1 } else { 2 }
    }
}

/// Represents an internal node inside of a node.
/// It can be used to point to elements in a page or point
/// to an element which hasn't been added to a page yet.
#[derive(Debug)]
struct INode {
    flags: u32,
    page_id: PageId,
    key: Vec<u8>,
    value: Vec<u8>,
}

mod tests {
    #[allow(unused_imports)]
    use crate::*;

    #[test]
    fn test_page_meta() {
        let meta = Meta {
            magic: 0,
            version: 1,
            page_size: 2,
            flags: 3,
            root: BucketInner {
                root: 4,
                sequence: 5,
            },
            freelist: 6,
            page_id: 7,
            tx_id: 8,
            checksum: 9,
        };

        let ptr: *const u8 = unsafe { std::mem::transmute(&meta) };
        let mut meta_bytes = unsafe { std::slice::from_raw_parts(ptr, std::mem::size_of::<Meta>()).to_vec() };
        let mut page_bytes = vec![0u8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0];
        page_bytes.append(&mut meta_bytes);
        let page: &Page = unsafe { std::mem::transmute(page_bytes.as_ptr()) };
        let meta = unsafe { page.meta() };

        assert!(if let PageType::Branch = page.page_type() { true } else { false });
        assert_eq!(meta.magic, 0);
        assert_eq!(meta.version, 1);
        assert_eq!(meta.page_size, 2);
        assert_eq!(meta.flags, 3);
        assert_eq!(meta.root.root, 4);
        assert_eq!(meta.root.sequence, 5);
        assert_eq!(meta.freelist, 6);
        assert_eq!(meta.page_id, 7);
        assert_eq!(meta.tx_id, 8);
        assert_eq!(meta.checksum, 9);
    }

    #[test]
    fn test_page_element() {
        let page_bytes = vec![0u8, 0, 0, 0, 0, 0, 0, 0, 2, 0, 4, 0, 0, 0, 0, 0, 6, 7, 8, 9];
        let page: &Page = unsafe { std::mem::transmute(page_bytes.as_ptr()) };
        assert!(if let PageType::Leaf = page.page_type() { true } else { false });
        assert_eq!(unsafe { *page.element::<u8>(0) as usize }, 6);
        assert_eq!(unsafe { *page.element::<u8>(1) as usize }, 7);
        assert_eq!(unsafe { *page.element::<u8>(2) as usize }, 8);
        assert_eq!(unsafe { *page.element::<u8>(3) as usize }, 9);
    }

    #[test]
    fn test_page_elements() {
        let page_bytes = vec![0u8, 0, 0, 0, 0, 0, 0, 0, 2, 0, 4, 0, 0, 0, 0, 0, 6, 7, 8, 9];
        let page: &Page = unsafe { std::mem::transmute(page_bytes.as_ptr()) };
        let elements = unsafe { page.elements::<u8>().unwrap() };
        assert!(if let PageType::Leaf = page.page_type() { true } else { false });
        assert_eq!(elements[0] as usize, 6);
        assert_eq!(elements[1] as usize, 7);
        assert_eq!(elements[2] as usize, 8);
        assert_eq!(elements[3] as usize, 9);
    }

    #[test]
    fn test_node_root() {
        let node = Rc::new(Box::new(Node {
            is_leaf: false,
            unbalanced: false,
            spilled: false,
            key: vec![],
            page_id: 0,
            parent: Some(Rc::new(Box::new(Node {
                is_leaf: false,
                unbalanced: false,
                spilled: false,
                key: vec![],
                page_id: 0,
                parent: Some(Rc::new(Box::new(Node {
                    is_leaf: true,
                    unbalanced: false,
                    spilled: false,
                    key: vec![],
                    page_id: 0,
                    parent: None,
                    children: vec![],
                    inodes: vec![],
                }))),
                children: vec![],
                inodes: vec![],
            }))),
            children: vec![],
            inodes: vec![],
        }));

        let root = Node::root(node);
        println!("{:?}", root.is_leaf);
    }
}