blob: 8424db41d52a91e7b8870a87381571222f67870e [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.
//! Encoding2 contains functions and traits for FIDL2 encoding and decoding.
use {
crate::{Error, Result},
byteorder::{ByteOrder, LittleEndian},
fuchsia_zircon::{self as zx, HandleBased},
std::{cell::RefCell, mem, ptr, str, u32, u64},
};
thread_local!(static CODING_BUF: RefCell<zx::MessageBuf> = RefCell::new(zx::MessageBuf::new()));
/// Acquire a mutable reference to the thread-local encoding buffers.
///
/// This function may not be called recursively.
pub fn with_tls_coding_bufs<R>(f: impl FnOnce(&mut Vec<u8>, &mut Vec<zx::Handle>) -> R) -> R {
CODING_BUF.with(|buf| {
let mut buf = buf.borrow_mut();
let (bytes, handles) = buf.split_mut();
let res = f(bytes, handles);
buf.clear();
res
})
}
/// Encodes the provided type into the thread-local encoding buffers.
///
/// This function may not be called recursively.
pub fn with_tls_encoded<T, E: From<Error>>(
val: &mut (impl Encodable + ?Sized),
f: impl FnOnce(&mut Vec<u8>, &mut Vec<zx::Handle>) -> std::result::Result<T, E>,
) -> std::result::Result<T, E> {
with_tls_coding_bufs(|bytes, handles| {
Encoder::encode(bytes, handles, val)?;
f(bytes, handles)
})
}
/// Rounds `x` up if necessary so that it is a multiple of `align`.
pub fn round_up_to_align(x: usize, align: usize) -> usize {
if align == 0 {
x
} else {
((x + align - 1) / align) * align
}
}
/// Split off the first element from a slice.
fn split_off_first<'a, T>(slice: &mut &'a [T]) -> Result<&'a T> {
split_off_front(slice, 1).map(|res| &res[0])
}
/// Split off the first element from a mutable slice.
fn split_off_first_mut<'a, T>(slice: &mut &'a mut [T]) -> Result<&'a mut T> {
split_off_front_mut(slice, 1).map(|res| &mut res[0])
}
/// Split of the first `n` bytes from `slice`.
fn split_off_front<'a, T>(slice: &mut &'a [T], n: usize) -> Result<&'a [T]> {
if n > slice.len() {
return Err(Error::OutOfRange);
}
let original = take_slice(slice);
let (head, tail) = original.split_at(n);
*slice = tail;
Ok(head)
}
/// Split of the first `n` mutable bytes from `slice`.
fn split_off_front_mut<'a, T>(slice: &mut &'a mut [T], n: usize) -> Result<&'a mut [T]> {
if n > slice.len() {
return Err(Error::OutOfRange);
}
let original = take_slice_mut(slice);
let (head, tail) = original.split_at_mut(n);
*slice = tail;
Ok(head)
}
/// Empty out a slice.
fn take_slice<'a, T>(x: &mut &'a [T]) -> &'a [T] {
mem::replace(x, &mut [])
}
/// Empty out a mutable slice.
fn take_slice_mut<'a, T>(x: &mut &'a mut [T]) -> &'a mut [T] {
mem::replace(x, &mut [])
}
#[doc(hidden)] // only exported for macro use
pub fn take_handle<T: HandleBased>(handle: &mut T) -> zx::Handle {
let invalid = T::from_handle(zx::Handle::invalid());
mem::replace(handle, invalid).into_handle()
}
/// The maximum recursion depth of encoding and decoding.
/// Each nested aggregate type (structs, unions, arrays, or vectors) counts as one step in the
/// recursion depth.
pub const MAX_RECURSION: usize = 32;
/// 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;
/// Encoding state
#[derive(Debug)]
pub struct Encoder<'a> {
/// Offset at which to write new objects.
offset: usize,
/// The maximum remaining number of recursive steps.
remaining_depth: usize,
/// Buffer to write output data into.
///
/// New chunks of out-of-line data should be appended to the end of the `Vec`.
/// `buf` should be resized to be large enough for any new data *prior* to encoding the inline
/// portion of that data.
buf: &'a mut Vec<u8>,
/// Buffer to write output handles into.
handles: &'a mut Vec<zx::Handle>,
}
/// Decoding state
#[derive(Debug)]
pub struct Decoder<'a> {
/// The maximum remaining number of recursive steps.
remaining_depth: usize,
/// Buffer from which to read data for the inline part of the message.
buf: &'a [u8],
/// Original length of the buf slice. Used to report error messages. A difference between this
/// value and buf.len() is the current decoding position for the inline part of the message.
initial_buf_len: usize,
/// Buffer from which to read out-of-line data.
out_of_line_buf: &'a [u8],
/// Original length of the out_of_line_buf slice. Used to report error messages. A difference
/// between this value and out_of_line_buf.len() is the current decoding position for th
/// out-of-line part of the message.
initial_out_of_line_buf_len: usize,
/// Buffer from which to read handles.
handles: &'a mut [zx::Handle],
}
impl<'a> Encoder<'a> {
/// FIDL2-encodes `x` into the provided data and handle buffers.
pub fn encode<T: Encodable + ?Sized>(
buf: &'a mut Vec<u8>,
handles: &'a mut Vec<zx::Handle>,
x: &mut T,
) -> Result<()> {
let inline_size = round_up_to_align(x.inline_size(), 8);
buf.truncate(0);
buf.resize(inline_size, 0);
handles.truncate(0);
let mut encoder = Encoder { offset: 0, remaining_depth: MAX_RECURSION, buf, handles };
x.encode(&mut encoder)
}
/// Runs the provided closure at at the next recursion depth level,
/// erroring if the maximum recursion limit has been reached.
pub fn recurse<F, R>(&mut self, f: F) -> Result<R>
where
F: FnOnce(&mut Encoder) -> Result<R>,
{
if self.remaining_depth == 0 {
return Err(Error::MaxRecursionDepth);
}
self.remaining_depth -= 1;
let res = f(self)?;
self.remaining_depth += 1;
Ok(res)
}
/// Returns a slice of the next `len` bytes after `offset` and increases `offset` by `len`.
pub fn next_slice(&mut self, len: usize) -> Result<&mut [u8]> {
let ret = self.buf.get_mut(self.offset..(self.offset + len)).ok_or(Error::OutOfRange)?;
self.offset += len;
Ok(ret)
}
/// Adds specified number of zero bytes as padding. Effectively, just increases `offset` by
/// `len`.
pub fn padding(&mut self, len: usize) -> Result<()> {
if self.offset + len > self.buf.len() {
return Err(Error::OutOfRange);
}
self.offset += len;
Ok(())
}
/// Adds as many zero bytes as padding as necessary to make sure that the `target` object size
/// is equal to `inline_size()`. `start_pos` is the position in the `encoder` buffer of where
/// the encoding started. See `Encoder::offset`.
pub fn tail_padding<Target>(&mut self, target: &mut Target, start_pos: usize) -> Result<()>
where
Target: Encodable
{
debug_assert!(start_pos <= self.offset);
self.padding(target.inline_size() - (self.offset - start_pos))
}
/// Runs the provided closure inside an encoder modified
/// to write the data out-of-line.
///
/// Once the closure has completed, this function resets the offset
/// to where it was at the beginning of the call.
pub fn write_out_of_line<F>(&mut self, len: usize, f: F) -> Result<()>
where
F: FnOnce(&mut Encoder) -> Result<()>,
{
let old_offset = self.offset;
self.offset = self.buf.len();
// Create space for the new data
self.buf.resize(self.offset + round_up_to_align(len, 8), 0);
f(self)?;
self.offset = old_offset;
Ok(())
}
/// Append bytes to the very end (out-of-line) of the buffer.
pub fn append_bytes(&mut self, bytes: &[u8]) {
let new_len = round_up_to_align(self.buf.len(), 8);
self.buf.resize(new_len, 0);
self.buf.extend_from_slice(bytes);
}
/// Append handles to the buffer.
pub fn append_handles(&mut self, handles: &mut [zx::Handle]) {
self.handles.reserve(handles.len());
for handle in handles {
self.handles.push(take_handle(handle));
}
}
}
impl<'a> Decoder<'a> {
/// FIDL2-decodes a value of type `T` from the provided data and handle buffers.
pub fn decode_into<T: Decodable>(
buf: &'a [u8],
handles: &'a mut [zx::Handle],
value: &mut T,
) -> Result<()> {
let out_of_line_offset = round_up_to_align(T::inline_size(), 8);
if buf.len() < out_of_line_offset {
return Err(Error::OutOfRange);
}
let (buf, out_of_line_buf) = buf.split_at(out_of_line_offset);
let mut decoder = Decoder {
remaining_depth: MAX_RECURSION,
buf,
initial_buf_len: buf.len(),
out_of_line_buf,
initial_out_of_line_buf_len: out_of_line_buf.len(),
handles,
};
value.decode(&mut decoder)?;
if decoder.out_of_line_buf.len() != 0 {
return Err(Error::ExtraBytes);
}
if decoder.handles.len() != 0 {
return Err(Error::ExtraHandles);
}
Ok(())
}
/// Runs the provided closure at at the next recursion depth level,
/// erroring if the maximum recursion limit has been reached.
pub fn recurse<F, R>(&mut self, f: F) -> Result<R>
where
F: FnOnce(&mut Decoder) -> Result<R>,
{
if self.remaining_depth == 0 {
return Err(Error::MaxRecursionDepth);
}
self.remaining_depth -= 1;
let res = f(self)?;
self.remaining_depth += 1;
Ok(res)
}
/// Runs the provided closure inside an decoder modified
/// to read out-of-line data.
///
/// `absolute_offset` indicates the offset of the start of the out-of-line data to read,
/// relative to the original start of the buffer.
pub fn read_out_of_line<F, R>(&mut self, len: usize, f: F) -> Result<R>
where
F: FnOnce(&mut Decoder) -> Result<R>,
{
// Currently, out-of-line points here:
// [---------------------------------]
// ^---buf--^ ^-out-of-line--^ (slices)
//
// We want to shift so that `buf` points to the first `len` bytes in `out-of-line` that
// are aligned to `align`, and `old-buf` points to the previous value of `buf`:
//
// [---------------------------------]
// ^old--buf^ ^--buf--^^ool^
//
// We also adjust the initial_buf_len, so that the index of an invalid byte can still be
// comupted using the same formula: initial_buf_len - buf.len().
// We split off the first `len` bytes from `out_of_line`.
let new_buf = split_off_front(&mut self.out_of_line_buf, len)?;
let new_initial_buf_len =
self.initial_buf_len + self.initial_out_of_line_buf_len - self.out_of_line_buf.len();
// Split off any trailing bytes up to the alignment and discard them.
if len % 8 != 0 {
let trailer = 8 - (len % 8);
let _ = split_off_front(&mut self.out_of_line_buf, trailer)?;
}
// Store the current `buf` slice and shift the `buf` slice to point at the out-of-line data.
let old_buf = take_slice(&mut self.buf);
let old_initial_buf_len = self.initial_buf_len;
self.buf = new_buf;
self.initial_buf_len = new_initial_buf_len;
let res = f(self);
// Set the current `buf` back to its original position.
//
// After this transformation, the final `Decoder` looks like this:
// [---------------------------------]
// ^---buf--^ ^ool^ (slices)
// ^out-of-line-advanced (index)
self.buf = old_buf;
self.initial_buf_len = old_initial_buf_len;
res
}
/// Whether or not the current section of inline bytes has been fully read.
pub fn is_empty(&self) -> bool {
self.buf.is_empty()
}
/// Current decoding position in the inline part of the message, counting from the original
/// first byte passed to `decode_into`.
pub fn inline_pos(&self) -> usize {
self.initial_buf_len - self.buf.len()
}
/// Current decoding position in the out-of-line part of the message, counting from the
/// original first byte passed to `decode_into`.
pub fn out_of_line_pos(&self) -> usize {
self.initial_out_of_line_buf_len - self.out_of_line_buf.len()
}
/// The number of out-of-line bytes not yet accounted for by a `read_out_of_line`
/// call.
pub fn remaining_out_of_line(&self) -> usize {
self.out_of_line_buf.len()
}
/// The number of handles that have not yet been consumed.
pub fn remaining_handles(&self) -> usize {
self.handles.len()
}
/// Returns a slice of the next `len` bytes to be decoded into and shifts the decoding buffer.
pub fn next_slice(&mut self, len: usize) -> Result<&[u8]> {
split_off_front(&mut self.buf, len)
}
/// Like `next_slice`, but doesn't remove the bytes from `Self`.
pub fn peek_slice(&mut self, len: usize) -> Result<&[u8]> {
self.buf.get(0..len).ok_or_else(|| std::process::abort())
}
/// Take the next handle from the `handles` list and shift the list down by one element.
pub fn take_handle(&mut self) -> Result<zx::Handle> {
split_off_first_mut(&mut self.handles).map(take_handle)
}
/// A convenience method to skip over the specified number of zero bytes used for padding, also
/// checking that all those bytes are in fact zeroes.
pub fn skip_padding(&mut self, len: usize) -> Result<()> {
let padding_start = self.inline_pos();
let padding = self.next_slice(len)?;
for i in 0..padding.len() {
if padding[i] != 0 {
return Err(Error::NonZeroPadding {
padding_start,
non_zero_pos: padding_start + i,
});
}
}
Ok(())
}
/// Skips padding at the end of the object, by decoding as many bytes as necessary to decode
/// `inline_size()` bytes starting at the `start_pos`. Uses `Decoder::skip_padding`, so will
/// check that all the skipped bytes are indeed zeroes.
pub fn skip_tail_padding<Target>(&mut self, _target: &Target, start_pos: usize) -> Result<()>
where
Target: Decodable + Sized,
{
debug_assert!(start_pos <= self.inline_pos());
self.skip_padding(Target::inline_size() - (self.inline_pos() - start_pos))
}
}
/// A type which can be FIDL2-encoded into a buffer.
pub trait Encodable {
/// Returns the minimum required alignment of the inline portion of the encoded object.
fn inline_align(&self) -> usize;
/// Returns the size of the inline portion of the encoded object.
fn inline_size(&self) -> usize;
/// Encode the object into the buffer.
/// Any handles stored in the object are swapped for `zx::Handle::INVALID`.
/// Calls to this function should ensure that `encoder.offset` is a multiple of `inline_size`.
/// Successful calls to this function should increase `encoder.offset` by `inline_size`.
fn encode(&mut self, encoder: &mut Encoder) -> Result<()>;
}
/// A type which can be FIDL2-decoded from a buffer.
pub trait Decodable {
/// Returns the minimum required alignment of the inline portion of the encoded object.
fn inline_align() -> usize
where
Self: Sized;
/// Returns the size of the inline portion of the encoded object.
fn inline_size() -> usize
where
Self: Sized;
/// Creates a new value of this type with an "empty" representation.
fn new_empty() -> Self
where
Self: Sized;
/// Decodes an object of this type from the provided buffer and list of handles.
/// On success, returns `Self`, as well as the yet-unused tails of the data and handle buffers.
fn decode(&mut self, decoder: &mut Decoder) -> Result<()>;
}
macro_rules! impl_codable_num { ($($prim_ty:ty => $reader:ident + $writer:ident,)*) => { $(
impl Encodable for $prim_ty {
fn inline_align(&self) -> usize { mem::size_of::<$prim_ty>() }
fn inline_size(&self) -> usize { mem::size_of::<$prim_ty>() }
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
let slot = encoder.next_slice(mem::size_of::<Self>())?;
LittleEndian::$writer(slot, *self);
Ok(())
}
}
impl Decodable for $prim_ty {
fn new_empty() -> Self { 0 as $prim_ty }
fn inline_size() -> usize { mem::size_of::<$prim_ty>() }
fn inline_align() -> usize { mem::size_of::<$prim_ty>() }
fn decode(&mut self, decoder: &mut Decoder) -> Result<()> {
let end = mem::size_of::<Self>();
let range = split_off_front(&mut decoder.buf, end)?;
*self = LittleEndian::$reader(range);
Ok(())
}
}
)* } }
impl_codable_num!(
u16 => read_u16 + write_u16,
u32 => read_u32 + write_u32,
u64 => read_u64 + write_u64,
i16 => read_i16 + write_i16,
i32 => read_i32 + write_i32,
i64 => read_i64 + write_i64,
f32 => read_f32 + write_f32,
f64 => read_f64 + write_f64,
);
impl Encodable for bool {
fn inline_align(&self) -> usize {
1
}
fn inline_size(&self) -> usize {
1
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
let slot = encoder.next_slice(1)?;
slot[0] = if *self { 1 } else { 0 };
Ok(())
}
}
impl Decodable for bool {
fn new_empty() -> Self {
false
}
fn inline_align() -> usize {
1
}
fn inline_size() -> usize {
1
}
fn decode(&mut self, decoder: &mut Decoder) -> Result<()> {
let num = *split_off_first(&mut decoder.buf)?;
*self = match num {
0 => false,
1 => true,
_ => return Err(Error::Invalid),
};
Ok(())
}
}
impl Encodable for u8 {
fn inline_align(&self) -> usize {
1
}
fn inline_size(&self) -> usize {
1
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
let slot = encoder.next_slice(1)?;
slot[0] = *self;
Ok(())
}
}
impl Decodable for u8 {
fn new_empty() -> Self {
0
}
fn inline_align() -> usize {
1
}
fn inline_size() -> usize {
1
}
fn decode(&mut self, decoder: &mut Decoder) -> Result<()> {
*self = *split_off_first(&mut decoder.buf)?;
Ok(())
}
}
impl Encodable for i8 {
fn inline_align(&self) -> usize {
1
}
fn inline_size(&self) -> usize {
1
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
let slot = encoder.next_slice(1)?;
slot[0] = *self as u8;
Ok(())
}
}
impl Decodable for i8 {
fn new_empty() -> Self {
0
}
fn inline_align() -> usize {
1
}
fn inline_size() -> usize {
1
}
fn decode(&mut self, decoder: &mut Decoder) -> Result<()> {
*self = *split_off_first(&mut decoder.buf)? as i8;
Ok(())
}
}
fn encode_array<T: Encodable>(encoder: &mut Encoder, slice: &mut [T]) -> Result<()> {
encoder.recurse(|encoder| {
for item in slice {
item.encode(encoder)?;
}
Ok(())
})
}
fn decode_array<T: Decodable>(decoder: &mut Decoder, slice: &mut [T]) -> Result<()> {
decoder.recurse(|decoder| {
for item in slice {
item.decode(decoder)?;
}
Ok(())
})
}
macro_rules! impl_codable_for_fixed_array { ($($len:expr,)*) => { $(
impl<T: Encodable> Encodable for [T; $len] {
fn inline_align(&self) -> usize {
self.get(0).map(Encodable::inline_align).unwrap_or(0)
}
fn inline_size(&self) -> usize {
self.get(0).map(Encodable::inline_size).unwrap_or(0) * $len
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
encode_array(encoder, self)
}
}
impl<T: Decodable> Decodable for [T; $len] {
fn new_empty() -> Self {
unsafe {
// We wrap the `arr` in a `ManuallyDrop` to prevent it from
// being dropped during a failure partway through initialization.
let mut arr: mem::ManuallyDrop<[T; $len]> = mem::uninitialized();
let arr_ptr: *mut T = arr.as_mut_ptr();
for i in 0..$len {
ptr::write(arr_ptr.offset(i as isize), T::new_empty());
}
mem::ManuallyDrop::into_inner(arr)
}
}
fn inline_align() -> usize { T::inline_align() }
fn inline_size() -> usize { T::inline_size() * $len }
fn decode(&mut self, decoder: &mut Decoder) -> Result<()> {
decode_array(decoder, self)
}
}
)* } }
// Unfortunately, we cannot be generic over the length of a fixed array
// even though its part of the type (this will hopefully be added in the
// future) so for now we implement encodable for only the first 33 fixed
// size array types.
#[rustfmt::skip]
impl_codable_for_fixed_array!( 0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32,);
// HAck for FIDL library fuchsia.sysmem
impl_codable_for_fixed_array!(64,);
// Hack for FIDL library fuchsia.net
impl_codable_for_fixed_array!(256,);
fn encode_byte_slice(encoder: &mut Encoder, slice_opt: Option<&[u8]>) -> Result<()> {
match slice_opt {
None => encode_absent_vector(encoder),
Some(slice) => {
// Two u64: (len, present)
(slice.len() as u64).encode(encoder)?;
ALLOC_PRESENT_U64.encode(encoder)?;
encoder.write_out_of_line(slice.len(), |encoder| {
let slot = encoder.next_slice(slice.len())?;
slot.copy_from_slice(slice);
Ok(())
})
}
}
}
/// Encode an missing vector-like component.
pub fn encode_absent_vector(encoder: &mut Encoder) -> Result<()> {
0u64.encode(encoder)?;
ALLOC_ABSENT_U64.encode(encoder)
}
/// Encode an optional iterator over encodable elements into a FIDL vector-like representation.
pub fn encode_encodable_iter<Iter, T>(encoder: &mut Encoder, iter_opt: Option<Iter>) -> Result<()>
where
Iter: ExactSizeIterator<Item = T>,
T: Encodable,
{
match iter_opt {
None => encode_absent_vector(encoder),
Some(mut iter) => {
// Two u64: (len, present)
(iter.len() as u64).encode(encoder)?;
ALLOC_PRESENT_U64.encode(encoder)?;
let mut first = if let Some(first) = iter.next() { first } else { return Ok(()) };
let bytes_len = (iter.len() + 1) * first.inline_size();
encoder.write_out_of_line(bytes_len, |encoder| {
encoder.recurse(|encoder| {
first.encode(encoder)?;
for mut item in iter {
item.encode(encoder)?;
}
Ok(())
})
})
}
}
}
/// Attempts to decode a string into `string`, returning a `bool`
/// indicating whether or not a string was present.
fn decode_string(decoder: &mut Decoder, string: &mut String) -> Result<bool> {
let mut len: u64 = 0;
len.decode(decoder)?;
let mut present: u64 = 0;
present.decode(decoder)?;
match present {
ALLOC_ABSENT_U64 => return Ok(false),
ALLOC_PRESENT_U64 => {}
_ => return Err(Error::Invalid),
};
let len = len as usize;
decoder.read_out_of_line(len, |decoder| {
string.truncate(0);
string.push_str(str::from_utf8(decoder.buf).map_err(|_| Error::Utf8Error)?);
Ok(true)
})
}
/// Attempts to decode a vec into `vec`, returning a `bool`
/// indicating whether or not a vec was present.
fn decode_vec<T: Decodable>(decoder: &mut Decoder, vec: &mut Vec<T>) -> Result<bool> {
let mut len: u64 = 0;
len.decode(decoder)?;
let mut present: u64 = 0;
present.decode(decoder)?;
match present {
ALLOC_ABSENT_U64 => return Ok(false),
ALLOC_PRESENT_U64 => {}
_ => return Err(Error::Invalid),
}
let len = len as usize;
let bytes_len = len * T::inline_size();
decoder.read_out_of_line(bytes_len, |decoder| {
decoder.recurse(|decoder| {
vec.truncate(0);
for _ in 0..len {
vec.push(T::new_empty());
// We just pushed an element on, so the `unwrap` will succeed.
vec.last_mut().unwrap().decode(decoder)?;
}
Ok(true)
})
})
}
impl<'a> Encodable for &'a str {
fn inline_align(&self) -> usize {
8
}
fn inline_size(&self) -> usize {
16
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
encode_byte_slice(encoder, Some(self.as_bytes()))
}
}
impl Encodable for String {
fn inline_align(&self) -> usize {
8
}
fn inline_size(&self) -> usize {
16
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
encode_byte_slice(encoder, Some(self.as_bytes()))
}
}
impl Decodable for String {
fn new_empty() -> Self {
String::new()
}
fn inline_align() -> usize {
8
}
fn inline_size() -> usize {
16
}
fn decode(&mut self, decoder: &mut Decoder) -> Result<()> {
if decode_string(decoder, self)? {
Ok(())
} else {
Err(Error::NotNullable)
}
}
}
impl<'a> Encodable for Option<&'a str> {
fn inline_align(&self) -> usize {
8
}
fn inline_size(&self) -> usize {
16
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
encode_byte_slice(encoder, self.as_ref().map(|x| x.as_bytes()))
}
}
impl Encodable for Option<String> {
fn inline_align(&self) -> usize {
8
}
fn inline_size(&self) -> usize {
16
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
encode_byte_slice(encoder, self.as_ref().map(|x| x.as_bytes()))
}
}
impl Decodable for Option<String> {
fn new_empty() -> Self {
None
}
fn inline_align() -> usize {
8
}
fn inline_size() -> usize {
16
}
fn decode(&mut self, decoder: &mut Decoder) -> Result<()> {
let was_some;
{
let string = self.get_or_insert(String::new());
was_some = decode_string(decoder, string)?;
}
if !was_some {
*self = None
}
Ok(())
}
}
impl<'a, 'b: 'a, T: Encodable> Encodable for &'a mut ExactSizeIterator<Item = T> {
fn inline_align(&self) -> usize {
8
}
fn inline_size(&self) -> usize {
16
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
encode_encodable_iter(encoder, Some(self))
}
}
impl<'a, T: Encodable> Encodable for &'a mut [T] {
fn inline_align(&self) -> usize {
8
}
fn inline_size(&self) -> usize {
16
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
encode_encodable_iter(encoder, Some(self.iter_mut()))
}
}
impl<T: Encodable> Encodable for Vec<T> {
fn inline_align(&self) -> usize {
8
}
fn inline_size(&self) -> usize {
16
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
encode_encodable_iter(encoder, Some(self.iter_mut()))
}
}
impl<T: Decodable> Decodable for Vec<T> {
fn new_empty() -> Self {
Vec::new()
}
fn inline_align() -> usize {
8
}
fn inline_size() -> usize {
16
}
fn decode(&mut self, decoder: &mut Decoder) -> Result<()> {
if decode_vec(decoder, self)? {
Ok(())
} else {
Err(Error::NotNullable)
}
}
}
impl<'a, 'b: 'a, T: Encodable> Encodable for Option<&'a mut ExactSizeIterator<Item = T>> {
fn inline_align(&self) -> usize {
8
}
fn inline_size(&self) -> usize {
16
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
encode_encodable_iter(encoder, self.as_mut().map(|x| &mut **x))
}
}
impl<'a, T: Encodable> Encodable for Option<&'a mut [T]> {
fn inline_align(&self) -> usize {
8
}
fn inline_size(&self) -> usize {
16
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
encode_encodable_iter(encoder, self.as_mut().map(|x| x.iter_mut()))
}
}
impl<T: Encodable> Encodable for Option<Vec<T>> {
fn inline_align(&self) -> usize {
8
}
fn inline_size(&self) -> usize {
16
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
encode_encodable_iter(encoder, self.as_mut().map(|x| x.iter_mut()))
}
}
impl<T: Decodable> Decodable for Option<Vec<T>> {
fn new_empty() -> Self {
None
}
fn inline_align() -> usize {
8
}
fn inline_size() -> usize {
16
}
fn decode(&mut self, decoder: &mut Decoder) -> Result<()> {
let was_some;
{
let vec = self.get_or_insert(Vec::new());
was_some = decode_vec(decoder, vec)?;
}
if !was_some {
*self = None
}
Ok(())
}
}
/// A shorthand macro for calling `Decodable::inline_size()` on a type
/// without importing the `Decodable` trait.
#[macro_export]
macro_rules! fidl_inline_size {
($type:ty) => {
<$type as $crate::encoding::Decodable>::inline_size()
};
}
/// A shorthand macro for calling `Decodable::inline_align()` on a type
/// without importing the `Decodable` trait.
#[macro_export]
macro_rules! fidl_inline_align {
($type:ty) => {
<$type as $crate::encoding::Decodable>::inline_align()
};
}
/// A shorthand macro for calling `Encodable::encode()` on a type
/// without importing the `Encodable` trait.
#[macro_export]
macro_rules! fidl_encode {
($val:expr, $encoder:expr) => {
$crate::encoding::Encodable::encode($val, $encoder)
};
}
/// A shorthand macro for calling `Decodable::decode()` on a type
/// without importing the `Decodable` trait.
#[macro_export]
macro_rules! fidl_decode {
($val:expr, $decoder:expr) => {
$crate::encoding::Decodable::decode($val, $decoder)
};
}
/// A shorthand macro for calling `Decodable::new_empty()` on a type
/// without importing the `Decodable` trait.
#[macro_export]
macro_rules! fidl_new_empty {
($type:ty) => {
<$type as $crate::encoding::Decodable>::new_empty()
};
}
/// Declare a bits type and implement the FIDL coding traits for it.
///
/// Example:
///
/// ```rust
/// fidl_bits!(MyBits (u32) { BAR = 5, BAZ = 6, });
///
/// // expands to:
///
/// bitflags! {
/// struct MyBits: u32 {
/// const BAR = 5;
/// const BAZ = 6;
/// }
/// }
///
/// impl Encodable for MyBits { ... }
/// impl Decodable for MyBits { ... }
/// ```
#[macro_export]
macro_rules! fidl_bits {
($name:ident ($prim_ty:ident) { $($key:ident = $value:expr,)* }) => {
$crate::bitflags! {
pub struct $name: $prim_ty {
$(
const $key = $value;
)*
}
}
impl $crate::encoding::Encodable for $name {
fn inline_align(&self) -> usize {
$crate::fidl_inline_align!($prim_ty)
}
fn inline_size(&self) -> usize {
$crate::fidl_inline_size!($prim_ty)
}
fn encode(&mut self, encoder: &mut $crate::encoding::Encoder)
-> ::std::result::Result<(), $crate::Error>
{
$crate::fidl_encode!(&mut self.bits, encoder)
}
}
impl $crate::encoding::Decodable for $name {
fn new_empty() -> Self {
Self::empty()
}
fn inline_align() -> usize {
$crate::fidl_inline_align!($prim_ty)
}
fn inline_size() -> usize {
$crate::fidl_inline_size!($prim_ty)
}
fn decode(&mut self, decoder: &mut $crate::encoding::Decoder)
-> ::std::result::Result<(), $crate::Error>
{
let mut prim = $crate::fidl_new_empty!($prim_ty);
$crate::fidl_decode!(&mut prim, decoder)?;
*self = Self::from_bits(prim).ok_or($crate::Error::Invalid)?;
Ok(())
}
}
}
}
/// Declare an enum type and implement the FIDL coding traits for it.
///
/// Example:
///
/// ```rust
/// fidl_enum!(MyEnum (u32) { BAR = 5, BAZ = 6, });
///
/// // expands to:
///
/// #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
/// #[repr($prim_ty)]
/// pub enum MyEnum {
/// BAR = 5,
/// BAZ = 6,
/// }
///
/// impl MyEnum {
/// pub fn from_primitive(prim: u32) -> Option<Self> { ... }
/// pub fn into_primitive(self) -> u32 { ... }
/// }
///
/// impl Encodable for MyEnum { ... }
/// impl Decodable for MyEnum { ... }
/// ```
#[macro_export]
macro_rules! fidl_enum {
($name:ident ($prim_ty:ident) { $($key:ident = $value:expr,)* }) => {
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr($prim_ty)]
pub enum $name {
$(
$key = $value,
)*
}
impl $name {
pub fn from_primitive(prim: $prim_ty) -> Option<Self> {
match prim {
$(
$value => Some($name::$key),
)*
_ => None,
}
}
pub fn into_primitive(self) -> $prim_ty {
self as $prim_ty
}
}
impl $crate::encoding::Encodable for $name {
fn inline_align(&self) -> usize {
$crate::fidl_inline_align!($prim_ty)
}
fn inline_size(&self) -> usize {
$crate::fidl_inline_size!($prim_ty)
}
fn encode(&mut self, encoder: &mut $crate::encoding::Encoder)
-> ::std::result::Result<(), $crate::Error>
{
$crate::fidl_encode!(&mut (*self as $prim_ty), encoder)
}
}
impl $crate::encoding::Decodable for $name {
fn new_empty() -> Self {
// Returns the first declared variant
#![allow(unreachable_code)]
$(
return $name::$key;
)*
panic!("new_empty called on enum with no variants")
}
fn inline_align() -> usize {
$crate::fidl_inline_align!($prim_ty)
}
fn inline_size() -> usize {
$crate::fidl_inline_size!($prim_ty)
}
fn decode(&mut self, decoder: &mut $crate::encoding::Decoder)
-> ::std::result::Result<(), $crate::Error>
{
let mut prim = $crate::fidl_new_empty!($prim_ty);
$crate::fidl_decode!(&mut prim, decoder)?;
*self = Self::from_primitive(prim).ok_or($crate::Error::Invalid)?;
Ok(())
}
}
}
}
impl Encodable for zx::Status {
fn inline_align(&self) -> usize {
mem::size_of::<zx::sys::zx_status_t>()
}
fn inline_size(&self) -> usize {
mem::size_of::<zx::sys::zx_status_t>()
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
let slot = encoder.next_slice(mem::size_of::<zx::sys::zx_status_t>())?;
LittleEndian::write_i32(slot, self.into_raw());
Ok(())
}
}
impl Decodable for zx::Status {
fn new_empty() -> Self {
Self::from_raw(0)
}
fn inline_size() -> usize {
mem::size_of::<zx::sys::zx_status_t>()
}
fn inline_align() -> usize {
mem::size_of::<zx::sys::zx_status_t>()
}
fn decode(&mut self, decoder: &mut Decoder) -> Result<()> {
let end = mem::size_of::<zx::sys::zx_status_t>();
let range = split_off_front(&mut decoder.buf, end)?;
*self = Self::from_raw(LittleEndian::read_i32(range));
Ok(())
}
}
impl Encodable for zx::Handle {
fn inline_align(&self) -> usize {
4
}
fn inline_size(&self) -> usize {
4
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
ALLOC_PRESENT_U32.encode(encoder)?;
let handle = take_handle(self);
encoder.handles.push(handle);
Ok(())
}
}
impl Decodable for zx::Handle {
fn new_empty() -> Self {
zx::Handle::invalid()
}
fn inline_align() -> usize {
4
}
fn inline_size() -> usize {
4
}
fn decode(&mut self, decoder: &mut Decoder) -> Result<()> {
let mut present: u32 = 0;
present.decode(decoder)?;
match present {
ALLOC_ABSENT_U32 => return Err(Error::NotNullable),
ALLOC_PRESENT_U32 => {}
_ => return Err(Error::Invalid),
}
*self = decoder.take_handle()?;
Ok(())
}
}
impl Encodable for Option<zx::Handle> {
fn inline_align(&self) -> usize {
4
}
fn inline_size(&self) -> usize {
4
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
match self {
Some(handle) => handle.encode(encoder),
None => ALLOC_ABSENT_U32.encode(encoder),
}
}
}
impl Decodable for Option<zx::Handle> {
fn new_empty() -> Self {
None
}
fn inline_align() -> usize {
4
}
fn inline_size() -> usize {
4
}
fn decode(&mut self, decoder: &mut Decoder) -> Result<()> {
let mut present: u32 = 0;
present.decode(decoder)?;
match present {
ALLOC_ABSENT_U32 => {
*self = None;
Ok(())
}
ALLOC_PRESENT_U32 => {
*self = Some(decoder.take_handle()?);
Ok(())
}
_ => Err(Error::Invalid),
}
}
}
/// A macro for implementing the `Encodable` and `Decodable` traits for a type
/// which implements the `fuchsia_zircon::HandleBased` trait.
// TODO(cramertj) replace when specialization is stable
#[macro_export]
macro_rules! handle_based_codable {
($($ty:ident$(:- <$($generic:ident,)*>)*, )*) => { $(
impl<$($($generic,)*)*> $crate::encoding::Encodable for $ty<$($($generic,)*)*> {
fn inline_align(&self) -> usize { 4 }
fn inline_size(&self) -> usize { 4 }
fn encode(&mut self, encoder: &mut $crate::encoding::Encoder)
-> $crate::Result<()>
{
let mut handle = $crate::encoding::take_handle(self);
$crate::fidl_encode!(&mut handle, encoder)
}
}
impl<$($($generic,)*)*> $crate::encoding::Decodable for $ty<$($($generic,)*)*> {
fn new_empty() -> Self {
<$ty<$($($generic,)*)*> as zx::HandleBased>::from_handle(zx::Handle::invalid())
}
fn inline_align() -> usize { 4 }
fn inline_size() -> usize { 4 }
fn decode(&mut self, decoder: &mut $crate::encoding::Decoder)
-> $crate::Result<()>
{
let mut handle = zx::Handle::invalid();
$crate::fidl_decode!(&mut handle, decoder)?;
*self = <$ty<$($($generic,)*)*> as zx::HandleBased>::from_handle(handle);
Ok(())
}
}
impl<$($($generic,)*)*> $crate::encoding::Encodable for Option<$ty<$($($generic,)*)*>> {
fn inline_align(&self) -> usize { 4 }
fn inline_size(&self) -> usize { 4 }
fn encode(&mut self, encoder: &mut $crate::encoding::Encoder)
-> $crate::Result<()>
{
match self {
Some(handle) => $crate::fidl_encode!(handle, encoder),
None => $crate::fidl_encode!(&mut $crate::encoding::ALLOC_ABSENT_U32, encoder),
}
}
}
impl<$($($generic,)*)*> $crate::encoding::Decodable for Option<$ty<$($($generic,)*)*>> {
fn new_empty() -> Self { None }
fn inline_align() -> usize { 4 }
fn inline_size() -> usize { 4 }
fn decode(&mut self, decoder: &mut $crate::encoding::Decoder) -> $crate::Result<()> {
let mut handle: Option<zx::Handle> = None;
$crate::fidl_decode!(&mut handle, decoder)?;
*self = handle.map(Into::into);
Ok(())
}
}
)* }
}
type ZxChannel = zx::Channel;
type ZxEvent = zx::Event;
type ZxEventPair = zx::EventPair;
type ZxFifo = zx::Fifo;
type ZxGuest = zx::Guest;
type ZxInterrupt = zx::Interrupt;
type ZxJob = zx::Job;
type ZxLog = zx::Log;
type ZxProcess = zx::Process;
type ZxResource = zx::Resource;
type ZxSocket = zx::Socket;
type ZxThread = zx::Thread;
type ZxTimer = zx::Timer;
type ZxPort = zx::Port;
type ZxVmar = zx::Vmar;
type ZxVmo = zx::Vmo;
handle_based_codable![
ZxChannel,
ZxEvent,
ZxEventPair,
ZxFifo,
ZxGuest,
ZxInterrupt,
ZxJob,
ZxLog,
ZxProcess,
ZxResource,
ZxSocket,
ZxThread,
ZxTimer,
ZxPort,
ZxVmar,
ZxVmo,
];
/// A trait that provides automatic `Encodable` and `Decodable`
/// implementations for a container that has inline data to decode,
/// and expects to find a presence indicator at the start of its
/// encoding/decoding.
///
/// Types that implement this trait will automatically receive
/// `Encodable` and `Decodable` implementations for `Option<Self>`
/// (rather than `Option<Box<Self>>` or `Option<OutOfLine<Self>>`).
pub trait AutonullContainer {
// FIXME(cramertj) dedup size and align in this file into an
// object-safe size and align trait and a non-object-safe size and align
// trait that can be reused, with a default impl of the former for implementors
// of the latter.
/// The inline size of the object.
fn inline_align() -> usize;
/// The out-of-line size of the object.
fn inline_size() -> usize;
}
/// A trait that provides automatic `Encodable` and `Decodable`
/// implementations for `Option<Box<Self>>` and `Option<OutOfLine<Self>>`.
pub trait Autonull: Encodable + Decodable {}
/// A wrapper for FIDL types that will cause them to be encoded
/// or decoded from the out-of-line buffer rather than inline.
pub struct OutOfLine<'a, T: 'a>(pub &'a mut T);
impl<T: AutonullContainer + Encodable> Encodable for Option<T> {
fn inline_align(&self) -> usize {
<T as AutonullContainer>::inline_align()
}
fn inline_size(&self) -> usize {
<T as AutonullContainer>::inline_size()
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
match self {
Some(x) => x.encode(encoder),
None => {
// `None` always corresponds to a full bout of inline zeros,
// aka `ALLOC_ABSENT_u64`, with an additional zero-length for
// out-of-line vectors and tables.
for byte in encoder.next_slice(<T as AutonullContainer>::inline_size())? {
*byte = 0;
}
Ok(())
}
}
}
}
impl<T: AutonullContainer + Decodable> Decodable for Option<T> {
fn inline_align() -> usize {
<T as Decodable>::inline_align()
}
fn inline_size() -> usize {
<T as Decodable>::inline_size()
}
fn new_empty() -> Self {
None
}
fn decode(&mut self, decoder: &mut Decoder) -> Result<()> {
let inline_size = <T as Decodable>::inline_size();
let mut present = false;
for byte in decoder.peek_slice(inline_size)? {
if *byte != 0 {
present = true;
break;
}
}
if present {
self.get_or_insert_with(|| T::new_empty()).decode(decoder)?;
Ok(())
} else {
*self = None;
// Eat the full `inline_size` bytes including the
// ALLOC_ABSENT that we only peeked at before
// TODO(FIDL-598) Switch to `skip_padding` when other bindings are ready.
decoder.next_slice(inline_size)?;
Ok(())
}
}
}
impl<'a, T: Autonull> AutonullContainer for OutOfLine<'a, T> {
fn inline_align() -> usize {
fidl_inline_align!(u64)
}
fn inline_size() -> usize {
fidl_inline_size!(u64)
}
}
impl<'a, T: Autonull> Encodable for OutOfLine<'a, T> {
fn inline_align(&self) -> usize {
fidl_inline_align!(u64)
}
fn inline_size(&self) -> usize {
fidl_inline_size!(u64)
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
ALLOC_PRESENT_U64.encode(encoder)?;
encoder.write_out_of_line(self.0.inline_size(), |encoder| self.0.encode(encoder))
}
}
impl<T: Autonull> AutonullContainer for Box<T> {
fn inline_align() -> usize {
fidl_inline_align!(u64)
}
fn inline_size() -> usize {
fidl_inline_size!(u64)
}
}
impl<T: Autonull> Encodable for Box<T> {
fn inline_align(&self) -> usize {
fidl_inline_align!(u64)
}
fn inline_size(&self) -> usize {
fidl_inline_size!(u64)
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
ALLOC_PRESENT_U64.encode(encoder)?;
encoder.write_out_of_line((&**self).inline_size(), |encoder| (&mut **self).encode(encoder))
}
}
impl<T: Autonull> Decodable for Box<T> {
fn inline_align() -> usize {
fidl_inline_align!(u64)
}
fn inline_size() -> usize {
fidl_inline_size!(u64)
}
fn new_empty() -> Self {
Box::new(T::new_empty())
}
fn decode(&mut self, decoder: &mut Decoder) -> Result<()> {
let mut present: u64 = 0;
fidl_decode!(&mut present, decoder)?;
if present != ALLOC_PRESENT_U64 {
return Err(Error::NotNullable);
}
return decoder.read_out_of_line(<T as Decodable>::inline_size(), |decoder| {
(&mut **self).decode(decoder)
});
}
}
/// A macro which implements the FIDL `Encodable` and `Decodable` traits
/// for an existing struct.
#[macro_export]
macro_rules! fidl_struct {
(
name: $name:ty,
members: [$(
$member_name:ident {
ty: $member_ty:ty,
offset: $member_offset:expr,
},
)*],
size: $size:expr,
align: $align:expr,
) => {
impl $crate::encoding::Encodable for $name {
fn inline_align(&self) -> usize {
$align
}
fn inline_size(&self) -> usize {
$size
}
fn encode(&mut self, encoder: &mut $crate::encoding::Encoder) -> $crate::Result<()> {
encoder.recurse(|encoder| {
let mut cur_offset = 0;
$(
// Skip to the start of the next field
encoder.padding($member_offset - cur_offset)?;
cur_offset = $member_offset;
$crate::fidl_encode!(&mut self.$member_name, encoder)?;
cur_offset += $crate::fidl_inline_size!($member_ty);
)*
// Skip to the end of the struct's size
encoder.padding($size - cur_offset)?;
Ok(())
})
}
}
impl $crate::encoding::Decodable for $name {
fn inline_align() -> usize {
$align
}
fn inline_size() -> usize {
$size
}
fn new_empty() -> Self {
Self {
$(
$member_name: $crate::fidl_new_empty!($member_ty),
)*
}
}
fn decode(&mut self, decoder: &mut $crate::encoding::Decoder) -> $crate::Result<()> {
decoder.recurse(|decoder| {
let mut cur_offset = 0;
$(
// Skip to the start of the next field
// TODO(FIDL-598) Switch to `skip_padding` when other bindings are ready.
decoder.next_slice($member_offset - cur_offset)?;
cur_offset = $member_offset;
$crate::fidl_decode!(&mut self.$member_name, decoder)?;
cur_offset += $crate::fidl_inline_size!($member_ty);
)*
// Skip to the end of the struct's size
// TODO(FIDL-598) Switch to `skip_padding` when other bindings are ready.
decoder.next_slice($size - cur_offset)?;
Ok(())
})
}
}
impl $crate::encoding::Autonull for $name {}
}
}
/// A macro which creates an empty struct and implements the FIDL `Encodable` and `Decodable`
/// traits for it.
#[macro_export]
macro_rules! fidl_empty_struct {
($(#[$attrs:meta])* $name:ident) => {
$(#[$attrs])*
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct $name;
impl $crate::encoding::Encodable for $name {
fn inline_align(&self) -> usize { 1 }
fn inline_size(&self) -> usize { 1 }
fn encode(&mut self, encoder: &mut $crate::encoding::Encoder) -> $crate::Result<()> {
$crate::fidl_encode!(&mut 0u8, encoder)
}
}
impl $crate::encoding::Decodable for $name {
fn inline_align() -> usize { 1 }
fn inline_size() -> usize { 1 }
fn new_empty() -> Self { $name }
fn decode(&mut self, decoder: &mut $crate::encoding::Decoder) -> $crate::Result<()> {
let mut x = 0u8;
$crate::fidl_decode!(&mut x, decoder)?;
if x == 0 {
Ok(())
} else {
Err($crate::Error::Invalid)
}
}
}
impl $crate::encoding::Autonull for $name {}
}
}
/// Encode the provided value behind a FIDL "envelope".
pub fn encode_in_envelope<T>(val: &mut Option<&mut T>, encoder: &mut Encoder) -> Result<()>
where
T: Encodable + ?Sized,
{
// u32 num_bytes
// u32 num_handles
// 64-bit presence indicator
// Record the offset of the number of bytes handles in the envelope,
// so that we can come back to it after writing the bytes and handles
// (until which point we don't know how many handles will be written).
let envelope_offset = encoder.offset;
0u32.encode(encoder)?; // num_bytes
0u32.encode(encoder)?; // num_handles
match val {
Some(x) => {
ALLOC_PRESENT_U64.encode(encoder)?;
let bytes_before = encoder.buf.len();
let handles_before = encoder.handles.len();
encoder.write_out_of_line(x.inline_size(), |e| x.encode(e))?;
let mut bytes_written = (encoder.buf.len() - bytes_before) as u32;
let mut handles_written = (encoder.handles.len() - handles_before) as u32;
// Back up and overwrite the `0s` for num_bytes and num_handles
let after_offset = encoder.offset;
encoder.offset = envelope_offset;
bytes_written.encode(encoder)?;
handles_written.encode(encoder)?;
encoder.offset = after_offset;
}
None => ALLOC_ABSENT_U64.encode(encoder)?,
}
Ok(())
}
/// A macro which implements the table empty constructor and the FIDL `Encodable` and `Decodable`
/// traits for an existing struct whose fields are all `Option`s and may or may not appear in the
/// wire-format representation.
#[macro_export]
macro_rules! fidl_table {
(
name: $name:ty,
members: {$(
// NOTE: members must be in order from lowest to highest ordinal
$member_name:ident {
ty: $member_ty:ty,
ordinal: $ordinal:expr,
},
)*},
) => {
impl $name {
fn empty() -> Self {
Self {$(
$member_name: None,
)*}
}
}
impl $crate::encoding::Encodable for $name {
fn inline_align(&self) -> usize { 8 }
fn inline_size(&self) -> usize { 16 }
fn encode(&mut self, encoder: &mut $crate::encoding::Encoder) -> $crate::Result<()> {
let members: &mut [(u64, Option<&mut $crate::encoding::Encodable>)] = &mut [$(
($ordinal, self.$member_name.as_mut().map(|x| x as &mut $crate::encoding::Encodable)),
)*];
// Cut off the `None` elements at the tail of the table
let last_some_index = members.iter().rposition(|x| x.1.is_some());
let members = if let Some(i) = last_some_index {
&mut members[..(i + 1)]
} else {
&mut []
};
// Vector header
let max_ordinal = members.last().map(|v| v.0).unwrap_or(0);
(max_ordinal as u64).encode(encoder)?;
$crate::encoding::ALLOC_PRESENT_U64.encode(encoder)?;
let bytes_len = (max_ordinal as usize) * 16; // 16 = ENVELOPE_INLINE_SIZE
encoder.write_out_of_line(bytes_len, |encoder| {
encoder.recurse(|encoder| {
let mut next_ordinal_to_write = 1;
for (ordinal, encodable) in members.iter_mut() {
let ordinal = *ordinal;
if ordinal < next_ordinal_to_write || ordinal > max_ordinal {
panic!("ordinals out of order in fidl_table! declaration");
}
while ordinal > next_ordinal_to_write {
// Fill in envelopes for missing ordinals.
$crate::encoding::encode_in_envelope::<()>(&mut None, encoder)?;
next_ordinal_to_write += 1;
}
$crate::encoding::encode_in_envelope(encodable, encoder)?;
next_ordinal_to_write += 1;
}
Ok(())
})
})
}
}
impl $crate::encoding::Decodable for $name {
fn inline_align() -> usize { 8 }
fn inline_size() -> usize { 16 }
fn new_empty() -> Self {
Self::empty()
}
fn decode(&mut self, decoder: &mut $crate::encoding::Decoder) -> $crate::Result<()> {
// Decode envelope vector header
let mut len: u64 = 0;
$crate::fidl_decode!(&mut len, decoder)?;
let mut present: u64 = 0;
$crate::fidl_decode!(&mut present, decoder)?;
if present != $crate::encoding::ALLOC_PRESENT_U64 {
return Err($crate::Error::Invalid);
}
let len = len as usize;
let bytes_len = len * 16; // envelope inline_size is 16
decoder.read_out_of_line(bytes_len, |decoder| {
// Decode the envelope for each type.
// u32 num_bytes
// u32_num_handles
// 64-bit presence indicator
$(
if decoder.is_empty() {
// The remaining fields have been omitted, so set them to None
self.$member_name = None;
} else {
let mut num_bytes: u32 = 0;
$crate::fidl_decode!(&mut num_bytes, decoder)?;
let mut num_handles: u32 = 0;
$crate::fidl_decode!(&mut num_handles, decoder)?;
let mut present: u64 = 0;
$crate::fidl_decode!(&mut present, decoder)?;
let bytes_before = decoder.remaining_out_of_line();
let handles_before = decoder.remaining_handles();
match present {
$crate::encoding::ALLOC_PRESENT_U64 => {
decoder.read_out_of_line(
$crate::fidl_inline_size!($member_ty),
|d| {
let val_ref =
self.$member_name.get_or_insert_with(
|| $crate::fidl_new_empty!($member_ty));
$crate::fidl_decode!(val_ref, d)?;
Ok(())
},
)?;
}
$crate::encoding::ALLOC_ABSENT_U64 => {
self.$member_name = None;
}
_ => return Err($crate::Error::Invalid),
}
if bytes_before != (decoder.remaining_out_of_line() + (num_bytes as usize)) {
return Err($crate::Error::Invalid);
}
if handles_before != (decoder.remaining_handles() + (num_handles as usize)) {
return Err($crate::Error::Invalid);
}
}
)*
// If there are any remaining non-empty envelopes,
// we error since the ordinal is unknown to these bindings.
//
// NOTE: this is the behavior discussed in previous FIDL
// team meetings and is consistent with the behavior on new
// extensible union variants and new interface methods.
// However, it means that receivers of tables must update
// to new generated bindings before they can receive
// messages containing new table fields.
while !decoder.is_empty() {
let mut num_bytes: u32 = 0;
$crate::fidl_decode!(&mut num_bytes, decoder)?;
let mut num_handles: u32 = 0;
$crate::fidl_decode!(&mut num_handles, decoder)?;
let mut present: u64 = 0;
$crate::fidl_decode!(&mut present, decoder)?;
if num_bytes != 0 ||
num_handles != 0 ||
present != $crate::encoding::ALLOC_ABSENT_U64
{
return Err($crate::Error::UnknownTableField);
}
}
Ok(())
})
}
}
impl $crate::encoding::AutonullContainer for $name {
fn inline_align() -> usize { 8 }
fn inline_size() -> usize { 16 }
}
}
}
impl<O, E> Encodable for std::result::Result<O, E>
where
O: Decodable + Encodable,
E: Decodable + Encodable,
{
fn inline_align(&self) -> usize {
// Alignment factor of union is defined by the maximal alignment factor of the tag field and any of its options.
// tag field will always be same size as E in the case of results
std::cmp::max(fidl_inline_align!(O), fidl_inline_align!(u32))
}
fn inline_size(&self) -> usize {
// Size of union is the size of the tag field plus the size of the largest option including padding necessary
// to satisfy its alignment requirements.
round_up_to_align(
std::cmp::max(fidl_inline_size!(O), fidl_inline_size!(E)) + fidl_inline_size!(u32),
fidl_inline_align!(Self),
)
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
let start_pos = encoder.offset;
match self {
Ok(val) => {
// Encode success tag
fidl_encode!(&mut 0, encoder)?;
// Padding
encoder.next_slice(fidl_inline_align!(Self) - 4)?;
// Encode success value
fidl_encode!(val, encoder)?;
// Ok() and Err() branches may be of a different size. We need to make sure we
// always encode inline_size() bytes.
encoder.tail_padding(self, start_pos)?;
}
Err(val) => {
// Encode Error tag
fidl_encode!(&mut 1, encoder)?;
// Padding
encoder.next_slice(fidl_inline_align!(Self) - 4)?;
// Encode Error value
fidl_encode!(val, encoder)?;
// Ok() and Err() branches may be of a different size. We need to make sure we
// always encode inline_size() bytes.
encoder.tail_padding(self, start_pos)?;
}
}
Ok(())
}
}
impl<O, E> Decodable for std::result::Result<O, E>
where
O: Decodable,
E: Decodable,
{
fn new_empty() -> Self {
return Ok(<O as Decodable>::new_empty());
}
fn inline_align() -> usize {
std::cmp::max(fidl_inline_align!(O), fidl_inline_align!(u32))
}
fn inline_size() -> usize {
round_up_to_align(
std::cmp::max(fidl_inline_size!(O), fidl_inline_size!(E)) + fidl_inline_size!(u32),
fidl_inline_align!(Self),
)
}
fn decode(&mut self, decoder: &mut Decoder) -> Result<()> {
let start_pos = decoder.inline_pos();
let mut tag: u32 = 0;
fidl_decode!(&mut tag, decoder)?;
// TODO(FIDL-598) Switch to `skip_padding` when other bindings are ready.
decoder.next_slice(fidl_inline_align!(Self) - 4)?;
match tag {
0 => {
loop {
match self {
Ok(val) => {
fidl_decode!(val, decoder)?;
decoder.skip_tail_padding(self, start_pos)?;
break;
}
Err(_) => {
// Only construct an empty instance of the nested type if we have an
// Err(T) value. Now `loop` to go into the first branch.
*self = Ok(<O as Decodable>::new_empty())
}
}
}
Ok(())
}
1 => {
loop {
match self {
Err(val) => {
fidl_decode!(val, decoder)?;
decoder.skip_tail_padding(self, start_pos)?;
break;
}
Ok(_) => {
// Only construct an empty instance of the nested type if we have an
// Ok(T) value. Now `loop` to go into the first branch.
*self = Err(<E as Decodable>::new_empty())
}
}
}
Ok(())
}
_ => Err(Error::UnknownUnionTag), // this would indicate it is not actually a result
}
}
}
/// A macro which declares a new FIDL union as a Rust enum and implements the
/// FIDL encoding and decoding traits for it.
#[macro_export]
macro_rules! fidl_union {
(
$(#[$attrs:meta])*
name: $name:ident,
members: [$(
$member_name:ident {
ty: $member_ty:ty,
offset: $member_offset:expr,
},
)*],
size: $size:expr,
align: $align:expr,
) => {
$( #[$attrs] )*
pub enum $name {
$(
$member_name ( $member_ty ),
)*
}
impl $name {
fn member_index(&self) -> u32 {
#![allow(unused)]
let mut index = 0;
// TODO(cramertj): switch to `if let` when irrefutable `if let` patterns
// stabilize
$(
match *self {
$name::$member_name(_) => return index,
_ => index += 1,
}
)*
panic!("unreachable union member")
}
}
impl $crate::encoding::Encodable for $name {
fn inline_align(&self) -> usize {
$align
}
fn inline_size(&self) -> usize {
$size
}
fn encode(&mut self, encoder: &mut $crate::encoding::Encoder) -> $crate::Result<()> {
let mut member_index = self.member_index();
// Encode tag
$crate::fidl_encode!(&mut member_index, encoder)?;
encoder.recurse(|encoder| {
match self { $(
$name::$member_name ( val ) => {
// Jump to offset minus 4-byte tag
encoder.padding($member_offset - 4)?;
// Encode value
$crate::fidl_encode!(val, encoder)?;
// Skip to the end of the union's size
encoder.padding($size - (
$crate::fidl_inline_size!($member_ty) + $member_offset
))?;
Ok(())
}
)* }
})
}
}
impl $crate::encoding::Decodable for $name {
fn inline_align() -> usize {
$align
}
fn inline_size() -> usize {
$size
}
fn new_empty() -> Self {
#![allow(unreachable_code)]
$(
return $name::$member_name($crate::fidl_new_empty!($member_ty));
)*
panic!("called new_empty on empty fidl union")
}
fn decode(&mut self, decoder: &mut $crate::encoding::Decoder) -> $crate::Result<()> {
#![allow(unused)]
let mut tag: u32 = 0;
$crate::fidl_decode!(&mut tag, decoder)?;
decoder.recurse(|decoder| {
let mut index = 0;
$(
if index == tag {
// Jump to offset minus 4-byte tag
// TODO(FIDL-598) Switch to `skip_padding` when other bindings are ready.
decoder.next_slice($member_offset - 4)?;
// Loop will only ever run once-- if the variant is not correct,
// it is fixed up.
loop {
match self {
$name::$member_name(val) => {
$crate::fidl_decode!(val, decoder)?;
break;
}
_ => {}
}
*self = $name::$member_name($crate::fidl_new_empty!($member_ty));
}
// Skip to the end of the union's size
// TODO(FIDL-598) Switch to `skip_padding` when other bindings are ready.
decoder.next_slice($size - ($crate::fidl_inline_size!($member_ty) + $member_offset))?;
return Ok(());
}
index += 1;
)*
Err($crate::Error::UnknownUnionTag)
})
}
}
impl $crate::encoding::Autonull for $name {}
}
}
/// A macro which declares a new FIDL xunion as a Rust enum and implements the
/// FIDL encoding and decoding traits for it.
#[macro_export]
macro_rules! fidl_xunion {
(
$(#[$attrs:meta])*
name: $name:ident,
members: [$(
$(#[$member_docs:meta])*
$member_name:ident {
ty: $member_ty:ty,
ordinal: $member_ordinal:expr,
},
)*],
) => {
$( #[$attrs] )*
pub enum $name {
$(
$(#[$member_docs])*
$member_name ( $member_ty ),
)*
#[doc(hidden)]
__UnknownVariant {
ordinal: u32,
bytes: Vec<u8>,
handles: Vec<zx::Handle>,
},
}
impl $name {
fn ordinal(&self) -> u32 {
match *self {
$(
$name::$member_name(_) => $member_ordinal,
)*
$name::__UnknownVariant { ordinal, .. } => ordinal,
}
}
}
impl $crate::encoding::Encodable for $name {
fn inline_align(&self) -> usize { 8 }
fn inline_size(&self) -> usize { 24 }
fn encode(&mut self, encoder: &mut $crate::encoding::Encoder) -> $crate::Result<()> {
let mut ordinal = self.ordinal();
// Encode tag
$crate::fidl_encode!(&mut ordinal, encoder)?;
// Reserved
$crate::fidl_encode!(&mut 0u32, encoder)?;
encoder.recurse(|encoder| {
match self {
$(
$name::$member_name ( val ) => $crate::encoding::encode_in_envelope(&mut Some(val), encoder),
)*
$name::__UnknownVariant { ordinal: _, bytes, handles } => {
// Throw the raw data from the unrecognized variant
// back onto the wire. This will allow correct proxies even in
// the event that they don't yet recognize this union variant.
$crate::fidl_encode!(&mut (bytes.len() as u32), encoder)?;
$crate::fidl_encode!(&mut (handles.len() as u32), encoder)?;
$crate::fidl_encode!(
&mut $crate::encoding::ALLOC_PRESENT_U64, encoder
)?;
encoder.append_bytes(bytes);
encoder.append_handles(handles);
Ok(())
},
}
})
}
}
impl $crate::encoding::Decodable for $name {
fn inline_align() -> usize { 8 }
fn inline_size() -> usize { 24 }
fn new_empty() -> Self {
#![allow(unreachable_code)]
$(
return $name::$member_name($crate::fidl_new_empty!($member_ty));
)*
$name::__UnknownVariant { ordinal: 0, bytes: vec![], handles: vec![] }
}
fn decode(&mut self, decoder: &mut $crate::encoding::Decoder) -> $crate::Result<()> {
#![allow(unused)]
let mut ordinal: u32 = 0;
$crate::fidl_decode!(&mut ordinal, decoder)?;
let mut _reserved: u32 = 0;
$crate::fidl_decode!(&mut _reserved, decoder)?;
let mut num_bytes: u32 = 0;
$crate::fidl_decode!(&mut num_bytes, decoder)?;
let mut num_handles: u32 = 0;
$crate::fidl_decode!(&mut num_handles, decoder)?;
let mut present: u64 = 0;
$crate::fidl_decode!(&mut present, decoder)?;
if present != $crate::encoding::ALLOC_PRESENT_U64 {
return Err($crate::Error::Invalid);
}
let member_inline_size = match ordinal {
$(
$member_ordinal => $crate::fidl_inline_size!($member_ty),
)*
// Unknown payloads are considered a wholly-inline string
// of bytes.
_ => num_bytes as usize,
};
decoder.read_out_of_line(member_inline_size, |decoder| {
decoder.recurse(|decoder| {
match ordinal {
$(
$member_ordinal => {
if let $name::$member_name(_) = self {
// Do nothing, read the value into the object
} else {
// Initialize `self` to the right variant
*self = $name::$member_name(
$crate::fidl_new_empty!($member_ty)
);
}
if let $name::$member_name(val) = self {
$crate::fidl_decode!(val, decoder)?;
} else {
unreachable!()
}
}
)*
ordinal => {
let bytes = decoder.next_slice(num_bytes as usize)?.to_vec();
let mut handles = Vec::with_capacity(num_handles as usize);
for _ in 0..num_handles {
handles.push(decoder.take_handle()?);
}
*self = $name::__UnknownVariant { ordinal, bytes, handles };
}
}
Ok(())
})
})
}
}
impl $crate::encoding::AutonullContainer for $name {
fn inline_align() -> usize { 8 }
fn inline_size() -> usize { 24 }
}
}
}
/// Header for transactional FIDL messages
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct TransactionHeader {
/// Transaction ID which identifies a request-response pair
pub tx_id: u32,
/// Flags (always zero for now)
pub flags: u32,
/// Ordinal which identifies the FIDL method
pub ordinal: u32,
}
fidl_struct! {
name: TransactionHeader,
members: [
tx_id {
ty: u32,
offset: 0,
},
flags {
ty: u32,
offset: 8, // Save 64 bits for id, even though it's only 32 bits
},
ordinal {
ty: u32,
offset: 12,
},
],
size: 16,
align: 0,
}
/// Transactional FIDL message
pub struct TransactionMessage<'a, T: 'a> {
/// Header of the message
pub header: TransactionHeader,
/// Body of the message
pub body: &'a mut T,
}
impl<'a, T: Encodable> Encodable for TransactionMessage<'a, T> {
fn inline_align(&self) -> usize {
0
}
fn inline_size(&self) -> usize {
self.header.inline_size() + self.body.inline_size()
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
self.header.encode(encoder)?;
(*self.body).encode(encoder)?;
Ok(())
}
}
impl<'a, T: Decodable> Decodable for TransactionMessage<'a, T> {
fn new_empty() -> Self {
panic!("cannot create an empty transaction message")
}
fn inline_align() -> usize {
0
}
fn inline_size() -> usize {
<TransactionHeader as Decodable>::inline_size() + T::inline_size()
}
fn decode(&mut self, decoder: &mut Decoder) -> Result<()> {
self.header.decode(decoder)?;
(*self.body).decode(decoder)?;
Ok(())
}
}
/// Decode 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 = TransactionHeader::new_empty();
let header_len = <TransactionHeader as Decodable>::inline_size();
if bytes.len() < header_len {
return Err(Error::OutOfRange);
}
let (header_bytes, body_bytes) = bytes.split_at(header_len);
let handles = &mut [];
Decoder::decode_into(header_bytes, handles, &mut header)?;
Ok((header, body_bytes))
}
// Implementations of Encodable for (&mut Head, ...Tail) and Decodable for (Head, ...Tail)
macro_rules! tuple_impls {
() => {};
(($idx:tt => $typ:ident), $( ($nidx:tt => $ntyp:ident), )*) => {
/*
* Invoke recursive reversal of list that ends in the macro expansion implementation
* of the reversed list
*/
tuple_impls!([($idx, $typ);] $( ($nidx => $ntyp), )*);
tuple_impls!($( ($nidx => $ntyp), )*); // invoke macro on tail
};
/*
* ([accumulatedList], listToReverse); recursively calls tuple_impls until the list to reverse
+ is empty (see next pattern)
*/
([$(($accIdx:tt, $accTyp:ident);)+]
($idx:tt => $typ:ident), $( ($nidx:tt => $ntyp:ident), )*) => {
tuple_impls!([($idx, $typ); $(($accIdx, $accTyp); )*] $( ($nidx => $ntyp), ) *);
};
// Finally expand into the implementation
([($idx:tt, $typ:ident); $( ($nidx:tt, $ntyp:ident); )*]) => {
impl<$typ, $( $ntyp ,)*>
Encodable for ($typ, $( $ntyp ,)*)
where $typ: Encodable,
$( $ntyp: Encodable,)*
{
fn inline_align(&self) -> usize {
let mut max = 0;
if max < self.$idx.inline_align() {
max = self.$idx.inline_align();
}
$(
if max < self.$nidx.inline_align() {
max = self.$nidx.inline_align();
}
)*
max
}
fn inline_size(&self) -> usize {
let mut offset = 0;
offset += self.$idx.inline_size();
$(
offset = round_up_to_align(offset, self.$nidx.inline_align());
offset += self.$nidx.inline_size();
)*
offset
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
encoder.recurse(|encoder| {
let mut cur_offset = 0;
self.$idx.encode(encoder)?;
cur_offset += self.$idx.inline_size();
$(
// Skip to the start of the next field
let member_offset = round_up_to_align(cur_offset, self.$nidx.inline_align());
encoder.padding(member_offset - cur_offset)?;
cur_offset = member_offset;
self.$nidx.encode(encoder)?;
cur_offset += self.$nidx.inline_size();
)*
// Skip to the end of the struct's size
encoder.padding(self.inline_size() - cur_offset)?;
Ok(())
})
}
}
impl<$typ, $( $ntyp ),*> Decodable for ($typ, $( $ntyp, )*)
where $typ: Decodable,
$( $ntyp: Decodable, )*
{
fn inline_align() -> usize {
let mut max = 0;
if max < $typ::inline_align() {
max = $typ::inline_align();
}
$(
if max < $ntyp::inline_align() {
max = $ntyp::inline_align();
}
)*
max
}
fn inline_size() -> usize {
let mut offset = 0;
offset += $typ::inline_size();
$(
offset = round_up_to_align(offset, $ntyp::inline_align());
offset += $ntyp::inline_size();
)*
offset
}
fn new_empty() -> Self {
(
$typ::new_empty(),
$(
$ntyp::new_empty(),
)*
)
}
fn decode(&mut self, decoder: &mut Decoder) -> Result<()> {
decoder.recurse(|decoder| {
let mut cur_offset = 0;
self.$idx.decode(decoder)?;
cur_offset += $typ::inline_size();
$(
// Skip to the start of the next field
let member_offset = round_up_to_align(cur_offset, $ntyp::inline_align());
// TODO(FIDL-598) Switch to `skip_padding` when other bindings are ready.
decoder.next_slice(member_offset - cur_offset)?;
cur_offset = member_offset;
self.$nidx.decode(decoder)?;
cur_offset += $ntyp::inline_size();
)*
// Skip to the end of the struct's size
// TODO(FIDL-598) Switch to `skip_padding` when other bindings are ready.
decoder.next_slice(Self::inline_size() - cur_offset)?;
Ok(())
})
}
}
}
}
tuple_impls!(
(10 => K),
(9 => J),
(8 => I),
(7 => H),
(6 => G),
(5 => F),
(4 => E),
(3 => D),
(2 => C),
(1 => B),
(0 => A),
);
impl Encodable for () {
fn inline_align(&self) -> usize {
0
}
fn inline_size(&self) -> usize {
0
}
fn encode(&mut self, _: &mut Encoder) -> Result<()> {
Ok(())
}
}
impl Decodable for () {
fn new_empty() -> Self {
()
}
fn inline_size() -> usize {
0
}
fn inline_align() -> usize {
0
}
fn decode(&mut self, _: &mut Decoder) -> Result<()> {
Ok(())
}
}
impl<'a, T> Encodable for &'a mut T
where
T: Encodable,
{
fn inline_align(&self) -> usize {
(**self).inline_align()
}
fn inline_size(&self) -> usize {
(**self).inline_size()
}
fn encode(&mut self, encoder: &mut Encoder) -> Result<()> {
(&mut **self).encode(encoder)
}
}
#[cfg(test)]
mod test {
use super::*;
use fuchsia_zircon::AsHandleRef;
use std::{f32, f64, fmt, i64, u64};
fn encode_decode<T: Encodable + Decodable>(start: &mut T) -> T {
let buf = &mut Vec::new();
let handle_buf = &mut Vec::new();
Encoder::encode(buf, handle_buf, start).expect("Encoding failed");
let mut out = T::new_empty();
Decoder::decode_into(buf, handle_buf, &mut out).expect("Decoding failed");
out
}
fn encode_assert_bytes<T: Encodable>(mut data: T, encoded_bytes: &[u8]) {
let buf = &mut Vec::new();
let handle_buf = &mut Vec::new();
Encoder::encode(buf, handle_buf, &mut data).expect("Encoding failed");
assert_eq!(&**buf, encoded_bytes);
}
fn assert_identity<T>(mut x: T, cloned: T)
where
T: Encodable + Decodable + PartialEq + fmt::Debug,
{
assert_eq!(cloned, encode_decode(&mut x));
}
macro_rules! identities { ($($x:expr,)*) => { $(
assert_identity($x, $x);
)* } }
#[test]
fn encode_decode_byte() {
identities![0u8, 57u8, 255u8, 0i8, -57i8, 12i8,];
}
#[test]
#[rustfmt::skip]
fn encode_decode_multibyte() {
identities![
0u64, 1u64, u64::MAX, u64::MIN,
0i64, 1i64, i64::MAX, i64::MIN,
0f32, 1f32, f32::MAX, f32::MIN,
0f64, 1f64, f64::MAX, f64::MIN,
];
}
#[test]
fn encode_decode_nan() {
let nan32 = encode_decode(&mut f32::NAN);
assert!(nan32.is_nan());
let nan64 = encode_decode(&mut f64::NAN);
assert!(nan64.is_nan());
}
#[test]
fn encode_decode_out_of_line() {
identities![
Vec::<i32>::new(),
vec![1, 2, 3],
None::<Vec<i32>>,
Some(Vec::<i32>::new()),
Some(vec![1, 2, 3]),
Some(vec![vec![1, 2, 3]]),
Some(vec![Some(vec![1, 2, 3])]),
"".to_string(),
"foo".to_string(),
None::<String>,
Some("".to_string()),
Some("foo".to_string()),
Some(vec![None, Some("foo".to_string())]),
vec!["foo".to_string(), "bar".to_string()],
];
}
#[test]
fn encode_handle() {
let mut handle = zx::Handle::from(zx::Port::create().expect("Port creation failed"));
let raw_handle = handle.raw_handle();
let buf = &mut Vec::new();
let handle_buf = &mut Vec::new();
Encoder::encode(buf, handle_buf, &mut handle).expect("Encoding failed");
assert!(handle.is_invalid());
let mut handle_out = zx::Handle::new_empty();
Decoder::decode_into(buf, handle_buf, &mut handle_out).expect("Decoding failed");
assert_eq!(raw_handle, handle_out.raw_handle());
}
#[test]
fn encode_decode_bits() {
fidl_bits!(Buttons(u32) {
PLAY = 1,
PAUSE = 2,
STOP = 4,
});
assert_eq!(Buttons::from_bits(1), Some(Buttons::PLAY));
assert_eq!(Buttons::from_bits(12), None);
assert_eq!(Buttons::STOP.bits(), 4);
identities![
Buttons::PLAY,
Buttons::PAUSE,
Buttons::STOP,
Buttons::from_bits(1).expect("should be Play"),
Buttons::from_bits(Buttons::PAUSE.bits()).expect("should be Pause"),
];
}
#[test]
fn encode_decode_enum() {
fidl_enum!(Animal(i32) {
Dog = 0,
Cat = 1,
Frog = 2,
});
assert_eq!(Animal::from_primitive(0), Some(Animal::Dog));
assert_eq!(Animal::from_primitive(3), None);
assert_eq!(Animal::Cat.into_primitive(), 1);
identities![
Animal::Dog,
Animal::Cat,
Animal::Frog,
Animal::from_primitive(0).expect("should be dog"),
Animal::from_primitive(Animal::Cat.into_primitive()).expect("should be cat"),
];
}
#[test]
fn result_and_union_compat() {
fidl_union! {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
name: OkayOrError,
members: [
Okay {
ty: u64,
offset: 8,
},
Error {
ty: u32,
offset: 8,
},
],
size: 16,
align: 8,
};
let buf = &mut Vec::new();
let handle_buf = &mut Vec::new();
let mut out: std::result::Result<u64, u32> = Decodable::new_empty();
Encoder::encode(buf, handle_buf, &mut OkayOrError::Okay(42u64)).expect("Encoding failed");
Decoder::decode_into(buf, handle_buf, &mut out).expect("Decoding failed");
assert_eq!(out, Ok(42));
Encoder::encode(buf, handle_buf, &mut OkayOrError::Error(3u32)).expect("Encoding failed");
Decoder::decode_into(buf, handle_buf, &mut out).expect("Decoding failed");
assert_eq!(out, Err(3));
}
#[test]
fn result_and_union_compat_smaller() {
fidl_union! {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
name: OkayOrError,
members: [
Okay {
ty: (),
offset: 4,
},
Error {
ty: i32,
offset: 4,
},
],
size: 8,
align: 4,
};
let buf = &mut Vec::new();
let handle_buf = &mut Vec::new();
// result to union
Encoder::encode(buf, handle_buf, &mut Ok::<(), i32>(())).expect("Encoding failed");
let mut out = OkayOrError::new_empty();
Decoder::decode_into(buf, handle_buf, &mut out).expect("Decoding failed");
assert_eq!(out, OkayOrError::Okay(()));
Encoder::encode(buf, handle_buf, &mut Err::<(), i32>(5)).expect("Encoding failed");
Decoder::decode_into(buf, handle_buf, &mut out).expect("Decoding failed");
assert_eq!(out, OkayOrError::Error(5));
// union to result
let mut out: std::result::Result<(), i32> = Decodable::new_empty();
Encoder::encode(buf, handle_buf, &mut OkayOrError::Okay(())).expect("Encoding failed");
Decoder::decode_into(buf, handle_buf, &mut out).expect("Decoding failed");
assert_eq!(out, Ok(()));
Encoder::encode(buf, handle_buf, &mut OkayOrError::Error(3i32)).expect("Encoding failed");
Decoder::decode_into(buf, handle_buf, &mut out).expect("Decoding failed");
assert_eq!(out, Err(3));
}
#[test]
fn encode_decode_result() {
let mut test_result: std::result::Result<String, u32> = Ok("fuchsia".to_string());
let mut test_result_err: std::result::Result<String, u32> = Err(5);
match encode_decode(&mut test_result) {
Ok(ref out_str) if "fuchsia".to_string() == *out_str => {}
x => panic!("unexpected decoded value {:?}", x),
}
match &encode_decode(&mut test_result_err) {
Err(err_code) if *err_code == 5 => {}
x => panic!("unexpected decoded value {:?}", x),
}
}
#[test]
fn encode_decode_result_array() {
use std::result::Result;
{
let mut input: [Result<_, u32>; 2] = [Ok("a".to_string()), Ok("bcd".to_string())];
match encode_decode(&mut input) {
[Ok(ref ok1), Ok(ref ok2)]
if *ok1 == "a".to_string() && *ok2 == "bcd".to_string() => {}
x => panic!("unexpected decoded value {:?}", x),
}
}
{
let mut input: [Result<String, u32>; 2] = [Err(7), Err(42)];
match encode_decode(&mut input) {
[Err(ref err1), Err(ref err2)] if *err1 == 7 && *err2 == 42 => {}
x => panic!("unexpected decoded value {:?}", x),
}
}
{
let mut input = [Ok("abc".to_string()), Err(42)];
match encode_decode(&mut input) {
[Ok(ref ok1), Err(ref err2)] if *ok1 == "abc".to_string() && *err2 == 42 => {}
x => panic!("unexpected decoded value {:?}", x),
}
}
}
#[test]
fn padding_errors_correct_offset() {
use std::result::Result;
// This kind of result uses more bytes for the Ok() branch than for the Err() branch, so we
// should have some padding in the Err() values.
let mut v: Result<String, u32> = Err(5);
let buf = &mut Vec::new();
let handle_buf = &mut Vec::new();
Encoder::encode(buf, handle_buf, &mut v).expect("Encoding failed");
// Try to corrupt the second byte of padding after the Err() value content.
let break_pos = fidl_inline_align!(Result<String, u32>) + fidl_inline_size!(u32) + 1;
buf[break_pos] = 10;
let res =
Decoder::decode_into(buf, handle_buf, &mut v).expect_err("Decoded broken padding");
match res {
Error::NonZeroPadding { padding_start, non_zero_pos } => {
// This check is fragile, as it is trying to mimic what array encoders/decoders do.
// If this fails after you update the array encoding, please update the check as
// well.
assert_eq!(
padding_start,
fidl_inline_align!(Result<String, u32>) + fidl_inline_size!(u32)
);
assert_eq!(non_zero_pos, break_pos);
}
_ => panic!("decode_into failed with: {}", res),
}
}
#[test]
fn padding_errors_correct_offset_for_out_of_line_data() {
use std::result::Result;
// This kind of result uses more bytes for the Ok() branch than for the Err() branch, so we
// should have some padding in the Err() values. Vec is expected to put the Result itself
// into the out_of_line area.
let mut v: Vec<Result<String, u32>> = vec![Err(5)];
let buf = &mut Vec::new();
let handle_buf = &mut Vec::new();
Encoder::encode(buf, handle_buf, &mut v).expect("Encoding failed");
// Try to corrupt the second byte of padding after the Err() value content. The object
// itself is in the out_of_line area, which follows the inlnie size of the vector.
let break_pos = <Vec<Result<String, u32>> as Decodable>::inline_size()
+ fidl_inline_align!(Result<String, u32>)
+ fidl_inline_size!(u32)
+ 1;
buf[break_pos] = 10;
let res =
Decoder::decode_into(buf, handle_buf, &mut v).expect_err("Decoded broken padding");
match res {
Error::NonZeroPadding { padding_start, non_zero_pos } => {
// This check is fragile, as it is trying to mimic what array encoders/decoders do.
// If this fails after you update the array encoding, please update the check as
// well.
assert_eq!(
padding_start,
<Vec<Result<String, u32>> as Decodable>::inline_size()
+ fidl_inline_align!(Result<String, u32>)
+ fidl_inline_size!(u32)
);
assert_eq!(non_zero_pos, break_pos);
}
_ => panic!("decode_into failed with: {}", res),
}
}
#[test]
fn encode_decode_union() {
fidl_union! {
#[derive(Debug, Clone, Eq, PartialEq)]
name: NumOrStr,
members: [
Num {
ty: u64,
offset: 8,
},
Str {
ty: String,
offset: 8,
},
],
size: 24,
align: 8,
};
// These need to be manually compared because of missing `PartialEq` impls.
for num in vec![0, 255, 256] {
match encode_decode(&mut NumOrStr::Num(num)) {
NumOrStr::Num(out_num) if num == out_num => {}
x => panic!("unexpected decoded value {:?}", x),
}
}
for string in vec![String::new(), "hello world!".to_string()] {
match &encode_decode(&mut NumOrStr::Str(string.clone())) {
NumOrStr::Str(out_str) if out_str == &string => {}
x => panic!("unexpected decoded value {:?}", x),
}
}
}
struct Foo {
byte: u8,
bignum: u64,
string: String,
}
fidl_struct! {
name: Foo,
members: [
byte {
ty: u8,
offset: 0,
},
bignum {
ty: u64,
offset: 8,
},
string {
ty: String,
offset: 16,
},
],
size: 32,
align: 8,
}
#[test]
fn encode_decode_struct() {
let out_foo = encode_decode(&mut Some(Box::new(Foo {
byte: 5,
bignum: 22,
string: "hello world".to_string(),
})))
.expect("should be some");
assert_eq!(out_foo.byte, 5);
assert_eq!(out_foo.bignum, 22);
assert_eq!(out_foo.string, "hello world");
let out_foo: Option<Box<Foo>> = encode_decode(&mut Box::new(None));
assert!(out_foo.is_none());
}
#[test]
fn encode_decode_tuple() {
let mut start: (&mut u8, &mut u64, &mut String) = (&mut 5, &mut 10, &mut "foo".to_string());
let mut out: (u8, u64, String) = Decodable::new_empty();
let buf = &mut Vec::new();
let handle_buf = &mut Vec::new();
Encoder::encode(buf, handle_buf, &mut start).expect("Encoding failed");
Decoder::decode_into(buf, handle_buf, &mut out).expect("Decoding failed");
assert_eq!(*start.0, out.0);
assert_eq!(*start.1, out.1);
assert_eq!(*start.2, out.2);
}
#[test]
fn encode_decode_struct_as_tuple() {
let mut start = Foo { byte: 5, bignum: 10, string: "foo".to_string() };
let mut out: (u8, u64, String) = Decodable::new_empty();
let buf = &mut Vec::new();
let handle_buf = &mut Vec::new();
Encoder::encode(buf, handle_buf, &mut start).expect("Encoding failed");
Decoder::decode_into(buf, handle_buf, &mut out).expect("Decoding failed");
assert_eq!(start.byte, out.0);
assert_eq!(start.bignum, out.1);
assert_eq!(start.string, out.2);
}
#[test]
fn encode_decode_tuple_as_struct() {
let mut start = (&mut 5u8, &mut 10u64, &mut "foo".to_string());
let mut out: Foo = Decodable::new_empty();
let buf = &mut Vec::new();
let handle_buf = &mut Vec::new();
Encoder::encode(buf, handle_buf, &mut start).expect("Encoding failed");
Decoder::decode_into(buf, handle_buf, &mut out).expect("Decoding failed");
assert_eq!(*start.0, out.byte);
assert_eq!(*start.1, out.bignum);
assert_eq!(*start.2, out.string);
}
#[test]
fn encode_decode_tuple_msg() {
let mut body_start = (&mut "foo".to_string(), &mut 5);
let mut body_out: (String, u8) = Decodable::new_empty();
let buf = &mut Vec::new();
let handle_buf = &mut Vec::new();
Encoder::encode(buf, handle_buf, &mut body_start).unwrap();
Decoder::decode_into(buf, handle_buf, &mut body_out).unwrap();
assert_eq!(body_start.0, &mut body_out.0);
assert_eq!(body_start.1, &mut body_out.1);
}
struct MyTable {
num: Option<i32>,
num_none: Option<i32>,
string: Option<String>,
handle: Option<zx::Handle>,
}
fidl_table! {
name: MyTable,
members: {
num {
ty: i32,
ordinal: 1,
},
num_none {
ty: i32,
ordinal: 2,
},
string {
ty: String,
ordinal: 3,
},
handle {
ty: zx::Handle,
ordinal: 4,
},
},
}
#[allow(unused)]
struct EmptyTableCompiles {}
fidl_table! {
name: EmptyTableCompiles,
members: {},
}
struct TablePrefix {
num: Option<i32>,
num_none: Option<i32>,
}
fidl_table! {
name: TablePrefix,
members: {
num {
ty: i32,
ordinal: 1,
},
num_none {
ty: i32,
ordinal: 2,
},
},
}
#[test]
fn empty_table() {
let mut table: MyTable = MyTable::empty();
assert_eq!(None, table.num);
table = MyTable { num: Some(32), ..MyTable::empty() };
assert_eq!(Some(32), table.num);
assert_eq!(None, table.string);
}
#[test]
fn encode_decode_table() {
// create a random handle to encode and then decode.
let handle = zx::Vmo::create(1024).expect("vmo creation failed");
let raw_handle = handle.raw_handle();
let mut starting_table = MyTable {
num: Some(5),
num_none: None,
string: Some("foo".to_string()),
handle: Some(handle.into_handle()),
};
let table_out = encode_decode(&mut starting_table);
assert_eq!(table_out.num, Some(5));
assert_eq!(table_out.num_none, None);
assert_eq!(table_out.string, Some("foo".to_string()));
assert_eq!(table_out.handle.unwrap().raw_handle(), raw_handle);
}
#[test]
fn encode_decode_table_some() {
// create a random handle to encode and then decode.
let handle = zx::Vmo::create(1024).expect("vmo creation failed");
let raw_handle = handle.raw_handle();
let mut starting_table = Some(MyTable {
num: Some(5),
num_none: None,
string: Some("foo".to_string()),
handle: Some(handle.into_handle()),
});
let table_out = encode_decode(&mut starting_table);
let table_out = table_out.expect("table was None");
assert_eq!(table_out.num, Some(5));
assert_eq!(table_out.num_none, None);
assert_eq!(table_out.string, Some("foo".to_string()));
assert_eq!(table_out.handle.unwrap().raw_handle(), raw_handle);
}
#[test]
fn encode_decode_table_none() {
let table_none = encode_decode::<Option<MyTable>>(&mut None);
assert!(table_none.is_none());
}
#[test]
fn table_encode_prefix_decode_full() {
let mut table_prefix_in = TablePrefix { num: Some(5), num_none: None };
let mut table_out: MyTable = Decodable::new_empty();
let buf = &mut Vec::new();
let handle_buf = &mut Vec::new();
Encoder::encode(buf, handle_buf, &mut table_prefix_in).unwrap();
Decoder::decode_into(buf, handle_buf, &mut table_out).unwrap();
assert_eq!(table_out.num, Some(5));
assert_eq!(table_out.num_none, None);
assert_eq!(table_out.string, None);
assert_eq!(table_out.handle, None);
}
#[test]
fn table_encode_omits_none_tail() {
// "None" fields at the tail of a table shouldn't be encoded at all.
let mut table_in = MyTable {
num: Some(5),
// These fields should all be omitted in the encoded repr,
// allowing decoding of the prefix to succeed.
num_none: None,
string: None,
handle: None,
};
let mut table_prefix_out: TablePrefix = Decodable::new_empty();
let buf = &mut Vec::new();
let handle_buf = &mut Vec::new();
Encoder::encode(buf, handle_buf, &mut table_in).unwrap();
Decoder::decode_into(buf, handle_buf, &mut table_prefix_out).unwrap();
assert_eq!(table_prefix_out.num, Some(5));
assert_eq!(table_prefix_out.num_none, None);
}
#[test]
fn table_decode_fails_on_unrecognized_tail() {
let mut table_in =
MyTable { num: Some(5), num_none: None, string: Some("foo".to_string()), handle: None };
let mut table_prefix_out: TablePrefix = Decodable::new_empty();
let buf = &mut Vec::new();
let handle_buf = &mut Vec::new();
Encoder::encode(buf, handle_buf, &mut table_in).unwrap();
let err = Decoder::decode_into(buf, handle_buf, &mut table_prefix_out).unwrap_err();
match err {
Error::UnknownTableField => {}
err => panic!("unexpected error decoding: {:?}", err),
}
}
#[derive(Debug, PartialEq)]
pub struct SimpleTable {
x: Option<i64>,
y: Option<i64>,
}
fidl_table! {
name: SimpleTable,
members: {
x {
ty: i64,
ordinal: 1,
},
y {
ty: i64,
ordinal: 5,
},
},
}
#[derive(Debug, PartialEq)]
pub struct TableWithStringAndVector {
foo: Option<String>,
bar: Option<i32>,
baz: Option<Vec<u8>>,
}
fidl_table! {
name: TableWithStringAndVector,
members: {
foo {
ty: String,
ordinal: 1,
},
bar {
ty: i32,
ordinal: 2,
},
baz {
ty: Vec<u8>,
ordinal: 3,
},
},
}
#[test]
fn table_golden_simple_table_with_xy() {
let simple_table_with_xy: &[u8] = &[
5, 0, 0, 0, 0, 0, 0, 0, // max ordinal
255, 255, 255, 255, 255, 255, 255, 255, // alloc present
8, 0, 0, 0, 0, 0, 0, 0, // envelope 1: num bytes / num handles
255, 255, 255, 255, 255, 255, 255, 255, // alloc present
0, 0, 0, 0, 0, 0, 0, 0, // envelope 2: num bytes / num handles
0, 0, 0, 0, 0, 0, 0, 0, // no alloc
0, 0, 0, 0, 0, 0, 0, 0, // envelope 3: num bytes / num handles
0, 0, 0, 0, 0, 0, 0, 0, // no alloc
0, 0, 0, 0, 0, 0, 0, 0, // envelope 4: num bytes / num handles
0, 0, 0, 0, 0, 0, 0, 0, // no alloc
8, 0, 0, 0, 0, 0, 0, 0, // envelope 5: num bytes / num handles
255, 255, 255, 255, 255, 255, 255, 255, // alloc present
42, 0, 0, 0, 0, 0, 0, 0, // field X
67, 0, 0, 0, 0, 0, 0, 0, // field Y
];
encode_assert_bytes(SimpleTable { x: Some(42), y: Some(67) }, simple_table_with_xy)
}
#[test]
fn table_golden_simple_table_with_y() {
let simple_table_with_y: &[u8] = &[
5, 0, 0, 0, 0, 0, 0, 0, // max ordinal
255, 255, 255, 255, 255, 255, 255, 255, // alloc present
0, 0, 0, 0, 0, 0, 0, 0, // envelope 1: num bytes / num handles
0, 0, 0, 0, 0, 0, 0, 0, // no alloc
0, 0, 0, 0, 0, 0, 0, 0, // envelope 2: num bytes / num handles
0, 0, 0, 0, 0, 0, 0, 0, // no alloc
0, 0, 0, 0, 0, 0, 0, 0, // envelope 3: num bytes / num handles
0, 0, 0, 0, 0, 0, 0, 0, // no alloc
0, 0, 0, 0, 0, 0, 0, 0, // envelope 4: num bytes / num handles
0, 0, 0, 0, 0, 0, 0, 0, // no alloc
8, 0, 0, 0, 0, 0, 0, 0, // envelope 5: num bytes / num handles
255, 255, 255, 255, 255, 255, 255, 255, // alloc present
67, 0, 0, 0, 0, 0, 0, 0, // field Y
];
encode_assert_bytes(SimpleTable { x: None, y: Some(67) }, simple_table_with_y)
}
#[test]
fn table_golden_string_and_vector_hello_27() {
let table_with_string_and_vector_hello_27: &[u8] = &[
2, 0, 0, 0, 0, 0, 0, 0, // max ordinal
255, 255, 255, 255, 255, 255, 255, 255, // alloc present
24, 0, 0, 0, 0, 0, 0, 0, // envelope 1: num bytes / num handles
255, 255, 255, 255, 255, 255, 255, 255, // envelope 1: alloc present
8, 0, 0, 0, 0, 0, 0, 0, // envelope 2: num bytes / num handles
255, 255, 255, 255, 255, 255, 255, 255, // envelope 2: alloc present
5, 0, 0, 0, 0, 0, 0, 0, // element 1: length
255, 255, 255, 255, 255, 255, 255, 255, // element 1: alloc present
104, 101, 108, 108, 111, 0, 0, 0, // element 1: hello
27, 0, 0, 0, 0, 0, 0, 0, // element 2: value
];
encode_assert_bytes(
TableWithStringAndVector { foo: Some("hello".to_string()), bar: Some(27), baz: None },
table_with_string_and_vector_hello_27,
)
}
#[test]
fn table_golden_empty_table() {
let empty_table: &[u8] = &[
0, 0, 0, 0, 0, 0, 0, 0, // max ordinal
255, 255, 255, 255, 255, 255, 255, 255, // alloc present
];
encode_assert_bytes(SimpleTable { x: None, y: None }, empty_table)
}
#[derive(Debug, PartialEq)]
pub struct Int64Struct {
x: u64,
}
fidl_struct! {
name: Int64Struct,
members: [
x {
ty: u64,
offset: 0,
},
],
size: 8,
align: 8,
}
fidl_union! {
#[derive(Debug, PartialEq)]
name: SimpleUnion,
members: [
I32 {
ty: i32,
offset: 4,
},
I64 {
ty: i64,
offset: 8,
},
S {
ty: Int64Struct,
offset: 8,
},
Os {
ty: Option<Box<Int64Struct>>,
offset: 8,
},
Str {
ty: String,
offset: 8,
},
],
size: 24,
align: 8,
}
fidl_xunion! {
#[derive(Debug, PartialEq)]
name: TestSampleXUnion,
members: [
U {
ty: u32,
ordinal: 0x29df47a5,
},
Su {
ty: SimpleUnion,
ordinal: 0x6f317664,
},
St {
ty: SimpleTable,
ordinal: 3,
},
],
}
fidl_xunion! {
#[derive(Debug, PartialEq)]
name: TestSampleXUnionExpanded,
members: [
SomethinElse {
ty: zx::Handle,
ordinal: 55,
},
],
}
#[test]
fn xunion_golden_u() {
let xunion_u_bytes = &[
0xa5, 0x47, 0xdf, 0x29, 0x00, 0x00, 0x00, 0x00, // xunion discriminator + padding
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // num bytes + num handles
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // presence indicator
0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00, // content + padding
];
encode_assert_bytes(TestSampleXUnion::U(0xdeadbeef), xunion_u_bytes)
}
#[test]
fn xunion_golden_su() {
let xunion_su_bytes = &[
0x64, 0x76, 0x31, 0x6f, 0x00, 0x00, 0x00, 0x00, // xunion discriminator + padding
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // num bytes + num handles
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // presence indicator
// secondary object 0
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // union discriminant + padding
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // string size
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // string present
// secondary object 1
b'h', b'e', b'l', b'l', b'o', 0x00, 0x00, 0x00, // string "hello" + padding
];
encode_assert_bytes(
TestSampleXUnion::Su(SimpleUnion::Str("hello".to_string())),
xunion_su_bytes,
)
}
#[test]
fn xunion_unknown_variant_transparent_passthrough() {
let handle = zx::Handle::from(zx::Port::create().expect("Port creation failed"));
let raw_handle = handle.raw_handle();
let mut input = TestSampleXUnionExpanded::SomethinElse(handle);
// encode expanded and decode as xunion w/ missing variant
let buf = &mut Vec::new();
let handle_buf = &mut Vec::new();
Encoder::encode(buf, handle_buf, &mut input)
.expect("Encoding TestSampleXUnionExpanded failed");
let mut intermediate_missing_variant = TestSampleXUnion::new_empty();
Decoder::decode_into(buf, handle_buf, &mut intermediate_missing_variant)
.expect("Decoding TestSampleXUnion failed");
// Ensure we've recorded the unknown variant
if let TestSampleXUnion::__UnknownVariant { .. } = intermediate_missing_variant {
// ok
} else {
panic!("unexpected variant")
}
let buf = &mut Vec::new();
let handle_buf = &mut Vec::new();
Encoder::encode(buf, handle_buf, &mut intermediate_missing_variant)
.expect("encoding unknown variant failed");
let mut out = TestSampleXUnionExpanded::new_empty();
Decoder::decode_into(buf, handle_buf, &mut out).expect("Decoding final output failed");
if let TestSampleXUnionExpanded::SomethinElse(handle_out) = out {
assert_eq!(raw_handle, handle_out.raw_handle());
} else {
panic!("wrong final variant")
}
}
#[test]
fn encode_decode_transaction_msg() {
let header = TransactionHeader { tx_id: 4, flags: 5, ordinal: 6 };
let body = "hello".to_string();
let start = &mut TransactionMessage { header, body: &mut body.clone() };
let (buf, handles) = (&mut vec![], &mut vec![]);
Encoder::encode(buf, handles, 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(out_buf, handles, &mut body_out).expect("Decoding body failed");
assert_eq!(body, body_out);
}
#[test]
fn array_of_arrays() {
let mut input = &mut [&mut [1u32, 2, 3, 4, 5], &mut [5, 4, 3, 2, 1]];
let (bytes, handles) = (&mut vec![], &mut vec![]);
assert!(Encoder::encode(bytes, handles, &mut input).is_ok());
let mut output = <[[u32; 5]; 2]>::new_empty();
Decoder::decode_into(bytes, handles, &mut output).expect(
format!(
"Array decoding failed\n\
bytes: {:X?}",
bytes
)
.as_str(),
);
assert_eq!(input, output.iter_mut().map(|v| v.as_mut()).collect::<Vec<_>>().as_mut_slice());
}
#[test]
fn xunion_with_out_of_line_data() {
fidl_xunion! {
#[derive(Debug, PartialEq)]
name: XUnion,
members: [
Variant {
ty: Vec<u8>,
ordinal: 1,
},
],
}
identities![XUnion::Variant(vec![1, 2, 3]),];
}
#[test]
fn extra_data_is_disallowed() {
let mut output = ();
match Decoder::decode_into(&[0], &mut [], &mut output).expect_err("bytes") {
Error::ExtraBytes => {}
e => panic!("expected ExtraBytes, found {:?}", e),
}
match Decoder::decode_into(&[], &mut [zx::Handle::invalid()], &mut output)
.expect_err("handles")
{
Error::ExtraHandles => {}
e => panic!("expected ExtraHandles, found {:?}", e),
}
}
}