chromium/mojo/public/rust/system/shared_buffer.rs

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::ptr;
use std::slice;

use crate::ffi;
// This full import is intentional; nearly every type in mojo_types needs to be
// used.
use crate::handle;
use crate::handle::{CastHandle, Handle};
use crate::mojo_types::*;

use super::UntypedHandle;

bitflags::bitflags! {
    #[derive(Clone, Copy, Default)]
    pub struct DuplicateFlags: u32 {
        /// The resulting duplicate shared buffer handle will map to a read only
        /// memory region. If a buffer is ever duplicated without this flag, no
        /// further duplicate calls can use this flag. Likewise, if a buffer is
        /// duplicated with this flag, all further duplicates must be read-only.
        const READ_ONLY = 1;
    }
}

/// A MappedBuffer represents the result of
/// calling map_buffer on a shared buffer handle.
///
/// The C API allocates a buffer which can then be
/// read or written to through this data structure.
///
/// The importance of this data structure is that
/// we bind the lifetime of the slice given to us
/// from the C API to this structure. Additionally,
/// reads and writes to this data structure are guaranteed
/// to be able to propagate through Mojo as they are
/// volatile. Language optimization backends are generally
/// unaware of other address spaces, and since this structure
/// represents some buffer from another address space, we
/// need to make sure loads and stores are volatile.
///
/// Changes to this data structure are propagated through Mojo
/// on the next Mojo operation (that is, Mojo operations are
/// considered barriers). So, unmapping the buffer, sending a
/// message across a pipe, duplicating a shared buffer handle,
/// etc. are all valid ways of propagating changes. The read
/// and write methods do NOT guarantee changes to propagate.
///
/// This structure also prevents resource leaks by
/// unmapping the buffer it contains on destruction.
pub struct MappedBuffer<'a> {
    buffer: &'a mut [u8],
}

impl<'a> MappedBuffer<'a> {
    /// Returns the length of the wrapped buffer.
    ///
    /// Part of reimplementing the array interface to be
    /// able to use the structure naturally.
    pub fn len(&self) -> usize {
        self.buffer.len()
    }

    /// Read safely from the shared buffer. Makes sure a real load
    /// is performed by marking the read as volatile.
    pub fn read(&self, index: usize) -> u8 {
        unsafe { ptr::read_volatile((&self.buffer[index]) as *const u8) }
    }

    /// Write safely to the shared buffer. Makes sure a real store
    /// is performed by marking the store as volatile.
    pub fn write(&mut self, index: usize, value: u8) {
        unsafe {
            ptr::write_volatile((&mut self.buffer[index]) as *mut u8, value);
        }
    }

    /// Returns the slice this buffer wraps.
    ///
    /// The reason this method is unsafe is because the way Rust maps
    /// reads and writes down to loads and stores may not be to real
    /// loads and stores which are required to allow changes to propagate
    /// through Mojo. If you are not careful, some writes and reads may be
    /// to incorrect data! Use at your own risk.
    pub unsafe fn as_slice(&'a mut self) -> &'a mut [u8] {
        self.buffer
    }
}

impl<'a> Drop for MappedBuffer<'a> {
    /// The destructor for MappedBuffer. Unmaps the buffer it
    /// encloses by using the original, raw pointer to the mapped
    /// memory region.
    fn drop(&mut self) {
        let r = MojoResult::from_code(unsafe {
            ffi::MojoUnmapBuffer(self.buffer.as_mut_ptr() as *mut ffi::c_void)
        });
        assert_eq!(r, MojoResult::Okay, "failed to unmap buffer");
    }
}

/// Represents a handle to a shared buffer in Mojo.
/// This data structure wraps a handle and acts
/// effectively as a typed handle.
pub struct SharedBuffer {
    handle: handle::UntypedHandle,
}

impl SharedBuffer {
    /// Creates a shared buffer in Mojo and returns a SharedBuffer
    /// structure which represents a handle to the shared buffer.
    pub fn new(num_bytes: u64) -> Result<SharedBuffer, MojoResult> {
        let opts = ffi::MojoCreateSharedBufferOptions::new(0);
        let mut handle = UntypedHandle::invalid();
        match MojoResult::from_code(unsafe {
            ffi::MojoCreateSharedBuffer(num_bytes, opts.inner_ptr(), handle.as_mut_ptr())
        }) {
            MojoResult::Okay => Ok(SharedBuffer { handle }),
            e => Err(e),
        }
    }

    /// Duplicates the shared buffer handle. This is NOT the same
    /// as cloning the structure which is illegal since cloning could
    /// lead to resource leaks. Instead this uses Mojo to duplicate the
    /// buffer handle (though the handle itself may not be represented by
    /// the same number) that maps to the same shared buffer as the original.
    pub fn duplicate(&self, flags: DuplicateFlags) -> Result<SharedBuffer, MojoResult> {
        let opts = ffi::MojoDuplicateBufferHandleOptions::new(flags.bits());
        let mut dup_h = UntypedHandle::invalid();
        match MojoResult::from_code(unsafe {
            ffi::MojoDuplicateBufferHandle(
                self.handle.get_native_handle(),
                opts.inner_ptr(),
                dup_h.as_mut_ptr(),
            )
        }) {
            MojoResult::Okay => Ok(SharedBuffer { handle: dup_h }),
            e => Err(e),
        }
    }

    /// Map the shared buffer into local memory. Generates a MappedBuffer
    /// structure. See MappedBuffer for more information on how to use it.
    pub fn map<'a>(&self, offset: u64, num_bytes: u64) -> Result<MappedBuffer<'a>, MojoResult> {
        let options = ffi::MojoMapBufferOptions::new(0);
        let mut ptr: *mut ffi::c_void = ptr::null_mut();
        match unsafe {
            MojoResult::from_code(ffi::MojoMapBuffer(
                self.handle.get_native_handle(),
                offset,
                num_bytes,
                options.inner_ptr(),
                &mut ptr,
            ))
        } {
            MojoResult::Okay => {
                let buffer =
                    unsafe { slice::from_raw_parts_mut(ptr as *mut u8, num_bytes as usize) };
                Ok(MappedBuffer { buffer })
            }
            e => Err(e),
        }
    }

    /// Retrieves information about this shared buffer. The return value is just
    /// the size of the shared buffer.
    pub fn get_info(&self) -> Result<u64, MojoResult> {
        let mut info = ffi::MojoSharedBufferInfo::new(0);
        let r = MojoResult::from_code(unsafe {
            ffi::MojoGetBufferInfo(
                self.handle.get_native_handle(),
                ptr::null(),
                info.inner_mut_ptr(),
            )
        });
        if r != MojoResult::Okay { Err(r) } else { Ok(info.size) }
    }
}

impl CastHandle for SharedBuffer {
    /// Generates a SharedBuffer from an untyped handle wrapper
    /// See crate::handle for information on untyped vs. typed
    unsafe fn from_untyped(handle: handle::UntypedHandle) -> Self {
        SharedBuffer { handle: handle }
    }

    /// Consumes this object and produces a plain handle wrapper
    /// See crate::handle for information on untyped vs. typed
    fn as_untyped(self) -> handle::UntypedHandle {
        self.handle
    }
}

impl Handle for SharedBuffer {
    /// Returns the native handle wrapped by this structure.
    ///
    /// See crate::handle for information on handle wrappers
    fn get_native_handle(&self) -> MojoHandle {
        self.handle.get_native_handle()
    }
}