flex-page 2.1.0

Database storage system
Documentation
#![doc = include_str!("../README.md")]
#![cfg_attr(target_family = "wasm", feature(wasi_ext))]

mod file_ext;
pub mod page_no;

use file_ext::FileExt;
use std::fmt;
use std::fs::File;
use std::io::{ErrorKind, Result};
use std::sync::atomic::{AtomicU64, Ordering};

pub struct Pages<const SIZE: usize> {
    /// Total Page number
    len: AtomicU64,
    pub file: File,
}

impl<const SIZE: usize> Pages<SIZE> {
    /// Create a new `Pages` instance.
    pub fn open(file: File) -> Result<Self> {
        let block_size = SIZE as u64;
        let file_len = file.metadata()?.len();
        // So that, There is no residue bytes.
        if file_len % block_size != 0 {
            return Err(ErrorKind::InvalidData.into());
        }
        Ok(Self {
            file,
            len: (file_len / block_size).into(),
        })
    }

    /// Read a page, without locking.
    pub fn read(&self, num: u64) -> Result<[u8; SIZE]> {
        debug_assert!(num < self.len() as u64);
        let mut buf = [0; SIZE];
        self.file.read_exact_at(&mut buf, SIZE as u64 * num)?;
        Ok(buf)
    }

    #[inline]
    pub fn write(&self, num: u64, buf: [u8; SIZE]) -> Result<()> {
        debug_assert!(num < self.len() as u64);
        self.file.write_all_at(&buf, SIZE as u64 * num)
    }

    /// Increase page number by `count`
    pub fn alloc(&self, count: u64) -> Result<u64> {
        let old_len = self.len.fetch_add(count, Ordering::SeqCst);
        self.file.set_len(SIZE as u64 * (old_len + count))?;
        Ok(old_len)
    }

    pub fn create(&self, buf: [u8; SIZE]) -> Result<u64> {
        let num = self.len.fetch_add(1, Ordering::SeqCst);
        self.write(num, buf)?;
        Ok(num)
    }

    #[inline]
    #[allow(clippy::len_without_is_empty)]
    pub fn len(&self) -> u64 {
        self.len.load(Ordering::SeqCst)
    }

    #[inline]
    pub fn set_len(&self, len: u64) -> Result<()> {
        self.len.store(len, Ordering::SeqCst);
        self.file.set_len(SIZE as u64 * len)
    }
}

impl<const SIZE: usize> fmt::Debug for Pages<SIZE> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Pages")
            .field("len", &self.len)
            .field("block size", &SIZE)
            .finish()
    }
}