use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OpaquePayloadTruncated {
pub field: &'static str,
pub expected: usize,
pub available: usize,
}
impl fmt::Display for OpaquePayloadTruncated {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"opaque payload truncated while reading `{}`: expected {} bytes, got {}. \
Fix: the extension author's encoder must emit {} byte(s) for this field \
via `to_le_bytes`; do not truncate or pack-substitute.",
self.field, self.expected, self.available, self.expected,
)
}
}
impl std::error::Error for OpaquePayloadTruncated {}
macro_rules! impl_le_codec {
($ty:ty, $push:ident, $read:ident, $width:expr) => {
#[doc = concat!("Append `value` to `buf` as `",
stringify!($ty), "::to_le_bytes`.")]
pub fn $push(buf: &mut Vec<u8>, value: $ty) {
buf.extend_from_slice(&value.to_le_bytes());
}
#[doc = concat!("Read the first `",
stringify!($width),
"` bytes of `bytes` as a little-endian `", stringify!($ty),
"`, returning the decoded value and the remaining tail. \
Returns [`OpaquePayloadTruncated`] if `bytes` is shorter than \
the fixed width.")]
pub fn $read(bytes: &[u8]) -> Result<($ty, &[u8]), OpaquePayloadTruncated> {
let width = $width;
if bytes.len() < width {
return Err(OpaquePayloadTruncated {
field: stringify!($ty),
expected: width,
available: bytes.len(),
});
}
let (head, tail) = bytes.split_at(width);
let mut array = [0u8; $width];
array.copy_from_slice(head);
Ok((<$ty>::from_le_bytes(array), tail))
}
};
}
impl_le_codec!(u16, push_u16, read_u16, 2);
impl_le_codec!(u32, push_u32, read_u32, 4);
impl_le_codec!(u64, push_u64, read_u64, 8);
impl_le_codec!(i16, push_i16, read_i16, 2);
impl_le_codec!(i32, push_i32, read_i32, 4);
impl_le_codec!(i64, push_i64, read_i64, 8);
impl_le_codec!(f32, push_f32, read_f32, 4);
impl_le_codec!(f64, push_f64, read_f64, 8);
#[derive(Debug, Clone, Default)]
pub struct LeBytesWriter {
buf: Vec<u8>,
}
impl LeBytesWriter {
#[must_use]
pub fn new() -> Self {
Self { buf: Vec::new() }
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
buf: Vec::with_capacity(capacity),
}
}
pub fn push_u16(&mut self, value: u16) {
push_u16(&mut self.buf, value);
}
pub fn push_u32(&mut self, value: u32) {
push_u32(&mut self.buf, value);
}
pub fn push_u64(&mut self, value: u64) {
push_u64(&mut self.buf, value);
}
pub fn push_i16(&mut self, value: i16) {
push_i16(&mut self.buf, value);
}
pub fn push_i32(&mut self, value: i32) {
push_i32(&mut self.buf, value);
}
pub fn push_i64(&mut self, value: i64) {
push_i64(&mut self.buf, value);
}
pub fn push_f32(&mut self, value: f32) {
push_f32(&mut self.buf, value);
}
pub fn push_f64(&mut self, value: f64) {
push_f64(&mut self.buf, value);
}
pub fn push_slice(&mut self, slice: &[u8]) {
self.buf.extend_from_slice(slice);
}
#[must_use]
pub fn into_inner(self) -> Vec<u8> {
self.buf
}
}
impl From<LeBytesWriter> for Vec<u8> {
fn from(writer: LeBytesWriter) -> Self {
writer.buf
}
}