matdb 0.1.0

An experimental embedded SQL-like DBMS
Documentation
use std::io::{Seek, SeekFrom, Write};

use anyhow::Result;

use crate::{Db, PAGE_SIZE, WRITE_SIZE};

use super::free::FreeStyle;

impl Db {
    pub(super) fn flush(&mut self) -> Result<()> {
        if let Some(file) = self.file.as_mut() {
            file.set_len(self.page_count * PAGE_SIZE as u64)?;
            for (no, page) in &self.write_pages {
                file.seek(SeekFrom::Start(no * PAGE_SIZE as u64))?;
                file.write_all(&page)?;
            }

            self.write_pages.clear();
        }

        Ok(())
    }

    pub(super) fn append_or_overwrite_page(
        &mut self,
        page_number: u64,
        page: &[u8],
    ) -> Result<Option<u64>> {
        Ok(
            if let FreeStyle::CanBeFreed = self.can_be_freed(page_number)? {
                self.set_page(page_number, page)?;

                None
            } else {
                Some(self.append_page(page)?)
            },
        )
    }

    fn set_page(&mut self, page_number: u64, page: &[u8]) -> Result<u64> {
        self.written_anything_this_tx = true;
        assert_eq!(PAGE_SIZE, page.len());

        if self.file.is_some() && self.write_pages.len() >= WRITE_SIZE {
            self.flush()?;
        }

        self.invalidate_cache(page_number);
        self.write_pages.insert(page_number, page.to_vec());

        Ok(page_number)
    }

    pub(super) fn set_first_page(&mut self, page: &[u8]) -> Result<()> {
        self.set_page(0, page)?;
        if self.page_count == 0 {
            self.page_count += 1;
        }
        Ok(())
    }

    pub(super) fn append_page_at_end(&mut self, page: &[u8]) -> Result<u64> {
        let pre_page_count = self.page_count;
        self.page_count += 1;

        self.set_page(pre_page_count, page)
    }

    pub(super) fn append_page(&mut self, page: &[u8]) -> Result<u64> {
        if let Some(free_page) = self
            .free_list
            .pop_last()
            .map(|p| Ok(p))
            .or_else(|| {
                self.load_next_freelist()
                    .map(|_| self.free_list.pop_last())
                    .transpose()
            })
            .transpose()?
        {
            self.set_page(free_page, page)
        } else {
            self.append_page_at_end(page)
        }
    }
}