use {Cookied, HandleBase, Handle, HandleRef, Status};
use {sys, into_result};
use std::{mem, ptr};
#[derive(Debug, Eq, PartialEq)]
pub struct Vmo(Handle);
impl HandleBase for Vmo {
fn get_ref(&self) -> HandleRef {
self.0.get_ref()
}
fn from_handle(handle: Handle) -> Self {
Vmo(handle)
}
}
impl Cookied for Vmo {
}
impl Vmo {
pub fn create(size: u64, options: VmoOpts) -> Result<Vmo, Status> {
let mut handle = 0;
let status = unsafe { sys::mx_vmo_create(size, options as u32, &mut handle) };
into_result(status, ||
Vmo::from_handle(Handle(handle)))
}
pub fn read(&self, data: &mut [u8], offset: u64) -> Result<usize, Status> {
unsafe {
let mut actual = 0;
let status = sys::mx_vmo_read(self.raw_handle(), data.as_mut_ptr(),
offset, data.len(), &mut actual);
into_result(status, || actual)
}
}
pub fn write(&self, data: &[u8], offset: u64) -> Result<usize, Status> {
unsafe {
let mut actual = 0;
let status = sys::mx_vmo_write(self.raw_handle(), data.as_ptr(),
offset, data.len(), &mut actual);
into_result(status, || actual)
}
}
pub fn get_size(&self) -> Result<u64, Status> {
let mut size = 0;
let status = unsafe { sys::mx_vmo_get_size(self.raw_handle(), &mut size) };
into_result(status, || size)
}
pub fn set_size(&self, size: u64) -> Result<(), Status> {
let status = unsafe { sys::mx_vmo_set_size(self.raw_handle(), size) };
into_result(status, || ())
}
pub fn op_range(&self, op: VmoOp, offset: u64, size: u64) -> Result<(), Status> {
let status = unsafe {
sys::mx_vmo_op_range(self.raw_handle(), op as u32, offset, size, ptr::null_mut(), 0)
};
into_result(status, || ())
}
pub fn lookup(&self, offset: u64, size: u64, buffer: &mut [sys::mx_paddr_t])
-> Result<(), Status>
{
let status = unsafe {
sys::mx_vmo_op_range(self.raw_handle(), sys::MX_VMO_OP_LOOKUP, offset, size,
buffer.as_mut_ptr() as *mut u8, buffer.len() * mem::size_of::<sys::mx_paddr_t>())
};
into_result(status, || ())
}
pub fn clone(&self, options: VmoCloneOpts, offset: u64, size: u64) -> Result<Vmo, Status> {
let mut out = 0;
let status = unsafe {
sys::mx_vmo_clone(self.raw_handle(), options as u32, offset, size, &mut out)
};
into_result(status, || Vmo::from_handle(Handle(out)))
}
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum VmoOpts {
Default = 0,
}
impl Default for VmoOpts {
fn default() -> Self {
VmoOpts::Default
}
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum VmoOp {
Commit = sys::MX_VMO_OP_COMMIT,
Decommit = sys::MX_VMO_OP_DECOMMIT,
Lock = sys::MX_VMO_OP_LOCK,
Unlock = sys::MX_VMO_OP_UNLOCK,
CacheSync = sys::MX_VMO_OP_CACHE_SYNC,
CacheInvalidate = sys::MX_VMO_OP_CACHE_INVALIDATE,
CacheClean = sys::MX_VMO_OP_CACHE_CLEAN,
CacheCleanInvalidate = sys::MX_VMO_OP_CACHE_CLEAN_INVALIDATE,
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum VmoCloneOpts {
CopyOnWrite = sys::MX_VMO_CLONE_COPY_ON_WRITE,
}
impl Default for VmoCloneOpts {
fn default() -> Self {
VmoCloneOpts::CopyOnWrite
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn vmo_get_size() {
let size = 16 * 1024 * 1024;
let vmo = Vmo::create(size, VmoOpts::Default).unwrap();
assert_eq!(size, vmo.get_size().unwrap());
}
#[test]
fn vmo_set_size() {
let start_size = 12;
let vmo = Vmo::create(start_size, VmoOpts::Default).unwrap();
assert_eq!(start_size, vmo.get_size().unwrap());
let new_size = 23;
assert!(vmo.set_size(new_size).is_ok());
assert_eq!(new_size, vmo.get_size().unwrap());
}
#[test]
fn vmo_read_write() {
let mut vec1 = vec![0; 16];
let vmo = Vmo::create(vec1.len() as u64, VmoOpts::Default).unwrap();
assert_eq!(vmo.write(b"abcdef", 0), Ok(6));
assert_eq!(16, vmo.read(&mut vec1, 0).unwrap());
assert_eq!(b"abcdef", &vec1[0..6]);
assert_eq!(vmo.write(b"123", 2), Ok(3));
assert_eq!(16, vmo.read(&mut vec1, 0).unwrap());
assert_eq!(b"ab123f", &vec1[0..6]);
assert_eq!(15, vmo.read(&mut vec1, 1).unwrap());
assert_eq!(b"b123f", &vec1[0..5]);
}
#[test]
fn vmo_op_range_unsupported() {
let vmo = Vmo::create(12, VmoOpts::Default).unwrap();
assert_eq!(vmo.op_range(VmoOp::Lock, 0, 1), Err(Status::ErrNotSupported));
assert_eq!(vmo.op_range(VmoOp::Unlock, 0, 1), Err(Status::ErrNotSupported));
}
#[test]
fn vmo_lookup() {
let vmo = Vmo::create(12, VmoOpts::Default).unwrap();
let mut buffer = vec![0; 2];
assert_eq!(vmo.lookup(0, 12, &mut buffer), Err(Status::ErrNoMemory));
assert_eq!(vmo.op_range(VmoOp::Commit, 0, 12), Ok(()));
assert_eq!(vmo.lookup(0, 12, &mut buffer), Ok(()));
assert_ne!(buffer[0], 0);
assert_eq!(buffer[1], 0);
assert_eq!(vmo.op_range(VmoOp::Decommit, 0, 12), Ok(()));
assert_eq!(vmo.lookup(0, 12, &mut buffer), Err(Status::ErrNoMemory));
}
#[test]
fn vmo_cache() {
let vmo = Vmo::create(12, VmoOpts::Default).unwrap();
assert_eq!(vmo.op_range(VmoOp::CacheSync, 0, 12), Ok(()));
assert_eq!(vmo.op_range(VmoOp::CacheInvalidate, 0, 12), Ok(()));
assert_eq!(vmo.op_range(VmoOp::CacheClean, 0, 12), Ok(()));
assert_eq!(vmo.op_range(VmoOp::CacheCleanInvalidate, 0, 12), Ok(()));
}
#[test]
fn vmo_clone() {
let original = Vmo::create(12, VmoOpts::Default).unwrap();
assert_eq!(original.write(b"one", 0), Ok(3));
let clone = original.clone(VmoCloneOpts::CopyOnWrite, 0, 10).unwrap();
let mut read_buffer = vec![0; 16];
assert_eq!(clone.read(&mut read_buffer, 0), Ok(10));
assert_eq!(&read_buffer[0..3], b"one");
assert_eq!(original.write(b"two", 0), Ok(3));
assert_eq!(original.read(&mut read_buffer, 0), Ok(12));
assert_eq!(&read_buffer[0..3], b"two");
assert_eq!(clone.read(&mut read_buffer, 0), Ok(10));
assert_eq!(&read_buffer[0..3], b"two");
assert_eq!(clone.write(b"three", 0), Ok(5));
assert_eq!(original.read(&mut read_buffer, 0), Ok(12));
assert_eq!(&read_buffer[0..3], b"two");
assert_eq!(clone.read(&mut read_buffer, 0), Ok(10));
assert_eq!(&read_buffer[0..5], b"three");
assert_eq!(original.write(b"four", 0), Ok(4));
assert_eq!(original.read(&mut read_buffer, 0), Ok(12));
assert_eq!(&read_buffer[0..4], b"four");
assert_eq!(clone.read(&mut read_buffer, 0), Ok(10));
assert_eq!(&read_buffer[0..5], b"three");
}
}