zawgl-core 0.1.6

Zawgl Graph Core Library
Documentation
// MIT License
// Copyright (c) 2022 Alexandre RICCIARDI
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

use std::io::{Write, Result};

///PAGING
const PAGE_SIZE: usize = 4096;
const PAGE_COUNTER_SIZE: usize = 8;
const FIRST_FREE_PAGE_PTR: usize = 8;

///RECORDS
const RECORDS_COUNTER_SIZE: usize = 8;
const FREE_LIST_PTR_SIZE: usize = 4;
const FREE_LIST_ITEM_COUNTER_SIZE: usize = 4;
const NEXT_PAGE_PTR: usize = 8;
const NEXT_FREE_PAGE_PTR: usize = 8;
const HEADER_FLAGS: usize = 1; 
const HEADER_SIZE: usize = HEADER_FLAGS + NEXT_FREE_PAGE_PTR + NEXT_PAGE_PTR + FREE_LIST_ITEM_COUNTER_SIZE;

///B+TREE
const NB_CELL: usize = 66;
const NODE_PTR_SIZE: usize = 8;
const KEY_SIZE: usize = 45;
const CELL_HEADER_SIZE: usize = 1;
const BTREE_NODE_HEADER_SIZE: usize = 1;
const FREE_CELLS_NEXT_NODE_PTR_SIZE: usize = 8;
const CELL_SIZE: usize = KEY_SIZE + NODE_PTR_SIZE + CELL_HEADER_SIZE + OVERFLOW_CELL_PTR_SIZE;
const BTREE_NODE_RECORD_SIZE: usize = BTREE_NODE_HEADER_SIZE + CELL_SIZE * NB_CELL + NODE_PTR_SIZE + FREE_CELLS_NEXT_NODE_PTR_SIZE;
const OVERFLOW_CELL_PTR_SIZE: usize = 4;

//NODES && RELATIONSHIPS && PROPERTIES
const NODE_HEADER_SIZE: usize = 1;
const RELATIONSHIP_HEADER_SIZE: usize = 1;
const PROPERTY_HEADER_SIZE: usize = 1;
const NODE_ID_SIZE: usize = 8;
const RELATIONSHIP_ID_SIZE: usize = 8;
const RELATIONSHIP_TYPE_SIZE: usize = 8;
const NODE_TYPE_SIZE: usize = 8;
const PROPERTY_ID_SIZE: usize = 8;
const PROPERTY_TYPE_SIZE: usize = 1;
const PROPERTY_KEY_ID_SIZE: usize = 8;
const PROPERTY_BLOCK_SIZE: usize = 24;
const NODE_RECORD_SIZE: usize = 2 * RELATIONSHIP_ID_SIZE + PROPERTY_ID_SIZE + NODE_TYPE_SIZE;
const RELATIONSHIP_RECORD_SIZE: usize =  2 * NODE_ID_SIZE + RELATIONSHIP_TYPE_SIZE + 2 * RELATIONSHIP_ID_SIZE + PROPERTY_ID_SIZE;
const PROPERTY_RECORD_SIZE: usize = PROPERTY_HEADER_SIZE + PROPERTY_KEY_ID_SIZE + PROPERTY_TYPE_SIZE + PROPERTY_BLOCK_SIZE + PROPERTY_ID_SIZE;

//DYNAMIC STORE
const DYN_HEADER_SIZE: usize = 1;
const DYN_ID_SIZE: usize = 8;
const DYN_PAYLOAD_SIZE: usize = 120;
const DYN_RECORD_SIZE: usize = DYN_HEADER_SIZE + DYN_ID_SIZE + DYN_PAYLOAD_SIZE;


const fn max_nb_records(record_size: usize) -> usize {
    (PAGE_SIZE - HEADER_SIZE) / record_size
}

const fn compute_unused_page_size(record_size: usize, nb_records: usize) -> usize {
    PAGE_SIZE - HEADER_SIZE - nb_records * record_size
}
const fn compute_freelist_size(free_list_capacity: usize) -> usize {
    FREE_LIST_PTR_SIZE * free_list_capacity
}

fn compute_nb_records_per_page(record_size: usize) -> usize {
    let mut nb_records = max_nb_records(record_size);
    let mut unused_space = compute_unused_page_size(record_size, nb_records);
    let mut free_list_size = compute_freelist_size(nb_records);
    while unused_space < free_list_size {
        nb_records -= 1;
        if nb_records == 0 {
            break;
        }
        unused_space = compute_unused_page_size(record_size, nb_records);
        free_list_size = compute_freelist_size(nb_records);
    }
    nb_records
}

fn compute_nb_pages_per_record(record_size: usize) -> usize {
    let page_payload_size = PAGE_SIZE - HEADER_SIZE;
    let nb_pages = record_size / page_payload_size;
    if page_payload_size == record_size {
        1
    } else if nb_pages > 0 {
        nb_pages + 1
    } else {
        nb_pages
    }
}

fn compute_page_free_space_size(record_size: usize, nb_records_per_page: usize, nb_pages_per_records: usize) -> usize {
    let page_payload_size = PAGE_SIZE - HEADER_SIZE;
    if nb_pages_per_records > 0 {
        (nb_pages_per_records * page_payload_size) % record_size 
    } else {
        let free_list_size = compute_freelist_size(nb_records_per_page);
        (page_payload_size - free_list_size) % record_size
    }
}

fn compute_page_payload_size(nb_records_per_page: usize) -> usize {
    let page_payload_size = PAGE_SIZE - HEADER_SIZE;
    let free_list_size = compute_freelist_size(nb_records_per_page);
    page_payload_size - free_list_size
}

fn generate_config() -> Result<()> {
    let mut config = std::fs::File::create("src/buf_config.rs")?;
    writeln!(config, "//PAGING")?;
    writeln!(config, "pub const PAGE_SIZE: usize = {};", PAGE_SIZE)?;
    writeln!(config, "pub const PAGE_COUNTER_SIZE: usize = {};", PAGE_COUNTER_SIZE)?;
    writeln!(config, "pub const FIRST_FREE_PAGE_PTR: usize = {};", FIRST_FREE_PAGE_PTR)?;
    
    writeln!(config, "//RECORDS")?;    
    writeln!(config, "pub const RECORDS_COUNTER_SIZE: usize = {};", RECORDS_COUNTER_SIZE)?;
    writeln!(config, "pub const FREE_LIST_PTR_SIZE: usize = {};", FREE_LIST_PTR_SIZE)?;
    writeln!(config, "pub const FREE_LIST_ITEM_COUNTER_SIZE: usize = {};", FREE_LIST_ITEM_COUNTER_SIZE)?;
    writeln!(config, "pub const NEXT_PAGE_PTR: usize = {};", NEXT_PAGE_PTR)?;
    writeln!(config, "pub const NEXT_FREE_PAGE_PTR: usize = {};", NEXT_FREE_PAGE_PTR)?;
    writeln!(config, "pub const HEADER_FLAGS: usize = {};", HEADER_FLAGS)?;
    writeln!(config, "pub const HEADER_SIZE: usize = {};", HEADER_SIZE)?;

    let nb_records_per_page = compute_nb_records_per_page(BTREE_NODE_RECORD_SIZE);
    let nb_pages_per_record = compute_nb_pages_per_record(BTREE_NODE_RECORD_SIZE);
    writeln!(config, "//BTREE")?;
    writeln!(config, "//PAGE PAYLOAD SIZE {} BYTES", compute_page_payload_size(nb_records_per_page))?;
    writeln!(config, "//UNUSED SPACE {} BYTES", compute_page_free_space_size(BTREE_NODE_RECORD_SIZE, nb_records_per_page, nb_pages_per_record))?;
    writeln!(config, "pub const NB_CELL: usize = {};", NB_CELL)?;
    writeln!(config, "pub const NODE_PTR_SIZE: usize = {};", NODE_PTR_SIZE)?;
    writeln!(config, "pub const KEY_SIZE: usize = {};", KEY_SIZE)?;
    writeln!(config, "pub const CELL_HEADER_SIZE: usize = {};", CELL_HEADER_SIZE)?;
    writeln!(config, "pub const FREE_CELLS_NEXT_NODE_PTR_SIZE: usize = {};", FREE_CELLS_NEXT_NODE_PTR_SIZE)?;
    writeln!(config, "pub const CELL_SIZE: usize = {};", CELL_SIZE)?;
    writeln!(config, "pub const BTREE_NODE_RECORD_SIZE: usize = {};", BTREE_NODE_RECORD_SIZE)?;
    writeln!(config, "pub const OVERFLOW_CELL_PTR_SIZE: usize = {};", OVERFLOW_CELL_PTR_SIZE)?;
    writeln!(config, "pub const BTREE_NODE_HEADER_SIZE: usize = {};", BTREE_NODE_HEADER_SIZE)?;
    writeln!(config, "pub const BTREE_NB_RECORDS_PER_PAGE: usize = {};", nb_records_per_page)?;
    writeln!(config, "pub const BTREE_NB_PAGES_PER_RECORD: usize = {};", nb_pages_per_record)?;


    let nb_node_records_per_page = compute_nb_records_per_page(NODE_RECORD_SIZE);
    let nb_pages_per_node_record = compute_nb_pages_per_record(NODE_RECORD_SIZE);
    writeln!(config, "//NODES")?;
    writeln!(config, "//PAGE PAYLOAD SIZE {} BYTES", compute_page_payload_size(nb_node_records_per_page))?;
    writeln!(config, "//UNUSED SPACE {} BYTES", compute_page_free_space_size(NODE_RECORD_SIZE, nb_node_records_per_page, nb_pages_per_node_record))?;
    writeln!(config, "pub const NODE_HEADER_SIZE: usize = {};", NODE_HEADER_SIZE)?;
    writeln!(config, "pub const NODE_ID_SIZE: usize = {};", NODE_ID_SIZE)?;
    writeln!(config, "pub const NODE_TYPE_SIZE: usize = {};", NODE_TYPE_SIZE)?;
    writeln!(config, "pub const NODE_RECORD_SIZE: usize = {};", NODE_RECORD_SIZE)?;
    writeln!(config, "pub const NODE_NB_RECORDS_PER_PAGE: usize = {};", nb_node_records_per_page)?;
    writeln!(config, "pub const NODE_NB_PAGES_PER_RECORD: usize = {};", nb_pages_per_node_record)?;

    let nb_relationship_records_per_page = compute_nb_records_per_page(RELATIONSHIP_RECORD_SIZE);
    let nb_pages_per_relationship_record = compute_nb_pages_per_record(RELATIONSHIP_RECORD_SIZE);
    writeln!(config, "//RELATIONSHIPS")?;
    writeln!(config, "//PAGE PAYLOAD SIZE {} BYTES", compute_page_payload_size(nb_relationship_records_per_page))?;
    writeln!(config, "//UNUSED SPACE {} BYTES", compute_page_free_space_size(RELATIONSHIP_RECORD_SIZE, nb_relationship_records_per_page, nb_pages_per_relationship_record))?;
    writeln!(config, "pub const RELATIONSHIP_HEADER_SIZE: usize = {};", RELATIONSHIP_HEADER_SIZE)?;
    writeln!(config, "pub const RELATIONSHIP_ID_SIZE: usize = {};", RELATIONSHIP_ID_SIZE)?;
    writeln!(config, "pub const RELATIONSHIP_RECORD_SIZE: usize = {};", RELATIONSHIP_RECORD_SIZE)?;
    writeln!(config, "pub const RELATIONSHIP_NB_RECORDS_PER_PAGE: usize = {};", nb_relationship_records_per_page)?;
    writeln!(config, "pub const RELATIONSHIP_NB_PAGES_PER_RECORD: usize = {};", nb_pages_per_relationship_record)?;
    writeln!(config, "pub const RELATIONSHIP_TYPE_SIZE: usize = {};", RELATIONSHIP_TYPE_SIZE)?;
    
    let nb_property_records_per_page = compute_nb_records_per_page(PROPERTY_RECORD_SIZE);
    let nb_pages_per_property_record = compute_nb_pages_per_record(PROPERTY_RECORD_SIZE);
    writeln!(config, "//PROPERTIES")?;
    writeln!(config, "//PAGE PAYLOAD SIZE {} BYTES", compute_page_payload_size(nb_property_records_per_page))?;
    writeln!(config, "//UNUSED SPACE {} BYTES", compute_page_free_space_size(NODE_RECORD_SIZE, nb_property_records_per_page, nb_pages_per_property_record))?;
    writeln!(config, "pub const PROPERTY_HEADER_SIZE: usize = {};", PROPERTY_HEADER_SIZE)?;
    writeln!(config, "pub const PROPERTY_ID_SIZE: usize = {};", PROPERTY_ID_SIZE)?;
    writeln!(config, "pub const PROPERTY_BLOCK_SIZE: usize = {};", PROPERTY_BLOCK_SIZE)?;
    writeln!(config, "pub const PROPERTY_RECORD_SIZE: usize = {};", PROPERTY_RECORD_SIZE)?;
    writeln!(config, "pub const PROPERTY_NB_RECORDS_PER_PAGE: usize = {};", nb_property_records_per_page)?;
    writeln!(config, "pub const PROPERTY_NB_PAGES_PER_RECORD: usize = {};", nb_pages_per_property_record)?;

    let nb_dyn_records_per_page = compute_nb_records_per_page(DYN_RECORD_SIZE);
    let nb_pages_per_dyn_record = compute_nb_pages_per_record(DYN_RECORD_SIZE);
    writeln!(config, "//DYN STORE")?;
    writeln!(config, "//PAGE PAYLOAD SIZE {} BYTES", compute_page_payload_size(nb_dyn_records_per_page))?;
    writeln!(config, "//UNUSED SPACE {} BYTES", compute_page_free_space_size(DYN_RECORD_SIZE, nb_dyn_records_per_page, nb_pages_per_dyn_record))?;
    writeln!(config, "pub const DYN_HEADER_SIZE: usize = {};", DYN_HEADER_SIZE)?;
    writeln!(config, "pub const DYN_ID_SIZE: usize = {};", DYN_ID_SIZE)?;
    writeln!(config, "pub const DYN_PAYLOAD_SIZE: usize = {};", DYN_PAYLOAD_SIZE)?;
    writeln!(config, "pub const DYN_RECORD_SIZE: usize = {};", DYN_RECORD_SIZE)?;
    writeln!(config, "pub const DYN_NB_RECORDS_PER_PAGE: usize = {};", nb_dyn_records_per_page)?;
    writeln!(config, "pub const DYN_NB_PAGES_PER_RECORD: usize = {};", nb_pages_per_dyn_record)?;
    Ok(())
}

fn main() {
    if let Err(e) = generate_config() {
        eprintln!("Error: {}", e);
    }
}