use crate::bits;
use std::fmt::Debug;
use std::fs::OpenOptions;
#[cfg(not(target_family = "wasm"))]
use std::fs::File;
use std::io::{Error, ErrorKind, Read, Write};
#[cfg(not(target_family = "wasm"))]
use std::ops::{Deref, Index};
#[cfg(not(target_family = "wasm"))]
use std::os::fd::AsRawFd;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::{env, fs, io, mem, process, slice, str};
#[cfg(not(target_family = "wasm"))]
use std::{marker, ptr};
#[cfg(test)]
mod tests;
pub trait Serialize: Sized {
fn serialize<T: Write>(&self, writer: &mut T) -> io::Result<()> {
self.serialize_header(writer)?;
self.serialize_body(writer)?;
Ok(())
}
fn serialize_header<T: Write>(&self, writer: &mut T) -> io::Result<()>;
fn serialize_body<T: Write>(&self, writer: &mut T) -> io::Result<()>;
fn load<T: Read>(reader: &mut T) -> io::Result<Self>;
fn size_in_elements(&self) -> usize;
fn size_in_bytes(&self) -> usize {
bits::words_to_bytes(self.size_in_elements())
}
}
pub trait Serializable: Sized + Default {
fn elements() -> usize {
mem::size_of::<Self>() / bits::WORD_BYTES
}
}
impl Serializable for u64 {}
impl Serializable for usize {}
impl Serializable for (u64, u64) {}
impl<V: Serializable> Serialize for V {
fn serialize_header<T: Write>(&self, _: &mut T) -> io::Result<()> {
Ok(())
}
fn serialize_body<T: Write>(&self, writer: &mut T) -> io::Result<()> {
unsafe {
let buf: &[u8] = slice::from_raw_parts(self as *const Self as *const u8, mem::size_of::<Self>());
writer.write_all(buf)?;
}
Ok(())
}
fn load<T: Read>(reader: &mut T) -> io::Result<Self> {
let mut value = Self::default();
unsafe {
let buf: &mut [u8] = slice::from_raw_parts_mut(&mut value as *mut Self as *mut u8, mem::size_of::<Self>());
reader.read_exact(buf)?;
}
Ok(value)
}
fn size_in_elements(&self) -> usize {
Self::elements()
}
}
impl<V: Serializable> Serialize for Vec<V> {
fn serialize_header<T: Write>(&self, writer: &mut T) -> io::Result<()> {
let size = self.len();
size.serialize(writer)?;
Ok(())
}
fn serialize_body<T: Write>(&self, writer: &mut T) -> io::Result<()> {
unsafe {
let buf: &[u8] = slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * mem::size_of::<V>());
writer.write_all(buf)?;
}
Ok(())
}
fn load<T: Read>(reader: &mut T) -> io::Result<Self> {
let size = usize::load(reader)?;
let mut value: Vec<V> = Vec::with_capacity(size);
unsafe {
let buf: &mut [u8] = slice::from_raw_parts_mut(value.as_mut_ptr() as *mut u8, size * mem::size_of::<V>());
reader.read_exact(buf)?;
value.set_len(size);
}
Ok(value)
}
fn size_in_elements(&self) -> usize {
1 + self.len() * V::elements()
}
}
impl Serialize for Vec<u8> {
fn serialize_header<T: Write>(&self, writer: &mut T) -> io::Result<()> {
let size = self.len();
size.serialize(writer)?;
Ok(())
}
fn serialize_body<T: Write>(&self, writer: &mut T) -> io::Result<()> {
writer.write_all(self.as_slice())?;
let padded_len = bits::round_up_to_word_bytes(self.len());
if padded_len > self.len() {
let padding = [0u8; bits::WORD_BYTES];
writer.write_all(&padding[0..padded_len - self.len()])?;
}
Ok(())
}
fn load<T: Read>(reader: &mut T) -> io::Result<Self> {
let size = usize::load(reader)?;
let mut value: Vec<u8> = vec![0; size];
reader.read_exact(value.as_mut_slice())?;
let padded_len = bits::round_up_to_word_bytes(value.len());
if padded_len > value.len() {
let mut padding = [0u8; bits::WORD_BYTES];
reader.read_exact(&mut padding[0..padded_len - value.len()])?;
}
Ok(value)
}
fn size_in_elements(&self) -> usize {
1 + bits::bytes_to_words(self.len())
}
}
impl Serialize for String {
fn serialize_header<T: Write>(&self, writer: &mut T) -> io::Result<()> {
let size = self.len();
size.serialize(writer)?;
Ok(())
}
fn serialize_body<T: Write>(&self, writer: &mut T) -> io::Result<()> {
writer.write_all(self.as_bytes())?;
let padded_len = bits::round_up_to_word_bytes(self.len());
if padded_len > self.len() {
let padding = [0u8; bits::WORD_BYTES];
writer.write_all(&padding[0..padded_len - self.len()])?;
}
Ok(())
}
fn load<T: Read>(reader: &mut T) -> io::Result<Self> {
let bytes = Vec::<u8>::load(reader)?;
String::from_utf8(bytes).map_err(|_| Error::new(ErrorKind::InvalidData, "Invalid UTF-8"))
}
fn size_in_elements(&self) -> usize {
1 + bits::bytes_to_words(self.len())
}
}
impl<V: Serialize> Serialize for Option<V> {
fn serialize_header<T: Write>(&self, writer: &mut T) -> io::Result<()> {
let mut size: usize = 0;
if let Some(value) = self {
size = value.size_in_elements();
}
size.serialize(writer)?;
Ok(())
}
fn serialize_body<T: Write>(&self, writer: &mut T) -> io::Result<()> {
if let Some(value) = self {
value.serialize(writer)?;
}
Ok(())
}
fn load<T: Read>(reader: &mut T) -> io::Result<Self> {
let size = usize::load(reader)?;
if size == 0 {
Ok(None)
} else {
let value = V::load(reader)?;
Ok(Some(value))
}
}
fn size_in_elements(&self) -> usize {
let mut result: usize = 1;
if let Some(value) = self {
result += value.size_in_elements();
}
result
}
}
#[cfg(not(target_family = "wasm"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum MappingMode {
ReadOnly,
Mutable,
}
#[cfg(not(target_family = "wasm"))]
#[derive(Debug)]
pub struct MemoryMap {
_file: File, filename: PathBuf,
mode: MappingMode,
ptr: *mut u64,
len: usize,
}
#[cfg(not(target_family = "wasm"))]
impl MemoryMap {
pub fn new<P: AsRef<Path>>(filename: P, mode: MappingMode) -> io::Result<MemoryMap> {
let write = match mode {
MappingMode::ReadOnly => false,
MappingMode::Mutable => true,
};
let mut options = OpenOptions::new();
let file = options.read(true).write(write).open(&filename)?;
let metadata = file.metadata()?;
let len = metadata.len() as usize;
if len != bits::round_up_to_word_bytes(len) {
return Err(Error::new(ErrorKind::Other, "File size must be a multiple of 8 bytes"));
}
let prot = match mode {
MappingMode::ReadOnly => libc::PROT_READ,
MappingMode::Mutable => libc::PROT_READ | libc::PROT_WRITE,
};
let ptr = unsafe { libc::mmap(ptr::null_mut(), len, prot, libc::MAP_SHARED, file.as_raw_fd(), 0) };
if ptr.is_null() {
return Err(Error::new(ErrorKind::Other, "Memory mapping failed"));
}
let mut buf = PathBuf::new();
buf.push(&filename);
Ok(MemoryMap {
_file: file,
filename: buf,
mode,
ptr: ptr.cast::<u64>(),
len: bits::bytes_to_words(len),
})
}
pub fn filename(&self) -> &Path {
self.filename.as_path()
}
#[inline]
pub fn mode(&self) -> MappingMode {
self.mode
}
pub unsafe fn as_mut_slice(&mut self) -> &mut [u64] {
slice::from_raw_parts_mut(self.ptr, self.len)
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
}
#[cfg(not(target_family = "wasm"))]
impl AsRef<[u64]> for MemoryMap {
fn as_ref(&self) -> &[u64] {
unsafe { slice::from_raw_parts(self.ptr, self.len) }
}
}
#[cfg(not(target_family = "wasm"))]
impl Drop for MemoryMap {
fn drop(&mut self) {
unsafe {
let _ = libc::munmap(self.ptr.cast::<libc::c_void>(), self.len);
}
}
}
#[cfg(not(target_family = "wasm"))]
pub trait MemoryMapped<'a>: Sized {
fn new(map: &'a MemoryMap, offset: usize) -> io::Result<Self>;
fn map_offset(&self) -> usize;
fn map_len(&self) -> usize;
}
#[cfg(not(target_family = "wasm"))]
#[derive(PartialEq, Eq, Debug)]
pub struct MappedSlice<'a, T: Serializable> {
data: &'a [T],
offset: usize,
}
#[cfg(not(target_family = "wasm"))]
impl<'a, T: Serializable> MappedSlice<'a, T> {
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
#[cfg(not(target_family = "wasm"))]
impl<'a, T: Serializable> AsRef<[T]> for MappedSlice<'a, T> {
fn as_ref(&self) -> &[T] {
self.data
}
}
#[cfg(not(target_family = "wasm"))]
impl<'a, T: Serializable> Deref for MappedSlice<'a, T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.data
}
}
#[cfg(not(target_family = "wasm"))]
impl<'a, T: Serializable> Index<usize> for MappedSlice<'a, T> {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
&self.data[index]
}
}
#[cfg(not(target_family = "wasm"))]
impl<'a, T: Serializable> MemoryMapped<'a> for MappedSlice<'a, T> {
fn new(map: &'a MemoryMap, offset: usize) -> io::Result<Self> {
if offset >= map.len() {
return Err(Error::new(ErrorKind::UnexpectedEof, "The starting offset is out of range"));
}
let slice: &[u64] = map.as_ref();
let len = slice[offset] as usize;
if offset + 1 + len * T::elements() > map.len() {
return Err(Error::new(ErrorKind::UnexpectedEof, "The file is too short"));
}
let source: &[u64] = &slice[offset + 1 ..];
let data: &[T] = unsafe { slice::from_raw_parts(source.as_ptr() as *const T, len) };
Ok(MappedSlice {
data, offset,
})
}
fn map_offset(&self) -> usize {
self.offset
}
fn map_len(&self) -> usize {
self.len() * T::elements() + 1
}
}
#[cfg(not(target_family = "wasm"))]
#[derive(PartialEq, Eq, Debug)]
pub struct MappedBytes<'a> {
data: &'a [u8],
offset: usize,
}
#[cfg(not(target_family = "wasm"))]
impl<'a> MappedBytes<'a> {
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
#[cfg(not(target_family = "wasm"))]
impl<'a> AsRef<[u8]> for MappedBytes<'a> {
fn as_ref(&self) -> &[u8] {
self.data
}
}
#[cfg(not(target_family = "wasm"))]
impl<'a> Deref for MappedBytes<'a> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.data
}
}
#[cfg(not(target_family = "wasm"))]
impl<'a> Index<usize> for MappedBytes<'a> {
type Output = u8;
fn index(&self, index: usize) -> &Self::Output {
&self.data[index]
}
}
#[cfg(not(target_family = "wasm"))]
impl<'a> MemoryMapped<'a> for MappedBytes<'a> {
fn new(map: &'a MemoryMap, offset: usize) -> io::Result<Self> {
if offset >= map.len() {
return Err(Error::new(ErrorKind::UnexpectedEof, "The starting offset is out of range"));
}
let slice: &[u64] = map.as_ref();
let len = slice[offset] as usize;
if offset + 1 + bits::bytes_to_words(len) > map.len() {
return Err(Error::new(ErrorKind::UnexpectedEof, "The file is too short"));
}
let source: &[u64] = &slice[offset + 1 ..];
let data: &[u8] = unsafe { slice::from_raw_parts(source.as_ptr() as *const u8, len) };
Ok(MappedBytes {
data, offset,
})
}
fn map_offset(&self) -> usize {
self.offset
}
fn map_len(&self) -> usize {
bits::bytes_to_words(self.len()) + 1
}
}
#[cfg(not(target_family = "wasm"))]
#[derive(PartialEq, Eq, Debug)]
pub struct MappedStr<'a> {
data: &'a str,
offset: usize,
}
#[cfg(not(target_family = "wasm"))]
impl<'a> MappedStr<'a> {
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
#[cfg(not(target_family = "wasm"))]
impl<'a> AsRef<str> for MappedStr<'a> {
fn as_ref(&self) -> &str {
self.data
}
}
#[cfg(not(target_family = "wasm"))]
impl<'a> Deref for MappedStr<'a> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.data
}
}
#[cfg(not(target_family = "wasm"))]
impl<'a> MemoryMapped<'a> for MappedStr<'a> {
fn new(map: &'a MemoryMap, offset: usize) -> io::Result<Self> {
if offset >= map.len() {
return Err(Error::new(ErrorKind::UnexpectedEof, "The starting offset is out of range"));
}
let slice: &[u64] = map.as_ref();
let len = slice[offset] as usize;
if offset + 1 + bits::bytes_to_words(len) > map.len() {
return Err(Error::new(ErrorKind::UnexpectedEof, "The file is too short"));
}
let source: &[u64] = &slice[offset + 1 ..];
let bytes: &[u8] = unsafe { slice::from_raw_parts(source.as_ptr() as *const u8, len) };
let data = str::from_utf8(bytes).map_err(|_| Error::new(ErrorKind::InvalidData, "Invalid UTF-8"))?;
Ok(MappedStr {
data, offset,
})
}
fn map_offset(&self) -> usize {
self.offset
}
fn map_len(&self) -> usize {
bits::bytes_to_words(self.len()) + 1
}
}
#[cfg(not(target_family = "wasm"))]
#[derive(PartialEq, Eq, Debug)]
pub struct MappedOption<'a, T: MemoryMapped<'a>> {
data: Option<T>,
offset: usize,
data_len: usize,
_marker: marker::PhantomData<&'a ()>,
}
#[cfg(not(target_family = "wasm"))]
impl<'a, T: MemoryMapped<'a>> MappedOption<'a, T> {
pub fn is_some(&self) -> bool {
self.data.is_some()
}
pub fn is_none(&self) -> bool {
self.data.is_none()
}
pub fn unwrap(&self) -> &T {
match &self.data {
Some(value) => value,
None => panic!("MappedOption::unwrap(): No value to unwrap"),
}
}
pub fn as_ref(&self) -> Option<&T> {
match &self.data {
Some(value) => Some(value),
None => None,
}
}
}
#[cfg(not(target_family = "wasm"))]
impl<'a, T: MemoryMapped<'a>> MemoryMapped<'a> for MappedOption<'a, T> {
fn new(map: &'a MemoryMap, offset: usize) -> io::Result<Self> {
if offset >= map.len() {
return Err(Error::new(ErrorKind::UnexpectedEof, "The starting offset is out of range"));
}
let mut result = MappedOption {
data: None,
offset,
data_len: map.as_ref()[offset] as usize,
_marker: marker::PhantomData,
};
if result.data_len > 0 {
let value = T::new(map, offset + 1)?;
result.data = Some(value)
}
Ok(result)
}
fn map_offset(&self) -> usize {
self.offset
}
fn map_len(&self) -> usize {
self.data_len + 1
}
}
pub fn serialize_to<T: Serialize, P: AsRef<Path>>(item: &T, filename: P) -> io::Result<()> {
let mut options = OpenOptions::new();
let mut file = options.create(true).write(true).truncate(true).open(filename)?;
item.serialize(&mut file)?;
Ok(())
}
pub fn load_from<T: Serialize, P: AsRef<Path>>(filename: P) -> io::Result<T> {
let mut options = OpenOptions::new();
let mut file = options.read(true).open(filename)?;
<T as Serialize>::load(&mut file)
}
pub fn absent_option<T: Write>(writer: &mut T) -> io::Result<()> {
let size: usize = 0;
size.serialize(writer)?;
Ok(())
}
pub fn skip_option<T: Read>(reader: &mut T) -> io::Result<()> {
let elements = usize::load(reader)?;
if elements > 0 {
io::copy(&mut reader.by_ref().take((elements * bits::WORD_BYTES) as u64), &mut io::sink())?;
}
Ok(())
}
pub fn absent_option_size() -> usize {
1
}
static TEMP_FILE_COUNTER: AtomicUsize = AtomicUsize::new(0);
pub fn temp_file_name(name_part: &str) -> PathBuf {
let count = TEMP_FILE_COUNTER.fetch_add(1, Ordering::SeqCst);
let mut buf = env::temp_dir();
buf.push(format!("{}_{}_{}", name_part, process::id(), count));
buf
}
pub fn test<T: Serialize + PartialEq + Debug>(original: &T, name: &str, expected_size: Option<usize>, remove: bool) -> Option<PathBuf> {
if let Some(value) = expected_size {
assert_eq!(original.size_in_elements(), value, "Size estimate for the serialized {} is not as expected", name);
}
let filename = temp_file_name(name);
serialize_to(original, &filename).unwrap();
let metadata = fs::metadata(&filename).unwrap();
let len = metadata.len() as usize;
assert_eq!(original.size_in_bytes(), len, "Invalid size estimate for the serialized {}", name);
let copy: T = load_from(&filename).unwrap();
assert_eq!(copy, *original, "Serialization changed the {}", name);
if remove {
fs::remove_file(&filename).unwrap();
None
} else {
Some(filename)
}
}