#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use core::mem::MaybeUninit as MU;
pub unsafe trait Reader<'r>: Sized {
fn read_i64(&mut self) -> ReadResult<i64>;
fn read_i32(&mut self) -> ReadResult<i32>;
fn read_i16(&mut self) -> ReadResult<i16>;
fn read_u64(&mut self) -> ReadResult<u64>;
fn read_u32(&mut self) -> ReadResult<u32>;
fn read_u16(&mut self) -> ReadResult<u16>;
fn read_f32(&mut self) -> ReadResult<f32>;
fn read_f64(&mut self) -> ReadResult<f64>;
fn read_opaque_fixed(&mut self, len: usize) -> ReadResult<&'r [u8]>;
fn read_opaque_variable(&mut self, max: u32) -> ReadResult<&'r [u8]>;
fn read_bool(&mut self) -> ReadResult<bool> {
match self.read_i32()? {
0 => Ok(false),
1 => Ok(true),
_ => Err(ReadError::bad_bool()),
}
}
fn read_opaque_fixed_into<'a>(
&mut self,
dst: &'a mut [MU<u8>],
len: u32,
) -> ReadResult<&'a mut [u8]> {
debug_assert_eq!(dst.len(), len as usize);
const _: () = assert!(core::mem::size_of::<usize>() >= core::mem::size_of::<u32>());
let data = self.read_opaque_fixed(len as usize)?;
let initialized = dst.write_copy_of_slice(data);
Ok(initialized)
}
fn read_opaque_variable_into<'a>(
&mut self,
dst: &'a mut [MU<u8>],
max: u32,
) -> ReadResult<&'a mut [u8]> {
debug_assert!(dst.len() >= max as usize);
let len = self.read_u32()?;
if len > max {
return Err(ReadError::max_length_exceeded());
}
self.read_opaque_fixed_into(&mut dst[..len as usize], len)
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
fn read_opaque_fixed_owned(&mut self, len: u32) -> ReadResult<Vec<u8>> {
let bytes = self.read_opaque_fixed(len as usize)?;
let buf = Vec::from(bytes); Ok(buf)
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
fn read_opaque_variable_owned(&mut self, max: u32) -> ReadResult<Vec<u8>> {
let bytes = self.read_opaque_variable(max)?;
let buf = Vec::from(bytes); Ok(buf)
}
}
pub type ReadResult<T> = core::result::Result<T, ReadError>;
#[cfg(feature = "detailed-errors")]
#[derive(Copy, Clone, Debug)]
pub enum ReadError {
SourceEmpty {
requested: usize,
remaining: usize,
},
SourceRemaining {
remaining: u64,
},
InvalidHeaderByte {
h: u8,
},
IntegerOutOfRange,
BadBool,
BadDiscriminator(i32),
MaxLengthExceeded,
NonZeroPadding,
BadUTF8,
NotMinimalEncoding,
}
#[cfg(not(feature = "detailed-errors"))]
#[derive(Copy, Clone, Debug)]
pub struct ReadError;
impl ReadError {
#[doc(hidden)]
#[inline]
pub fn source_empty(requested: usize, remaining: usize) -> Self {
#[cfg(feature = "detailed-errors")]
{
Self::SourceEmpty {
requested,
remaining,
}
}
#[cfg(not(feature = "detailed-errors"))]
{
let _ = (requested, remaining);
Self
}
}
#[doc(hidden)]
#[inline]
pub fn source_remaining(remaining: u64) -> Self {
#[cfg(feature = "detailed-errors")]
{
Self::SourceRemaining { remaining }
}
#[cfg(not(feature = "detailed-errors"))]
{
let _ = remaining;
Self
}
}
#[doc(hidden)]
#[inline]
pub fn invalid_header_byte(h: u8) -> Self {
#[cfg(feature = "detailed-errors")]
{
Self::InvalidHeaderByte { h }
}
#[cfg(not(feature = "detailed-errors"))]
{
let _ = h;
Self
}
}
#[doc(hidden)]
#[inline]
pub fn integer_out_of_range() -> Self {
#[cfg(feature = "detailed-errors")]
{
Self::IntegerOutOfRange
}
#[cfg(not(feature = "detailed-errors"))]
{
Self
}
}
#[doc(hidden)]
#[inline]
pub fn bad_bool() -> Self {
#[cfg(feature = "detailed-errors")]
{
Self::BadBool
}
#[cfg(not(feature = "detailed-errors"))]
{
Self
}
}
#[doc(hidden)]
#[inline]
pub fn bad_discriminator(discriminant: i32) -> Self {
#[cfg(feature = "detailed-errors")]
{
Self::BadDiscriminator(discriminant)
}
#[cfg(not(feature = "detailed-errors"))]
{
let _ = discriminant;
Self
}
}
#[doc(hidden)]
#[inline]
pub fn max_length_exceeded() -> Self {
#[cfg(feature = "detailed-errors")]
{
Self::MaxLengthExceeded
}
#[cfg(not(feature = "detailed-errors"))]
{
Self
}
}
#[doc(hidden)]
#[inline]
pub fn non_zero_padding() -> Self {
#[cfg(feature = "detailed-errors")]
{
Self::NonZeroPadding
}
#[cfg(not(feature = "detailed-errors"))]
{
Self
}
}
#[doc(hidden)]
#[inline]
pub fn bad_utf8() -> Self {
#[cfg(feature = "detailed-errors")]
{
Self::BadUTF8
}
#[cfg(not(feature = "detailed-errors"))]
{
Self
}
}
#[doc(hidden)]
#[inline]
pub fn not_minimal_encoding() -> Self {
#[cfg(feature = "detailed-errors")]
{
Self::NotMinimalEncoding
}
#[cfg(not(feature = "detailed-errors"))]
{
Self
}
}
}
#[cfg(feature = "detailed-errors")]
impl core::fmt::Display for ReadError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ReadError::SourceEmpty {
requested,
remaining,
} => write!(
f,
"unexpected end of input (requested: {requested}, remaining: {remaining})"
),
ReadError::SourceRemaining { remaining } => {
write!(f, "input remaining after decoding: {remaining} bytes")
}
ReadError::InvalidHeaderByte { h } => write!(f, "invalid header byte: 0x{h:02x}"),
ReadError::IntegerOutOfRange => write!(f, "decoded integer out of range"),
ReadError::BadBool => write!(f, "bad bool value"),
ReadError::BadDiscriminator(d) => write!(f, "unexpected discriminator value: {d}"),
ReadError::MaxLengthExceeded => write!(f, "length exceeds array maximum"),
ReadError::NonZeroPadding => write!(f, "non-zero XDR padding"),
ReadError::BadUTF8 => write!(f, "bad UTF8"),
ReadError::NotMinimalEncoding => write!(f, "not the minimal-size encoding"),
}
}
}
#[cfg(not(feature = "detailed-errors"))]
impl core::fmt::Display for ReadError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("XDR decoding failed")
}
}
impl core::error::Error for ReadError {}