blob: 8401bb7ddbe3b94fa39acf702b9da000a08369e3 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use bindings::decoding::{Decoder, ValidationError};
use bindings::encoding;
use bindings::encoding::{Bits, Encoder, Context, DATA_HEADER_SIZE, DataHeader, DataHeaderValue};
use bindings::message::MessageHeader;
use std::cmp::Eq;
use std::collections::HashMap;
use std::hash::Hash;
use std::mem;
use std::panic;
use std::ptr;
use std::vec::Vec;
use system::{MojoResult, CastHandle, Handle, UntypedHandle};
use system::data_pipe;
use system::message_pipe;
use system::shared_buffer;
use system::wait_set;
/// The size of a Mojom map plus header in bytes.
const MAP_SIZE: usize = 24;
/// The sorted set of versions for a map.
const MAP_VERSIONS: [(u32, u32); 1] = [(0, MAP_SIZE as u32)];
/// The size of a Mojom union in bytes (header included).
pub const UNION_SIZE: usize = 16;
/// The size of a Mojom pointer in bits.
pub const POINTER_BIT_SIZE: Bits = Bits(64);
/// The value of a Mojom null pointer.
pub const MOJOM_NULL_POINTER: u64 = 0;
/// An enumeration of all the possible low-level Mojom types.
pub enum MojomType {
Simple,
Pointer,
Union,
Handle,
Interface,
}
/// Whatever implements this trait can be serialized in the Mojom format.
pub trait MojomEncodable: Sized {
/// Get the Mojom type.
fn mojom_type() -> MojomType;
/// Get this type's Mojom alignment.
fn mojom_alignment() -> usize;
/// The amount of space in bits the type takes up when inlined
/// into another type at serialization time.
fn embed_size(context: &Context) -> Bits;
/// Recursively computes the size of the complete Mojom archive
/// starting from this type.
fn compute_size(&self, context: Context) -> usize;
/// Encodes this type into the encoder given a context.
fn encode(self, encoder: &mut Encoder, context: Context);
/// Using a decoder, decodes itself out of a byte buffer.
fn decode(decoder: &mut Decoder, context: Context) -> Result<Self, ValidationError>;
}
/// Whatever implements this trait is a Mojom pointer type which means
/// that on encode, a pointer is inlined and the implementer is
/// serialized elsewhere in the output buffer.
pub trait MojomPointer: MojomEncodable {
/// Get the DataHeader meta-data for this pointer type.
fn header_data(&self) -> DataHeaderValue;
/// Get the size of only this type when serialized.
fn serialized_size(&self, context: &Context) -> usize;
/// Encodes the actual values of the type into the encoder.
fn encode_value(self, encoder: &mut Encoder, context: Context);
/// Decodes the actual values of the type into the decoder.
fn decode_value(decoder: &mut Decoder, context: Context) -> Result<Self, ValidationError>;
/// Writes a pointer inlined into the current context before calling
/// encode_value.
fn encode_new(self, encoder: &mut Encoder, context: Context) {
let data_size = self.serialized_size(&context);
let data_header = DataHeader::new(data_size, self.header_data());
let new_context = encoder.add(&data_header).unwrap();
self.encode_value(encoder, new_context);
}
/// Reads a pointer inlined into the current context before calling
/// decode_value.
fn decode_new(decoder: &mut Decoder, _context: Context, pointer: u64) -> Result<Self, ValidationError> {
match decoder.claim(pointer as usize) {
Ok(new_context) => Self::decode_value(decoder, new_context),
Err(err) => Err(err),
}
}
}
/// Whatever implements this trait is a Mojom union type which means that
/// on encode it is inlined, but if the union is nested inside of another
/// union type, it is treated as a pointer type.
pub trait MojomUnion: MojomEncodable {
/// Get the union's current tag.
fn get_tag(&self) -> u32;
/// Encode the actual value of the union.
fn encode_value(self, encoder: &mut Encoder, context: Context);
/// Decode the actual value of the union.
fn decode_value(decoder: &mut Decoder, context: Context) -> Result<Self, ValidationError>;
/// The embed_size for when the union acts as a pointer type.
fn nested_embed_size() -> Bits {
POINTER_BIT_SIZE
}
/// The encoding routine for when the union acts as a pointer type.
fn nested_encode(self, encoder: &mut Encoder, context: Context) {
let loc = encoder.size() as u64;
{
let state = encoder.get_mut(&context);
state.encode_pointer(loc);
}
let tag = DataHeaderValue::UnionTag(self.get_tag());
let data_header = DataHeader::new(UNION_SIZE, tag);
let new_context = encoder.add(&data_header).unwrap();
self.encode_value(encoder, new_context.set_is_union(true));
}
/// The decoding routine for when the union acts as a pointer type.
fn nested_decode(decoder: &mut Decoder, context: Context) -> Result<Self, ValidationError> {
let global_offset = {
let state = decoder.get_mut(&context);
match state.decode_pointer() {
Some(ptr) => ptr as usize,
None => return Err(ValidationError::IllegalPointer),
}
};
if global_offset == (MOJOM_NULL_POINTER as usize) {
return Err(ValidationError::UnexpectedNullPointer);
}
match decoder.claim(global_offset as usize) {
Ok(new_context) => Self::decode_value(decoder, new_context),
Err(err) => Err(err),
}
}
/// The embed_size for when the union is inlined into the current context.
fn inline_embed_size() -> Bits {
Bits(8 * (UNION_SIZE as usize))
}
/// The encoding routine for when the union is inlined into the current context.
fn inline_encode(self, encoder: &mut Encoder, context: Context) {
{
let mut state = encoder.get_mut(&context);
state.align_to_bytes(8);
state.encode(UNION_SIZE as u32);
state.encode(self.get_tag());
}
self.encode_value(encoder, context.clone());
{
let mut state = encoder.get_mut(&context);
state.align_to_bytes(8);
state.align_to_byte();
}
}
/// The decoding routine for when the union is inlined into the current context.
fn inline_decode(decoder: &mut Decoder, context: Context) -> Result<Self, ValidationError> {
{
let mut state = decoder.get_mut(&context);
state.align_to_byte();
state.align_to_bytes(8);
}
let value = Self::decode_value(decoder, context.clone());
{
let mut state = decoder.get_mut(&context);
state.align_to_byte();
state.align_to_bytes(8);
}
value
}
}
/// A marker trait that marks Mojo handles as encodable.
pub trait MojomHandle: CastHandle + MojomEncodable {}
/// Whatever implements this trait is considered to be a Mojom
/// interface, that is, a message pipe which conforms to some
/// messaging interface.
///
/// We force an underlying message pipe to be used via the pipe()
/// and unwrap() routines.
pub trait MojomInterface: MojomEncodable {
/// Get the service name for this interface.
fn service_name() -> &'static str;
/// Get the version for this interface.
fn version(&self) -> u32;
/// Access the underlying message pipe for this interface.
fn pipe(&self) -> &message_pipe::MessageEndpoint;
/// Unwrap the interface into its underlying message pipe.
fn unwrap(self) -> message_pipe::MessageEndpoint;
}
/// An error that may occur when sending data over a Mojom interface.
#[derive(Debug)]
pub enum MojomSendError {
/// Failed to write to the underlying message pipe.
FailedWrite(MojoResult),
/// The version is too old to write the attempted message.
OldVersion(u32, u32),
}
/// Whatever implements this trait is considered to be a Mojom
/// interface that may send messages of some generic type.
///
/// When implementing this trait, the correct way is to specify
/// a tighter trait bound than MojomMessage that limits the types
/// available for sending to those that are valid messages available
/// to the interface.
///
/// TODO(mknyszek): Add sending control messages
pub trait MojomInterfaceSend<R: MojomMessage>: MojomInterface {
/// Creates a message.
fn create_request(&self, req_id: u64, payload: R) -> (Vec<u8>, Vec<UntypedHandle>) {
let mut header = R::create_header();
header.request_id = req_id;
let header_size = header.compute_size(Default::default());
let size = header_size + payload.compute_size(Default::default());
let mut buffer: Vec<u8> = Vec::with_capacity(size);
buffer.resize(size, 0);
let handles = {
let (header_buf, rest_buf) = buffer.split_at_mut(header_size);
let mut handles = header.serialize(header_buf);
handles.extend(payload.serialize(rest_buf).into_iter());
handles
};
(buffer, handles)
}
/// Creates and sends a message, and returns its request ID.
fn send_request(&self, req_id: u64, payload: R) -> Result<(), MojomSendError> {
if self.version() < R::min_version() {
return Err(MojomSendError::OldVersion(self.version(), R::min_version()));
}
let (buffer, handles) = self.create_request(req_id, payload);
match self.pipe().write(&buffer, handles, mpflags!(Write::None)) {
MojoResult::Okay => Ok(()),
err => Err(MojomSendError::FailedWrite(err)),
}
}
}
/// An error that may occur when attempting to recieve a message over a
/// Mojom interface.
#[derive(Debug)]
pub enum MojomRecvError {
/// Failed to read from the underlying message pipe.
FailedRead(MojoResult),
/// Failed to validate the buffer during decode.
FailedValidation(ValidationError),
}
/// Whatever implements this trait is considered to be a Mojom
/// interface that may recieve messages for some interface.
///
/// When implementing this trait, specify the container "union" type
/// which can contain any of the potential messages that may be recieved.
/// This way, we can return that type and let the user multiplex over
/// what message was received.
///
/// TODO(mknyszek): Add responding to control messages
pub trait MojomInterfaceRecv: MojomInterface {
type Container: MojomMessageOption;
/// Tries to read a message from a pipe and decodes it.
fn recv_response(&self) -> Result<(u64, Self::Container), MojomRecvError> {
match self.pipe().read(mpflags!(Read::None)) {
Ok((buffer, handles)) => {
match Self::Container::decode_message(buffer, handles) {
Ok((req_id, val)) => Ok((req_id, val)),
Err(err) => Err(MojomRecvError::FailedValidation(err)),
}
},
Err(err) => Err(MojomRecvError::FailedRead(err)),
}
}
}
/// Whatever implements this trait is considered to be a Mojom struct.
///
/// Mojom structs are always the root of any Mojom message. Thus, we
/// provide convenience functions for serialization here.
pub trait MojomStruct: MojomPointer {
/// Given a pre-allocated buffer, the struct serializes itself.
fn serialize(self, buffer: &mut [u8]) -> Vec<UntypedHandle> {
let mut encoder = Encoder::new(buffer);
self.encode_new(&mut encoder, Default::default());
encoder.unwrap()
}
/// The struct computes its own size, allocates a buffer, and then
/// serializes itself into that buffer.
fn auto_serialize(self) -> (Vec<u8>, Vec<UntypedHandle>) {
let size = self.compute_size(Default::default());
let mut buf = Vec::with_capacity(size);
buf.resize(size, 0);
let handles = self.serialize(&mut buf);
(buf, handles)
}
/// Decode the type from a byte array and a set of handles.
fn deserialize(buffer: &[u8], handles: Vec<UntypedHandle>) -> Result<Self, ValidationError> {
let mut decoder = Decoder::new(buffer, handles);
Self::decode_new(&mut decoder, Default::default(), 0)
}
}
/// Marks a MojomStruct as being capable of being sent across some
/// Mojom interface.
pub trait MojomMessage: MojomStruct {
fn min_version() -> u32;
fn create_header() -> MessageHeader;
}
/// The trait for a "container" type intended to be used in MojomInterfaceRecv.
///
/// This trait contains the decode logic which decodes based on the message header
/// and returns itself: a union type which may contain any of the possible messages
/// that may be sent across this interface.
pub trait MojomMessageOption: Sized {
/// Decodes the actual payload of the message.
///
/// Implemented by a code generator.
fn decode_payload(header: MessageHeader, buffer: &[u8], handles: Vec<UntypedHandle>) -> Result<Self, ValidationError>;
/// Decodes the message header and then the payload, returning a new
/// copy of itself and the request ID found in the header.
fn decode_message(buffer: Vec<u8>, handles: Vec<UntypedHandle>) -> Result<(u64, Self), ValidationError> {
let header = try!(MessageHeader::deserialize(&buffer[..], Vec::new()));
let payload_buffer = &buffer[header.serialized_size(&Default::default())..];
let req_id = header.request_id;
let ret = try!(Self::decode_payload(header, payload_buffer, handles));
Ok((req_id, ret))
}
}
// ********************************************** //
// ****** IMPLEMENTATIONS FOR COMMON TYPES ****** //
// ********************************************** //
macro_rules! impl_encodable_for_prim {
($($prim_type:ty),*) => {
$(
impl MojomEncodable for $prim_type {
fn mojom_type() -> MojomType {
MojomType::Simple
}
fn mojom_alignment() -> usize {
mem::size_of::<$prim_type>()
}
fn embed_size(_context: &Context) -> Bits {
Bits(8 * mem::size_of::<$prim_type>())
}
fn compute_size(&self, _context: Context) -> usize {
0 // Indicates that this type is inlined and it adds nothing external to the size
}
fn encode(self, encoder: &mut Encoder, context: Context) {
let mut state = encoder.get_mut(&context);
state.encode(self);
}
fn decode(decoder: &mut Decoder, context: Context) -> Result<Self, ValidationError> {
let mut state = decoder.get_mut(&context);
Ok(state.decode::<Self>())
}
}
)*
}
}
impl_encodable_for_prim!(i8, i16, i32, i64, u8, u16, u32, u64, f32, f64);
impl MojomEncodable for bool {
fn mojom_alignment() -> usize {
panic!("Should never check_decode mojom_alignment of bools (they're bit-aligned)!");
}
fn mojom_type() -> MojomType {
MojomType::Simple
}
fn embed_size(_context: &Context) -> Bits {
Bits(1)
}
fn compute_size(&self, _context: Context) -> usize {
0 // Indicates that this type is inlined and it adds nothing external to the size
}
fn encode(self, encoder: &mut Encoder, context: Context) {
let mut state = encoder.get_mut(&context);
state.encode_bool(self);
}
fn decode(decoder: &mut Decoder, context: Context) -> Result<Self, ValidationError> {
let mut state = decoder.get_mut(&context);
Ok(state.decode_bool())
}
}
// Options should be considered to represent nullability the Mojom IDL.
// Any type wrapped in an Option type is nullable.
impl<T: MojomEncodable> MojomEncodable for Option<T> {
fn mojom_alignment() -> usize {
T::mojom_alignment()
}
fn mojom_type() -> MojomType {
T::mojom_type()
}
fn embed_size(context: &Context) -> Bits {
T::embed_size(context)
}
fn compute_size(&self, context: Context) -> usize {
match *self {
Some(ref value) => value.compute_size(context),
None => 0,
}
}
fn encode(self, encoder: &mut Encoder, context: Context) {
match self {
Some(value) => value.encode(encoder, context),
None => {
let mut state = encoder.get_mut(&context);
match T::mojom_type() {
MojomType::Pointer => state.encode_null_pointer(),
MojomType::Union => state.encode_null_union(),
MojomType::Handle => state.encode_null_handle(),
MojomType::Interface => {
state.encode_null_handle();
state.encode(0 as u32);
},
MojomType::Simple => panic!("Unexpected simple type in Option!"),
}
},
}
}
fn decode(decoder: &mut Decoder, context: Context) -> Result<Self, ValidationError> {
let skipped = {
let mut state = decoder.get_mut(&context);
match T::mojom_type() {
MojomType::Pointer => state.skip_if_null_pointer(),
MojomType::Union => state.skip_if_null_union(),
MojomType::Handle => state.skip_if_null_handle(),
MojomType::Interface => state.skip_if_null_interface(),
MojomType::Simple => panic!("Unexpected simple type in Option!"),
}
};
if skipped {
Ok(None)
} else {
match T::decode(decoder, context) {
Ok(value) => Ok(Some(value)),
Err(err) => Err(err),
}
}
}
}
macro_rules! impl_pointer_for_array {
() => {
fn header_data(&self) -> DataHeaderValue {
DataHeaderValue::Elements(self.len() as u32)
}
fn serialized_size(&self, context: &Context) -> usize {
DATA_HEADER_SIZE + if self.len() > 0 {
(T::embed_size(context) * self.len()).as_bytes()
} else {
0
}
}
}
}
macro_rules! impl_encodable_for_array {
() => {
impl_encodable_for_pointer!();
fn compute_size(&self, context: Context) -> usize {
let mut size = encoding::align_default(self.serialized_size(&context));
for elem in self.iter() {
size += elem.compute_size(context.clone());
}
size
}
}
}
impl<T: MojomEncodable> MojomPointer for Vec<T> {
impl_pointer_for_array!();
fn encode_value(self, encoder: &mut Encoder, context: Context) {
for elem in self.into_iter() {
elem.encode(encoder, context.clone());
}
}
fn decode_value(decoder: &mut Decoder, context: Context) -> Result<Vec<T>, ValidationError> {
let elems = {
let mut state = decoder.get_mut(&context);
match state.decode_array_header::<T>() {
Ok(header) => header.data(),
Err(err) => return Err(err),
}
};
let mut value = Vec::with_capacity(elems as usize);
for _ in 0..elems {
match T::decode(decoder, context.clone()) {
Ok(elem) => value.push(elem),
Err(err) => return Err(err),
}
}
Ok(value)
}
}
impl<T: MojomEncodable> MojomEncodable for Vec<T> {
impl_encodable_for_array!();
}
macro_rules! impl_encodable_for_fixed_array {
($($len:expr),*) => {
$(
impl<T: MojomEncodable> MojomPointer for [T; $len] {
impl_pointer_for_array!();
fn encode_value(mut self, encoder: &mut Encoder, context: Context) {
let mut panic_error = None;
let mut moves = 0;
unsafe {
// In order to move elements out of an array we need to replace the
// value with uninitialized memory.
for elem in self.iter_mut() {
let owned_elem = mem::replace(elem, mem::uninitialized());
// We need to handle if an unwinding panic happens to prevent use of
// uninitialized memory...
let next_context = context.clone();
// We assert everything going into this closure is unwind safe. If anything
// is added, PLEASE make sure it is also unwind safe...
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
owned_elem.encode(encoder, next_context);
}));
if let Err(err) = result {
panic_error = Some(err);
break;
}
moves += 1;
}
if let Some(err) = panic_error {
for i in moves..self.len() {
ptr::drop_in_place(&mut self[i] as *mut T);
}
// Forget the array to prevent a drop
mem::forget(self);
// Continue unwinding
panic::resume_unwind(err);
}
// We cannot risk drop() getting run on the array values, so we just
// forget self.
mem::forget(self);
}
}
fn decode_value(decoder: &mut Decoder, context: Context) -> Result<[T; $len], ValidationError> {
let elems = {
let mut state = decoder.get_mut(&context);
match state.decode_array_header::<T>() {
Ok(header) => header.data(),
Err(err) => return Err(err),
}
};
if elems != $len {
return Err(ValidationError::UnexpectedArrayHeader);
}
let mut array: [T; $len];
let mut panic_error = None;
let mut inits = 0;
let mut error = None;
unsafe {
// Since we don't force Clone to be implemented on Mojom types
// (mainly due to handles) we need to create this array as uninitialized
// and initialize it manually.
array = mem::uninitialized();
for elem in &mut array[..] {
// When a panic unwinds it may try to read and drop uninitialized
// memory, so we need to catch this. However, we pass mutable state!
// This could be bad as we could observe a broken invariant inside
// of decoder and access it as usual, but we do NOT access decoder
// here, nor do we ever unwind through one of decoder's methods.
// Therefore, it should be safe to assert that decoder is unwind safe.
let next_context = context.clone();
// We assert everything going into this closure is unwind safe. If anything
// is added, PLEASE make sure it is also unwind safe...
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
T::decode(decoder, next_context)
}));
match result {
Ok(non_panic_value) => match non_panic_value {
Ok(value) => ptr::write(elem, value),
Err(err) => {
error = Some(err);
break;
},
},
Err(err) => {
panic_error = Some(err);
break;
},
}
inits += 1;
}
if panic_error.is_some() || error.is_some() {
// Drop everything that was initialized
for i in 0..inits {
ptr::drop_in_place(&mut array[i] as *mut T);
}
// Forget the array to prevent a drop
mem::forget(array);
if let Some(err) = panic_error {
panic::resume_unwind(err);
}
return Err(error.take().expect("Corrupted stack?"));
}
}
Ok(array)
}
}
impl<T: MojomEncodable> MojomEncodable for [T; $len] {
impl_encodable_for_array!();
}
)*
}
}
// Unfortunately, we cannot be generic over the length of a fixed array
// even though its part of the type (this will hopefully be added in the
// future) so for now we implement encodable for only the first 33 fixed
// size array types.
impl_encodable_for_fixed_array!( 0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32);
impl<T: MojomEncodable> MojomPointer for Box<[T]> {
impl_pointer_for_array!();
fn encode_value(self, encoder: &mut Encoder, context: Context) {
for elem in self.into_vec().into_iter() {
elem.encode(encoder, context.clone());
}
}
fn decode_value(decoder: &mut Decoder, context: Context) -> Result<Box<[T]>, ValidationError> {
match Vec::<T>::decode_value(decoder, context) {
Ok(vec) => Ok(vec.into_boxed_slice()),
Err(err) => Err(err),
}
}
}
impl<T: MojomEncodable> MojomEncodable for Box<[T]> {
impl_encodable_for_array!();
}
// We can represent a Mojom string as just a Rust String type
// since both are UTF-8.
impl MojomPointer for String {
fn header_data(&self) -> DataHeaderValue {
DataHeaderValue::Elements(self.len() as u32)
}
fn serialized_size(&self, _context: &Context) -> usize {
DATA_HEADER_SIZE + self.len()
}
fn encode_value(self, encoder: &mut Encoder, context: Context) {
for byte in self.as_bytes() {
byte.encode(encoder, context.clone());
}
}
fn decode_value(decoder: &mut Decoder, context: Context) -> Result<String, ValidationError> {
let mut state = decoder.get_mut(&context);
let elems = match state.decode_array_header::<u8>() {
Ok(header) => header.data(),
Err(err) => return Err(err),
};
let mut value = Vec::with_capacity(elems as usize);
for _ in 0..elems {
value.push(state.decode::<u8>());
}
match String::from_utf8(value) {
Ok(string) => Ok(string),
Err(err) => panic!("Error decoding String: {}", err),
}
}
}
impl MojomEncodable for String {
impl_encodable_for_pointer!();
fn compute_size(&self, context: Context) -> usize {
encoding::align_default(self.serialized_size(&context))
}
}
/// Helper function to clean up duplicate code in HashMap.
fn array_claim_and_decode_header<T: MojomEncodable>(decoder: &mut Decoder, offset: usize) -> Result<(Context, usize), ValidationError> {
let context = match decoder.claim(offset) {
Ok(new_context) => new_context,
Err(err) => return Err(err),
};
let elems = {
let state = decoder.get_mut(&context);
match state.decode_array_header::<T>() {
Ok(header) => header.data(),
Err(err) => return Err(err),
}
};
Ok((context, elems as usize))
}
impl<K: MojomEncodable + Eq + Hash, V: MojomEncodable> MojomPointer for HashMap<K, V> {
fn header_data(&self) -> DataHeaderValue {
DataHeaderValue::Version(0)
}
fn serialized_size(&self, _context: &Context) -> usize {
MAP_SIZE
}
fn encode_value(self, encoder: &mut Encoder, context: Context) {
let elems = self.len();
let meta_value = DataHeaderValue::Elements(elems as u32);
// We need to move values into this vector because we can't copy the keys.
// (Handles are not copyable so MojomEncodable cannot be copyable!)
let mut vals_vec = Vec::with_capacity(elems);
// Key setup
// Write a pointer to the keys array.
let keys_loc = encoder.size() as u64;
{
let state = encoder.get_mut(&context);
state.encode_pointer(keys_loc);
}
// Create the keys data header
let keys_bytes = DATA_HEADER_SIZE + (K::embed_size(&context) * elems).as_bytes();
let keys_data_header = DataHeader::new(keys_bytes, meta_value);
// Claim space for the keys array in the encoder
let keys_context = encoder.add(&keys_data_header).unwrap();
// Encode keys, setup vals
for (key, value) in self.into_iter() {
key.encode(encoder, keys_context.clone());
vals_vec.push(value);
}
// Encode vals
vals_vec.encode(encoder, context.clone())
}
fn decode_value(decoder: &mut Decoder, context: Context) -> Result<HashMap<K, V>, ValidationError> {
let (keys_offset, vals_offset) = {
let state = decoder.get_mut(&context);
match state.decode_struct_header(&MAP_VERSIONS) {
Ok(_) => (),
Err(err) => return Err(err),
};
// Decode the keys pointer and check for overflow
let keys_offset = match state.decode_pointer() {
Some(ptr) => ptr,
None => return Err(ValidationError::IllegalPointer),
};
// Decode the keys pointer and check for overflow
let vals_offset = match state.decode_pointer() {
Some(ptr) => ptr,
None => return Err(ValidationError::IllegalPointer),
};
if keys_offset == MOJOM_NULL_POINTER || vals_offset == MOJOM_NULL_POINTER {
return Err(ValidationError::UnexpectedNullPointer);
}
(keys_offset as usize, vals_offset as usize)
};
let (keys_context, keys_elems) = match array_claim_and_decode_header::<K>(decoder, keys_offset) {
Ok((context, elems)) => (context, elems),
Err(err) => return Err(err),
};
let mut keys_vec: Vec<K> = Vec::with_capacity(keys_elems as usize);
for _ in 0..keys_elems {
let key = match K::decode(decoder, keys_context.clone()) {
Ok(value) => value,
Err(err) => return Err(err),
};
keys_vec.push(key);
}
let (vals_context, vals_elems) = match array_claim_and_decode_header::<V>(decoder, vals_offset) {
Ok((context, elems)) => (context, elems),
Err(err) => return Err(err),
};
if keys_elems != vals_elems {
return Err(ValidationError::DifferentSizedArraysInMap);
}
let mut map = HashMap::with_capacity(keys_elems as usize);
for key in keys_vec.into_iter() {
let val = match V::decode(decoder, vals_context.clone()) {
Ok(value) => value,
Err(err) => return Err(err),
};
map.insert(key, val);
}
Ok(map)
}
}
impl<K: MojomEncodable + Eq + Hash, V: MojomEncodable> MojomEncodable for HashMap<K, V> {
impl_encodable_for_pointer!();
fn compute_size(&self, context: Context) -> usize {
let mut size = encoding::align_default(self.serialized_size(&context));
// The size of the one array
size += DATA_HEADER_SIZE;
size += (K::embed_size(&context) * self.len()).as_bytes();
size = encoding::align_default(size);
// Any extra space used by the keys
for (key, _) in self {
size += key.compute_size(context.clone());
}
// Need to re-align after this for the next array
size = encoding::align_default(size);
// The size of the one array
size += DATA_HEADER_SIZE;
size += (V::embed_size(&context) * self.len()).as_bytes();
size = encoding::align_default(size);
// Any extra space used by the values
for (_, value) in self {
size += value.compute_size(context.clone());
}
// Align one more time at the end to keep the next object aligned.
encoding::align_default(size)
}
}
impl<T: MojomEncodable + CastHandle + Handle> MojomHandle for T {}
macro_rules! impl_encodable_for_handle {
($handle_type:path) => {
fn mojom_alignment() -> usize {
4
}
fn mojom_type() -> MojomType {
MojomType::Handle
}
fn embed_size(_context: &Context) -> Bits {
Bits(8 * mem::size_of::<u32>())
}
fn compute_size(&self, _context: Context) -> usize {
0
}
fn encode(self, encoder: &mut Encoder, context: Context) {
let pos = encoder.add_handle(self.as_untyped());
let mut state = encoder.get_mut(&context);
state.encode(pos as i32);
}
fn decode(decoder: &mut Decoder, context: Context) -> Result<$handle_type, ValidationError> {
let handle_index = {
let mut state = decoder.get_mut(&context);
state.decode::<i32>()
};
decoder.claim_handle::<$handle_type>(handle_index)
}
}
}
impl MojomEncodable for UntypedHandle {
impl_encodable_for_handle!(UntypedHandle);
}
impl MojomEncodable for message_pipe::MessageEndpoint {
impl_encodable_for_handle!(message_pipe::MessageEndpoint);
}
impl MojomEncodable for shared_buffer::SharedBuffer {
impl_encodable_for_handle!(shared_buffer::SharedBuffer);
}
impl<T> MojomEncodable for data_pipe::Consumer<T> {
impl_encodable_for_handle!(data_pipe::Consumer<T>);
}
impl<T> MojomEncodable for data_pipe::Producer<T> {
impl_encodable_for_handle!(data_pipe::Producer<T>);
}
impl MojomEncodable for wait_set::WaitSet {
impl_encodable_for_handle!(wait_set::WaitSet);
}