#![allow(unsafe_code)]
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use core::fmt;
use core::mem::MaybeUninit;
pub use fuel_derive::{
Deserialize,
Serialize,
};
#[derive(Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum Error {
BufferIsTooShort,
UnknownDiscriminant,
InvalidPrefix,
AllocationLimit,
Unknown(&'static str),
}
impl Error {
pub(crate) fn as_str(&self) -> &'static str {
match self {
Error::BufferIsTooShort => "buffer is too short",
Error::UnknownDiscriminant => "unknown discriminant",
Error::InvalidPrefix => {
"prefix set with #[canonical(prefix = ...)] was invalid"
}
Error::AllocationLimit => "allocation too large",
Error::Unknown(str) => str,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.write_str(self.as_str())
}
}
pub trait Output {
fn write(&mut self, bytes: &[u8]) -> Result<(), Error>;
fn push_byte(&mut self, byte: u8) -> Result<(), Error> {
self.write(&[byte])
}
}
pub trait Serialize {
#[doc(hidden)]
const UNALIGNED_BYTES: bool = false;
fn size_static(&self) -> usize;
fn size_dynamic(&self) -> usize;
fn size(&self) -> usize {
self.size_static().saturating_add(self.size_dynamic())
}
fn encode<O: Output + ?Sized>(&self, buffer: &mut O) -> Result<(), Error> {
self.encode_static(buffer)?;
self.encode_dynamic(buffer)
}
fn encode_static<O: Output + ?Sized>(&self, buffer: &mut O) -> Result<(), Error>;
fn encode_dynamic<O: Output + ?Sized>(&self, _buffer: &mut O) -> Result<(), Error> {
Ok(())
}
#[cfg(feature = "alloc")]
fn to_bytes(&self) -> Vec<u8> {
let mut vec = Vec::with_capacity(self.size());
self.encode(&mut vec).expect("Unable to encode self");
vec
}
}
pub trait Input {
fn remaining(&mut self) -> usize;
fn peek(&self, buf: &mut [u8]) -> Result<(), Error>;
fn read(&mut self, buf: &mut [u8]) -> Result<(), Error>;
fn peek_byte(&mut self) -> Result<u8, Error> {
let mut buf = [0u8];
self.peek(&mut buf[..])?;
Ok(buf[0])
}
fn read_byte(&mut self) -> Result<u8, Error> {
let mut buf = [0u8];
self.read(&mut buf[..])?;
Ok(buf[0])
}
fn skip(&mut self, n: usize) -> Result<(), Error>;
}
pub trait Deserialize: Sized {
#[doc(hidden)]
const UNALIGNED_BYTES: bool = false;
fn decode<I: Input + ?Sized>(buffer: &mut I) -> Result<Self, Error> {
let mut object = Self::decode_static(buffer)?;
object.decode_dynamic(buffer)?;
Ok(object)
}
fn decode_static<I: Input + ?Sized>(buffer: &mut I) -> Result<Self, Error>;
fn decode_dynamic<I: Input + ?Sized>(
&mut self,
_buffer: &mut I,
) -> Result<(), Error> {
Ok(())
}
fn from_bytes(mut buffer: &[u8]) -> Result<Self, Error> {
Self::decode(&mut buffer)
}
}
pub const ALIGN: usize = 8;
#[allow(clippy::arithmetic_side_effects)] const fn alignment_bytes(len: usize) -> usize {
let modulo = len % ALIGN;
if modulo == 0 {
0
} else {
ALIGN - modulo
}
}
pub const fn aligned_size(len: usize) -> usize {
len.saturating_add(alignment_bytes(len))
}
macro_rules! impl_for_primitives {
($t:ident, $unpadded:literal) => {
impl Serialize for $t {
const UNALIGNED_BYTES: bool = $unpadded;
#[inline(always)]
fn size_static(&self) -> usize {
aligned_size(::core::mem::size_of::<$t>())
}
#[inline(always)]
fn size_dynamic(&self) -> usize {
0
}
#[inline(always)]
fn encode_static<O: Output + ?Sized>(
&self,
buffer: &mut O,
) -> Result<(), Error> {
let bytes = <$t>::to_be_bytes(*self);
for _ in 0..alignment_bytes(bytes.len()) {
buffer.push_byte(0)?;
}
buffer.write(bytes.as_ref())?;
Ok(())
}
}
impl Deserialize for $t {
const UNALIGNED_BYTES: bool = $unpadded;
fn decode_static<I: Input + ?Sized>(buffer: &mut I) -> Result<Self, Error> {
let mut asset = [0u8; ::core::mem::size_of::<$t>()];
buffer.skip(alignment_bytes(asset.len()))?; buffer.read(asset.as_mut())?;
Ok(<$t>::from_be_bytes(asset))
}
}
};
}
impl_for_primitives!(u8, true);
impl_for_primitives!(u16, false);
impl_for_primitives!(u32, false);
impl_for_primitives!(usize, false);
impl_for_primitives!(u64, false);
impl_for_primitives!(u128, false);
impl Serialize for () {
fn size_static(&self) -> usize {
0
}
#[inline(always)]
fn size_dynamic(&self) -> usize {
0
}
#[inline(always)]
fn encode_static<O: Output + ?Sized>(&self, _buffer: &mut O) -> Result<(), Error> {
Ok(())
}
}
impl Deserialize for () {
fn decode_static<I: Input + ?Sized>(_buffer: &mut I) -> Result<Self, Error> {
Ok(())
}
}
pub const VEC_DECODE_LIMIT: usize = 100 * (1 << 20); #[cfg(feature = "alloc")]
impl<T: Serialize> Serialize for Vec<T> {
fn size_static(&self) -> usize {
8
}
#[inline(always)]
fn size_dynamic(&self) -> usize {
if T::UNALIGNED_BYTES {
aligned_size(self.len())
} else {
aligned_size(
self.iter()
.map(|e| e.size())
.reduce(usize::saturating_add)
.unwrap_or_default(),
)
}
}
#[inline(always)]
fn encode_static<O: Output + ?Sized>(&self, buffer: &mut O) -> Result<(), Error> {
if self.len() > VEC_DECODE_LIMIT {
return Err(Error::AllocationLimit)
}
let len: u64 = self.len().try_into().expect("msg.len() > u64::MAX");
len.encode(buffer)
}
fn encode_dynamic<O: Output + ?Sized>(&self, buffer: &mut O) -> Result<(), Error> {
if T::UNALIGNED_BYTES {
let bytes = unsafe { ::core::mem::transmute::<&Vec<T>, &Vec<u8>>(self) };
buffer.write(bytes.as_slice())?;
for _ in 0..alignment_bytes(self.len()) {
buffer.push_byte(0)?;
}
} else {
for e in self.iter() {
e.encode(buffer)?;
}
}
Ok(())
}
}
#[cfg(feature = "alloc")]
impl<T: Deserialize> Deserialize for Vec<T> {
fn decode_static<I: Input + ?Sized>(buffer: &mut I) -> Result<Self, Error> {
let cap = u64::decode(buffer)?;
let cap: usize = cap.try_into().map_err(|_| Error::AllocationLimit)?;
if cap > VEC_DECODE_LIMIT {
return Err(Error::AllocationLimit)
}
Ok(Vec::with_capacity(cap))
}
fn decode_dynamic<I: Input + ?Sized>(&mut self, buffer: &mut I) -> Result<(), Error> {
for _ in 0..self.capacity() {
if T::UNALIGNED_BYTES {
let byte = buffer.read_byte()?;
let _self =
unsafe { ::core::mem::transmute::<&mut Vec<T>, &mut Vec<u8>>(self) };
_self.push(byte);
} else {
self.push(T::decode(buffer)?);
}
}
if T::UNALIGNED_BYTES {
buffer.skip(alignment_bytes(self.capacity()))?;
}
Ok(())
}
}
impl<const N: usize, T: Serialize> Serialize for [T; N] {
fn size_static(&self) -> usize {
if T::UNALIGNED_BYTES {
aligned_size(N)
} else {
aligned_size(
self.iter()
.map(|e| e.size_static())
.reduce(usize::saturating_add)
.unwrap_or_default(),
)
}
}
#[inline(always)]
fn size_dynamic(&self) -> usize {
if T::UNALIGNED_BYTES {
0
} else {
aligned_size(
self.iter()
.map(|e| e.size_dynamic())
.reduce(usize::saturating_add)
.unwrap_or_default(),
)
}
}
#[inline(always)]
fn encode_static<O: Output + ?Sized>(&self, buffer: &mut O) -> Result<(), Error> {
if T::UNALIGNED_BYTES {
let bytes = unsafe { ::core::mem::transmute::<&[T; N], &[u8; N]>(self) };
buffer.write(bytes.as_slice())?;
for _ in 0..alignment_bytes(N) {
buffer.push_byte(0)?;
}
} else {
for e in self.iter() {
e.encode_static(buffer)?;
}
}
Ok(())
}
fn encode_dynamic<O: Output + ?Sized>(&self, buffer: &mut O) -> Result<(), Error> {
for e in self.iter() {
e.encode_dynamic(buffer)?;
}
Ok(())
}
}
impl<const N: usize, T: Deserialize> Deserialize for [T; N] {
fn decode_static<I: Input + ?Sized>(buffer: &mut I) -> Result<Self, Error> {
if T::UNALIGNED_BYTES {
let mut bytes: [u8; N] = [0; N];
buffer.read(bytes.as_mut())?;
buffer.skip(alignment_bytes(N))?;
let ref_typed: &[T; N] = unsafe { core::mem::transmute(&bytes) };
let typed: [T; N] = unsafe { core::ptr::read(ref_typed) };
Ok(typed)
} else {
let mut uninit = <MaybeUninit<[T; N]>>::uninit();
let mut ptr = uninit.as_mut_ptr() as *mut T;
for _ in 0..N {
let decoded = T::decode_static(buffer)?;
unsafe {
core::ptr::write(ptr, decoded);
}
ptr = unsafe { ptr.add(1) };
}
let init = unsafe { uninit.assume_init() };
Ok(init)
}
}
fn decode_dynamic<I: Input + ?Sized>(&mut self, buffer: &mut I) -> Result<(), Error> {
for e in self.iter_mut() {
e.decode_dynamic(buffer)?;
}
Ok(())
}
}
#[cfg(feature = "alloc")]
impl Output for Vec<u8> {
fn write(&mut self, bytes: &[u8]) -> Result<(), Error> {
self.extend_from_slice(bytes);
Ok(())
}
}
impl<'a> Output for &'a mut [u8] {
fn write(&mut self, from: &[u8]) -> Result<(), Error> {
if from.len() > self.len() {
return Err(Error::BufferIsTooShort)
}
let len = from.len();
self[..len].copy_from_slice(from);
let reduced = &mut self[len..];
*self = unsafe { &mut *(reduced as *mut [u8]) };
Ok(())
}
}
impl<'a> Input for &'a [u8] {
fn remaining(&mut self) -> usize {
self.len()
}
fn peek(&self, into: &mut [u8]) -> Result<(), Error> {
if into.len() > self.len() {
return Err(Error::BufferIsTooShort)
}
let len = into.len();
into.copy_from_slice(&self[..len]);
Ok(())
}
fn read(&mut self, into: &mut [u8]) -> Result<(), Error> {
if into.len() > self.len() {
return Err(Error::BufferIsTooShort)
}
let len = into.len();
into.copy_from_slice(&self[..len]);
*self = &self[len..];
Ok(())
}
fn skip(&mut self, n: usize) -> Result<(), Error> {
if n > self.len() {
return Err(Error::BufferIsTooShort)
}
*self = &self[n..];
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn validate<T: Serialize + Deserialize + Eq + core::fmt::Debug>(t: T) {
let bytes = t.to_bytes();
let t2 = T::from_bytes(&bytes).expect("Roundtrip failed");
assert_eq!(t, t2);
assert_eq!(t.to_bytes(), t2.to_bytes());
let mut vec = Vec::new();
t.encode_static(&mut vec).expect("Encode failed");
assert_eq!(vec.len(), t.size_static());
}
fn validate_enum<T: Serialize + Deserialize + Eq + fmt::Debug>(t: T) {
let bytes = t.to_bytes();
let t2 = T::from_bytes(&bytes).expect("Roundtrip failed");
assert_eq!(t, t2);
assert_eq!(t.to_bytes(), t2.to_bytes());
let mut vec = Vec::new();
t.encode_static(&mut vec).expect("Encode failed");
assert_eq!(vec.len(), t.size_static());
t.encode_dynamic(&mut vec).expect("Encode failed");
assert_eq!(vec.len(), t.size());
let mut vec2 = Vec::new();
t.encode_dynamic(&mut vec2).expect("Encode failed");
assert_eq!(vec2.len(), t.size_dynamic());
}
#[test]
fn test_canonical_encode_decode() {
validate(());
validate(123u8);
validate(u8::MAX);
validate(123u16);
validate(u16::MAX);
validate(123u32);
validate(u32::MAX);
validate(123u64);
validate(u64::MAX);
validate(123u128);
validate(u128::MAX);
validate(Vec::<u8>::new());
validate(Vec::<u16>::new());
validate(Vec::<u32>::new());
validate(Vec::<u64>::new());
validate(Vec::<u128>::new());
validate(vec![1u8]);
validate(vec![1u16]);
validate(vec![1u32]);
validate(vec![1u64]);
validate(vec![1u128]);
validate(vec![1u8, 2u8]);
validate(vec![1u16, 2u16]);
validate(vec![1u32, 2u32]);
validate(vec![1u64, 2u64]);
validate(vec![1u128, 2u128]);
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
struct TestStruct1 {
a: u8,
b: u16,
}
let t = TestStruct1 { a: 123, b: 456 };
assert_eq!(t.size_static(), 16);
assert_eq!(t.size(), 16);
validate(t);
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
struct TestStruct2 {
a: u8,
v: Vec<u8>,
b: u16,
arr0: [u8; 0],
arr1: [u8; 2],
arr2: [u16; 3],
arr3: [u64; 4],
}
validate(TestStruct2 {
a: 123,
v: vec![1, 2, 3],
b: 456,
arr0: [],
arr1: [1, 2],
arr2: [1, 2, u16::MAX],
arr3: [0, 3, 1111, u64::MAX],
});
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
#[repr(transparent)]
struct TestStruct3([u8; 64]);
let t = TestStruct3([1; 64]);
assert_eq!(t.size_static(), 64);
assert_eq!(t.size(), 64);
validate(t);
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[canonical(prefix = 1u64)]
struct Prefixed1 {
a: [u8; 3],
b: Vec<u8>,
}
validate(Prefixed1 {
a: [1, 2, 3],
b: vec![4, 5, 6],
});
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
#[repr(u8)]
enum TestEnum1 {
A,
B,
C = 0x13,
D,
}
validate(TestEnum1::A);
validate(TestEnum1::B);
validate(TestEnum1::C);
validate(TestEnum1::D);
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
enum TestEnum2 {
A(u8),
B([u8; 3]),
C(Vec<u8>),
}
validate_enum(TestEnum2::A(2));
validate_enum(TestEnum2::B([1, 2, 3]));
validate_enum(TestEnum2::C(vec![1, 2, 3]));
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
#[canonical(prefix = 2u64)]
struct Prefixed2(u16);
validate(Prefixed2(u16::MAX));
assert_eq!(
&Prefixed1 {
a: [1, 2, 3],
b: vec![4, 5]
}
.to_bytes()[..8],
&[0u8, 0, 0, 0, 0, 0, 0, 1]
);
assert_eq!(
Prefixed2(u16::MAX).to_bytes(),
[0u8, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0xff, 0xff]
);
}
}