chromium/mojo/public/rust/system/message_pipe.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::vec;

use std::convert::TryInto;

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

use ffi::c_void;

use super::UntypedHandle;

bitflags::bitflags! {
    #[derive(Clone, Copy, Default)]
    pub struct CreateMessageFlags: u32 {
            /// Do not enforce size restrictions on this message, allowing its serialized
    /// payload to grow arbitrarily large. If this flag is NOT specified, Mojo will
    /// throw an assertion failure at serialization time when the message exceeds a
    /// globally configured maximum size.
    const UNLIMITED_SIZE = 1 << 0;
    }
}

bitflags::bitflags! {
    #[derive(Clone, Copy, Default)]
    pub struct AppendMessageFlags: u32 {
    /// If set, this comments the resulting (post-append) message size as the final
    /// size of the message payload, in terms of both bytes and attached handles.
    const COMMIT_SIZE = 1 << 0;
    }
}

bitflags::bitflags! {
    #[derive(Clone, Copy, Default)]
    pub struct ReadMessageFlags: u32 {
            /// Ignores attached handles when retrieving message data. This leaves any
    /// attached handles intact and owned by the message object.
        const IGNORE_HANDLES = 1 << 0;
    }
}

/// Creates a message pipe in Mojo and gives back two
/// MessageEndpoints which represent the endpoints of the
/// message pipe
pub fn create() -> Result<(MessageEndpoint, MessageEndpoint), MojoResult> {
    let mut handle0 = UntypedHandle::invalid();
    let mut handle1 = UntypedHandle::invalid();
    let opts = ffi::MojoCreateMessagePipeOptions::new(0);
    match MojoResult::from_code(unsafe {
        ffi::MojoCreateMessagePipe(opts.inner_ptr(), handle0.as_mut_ptr(), handle1.as_mut_ptr())
    }) {
        MojoResult::Okay => {
            Ok((MessageEndpoint { handle: handle0 }, MessageEndpoint { handle: handle1 }))
        }
        e => Err(e),
    }
}

/// Represents the one endpoint of a message pipe.
/// This data structure wraps a handle and acts
/// effectively as a typed handle.
pub struct MessageEndpoint {
    handle: handle::UntypedHandle,
}

impl MessageEndpoint {
    /// Read the next message from the endpoint. Messages in Mojo
    /// are some set of bytes plus a bunch of handles, so we
    /// return both a vector of bytes and a vector of untyped handles.
    ///
    /// Because the handles are untyped, it is up to the user of this
    /// library to know what type the handle actually is and to use
    /// from_untyped in order to convert the handle to the correct type.
    /// This is abstracted away, however, when using the Mojo bindings
    /// generator where you may specify your interface in Mojom.
    ///
    /// If an empty message (that is, it has neither data nor handles)
    /// is received, it will show up as an Err() containing MojoResult::Okay.
    pub fn read(&self) -> Result<(vec::Vec<u8>, vec::Vec<handle::UntypedHandle>), MojoResult> {
        // Read the message, yielding a message object we can copy data from.
        let message_handle = {
            let mut h = 0;
            match MojoResult::from_code(unsafe {
                ffi::MojoReadMessage(
                    self.handle.get_native_handle(),
                    ptr::null(),
                    &mut h as *mut ffi::types::MojoMessageHandle,
                )
            }) {
                MojoResult::Okay => h,
                e => return Err(e),
            }
        };

        let mut buffer: *mut c_void = ptr::null_mut();
        let mut num_bytes: u32 = 0;
        let mut num_handles: u32 = 0;
        let result_prelim = MojoResult::from_code(unsafe {
            ffi::MojoGetMessageData(
                message_handle,
                ptr::null(),
                &mut buffer as *mut _,
                &mut num_bytes as *mut _,
                ptr::null_mut(),
                &mut num_handles as *mut _,
            )
        });
        if result_prelim != MojoResult::Okay && result_prelim != MojoResult::ResourceExhausted {
            return Err(result_prelim);
        }

        let mut handles: vec::Vec<UntypedHandle> = vec::Vec::with_capacity(num_handles as usize);
        if num_handles > 0 {
            let result = MojoResult::from_code(unsafe {
                ffi::MojoGetMessageData(
                    message_handle,
                    ptr::null(),
                    &mut buffer as *mut _,
                    &mut num_bytes as *mut _,
                    UntypedHandle::slice_as_mut_ptr(&mut handles),
                    &mut num_handles as *mut _,
                )
            });
            if result != MojoResult::Okay {
                return Err(result);
            }
        }

        let data: Vec<u8> = if num_bytes > 0 {
            assert_ne!(buffer, ptr::null_mut());
            // Will not panic if usize has at least 32 bits, which is true for our targets
            let buffer_size: usize = num_bytes.try_into().unwrap();
            // MojoGetMessageData points us to the data with a c_void pointer and a length.
            // This is only available until we destroy the message. We want to
            // copy this into our own Vec. Read the buffer as a slice, which is
            // safe.
            unsafe {
                let buffer_slice = std::slice::from_raw_parts(buffer.cast(), buffer_size);
                buffer_slice.to_vec()
            }
        } else {
            Vec::new()
        };

        unsafe {
            ffi::MojoDestroyMessage(message_handle);
        }

        Ok((data, handles))
    }

    /// Write a message to the endpoint. Messages in Mojo
    /// are some set of bytes plus a bunch of handles, so we
    /// return both a vector of bytes and a vector of untyped handles.
    ///
    /// Because the handles are untyped, it is up to the user of this
    /// library to know what type the handle actually is and to use
    /// from_untyped in order to convert the handle to the correct type.
    /// This is abstracted away, however, when using the Mojo bindings
    /// generator where you may specify your interface in Mojom.
    ///
    /// Additionally, the handles passed in are consumed. This is because
    /// Mojo handles operate on move semantics much like Rust data types.
    /// When a handle is sent through a message pipe it is invalidated and
    /// may not even be represented by the same integer on the other side,
    /// so care must be taken to design your application with this in mind.
    pub fn write(&self, bytes: &[u8], handles: vec::Vec<handle::UntypedHandle>) -> MojoResult {
        // Create the message object we will write data into then send.
        let message_handle = unsafe {
            let mut h = 0;
            let result_code = ffi::MojoCreateMessage(std::ptr::null(), &mut h as *mut _);
            assert_eq!(MojoResult::Okay, MojoResult::from_code(result_code));
            h
        };

        // "Append" to the message, getting a buffer to copy our data to.
        let raw_handles_ptr: *const MojoHandle =
            if handles.len() == 0 { ptr::null() } else { UntypedHandle::slice_as_ptr(&handles) };

        let mut buffer_ptr: *mut c_void = std::ptr::null_mut();
        let mut buffer_size: u32 = 0;

        let append_message_options =
            ffi::MojoAppendMessageDataOptions::new(AppendMessageFlags::COMMIT_SIZE.bits());

        let result = MojoResult::from_code(unsafe {
            ffi::MojoAppendMessageData(
                message_handle,
                bytes.len() as u32,
                raw_handles_ptr,
                handles.len() as u32,
                append_message_options.inner_ptr(),
                &mut buffer_ptr as *mut _,
                &mut buffer_size as *mut _,
            )
        });

        if result != MojoResult::Okay {
            return result;
        }

        // Copy into the message storage
        if bytes.len() > 0 {
            // Will not panic if usize has at least 32 bits, which is true for our targets
            let buffer_size: usize = buffer_size.try_into().unwrap();
            assert!(bytes.len() <= buffer_size);
            assert_ne!(buffer_ptr, ptr::null_mut());
            // MojoAppendMessageData tells us where to write with a c_void pointer and a
            // length. This is only available until we destroy or send the
            // message. We can view this through a slice and copy our `bytes`
            // into it.
            unsafe {
                // We know `bytes.len() <= buffer_size`, and `buffer_size` is the limit of the
                // provided buffer.
                let buffer_slice = std::slice::from_raw_parts_mut(buffer_ptr.cast(), bytes.len());
                buffer_slice.copy_from_slice(bytes);
            }
        }

        // Send the message. This takes ownership of the message object.
        let write_message_options = ffi::MojoWriteMessageOptions::new(0);
        return MojoResult::from_code(unsafe {
            ffi::MojoWriteMessage(
                self.handle.get_native_handle(),
                message_handle,
                write_message_options.inner_ptr(),
            )
        });
    }
}

impl CastHandle for MessageEndpoint {
    /// Generates a MessageEndpoint from an untyped handle wrapper
    /// See crate::handle for information on untyped vs. typed
    unsafe fn from_untyped(handle: handle::UntypedHandle) -> Self {
        MessageEndpoint { 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 MessageEndpoint {
    /// 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()
    }
}