agilulf 0.1.0

Agilulf KV server
use memmap::{MmapMut, MmapOptions};
use std::marker::PhantomData;
use std::mem::size_of;
use std::sync::atomic::{AtomicUsize, Ordering};

quick_error! {
    #[derive(Debug)]
    pub enum LogError {
        FSError(err: agilulf_fs::FSError) {
            from()
        }
        IOError(err: std::io::Error) {
            from()
        }
    }
}
pub type Result<T> = std::result::Result<T, LogError>;

pub trait JudgeReal {
    fn is_real(&self) -> bool;
    fn set_real(&mut self, real: bool);
}

pub struct LogIterator<'a, T> {
    inner_mmap: &'a MmapMut,
    index: usize,
    phantom: PhantomData<T>,
}

impl<'a, T: Clone + JudgeReal> Iterator for LogIterator<'a, T> {
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        let record =
            self.inner_mmap[(self.index * size_of::<T>())..].as_ref() as *const [u8] as *mut T;
        unsafe {
            if (*record).is_real() {
                self.index += 1;
                return Some((*record).clone());
            } else {
                return None;
            }
        }
    }
}

pub struct LogManager<T: JudgeReal + Clone> {
    inner_mmap: MmapMut,
    index: AtomicUsize,
    phantom: PhantomData<T>,
    path: String,
}

impl<T: JudgeReal + Clone> LogManager<T> {
    pub fn create_new(path: &str, length: usize) -> Result<LogManager<T>> {
        std::fs::OpenOptions::new()
            .create(true)
            .write(true)
            .truncate(true)
            .open(path)?;

        LogManager::open(path, length)
    }

    pub fn open(path: &str, length: usize) -> Result<LogManager<T>> {
        log::info!("Opening log from {:#?}", path);

        {
            let file = agilulf_fs::File::open(path)?;
            file.fallocate(0, (size_of::<T>() * length) as i64)?;
        }

        let file = std::fs::OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .open(path)?;

        let mmap = unsafe { MmapOptions::new().map_mut(&file)? };
        let records = mmap.as_ref() as *const [u8] as *mut [T];

        let mut index = 0;
        unsafe {
            let records = &(*records);
            loop {
                if records[index].is_real() {
                    index += 1;
                } else {
                    break;
                }
            }
        }

        Ok(Self {
            inner_mmap: mmap,
            index: AtomicUsize::new(index),
            phantom: PhantomData,
            path: path.to_string(),
        })
    }

    pub fn iter(&self) -> LogIterator<T> {
        LogIterator {
            inner_mmap: &self.inner_mmap,
            index: 0,
            phantom: PhantomData,
        }
    }

    pub fn add_entry(&self, data: T) {
        let index = self.index.fetch_add(1, Ordering::SeqCst);

        unsafe {
            let record =
                self.inner_mmap[(index * size_of::<T>())..].as_ref() as *const [u8] as *mut T;
            (*record).clone_from(&data);
        }
    }

    pub fn rename(&self, new_path: &str) -> Result<()> {
        std::fs::rename(self.path.as_str(), new_path)?;
        Ok(())
    }
}