blob: 78f8d04f3c0416ead1d9e091ae3b81b68db17199 [file] [log] [blame]
// 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);
}
}
}