1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
use crate::error::{Result, Wrap};
use crate::ffi::{
    unqlite_util_load_mmaped_file, unqlite_util_random_num, unqlite_util_random_string,
    unqlite_util_release_mmaped_file,
};
use std::ffi::CString;
use std::mem;
use std::os::raw::c_void;
use std::path::Path;
use crate::UnQLite;

/// Utility interfaces.
pub trait Util {
    /// Generate random string using the UnQLite PRNG.
    ///
    /// It will generate a english alphabet based string of length buf_size (last argument).
    fn random_string(&self, buf_size: u32) -> Vec<u8>;

    /// Generate random number using the UnQLite PRNG.
    ///
    /// It will return a 32-bit unsigned integer between 0 and 0xFFFFFFFF.
    fn random_num(&self) -> u32;
}

impl Util for UnQLite {
    fn random_string(&self, buf_size: u32) -> Vec<u8> {
        unsafe {
            let mut vec: Vec<u8> = Vec::with_capacity(buf_size as usize);
            unqlite_util_random_string(
                self.as_raw_mut_ptr(),
                vec.as_mut_ptr() as *mut i8,
                buf_size,
            )
            .wrap()
            .unwrap();
            vec
        }
    }

    fn random_num(&self) -> u32 {
        unsafe { unqlite_util_random_num(self.as_raw_mut_ptr()) }
    }
}

/// Load memory-mapped file so that we can save it to UnQLite
///
/// NOTE: DONOT USE: will throw unimplemented error.
pub fn load_mmaped_file<P: AsRef<Path>>(path: P) -> Result<Mmap> {
    unsafe {
        let path = path.as_ref();
        let mut ptr: *mut c_void = mem::MaybeUninit::uninit().assume_init();
        let mut size: i64 = 0;
        let cpath = CString::new(
            path.to_str().expect("cannot convert the path to str")
        )?;
        unqlite_util_load_mmaped_file(cpath.as_ptr(), &mut ptr, &mut size)
            .wrap()
            .map(|_| Mmap {
                ptr: ptr,
                size: size,
            })
    }
}

/// UnQLite hosted memory mapped file
pub struct Mmap {
    pub ptr: *mut c_void,
    pub size: i64,
}

impl Drop for Mmap {
    fn drop(&mut self) {
        let _ = wrap!(util_release_mmaped_file, self.ptr, self.size);
    }
}

#[cfg(test)]
#[cfg(feature = "enable-threads")]
mod tests {
    use super::*;
    use UnQLite;

    #[test]
    fn test_random_string() {
        let unqlite = UnQLite::create_in_memory();
        let _ = unqlite.random_string(32);
    }

    #[test]
    fn test_random_num() {
        let _ = UnQLite::create_in_memory().random_num();
    }

    #[test]
    #[cfg(feature = "mmap")]
    fn test_mmap() {
        use std::io::Write;
        use tempfile::NamedTempFile;
        let mut f = NamedTempFile::new().expect("get named temp file");
        let _ = f.write_all(b"Hello, world!");
        let _ = f.sync_all();
        load_mmaped_file(f.path()).unwrap();
    }
}