use core::mem::{ManuallyDrop, MaybeUninit};
use core::ptr::addr_of_mut;
use munge::munge;
use crate::decoder::InternalHandleDecoder;
use crate::{
decode, encode, u16_le, u32_le, Decode, Decoder, DecoderExt as _, Encode, Encoder,
EncoderExt as _, Slot, CHUNK_SIZE,
};
#[derive(Clone, Copy)]
#[repr(C)]
struct Encoded {
maybe_num_bytes: u32_le,
num_handles: u16_le,
flags: u16_le,
}
#[repr(C, align(8))]
pub union WireEnvelope {
zero: [u8; 8],
encoded: Encoded,
decoded_inline: [MaybeUninit<u8>; 4],
decoded_out_of_line: *mut (),
}
impl WireEnvelope {
const IS_INLINE_BIT: u16 = 1;
pub fn encode_zero(slot: Slot<'_, Self>) {
munge!(let Self { mut zero } = slot);
*zero = [0; 8];
}
pub fn encode_value<E: Encoder + ?Sized, T: Encode<E>>(
value: &mut T,
encoder: &mut E,
slot: Slot<'_, Self>,
) -> Result<(), encode::EncodeError> {
munge! {
let Self {
encoded: Encoded {
mut maybe_num_bytes,
mut num_handles,
mut flags,
},
} = slot;
}
let handles_before = encoder.__internal_handle_count();
if size_of::<T::Encoded<'_>>() <= 4 {
let slot = unsafe { Slot::new_unchecked(maybe_num_bytes.as_mut_ptr().cast()) };
value.encode(encoder, slot)?;
*flags = u16_le::from_native(Self::IS_INLINE_BIT);
} else {
let bytes_before = encoder.bytes_written();
encoder.encode_next(value)?;
*maybe_num_bytes =
u32_le::from_native((encoder.bytes_written() - bytes_before).try_into().unwrap());
}
*num_handles = u16_le::from_native(
(encoder.__internal_handle_count() - handles_before).try_into().unwrap(),
);
Ok(())
}
pub fn zero() -> Self {
Self { zero: [0; 8] }
}
pub fn is_encoded_zero(slot: Slot<'_, Self>) -> bool {
munge!(let Self { zero } = slot);
*zero == [0; 8]
}
pub fn is_zero(&self) -> bool {
unsafe { self.zero == [0; 8] }
}
fn out_of_line_chunks(
maybe_num_bytes: Slot<'_, u32_le>,
flags: Slot<'_, u16_le>,
) -> Result<Option<usize>, decode::DecodeError> {
if flags.to_native() & Self::IS_INLINE_BIT == 0 {
let num_bytes = maybe_num_bytes.to_native();
if num_bytes as usize % CHUNK_SIZE != 0 {
return Err(decode::DecodeError::InvalidEnvelopeSize(num_bytes));
}
if num_bytes <= 4 {
return Err(decode::DecodeError::OutOfLineValueTooSmall(num_bytes));
}
Ok(Some(num_bytes as usize / CHUNK_SIZE))
} else {
Ok(None)
}
}
pub fn decode_unknown_static<D: InternalHandleDecoder + ?Sized>(
slot: Slot<'_, Self>,
decoder: &mut D,
) -> Result<(), decode::DecodeError> {
munge! {
let Self {
encoded: Encoded {
maybe_num_bytes,
num_handles,
flags,
},
} = slot;
}
if let Some(count) = Self::out_of_line_chunks(maybe_num_bytes, flags)? {
return Err(decode::DecodeError::ExpectedInline(count * CHUNK_SIZE));
}
decoder.__internal_take_handles(num_handles.to_native() as usize)?;
Ok(())
}
pub fn decode_unknown<'buf, D: Decoder<'buf> + ?Sized>(
slot: Slot<'_, Self>,
decoder: &mut D,
) -> Result<(), decode::DecodeError> {
munge! {
let Self {
encoded: Encoded {
maybe_num_bytes,
num_handles,
flags,
},
} = slot;
}
if let Some(count) = Self::out_of_line_chunks(maybe_num_bytes, flags)? {
decoder.take_chunks(count)?;
}
decoder.__internal_take_handles(num_handles.to_native() as usize)?;
Ok(())
}
pub fn decode_as_static<D: InternalHandleDecoder + ?Sized, T: Decode<D>>(
mut slot: Slot<'_, Self>,
decoder: &mut D,
) -> Result<(), decode::DecodeError> {
munge! {
let Self {
encoded: Encoded {
maybe_num_bytes,
num_handles,
flags,
},
} = slot.as_mut();
}
let handles_before = decoder.__internal_handles_remaining();
let num_handles = num_handles.to_native() as usize;
if let Some(count) = Self::out_of_line_chunks(maybe_num_bytes, flags)? {
return Err(decode::DecodeError::ExpectedInline(count * CHUNK_SIZE));
}
if size_of::<T>() > 4 {
return Err(decode::DecodeError::InlineValueTooBig(size_of::<T>()));
}
munge!(let Self { mut decoded_inline } = slot);
let mut slot = unsafe { Slot::<T>::new_unchecked(decoded_inline.as_mut_ptr().cast()) };
T::decode(slot.as_mut(), decoder)?;
let handles_consumed = handles_before - decoder.__internal_handles_remaining();
if handles_consumed != num_handles {
return Err(decode::DecodeError::IncorrectNumberOfHandlesConsumed {
expected: num_handles,
actual: handles_consumed,
});
}
Ok(())
}
pub fn decode_as<'buf, D: Decoder<'buf> + ?Sized, T: Decode<D>>(
mut slot: Slot<'_, Self>,
decoder: &mut D,
) -> Result<(), decode::DecodeError> {
munge! {
let Self {
encoded: Encoded {
mut maybe_num_bytes,
num_handles,
flags,
},
} = slot.as_mut();
}
let handles_before = decoder.__internal_handles_remaining();
let num_handles = num_handles.to_native() as usize;
let out_of_line_chunks = Self::out_of_line_chunks(maybe_num_bytes.as_mut(), flags)?;
if let Some(_count) = out_of_line_chunks {
let mut value_slot = decoder.take_slot::<T>()?;
let value_ptr = value_slot.as_mut_ptr();
T::decode(value_slot, decoder)?;
munge!(let Self { mut decoded_out_of_line } = slot);
unsafe { decoded_out_of_line.as_mut_ptr().write(value_ptr.cast()) };
} else {
if size_of::<T>() > 4 {
return Err(decode::DecodeError::InlineValueTooBig(size_of::<T>()));
}
munge!(let Self { mut decoded_inline } = slot);
let mut slot = unsafe { Slot::<T>::new_unchecked(decoded_inline.as_mut_ptr().cast()) };
T::decode(slot.as_mut(), decoder)?;
}
let handles_consumed = handles_before - decoder.__internal_handles_remaining();
if handles_consumed != num_handles {
return Err(decode::DecodeError::IncorrectNumberOfHandlesConsumed {
expected: num_handles,
actual: handles_consumed,
});
}
Ok(())
}
unsafe fn as_ptr<T>(this: *mut Self) -> *mut T {
if size_of::<T>() <= 4 {
let inline = unsafe { addr_of_mut!((*this).decoded_inline) };
inline.cast()
} else {
unsafe { (*this).decoded_out_of_line.cast() }
}
}
pub unsafe fn deref_unchecked<T>(&self) -> &T {
let ptr = unsafe { Self::as_ptr::<T>((self as *const Self).cast_mut()).cast_const() };
unsafe { &*ptr }
}
pub unsafe fn deref_mut_unchecked<T>(&mut self) -> &mut T {
let ptr = unsafe { Self::as_ptr::<T>(self as *mut Self) };
unsafe { &mut *ptr }
}
pub unsafe fn take_unchecked<T>(&mut self) -> T {
let ptr = unsafe { Self::as_ptr::<T>(self as *mut Self) };
let result = unsafe { ptr.read() };
*self = Self { zero: [0; 8] };
result
}
pub unsafe fn clone_unchecked<T: Clone>(&self) -> Self {
debug_assert_eq!(size_of::<T>(), 4);
union ClonedToDecodedInline<T> {
cloned: ManuallyDrop<T>,
decoded_inline: [MaybeUninit<u8>; 4],
}
let cloned = unsafe { self.deref_unchecked::<T>().clone() };
unsafe {
Self {
decoded_inline: ClonedToDecodedInline { cloned: ManuallyDrop::new(cloned) }
.decoded_inline,
}
}
}
}