#[cfg(test)]
mod tests;
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
use std::path::PathBuf;
use thiserror::Error;
pub const MAX_BYTE_LEN: u32 = 256 * 1024 * 1024;
pub const MAX_VEC_ELEMENTS: u32 = 16 * 1024 * 1024;
#[derive(Debug, Error)]
pub enum EncodingError {
#[error("unexpected end of buffer (need {needed} bytes, have {available})")]
UnexpectedEof {
needed: usize,
available: usize,
},
#[error("invalid tag {tag} for {type_name}")]
InvalidTag {
tag: u32,
type_name: &'static str,
},
#[error("invalid bool byte: 0x{0:02X} (expected 0x00 or 0x01)")]
InvalidBool(u8),
#[error("invalid UTF-8: {0}")]
InvalidUtf8(#[from] std::string::FromUtf8Error),
#[error("length overflow: {0}")]
LengthOverflow(String),
#[error("{0}")]
Custom(String),
}
pub trait Encode {
fn encode_to(&self, buf: &mut Vec<u8>) -> Result<(), EncodingError>;
}
pub trait Decode: Sized {
fn decode_from(buf: &[u8]) -> Result<(Self, usize), EncodingError>;
}
pub fn encode_to_vec<T: Encode>(value: &T) -> Result<Vec<u8>, EncodingError> {
let mut buf = Vec::new();
value.encode_to(&mut buf)?;
Ok(buf)
}
pub fn decode_from_slice<T: Decode>(buf: &[u8]) -> Result<(T, usize), EncodingError> {
T::decode_from(buf)
}
#[inline]
fn require(buf: &[u8], needed: usize) -> Result<(), EncodingError> {
if buf.len() < needed {
Err(EncodingError::UnexpectedEof {
needed,
available: buf.len(),
})
} else {
Ok(())
}
}
#[inline]
fn len_to_u32(len: usize) -> Result<u32, EncodingError> {
u32::try_from(len)
.map_err(|_| EncodingError::LengthOverflow(format!("length {len} exceeds u32::MAX")))
}
impl Encode for u8 {
#[inline]
fn encode_to(&self, buf: &mut Vec<u8>) -> Result<(), EncodingError> {
buf.push(*self);
Ok(())
}
}
impl Decode for u8 {
#[inline]
fn decode_from(buf: &[u8]) -> Result<(Self, usize), EncodingError> {
require(buf, 1)?;
Ok((buf[0], 1))
}
}
impl Encode for u16 {
#[inline]
fn encode_to(&self, buf: &mut Vec<u8>) -> Result<(), EncodingError> {
buf.extend_from_slice(&self.to_le_bytes());
Ok(())
}
}
impl Decode for u16 {
#[inline]
fn decode_from(buf: &[u8]) -> Result<(Self, usize), EncodingError> {
require(buf, 2)?;
Ok((u16::from_le_bytes([buf[0], buf[1]]), 2))
}
}
impl Encode for u32 {
#[inline]
fn encode_to(&self, buf: &mut Vec<u8>) -> Result<(), EncodingError> {
buf.extend_from_slice(&self.to_le_bytes());
Ok(())
}
}
impl Decode for u32 {
#[inline]
fn decode_from(buf: &[u8]) -> Result<(Self, usize), EncodingError> {
require(buf, 4)?;
let bytes: [u8; 4] = match buf[..4].try_into() {
Ok(b) => b,
Err(_) => {
return Err(EncodingError::Custom(
"internal: slice-to-array conversion failed for u32".into(),
));
}
};
Ok((u32::from_le_bytes(bytes), 4))
}
}
impl Encode for u64 {
#[inline]
fn encode_to(&self, buf: &mut Vec<u8>) -> Result<(), EncodingError> {
buf.extend_from_slice(&self.to_le_bytes());
Ok(())
}
}
impl Decode for u64 {
#[inline]
fn decode_from(buf: &[u8]) -> Result<(Self, usize), EncodingError> {
require(buf, 8)?;
let bytes: [u8; 8] = match buf[..8].try_into() {
Ok(b) => b,
Err(_) => {
return Err(EncodingError::Custom(
"internal: slice-to-array conversion failed for u64".into(),
));
}
};
Ok((u64::from_le_bytes(bytes), 8))
}
}
impl Encode for i64 {
#[inline]
fn encode_to(&self, buf: &mut Vec<u8>) -> Result<(), EncodingError> {
buf.extend_from_slice(&self.to_le_bytes());
Ok(())
}
}
impl Decode for i64 {
#[inline]
fn decode_from(buf: &[u8]) -> Result<(Self, usize), EncodingError> {
require(buf, 8)?;
let bytes: [u8; 8] = match buf[..8].try_into() {
Ok(b) => b,
Err(_) => {
return Err(EncodingError::Custom(
"internal: slice-to-array conversion failed for i64".into(),
));
}
};
Ok((i64::from_le_bytes(bytes), 8))
}
}
impl Encode for bool {
#[inline]
fn encode_to(&self, buf: &mut Vec<u8>) -> Result<(), EncodingError> {
buf.push(u8::from(*self));
Ok(())
}
}
impl Decode for bool {
#[inline]
fn decode_from(buf: &[u8]) -> Result<(Self, usize), EncodingError> {
require(buf, 1)?;
match buf[0] {
0 => Ok((false, 1)),
1 => Ok((true, 1)),
other => Err(EncodingError::InvalidBool(other)),
}
}
}
impl<const N: usize> Encode for [u8; N] {
#[inline]
fn encode_to(&self, buf: &mut Vec<u8>) -> Result<(), EncodingError> {
buf.extend_from_slice(self);
Ok(())
}
}
impl<const N: usize> Decode for [u8; N] {
#[inline]
fn decode_from(buf: &[u8]) -> Result<(Self, usize), EncodingError> {
require(buf, N)?;
let mut arr = [0u8; N];
arr.copy_from_slice(&buf[..N]);
Ok((arr, N))
}
}
impl Encode for Vec<u8> {
#[inline]
fn encode_to(&self, buf: &mut Vec<u8>) -> Result<(), EncodingError> {
len_to_u32(self.len())?.encode_to(buf)?;
buf.extend_from_slice(self);
Ok(())
}
}
impl Decode for Vec<u8> {
#[inline]
fn decode_from(buf: &[u8]) -> Result<(Self, usize), EncodingError> {
let (len, mut offset) = u32::decode_from(buf)?;
if len > MAX_BYTE_LEN {
return Err(EncodingError::LengthOverflow(format!(
"byte vector length {len} exceeds MAX_BYTE_LEN ({MAX_BYTE_LEN})"
)));
}
let len = len as usize;
require(&buf[offset..], len)?;
let data = buf[offset..offset + len].to_vec();
offset += len;
Ok((data, offset))
}
}
impl Encode for &[u8] {
#[inline]
fn encode_to(&self, buf: &mut Vec<u8>) -> Result<(), EncodingError> {
len_to_u32(self.len())?.encode_to(buf)?;
buf.extend_from_slice(self);
Ok(())
}
}
impl Encode for String {
#[inline]
fn encode_to(&self, buf: &mut Vec<u8>) -> Result<(), EncodingError> {
len_to_u32(self.len())?.encode_to(buf)?;
buf.extend_from_slice(self.as_bytes());
Ok(())
}
}
impl Decode for String {
#[inline]
fn decode_from(buf: &[u8]) -> Result<(Self, usize), EncodingError> {
let (raw, consumed) = Vec::<u8>::decode_from(buf)?;
let s = String::from_utf8(raw)?;
Ok((s, consumed))
}
}
impl Encode for &str {
#[inline]
fn encode_to(&self, buf: &mut Vec<u8>) -> Result<(), EncodingError> {
len_to_u32(self.len())?.encode_to(buf)?;
buf.extend_from_slice(self.as_bytes());
Ok(())
}
}
impl Encode for PathBuf {
#[inline]
fn encode_to(&self, buf: &mut Vec<u8>) -> Result<(), EncodingError> {
let raw = self.as_os_str().as_bytes();
len_to_u32(raw.len())?.encode_to(buf)?;
buf.extend_from_slice(raw);
Ok(())
}
}
impl Decode for PathBuf {
#[inline]
fn decode_from(buf: &[u8]) -> Result<(Self, usize), EncodingError> {
let (len, mut offset) = u32::decode_from(buf)?;
if len > MAX_BYTE_LEN {
return Err(EncodingError::LengthOverflow(format!(
"path length {len} exceeds MAX_BYTE_LEN ({MAX_BYTE_LEN})"
)));
}
let len = len as usize;
require(&buf[offset..], len)?;
let os_str = OsStr::from_bytes(&buf[offset..offset + len]);
offset += len;
Ok((PathBuf::from(os_str), offset))
}
}
impl<T: Encode> Encode for Option<T> {
#[inline]
fn encode_to(&self, buf: &mut Vec<u8>) -> Result<(), EncodingError> {
match self {
None => buf.push(0),
Some(val) => {
buf.push(1);
val.encode_to(buf)?;
}
}
Ok(())
}
}
impl<T: Decode> Decode for Option<T> {
#[inline]
fn decode_from(buf: &[u8]) -> Result<(Self, usize), EncodingError> {
require(buf, 1)?;
match buf[0] {
0 => Ok((None, 1)),
1 => {
let (val, consumed) = T::decode_from(&buf[1..])?;
Ok((Some(val), 1 + consumed))
}
other => Err(EncodingError::InvalidTag {
tag: other as u32,
type_name: "Option<T>",
}),
}
}
}
pub fn encode_vec<T: Encode>(items: &[T], buf: &mut Vec<u8>) -> Result<(), EncodingError> {
len_to_u32(items.len())?.encode_to(buf)?;
for item in items {
item.encode_to(buf)?;
}
Ok(())
}
pub fn decode_vec<T: Decode>(buf: &[u8]) -> Result<(Vec<T>, usize), EncodingError> {
let (count, mut offset) = u32::decode_from(buf)?;
if count > MAX_VEC_ELEMENTS {
return Err(EncodingError::LengthOverflow(format!(
"vector element count {count} exceeds MAX_VEC_ELEMENTS ({MAX_VEC_ELEMENTS})"
)));
}
let count = count as usize;
let mut items = Vec::with_capacity(count);
for _ in 0..count {
let (item, consumed) = T::decode_from(&buf[offset..])?;
offset += consumed;
items.push(item);
}
Ok((items, offset))
}