| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| //! FIDL encoding and decoding. |
| |
| // TODO(https://fxbug.dev/42069912): This file is too big. Split it into smaller files. |
| |
| pub use static_assertions::const_assert_eq; |
| |
| use { |
| crate::endpoints::ProtocolMarker, |
| crate::handle::{ |
| Handle, HandleBased, HandleDisposition, HandleInfo, HandleOp, ObjectType, Rights, Status, |
| }, |
| crate::{Error, MethodType, Result}, |
| bitflags::bitflags, |
| fuchsia_zircon_status as zx_status, fuchsia_zircon_types as zx_types, |
| std::{cell::RefCell, cell::RefMut, marker::PhantomData, mem, ptr, str}, |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Traits |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /// A FIDL type marker. |
| /// |
| /// This trait is only used for compile time dispatch. For example, we can |
| /// parameterize code on `T: TypeMarker`, but we would never write `value: T`. |
| /// In fact, `T` is often a zero-sized struct. From the user's perspective, |
| /// `T::Owned` is the FIDL type's "Rust type". For example, for the FIDL type |
| /// `string:10`, `T` is `BoundedString<10>` and `T::Owned` is `String`. |
| /// |
| /// For primitive types and user-defined types, `Self` is actually the same as |
| /// `Self::Owned`. For all others (strings, arrays, vectors, handles, endpoints, |
| /// optionals, error results), `Self` is a zero-sized struct that uses generics |
| /// to represent FIDL type information such as the element type or constraints. |
| /// |
| /// # Safety |
| /// |
| /// * Implementations of `encode_is_copy` must only return true if it is safe to |
| /// transmute from `*const Self::Owned` to `*const u8` and read `inline_size` |
| /// bytes starting from that address. |
| /// |
| /// * Implementations of `decode_is_copy` must only return true if it is safe to |
| /// transmute from `*mut Self::Owned` to `*mut u8` and write `inline_size` |
| /// bytes starting at that address. |
| pub unsafe trait TypeMarker: 'static + Sized { |
| /// The owned Rust type which this FIDL type decodes into. |
| type Owned: Decode<Self>; |
| |
| /// Returns the minimum required alignment of the inline portion of the |
| /// encoded object. It must be a (nonzero) power of two. |
| fn inline_align(context: Context) -> usize; |
| |
| /// Returns the size of the inline portion of the encoded object, including |
| /// padding for alignment. Must be a multiple of `inline_align`. |
| fn inline_size(context: Context) -> usize; |
| |
| /// Returns true if the memory layout of `Self::Owned` matches the FIDL wire |
| /// format and encoding requires no validation. When true, we can optimize |
| /// encoding arrays and vectors of `Self::Owned` to a single memcpy. |
| /// |
| /// This can be true even when `decode_is_copy` is false. For example, bools |
| /// require validation when decoding, but they do not require validation |
| /// when encoding because Rust guarantees a bool is either 0x00 or 0x01. |
| #[inline(always)] |
| fn encode_is_copy() -> bool { |
| false |
| } |
| |
| /// Returns true if the memory layout of `Self::Owned` matches the FIDL wire |
| /// format and decoding requires no validation. When true, we can optimize |
| /// decoding arrays and vectors of `Self::Owned` to a single memcpy. |
| #[inline(always)] |
| fn decode_is_copy() -> bool { |
| false |
| } |
| } |
| |
| /// A FIDL value type marker. |
| /// |
| /// Value types are guaranteed to never contain handles. As a result, they can |
| /// be encoded by immutable reference (or by value for `Copy` types). |
| pub trait ValueTypeMarker: TypeMarker { |
| /// The Rust type to use for encoding. This is a particular `Encode<Self>` |
| /// type cheaply obtainable from `&Self::Owned`. There are three cases: |
| /// |
| /// - Special cases such as `&[T]` for vectors. |
| /// - For primitives, bits, and enums, it is `Owned`. |
| /// - Otherwise, it is `&Owned`. |
| type Borrowed<'a>: Encode<Self>; |
| |
| /// Cheaply converts from `&Self::Owned` to `Self::Borrowed`. |
| fn borrow(value: &Self::Owned) -> Self::Borrowed<'_>; |
| } |
| |
| /// A FIDL resource type marker. |
| /// |
| /// Resource types are allowed to contain handles. As a result, they must be |
| /// encoded by mutable reference so that handles can be zeroed out. |
| pub trait ResourceTypeMarker: TypeMarker { |
| /// The Rust type to use for encoding. This is a particular `Encode<Self>` |
| /// type cheaply obtainable from `&mut Self::Owned`. There are three cases: |
| /// |
| /// - Special cases such as `&mut [T]` for vectors. |
| /// - When `Owned: HandleBased`, it is `Owned`. |
| /// - Otherwise, it is `&mut Owned`. |
| type Borrowed<'a>: Encode<Self>; |
| |
| /// Cheaply converts from `&mut Self::Owned` to `Self::Borrowed`. For |
| /// `HandleBased` types this is "take" (it returns an owned handle and |
| /// replaces `value` with `Handle::invalid`), and for all other types it is |
| /// "borrow" (just converts from one reference to another). |
| fn take_or_borrow(value: &mut Self::Owned) -> Self::Borrowed<'_>; |
| } |
| |
| /// A Rust type that can be encoded as the FIDL type `T`. |
| /// |
| /// # Safety |
| /// |
| /// Implementations of `encode` must write every byte in |
| /// `encoder.buf[offset..offset + T::inline_size(encoder.context)]` unless |
| /// returning an `Err` value. |
| pub unsafe trait Encode<T: TypeMarker>: Sized { |
| /// Encodes the object into the encoder's buffers. Any handles stored in the |
| /// object are swapped for `Handle::INVALID`. |
| /// |
| /// Implementations that encode out-of-line objects must call `depth.increment()?`. |
| /// |
| /// # Safety |
| /// |
| /// Callers must ensure `offset` is a multiple of `T::inline_align` and |
| /// `encoder.buf` has room for writing `T::inline_size` bytes at `offset`. |
| unsafe fn encode(self, encoder: &mut Encoder<'_>, offset: usize, depth: Depth) -> Result<()>; |
| } |
| |
| /// A Rust type that can be decoded from the FIDL type `T`. |
| pub trait Decode<T: TypeMarker>: 'static + Sized { |
| /// Creates a valid instance of `Self`. The specific value does not matter, |
| /// since it will be overwritten by `decode`. |
| // TODO(https://fxbug.dev/42069855): Take context parameter to discourage using this. |
| fn new_empty() -> Self; |
| |
| /// Decodes an object of type `T` from the decoder's buffers into `self`. |
| /// |
| /// Implementations must validate every byte in |
| /// `decoder.buf[offset..offset + T::inline_size(decoder.context)]` unless |
| /// returning an `Err` value. Implementations that decode out-of-line |
| /// objects must call `depth.increment()?`. |
| /// |
| /// # Safety |
| /// |
| /// Callers must ensure `offset` is a multiple of `T::inline_align` and |
| /// `decoder.buf` has room for reading `T::inline_size` bytes at `offset`. |
| unsafe fn decode( |
| &mut self, |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| depth: Depth, |
| ) -> Result<()>; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Constants |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /// The maximum recursion depth of encoding and decoding. Each pointer to an |
| /// out-of-line object counts as one step in the recursion depth. |
| pub const MAX_RECURSION: usize = 32; |
| |
| /// The maximum number of handles allowed in a FIDL message. Note that this |
| /// number is one less for large messages for the time being. See |
| /// (https://fxbug.dev/42068341) for progress, or to report problems caused by this |
| /// specific limitation. |
| pub const MAX_HANDLES: usize = 64; |
| |
| /// Indicates that an optional value is present. |
| pub const ALLOC_PRESENT_U64: u64 = u64::MAX; |
| /// Indicates that an optional value is present. |
| pub const ALLOC_PRESENT_U32: u32 = u32::MAX; |
| /// Indicates that an optional value is absent. |
| pub const ALLOC_ABSENT_U64: u64 = 0; |
| /// Indicates that an optional value is absent. |
| pub const ALLOC_ABSENT_U32: u32 = 0; |
| |
| /// Special ordinal signifying an epitaph message. |
| pub const EPITAPH_ORDINAL: u64 = 0xffffffffffffffffu64; |
| |
| /// The current wire format magic number |
| pub const MAGIC_NUMBER_INITIAL: u8 = 1; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Helper functions |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /// Rounds `x` up if necessary so that it is a multiple of `align`. |
| /// |
| /// Requires `align` to be a (nonzero) power of two. |
| #[doc(hidden)] // only exported for use in macros or generated code |
| #[inline(always)] |
| pub fn round_up_to_align(x: usize, align: usize) -> usize { |
| debug_assert_ne!(align, 0); |
| debug_assert_eq!(align & (align - 1), 0); |
| // https://en.wikipedia.org/wiki/Data_structure_alignment#Computing_padding |
| (x + align - 1) & !(align - 1) |
| } |
| |
| /// Resize a vector without zeroing added bytes. |
| /// |
| /// The type `T` must be `Copy`. This is not enforced in the type signature |
| /// because it is used in generic contexts where verifying this requires looking |
| /// at control flow. See `decode_vector` for an example. |
| /// |
| /// # Safety |
| /// |
| /// This is unsafe when `new_len > old_len` because it leaves new elements at |
| /// indices `old_len..new_len` uninitialized. The caller must overwrite all the |
| /// new elements before reading them. "Reading" includes any operation that |
| /// extends the vector, such as `push`, because this could reallocate the vector |
| /// and copy the uninitialized bytes. |
| /// |
| /// FIDL conformance tests are used to validate that there are no uninitialized |
| /// bytes in the output across a range of types and values. |
| // TODO(https://fxbug.dev/42075223): Fix safety issues, use MaybeUninit. |
| #[inline] |
| unsafe fn resize_vec_no_zeroing<T>(buf: &mut Vec<T>, new_len: usize) { |
| if new_len > buf.capacity() { |
| buf.reserve(new_len - buf.len()); |
| } |
| // Safety: |
| // - `new_len` must be less than or equal to `capacity()`: |
| // The if-statement above guarantees this. |
| // - The elements at `old_len..new_len` must be initialized: |
| // They are purposely left uninitialized, making this function unsafe. |
| buf.set_len(new_len); |
| } |
| |
| /// Helper type for checking encoding/decoding recursion depth. |
| #[doc(hidden)] // only exported for use in macros or generated code |
| #[derive(Debug, Copy, Clone)] |
| #[repr(transparent)] |
| pub struct Depth(usize); |
| |
| impl Depth { |
| /// Increments the depth, and returns an error if it exceeds the limit. |
| #[inline(always)] |
| pub fn increment(&mut self) -> Result<()> { |
| self.0 += 1; |
| if self.0 > MAX_RECURSION { |
| return Err(Error::MaxRecursionDepth); |
| } |
| Ok(()) |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Helper macros |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /// Given `T: TypeMarker`, expands to a `T::Owned::new_empty` call. |
| #[doc(hidden)] // only exported for use in macros or generated code |
| #[macro_export] |
| macro_rules! new_empty { |
| ($ty:ty) => { |
| <<$ty as $crate::encoding::TypeMarker>::Owned as $crate::encoding::Decode<$ty>>::new_empty() |
| }; |
| } |
| |
| /// Given `T: TypeMarker`, expands to a `T::Owned::decode` call. |
| #[doc(hidden)] // only exported for use in macros or generated code |
| #[macro_export] |
| macro_rules! decode { |
| ($ty:ty, $out_value:expr, $decoder:expr, $offset:expr, $depth:expr) => { |
| <<$ty as $crate::encoding::TypeMarker>::Owned as $crate::encoding::Decode<$ty>>::decode( |
| $out_value, $decoder, $offset, $depth, |
| ) |
| }; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Wire format |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /// Wire format version to use during encode / decode. |
| #[derive(Clone, Copy, Debug)] |
| pub enum WireFormatVersion { |
| /// FIDL 2023 wire format. |
| V2, |
| } |
| |
| /// Context for encoding and decoding. |
| /// |
| /// WARNING: Do not construct this directly unless you know what you're doing. |
| /// FIDL uses `Context` to coordinate soft migrations, so improper uses of it |
| /// could result in ABI breakage. |
| #[derive(Clone, Copy, Debug)] |
| pub struct Context { |
| /// Wire format version to use when encoding / decoding. |
| pub wire_format_version: WireFormatVersion, |
| } |
| |
| // We only support one wire format right now, so context should be zero size. |
| const_assert_eq!(mem::size_of::<Context>(), 0); |
| |
| impl Context { |
| /// Returns the header flags to set when encoding with this context. |
| #[inline] |
| pub(crate) fn at_rest_flags(&self) -> AtRestFlags { |
| match self.wire_format_version { |
| WireFormatVersion::V2 => AtRestFlags::USE_V2_WIRE_FORMAT, |
| } |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Encoder |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /// Encoding state |
| #[derive(Debug)] |
| pub struct Encoder<'a> { |
| /// Encoding context. |
| pub context: Context, |
| |
| /// Buffer to write output data into. |
| pub buf: &'a mut Vec<u8>, |
| |
| /// Buffer to write output handles into. |
| handles: &'a mut Vec<HandleDisposition<'static>>, |
| } |
| |
| /// The default context for encoding. |
| #[inline] |
| fn default_encode_context() -> Context { |
| Context { wire_format_version: WireFormatVersion::V2 } |
| } |
| |
| impl<'a> Encoder<'a> { |
| /// FIDL-encodes `x` into the provided data and handle buffers. |
| #[inline] |
| pub fn encode<T: TypeMarker>( |
| buf: &'a mut Vec<u8>, |
| handles: &'a mut Vec<HandleDisposition<'static>>, |
| x: impl Encode<T>, |
| ) -> Result<()> { |
| let context = default_encode_context(); |
| Self::encode_with_context::<T>(context, buf, handles, x) |
| } |
| |
| /// FIDL-encodes `x` into the provided data and handle buffers, using the |
| /// specified encoding context. |
| /// |
| /// WARNING: Do not call this directly unless you know what you're doing. |
| /// FIDL uses `Context` to coordinate soft migrations, so improper uses of |
| /// this function could result in ABI breakage. |
| #[inline] |
| pub fn encode_with_context<T: TypeMarker>( |
| context: Context, |
| buf: &'a mut Vec<u8>, |
| handles: &'a mut Vec<HandleDisposition<'static>>, |
| x: impl Encode<T>, |
| ) -> Result<()> { |
| fn prepare_for_encoding<'a>( |
| context: Context, |
| buf: &'a mut Vec<u8>, |
| handles: &'a mut Vec<HandleDisposition<'static>>, |
| ty_inline_size: usize, |
| ) -> Encoder<'a> { |
| // An empty response can have size zero. |
| // This if statement is needed to not break the padding write below. |
| if ty_inline_size != 0 { |
| let aligned_inline_size = round_up_to_align(ty_inline_size, 8); |
| // Safety: The uninitialized elements are written by `x.encode`, |
| // except for the trailing padding which is zeroed below. |
| unsafe { |
| resize_vec_no_zeroing(buf, aligned_inline_size); |
| |
| // Zero the last 8 bytes in the block to ensure padding bytes are zero. |
| let padding_ptr = buf.get_unchecked_mut(aligned_inline_size - 8) as *mut u8; |
| (padding_ptr as *mut u64).write_unaligned(0); |
| } |
| } |
| handles.truncate(0); |
| Encoder { buf, handles, context } |
| } |
| let mut encoder = prepare_for_encoding(context, buf, handles, T::inline_size(context)); |
| // Safety: We reserve `T::inline_size` bytes in `encoder.buf` above. |
| unsafe { x.encode(&mut encoder, 0, Depth(0)) } |
| } |
| |
| /// In debug mode only, asserts that there is enough room in the buffer to |
| /// write an object of type `T` at `offset`. |
| #[inline(always)] |
| pub fn debug_check_bounds<T: TypeMarker>(&self, offset: usize) { |
| debug_assert!(offset + T::inline_size(self.context) <= self.buf.len()); |
| } |
| |
| /// Encodes a primitive numeric type. |
| /// |
| /// # Safety |
| /// |
| /// The caller must ensure that `self.buf` has room for writing |
| /// `T::inline_size` bytes as `offset`. |
| #[inline(always)] |
| pub unsafe fn write_num<T: numeric::Numeric>(&mut self, num: T, offset: usize) { |
| debug_assert!(offset + mem::size_of::<T>() <= self.buf.len()); |
| // Safety: The caller ensures `offset` is valid for writing |
| // sizeof(T) bytes. Transmuting to a same-or-wider |
| // integer or float pointer is safe because we use `write_unaligned`. |
| let ptr = self.buf.get_unchecked_mut(offset) as *mut u8; |
| (ptr as *mut T).write_unaligned(num); |
| } |
| |
| /// Returns an offset for writing `len` out-of-line bytes. Zeroes padding |
| /// bytes at the end if `len` is not a multiple of 8. |
| /// |
| /// # Safety |
| /// |
| /// The caller must ensure that `len` is nonzero. |
| #[inline] |
| pub unsafe fn out_of_line_offset(&mut self, len: usize) -> usize { |
| debug_assert!(len > 0); |
| let new_offset = self.buf.len(); |
| let padded_len = round_up_to_align(len, 8); |
| debug_assert!(padded_len >= 8); |
| let new_len = self.buf.len() + padded_len; |
| resize_vec_no_zeroing(self.buf, new_len); |
| // Zero the last 8 bytes in the block to ensure padding bytes are zero. |
| // It's more efficient to always write 8 bytes regardless of how much |
| // padding is needed because we will overwrite non-padding afterwards. |
| let padding_ptr = self.buf.get_unchecked_mut(new_len - 8) as *mut u8; |
| (padding_ptr as *mut u64).write_unaligned(0); |
| new_offset |
| } |
| |
| /// Write padding at the specified offset. |
| /// |
| /// # Safety |
| /// |
| /// The caller must ensure that `self.buf` has room for writing `len` bytes |
| /// as `offset`. |
| #[inline(always)] |
| pub unsafe fn padding(&mut self, offset: usize, len: usize) { |
| if len == 0 { |
| return; |
| } |
| debug_assert!(offset + len <= self.buf.len()); |
| // Safety: |
| // - The caller ensures `offset` is valid for writing `len` bytes. |
| // - All u8 pointers are properly aligned. |
| ptr::write_bytes(self.buf.as_mut_ptr().add(offset), 0, len); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Decoder |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /// Decoding state |
| #[derive(Debug)] |
| pub struct Decoder<'a> { |
| /// Decoding context. |
| pub context: Context, |
| |
| /// Buffer from which to read data. |
| pub buf: &'a [u8], |
| |
| /// Next out of line block in buf. |
| next_out_of_line: usize, |
| |
| /// Buffer from which to read handles. |
| handles: &'a mut [HandleInfo], |
| |
| /// Index of the next handle to read from the handle array |
| next_handle: usize, |
| } |
| |
| impl<'a> Decoder<'a> { |
| /// Decodes a value of FIDL type `T` into the Rust type `T::Owned` from the |
| /// provided data and handle buffers. Assumes the buffers came from inside a |
| /// transaction message wrapped by `header`. |
| #[inline] |
| pub fn decode_into<T: TypeMarker>( |
| header: &TransactionHeader, |
| buf: &'a [u8], |
| handles: &'a mut [HandleInfo], |
| value: &mut T::Owned, |
| ) -> Result<()> { |
| Self::decode_with_context::<T>(header.decoding_context(), buf, handles, value) |
| } |
| |
| /// Decodes a value of FIDL type `T` into the Rust type `T::Owned` from the |
| /// provided data and handle buffers, using the specified context. |
| /// |
| /// WARNING: Do not call this directly unless you know what you're doing. |
| /// FIDL uses `Context` to coordinate soft migrations, so improper uses of |
| /// this function could result in ABI breakage. |
| #[inline] |
| pub fn decode_with_context<T: TypeMarker>( |
| context: Context, |
| buf: &'a [u8], |
| handles: &'a mut [HandleInfo], |
| value: &mut T::Owned, |
| ) -> Result<()> { |
| let inline_size = T::inline_size(context); |
| let next_out_of_line = round_up_to_align(inline_size, 8); |
| if next_out_of_line > buf.len() { |
| return Err(Error::OutOfRange); |
| } |
| let mut decoder = Decoder { next_out_of_line, buf, handles, next_handle: 0, context }; |
| // Safety: buf.len() >= inline_size based on the check above. |
| unsafe { |
| value.decode(&mut decoder, 0, Depth(0))?; |
| } |
| // Safety: next_out_of_line <= buf.len() based on the check above. |
| unsafe { decoder.post_decoding(inline_size, next_out_of_line) } |
| } |
| |
| /// Checks for errors after decoding. This is a separate function to reduce |
| /// binary bloat. |
| /// |
| /// # Safety |
| /// |
| /// Requires `padding_end <= self.buf.len()`. |
| unsafe fn post_decoding(&self, padding_start: usize, padding_end: usize) -> Result<()> { |
| if self.next_out_of_line < self.buf.len() { |
| return Err(Error::ExtraBytes); |
| } |
| if self.next_handle < self.handles.len() { |
| return Err(Error::ExtraHandles); |
| } |
| |
| let padding = padding_end - padding_start; |
| if padding > 0 { |
| // Safety: |
| // padding_end <= self.buf.len() is guaranteed by the caller. |
| let last_u64 = unsafe { |
| let last_u64_ptr = self.buf.get_unchecked(padding_end - 8) as *const u8; |
| (last_u64_ptr as *const u64).read_unaligned() |
| }; |
| // padding == 0 => mask == 0x0000000000000000 |
| // padding == 1 => mask == 0xff00000000000000 |
| // padding == 2 => mask == 0xffff000000000000 |
| // ... |
| let mask = !(!0u64 >> (padding * 8)); |
| if last_u64 & mask != 0 { |
| return Err(self.end_of_block_padding_error(padding_start, padding_end)); |
| } |
| } |
| |
| Ok(()) |
| } |
| |
| /// The position of the next out of line block and the end of the current |
| /// blocks. |
| #[inline(always)] |
| pub fn next_out_of_line(&self) -> usize { |
| self.next_out_of_line |
| } |
| |
| /// The number of handles that have not yet been consumed. |
| #[inline(always)] |
| pub fn remaining_handles(&self) -> usize { |
| self.handles.len() - self.next_handle |
| } |
| |
| /// In debug mode only, asserts that there is enough room in the buffer to |
| /// read an object of type `T` at `offset`. |
| #[inline(always)] |
| pub fn debug_check_bounds<T: TypeMarker>(&self, offset: usize) { |
| debug_assert!(offset + T::inline_size(self.context) <= self.buf.len()); |
| } |
| |
| /// Decodes a primitive numeric type. The caller must ensure that `self.buf` |
| /// has room for reading `T::inline_size` bytes as `offset`. |
| #[inline(always)] |
| pub fn read_num<T: numeric::Numeric>(&mut self, offset: usize) -> T { |
| debug_assert!(offset + mem::size_of::<T>() <= self.buf.len()); |
| // Safety: The caller ensures `offset` is valid for reading |
| // sizeof(T) bytes. Transmuting to a same-or-wider |
| // integer pointer is safe because we use `read_unaligned`. |
| unsafe { |
| let ptr = self.buf.get_unchecked(offset) as *const u8; |
| (ptr as *const T).read_unaligned() |
| } |
| } |
| |
| /// Returns an offset for reading `len` out-of-line bytes. Validates that |
| /// padding bytes at the end are zero if `len` is not a multiple of 8. |
| /// |
| /// # Safety |
| /// |
| /// The caller must ensure that `len` is nonzero. |
| #[inline(always)] |
| pub unsafe fn out_of_line_offset(&mut self, len: usize) -> Result<usize> { |
| debug_assert!(len > 0); |
| let offset = self.next_out_of_line; |
| let aligned_len = round_up_to_align(len, 8); |
| self.next_out_of_line += aligned_len; |
| debug_assert!(self.next_out_of_line >= 8); |
| if self.next_out_of_line > self.buf.len() { |
| return Err(Error::OutOfRange); |
| } |
| // Validate padding bytes at the end of the block. |
| // Safety: |
| // - The caller ensures `len > 0`, therefore `aligned_len >= 8`. |
| // - After `self.next_out_of_line += aligned_len`, we know `self.next_out_of_line >= aligned_len >= 8`. |
| // - Therefore `self.next_out_of_line - 8 >= 0` is a valid *const u64. |
| let last_u64_ptr = self.buf.get_unchecked(self.next_out_of_line - 8) as *const u8; |
| let last_u64 = (last_u64_ptr as *const u64).read_unaligned(); |
| let padding = aligned_len - len; |
| // padding == 0 => mask == 0x0000000000000000 |
| // padding == 1 => mask == 0xff00000000000000 |
| // padding == 2 => mask == 0xffff000000000000 |
| // ... |
| let mask = !(!0u64 >> (padding * 8)); |
| if last_u64 & mask != 0 { |
| return Err(self.end_of_block_padding_error(offset + len, self.next_out_of_line)); |
| } |
| |
| Ok(offset) |
| } |
| |
| /// Generates an error for bad padding bytes at the end of a block. |
| /// Assumes it is already known that there is a nonzero padding byte. |
| fn end_of_block_padding_error(&self, start: usize, end: usize) -> Error { |
| for i in start..end { |
| if self.buf[i] != 0 { |
| return Error::NonZeroPadding { padding_start: start }; |
| } |
| } |
| // This should be unreachable because we only call this after finding |
| // nonzero padding. Abort instead of panicking to save code size. |
| std::process::abort(); |
| } |
| |
| /// Checks that the specified padding bytes are in fact zeroes. Like |
| /// `Decode::decode`, the caller is responsible for bounds checks. |
| #[inline] |
| pub fn check_padding(&self, offset: usize, len: usize) -> Result<()> { |
| if len == 0 { |
| // Skip body (so it can be optimized out). |
| return Ok(()); |
| } |
| debug_assert!(offset + len <= self.buf.len()); |
| for i in offset..offset + len { |
| // Safety: Caller guarantees offset..offset+len is in bounds. |
| if unsafe { *self.buf.get_unchecked(i) } != 0 { |
| return Err(Error::NonZeroPadding { padding_start: offset }); |
| } |
| } |
| Ok(()) |
| } |
| |
| /// Checks the padding of the inline value portion of an envelope. Like |
| /// `Decode::decode`, the caller is responsible for bounds checks. |
| /// |
| /// Note: `check_padding` could be used instead, but doing so leads to long |
| /// compilation times which is why this method exists. |
| #[inline] |
| pub fn check_inline_envelope_padding( |
| &self, |
| value_offset: usize, |
| value_len: usize, |
| ) -> Result<()> { |
| // Safety: The caller ensures `value_offset` is valid for reading |
| // `value_len` bytes. |
| let valid_padding = unsafe { |
| match value_len { |
| 1 => { |
| *self.buf.get_unchecked(value_offset + 1) == 0 |
| && *self.buf.get_unchecked(value_offset + 2) == 0 |
| && *self.buf.get_unchecked(value_offset + 3) == 0 |
| } |
| 2 => { |
| *self.buf.get_unchecked(value_offset + 2) == 0 |
| && *self.buf.get_unchecked(value_offset + 3) == 0 |
| } |
| 3 => *self.buf.get_unchecked(value_offset + 3) == 0, |
| 4 => true, |
| value_len => unreachable!("value_len={}", value_len), |
| } |
| }; |
| if valid_padding { |
| Ok(()) |
| } else { |
| Err(Error::NonZeroPadding { padding_start: value_offset + value_len }) |
| } |
| } |
| |
| /// Take the next handle from the `handles` list. |
| #[inline] |
| pub fn take_next_handle( |
| &mut self, |
| expected_object_type: ObjectType, |
| expected_rights: Rights, |
| ) -> Result<Handle> { |
| let Some(next_handle) = self.handles.get_mut(self.next_handle) else { |
| return Err(Error::OutOfRange); |
| }; |
| let handle_info = mem::replace( |
| next_handle, |
| HandleInfo { |
| handle: Handle::invalid(), |
| object_type: ObjectType::NONE, |
| rights: Rights::NONE, |
| }, |
| ); |
| let handle = |
| self.consume_handle_info(handle_info, expected_object_type, expected_rights)?; |
| self.next_handle += 1; |
| Ok(handle) |
| } |
| |
| /// Drops the next handle in the handle array. |
| #[inline] |
| pub fn drop_next_handle(&mut self) -> Result<()> { |
| let Some(next_handle) = self.handles.get_mut(self.next_handle) else { |
| return Err(Error::OutOfRange); |
| }; |
| drop(mem::replace( |
| next_handle, |
| HandleInfo { |
| handle: Handle::invalid(), |
| object_type: ObjectType::NONE, |
| rights: Rights::NONE, |
| }, |
| )); |
| self.next_handle += 1; |
| Ok(()) |
| } |
| |
| fn consume_handle_info( |
| &self, |
| mut handle_info: HandleInfo, |
| expected_object_type: ObjectType, |
| expected_rights: Rights, |
| ) -> Result<Handle> { |
| let received_object_type = handle_info.object_type; |
| if expected_object_type != ObjectType::NONE |
| && received_object_type != ObjectType::NONE |
| && expected_object_type != received_object_type |
| { |
| return Err(Error::IncorrectHandleSubtype { |
| expected: expected_object_type, |
| received: received_object_type, |
| }); |
| } |
| |
| let received_rights = handle_info.rights; |
| if expected_rights != Rights::SAME_RIGHTS |
| && received_rights != Rights::SAME_RIGHTS |
| && expected_rights != received_rights |
| { |
| if !received_rights.contains(expected_rights) { |
| return Err(Error::MissingExpectedHandleRights { |
| missing_rights: expected_rights - received_rights, |
| }); |
| } |
| return match handle_info.handle.replace(expected_rights) { |
| Ok(r) => Ok(r), |
| Err(status) => Err(Error::HandleReplace(status)), |
| }; |
| } |
| Ok(mem::replace(&mut handle_info.handle, Handle::invalid())) |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Ambiguous types |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /// A fake FIDL type that can encode from and decode into any Rust type. This |
| /// exists solely to prevent the compiler from inferring `T: TypeMarker`, |
| /// allowing us to add new generic impls without source breakage. It also |
| /// improves error messages when no suitable `T: TypeMarker` exists, preventing |
| /// spurious guesses about what you should do (e.g. implement `HandleBased`). |
| pub struct Ambiguous1; |
| |
| /// Like `Ambiguous1`. There needs to be two of these types so that the compiler |
| /// doesn't infer one of them and generate a call to the panicking methods. |
| pub struct Ambiguous2; |
| |
| /// An uninhabited type used as owned and borrowed type for ambiguous markers. |
| /// Can be replaced by `!` once that is stable. |
| pub enum AmbiguousNever {} |
| |
| macro_rules! impl_ambiguous { |
| ($ambiguous:ident) => { |
| unsafe impl TypeMarker for $ambiguous { |
| type Owned = AmbiguousNever; |
| |
| fn inline_align(_context: Context) -> usize { |
| panic!("reached code for fake ambiguous type"); |
| } |
| |
| fn inline_size(_context: Context) -> usize { |
| panic!("reached code for fake ambiguous type"); |
| } |
| } |
| |
| impl ValueTypeMarker for $ambiguous { |
| type Borrowed<'a> = AmbiguousNever; |
| fn borrow(value: &Self::Owned) -> Self::Borrowed<'_> { |
| match *value {} |
| } |
| } |
| |
| impl ResourceTypeMarker for $ambiguous { |
| type Borrowed<'a> = AmbiguousNever; |
| fn take_or_borrow(value: &mut Self::Owned) -> Self::Borrowed<'_> { |
| match *value {} |
| } |
| } |
| |
| unsafe impl<T> Encode<$ambiguous> for T { |
| unsafe fn encode( |
| self, |
| _encoder: &mut Encoder<'_>, |
| _offset: usize, |
| _depth: Depth, |
| ) -> Result<()> { |
| panic!("reached code for fake ambiguous type"); |
| } |
| } |
| |
| // TODO(https://fxbug.dev/42069855): impl for `T: 'static` this once user code has |
| // migrated off new_empty(), which is meant to be internal. |
| impl Decode<$ambiguous> for AmbiguousNever { |
| fn new_empty() -> Self { |
| panic!("reached code for fake ambiguous type"); |
| } |
| |
| unsafe fn decode( |
| &mut self, |
| _decoder: &mut Decoder<'_>, |
| _offset: usize, |
| _depth: Depth, |
| ) -> Result<()> { |
| match *self {} |
| } |
| } |
| }; |
| } |
| |
| impl_ambiguous!(Ambiguous1); |
| impl_ambiguous!(Ambiguous2); |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Empty types |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /// A FIDL type representing an empty payload (0 bytes). |
| pub struct EmptyPayload; |
| |
| unsafe impl TypeMarker for EmptyPayload { |
| type Owned = (); |
| |
| #[inline(always)] |
| fn inline_align(_context: Context) -> usize { |
| 1 |
| } |
| |
| #[inline(always)] |
| fn inline_size(_context: Context) -> usize { |
| 0 |
| } |
| } |
| |
| impl ValueTypeMarker for EmptyPayload { |
| type Borrowed<'a> = (); |
| #[inline(always)] |
| fn borrow(value: &Self::Owned) -> Self::Borrowed<'_> { |
| *value |
| } |
| } |
| |
| unsafe impl Encode<EmptyPayload> for () { |
| #[inline(always)] |
| unsafe fn encode( |
| self, |
| _encoder: &mut Encoder<'_>, |
| _offset: usize, |
| _depth: Depth, |
| ) -> Result<()> { |
| Ok(()) |
| } |
| } |
| |
| impl Decode<EmptyPayload> for () { |
| #[inline(always)] |
| fn new_empty() -> Self {} |
| |
| #[inline(always)] |
| unsafe fn decode( |
| &mut self, |
| _decoder: &mut Decoder<'_>, |
| _offset: usize, |
| _depth: Depth, |
| ) -> Result<()> { |
| Ok(()) |
| } |
| } |
| |
| /// The FIDL type used for an empty success variant in a result union. Result |
| /// unions occur in two-way methods that are flexible or that use error syntax. |
| pub struct EmptyStruct; |
| |
| unsafe impl TypeMarker for EmptyStruct { |
| type Owned = (); |
| |
| #[inline(always)] |
| fn inline_align(_context: Context) -> usize { |
| 1 |
| } |
| |
| #[inline(always)] |
| fn inline_size(_context: Context) -> usize { |
| 1 |
| } |
| } |
| |
| impl ValueTypeMarker for EmptyStruct { |
| type Borrowed<'a> = (); |
| #[inline(always)] |
| fn borrow(value: &Self::Owned) -> Self::Borrowed<'_> { |
| *value |
| } |
| } |
| |
| unsafe impl Encode<EmptyStruct> for () { |
| #[inline] |
| unsafe fn encode(self, encoder: &mut Encoder<'_>, offset: usize, _depth: Depth) -> Result<()> { |
| encoder.debug_check_bounds::<EmptyStruct>(offset); |
| encoder.write_num(0u8, offset); |
| Ok(()) |
| } |
| } |
| |
| impl Decode<EmptyStruct> for () { |
| #[inline(always)] |
| fn new_empty() -> Self {} |
| |
| #[inline] |
| unsafe fn decode( |
| &mut self, |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| _depth: Depth, |
| ) -> Result<()> { |
| decoder.debug_check_bounds::<EmptyStruct>(offset); |
| match decoder.read_num::<u8>(offset) { |
| 0 => Ok(()), |
| _ => Err(Error::Invalid), |
| } |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Primitive types |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| // Private module to prevent others from implementing `Numeric`. |
| mod numeric { |
| use super::*; |
| |
| /// Marker trait for primitive numeric types. |
| pub trait Numeric {} |
| |
| /// Implements `Numeric`, `TypeMarker`, `ValueTypeMarker`, `Encode`, and |
| /// `Decode` for a primitive numeric type (integer or float). |
| macro_rules! impl_numeric { |
| ($numeric_ty:ty) => { |
| impl Numeric for $numeric_ty {} |
| |
| unsafe impl TypeMarker for $numeric_ty { |
| type Owned = $numeric_ty; |
| |
| #[inline(always)] |
| fn inline_align(_context: Context) -> usize { |
| mem::align_of::<$numeric_ty>() |
| } |
| |
| #[inline(always)] |
| fn inline_size(_context: Context) -> usize { |
| mem::size_of::<$numeric_ty>() |
| } |
| |
| #[inline(always)] |
| fn encode_is_copy() -> bool { |
| true |
| } |
| |
| #[inline(always)] |
| fn decode_is_copy() -> bool { |
| true |
| } |
| } |
| |
| impl ValueTypeMarker for $numeric_ty { |
| type Borrowed<'a> = $numeric_ty; |
| |
| #[inline(always)] |
| fn borrow(value: &<Self as TypeMarker>::Owned) -> Self::Borrowed<'_> { |
| *value |
| } |
| } |
| |
| unsafe impl Encode<$numeric_ty> for $numeric_ty { |
| #[inline(always)] |
| unsafe fn encode( |
| self, |
| encoder: &mut Encoder<'_>, |
| offset: usize, |
| _depth: Depth, |
| ) -> Result<()> { |
| encoder.debug_check_bounds::<$numeric_ty>(offset); |
| encoder.write_num::<$numeric_ty>(self, offset); |
| Ok(()) |
| } |
| } |
| |
| impl Decode<$numeric_ty> for $numeric_ty { |
| #[inline(always)] |
| fn new_empty() -> Self { |
| 0 as $numeric_ty |
| } |
| |
| #[inline(always)] |
| unsafe fn decode( |
| &mut self, |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| _depth: Depth, |
| ) -> Result<()> { |
| decoder.debug_check_bounds::<$numeric_ty>(offset); |
| *self = decoder.read_num::<$numeric_ty>(offset); |
| Ok(()) |
| } |
| } |
| }; |
| } |
| |
| impl_numeric!(u8); |
| impl_numeric!(u16); |
| impl_numeric!(u32); |
| impl_numeric!(u64); |
| impl_numeric!(i8); |
| impl_numeric!(i16); |
| impl_numeric!(i32); |
| impl_numeric!(i64); |
| impl_numeric!(f32); |
| impl_numeric!(f64); |
| } |
| |
| unsafe impl TypeMarker for bool { |
| type Owned = bool; |
| |
| #[inline(always)] |
| fn inline_align(_context: Context) -> usize { |
| mem::align_of::<bool>() |
| } |
| |
| #[inline(always)] |
| fn inline_size(_context: Context) -> usize { |
| mem::size_of::<bool>() |
| } |
| |
| #[inline(always)] |
| fn encode_is_copy() -> bool { |
| // Rust guarantees a bool is 0x00 or 0x01. |
| // https://doc.rust-lang.org/reference/types/boolean.html |
| true |
| } |
| |
| #[inline(always)] |
| fn decode_is_copy() -> bool { |
| // Decoding isn't just a copy because we have to ensure it's 0x00 or 0x01. |
| false |
| } |
| } |
| |
| impl ValueTypeMarker for bool { |
| type Borrowed<'a> = bool; |
| |
| #[inline(always)] |
| fn borrow(value: &<Self as TypeMarker>::Owned) -> Self::Borrowed<'_> { |
| *value |
| } |
| } |
| |
| unsafe impl Encode<bool> for bool { |
| #[inline] |
| unsafe fn encode(self, encoder: &mut Encoder<'_>, offset: usize, _depth: Depth) -> Result<()> { |
| encoder.debug_check_bounds::<bool>(offset); |
| // From https://doc.rust-lang.org/std/primitive.bool.html: "If you |
| // cast a bool into an integer, true will be 1 and false will be 0." |
| encoder.write_num(self as u8, offset); |
| Ok(()) |
| } |
| } |
| |
| impl Decode<bool> for bool { |
| #[inline(always)] |
| fn new_empty() -> Self { |
| false |
| } |
| |
| #[inline] |
| unsafe fn decode( |
| &mut self, |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| _depth: Depth, |
| ) -> Result<()> { |
| decoder.debug_check_bounds::<bool>(offset); |
| // Safety: The caller ensures `offset` is valid for reading 1 byte. |
| *self = match unsafe { *decoder.buf.get_unchecked(offset) } { |
| 0 => false, |
| 1 => true, |
| _ => return Err(Error::InvalidBoolean), |
| }; |
| Ok(()) |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Arrays |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /// The FIDL type `array<T, N>`. |
| pub struct Array<T: TypeMarker, const N: usize>(PhantomData<T>); |
| |
| unsafe impl<T: TypeMarker, const N: usize> TypeMarker for Array<T, N> { |
| type Owned = [T::Owned; N]; |
| |
| #[inline(always)] |
| fn inline_align(context: Context) -> usize { |
| T::inline_align(context) |
| } |
| |
| #[inline(always)] |
| fn inline_size(context: Context) -> usize { |
| N * T::inline_size(context) |
| } |
| |
| #[inline(always)] |
| fn encode_is_copy() -> bool { |
| T::encode_is_copy() |
| } |
| |
| #[inline(always)] |
| fn decode_is_copy() -> bool { |
| T::decode_is_copy() |
| } |
| } |
| |
| impl<T: ValueTypeMarker, const N: usize> ValueTypeMarker for Array<T, N> { |
| type Borrowed<'a> = &'a [T::Owned; N]; |
| #[inline(always)] |
| fn borrow(value: &Self::Owned) -> Self::Borrowed<'_> { |
| value |
| } |
| } |
| |
| impl<T: ResourceTypeMarker, const N: usize> ResourceTypeMarker for Array<T, N> { |
| type Borrowed<'a> = &'a mut [T::Owned; N]; |
| #[inline(always)] |
| fn take_or_borrow(value: &mut Self::Owned) -> Self::Borrowed<'_> { |
| value |
| } |
| } |
| |
| unsafe impl<'a, T: ValueTypeMarker, const N: usize> Encode<Array<T, N>> for &'a [T::Owned; N] { |
| #[inline] |
| unsafe fn encode(self, encoder: &mut Encoder<'_>, offset: usize, depth: Depth) -> Result<()> { |
| encoder.debug_check_bounds::<Array<T, N>>(offset); |
| encode_array_value::<T>(self, encoder, offset, depth) |
| } |
| } |
| |
| unsafe impl<'a, T: ResourceTypeMarker, const N: usize> Encode<Array<T, N>> |
| for &'a mut [T::Owned; N] |
| { |
| #[inline] |
| unsafe fn encode(self, encoder: &mut Encoder<'_>, offset: usize, depth: Depth) -> Result<()> { |
| encoder.debug_check_bounds::<Array<T, N>>(offset); |
| encode_array_resource::<T>(self, encoder, offset, depth) |
| } |
| } |
| |
| impl<T: TypeMarker, const N: usize> Decode<Array<T, N>> for [T::Owned; N] { |
| #[inline] |
| fn new_empty() -> Self { |
| let mut arr = mem::MaybeUninit::<[T::Owned; N]>::uninit(); |
| unsafe { |
| let arr_ptr = arr.as_mut_ptr() as *mut T::Owned; |
| for i in 0..N { |
| ptr::write(arr_ptr.add(i), T::Owned::new_empty()); |
| } |
| arr.assume_init() |
| } |
| } |
| |
| #[inline] |
| unsafe fn decode( |
| &mut self, |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| depth: Depth, |
| ) -> Result<()> { |
| decoder.debug_check_bounds::<Array<T, N>>(offset); |
| decode_array::<T>(self, decoder, offset, depth) |
| } |
| } |
| |
| #[inline] |
| unsafe fn encode_array_value<T: ValueTypeMarker>( |
| slice: &[T::Owned], |
| encoder: &mut Encoder<'_>, |
| offset: usize, |
| depth: Depth, |
| ) -> Result<()> { |
| let stride = T::inline_size(encoder.context); |
| let len = slice.len(); |
| // Not a safety requirement, but len should be nonzero since FIDL does not allow empty arrays. |
| debug_assert_ne!(len, 0); |
| if T::encode_is_copy() { |
| debug_assert_eq!(stride, mem::size_of::<T::Owned>()); |
| // Safety: |
| // - The caller ensures `offset` if valid for writing `stride` bytes |
| // (inline size of `T`) `len` times, i.e. `len * stride`. |
| // - Since T::inline_size is the same as mem::size_of for simple |
| // copy types, `slice` also has exactly `len * stride` bytes. |
| // - Rust guarantees `slice` and `encoder.buf` do not alias. |
| unsafe { |
| let src = slice.as_ptr() as *const u8; |
| let dst: *mut u8 = encoder.buf.as_mut_ptr().add(offset); |
| ptr::copy_nonoverlapping(src, dst, len * stride); |
| } |
| } else { |
| for i in 0..len { |
| // Safety: `i` is in bounds since `len` is defined as `slice.len()`. |
| let item = unsafe { slice.get_unchecked(i) }; |
| T::borrow(item).encode(encoder, offset + i * stride, depth)?; |
| } |
| } |
| Ok(()) |
| } |
| |
| #[inline] |
| unsafe fn encode_array_resource<T: ResourceTypeMarker>( |
| slice: &mut [T::Owned], |
| encoder: &mut Encoder<'_>, |
| offset: usize, |
| depth: Depth, |
| ) -> Result<()> { |
| let stride = T::inline_size(encoder.context); |
| let len = slice.len(); |
| // Not a safety requirement, but len should be nonzero since FIDL does not allow empty arrays. |
| debug_assert_ne!(len, 0); |
| if T::encode_is_copy() { |
| debug_assert_eq!(stride, mem::size_of::<T::Owned>()); |
| // Safety: |
| // - The caller ensures `offset` if valid for writing `stride` bytes |
| // (inline size of `T`) `len` times, i.e. `len * stride`. |
| // - Since T::inline_size is the same as mem::size_of for simple |
| // copy types, `slice` also has exactly `len * stride` bytes. |
| // - Rust guarantees `slice` and `encoder.buf` do not alias. |
| unsafe { |
| let src = slice.as_ptr() as *const u8; |
| let dst: *mut u8 = encoder.buf.as_mut_ptr().add(offset); |
| ptr::copy_nonoverlapping(src, dst, len * stride); |
| } |
| } else { |
| for i in 0..len { |
| // Safety: `i` is in bounds since `len` is defined as `slice.len()`. |
| let item = unsafe { slice.get_unchecked_mut(i) }; |
| T::take_or_borrow(item).encode(encoder, offset + i * stride, depth)?; |
| } |
| } |
| Ok(()) |
| } |
| |
| #[inline] |
| unsafe fn decode_array<T: TypeMarker>( |
| slice: &mut [T::Owned], |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| depth: Depth, |
| ) -> Result<()> { |
| let stride = T::inline_size(decoder.context); |
| let len = slice.len(); |
| // Not a safety requirement, but len should be nonzero since FIDL does not allow empty arrays. |
| debug_assert_ne!(len, 0); |
| if T::decode_is_copy() { |
| debug_assert_eq!(stride, mem::size_of::<T::Owned>()); |
| // Safety: |
| // - The caller ensures `offset` if valid for reading `stride` bytes |
| // (inline size of `T`) `len` times, i.e. `len * stride`. |
| // - Since T::inline_size is the same as mem::size_of for simple copy |
| // types, `slice` also has exactly `len * stride` bytes. |
| // - Rust guarantees `slice` and `decoder.buf` do not alias. |
| unsafe { |
| let src: *const u8 = decoder.buf.as_ptr().add(offset); |
| let dst = slice.as_mut_ptr() as *mut u8; |
| ptr::copy_nonoverlapping(src, dst, len * stride); |
| } |
| } else { |
| for i in 0..len { |
| // Safety: `i` is in bounds since `len` is defined as `slice.len()`. |
| let item = unsafe { slice.get_unchecked_mut(i) }; |
| item.decode(decoder, offset + i * stride, depth)?; |
| } |
| } |
| Ok(()) |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Vectors |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /// The maximum vector bound, corresponding to the `MAX` constraint in FIDL. |
| pub const MAX_BOUND: usize = usize::MAX; |
| |
| /// The FIDL type `vector<T>:N`. |
| pub struct Vector<T: TypeMarker, const N: usize>(PhantomData<T>); |
| |
| /// The FIDL type `vector<T>` or `vector<T>:MAX`. |
| pub type UnboundedVector<T> = Vector<T, MAX_BOUND>; |
| |
| unsafe impl<T: TypeMarker, const N: usize> TypeMarker for Vector<T, N> { |
| type Owned = Vec<T::Owned>; |
| |
| #[inline(always)] |
| fn inline_align(_context: Context) -> usize { |
| 8 |
| } |
| |
| #[inline(always)] |
| fn inline_size(_context: Context) -> usize { |
| 16 |
| } |
| } |
| |
| impl<T: ValueTypeMarker, const N: usize> ValueTypeMarker for Vector<T, N> { |
| type Borrowed<'a> = &'a [T::Owned]; |
| #[inline(always)] |
| fn borrow(value: &Self::Owned) -> Self::Borrowed<'_> { |
| value |
| } |
| } |
| |
| impl<T: ResourceTypeMarker, const N: usize> ResourceTypeMarker for Vector<T, N> { |
| type Borrowed<'a> = &'a mut [T::Owned]; |
| #[inline(always)] |
| fn take_or_borrow(value: &mut Self::Owned) -> Self::Borrowed<'_> { |
| value |
| } |
| } |
| |
| unsafe impl<'a, T: ValueTypeMarker, const N: usize> Encode<Vector<T, N>> for &'a [T::Owned] { |
| #[inline] |
| unsafe fn encode(self, encoder: &mut Encoder<'_>, offset: usize, depth: Depth) -> Result<()> { |
| encoder.debug_check_bounds::<Vector<T, N>>(offset); |
| encode_vector_value::<T>(self, N, check_vector_length, encoder, offset, depth) |
| } |
| } |
| |
| unsafe impl<'a, T: ResourceTypeMarker, const N: usize> Encode<Vector<T, N>> |
| for &'a mut [T::Owned] |
| { |
| #[inline] |
| unsafe fn encode(self, encoder: &mut Encoder<'_>, offset: usize, depth: Depth) -> Result<()> { |
| encoder.debug_check_bounds::<Vector<T, N>>(offset); |
| encode_vector_resource::<T>(self, N, encoder, offset, depth) |
| } |
| } |
| |
| impl<T: TypeMarker, const N: usize> Decode<Vector<T, N>> for Vec<T::Owned> { |
| #[inline(always)] |
| fn new_empty() -> Self { |
| Vec::new() |
| } |
| |
| #[inline] |
| unsafe fn decode( |
| &mut self, |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| depth: Depth, |
| ) -> Result<()> { |
| decoder.debug_check_bounds::<Vector<T, N>>(offset); |
| decode_vector::<T>(self, N, decoder, offset, depth) |
| } |
| } |
| |
| #[inline] |
| unsafe fn encode_vector_value<T: ValueTypeMarker>( |
| slice: &[T::Owned], |
| max_length: usize, |
| check_length: impl Fn(usize, usize) -> Result<()>, |
| encoder: &mut Encoder<'_>, |
| offset: usize, |
| mut depth: Depth, |
| ) -> Result<()> { |
| encoder.write_num(slice.len() as u64, offset); |
| encoder.write_num(ALLOC_PRESENT_U64, offset + 8); |
| // Calling encoder.out_of_line_offset(0) is not allowed. |
| if slice.is_empty() { |
| return Ok(()); |
| } |
| check_length(slice.len(), max_length)?; |
| depth.increment()?; |
| let bytes_len = slice.len() * T::inline_size(encoder.context); |
| let offset = encoder.out_of_line_offset(bytes_len); |
| encode_array_value::<T>(slice, encoder, offset, depth) |
| } |
| |
| #[inline] |
| unsafe fn encode_vector_resource<T: ResourceTypeMarker>( |
| slice: &mut [T::Owned], |
| max_length: usize, |
| encoder: &mut Encoder<'_>, |
| offset: usize, |
| mut depth: Depth, |
| ) -> Result<()> { |
| encoder.write_num(slice.len() as u64, offset); |
| encoder.write_num(ALLOC_PRESENT_U64, offset + 8); |
| // Calling encoder.out_of_line_offset(0) is not allowed. |
| if slice.is_empty() { |
| return Ok(()); |
| } |
| check_vector_length(slice.len(), max_length)?; |
| depth.increment()?; |
| let bytes_len = slice.len() * T::inline_size(encoder.context); |
| let offset = encoder.out_of_line_offset(bytes_len); |
| encode_array_resource::<T>(slice, encoder, offset, depth) |
| } |
| |
| #[inline] |
| unsafe fn decode_vector<T: TypeMarker>( |
| vec: &mut Vec<T::Owned>, |
| max_length: usize, |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| mut depth: Depth, |
| ) -> Result<()> { |
| let Some(len) = decode_vector_header(decoder, offset)? else { |
| return Err(Error::NotNullable); |
| }; |
| // Calling decoder.out_of_line_offset(0) is not allowed. |
| if len == 0 { |
| return Ok(()); |
| } |
| check_vector_length(len, max_length)?; |
| depth.increment()?; |
| let bytes_len = len * T::inline_size(decoder.context); |
| let offset = decoder.out_of_line_offset(bytes_len)?; |
| if T::decode_is_copy() { |
| // Safety: The uninitialized elements are immediately written by |
| // `decode_array`, which always succeeds in the simple copy case. |
| unsafe { |
| resize_vec_no_zeroing(vec, len); |
| } |
| } else { |
| vec.resize_with(len, T::Owned::new_empty); |
| } |
| // Safety: `vec` has `len` elements based on the above code. |
| decode_array::<T>(vec, decoder, offset, depth)?; |
| Ok(()) |
| } |
| |
| /// Decodes and validates a 16-byte vector header. Returns `Some(len)` if |
| /// the vector is present (including empty vectors), otherwise `None`. |
| #[doc(hidden)] // only exported for use in macros or generated code |
| #[inline] |
| pub fn decode_vector_header(decoder: &mut Decoder<'_>, offset: usize) -> Result<Option<usize>> { |
| let len = decoder.read_num::<u64>(offset) as usize; |
| match decoder.read_num::<u64>(offset + 8) { |
| ALLOC_PRESENT_U64 => { |
| // Check that the length does not exceed `u32::MAX` (per RFC-0059) |
| // nor the total size of the message (to avoid a huge allocation |
| // when the message cannot possibly be valid). |
| if len <= u32::MAX as usize && len <= decoder.buf.len() { |
| Ok(Some(len)) |
| } else { |
| Err(Error::OutOfRange) |
| } |
| } |
| ALLOC_ABSENT_U64 => { |
| if len == 0 { |
| Ok(None) |
| } else { |
| Err(Error::UnexpectedNullRef) |
| } |
| } |
| _ => Err(Error::InvalidPresenceIndicator), |
| } |
| } |
| |
| #[inline(always)] |
| fn check_vector_length(actual_length: usize, max_length: usize) -> Result<()> { |
| if actual_length > max_length { |
| return Err(Error::VectorTooLong { max_length, actual_length }); |
| } |
| Ok(()) |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Strings |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /// The FIDL type `string:N`. |
| pub struct BoundedString<const N: usize>; |
| |
| /// The FIDL type `string` or `string:MAX`. |
| pub type UnboundedString = BoundedString<MAX_BOUND>; |
| |
| unsafe impl<const N: usize> TypeMarker for BoundedString<N> { |
| type Owned = String; |
| |
| #[inline(always)] |
| fn inline_align(_context: Context) -> usize { |
| 8 |
| } |
| |
| #[inline(always)] |
| fn inline_size(_context: Context) -> usize { |
| 16 |
| } |
| } |
| |
| impl<const N: usize> ValueTypeMarker for BoundedString<N> { |
| type Borrowed<'a> = &'a str; |
| #[inline(always)] |
| fn borrow(value: &Self::Owned) -> Self::Borrowed<'_> { |
| value |
| } |
| } |
| |
| unsafe impl<'a, const N: usize> Encode<BoundedString<N>> for &'a str { |
| #[inline] |
| unsafe fn encode(self, encoder: &mut Encoder<'_>, offset: usize, depth: Depth) -> Result<()> { |
| encoder.debug_check_bounds::<BoundedString<N>>(offset); |
| encode_vector_value::<u8>(self.as_bytes(), N, check_string_length, encoder, offset, depth) |
| } |
| } |
| |
| impl<const N: usize> Decode<BoundedString<N>> for String { |
| #[inline(always)] |
| fn new_empty() -> Self { |
| String::new() |
| } |
| |
| #[inline] |
| unsafe fn decode( |
| &mut self, |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| depth: Depth, |
| ) -> Result<()> { |
| decoder.debug_check_bounds::<BoundedString<N>>(offset); |
| decode_string(self, N, decoder, offset, depth) |
| } |
| } |
| |
| #[inline] |
| fn decode_string( |
| string: &mut String, |
| max_length: usize, |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| mut depth: Depth, |
| ) -> Result<()> { |
| let Some(len) = decode_vector_header(decoder, offset)? else { |
| return Err(Error::NotNullable); |
| }; |
| // Calling decoder.out_of_line_offset(0) is not allowed. |
| if len == 0 { |
| return Ok(()); |
| } |
| check_string_length(len, max_length)?; |
| depth.increment()?; |
| // Safety: we return early above if `len == 0`. |
| let offset = unsafe { decoder.out_of_line_offset(len)? }; |
| // Safety: `out_of_line_offset` does this bounds check. |
| let bytes = unsafe { &decoder.buf.get_unchecked(offset..offset + len) }; |
| let utf8 = str::from_utf8(bytes).map_err(|_| Error::Utf8Error)?; |
| let boxed_utf8: Box<str> = utf8.into(); |
| *string = boxed_utf8.into_string(); |
| Ok(()) |
| } |
| |
| #[inline(always)] |
| fn check_string_length(actual_bytes: usize, max_bytes: usize) -> Result<()> { |
| if actual_bytes > max_bytes { |
| return Err(Error::StringTooLong { max_bytes, actual_bytes }); |
| } |
| Ok(()) |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Handles |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /// The FIDL type `zx.Handle:<OBJECT_TYPE, RIGHTS>`, or a `client_end` or `server_end`. |
| pub struct HandleType<T: HandleBased, const OBJECT_TYPE: u32, const RIGHTS: u32>(PhantomData<T>); |
| |
| /// An abbreviation of `HandleType` that for channels with default rights, used |
| /// for the FIDL types `client_end:P` and `server_end:P`. |
| pub type Endpoint<T> = HandleType< |
| T, |
| { crate::ObjectType::CHANNEL.into_raw() }, |
| { crate::Rights::CHANNEL_DEFAULT.bits() }, |
| >; |
| |
| unsafe impl<T: 'static + HandleBased, const OBJECT_TYPE: u32, const RIGHTS: u32> TypeMarker |
| for HandleType<T, OBJECT_TYPE, RIGHTS> |
| { |
| type Owned = T; |
| |
| #[inline(always)] |
| fn inline_align(_context: Context) -> usize { |
| 4 |
| } |
| |
| #[inline(always)] |
| fn inline_size(_context: Context) -> usize { |
| 4 |
| } |
| } |
| |
| impl<T: 'static + HandleBased, const OBJECT_TYPE: u32, const RIGHTS: u32> ResourceTypeMarker |
| for HandleType<T, OBJECT_TYPE, RIGHTS> |
| { |
| type Borrowed<'a> = T; |
| #[inline(always)] |
| fn take_or_borrow(value: &mut Self::Owned) -> Self::Borrowed<'_> { |
| mem::replace(value, Handle::invalid().into()) |
| } |
| } |
| |
| unsafe impl<T: 'static + HandleBased, const OBJECT_TYPE: u32, const RIGHTS: u32> |
| Encode<HandleType<T, OBJECT_TYPE, RIGHTS>> for T |
| { |
| #[inline] |
| unsafe fn encode(self, encoder: &mut Encoder<'_>, offset: usize, _depth: Depth) -> Result<()> { |
| encoder.debug_check_bounds::<HandleType<T, OBJECT_TYPE, RIGHTS>>(offset); |
| encode_handle( |
| self.into(), |
| ObjectType::from_raw(OBJECT_TYPE), |
| Rights::from_bits_retain(RIGHTS), |
| encoder, |
| offset, |
| ) |
| } |
| } |
| |
| impl<T: 'static + HandleBased, const OBJECT_TYPE: u32, const RIGHTS: u32> |
| Decode<HandleType<T, OBJECT_TYPE, RIGHTS>> for T |
| { |
| #[inline(always)] |
| fn new_empty() -> Self { |
| Handle::invalid().into() |
| } |
| |
| #[inline] |
| unsafe fn decode( |
| &mut self, |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| _depth: Depth, |
| ) -> Result<()> { |
| decoder.debug_check_bounds::<HandleType<T, OBJECT_TYPE, RIGHTS>>(offset); |
| *self = decode_handle( |
| ObjectType::from_raw(OBJECT_TYPE), |
| Rights::from_bits_retain(RIGHTS), |
| decoder, |
| offset, |
| )? |
| .into(); |
| Ok(()) |
| } |
| } |
| |
| #[inline] |
| unsafe fn encode_handle( |
| handle: Handle, |
| object_type: ObjectType, |
| rights: Rights, |
| encoder: &mut Encoder<'_>, |
| offset: usize, |
| ) -> Result<()> { |
| if handle.is_invalid() { |
| return Err(Error::NotNullable); |
| } |
| encoder.write_num(ALLOC_PRESENT_U32, offset); |
| encoder.handles.push(HandleDisposition { |
| handle_op: HandleOp::Move(handle), |
| object_type, |
| rights, |
| result: Status::OK, |
| }); |
| Ok(()) |
| } |
| |
| #[inline] |
| unsafe fn decode_handle( |
| object_type: ObjectType, |
| rights: Rights, |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| ) -> Result<Handle> { |
| match decoder.read_num::<u32>(offset) { |
| ALLOC_PRESENT_U32 => {} |
| ALLOC_ABSENT_U32 => return Err(Error::NotNullable), |
| _ => return Err(Error::InvalidPresenceIndicator), |
| } |
| decoder.take_next_handle(object_type, rights) |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Optionals |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /// The FIDL type `T:optional` where `T` is a vector, string, handle, or client/server end. |
| pub struct Optional<T: TypeMarker>(PhantomData<T>); |
| |
| /// The FIDL type `T:optional` where `T` is a union. |
| pub struct OptionalUnion<T: TypeMarker>(PhantomData<T>); |
| |
| /// The FIDL type `box<T>`. |
| pub struct Boxed<T: TypeMarker>(PhantomData<T>); |
| |
| unsafe impl<T: TypeMarker> TypeMarker for Optional<T> { |
| type Owned = Option<T::Owned>; |
| |
| #[inline(always)] |
| fn inline_align(context: Context) -> usize { |
| T::inline_align(context) |
| } |
| |
| #[inline(always)] |
| fn inline_size(context: Context) -> usize { |
| T::inline_size(context) |
| } |
| } |
| |
| unsafe impl<T: TypeMarker> TypeMarker for OptionalUnion<T> { |
| type Owned = Option<Box<T::Owned>>; |
| |
| #[inline(always)] |
| fn inline_align(context: Context) -> usize { |
| T::inline_align(context) |
| } |
| |
| #[inline(always)] |
| fn inline_size(context: Context) -> usize { |
| T::inline_size(context) |
| } |
| } |
| |
| unsafe impl<T: TypeMarker> TypeMarker for Boxed<T> { |
| type Owned = Option<Box<T::Owned>>; |
| |
| #[inline(always)] |
| fn inline_align(_context: Context) -> usize { |
| 8 |
| } |
| |
| #[inline(always)] |
| fn inline_size(_context: Context) -> usize { |
| 8 |
| } |
| } |
| |
| impl<T: ValueTypeMarker> ValueTypeMarker for Optional<T> { |
| type Borrowed<'a> = Option<T::Borrowed<'a>>; |
| #[inline(always)] |
| fn borrow(value: &Self::Owned) -> Self::Borrowed<'_> { |
| value.as_ref().map(T::borrow) |
| } |
| } |
| |
| impl<T: ValueTypeMarker> ValueTypeMarker for OptionalUnion<T> { |
| type Borrowed<'a> = Option<T::Borrowed<'a>>; |
| #[inline(always)] |
| fn borrow(value: &Self::Owned) -> Self::Borrowed<'_> { |
| value.as_deref().map(T::borrow) |
| } |
| } |
| |
| impl<T: ValueTypeMarker> ValueTypeMarker for Boxed<T> { |
| type Borrowed<'a> = Option<T::Borrowed<'a>>; |
| #[inline(always)] |
| fn borrow(value: &Self::Owned) -> Self::Borrowed<'_> { |
| value.as_deref().map(T::borrow) |
| } |
| } |
| |
| impl<T: ResourceTypeMarker> ResourceTypeMarker for Optional<T> { |
| type Borrowed<'a> = Option<T::Borrowed<'a>>; |
| #[inline(always)] |
| fn take_or_borrow(value: &mut Self::Owned) -> Self::Borrowed<'_> { |
| value.as_mut().map(T::take_or_borrow) |
| } |
| } |
| |
| impl<T: ResourceTypeMarker> ResourceTypeMarker for OptionalUnion<T> { |
| type Borrowed<'a> = Option<T::Borrowed<'a>>; |
| #[inline(always)] |
| fn take_or_borrow(value: &mut Self::Owned) -> Self::Borrowed<'_> { |
| value.as_deref_mut().map(T::take_or_borrow) |
| } |
| } |
| |
| impl<T: ResourceTypeMarker> ResourceTypeMarker for Boxed<T> { |
| type Borrowed<'a> = Option<T::Borrowed<'a>>; |
| #[inline(always)] |
| fn take_or_borrow(value: &mut Self::Owned) -> Self::Borrowed<'_> { |
| value.as_deref_mut().map(T::take_or_borrow) |
| } |
| } |
| |
| unsafe impl<T: TypeMarker, E: Encode<T>> Encode<Optional<T>> for Option<E> { |
| #[inline] |
| unsafe fn encode(self, encoder: &mut Encoder<'_>, offset: usize, depth: Depth) -> Result<()> { |
| encoder.debug_check_bounds::<Optional<T>>(offset); |
| encode_naturally_optional::<T, E>(self, encoder, offset, depth) |
| } |
| } |
| |
| unsafe impl<T: TypeMarker, E: Encode<T>> Encode<OptionalUnion<T>> for Option<E> { |
| #[inline] |
| unsafe fn encode(self, encoder: &mut Encoder<'_>, offset: usize, depth: Depth) -> Result<()> { |
| encoder.debug_check_bounds::<OptionalUnion<T>>(offset); |
| encode_naturally_optional::<T, E>(self, encoder, offset, depth) |
| } |
| } |
| |
| unsafe impl<T: TypeMarker, E: Encode<T>> Encode<Boxed<T>> for Option<E> { |
| #[inline] |
| unsafe fn encode( |
| self, |
| encoder: &mut Encoder<'_>, |
| offset: usize, |
| mut depth: Depth, |
| ) -> Result<()> { |
| encoder.debug_check_bounds::<Boxed<T>>(offset); |
| match self { |
| Some(val) => { |
| depth.increment()?; |
| encoder.write_num(ALLOC_PRESENT_U64, offset); |
| let offset = encoder.out_of_line_offset(T::inline_size(encoder.context)); |
| val.encode(encoder, offset, depth)?; |
| } |
| None => encoder.write_num(ALLOC_ABSENT_U64, offset), |
| } |
| Ok(()) |
| } |
| } |
| |
| impl<T: TypeMarker> Decode<Optional<T>> for Option<T::Owned> { |
| #[inline(always)] |
| fn new_empty() -> Self { |
| None |
| } |
| |
| #[inline] |
| unsafe fn decode( |
| &mut self, |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| depth: Depth, |
| ) -> Result<()> { |
| decoder.debug_check_bounds::<Optional<T>>(offset); |
| let inline_size = T::inline_size(decoder.context); |
| if check_for_presence(decoder, offset, inline_size) { |
| self.get_or_insert(T::Owned::new_empty()).decode(decoder, offset, depth) |
| } else { |
| *self = None; |
| decoder.check_padding(offset, inline_size)?; |
| Ok(()) |
| } |
| } |
| } |
| |
| impl<T: TypeMarker> Decode<OptionalUnion<T>> for Option<Box<T::Owned>> { |
| #[inline(always)] |
| fn new_empty() -> Self { |
| None |
| } |
| |
| #[inline] |
| unsafe fn decode( |
| &mut self, |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| depth: Depth, |
| ) -> Result<()> { |
| decoder.debug_check_bounds::<OptionalUnion<T>>(offset); |
| let inline_size = T::inline_size(decoder.context); |
| if check_for_presence(decoder, offset, inline_size) { |
| decode!( |
| T, |
| self.get_or_insert_with(|| Box::new(T::Owned::new_empty())), |
| decoder, |
| offset, |
| depth |
| ) |
| } else { |
| *self = None; |
| decoder.check_padding(offset, inline_size)?; |
| Ok(()) |
| } |
| } |
| } |
| |
| impl<T: TypeMarker> Decode<Boxed<T>> for Option<Box<T::Owned>> { |
| #[inline(always)] |
| fn new_empty() -> Self { |
| None |
| } |
| |
| #[inline] |
| unsafe fn decode( |
| &mut self, |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| mut depth: Depth, |
| ) -> Result<()> { |
| decoder.debug_check_bounds::<Boxed<T>>(offset); |
| match decoder.read_num::<u64>(offset) { |
| ALLOC_PRESENT_U64 => { |
| depth.increment()?; |
| let offset = decoder.out_of_line_offset(T::inline_size(decoder.context))?; |
| decode!( |
| T, |
| self.get_or_insert_with(|| Box::new(T::Owned::new_empty())), |
| decoder, |
| offset, |
| depth |
| )?; |
| Ok(()) |
| } |
| ALLOC_ABSENT_U64 => { |
| *self = None; |
| Ok(()) |
| } |
| _ => Err(Error::InvalidPresenceIndicator), |
| } |
| } |
| } |
| |
| /// Encodes a "naturally optional" value, i.e. one where absence is represented |
| /// by a run of 0x00 bytes matching the type's inline size. |
| #[inline] |
| unsafe fn encode_naturally_optional<T: TypeMarker, E: Encode<T>>( |
| value: Option<E>, |
| encoder: &mut Encoder<'_>, |
| offset: usize, |
| depth: Depth, |
| ) -> Result<()> { |
| match value { |
| Some(val) => val.encode(encoder, offset, depth)?, |
| None => encoder.padding(offset, T::inline_size(encoder.context)), |
| } |
| Ok(()) |
| } |
| |
| /// Presence indicators always include at least one non-zero byte, while absence |
| /// indicators should always be entirely zeros. Like `Decode::decode`, the |
| /// caller is responsible for bounds checks. |
| #[inline] |
| fn check_for_presence(decoder: &Decoder<'_>, offset: usize, inline_size: usize) -> bool { |
| debug_assert!(offset + inline_size <= decoder.buf.len()); |
| let range = unsafe { decoder.buf.get_unchecked(offset..offset + inline_size) }; |
| range.iter().any(|byte| *byte != 0) |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Envelopes |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| #[doc(hidden)] // only exported for use in macros or generated code |
| #[inline] |
| pub unsafe fn encode_in_envelope<T: TypeMarker>( |
| val: impl Encode<T>, |
| encoder: &mut Encoder<'_>, |
| offset: usize, |
| mut depth: Depth, |
| ) -> Result<()> { |
| depth.increment()?; |
| let bytes_before = encoder.buf.len(); |
| let handles_before = encoder.handles.len(); |
| let inline_size = T::inline_size(encoder.context); |
| if inline_size <= 4 { |
| // Zero out the 4 byte inlined region and set the flag at the same time. |
| encoder.write_num(1u64 << 48, offset); |
| val.encode(encoder, offset, depth)?; |
| let handles_written = (encoder.handles.len() - handles_before) as u16; |
| encoder.write_num(handles_written, offset + 4); |
| } else { |
| let out_of_line_offset = encoder.out_of_line_offset(inline_size); |
| val.encode(encoder, out_of_line_offset, depth)?; |
| let bytes_written = (encoder.buf.len() - bytes_before) as u32; |
| let handles_written = (encoder.handles.len() - handles_before) as u32; |
| debug_assert_eq!(bytes_written % 8, 0); |
| encoder.write_num(bytes_written, offset); |
| encoder.write_num(handles_written, offset + 4); |
| } |
| Ok(()) |
| } |
| |
| #[doc(hidden)] // only exported for use in macros or generated code |
| #[inline] |
| pub unsafe fn encode_in_envelope_optional<T: TypeMarker>( |
| val: Option<impl Encode<T>>, |
| encoder: &mut Encoder<'_>, |
| offset: usize, |
| depth: Depth, |
| ) -> Result<()> { |
| match val { |
| None => encoder.write_num(0u64, offset), |
| Some(val) => encode_in_envelope(val, encoder, offset, depth)?, |
| } |
| Ok(()) |
| } |
| |
| /// Decodes and validates an envelope header. Returns `None` if absent and |
| /// `Some((inlined, num_bytes, num_handles))` if present. |
| #[doc(hidden)] // only exported for use in macros or generated code |
| #[inline(always)] |
| pub unsafe fn decode_envelope_header( |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| ) -> Result<Option<(bool, u32, u32)>> { |
| let num_bytes = decoder.read_num::<u32>(offset); |
| let num_handles = decoder.read_num::<u16>(offset + 4) as u32; |
| let inlined = decoder.read_num::<u16>(offset + 6); |
| match (num_bytes, num_handles, inlined) { |
| (0, 0, 0) => Ok(None), |
| (_, _, 1) => Ok(Some((true, 4, num_handles))), |
| (_, _, 0) if num_bytes % 8 == 0 => Ok(Some((false, num_bytes, num_handles))), |
| (_, _, 0) => Err(Error::InvalidNumBytesInEnvelope), |
| _ => Err(Error::InvalidInlineMarkerInEnvelope), |
| } |
| } |
| |
| /// Decodes a FIDL envelope and skips over any out-of-line bytes and handles. |
| #[doc(hidden)] // only exported for use in macros or generated code |
| #[inline] |
| pub unsafe fn decode_unknown_envelope( |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| mut depth: Depth, |
| ) -> Result<()> { |
| if let Some((inlined, num_bytes, num_handles)) = decode_envelope_header(decoder, offset)? { |
| if !inlined { |
| depth.increment()?; |
| // Calling decoder.out_of_line_offset(0) is not allowed. |
| if num_bytes != 0 { |
| let _ = decoder.out_of_line_offset(num_bytes as usize)?; |
| } |
| } |
| if num_handles != 0 { |
| for _ in 0..num_handles { |
| decoder.drop_next_handle()?; |
| } |
| } |
| } |
| Ok(()) |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Unions |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /// Decodes the inline portion of a union. |
| /// Returns `(ordinal, inlined, num_bytes, num_handles)`. |
| #[doc(hidden)] // only exported for use in macros or generated code |
| #[inline] |
| pub unsafe fn decode_union_inline_portion( |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| ) -> Result<(u64, bool, u32, u32)> { |
| let ordinal = decoder.read_num::<u64>(offset); |
| match decode_envelope_header(decoder, offset + 8)? { |
| Some((inlined, num_bytes, num_handles)) => Ok((ordinal, inlined, num_bytes, num_handles)), |
| None => Err(Error::NotNullable), |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Result unions |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /// The FIDL union generated for strict two-way methods with errors. |
| pub struct ResultType<T: TypeMarker, E: TypeMarker>(PhantomData<(T, E)>); |
| |
| /// The FIDL union generated for flexible two-way methods without errors. |
| pub struct FlexibleType<T: TypeMarker>(PhantomData<T>); |
| |
| /// The FIDL union generated for flexible two-way methods with errors. |
| pub struct FlexibleResultType<T: TypeMarker, E: TypeMarker>(PhantomData<(T, E)>); |
| |
| /// Owned type for `FlexibleType`. |
| #[doc(hidden)] // only exported for use in macros or generated code |
| #[derive(Debug)] |
| pub enum Flexible<T> { |
| Ok(T), |
| FrameworkErr(FrameworkErr), |
| } |
| |
| /// Owned type for `FlexibleResultType`. |
| #[doc(hidden)] // only exported for use in macros or generated code |
| #[derive(Debug)] |
| pub enum FlexibleResult<T, E> { |
| Ok(T), |
| DomainErr(E), |
| FrameworkErr(FrameworkErr), |
| } |
| |
| /// Internal FIDL framework error type used to identify unknown methods. |
| #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] |
| #[repr(i32)] |
| pub enum FrameworkErr { |
| /// Method was not recognized. |
| UnknownMethod = zx_types::ZX_ERR_NOT_SUPPORTED, |
| } |
| |
| impl FrameworkErr { |
| #[inline] |
| fn from_primitive(prim: i32) -> Option<Self> { |
| match prim { |
| zx_types::ZX_ERR_NOT_SUPPORTED => Some(Self::UnknownMethod), |
| _ => None, |
| } |
| } |
| |
| #[inline(always)] |
| const fn into_primitive(self) -> i32 { |
| self as i32 |
| } |
| } |
| |
| unsafe impl TypeMarker for FrameworkErr { |
| type Owned = Self; |
| |
| #[inline(always)] |
| fn inline_align(_context: Context) -> usize { |
| std::mem::align_of::<i32>() |
| } |
| |
| #[inline(always)] |
| fn inline_size(_context: Context) -> usize { |
| std::mem::size_of::<i32>() |
| } |
| |
| #[inline(always)] |
| fn encode_is_copy() -> bool { |
| true |
| } |
| |
| #[inline(always)] |
| fn decode_is_copy() -> bool { |
| false |
| } |
| } |
| |
| impl ValueTypeMarker for FrameworkErr { |
| type Borrowed<'a> = Self; |
| #[inline(always)] |
| fn borrow(value: &<Self as TypeMarker>::Owned) -> Self::Borrowed<'_> { |
| *value |
| } |
| } |
| |
| unsafe impl Encode<Self> for FrameworkErr { |
| #[inline] |
| unsafe fn encode(self, encoder: &mut Encoder<'_>, offset: usize, _depth: Depth) -> Result<()> { |
| encoder.debug_check_bounds::<Self>(offset); |
| encoder.write_num(self.into_primitive(), offset); |
| Ok(()) |
| } |
| } |
| |
| impl Decode<Self> for FrameworkErr { |
| #[inline(always)] |
| fn new_empty() -> Self { |
| Self::UnknownMethod |
| } |
| |
| #[inline] |
| unsafe fn decode( |
| &mut self, |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| _depth: Depth, |
| ) -> Result<()> { |
| decoder.debug_check_bounds::<Self>(offset); |
| let prim = decoder.read_num::<i32>(offset); |
| *self = Self::from_primitive(prim).ok_or(Error::InvalidEnumValue)?; |
| Ok(()) |
| } |
| } |
| |
| impl<T> Flexible<T> { |
| /// Creates a new instance from the underlying value. |
| pub fn new(value: T) -> Self { |
| Self::Ok(value) |
| } |
| |
| /// Converts to a `fidl::Result`, mapping framework errors to `fidl::Error`. |
| pub fn into_result<P: ProtocolMarker>(self, method_name: &'static str) -> Result<T> { |
| match self { |
| Flexible::Ok(ok) => Ok(ok), |
| Flexible::FrameworkErr(FrameworkErr::UnknownMethod) => { |
| Err(Error::UnsupportedMethod { method_name, protocol_name: P::DEBUG_NAME }) |
| } |
| } |
| } |
| } |
| |
| impl<T, E> FlexibleResult<T, E> { |
| /// Creates a new instance from an `std::result::Result`. |
| pub fn new(result: std::result::Result<T, E>) -> Self { |
| match result { |
| Ok(value) => Self::Ok(value), |
| Err(err) => Self::DomainErr(err), |
| } |
| } |
| |
| /// Converts to a `fidl::Result`, mapping framework errors to `fidl::Error`. |
| pub fn into_result<P: ProtocolMarker>( |
| self, |
| method_name: &'static str, |
| ) -> Result<std::result::Result<T, E>> { |
| match self { |
| FlexibleResult::Ok(ok) => Ok(Ok(ok)), |
| FlexibleResult::DomainErr(err) => Ok(Err(err)), |
| FlexibleResult::FrameworkErr(FrameworkErr::UnknownMethod) => { |
| Err(Error::UnsupportedMethod { method_name, protocol_name: P::DEBUG_NAME }) |
| } |
| } |
| } |
| } |
| |
| /// Implements `TypeMarker`, `Encode`, and `Decode` for a result union type. |
| macro_rules! impl_result_union { |
| ( |
| params: [$($encode_param:ident: Encode<$type_param:ident>),*], |
| ty: $ty:ty, |
| owned: $owned:ty, |
| encode: $encode:ty, |
| members: [$( |
| { |
| ctor: { $($member_ctor:tt)* }, |
| ty: $member_ty:ty, |
| ordinal: $member_ordinal:tt, |
| }, |
| )*] |
| ) => { |
| unsafe impl<$($type_param: TypeMarker),*> TypeMarker for $ty { |
| type Owned = $owned; |
| |
| #[inline(always)] |
| fn inline_align(_context: Context) -> usize { |
| 8 |
| } |
| |
| #[inline(always)] |
| fn inline_size(_context: Context) -> usize { |
| 16 |
| } |
| } |
| |
| unsafe impl<$($type_param: TypeMarker, $encode_param: Encode<$type_param>),*> Encode<$ty> for $encode { |
| #[inline] |
| unsafe fn encode(self, encoder: &mut Encoder<'_>, offset: usize, depth: Depth) -> Result<()> { |
| encoder.debug_check_bounds::<$ty>(offset); |
| match self { |
| $( |
| $($member_ctor)*(val) => { |
| encoder.write_num::<u64>($member_ordinal, offset); |
| encode_in_envelope::<$member_ty>(val, encoder, offset + 8, depth) |
| } |
| )* |
| } |
| } |
| } |
| |
| impl<$($type_param: TypeMarker),*> Decode<$ty> for $owned { |
| #[inline(always)] |
| fn new_empty() -> Self { |
| #![allow(unreachable_code)] |
| $( |
| return $($member_ctor)*(new_empty!($member_ty)); |
| )* |
| } |
| |
| #[inline] |
| unsafe fn decode(&mut self, decoder: &mut Decoder<'_>, offset: usize, mut depth: Depth) -> Result<()> { |
| decoder.debug_check_bounds::<$ty>(offset); |
| let next_out_of_line = decoder.next_out_of_line(); |
| let handles_before = decoder.remaining_handles(); |
| let (ordinal, inlined, num_bytes, num_handles) = decode_union_inline_portion(decoder, offset)?; |
| let member_inline_size = match ordinal { |
| $( |
| $member_ordinal => <$member_ty as TypeMarker>::inline_size(decoder.context), |
| )* |
| _ => return Err(Error::UnknownUnionTag), |
| }; |
| if inlined != (member_inline_size <= 4) { |
| return Err(Error::InvalidInlineBitInEnvelope); |
| } |
| let inner_offset; |
| if inlined { |
| decoder.check_inline_envelope_padding(offset + 8, member_inline_size)?; |
| inner_offset = offset + 8; |
| } else { |
| depth.increment()?; |
| inner_offset = decoder.out_of_line_offset(member_inline_size)?; |
| } |
| match ordinal { |
| $( |
| $member_ordinal => { |
| #[allow(irrefutable_let_patterns)] |
| if let $($member_ctor)*(_) = self { |
| // Do nothing, read the value into the object |
| } else { |
| // Initialize `self` to the right variant |
| *self = $($member_ctor)*(new_empty!($member_ty)); |
| } |
| #[allow(irrefutable_let_patterns)] |
| if let $($member_ctor)*(ref mut val) = self { |
| decode!($member_ty, val, decoder, inner_offset, depth)?; |
| } else { |
| unreachable!() |
| } |
| } |
| )* |
| ordinal => panic!("unexpected ordinal {:?}", ordinal) |
| } |
| if !inlined && decoder.next_out_of_line() != next_out_of_line + (num_bytes as usize) { |
| return Err(Error::InvalidNumBytesInEnvelope); |
| } |
| if handles_before != decoder.remaining_handles() + (num_handles as usize) { |
| return Err(Error::InvalidNumHandlesInEnvelope); |
| } |
| Ok(()) |
| } |
| } |
| }; |
| } |
| |
| impl_result_union! { |
| params: [X: Encode<T>, Y: Encode<E>], |
| ty: ResultType<T, E>, |
| owned: std::result::Result<T::Owned, E::Owned>, |
| encode: std::result::Result<X, Y>, |
| members: [ |
| { ctor: { Ok }, ty: T, ordinal: 1, }, |
| { ctor: { Err }, ty: E, ordinal: 2, }, |
| ] |
| } |
| |
| impl_result_union! { |
| params: [X: Encode<T>], |
| ty: FlexibleType<T>, |
| owned: Flexible<T::Owned>, |
| encode: Flexible<X>, |
| members: [ |
| { ctor: { Flexible::Ok }, ty: T, ordinal: 1, }, |
| { ctor: { Flexible::FrameworkErr }, ty: FrameworkErr, ordinal: 3, }, |
| ] |
| } |
| |
| impl_result_union! { |
| params: [X: Encode<T>, Y: Encode<E>], |
| ty: FlexibleResultType<T, E>, |
| owned: FlexibleResult<T::Owned, E::Owned>, |
| encode: FlexibleResult<X, Y>, |
| members: [ |
| { ctor: { FlexibleResult::Ok }, ty: T, ordinal: 1, }, |
| { ctor: { FlexibleResult::DomainErr }, ty: E, ordinal: 2, }, |
| { ctor: { FlexibleResult::FrameworkErr }, ty: FrameworkErr, ordinal: 3, }, |
| ] |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Epitaphs |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /// The body of a FIDL Epitaph |
| #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
| pub struct EpitaphBody { |
| /// The error status. |
| pub error: zx_status::Status, |
| } |
| |
| unsafe impl TypeMarker for EpitaphBody { |
| type Owned = Self; |
| |
| #[inline(always)] |
| fn inline_align(_context: Context) -> usize { |
| 4 |
| } |
| |
| #[inline(always)] |
| fn inline_size(_context: Context) -> usize { |
| 4 |
| } |
| } |
| |
| impl ValueTypeMarker for EpitaphBody { |
| type Borrowed<'a> = &'a Self; |
| fn borrow(value: &<Self as TypeMarker>::Owned) -> Self::Borrowed<'_> { |
| value |
| } |
| } |
| |
| unsafe impl Encode<EpitaphBody> for &EpitaphBody { |
| #[inline] |
| unsafe fn encode(self, encoder: &mut Encoder<'_>, offset: usize, _depth: Depth) -> Result<()> { |
| encoder.debug_check_bounds::<EpitaphBody>(offset); |
| encoder.write_num::<i32>(self.error.into_raw(), offset); |
| Ok(()) |
| } |
| } |
| |
| impl Decode<Self> for EpitaphBody { |
| #[inline(always)] |
| fn new_empty() -> Self { |
| Self { error: zx_status::Status::from_raw(0) } |
| } |
| |
| #[inline] |
| unsafe fn decode( |
| &mut self, |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| _depth: Depth, |
| ) -> Result<()> { |
| decoder.debug_check_bounds::<Self>(offset); |
| self.error = zx_status::Status::from_raw(decoder.read_num::<i32>(offset)); |
| Ok(()) |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Zircon types |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| unsafe impl TypeMarker for ObjectType { |
| type Owned = Self; |
| |
| #[inline(always)] |
| fn inline_align(_context: Context) -> usize { |
| mem::align_of::<Self>() |
| } |
| |
| #[inline(always)] |
| fn inline_size(_context: Context) -> usize { |
| mem::size_of::<Self>() |
| } |
| } |
| |
| impl ValueTypeMarker for ObjectType { |
| type Borrowed<'a> = Self; |
| fn borrow(value: &<Self as TypeMarker>::Owned) -> Self::Borrowed<'_> { |
| *value |
| } |
| } |
| |
| unsafe impl Encode<ObjectType> for ObjectType { |
| #[inline] |
| unsafe fn encode(self, encoder: &mut Encoder<'_>, offset: usize, _depth: Depth) -> Result<()> { |
| encoder.debug_check_bounds::<Self>(offset); |
| encoder.write_num(self.into_raw(), offset); |
| Ok(()) |
| } |
| } |
| |
| impl Decode<Self> for ObjectType { |
| #[inline(always)] |
| fn new_empty() -> Self { |
| ObjectType::NONE |
| } |
| |
| #[inline] |
| unsafe fn decode( |
| &mut self, |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| _depth: Depth, |
| ) -> Result<()> { |
| decoder.debug_check_bounds::<Self>(offset); |
| *self = Self::from_raw(decoder.read_num(offset)); |
| Ok(()) |
| } |
| } |
| |
| unsafe impl TypeMarker for Rights { |
| type Owned = Self; |
| |
| #[inline(always)] |
| fn inline_align(_context: Context) -> usize { |
| mem::align_of::<Self>() |
| } |
| |
| #[inline(always)] |
| fn inline_size(_context: Context) -> usize { |
| mem::size_of::<Self>() |
| } |
| } |
| |
| impl ValueTypeMarker for Rights { |
| type Borrowed<'a> = Self; |
| fn borrow(value: &<Self as TypeMarker>::Owned) -> Self::Borrowed<'_> { |
| *value |
| } |
| } |
| |
| unsafe impl Encode<Rights> for Rights { |
| #[inline] |
| unsafe fn encode(self, encoder: &mut Encoder<'_>, offset: usize, _depth: Depth) -> Result<()> { |
| encoder.debug_check_bounds::<Self>(offset); |
| if self.bits() & Self::all().bits() != self.bits() { |
| return Err(Error::InvalidBitsValue); |
| } |
| encoder.write_num(self.bits(), offset); |
| Ok(()) |
| } |
| } |
| |
| impl Decode<Self> for Rights { |
| #[inline(always)] |
| fn new_empty() -> Self { |
| Rights::empty() |
| } |
| |
| #[inline] |
| unsafe fn decode( |
| &mut self, |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| _depth: Depth, |
| ) -> Result<()> { |
| decoder.debug_check_bounds::<Self>(offset); |
| *self = Self::from_bits(decoder.read_num(offset)).ok_or(Error::InvalidBitsValue)?; |
| Ok(()) |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Messages |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /// The FIDL type for a message consisting of a header `H` and body `T`. |
| pub struct GenericMessageType<H: ValueTypeMarker, T: TypeMarker>(PhantomData<(H, T)>); |
| |
| /// A struct which encodes as `GenericMessageType<H, T>` where `E: Encode<T>`. |
| pub struct GenericMessage<H, E> { |
| /// Header of the message. |
| pub header: H, |
| /// Body of the message. |
| pub body: E, |
| } |
| |
| /// The owned type for `GenericMessageType` is uninhabited because we never |
| /// decode full messages. We decode the header and body separately, as we |
| /// usually we don't know the body's type until after we've decoded the header. |
| pub enum GenericMessageOwned {} |
| |
| unsafe impl<H: ValueTypeMarker, T: TypeMarker> TypeMarker for GenericMessageType<H, T> { |
| type Owned = GenericMessageOwned; |
| |
| #[inline(always)] |
| fn inline_align(context: Context) -> usize { |
| std::cmp::max(H::inline_align(context), T::inline_align(context)) |
| } |
| |
| #[inline(always)] |
| fn inline_size(context: Context) -> usize { |
| H::inline_size(context) + T::inline_size(context) |
| } |
| } |
| |
| unsafe impl<H: ValueTypeMarker, T: TypeMarker, E: Encode<T>> Encode<GenericMessageType<H, T>> |
| for GenericMessage<H::Owned, E> |
| { |
| #[inline] |
| unsafe fn encode(self, encoder: &mut Encoder<'_>, offset: usize, depth: Depth) -> Result<()> { |
| encoder.debug_check_bounds::<GenericMessageType<H, T>>(offset); |
| H::borrow(&self.header).encode(encoder, offset, depth)?; |
| self.body.encode(encoder, offset + H::inline_size(encoder.context), depth) |
| } |
| } |
| |
| impl<H: ValueTypeMarker, T: TypeMarker> Decode<GenericMessageType<H, T>> for GenericMessageOwned { |
| fn new_empty() -> Self { |
| panic!("cannot create GenericMessageOwned"); |
| } |
| |
| unsafe fn decode( |
| &mut self, |
| _decoder: &mut Decoder<'_>, |
| _offset: usize, |
| _depth: Depth, |
| ) -> Result<()> { |
| match *self {} |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Transaction messages |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /// The FIDL type for a transaction message with body `T`. |
| pub type TransactionMessageType<T> = GenericMessageType<TransactionHeader, T>; |
| |
| /// A struct which encodes as `TransactionMessageType<T>` where `E: Encode<T>`. |
| pub type TransactionMessage<E> = GenericMessage<TransactionHeader, E>; |
| |
| /// Header for transactional FIDL messages |
| #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
| #[repr(C)] |
| pub struct TransactionHeader { |
| /// Transaction ID which identifies a request-response pair |
| pub tx_id: u32, |
| /// Flags set for this message. MUST NOT be validated by bindings. Usually |
| /// temporarily during migrations. |
| pub at_rest_flags: [u8; 2], |
| /// Flags used for dynamically interpreting the request if it is unknown to |
| /// the receiver. |
| pub dynamic_flags: u8, |
| /// Magic number indicating the message's wire format. Two sides with |
| /// different magic numbers are incompatible with each other. |
| pub magic_number: u8, |
| /// Ordinal which identifies the FIDL method |
| pub ordinal: u64, |
| } |
| |
| impl TransactionHeader { |
| /// Returns whether the message containing this TransactionHeader is in a |
| /// compatible wire format |
| #[inline] |
| pub fn is_compatible(&self) -> bool { |
| self.magic_number == MAGIC_NUMBER_INITIAL |
| } |
| } |
| |
| bitflags! { |
| /// Bitflags type for transaction header at-rest flags. |
| #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] |
| pub struct AtRestFlags: u16 { |
| /// Indicates that the V2 wire format should be used instead of the V1 |
| /// wire format. |
| /// This includes the following RFCs: |
| /// - Efficient envelopes |
| /// - Inlining small values in FIDL envelopes |
| const USE_V2_WIRE_FORMAT = 2; |
| } |
| } |
| |
| bitflags! { |
| /// Bitflags type to flags that aid in dynamically identifying features of |
| /// the request. |
| #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] |
| pub struct DynamicFlags: u8 { |
| /// Indicates that the request is for a flexible method. |
| const FLEXIBLE = 1 << 7; |
| } |
| } |
| |
| impl From<AtRestFlags> for [u8; 2] { |
| #[inline] |
| fn from(value: AtRestFlags) -> Self { |
| value.bits().to_le_bytes() |
| } |
| } |
| |
| impl TransactionHeader { |
| /// Creates a new transaction header with the default encode context and magic number. |
| #[inline] |
| pub fn new(tx_id: u32, ordinal: u64, dynamic_flags: DynamicFlags) -> Self { |
| TransactionHeader::new_full( |
| tx_id, |
| ordinal, |
| default_encode_context(), |
| dynamic_flags, |
| MAGIC_NUMBER_INITIAL, |
| ) |
| } |
| |
| /// Creates a new transaction header with a specific context and magic number. |
| #[inline] |
| pub fn new_full( |
| tx_id: u32, |
| ordinal: u64, |
| context: Context, |
| dynamic_flags: DynamicFlags, |
| magic_number: u8, |
| ) -> Self { |
| TransactionHeader { |
| tx_id, |
| at_rest_flags: context.at_rest_flags().into(), |
| dynamic_flags: dynamic_flags.bits(), |
| magic_number, |
| ordinal, |
| } |
| } |
| |
| /// Returns true if the header is for an epitaph message. |
| #[inline] |
| pub fn is_epitaph(&self) -> bool { |
| self.ordinal == EPITAPH_ORDINAL |
| } |
| |
| /// Returns an error if this header has an incompatible wire format. |
| #[inline] |
| pub fn validate_wire_format(&self) -> Result<()> { |
| if self.magic_number != MAGIC_NUMBER_INITIAL { |
| return Err(Error::IncompatibleMagicNumber(self.magic_number)); |
| } |
| if !self.at_rest_flags().contains(AtRestFlags::USE_V2_WIRE_FORMAT) { |
| return Err(Error::UnsupportedWireFormatVersion); |
| } |
| Ok(()) |
| } |
| |
| /// Returns an error if this request header has an incorrect transaction id |
| /// for the given method type. |
| #[inline] |
| pub fn validate_request_tx_id(&self, method_type: MethodType) -> Result<()> { |
| match method_type { |
| MethodType::OneWay if self.tx_id != 0 => Err(Error::InvalidRequestTxid), |
| MethodType::TwoWay if self.tx_id == 0 => Err(Error::InvalidRequestTxid), |
| _ => Ok(()), |
| } |
| } |
| |
| /// Returns the header's migration flags as a `AtRestFlags` value. |
| #[inline] |
| pub fn at_rest_flags(&self) -> AtRestFlags { |
| AtRestFlags::from_bits_truncate(u16::from_le_bytes(self.at_rest_flags)) |
| } |
| |
| /// Returns the header's dynamic flags as a `DynamicFlags` value. |
| #[inline] |
| pub fn dynamic_flags(&self) -> DynamicFlags { |
| DynamicFlags::from_bits_truncate(self.dynamic_flags) |
| } |
| |
| /// Returns the context to use for decoding the message body associated with |
| /// this header. During migrations, this is dependent on `self.flags()` and |
| /// controls dynamic behavior in the read path. |
| #[inline] |
| pub fn decoding_context(&self) -> Context { |
| Context { wire_format_version: WireFormatVersion::V2 } |
| } |
| } |
| |
| /// Decodes the transaction header from a message. |
| /// Returns the header and a reference to the tail of the message. |
| pub fn decode_transaction_header(bytes: &[u8]) -> Result<(TransactionHeader, &[u8])> { |
| let mut header = new_empty!(TransactionHeader); |
| let context = Context { wire_format_version: WireFormatVersion::V2 }; |
| let header_len = <TransactionHeader as TypeMarker>::inline_size(context); |
| if bytes.len() < header_len { |
| return Err(Error::OutOfRange); |
| } |
| let (header_bytes, body_bytes) = bytes.split_at(header_len); |
| Decoder::decode_with_context::<TransactionHeader>(context, header_bytes, &mut [], &mut header) |
| .map_err(|_| Error::InvalidHeader)?; |
| header.validate_wire_format()?; |
| Ok((header, body_bytes)) |
| } |
| |
| unsafe impl TypeMarker for TransactionHeader { |
| type Owned = Self; |
| |
| #[inline(always)] |
| fn inline_align(_context: Context) -> usize { |
| 8 |
| } |
| |
| #[inline(always)] |
| fn inline_size(_context: Context) -> usize { |
| 16 |
| } |
| } |
| |
| impl ValueTypeMarker for TransactionHeader { |
| type Borrowed<'a> = &'a Self; |
| fn borrow(value: &<Self as TypeMarker>::Owned) -> Self::Borrowed<'_> { |
| value |
| } |
| } |
| |
| unsafe impl Encode<TransactionHeader> for &TransactionHeader { |
| #[inline] |
| unsafe fn encode(self, encoder: &mut Encoder<'_>, offset: usize, _depth: Depth) -> Result<()> { |
| encoder.debug_check_bounds::<TransactionHeader>(offset); |
| unsafe { |
| let buf_ptr = encoder.buf.as_mut_ptr().add(offset); |
| (buf_ptr as *mut TransactionHeader).write_unaligned(*self); |
| } |
| Ok(()) |
| } |
| } |
| |
| impl Decode<Self> for TransactionHeader { |
| #[inline(always)] |
| fn new_empty() -> Self { |
| Self { tx_id: 0, at_rest_flags: [0; 2], dynamic_flags: 0, magic_number: 0, ordinal: 0 } |
| } |
| |
| #[inline] |
| unsafe fn decode( |
| &mut self, |
| decoder: &mut Decoder<'_>, |
| offset: usize, |
| _depth: Depth, |
| ) -> Result<()> { |
| decoder.debug_check_bounds::<Self>(offset); |
| unsafe { |
| let buf_ptr = decoder.buf.as_ptr().add(offset); |
| let obj_ptr = self as *mut TransactionHeader; |
| std::ptr::copy_nonoverlapping(buf_ptr, obj_ptr as *mut u8, 16); |
| } |
| Ok(()) |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // TLS buffer |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| struct TlsBuf { |
| bytes: Vec<u8>, |
| encode_handles: Vec<HandleDisposition<'static>>, |
| decode_handles: Vec<HandleInfo>, |
| } |
| |
| impl TlsBuf { |
| fn new() -> TlsBuf { |
| TlsBuf { bytes: Vec::new(), encode_handles: Vec::new(), decode_handles: Vec::new() } |
| } |
| } |
| |
| thread_local!(static TLS_BUF: RefCell<TlsBuf> = RefCell::new(TlsBuf::new())); |
| |
| const MIN_TLS_BUF_BYTES_SIZE: usize = 512; |
| |
| /// Acquire a mutable reference to the thread-local buffers used for encoding. |
| /// |
| /// This function may not be called recursively. |
| #[inline] |
| pub fn with_tls_encode_buf<R>( |
| f: impl FnOnce(&mut Vec<u8>, &mut Vec<HandleDisposition<'static>>) -> R, |
| ) -> R { |
| TLS_BUF.with(|buf| { |
| let (mut bytes, mut handles) = |
| RefMut::map_split(buf.borrow_mut(), |b| (&mut b.bytes, &mut b.encode_handles)); |
| if bytes.capacity() == 0 { |
| bytes.reserve(MIN_TLS_BUF_BYTES_SIZE); |
| } |
| let res = f(&mut bytes, &mut handles); |
| bytes.clear(); |
| handles.clear(); |
| res |
| }) |
| } |
| |
| /// Acquire a mutable reference to the thread-local buffers used for decoding. |
| /// |
| /// This function may not be called recursively. |
| #[inline] |
| pub fn with_tls_decode_buf<R>(f: impl FnOnce(&mut Vec<u8>, &mut Vec<HandleInfo>) -> R) -> R { |
| TLS_BUF.with(|buf| { |
| let (mut bytes, mut handles) = |
| RefMut::map_split(buf.borrow_mut(), |b| (&mut b.bytes, &mut b.decode_handles)); |
| if bytes.capacity() == 0 { |
| bytes.reserve(MIN_TLS_BUF_BYTES_SIZE); |
| } |
| let res = f(&mut bytes, &mut handles); |
| bytes.clear(); |
| handles.clear(); |
| res |
| }) |
| } |
| |
| /// Encodes the provided type into the thread-local encoding buffers. |
| /// |
| /// This function may not be called recursively. |
| #[inline] |
| pub fn with_tls_encoded<T: TypeMarker, Out>( |
| val: impl Encode<T>, |
| f: impl FnOnce(&mut Vec<u8>, &mut Vec<HandleDisposition<'static>>) -> Result<Out>, |
| ) -> Result<Out> { |
| with_tls_encode_buf(|bytes, handles| { |
| Encoder::encode(bytes, handles, val)?; |
| f(bytes, handles) |
| }) |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Tests |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| #[cfg(test)] |
| mod test { |
| // Silence dead code errors from unused functions produced by macros like |
| // `fidl_bits!`, `fidl_union!`, etc. To the compiler, it's as if we defined |
| // a pub fn in a private mod and never used it. Unfortunately placing this |
| // attribute directly on the macro invocations does not work. |
| #![allow(dead_code)] |
| |
| use super::*; |
| use crate::handle::{convert_handle_dispositions_to_infos, AsHandleRef}; |
| use assert_matches::assert_matches; |
| use std::fmt; |
| |
| const CONTEXTS: [Context; 1] = [Context { wire_format_version: WireFormatVersion::V2 }]; |
| |
| const OBJECT_TYPE_NONE: u32 = crate::handle::ObjectType::NONE.into_raw(); |
| const SAME_RIGHTS: u32 = crate::handle::Rights::SAME_RIGHTS.bits(); |
| |
| #[track_caller] |
| fn to_infos(dispositions: &mut Vec<HandleDisposition<'_>>) -> Vec<HandleInfo> { |
| convert_handle_dispositions_to_infos(mem::take(dispositions)).unwrap() |
| } |
| |
| #[track_caller] |
| pub fn encode_decode<T: TypeMarker>(ctx: Context, start: impl Encode<T>) -> T::Owned { |
| let buf = &mut Vec::new(); |
| let handle_buf = &mut Vec::new(); |
| Encoder::encode_with_context::<T>(ctx, buf, handle_buf, start).expect("Encoding failed"); |
| let mut out = T::Owned::new_empty(); |
| Decoder::decode_with_context::<T>(ctx, buf, &mut to_infos(handle_buf), &mut out) |
| .expect("Decoding failed"); |
| out |
| } |
| |
| #[track_caller] |
| fn encode_assert_bytes<T: TypeMarker>( |
| ctx: Context, |
| data: impl Encode<T>, |
| encoded_bytes: &[u8], |
| ) { |
| let buf = &mut Vec::new(); |
| let handle_buf = &mut Vec::new(); |
| Encoder::encode_with_context::<T>(ctx, buf, handle_buf, data).expect("Encoding failed"); |
| assert_eq!(buf, encoded_bytes); |
| } |
| |
| #[track_caller] |
| fn identity<T>(data: &T::Owned) |
| where |
| T: ValueTypeMarker, |
| T::Owned: fmt::Debug + PartialEq, |
| { |
| for ctx in CONTEXTS { |
| assert_eq!(*data, encode_decode(ctx, T::borrow(data))); |
| } |
| } |
| |
| #[track_caller] |
| fn identities<T>(values: &[T::Owned]) |
| where |
| T: ValueTypeMarker, |
| T::Owned: fmt::Debug + PartialEq, |
| { |
| for value in values { |
| identity::<T>(value); |
| } |
| } |
| |
| #[test] |
| fn encode_decode_byte() { |
| identities::<u8>(&[0u8, 57u8, 255u8]); |
| identities::<i8>(&[0i8, -57i8, 12i8]); |
| identity::<Optional<Vector<i32, 3>>>(&None::<Vec<i32>>); |
| } |
| |
| #[test] |
| fn encode_decode_multibyte() { |
| identities::<u64>(&[0u64, 1u64, u64::MAX, u64::MIN]); |
| identities::<i64>(&[0i64, 1i64, i64::MAX, i64::MIN]); |
| identities::<f32>(&[0f32, 1f32, f32::MAX, f32::MIN]); |
| identities::<f64>(&[0f64, 1f64, f64::MAX, f64::MIN]); |
| } |
| |
| #[test] |
| fn encode_decode_nan() { |
| for ctx in CONTEXTS { |
| assert!(encode_decode::<f32>(ctx, f32::NAN).is_nan()); |
| assert!(encode_decode::<f64>(ctx, f64::NAN).is_nan()); |
| } |
| } |
| |
| #[test] |
| fn encode_decode_out_of_line() { |
| type V<T> = UnboundedVector<T>; |
| type S = UnboundedString; |
| type O<T> = Optional<T>; |
| |
| identity::<V<i32>>(&Vec::<i32>::new()); |
| identity::<V<i32>>(&vec![1, 2, 3]); |
| identity::<O<V<i32>>>(&None::<Vec<i32>>); |
| identity::<O<V<i32>>>(&Some(Vec::<i32>::new())); |
| identity::<O<V<i32>>>(&Some(vec![1, 2, 3])); |
| identity::<O<V<V<i32>>>>(&Some(vec![vec![1, 2, 3]])); |
| identity::<O<V<O<V<i32>>>>>(&Some(vec![Some(vec![1, 2, 3])])); |
| identity::<S>(&"".to_string()); |
| identity::<S>(&"foo".to_string()); |
| identity::<O<S>>(&None::<String>); |
| identity::<O<S>>(&Some("".to_string())); |
| identity::<O<S>>(&Some("foo".to_string())); |
| identity::<O<V<O<S>>>>(&Some(vec![None, Some("foo".to_string())])); |
| identity::<V<S>>(&vec!["foo".to_string(), "bar".to_string()]); |
| } |
| |
| #[test] |
| fn array_of_arrays() { |
| identity::<Array<Array<u32, 5>, 2>>(&[[1, 2, 3, 4, 5], [5, 4, 3, 2, 1]]); |
| } |
| |
| fn slice_identity<T>(start: &[T::Owned]) |
| where |
| T: ValueTypeMarker, |
| T::Owned: fmt::Debug + PartialEq, |
| { |
| for ctx in CONTEXTS { |
| let decoded = encode_decode::<UnboundedVector<T>>(ctx, start); |
| assert_eq!(start, UnboundedVector::<T>::borrow(&decoded)); |
| } |
| } |
| |
| #[test] |
| fn encode_slices_of_primitives() { |
| slice_identity::<u8>(&[]); |
| slice_identity::<u8>(&[0]); |
| slice_identity::<u8>(&[1, 2, 3, 4, 5, 255]); |
| |
| slice_identity::<i8>(&[]); |
| slice_identity::<i8>(&[0]); |
| slice_identity::<i8>(&[1, 2, 3, 4, 5, -128, 127]); |
| |
| slice_identity::<u64>(&[]); |
| slice_identity::<u64>(&[0]); |
| slice_identity::<u64>(&[1, 2, 3, 4, 5, u64::MAX]); |
| |
| slice_identity::<f32>(&[]); |
| slice_identity::<f32>(&[0.0]); |
| slice_identity::<f32>(&[1.0, 2.0, 3.0, 4.0, 5.0, f32::MIN, f32::MAX]); |
| |
| slice_identity::<f64>(&[]); |
| slice_identity::<f64>(&[0.0]); |
| slice_identity::<f64>(&[1.0, 2.0, 3.0, 4.0, 5.0, f64::MIN, f64::MAX]); |
| } |
| |
| #[test] |
| fn result_encode_empty_ok_value() { |
| for ctx in CONTEXTS { |
| // An empty response is represented by () and has zero size. |
| encode_assert_bytes::<EmptyPayload>(ctx, (), &[]); |
| } |
| // But in the context of an error result type Result<(), ErrorType>, the |
| // () in Ok(()) represents an empty struct (with size 1). |
| encode_assert_bytes::<ResultType<EmptyStruct, i32>>( |
| Context { wire_format_version: WireFormatVersion::V2 }, |
| Ok::<(), i32>(()), |
| &[ |
| 0x01, 0x00, 0x00, 0x00, // success ordinal |
| 0x00, 0x00, 0x00, 0x00, // success ordinal [cont.] |
| 0x00, 0x00, 0x00, 0x00, // inline value: empty struct + 3 bytes padding |
| 0x00, 0x00, 0x01, 0x00, // 0 handles, flags (inlined) |
| ], |
| ); |
| } |
| |
| #[test] |
| fn result_decode_empty_ok_value() { |
| let mut result = Err(0); |
| Decoder::decode_with_context::<ResultType<EmptyStruct, u32>>( |
| Context { wire_format_version: WireFormatVersion::V2 }, |
| &[ |
| 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // success ordinal |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, // empty struct inline |
| ], |
| &mut [], |
| &mut result, |
| ) |
| .expect("Decoding failed"); |
| assert_matches!(result, Ok(())); |
| } |
| |
| #[test] |
| fn encode_decode_result() { |
| type Res = ResultType<UnboundedString, u32>; |
| for ctx in CONTEXTS { |
| assert_eq!(encode_decode::<Res>(ctx, Ok::<&str, u32>("foo")), Ok("foo".to_string())); |
| assert_eq!(encode_decode::<Res>(ctx, Err::<&str, u32>(5)), Err(5)); |
| } |
| } |
| |
| #[test] |
| fn result_validates_num_bytes() { |
| type Res = ResultType<u64, u64>; |
| for ctx in CONTEXTS { |
| for ordinal in [1, 2] { |
| // Envelope should have num_bytes set to 8, not 16. |
| let bytes = [ |
| ordinal, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ordinal |
| 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 16 bytes, 0 handles |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // present |
| 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // number |
| ]; |
| let mut out = new_empty!(Res); |
| assert_matches!( |
| Decoder::decode_with_context::<Res>(ctx, &bytes, &mut [], &mut out), |
| Err(Error::InvalidNumBytesInEnvelope) |
| ); |
| } |
| } |
| } |
| |
| #[test] |
| fn result_validates_num_handles() { |
| type Res = ResultType<u64, u64>; |
| for ctx in CONTEXTS { |
| for ordinal in [1, 2] { |
| // Envelope should have num_handles set to 0, not 1. |
| let bytes = [ |
| ordinal, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ordinal |
| 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 16 bytes, 1 handle |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // present |
| 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // number |
| ]; |
| let mut out = new_empty!(Res); |
| assert_matches!( |
| Decoder::decode_with_context::<Res>(ctx, &bytes, &mut [], &mut out), |
| Err(Error::InvalidNumHandlesInEnvelope) |
| ); |
| } |
| } |
| } |
| |
| #[test] |
| fn decode_result_unknown_tag() { |
| type Res = ResultType<u32, u32>; |
| let ctx = Context { wire_format_version: WireFormatVersion::V2 }; |
| |
| let bytes: &[u8] = &[ |
| // Ordinal 3 (not known to result) ----------| |
| 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| // inline value -----| NHandles | Flags ---| |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, |
| ]; |
| let handle_buf = &mut Vec::<HandleInfo>::new(); |
| |
| let mut out = new_empty!(Res); |
| let res = Decoder::decode_with_context::<Res>(ctx, bytes, handle_buf, &mut out); |
| assert_matches!(res, Err(Error::UnknownUnionTag)); |
| } |
| |
| #[test] |
| fn decode_result_success_invalid_empty_struct() { |
| type Res = ResultType<EmptyStruct, u32>; |
| let ctx = Context { wire_format_version: WireFormatVersion::V2 }; |
| |
| let bytes: &[u8] = &[ |
| // Ordinal 1 (success) ----------------------| |
| 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| // inline value -----| NHandles | Flags ---| |
| 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, |
| ]; |
| let handle_buf = &mut Vec::<HandleInfo>::new(); |
| |
| let mut out = new_empty!(Res); |
| let res = Decoder::decode_with_context::<Res>(ctx, bytes, handle_buf, &mut out); |
| assert_matches!(res, Err(Error::Invalid)); |
| } |
| |
| #[test] |
| fn encode_decode_transaction_msg() { |
| for ctx in CONTEXTS { |
| let header = TransactionHeader { |
| tx_id: 4, |
| ordinal: 6, |
| at_rest_flags: [2, 0], |
| dynamic_flags: 0, |
| magic_number: 1, |
| }; |
| type Body = UnboundedString; |
| let body = "hello"; |
| |
| let start = TransactionMessage { header, body }; |
| |
| let buf = &mut Vec::new(); |
| let handle_buf = &mut Vec::new(); |
| Encoder::encode_with_context::<TransactionMessageType<Body>>( |
| ctx, buf, handle_buf, start, |
| ) |
| .expect("Encoding failed"); |
| |
| let (out_header, out_buf) = |
| decode_transaction_header(buf).expect("Decoding header failed"); |
| assert_eq!(header, out_header); |
| |
| let mut body_out = String::new(); |
| Decoder::decode_into::<Body>( |
| &header, |
| out_buf, |
| &mut to_infos(handle_buf), |
| &mut body_out, |
| ) |
| .expect("Decoding body failed"); |
| assert_eq!(body, body_out); |
| } |
| } |
| |
| #[test] |
| fn direct_encode_transaction_header_strict() { |
| let bytes = &[ |
| 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // |
| 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |
| ]; |
| let header = TransactionHeader { |
| tx_id: 4, |
| ordinal: 6, |
| at_rest_flags: [0; 2], |
| dynamic_flags: DynamicFlags::empty().bits(), |
| magic_number: 1, |
| }; |
| |
| for ctx in CONTEXTS { |
| encode_assert_bytes::<TransactionHeader>(ctx, &header, bytes); |
| } |
| } |
| |
| #[test] |
| fn direct_decode_transaction_header_strict() { |
| let bytes = &[ |
| 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // |
| 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |
| ]; |
| let header = TransactionHeader { |
| tx_id: 4, |
| ordinal: 6, |
| at_rest_flags: [0; 2], |
| dynamic_flags: DynamicFlags::empty().bits(), |
| magic_number: 1, |
| }; |
| |
| for ctx in CONTEXTS { |
| let mut out = new_empty!(TransactionHeader); |
| Decoder::decode_with_context::<TransactionHeader>(ctx, bytes, &mut [], &mut out) |
| .expect("Decoding failed"); |
| assert_eq!(out, header); |
| } |
| } |
| |
| #[test] |
| fn direct_encode_transaction_header_flexible() { |
| let bytes = &[ |
| 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, // |
| 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |
| ]; |
| let header = TransactionHeader { |
| tx_id: 4, |
| ordinal: 6, |
| at_rest_flags: [0; 2], |
| dynamic_flags: DynamicFlags::FLEXIBLE.bits(), |
| magic_number: 1, |
| }; |
| |
| for ctx in CONTEXTS { |
| encode_assert_bytes::<TransactionHeader>(ctx, &header, bytes); |
| } |
| } |
| |
| #[test] |
| fn direct_decode_transaction_header_flexible() { |
| let bytes = &[ |
| 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, // |
| 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |
| ]; |
| let header = TransactionHeader { |
| tx_id: 4, |
| ordinal: 6, |
| at_rest_flags: [0; 2], |
| dynamic_flags: DynamicFlags::FLEXIBLE.bits(), |
| magic_number: 1, |
| }; |
| |
| for ctx in CONTEXTS { |
| let mut out = new_empty!(TransactionHeader); |
| Decoder::decode_with_context::<TransactionHeader>(ctx, bytes, &mut [], &mut out) |
| .expect("Decoding failed"); |
| assert_eq!(out, header); |
| } |
| } |
| |
| #[test] |
| fn extra_data_is_disallowed() { |
| for ctx in CONTEXTS { |
| assert_matches!( |
| Decoder::decode_with_context::<EmptyPayload>(ctx, &[0], &mut [], &mut ()), |
| Err(Error::ExtraBytes) |
| ); |
| assert_matches!( |
| Decoder::decode_with_context::<EmptyPayload>( |
| ctx, |
| &[], |
| &mut [HandleInfo { |
| handle: Handle::invalid(), |
| object_type: ObjectType::NONE, |
| rights: Rights::NONE, |
| }], |
| &mut () |
| ), |
| Err(Error::ExtraHandles) |
| ); |
| } |
| } |
| |
| #[test] |
| fn encode_default_context() { |
| let buf = &mut Vec::new(); |
| Encoder::encode::<u8>(buf, &mut Vec::new(), 1u8).expect("Encoding failed"); |
| assert_eq!(buf, &[1u8, 0, 0, 0, 0, 0, 0, 0]); |
| } |
| |
| #[test] |
| fn encode_handle() { |
| type T = HandleType<Handle, OBJECT_TYPE_NONE, SAME_RIGHTS>; |
| for ctx in CONTEXTS { |
| let handle = crate::handle::Event::create().into_handle(); |
| let raw_handle = handle.raw_handle(); |
| let buf = &mut Vec::new(); |
| let handle_buf = &mut Vec::new(); |
| Encoder::encode_with_context::<T>(ctx, buf, handle_buf, handle) |
| .expect("Encoding failed"); |
| |
| assert_eq!(handle_buf.len(), 1); |
| assert_matches!(handle_buf[0].handle_op, HandleOp::Move(ref h) if h.raw_handle() == raw_handle); |
| |
| let mut handle_out = new_empty!(T); |
| Decoder::decode_with_context::<T>(ctx, buf, &mut to_infos(handle_buf), &mut handle_out) |
| .expect("Decoding failed"); |
| assert_eq!(handle_out.raw_handle(), raw_handle, "foobar"); |
| } |
| } |
| |
| #[test] |
| fn decode_too_few_handles() { |
| type T = HandleType<Handle, OBJECT_TYPE_NONE, SAME_RIGHTS>; |
| for ctx in CONTEXTS { |
| let bytes: &[u8] = &[0xff; 8]; |
| let handle_buf = &mut Vec::new(); |
| let mut handle_out = Handle::invalid(); |
| let res = Decoder::decode_with_context::<T>(ctx, bytes, handle_buf, &mut handle_out); |
| assert_matches!(res, Err(Error::OutOfRange)); |
| } |
| } |
| |
| #[test] |
| fn encode_epitaph() { |
| for ctx in CONTEXTS { |
| let buf = &mut Vec::new(); |
| let handle_buf = &mut Vec::new(); |
| Encoder::encode_with_context::<EpitaphBody>( |
| ctx, |
| buf, |
| handle_buf, |
| &EpitaphBody { error: zx_status::Status::UNAVAILABLE }, |
| ) |
| .expect("encoding failed"); |
| assert_eq!(buf, &[0xe4, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00]); |
| |
| let mut out = new_empty!(EpitaphBody); |
| Decoder::decode_with_context::<EpitaphBody>( |
| ctx, |
| buf, |
| &mut to_infos(handle_buf), |
| &mut out, |
| ) |
| .expect("Decoding failed"); |
| assert_eq!(EpitaphBody { error: zx_status::Status::UNAVAILABLE }, out); |
| } |
| } |
| } |