pub use static_assertions::const_assert_eq;
use crate::endpoints::ProtocolMarker;
use crate::handle::{
Handle, HandleBased, HandleDisposition, HandleInfo, HandleOp, ObjectType, Rights, Status,
};
use crate::time::{Instant, Ticks, Timeline};
use crate::{Error, MethodType, Result};
use bitflags::bitflags;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::{mem, ptr, str};
pub trait ProxyChannelBox<D: ResourceDialect>: std::fmt::Debug + Send + Sync {
#[allow(clippy::type_complexity)]
fn recv_etc_from(
&self,
ctx: &mut std::task::Context<'_>,
buf: &mut D::MessageBufEtc,
) -> std::task::Poll<Result<(), Option<<D::ProxyChannel as ProxyChannelFor<D>>::Error>>>;
#[cfg(not(target_os = "fuchsia"))]
fn closed_reason(&self) -> Option<String> {
None
}
fn as_channel(&self) -> &D::ProxyChannel;
fn write_etc(
&self,
bytes: &[u8],
handles: &mut [<D::ProxyChannel as ProxyChannelFor<D>>::HandleDisposition],
) -> Result<(), Option<<D::ProxyChannel as ProxyChannelFor<D>>::Error>>;
fn is_closed(&self) -> bool;
fn unbox(self) -> D::ProxyChannel;
}
pub trait MessageBufFor<D: ResourceDialect>: std::fmt::Debug + Send + Sync {
fn new() -> Self;
fn shrink_bytes_to_fit(&mut self) {}
fn split_mut(&mut self) -> (&mut Vec<u8>, &mut Vec<<D::Handle as HandleFor<D>>::HandleInfo>);
}
pub trait ProxyChannelFor<D: ResourceDialect>:
std::fmt::Debug + crate::epitaph::ChannelLike
{
type Boxed: ProxyChannelBox<D>;
type Error: Into<crate::TransportError>;
type HandleDisposition: HandleDispositionFor<D>;
fn boxed(self) -> Self::Boxed;
fn write_etc(
&self,
bytes: &[u8],
handles: &mut [Self::HandleDisposition],
) -> Result<(), Option<Self::Error>>;
}
pub trait HandleDispositionFor<D: ResourceDialect>: std::fmt::Debug {
fn from_handle(
handle: D::Handle,
object_type: crate::ObjectType,
rights: crate::Rights,
) -> Self;
}
pub trait HandleFor<D: ResourceDialect> {
type HandleInfo: HandleInfoFor<D>;
fn invalid() -> Self;
fn is_invalid(&self) -> bool;
}
pub trait HandleInfoFor<D: ResourceDialect>: std::fmt::Debug {
fn consume(
&mut self,
expected_object_type: crate::ObjectType,
expected_rights: crate::Rights,
) -> Result<D::Handle>;
fn drop_in_place(&mut self);
}
pub trait ResourceDialect: 'static + Sized + Default + std::fmt::Debug + Copy + Clone {
type Handle: HandleFor<Self>;
type MessageBufEtc: MessageBufFor<Self>;
type ProxyChannel: ProxyChannelFor<Self>;
fn with_tls_buf<R>(f: impl FnOnce(&mut TlsBuf<Self>) -> R) -> R;
}
pub trait EncodableAsHandle: Into<<Self::Dialect as ResourceDialect>::Handle> {
type Dialect: ResourceDialect<Handle: Into<Self>>;
}
pub unsafe trait TypeMarker: 'static + Sized {
type Owned;
fn inline_align(context: Context) -> usize;
fn inline_size(context: Context) -> usize;
#[inline(always)]
fn encode_is_copy() -> bool {
false
}
#[inline(always)]
fn decode_is_copy() -> bool {
false
}
}
pub trait ValueTypeMarker: TypeMarker {
type Borrowed<'a>;
fn borrow(value: &Self::Owned) -> Self::Borrowed<'_>;
}
pub trait ResourceTypeMarker: TypeMarker {
type Borrowed<'a>;
fn take_or_borrow(value: &mut Self::Owned) -> Self::Borrowed<'_>;
}
pub unsafe trait Encode<T: TypeMarker, D: ResourceDialect>: Sized {
unsafe fn encode(self, encoder: &mut Encoder<'_, D>, offset: usize, depth: Depth)
-> Result<()>;
}
pub trait Decode<T: TypeMarker, D>: 'static + Sized {
fn new_empty() -> Self;
unsafe fn decode(
&mut self,
decoder: &mut Decoder<'_, D>,
offset: usize,
depth: Depth,
) -> Result<()>
where
D: ResourceDialect;
}
#[derive(Debug)]
pub struct FuchsiaProxyBox(crate::AsyncChannel);
impl FuchsiaProxyBox {
pub fn on_closed(&self) -> crate::OnSignalsRef<'_> {
self.0.on_closed()
}
pub fn read_etc(
&self,
cx: &mut std::task::Context<'_>,
bytes: &mut Vec<u8>,
handles: &mut Vec<crate::HandleInfo>,
) -> std::task::Poll<Result<(), crate::Status>> {
self.0.read_etc(cx, bytes, handles)
}
pub fn signal_peer(
&self,
clear: crate::Signals,
set: crate::Signals,
) -> Result<(), zx_status::Status> {
use crate::Peered;
self.0.as_ref().signal_peer(clear, set)
}
}
impl ProxyChannelBox<DefaultFuchsiaResourceDialect> for FuchsiaProxyBox {
fn write_etc(
&self,
bytes: &[u8],
handles: &mut [HandleDisposition<'static>],
) -> Result<(), Option<zx_status::Status>> {
self.0
.write_etc(bytes, handles)
.map_err(|x| Some(x).filter(|x| *x != zx_status::Status::PEER_CLOSED))
}
fn recv_etc_from(
&self,
ctx: &mut std::task::Context<'_>,
buf: &mut crate::MessageBufEtc,
) -> std::task::Poll<Result<(), Option<zx_status::Status>>> {
self.0
.recv_etc_from(ctx, buf)
.map_err(|x| Some(x).filter(|x| *x != zx_status::Status::PEER_CLOSED))
}
fn is_closed(&self) -> bool {
self.0.is_closed()
}
#[cfg(not(target_os = "fuchsia"))]
fn closed_reason(&self) -> Option<String> {
self.0.closed_reason()
}
fn unbox(self) -> <DefaultFuchsiaResourceDialect as ResourceDialect>::ProxyChannel {
self.0
}
fn as_channel(&self) -> &<DefaultFuchsiaResourceDialect as ResourceDialect>::ProxyChannel {
&self.0
}
}
#[derive(Debug, Default, Copy, Clone)]
pub struct DefaultFuchsiaResourceDialect;
impl ResourceDialect for DefaultFuchsiaResourceDialect {
type Handle = Handle;
type MessageBufEtc = crate::MessageBufEtc;
type ProxyChannel = crate::AsyncChannel;
#[inline]
fn with_tls_buf<R>(f: impl FnOnce(&mut TlsBuf<Self>) -> R) -> R {
thread_local!(static TLS_BUF: RefCell<TlsBuf<DefaultFuchsiaResourceDialect>> =
RefCell::new(TlsBuf::default()));
TLS_BUF.with(|buf| f(&mut buf.borrow_mut()))
}
}
impl MessageBufFor<DefaultFuchsiaResourceDialect> for crate::MessageBufEtc {
fn new() -> crate::MessageBufEtc {
let mut ret = crate::MessageBufEtc::new();
ret.ensure_capacity_bytes(MIN_BUF_BYTES_SIZE);
ret
}
fn shrink_bytes_to_fit(&mut self) {
self.shrink_bytes_to_fit();
}
fn split_mut(&mut self) -> (&mut Vec<u8>, &mut Vec<HandleInfo>) {
self.split_mut()
}
}
impl ProxyChannelFor<DefaultFuchsiaResourceDialect> for crate::AsyncChannel {
type Boxed = FuchsiaProxyBox;
type Error = zx_status::Status;
type HandleDisposition = HandleDisposition<'static>;
fn boxed(self) -> FuchsiaProxyBox {
FuchsiaProxyBox(self)
}
fn write_etc(
&self,
bytes: &[u8],
handles: &mut [HandleDisposition<'static>],
) -> Result<(), Option<zx_status::Status>> {
self.write_etc(bytes, handles)
.map_err(|x| Some(x).filter(|x| *x != zx_status::Status::PEER_CLOSED))
}
}
impl HandleDispositionFor<DefaultFuchsiaResourceDialect> for HandleDisposition<'static> {
fn from_handle(handle: Handle, object_type: ObjectType, rights: Rights) -> Self {
HandleDisposition::new(HandleOp::Move(handle), object_type, rights, Status::OK)
}
}
impl HandleFor<DefaultFuchsiaResourceDialect> for Handle {
type HandleInfo = HandleInfo;
fn invalid() -> Self {
Handle::invalid()
}
fn is_invalid(&self) -> bool {
Handle::is_invalid(self)
}
}
impl HandleInfoFor<DefaultFuchsiaResourceDialect> for HandleInfo {
fn consume(
&mut self,
expected_object_type: ObjectType,
expected_rights: Rights,
) -> Result<Handle> {
let handle_info = std::mem::replace(
self,
HandleInfo::new(Handle::invalid(), ObjectType::NONE, Rights::NONE),
);
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(handle_info.handle)
}
#[inline(always)]
fn drop_in_place(&mut self) {
*self = HandleInfo::new(Handle::invalid(), ObjectType::NONE, Rights::NONE);
}
}
#[cfg(not(target_os = "fuchsia"))]
#[derive(Debug)]
pub enum NoHandles {}
#[cfg(not(target_os = "fuchsia"))]
impl ProxyChannelBox<NoHandleResourceDialect> for NoHandles {
fn recv_etc_from(
&self,
_ctx: &mut std::task::Context<'_>,
_buf: &mut <NoHandleResourceDialect as ResourceDialect>::MessageBufEtc,
) -> std::task::Poll<Result<(), Option<zx_status::Status>>> {
unreachable!()
}
fn write_etc(
&self,
_bytes: &[u8],
_handles: &mut [
<<NoHandleResourceDialect as ResourceDialect>::ProxyChannel
as ProxyChannelFor<NoHandleResourceDialect>>::HandleDisposition],
) -> Result<(), Option<zx_status::Status>> {
unreachable!()
}
fn is_closed(&self) -> bool {
unreachable!()
}
fn unbox(self) -> <NoHandleResourceDialect as ResourceDialect>::ProxyChannel {
unreachable!()
}
fn as_channel(&self) -> &<NoHandleResourceDialect as ResourceDialect>::ProxyChannel {
unreachable!()
}
}
#[cfg(not(target_os = "fuchsia"))]
#[derive(Debug, Default, Copy, Clone)]
pub struct NoHandleResourceDialect;
#[cfg(not(target_os = "fuchsia"))]
impl ResourceDialect for NoHandleResourceDialect {
type Handle = NoHandles;
type MessageBufEtc = NoHandles;
type ProxyChannel = NoHandles;
#[inline]
fn with_tls_buf<R>(f: impl FnOnce(&mut TlsBuf<Self>) -> R) -> R {
thread_local!(static TLS_BUF: RefCell<TlsBuf<NoHandleResourceDialect>> =
RefCell::new(TlsBuf::default()));
TLS_BUF.with(|buf| f(&mut buf.borrow_mut()))
}
}
#[cfg(not(target_os = "fuchsia"))]
impl MessageBufFor<NoHandleResourceDialect> for NoHandles {
fn new() -> Self {
unreachable!()
}
fn split_mut(&mut self) -> (&mut Vec<u8>, &mut Vec<NoHandles>) {
unreachable!()
}
}
#[cfg(not(target_os = "fuchsia"))]
impl ProxyChannelFor<NoHandleResourceDialect> for NoHandles {
type Boxed = NoHandles;
type Error = zx_status::Status;
type HandleDisposition = NoHandles;
fn boxed(self) -> NoHandles {
unreachable!()
}
fn write_etc(
&self,
_bytes: &[u8],
_handles: &mut [NoHandles],
) -> Result<(), Option<zx_status::Status>> {
unreachable!()
}
}
#[cfg(not(target_os = "fuchsia"))]
impl crate::epitaph::ChannelLike for NoHandles {
fn write_epitaph(&self, _bytes: &[u8]) -> std::result::Result<(), crate::TransportError> {
unreachable!()
}
}
#[cfg(not(target_os = "fuchsia"))]
impl HandleFor<NoHandleResourceDialect> for NoHandles {
type HandleInfo = NoHandles;
fn invalid() -> Self {
unreachable!()
}
fn is_invalid(&self) -> bool {
unreachable!()
}
}
#[cfg(not(target_os = "fuchsia"))]
impl HandleDispositionFor<NoHandleResourceDialect> for NoHandles {
fn from_handle(
_handle: <NoHandleResourceDialect as ResourceDialect>::Handle,
_object_type: crate::ObjectType,
_rights: crate::Rights,
) -> Self {
unreachable!()
}
}
#[cfg(not(target_os = "fuchsia"))]
impl HandleInfoFor<NoHandleResourceDialect> for NoHandles {
fn consume(
&mut self,
_expected_object_type: crate::ObjectType,
_expected_rights: crate::Rights,
) -> Result<<NoHandleResourceDialect as ResourceDialect>::Handle> {
unreachable!()
}
fn drop_in_place(&mut self) {
unreachable!()
}
}
#[cfg(target_os = "fuchsia")]
pub type NoHandleResourceDialect = DefaultFuchsiaResourceDialect;
pub const MAX_RECURSION: usize = 32;
pub const MAX_HANDLES: usize = 64;
pub const ALLOC_PRESENT_U64: u64 = u64::MAX;
pub const ALLOC_PRESENT_U32: u32 = u32::MAX;
pub const ALLOC_ABSENT_U64: u64 = 0;
pub const ALLOC_ABSENT_U32: u32 = 0;
pub const EPITAPH_ORDINAL: u64 = 0xffffffffffffffffu64;
pub const MAGIC_NUMBER_INITIAL: u8 = 1;
#[doc(hidden)] #[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);
(x + align - 1) & !(align - 1)
}
#[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());
}
buf.set_len(new_len);
}
#[doc(hidden)] #[derive(Debug, Copy, Clone)]
#[repr(transparent)]
pub struct Depth(usize);
impl Depth {
#[inline(always)]
pub fn increment(&mut self) -> Result<()> {
self.0 += 1;
if self.0 > MAX_RECURSION {
return Err(Error::MaxRecursionDepth);
}
Ok(())
}
}
#[doc(hidden)] #[macro_export]
macro_rules! new_empty {
($ty:ty) => {
<<$ty as $crate::encoding::TypeMarker>::Owned as $crate::encoding::Decode<$ty, _>>::new_empty()
};
($ty:ty, $d:path) => {
<<$ty as $crate::encoding::TypeMarker>::Owned as $crate::encoding::Decode<$ty, $d>>::new_empty()
};
}
#[doc(hidden)] #[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,
)
};
($ty:ty, $d:path, $out_value:expr, $decoder:expr, $offset:expr, $depth:expr) => {
<<$ty as $crate::encoding::TypeMarker>::Owned as $crate::encoding::Decode<$ty, $d>>::decode(
$out_value, $decoder, $offset, $depth,
)
};
}
#[derive(Clone, Copy, Debug)]
pub enum WireFormatVersion {
V2,
}
#[derive(Clone, Copy, Debug)]
pub struct Context {
pub wire_format_version: WireFormatVersion,
}
const_assert_eq!(mem::size_of::<Context>(), 0);
impl Context {
#[inline]
pub(crate) fn at_rest_flags(&self) -> AtRestFlags {
match self.wire_format_version {
WireFormatVersion::V2 => AtRestFlags::USE_V2_WIRE_FORMAT,
}
}
}
#[derive(Debug)]
pub struct Encoder<'a, D: ResourceDialect> {
pub context: Context,
pub buf: &'a mut Vec<u8>,
handles: &'a mut Vec<<D::ProxyChannel as ProxyChannelFor<D>>::HandleDisposition>,
_dialect: PhantomData<D>,
}
#[inline]
fn default_encode_context() -> Context {
Context { wire_format_version: WireFormatVersion::V2 }
}
impl<'a, D: ResourceDialect> Encoder<'a, D> {
#[inline]
pub fn encode<T: TypeMarker>(
buf: &'a mut Vec<u8>,
handles: &'a mut Vec<<D::ProxyChannel as ProxyChannelFor<D>>::HandleDisposition>,
x: impl Encode<T, D>,
) -> Result<()> {
let context = default_encode_context();
Self::encode_with_context::<T>(context, buf, handles, x)
}
#[inline]
pub fn encode_with_context<T: TypeMarker>(
context: Context,
buf: &'a mut Vec<u8>,
handles: &'a mut Vec<<D::ProxyChannel as ProxyChannelFor<D>>::HandleDisposition>,
x: impl Encode<T, D>,
) -> Result<()> {
fn prepare_for_encoding<'a, D: ResourceDialect>(
context: Context,
buf: &'a mut Vec<u8>,
handles: &'a mut Vec<<D::ProxyChannel as ProxyChannelFor<D>>::HandleDisposition>,
ty_inline_size: usize,
) -> Encoder<'a, D> {
if ty_inline_size != 0 {
let aligned_inline_size = round_up_to_align(ty_inline_size, 8);
unsafe {
resize_vec_no_zeroing(buf, aligned_inline_size);
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, _dialect: PhantomData }
}
let mut encoder = prepare_for_encoding(context, buf, handles, T::inline_size(context));
unsafe { x.encode(&mut encoder, 0, Depth(0)) }
}
#[inline(always)]
pub fn debug_check_bounds<T: TypeMarker>(&self, offset: usize) {
debug_assert!(offset + T::inline_size(self.context) <= self.buf.len());
}
#[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());
let ptr = self.buf.get_unchecked_mut(offset) as *mut u8;
(ptr as *mut T).write_unaligned(num);
}
#[inline(always)]
pub fn push_next_handle(
&mut self,
handle: <D::ProxyChannel as ProxyChannelFor<D>>::HandleDisposition,
) {
self.handles.push(handle)
}
#[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);
let padding_ptr = self.buf.get_unchecked_mut(new_len - 8) as *mut u8;
(padding_ptr as *mut u64).write_unaligned(0);
new_offset
}
#[inline(always)]
pub unsafe fn padding(&mut self, offset: usize, len: usize) {
if len == 0 {
return;
}
debug_assert!(offset + len <= self.buf.len());
ptr::write_bytes(self.buf.as_mut_ptr().add(offset), 0, len);
}
}
unsafe impl<T: Timeline + 'static, U: 'static> TypeMarker for Instant<T, U> {
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<T: Timeline + Copy + 'static, U: Copy + 'static> ValueTypeMarker for Instant<T, U> {
type Borrowed<'a> = Self;
fn borrow(value: &Self::Owned) -> Self::Borrowed<'_> {
*value
}
}
unsafe impl<T: Timeline + Copy + 'static, D: ResourceDialect> Encode<Instant<T>, D> for Instant<T> {
#[inline]
unsafe fn encode(
self,
encoder: &mut Encoder<'_, D>,
offset: usize,
_depth: Depth,
) -> Result<()> {
encoder.debug_check_bounds::<Self>(offset);
encoder.write_num(self.into_nanos(), offset);
Ok(())
}
}
unsafe impl<T: Timeline + 'static, D: ResourceDialect> Encode<Ticks<T>, D> for Ticks<T> {
#[inline]
unsafe fn encode(
self,
encoder: &mut Encoder<'_, D>,
offset: usize,
_depth: Depth,
) -> Result<()> {
encoder.debug_check_bounds::<Self>(offset);
encoder.write_num(self.into_raw(), offset);
Ok(())
}
}
#[derive(Debug)]
pub struct Decoder<'a, D: ResourceDialect> {
pub context: Context,
pub buf: &'a [u8],
next_out_of_line: usize,
handles: &'a mut [<D::Handle as HandleFor<D>>::HandleInfo],
next_handle: usize,
_dialect: PhantomData<D>,
}
impl<'a, D: ResourceDialect> Decoder<'a, D> {
#[inline]
pub fn decode_into<T: TypeMarker>(
header: &TransactionHeader,
buf: &'a [u8],
handles: &'a mut [<D::Handle as HandleFor<D>>::HandleInfo],
value: &mut T::Owned,
) -> Result<()>
where
T::Owned: Decode<T, D>,
{
Self::decode_with_context::<T>(header.decoding_context(), buf, handles, value)
}
#[inline]
pub fn decode_with_context<T: TypeMarker>(
context: Context,
buf: &'a [u8],
handles: &'a mut [<D::Handle as HandleFor<D>>::HandleInfo],
value: &mut T::Owned,
) -> Result<()>
where
T::Owned: Decode<T, D>,
{
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,
_dialect: PhantomData,
};
unsafe {
value.decode(&mut decoder, 0, Depth(0))?;
}
unsafe { decoder.post_decoding(inline_size, next_out_of_line) }
}
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 {
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()
};
let mask = !(!0u64 >> (padding * 8));
if last_u64 & mask != 0 {
return Err(self.end_of_block_padding_error(padding_start, padding_end));
}
}
Ok(())
}
#[inline(always)]
pub fn next_out_of_line(&self) -> usize {
self.next_out_of_line
}
#[inline(always)]
pub fn remaining_handles(&self) -> usize {
self.handles.len() - self.next_handle
}
#[inline(always)]
pub fn debug_check_bounds<T: TypeMarker>(&self, offset: usize) {
debug_assert!(offset + T::inline_size(self.context) <= self.buf.len());
}
#[inline(always)]
pub fn read_num<T: numeric::Numeric>(&mut self, offset: usize) -> T {
debug_assert!(offset + mem::size_of::<T>() <= self.buf.len());
unsafe {
let ptr = self.buf.get_unchecked(offset) as *const u8;
(ptr as *const T).read_unaligned()
}
}
#[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);
}
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;
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)
}
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 };
}
}
std::process::abort();
}
#[inline]
pub fn check_padding(&self, offset: usize, len: usize) -> Result<()> {
if len == 0 {
return Ok(());
}
debug_assert!(offset + len <= self.buf.len());
for i in offset..offset + len {
if unsafe { *self.buf.get_unchecked(i) } != 0 {
return Err(Error::NonZeroPadding { padding_start: offset });
}
}
Ok(())
}
#[inline]
pub fn check_inline_envelope_padding(
&self,
value_offset: usize,
value_len: usize,
) -> Result<()> {
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 })
}
}
#[inline]
pub fn take_next_handle(
&mut self,
expected_object_type: crate::ObjectType,
expected_rights: crate::Rights,
) -> Result<D::Handle> {
let Some(next_handle) = self.handles.get_mut(self.next_handle) else {
return Err(Error::OutOfRange);
};
let handle = next_handle.consume(expected_object_type, expected_rights)?;
self.next_handle += 1;
Ok(handle)
}
#[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);
};
next_handle.drop_in_place();
self.next_handle += 1;
Ok(())
}
}
impl<T: Timeline + 'static, D: ResourceDialect> Decode<Self, D> for Instant<T> {
#[inline(always)]
fn new_empty() -> Self {
Instant::ZERO
}
#[inline]
unsafe fn decode(
&mut self,
decoder: &mut Decoder<'_, D>,
offset: usize,
_depth: Depth,
) -> Result<()> {
decoder.debug_check_bounds::<Self>(offset);
*self = Self::from_nanos(decoder.read_num(offset));
Ok(())
}
}
impl<T: Timeline + 'static, D: ResourceDialect> Decode<Self, D> for Ticks<T> {
#[inline(always)]
fn new_empty() -> Self {
Ticks::<T>::ZERO
}
#[inline]
unsafe fn decode(
&mut self,
decoder: &mut Decoder<'_, D>,
offset: usize,
_depth: Depth,
) -> Result<()> {
decoder.debug_check_bounds::<Self>(offset);
*self = Self::from_raw(decoder.read_num(offset));
Ok(())
}
}
pub struct Ambiguous1;
pub struct Ambiguous2;
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 as TypeMarker>::Owned) -> Self::Borrowed<'_> {
match *value {}
}
}
impl ResourceTypeMarker for $ambiguous {
type Borrowed<'a> = AmbiguousNever;
fn take_or_borrow(value: &mut <Self as TypeMarker>::Owned) -> Self::Borrowed<'_>
where
Self: TypeMarker,
{
match *value {}
}
}
unsafe impl<T, D: ResourceDialect> Encode<$ambiguous, D> for T {
unsafe fn encode(
self,
_encoder: &mut Encoder<'_, D>,
_offset: usize,
_depth: Depth,
) -> Result<()> {
panic!("reached code for fake ambiguous type");
}
}
impl<D: ResourceDialect> Decode<$ambiguous, D> for AmbiguousNever {
fn new_empty() -> Self {
panic!("reached code for fake ambiguous type");
}
unsafe fn decode(
&mut self,
_decoder: &mut Decoder<'_, D>,
_offset: usize,
_depth: Depth,
) -> Result<()> {
match *self {}
}
}
};
}
impl_ambiguous!(Ambiguous1);
impl_ambiguous!(Ambiguous2);
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<D: ResourceDialect> Encode<EmptyPayload, D> for () {
#[inline(always)]
unsafe fn encode(
self,
_encoder: &mut Encoder<'_, D>,
_offset: usize,
_depth: Depth,
) -> Result<()> {
Ok(())
}
}
impl<D: ResourceDialect> Decode<EmptyPayload, D> for () {
#[inline(always)]
fn new_empty() -> Self {}
#[inline(always)]
unsafe fn decode(
&mut self,
_decoder: &mut Decoder<'_, D>,
_offset: usize,
_depth: Depth,
) -> Result<()> {
Ok(())
}
}
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<D: ResourceDialect> Encode<EmptyStruct, D> for () {
#[inline]
unsafe fn encode(
self,
encoder: &mut Encoder<'_, D>,
offset: usize,
_depth: Depth,
) -> Result<()> {
encoder.debug_check_bounds::<EmptyStruct>(offset);
encoder.write_num(0u8, offset);
Ok(())
}
}
impl<D: ResourceDialect> Decode<EmptyStruct, D> for () {
#[inline(always)]
fn new_empty() -> Self {}
#[inline]
unsafe fn decode(
&mut self,
decoder: &mut Decoder<'_, D>,
offset: usize,
_depth: Depth,
) -> Result<()> {
decoder.debug_check_bounds::<EmptyStruct>(offset);
match decoder.read_num::<u8>(offset) {
0 => Ok(()),
_ => Err(Error::Invalid),
}
}
}
mod numeric {
use super::*;
pub trait Numeric {}
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::Owned) -> Self::Borrowed<'_> {
*value
}
}
unsafe impl<D: ResourceDialect> Encode<$numeric_ty, D> for $numeric_ty {
#[inline(always)]
unsafe fn encode(
self,
encoder: &mut Encoder<'_, D>,
offset: usize,
_depth: Depth,
) -> Result<()> {
encoder.debug_check_bounds::<$numeric_ty>(offset);
encoder.write_num::<$numeric_ty>(self, offset);
Ok(())
}
}
impl<D: ResourceDialect> Decode<$numeric_ty, D> for $numeric_ty {
#[inline(always)]
fn new_empty() -> Self {
0 as $numeric_ty
}
#[inline(always)]
unsafe fn decode(
&mut self,
decoder: &mut Decoder<'_, D>,
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 {
true
}
#[inline(always)]
fn decode_is_copy() -> bool {
false
}
}
impl ValueTypeMarker for bool {
type Borrowed<'a> = bool;
#[inline(always)]
fn borrow(value: &Self::Owned) -> Self::Borrowed<'_> {
*value
}
}
unsafe impl<D: ResourceDialect> Encode<bool, D> for bool {
#[inline]
unsafe fn encode(
self,
encoder: &mut Encoder<'_, D>,
offset: usize,
_depth: Depth,
) -> Result<()> {
encoder.debug_check_bounds::<bool>(offset);
encoder.write_num(self as u8, offset);
Ok(())
}
}
impl<D: ResourceDialect> Decode<bool, D> for bool {
#[inline(always)]
fn new_empty() -> Self {
false
}
#[inline]
unsafe fn decode(
&mut self,
decoder: &mut Decoder<'_, D>,
offset: usize,
_depth: Depth,
) -> Result<()> {
decoder.debug_check_bounds::<bool>(offset);
*self = match unsafe { *decoder.buf.get_unchecked(offset) } {
0 => false,
1 => true,
_ => return Err(Error::InvalidBoolean),
};
Ok(())
}
}
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 as TypeMarker>::Owned) -> Self::Borrowed<'_> {
value
}
}
unsafe impl<T: ValueTypeMarker, const N: usize, D: ResourceDialect> Encode<Array<T, N>, D>
for &[T::Owned; N]
where
for<'q> T::Borrowed<'q>: Encode<T, D>,
{
#[inline]
unsafe fn encode(
self,
encoder: &mut Encoder<'_, D>,
offset: usize,
depth: Depth,
) -> Result<()> {
encoder.debug_check_bounds::<Array<T, N>>(offset);
encode_array_value::<T, D>(self, encoder, offset, depth)
}
}
unsafe impl<T: ResourceTypeMarker, const N: usize, D: ResourceDialect> Encode<Array<T, N>, D>
for &mut [<T as TypeMarker>::Owned; N]
where
for<'q> T::Borrowed<'q>: Encode<T, D>,
{
#[inline]
unsafe fn encode(
self,
encoder: &mut Encoder<'_, D>,
offset: usize,
depth: Depth,
) -> Result<()> {
encoder.debug_check_bounds::<Array<T, N>>(offset);
encode_array_resource::<T, D>(self, encoder, offset, depth)
}
}
impl<T: TypeMarker, const N: usize, D: ResourceDialect> Decode<Array<T, N>, D> for [T::Owned; N]
where
T::Owned: Decode<T, D>,
{
#[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<'_, D>,
offset: usize,
depth: Depth,
) -> Result<()> {
decoder.debug_check_bounds::<Array<T, N>>(offset);
decode_array::<T, D>(self, decoder, offset, depth)
}
}
#[inline]
unsafe fn encode_array_value<T: ValueTypeMarker, D: ResourceDialect>(
slice: &[T::Owned],
encoder: &mut Encoder<'_, D>,
offset: usize,
depth: Depth,
) -> Result<()>
where
for<'a> T::Borrowed<'a>: Encode<T, D>,
{
let stride = T::inline_size(encoder.context);
let len = slice.len();
debug_assert_ne!(len, 0);
if T::encode_is_copy() {
debug_assert_eq!(stride, mem::size_of::<T::Owned>());
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 {
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 + TypeMarker, D: ResourceDialect>(
slice: &mut [T::Owned],
encoder: &mut Encoder<'_, D>,
offset: usize,
depth: Depth,
) -> Result<()>
where
for<'a> T::Borrowed<'a>: Encode<T, D>,
{
let stride = T::inline_size(encoder.context);
let len = slice.len();
debug_assert_ne!(len, 0);
if T::encode_is_copy() {
debug_assert_eq!(stride, mem::size_of::<T::Owned>());
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 {
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, D: ResourceDialect>(
slice: &mut [T::Owned],
decoder: &mut Decoder<'_, D>,
offset: usize,
depth: Depth,
) -> Result<()>
where
T::Owned: Decode<T, D>,
{
let stride = T::inline_size(decoder.context);
let len = slice.len();
debug_assert_ne!(len, 0);
if T::decode_is_copy() {
debug_assert_eq!(stride, mem::size_of::<T::Owned>());
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 {
let item = unsafe { slice.get_unchecked_mut(i) };
item.decode(decoder, offset + i * stride, depth)?;
}
}
Ok(())
}
pub const MAX_BOUND: usize = usize::MAX;
pub struct Vector<T: TypeMarker, const N: usize>(PhantomData<T>);
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 as TypeMarker>::Owned) -> Self::Borrowed<'_> {
value.as_mut_slice()
}
}
unsafe impl<T: ValueTypeMarker, const N: usize, D: ResourceDialect> Encode<Vector<T, N>, D>
for &[<T as TypeMarker>::Owned]
where
for<'q> T::Borrowed<'q>: Encode<T, D>,
{
#[inline]
unsafe fn encode(
self,
encoder: &mut Encoder<'_, D>,
offset: usize,
depth: Depth,
) -> Result<()> {
encoder.debug_check_bounds::<Vector<T, N>>(offset);
encode_vector_value::<T, D>(self, N, check_vector_length, encoder, offset, depth)
}
}
unsafe impl<T: ResourceTypeMarker + TypeMarker, const N: usize, D: ResourceDialect>
Encode<Vector<T, N>, D> for &mut [<T as TypeMarker>::Owned]
where
for<'q> T::Borrowed<'q>: Encode<T, D>,
{
#[inline]
unsafe fn encode(
self,
encoder: &mut Encoder<'_, D>,
offset: usize,
depth: Depth,
) -> Result<()> {
encoder.debug_check_bounds::<Vector<T, N>>(offset);
encode_vector_resource::<T, D>(self, N, encoder, offset, depth)
}
}
impl<T: TypeMarker, const N: usize, D: ResourceDialect> Decode<Vector<T, N>, D> for Vec<T::Owned>
where
T::Owned: Decode<T, D>,
{
#[inline(always)]
fn new_empty() -> Self {
Vec::new()
}
#[inline]
unsafe fn decode(
&mut self,
decoder: &mut Decoder<'_, D>,
offset: usize,
depth: Depth,
) -> Result<()> {
decoder.debug_check_bounds::<Vector<T, N>>(offset);
decode_vector::<T, D>(self, N, decoder, offset, depth)
}
}
#[inline]
unsafe fn encode_vector_value<T: ValueTypeMarker, D: ResourceDialect>(
slice: &[<T as TypeMarker>::Owned],
max_length: usize,
check_length: impl Fn(usize, usize) -> Result<()>,
encoder: &mut Encoder<'_, D>,
offset: usize,
mut depth: Depth,
) -> Result<()>
where
for<'a> T::Borrowed<'a>: Encode<T, D>,
{
encoder.write_num(slice.len() as u64, offset);
encoder.write_num(ALLOC_PRESENT_U64, offset + 8);
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, D>(slice, encoder, offset, depth)
}
#[inline]
unsafe fn encode_vector_resource<T: ResourceTypeMarker + TypeMarker, D: ResourceDialect>(
slice: &mut [T::Owned],
max_length: usize,
encoder: &mut Encoder<'_, D>,
offset: usize,
mut depth: Depth,
) -> Result<()>
where
for<'a> T::Borrowed<'a>: Encode<T, D>,
{
encoder.write_num(slice.len() as u64, offset);
encoder.write_num(ALLOC_PRESENT_U64, offset + 8);
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, D>(slice, encoder, offset, depth)
}
#[inline]
unsafe fn decode_vector<T: TypeMarker, D: ResourceDialect>(
vec: &mut Vec<T::Owned>,
max_length: usize,
decoder: &mut Decoder<'_, D>,
offset: usize,
mut depth: Depth,
) -> Result<()>
where
T::Owned: Decode<T, D>,
{
let Some(len) = decode_vector_header(decoder, offset)? else {
return Err(Error::NotNullable);
};
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() {
unsafe {
resize_vec_no_zeroing(vec, len);
}
} else {
vec.resize_with(len, T::Owned::new_empty);
}
decode_array::<T, D>(vec, decoder, offset, depth)?;
Ok(())
}
#[doc(hidden)] #[inline]
pub fn decode_vector_header<D: ResourceDialect>(
decoder: &mut Decoder<'_, D>,
offset: usize,
) -> Result<Option<usize>> {
let len = decoder.read_num::<u64>(offset) as usize;
match decoder.read_num::<u64>(offset + 8) {
ALLOC_PRESENT_U64 => {
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(())
}
pub struct BoundedString<const N: usize>;
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<const N: usize, D: ResourceDialect> Encode<BoundedString<N>, D> for &str {
#[inline]
unsafe fn encode(
self,
encoder: &mut Encoder<'_, D>,
offset: usize,
depth: Depth,
) -> Result<()> {
encoder.debug_check_bounds::<BoundedString<N>>(offset);
encode_vector_value::<u8, D>(
self.as_bytes(),
N,
check_string_length,
encoder,
offset,
depth,
)
}
}
impl<const N: usize, D: ResourceDialect> Decode<BoundedString<N>, D> for String {
#[inline(always)]
fn new_empty() -> Self {
String::new()
}
#[inline]
unsafe fn decode(
&mut self,
decoder: &mut Decoder<'_, D>,
offset: usize,
depth: Depth,
) -> Result<()> {
decoder.debug_check_bounds::<BoundedString<N>>(offset);
decode_string(self, N, decoder, offset, depth)
}
}
#[inline]
fn decode_string<D: ResourceDialect>(
string: &mut String,
max_length: usize,
decoder: &mut Decoder<'_, D>,
offset: usize,
mut depth: Depth,
) -> Result<()> {
let Some(len) = decode_vector_header(decoder, offset)? else {
return Err(Error::NotNullable);
};
if len == 0 {
return Ok(());
}
check_string_length(len, max_length)?;
depth.increment()?;
let offset = unsafe { decoder.out_of_line_offset(len)? };
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(())
}
impl<T: HandleBased> EncodableAsHandle for T {
type Dialect = DefaultFuchsiaResourceDialect;
}
pub struct HandleType<T: EncodableAsHandle, const OBJECT_TYPE: u32, const RIGHTS: u32>(
PhantomData<T>,
);
pub type Endpoint<T> = HandleType<
T,
{ crate::ObjectType::CHANNEL.into_raw() },
{ crate::Rights::CHANNEL_DEFAULT.bits() },
>;
unsafe impl<T: 'static + EncodableAsHandle, 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 + EncodableAsHandle, 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 as TypeMarker>::Owned) -> Self::Borrowed<'_> {
mem::replace(value, <T::Dialect as ResourceDialect>::Handle::invalid().into())
}
}
unsafe impl<T: 'static + EncodableAsHandle, const OBJECT_TYPE: u32, const RIGHTS: u32>
Encode<HandleType<T, OBJECT_TYPE, RIGHTS>, T::Dialect> for T
{
#[inline]
unsafe fn encode(
self,
encoder: &mut Encoder<'_, T::Dialect>,
offset: usize,
_depth: Depth,
) -> Result<()> {
encoder.debug_check_bounds::<HandleType<T, OBJECT_TYPE, RIGHTS>>(offset);
encode_handle(
self.into(),
crate::ObjectType::from_raw(OBJECT_TYPE),
crate::Rights::from_bits_retain(RIGHTS),
encoder,
offset,
)
}
}
impl<T: 'static + EncodableAsHandle, const OBJECT_TYPE: u32, const RIGHTS: u32>
Decode<HandleType<T, OBJECT_TYPE, RIGHTS>, T::Dialect> for T
{
#[inline(always)]
fn new_empty() -> Self {
<T::Dialect as ResourceDialect>::Handle::invalid().into()
}
#[inline]
unsafe fn decode(
&mut self,
decoder: &mut Decoder<'_, T::Dialect>,
offset: usize,
_depth: Depth,
) -> Result<()> {
decoder.debug_check_bounds::<HandleType<T, OBJECT_TYPE, RIGHTS>>(offset);
*self = decode_handle(
crate::ObjectType::from_raw(OBJECT_TYPE),
crate::Rights::from_bits_retain(RIGHTS),
decoder,
offset,
)?
.into();
Ok(())
}
}
#[inline]
unsafe fn encode_handle<D: ResourceDialect>(
handle: D::Handle,
object_type: crate::ObjectType,
rights: crate::Rights,
encoder: &mut Encoder<'_, D>,
offset: usize,
) -> Result<()> {
if handle.is_invalid() {
return Err(Error::NotNullable);
}
encoder.write_num(ALLOC_PRESENT_U32, offset);
encoder.handles.push(<D::ProxyChannel as ProxyChannelFor<D>>::HandleDisposition::from_handle(
handle,
object_type,
rights,
));
Ok(())
}
#[inline]
unsafe fn decode_handle<D: ResourceDialect>(
object_type: crate::ObjectType,
rights: crate::Rights,
decoder: &mut Decoder<'_, D>,
offset: usize,
) -> Result<D::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)
}
pub struct Optional<T: TypeMarker>(PhantomData<T>);
pub struct OptionalUnion<T: TypeMarker>(PhantomData<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 + TypeMarker> ResourceTypeMarker for Optional<T> {
type Borrowed<'a> = Option<T::Borrowed<'a>>;
#[inline(always)]
fn take_or_borrow(value: &mut <Self as TypeMarker>::Owned) -> Self::Borrowed<'_> {
value.as_mut().map(T::take_or_borrow)
}
}
impl<T: ResourceTypeMarker + TypeMarker> ResourceTypeMarker for OptionalUnion<T> {
type Borrowed<'a> = Option<T::Borrowed<'a>>;
#[inline(always)]
fn take_or_borrow(value: &mut <Self as TypeMarker>::Owned) -> Self::Borrowed<'_> {
value.as_deref_mut().map(T::take_or_borrow)
}
}
impl<T: ResourceTypeMarker + TypeMarker> ResourceTypeMarker for Boxed<T> {
type Borrowed<'a> = Option<T::Borrowed<'a>>;
#[inline(always)]
fn take_or_borrow(value: &mut <Self as TypeMarker>::Owned) -> Self::Borrowed<'_> {
value.as_deref_mut().map(T::take_or_borrow)
}
}
unsafe impl<T: TypeMarker, E: Encode<T, D>, D: ResourceDialect> Encode<Optional<T>, D>
for Option<E>
{
#[inline]
unsafe fn encode(
self,
encoder: &mut Encoder<'_, D>,
offset: usize,
depth: Depth,
) -> Result<()> {
encoder.debug_check_bounds::<Optional<T>>(offset);
encode_naturally_optional::<T, E, D>(self, encoder, offset, depth)
}
}
unsafe impl<T: TypeMarker, E: Encode<T, D>, D: ResourceDialect> Encode<OptionalUnion<T>, D>
for Option<E>
{
#[inline]
unsafe fn encode(
self,
encoder: &mut Encoder<'_, D>,
offset: usize,
depth: Depth,
) -> Result<()> {
encoder.debug_check_bounds::<OptionalUnion<T>>(offset);
encode_naturally_optional::<T, E, D>(self, encoder, offset, depth)
}
}
unsafe impl<T: TypeMarker, E: Encode<T, D>, D: ResourceDialect> Encode<Boxed<T>, D> for Option<E> {
#[inline]
unsafe fn encode(
self,
encoder: &mut Encoder<'_, D>,
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, D: ResourceDialect> Decode<Optional<T>, D> for Option<T::Owned>
where
T::Owned: Decode<T, D>,
{
#[inline(always)]
fn new_empty() -> Self {
None
}
#[inline]
unsafe fn decode(
&mut self,
decoder: &mut Decoder<'_, D>,
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, D: ResourceDialect> Decode<OptionalUnion<T>, D> for Option<Box<T::Owned>>
where
T::Owned: Decode<T, D>,
{
#[inline(always)]
fn new_empty() -> Self {
None
}
#[inline]
unsafe fn decode(
&mut self,
decoder: &mut Decoder<'_, D>,
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, D: ResourceDialect> Decode<Boxed<T>, D> for Option<Box<T::Owned>>
where
T::Owned: Decode<T, D>,
{
#[inline(always)]
fn new_empty() -> Self {
None
}
#[inline]
unsafe fn decode(
&mut self,
decoder: &mut Decoder<'_, D>,
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),
}
}
}
#[inline]
unsafe fn encode_naturally_optional<T: TypeMarker, E: Encode<T, D>, D: ResourceDialect>(
value: Option<E>,
encoder: &mut Encoder<'_, D>,
offset: usize,
depth: Depth,
) -> Result<()> {
match value {
Some(val) => val.encode(encoder, offset, depth)?,
None => encoder.padding(offset, T::inline_size(encoder.context)),
}
Ok(())
}
#[inline]
fn check_for_presence<D: ResourceDialect>(
decoder: &Decoder<'_, D>,
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)
}
#[doc(hidden)] #[inline]
pub unsafe fn encode_in_envelope<T: TypeMarker, D: ResourceDialect>(
val: impl Encode<T, D>,
encoder: &mut Encoder<'_, D>,
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 {
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)] #[inline]
pub unsafe fn encode_in_envelope_optional<T: TypeMarker, D: ResourceDialect>(
val: Option<impl Encode<T, D>>,
encoder: &mut Encoder<'_, D>,
offset: usize,
depth: Depth,
) -> Result<()> {
match val {
None => encoder.write_num(0u64, offset),
Some(val) => encode_in_envelope(val, encoder, offset, depth)?,
}
Ok(())
}
#[doc(hidden)] #[inline(always)]
pub unsafe fn decode_envelope_header<D: ResourceDialect>(
decoder: &mut Decoder<'_, D>,
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),
}
}
#[doc(hidden)] #[inline]
pub unsafe fn decode_unknown_envelope<D: ResourceDialect>(
decoder: &mut Decoder<'_, D>,
offset: usize,
mut depth: Depth,
) -> Result<()> {
if let Some((inlined, num_bytes, num_handles)) = decode_envelope_header(decoder, offset)? {
if !inlined {
depth.increment()?;
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(())
}
#[doc(hidden)] #[inline]
pub unsafe fn decode_union_inline_portion<D: ResourceDialect>(
decoder: &mut Decoder<'_, D>,
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),
}
}
pub struct ResultType<T: TypeMarker, E: TypeMarker>(PhantomData<(T, E)>);
pub struct FlexibleType<T: TypeMarker>(PhantomData<T>);
pub struct FlexibleResultType<T: TypeMarker, E: TypeMarker>(PhantomData<(T, E)>);
#[doc(hidden)] #[derive(Debug)]
pub enum Flexible<T> {
Ok(T),
FrameworkErr(FrameworkErr),
}
#[doc(hidden)] #[derive(Debug)]
pub enum FlexibleResult<T, E> {
Ok(T),
DomainErr(E),
FrameworkErr(FrameworkErr),
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(i32)]
pub enum FrameworkErr {
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::Owned) -> Self::Borrowed<'_> {
*value
}
}
unsafe impl<D: ResourceDialect> Encode<Self, D> for FrameworkErr {
#[inline]
unsafe fn encode(
self,
encoder: &mut Encoder<'_, D>,
offset: usize,
_depth: Depth,
) -> Result<()> {
encoder.debug_check_bounds::<Self>(offset);
encoder.write_num(self.into_primitive(), offset);
Ok(())
}
}
impl<D: ResourceDialect> Decode<Self, D> for FrameworkErr {
#[inline(always)]
fn new_empty() -> Self {
Self::UnknownMethod
}
#[inline]
unsafe fn decode(
&mut self,
decoder: &mut Decoder<'_, D>,
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> {
pub fn new(value: T) -> Self {
Self::Ok(value)
}
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> {
pub fn new(result: std::result::Result<T, E>) -> Self {
match result {
Ok(value) => Self::Ok(value),
Err(err) => Self::DomainErr(err),
}
}
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 })
}
}
}
}
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<D: ResourceDialect, $($type_param: TypeMarker, $encode_param: Encode<$type_param, D>),*> Encode<$ty, D> for $encode {
#[inline]
unsafe fn encode(self, encoder: &mut Encoder<'_, D>, 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, D>(val, encoder, offset + 8, depth)
}
)*
}
}
}
impl<D: ResourceDialect, $($type_param: TypeMarker),*> Decode<$ty, D> for $owned
where $($type_param::Owned: Decode<$type_param, D>),*
{
#[inline(always)]
fn new_empty() -> Self {
#![allow(unreachable_code)]
$(
return $($member_ctor)*(new_empty!($member_ty, D));
)*
}
#[inline]
unsafe fn decode(&mut self, decoder: &mut Decoder<'_, D>, 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 {
} else {
*self = $($member_ctor)*(new_empty!($member_ty, D));
}
#[allow(irrefutable_let_patterns)]
if let $($member_ctor)*(ref mut val) = self {
decode!($member_ty, D, 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, },
]
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct EpitaphBody {
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::Owned) -> Self::Borrowed<'_> {
value
}
}
unsafe impl<D: ResourceDialect> Encode<EpitaphBody, D> for &EpitaphBody {
#[inline]
unsafe fn encode(
self,
encoder: &mut Encoder<'_, D>,
offset: usize,
_depth: Depth,
) -> Result<()> {
encoder.debug_check_bounds::<EpitaphBody>(offset);
encoder.write_num::<i32>(self.error.into_raw(), offset);
Ok(())
}
}
impl<D: ResourceDialect> Decode<Self, D> 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<'_, D>,
offset: usize,
_depth: Depth,
) -> Result<()> {
decoder.debug_check_bounds::<Self>(offset);
self.error = zx_status::Status::from_raw(decoder.read_num::<i32>(offset));
Ok(())
}
}
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::Owned) -> Self::Borrowed<'_> {
*value
}
}
unsafe impl<D: ResourceDialect> Encode<ObjectType, D> for ObjectType {
#[inline]
unsafe fn encode(
self,
encoder: &mut Encoder<'_, D>,
offset: usize,
_depth: Depth,
) -> Result<()> {
encoder.debug_check_bounds::<Self>(offset);
encoder.write_num(self.into_raw(), offset);
Ok(())
}
}
impl<D: ResourceDialect> Decode<Self, D> for ObjectType {
#[inline(always)]
fn new_empty() -> Self {
ObjectType::NONE
}
#[inline]
unsafe fn decode(
&mut self,
decoder: &mut Decoder<'_, D>,
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::Owned) -> Self::Borrowed<'_> {
*value
}
}
unsafe impl<D: ResourceDialect> Encode<Rights, D> for Rights {
#[inline]
unsafe fn encode(
self,
encoder: &mut Encoder<'_, D>,
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<D: ResourceDialect> Decode<Self, D> for Rights {
#[inline(always)]
fn new_empty() -> Self {
Rights::empty()
}
#[inline]
unsafe fn decode(
&mut self,
decoder: &mut Decoder<'_, D>,
offset: usize,
_depth: Depth,
) -> Result<()> {
decoder.debug_check_bounds::<Self>(offset);
*self = Self::from_bits(decoder.read_num(offset)).ok_or(Error::InvalidBitsValue)?;
Ok(())
}
}
pub struct GenericMessageType<H: ValueTypeMarker, T: TypeMarker>(PhantomData<(H, T)>);
pub struct GenericMessage<H, E> {
pub header: H,
pub body: E,
}
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, D>, D: ResourceDialect>
Encode<GenericMessageType<H, T>, D> for GenericMessage<<H as TypeMarker>::Owned, E>
where
for<'a> H::Borrowed<'a>: Encode<H, D>,
{
#[inline]
unsafe fn encode(
self,
encoder: &mut Encoder<'_, D>,
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, D: ResourceDialect> Decode<GenericMessageType<H, T>, D>
for GenericMessageOwned
{
fn new_empty() -> Self {
panic!("cannot create GenericMessageOwned");
}
unsafe fn decode(
&mut self,
_decoder: &mut Decoder<'_, D>,
_offset: usize,
_depth: Depth,
) -> Result<()> {
match *self {}
}
}
pub type TransactionMessageType<T> = GenericMessageType<TransactionHeader, T>;
pub type TransactionMessage<E> = GenericMessage<TransactionHeader, E>;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(C)]
pub struct TransactionHeader {
pub tx_id: u32,
pub at_rest_flags: [u8; 2],
pub dynamic_flags: u8,
pub magic_number: u8,
pub ordinal: u64,
}
impl TransactionHeader {
#[inline]
pub fn is_compatible(&self) -> bool {
self.magic_number == MAGIC_NUMBER_INITIAL
}
}
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AtRestFlags: u16 {
const USE_V2_WIRE_FORMAT = 2;
}
}
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DynamicFlags: u8 {
const FLEXIBLE = 1 << 7;
}
}
impl From<AtRestFlags> for [u8; 2] {
#[inline]
fn from(value: AtRestFlags) -> Self {
value.bits().to_le_bytes()
}
}
impl TransactionHeader {
#[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,
)
}
#[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,
}
}
#[inline]
pub fn is_epitaph(&self) -> bool {
self.ordinal == EPITAPH_ORDINAL
}
#[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(())
}
#[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(()),
}
}
#[inline]
pub fn at_rest_flags(&self) -> AtRestFlags {
AtRestFlags::from_bits_truncate(u16::from_le_bytes(self.at_rest_flags))
}
#[inline]
pub fn dynamic_flags(&self) -> DynamicFlags {
DynamicFlags::from_bits_truncate(self.dynamic_flags)
}
#[inline]
pub fn decoding_context(&self) -> Context {
Context { wire_format_version: WireFormatVersion::V2 }
}
}
pub fn decode_transaction_header(bytes: &[u8]) -> Result<(TransactionHeader, &[u8])> {
let mut header = new_empty!(TransactionHeader, NoHandleResourceDialect);
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::<NoHandleResourceDialect>::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::Owned) -> Self::Borrowed<'_> {
value
}
}
unsafe impl<D: ResourceDialect> Encode<TransactionHeader, D> for &TransactionHeader {
#[inline]
unsafe fn encode(
self,
encoder: &mut Encoder<'_, D>,
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<D: ResourceDialect> Decode<Self, D> 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<'_, D>,
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(())
}
}
pub struct TlsBuf<D: ResourceDialect> {
bytes: Vec<u8>,
encode_handles: Vec<<D::ProxyChannel as ProxyChannelFor<D>>::HandleDisposition>,
decode_handles: Vec<<D::Handle as HandleFor<D>>::HandleInfo>,
}
impl<D: ResourceDialect> Default for TlsBuf<D> {
fn default() -> TlsBuf<D> {
TlsBuf {
bytes: Vec::with_capacity(MIN_BUF_BYTES_SIZE),
encode_handles: Vec::new(),
decode_handles: Vec::new(),
}
}
}
#[inline]
fn with_tls_buf<D: ResourceDialect, R>(f: impl FnOnce(&mut TlsBuf<D>) -> R) -> R {
D::with_tls_buf(f)
}
pub(crate) const MIN_BUF_BYTES_SIZE: usize = 512;
#[inline]
pub fn with_tls_encode_buf<R, D: ResourceDialect>(
f: impl FnOnce(
&mut Vec<u8>,
&mut Vec<<D::ProxyChannel as ProxyChannelFor<D>>::HandleDisposition>,
) -> R,
) -> R {
with_tls_buf::<D, R>(|buf| {
let res = f(&mut buf.bytes, &mut buf.encode_handles);
buf.bytes.clear();
buf.encode_handles.clear();
res
})
}
#[inline]
pub fn with_tls_decode_buf<R, D: ResourceDialect>(
f: impl FnOnce(&mut Vec<u8>, &mut Vec<<D::Handle as HandleFor<D>>::HandleInfo>) -> R,
) -> R {
with_tls_buf::<D, R>(|buf| {
let res = f(&mut buf.bytes, &mut buf.decode_handles);
buf.bytes.clear();
buf.decode_handles.clear();
res
})
}
#[inline]
pub fn with_tls_encoded<T: TypeMarker, D: ResourceDialect, Out>(
val: impl Encode<T, D>,
f: impl FnOnce(
&mut Vec<u8>,
&mut Vec<<D::ProxyChannel as ProxyChannelFor<D>>::HandleDisposition>,
) -> Result<Out>,
) -> Result<Out> {
with_tls_encode_buf::<Result<Out>, D>(|bytes, handles| {
Encoder::<D>::encode(bytes, handles, val)?;
f(bytes, handles)
})
}
#[cfg(test)]
mod test {
#![allow(dead_code)]
use super::*;
use crate::handle::{convert_handle_dispositions_to_infos, AsHandleRef};
use crate::time::{BootInstant, BootTicks, MonotonicInstant, MonotonicTicks};
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, DefaultFuchsiaResourceDialect>,
) -> T::Owned
where
T::Owned: Decode<T, DefaultFuchsiaResourceDialect>,
{
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::<DefaultFuchsiaResourceDialect>::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, DefaultFuchsiaResourceDialect>,
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 + Decode<T, DefaultFuchsiaResourceDialect>,
for<'a> T::Borrowed<'a>: Encode<T, DefaultFuchsiaResourceDialect>,
{
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 + Decode<T, DefaultFuchsiaResourceDialect>,
for<'a> T::Borrowed<'a>: Encode<T, DefaultFuchsiaResourceDialect>,
{
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_instants() {
let monotonic = MonotonicInstant::from_nanos(987654321);
let boot = BootInstant::from_nanos(987654321);
let monotonic_ticks = MonotonicTicks::from_raw(111111111);
let boot_ticks = BootTicks::from_raw(22222222);
for ctx in CONTEXTS {
assert_eq!(encode_decode::<BootInstant>(ctx, boot), boot);
assert_eq!(encode_decode::<MonotonicInstant>(ctx, monotonic), monotonic);
assert_eq!(encode_decode::<BootTicks>(ctx, boot_ticks), boot_ticks);
assert_eq!(encode_decode::<MonotonicTicks>(ctx, monotonic_ticks), monotonic_ticks);
}
}
#[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 + Decode<T, DefaultFuchsiaResourceDialect>,
for<'a> T::Borrowed<'a>: Encode<T, DefaultFuchsiaResourceDialect>,
{
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 {
encode_assert_bytes::<EmptyPayload>(ctx, (), &[]);
}
encode_assert_bytes::<ResultType<EmptyStruct, i32>>(
Context { wire_format_version: WireFormatVersion::V2 },
Ok::<(), i32>(()),
&[
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, ],
);
}
#[test]
fn result_decode_empty_ok_value() {
let mut result = Err(0);
Decoder::<DefaultFuchsiaResourceDialect>::decode_with_context::<ResultType<EmptyStruct, u32>>(
Context { wire_format_version: WireFormatVersion::V2 },
&[
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, ],
&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] {
let bytes = [
ordinal, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, ];
let mut out = new_empty!(Res, DefaultFuchsiaResourceDialect);
assert_matches!(
Decoder::<DefaultFuchsiaResourceDialect>::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] {
let bytes = [
ordinal, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, ];
let mut out = new_empty!(Res, DefaultFuchsiaResourceDialect);
assert_matches!(
Decoder::<DefaultFuchsiaResourceDialect>::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] = &[
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
];
let handle_buf = &mut Vec::<HandleInfo>::new();
let mut out = new_empty!(Res, DefaultFuchsiaResourceDialect);
let res = Decoder::<DefaultFuchsiaResourceDialect>::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] = &[
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
];
let handle_buf = &mut Vec::<HandleInfo>::new();
let mut out = new_empty!(Res, DefaultFuchsiaResourceDialect);
let res = Decoder::<DefaultFuchsiaResourceDialect>::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::<DefaultFuchsiaResourceDialect>::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::<DefaultFuchsiaResourceDialect>::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, DefaultFuchsiaResourceDialect);
Decoder::<DefaultFuchsiaResourceDialect>::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, DefaultFuchsiaResourceDialect);
Decoder::<DefaultFuchsiaResourceDialect>::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::<DefaultFuchsiaResourceDialect>::decode_with_context::<EmptyPayload>(
ctx,
&[0],
&mut [],
&mut ()
),
Err(Error::ExtraBytes)
);
assert_matches!(
Decoder::<DefaultFuchsiaResourceDialect>::decode_with_context::<EmptyPayload>(
ctx,
&[],
&mut [HandleInfo::new(Handle::invalid(), ObjectType::NONE, Rights::NONE,)],
&mut ()
),
Err(Error::ExtraHandles)
);
}
}
#[test]
fn encode_default_context() {
let buf = &mut Vec::new();
Encoder::<DefaultFuchsiaResourceDialect>::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::<DefaultFuchsiaResourceDialect>::encode_with_context::<T>(
ctx, buf, handle_buf, handle,
)
.expect("Encoding failed");
assert_eq!(handle_buf.len(), 1);
assert!(handle_buf[0].is_move());
assert_eq!(handle_buf[0].raw_handle(), raw_handle);
let mut handle_out = new_empty!(T);
Decoder::<DefaultFuchsiaResourceDialect>::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,
"decoded handle must match encoded handle"
);
}
}
#[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::<DefaultFuchsiaResourceDialect>::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::<DefaultFuchsiaResourceDialect>::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, DefaultFuchsiaResourceDialect);
Decoder::<DefaultFuchsiaResourceDialect>::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);
}
}
}