use crate::map::{Kind, Private};
use super::map::Type;
use super::{Error, Map};
use std::io::ErrorKind;
use std::marker::PhantomData;
use std::mem::forget;
use std::os::unix::io::{AsRawFd, RawFd};
pub trait Stage {}
pub enum Address {
None,
At(usize),
Near(usize),
Onto(usize),
}
pub struct Size<M> {
pub(crate) prev: M,
pub(crate) size: usize,
}
pub struct Destination<M> {
pub(crate) prev: Size<M>,
pub(crate) addr: Address,
}
pub struct Source<M, K: Kind> {
prev: Destination<M>,
fd: RawFd,
offset: libc::off_t,
huge: Option<i32>,
kind: K,
}
impl<M> Stage for Size<M> {}
impl<M> Stage for Destination<M> {}
impl<M, K: Kind> Stage for Source<M, K> {}
pub struct Builder<S: Stage>(pub(crate) S);
impl<M> Builder<Size<M>> {
#[inline]
pub fn anywhere(self) -> Builder<Destination<M>> {
Builder(Destination {
prev: self.0,
addr: Address::None,
})
}
#[inline]
pub fn at(self, addr: usize) -> Builder<Destination<M>> {
Builder(Destination {
prev: self.0,
addr: Address::At(addr),
})
}
#[inline]
pub fn near(self, addr: usize) -> Builder<Destination<M>> {
Builder(Destination {
prev: self.0,
addr: Address::Near(addr),
})
}
#[inline]
pub unsafe fn onto(self, addr: usize) -> Builder<Destination<M>> {
Builder(Destination {
prev: self.0,
addr: Address::Onto(addr),
})
}
}
impl<M> Builder<Destination<M>> {
#[inline]
pub fn anonymously(self) -> Builder<Source<M, Private>> {
Builder(Source {
kind: Private,
prev: self.0,
huge: None,
offset: 0,
fd: -1,
})
}
#[inline]
pub fn from<U: AsRawFd>(self, file: &mut U, offset: i64) -> Builder<Source<M, Private>> {
Builder(Source {
fd: file.as_raw_fd(),
kind: Private,
prev: self.0,
huge: None,
offset,
})
}
}
impl<M, K: Kind> Builder<Source<M, K>> {
#[inline]
pub fn with_huge_pages(mut self, pow: u8) -> Self {
self.0.huge = Some(pow.into());
self
}
#[inline]
pub fn with_kind<X: Kind>(self, kind: X) -> Builder<Source<M, X>> {
Builder(Source {
offset: self.0.offset,
prev: self.0.prev,
huge: self.0.huge,
fd: self.0.fd,
kind,
})
}
#[inline]
pub fn with<T: Type>(self, perms: T) -> Result<Map<T, K>, Error<M>> {
let einval = ErrorKind::InvalidInput.into();
let perms = perms.perms();
let kind = self.0.kind.kind();
let huge = match self.0.huge {
Some(x) if x & !libc::MAP_HUGE_MASK != 0 => {
return Err(Error {
map: self.0.prev.prev.prev,
err: einval,
})
}
Some(x) => (x << libc::MAP_HUGE_SHIFT) | libc::MAP_HUGETLB,
None => 0,
};
let (addr, fixed) = match self.0.prev.addr {
Address::None => (0, 0),
Address::At(a) if a != 0 => (a, libc::MAP_FIXED_NOREPLACE),
Address::Near(a) if a != 0 => (a, 0),
Address::Onto(a) if a != 0 => (a, libc::MAP_FIXED),
_ => {
return Err(Error {
map: self.0.prev.prev.prev,
err: einval,
})
}
};
let anon = match self.0.fd {
-1 => libc::MAP_ANONYMOUS,
_ => 0,
};
let size = self.0.prev.prev.size;
let flags = kind | fixed | anon | huge;
let ret = unsafe { libc::mmap(addr as _, size, perms, flags, self.0.fd, self.0.offset) };
if ret == libc::MAP_FAILED {
return Err(Error {
map: self.0.prev.prev.prev,
err: std::io::Error::last_os_error(),
});
}
forget(self.0.prev.prev.prev);
Ok(Map {
addr: ret as usize,
size: self.0.prev.prev.size,
data: PhantomData,
})
}
}