| // 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() |
| } |
| } |