use custom_error::custom_error;
use std::{cell::RefCell, mem, rc::Rc, sync::Arc};
pub use psibase_macros::{Pack, Unpack};
custom_error! {pub Error
ReadPastEnd = "Read past end",
BadOffset = "Bad offset",
BadSize = "Bad size",
BadUTF8 = "Bad UTF-8 encoding",
BadEnumIndex = "Bad enum index",
ExtraData = "Extra data in buffer",
}
pub type Result<T> = std::result::Result<T, Error>;
pub trait UnpackOwned: for<'a> Unpack<'a> {}
impl<T> UnpackOwned for T where T: for<'a> Unpack<'a> {}
pub trait Pack {
#[doc(hidden)]
const FIXED_SIZE: u32;
#[doc(hidden)]
const VARIABLE_SIZE: bool;
#[doc(hidden)]
const IS_OPTIONAL: bool = false;
fn pack(&self, dest: &mut Vec<u8>);
fn packed(&self) -> Vec<u8> {
let mut bytes = Vec::new();
self.pack(&mut bytes);
bytes
}
#[doc(hidden)]
fn is_empty_container(&self) -> bool {
false
}
#[doc(hidden)]
fn embedded_fixed_pack(&self, dest: &mut Vec<u8>) {
if Self::VARIABLE_SIZE {
dest.extend_from_slice(&0_u32.to_le_bytes());
} else {
Self::pack(self, dest);
}
}
#[doc(hidden)]
fn embedded_fixed_repack(&self, fixed_pos: u32, heap_pos: u32, dest: &mut Vec<u8>) {
if Self::VARIABLE_SIZE && !self.is_empty_container() {
dest[fixed_pos as usize..fixed_pos as usize + 4]
.copy_from_slice(&(heap_pos - fixed_pos).to_le_bytes());
}
}
#[doc(hidden)]
fn embedded_variable_pack(&self, dest: &mut Vec<u8>) {
if Self::VARIABLE_SIZE && !self.is_empty_container() {
self.pack(dest);
}
}
}
pub trait Unpack<'a>: Sized {
#[doc(hidden)]
const FIXED_SIZE: u32;
#[doc(hidden)]
const VARIABLE_SIZE: bool;
#[doc(hidden)]
const IS_OPTIONAL: bool = false;
fn unpack(src: &'a [u8], pos: &mut u32) -> Result<Self>;
fn unpacked(src: &'a [u8]) -> Result<Self> {
let mut pos = 0;
Self::unpack(src, &mut pos)
}
fn verify(src: &'a [u8], pos: &mut u32) -> Result<()>;
fn verify_no_extra(src: &'a [u8]) -> Result<()> {
let mut pos = 0;
Self::verify(src, &mut pos)?;
if pos as usize != src.len() {
return Err(Error::ExtraData);
}
Ok(())
}
#[doc(hidden)]
fn new_empty_container() -> Result<Self> {
Err(Error::BadOffset)
}
#[doc(hidden)]
fn embedded_variable_unpack(
src: &'a [u8],
fixed_pos: &mut u32,
heap_pos: &mut u32,
) -> Result<Self> {
let orig_pos = *fixed_pos;
let offset = u32::unpack(src, fixed_pos)?;
if offset == 0 {
return Self::new_empty_container();
}
if *heap_pos as u64 != orig_pos as u64 + offset as u64 {
return Err(Error::BadOffset);
}
Self::unpack(src, heap_pos)
}
#[doc(hidden)]
fn embedded_unpack(src: &'a [u8], fixed_pos: &mut u32, heap_pos: &mut u32) -> Result<Self> {
if Self::VARIABLE_SIZE {
Self::embedded_variable_unpack(src, fixed_pos, heap_pos)
} else {
Self::unpack(src, fixed_pos)
}
}
#[doc(hidden)]
fn embedded_variable_verify(
src: &'a [u8],
fixed_pos: &mut u32,
heap_pos: &mut u32,
) -> Result<()> {
let orig_pos = *fixed_pos;
let offset = u32::unpack(src, fixed_pos)?;
if offset == 0 {
let _ = Self::new_empty_container();
return Ok(());
}
if *heap_pos as u64 != orig_pos as u64 + offset as u64 {
return Err(Error::BadOffset);
}
Self::verify(src, heap_pos)
}
#[doc(hidden)]
fn embedded_verify(src: &'a [u8], fixed_pos: &mut u32, heap_pos: &mut u32) -> Result<()> {
if Self::VARIABLE_SIZE {
Self::embedded_variable_verify(src, fixed_pos, heap_pos)
} else {
Self::verify(src, fixed_pos)
}
}
}
fn read_u8_arr<const SIZE: usize>(src: &[u8], pos: &mut u32) -> Result<[u8; SIZE]> {
let mut bytes: [u8; SIZE] = [0; SIZE];
bytes.copy_from_slice(
src.get(*pos as usize..*pos as usize + SIZE)
.ok_or(Error::ReadPastEnd)?,
);
*pos += SIZE as u32;
Ok(bytes)
}
trait MissingBoolConversions {
fn from_le_bytes(bytes: [u8; 1]) -> bool;
fn to_le_bytes(self) -> [u8; 1];
}
impl MissingBoolConversions for bool {
fn from_le_bytes(bytes: [u8; 1]) -> bool {
bytes[0] != 0
}
fn to_le_bytes(self) -> [u8; 1] {
match self {
true => [1],
false => [0],
}
}
}
impl<'a, T: Pack> Pack for &'a T {
const FIXED_SIZE: u32 = T::FIXED_SIZE;
const VARIABLE_SIZE: bool = T::VARIABLE_SIZE;
const IS_OPTIONAL: bool = T::IS_OPTIONAL;
fn pack(&self, dest: &mut Vec<u8>) {
(*self).pack(dest)
}
fn is_empty_container(&self) -> bool {
(*self).is_empty_container()
}
fn embedded_fixed_pack(&self, dest: &mut Vec<u8>) {
(*self).embedded_fixed_pack(dest)
}
fn embedded_fixed_repack(&self, fixed_pos: u32, heap_pos: u32, dest: &mut Vec<u8>) {
(*self).embedded_fixed_repack(fixed_pos, heap_pos, dest)
}
fn embedded_variable_pack(&self, dest: &mut Vec<u8>) {
(*self).embedded_variable_pack(dest)
}
}
macro_rules! scalar_impl {
($t:ty) => {
impl Pack for $t {
const FIXED_SIZE: u32 = mem::size_of::<Self>() as u32;
const VARIABLE_SIZE: bool = false;
fn pack(&self, dest: &mut Vec<u8>) {
dest.extend_from_slice(&self.to_le_bytes());
}
}
impl<'a> Unpack<'a> for $t {
const FIXED_SIZE: u32 = mem::size_of::<Self>() as u32;
const VARIABLE_SIZE: bool = false;
fn unpack(src: &'a [u8], pos: &mut u32) -> Result<Self> {
Ok(Self::from_le_bytes(read_u8_arr(src, pos)?.into()))
}
fn verify(src: &'a [u8], pos: &mut u32) -> Result<()> {
if (*pos as u64 + <Self as Unpack>::FIXED_SIZE as u64 > src.len() as u64) {
Err(Error::ReadPastEnd)
} else {
*pos += <Self as Unpack>::FIXED_SIZE;
Ok(())
}
}
}
};
} scalar_impl! {bool}
scalar_impl! {i8}
scalar_impl! {i16}
scalar_impl! {i32}
scalar_impl! {i64}
scalar_impl! {u8}
scalar_impl! {u16}
scalar_impl! {u32}
scalar_impl! {u64}
scalar_impl! {f32}
scalar_impl! {f64}
macro_rules! pack_ptr {
($ptr:ident, $to_ref:ident) => {
impl<T: Pack> Pack for $ptr<T> {
const FIXED_SIZE: u32 = T::FIXED_SIZE;
const VARIABLE_SIZE: bool = T::VARIABLE_SIZE;
const IS_OPTIONAL: bool = T::IS_OPTIONAL;
fn pack(&self, dest: &mut Vec<u8>) {
self.$to_ref().pack(dest)
}
fn is_empty_container(&self) -> bool {
self.$to_ref().is_empty_container()
}
fn embedded_fixed_pack(&self, dest: &mut Vec<u8>) {
self.$to_ref().embedded_fixed_pack(dest)
}
fn embedded_fixed_repack(&self, fixed_pos: u32, heap_pos: u32, dest: &mut Vec<u8>) {
self.$to_ref()
.embedded_fixed_repack(fixed_pos, heap_pos, dest)
}
fn embedded_variable_pack(&self, dest: &mut Vec<u8>) {
self.$to_ref().embedded_variable_pack(dest)
}
}
};
}
macro_rules! unpack_ptr {
($ptr:ident) => {
impl<'a, T: Unpack<'a>> Unpack<'a> for $ptr<T> {
const FIXED_SIZE: u32 = T::FIXED_SIZE;
const VARIABLE_SIZE: bool = T::VARIABLE_SIZE;
const IS_OPTIONAL: bool = T::IS_OPTIONAL;
fn unpack(src: &'a [u8], pos: &mut u32) -> Result<Self> {
Ok(Self::new(<T>::unpack(src, pos)?))
}
fn verify(src: &'a [u8], pos: &mut u32) -> Result<()> {
<T>::verify(src, pos)
}
fn new_empty_container() -> Result<Self> {
Ok(Self::new(<T>::new_empty_container()?))
}
fn embedded_variable_unpack(
src: &'a [u8],
fixed_pos: &mut u32,
heap_pos: &mut u32,
) -> Result<Self> {
Ok(Self::new(<T>::embedded_variable_unpack(
src, fixed_pos, heap_pos,
)?))
}
fn embedded_unpack(
src: &'a [u8],
fixed_pos: &mut u32,
heap_pos: &mut u32,
) -> Result<Self> {
Ok(Self::new(<T>::embedded_unpack(src, fixed_pos, heap_pos)?))
}
fn embedded_variable_verify(
src: &'a [u8],
fixed_pos: &mut u32,
heap_pos: &mut u32,
) -> Result<()> {
<T>::embedded_variable_verify(src, fixed_pos, heap_pos)
}
fn embedded_verify(
src: &'a [u8],
fixed_pos: &mut u32,
heap_pos: &mut u32,
) -> Result<()> {
<T>::embedded_verify(src, fixed_pos, heap_pos)
}
}
};
}
pack_ptr!(Box, as_ref);
pack_ptr!(Rc, as_ref);
pack_ptr!(Arc, as_ref);
pack_ptr!(RefCell, borrow);
unpack_ptr!(Box);
unpack_ptr!(Rc);
unpack_ptr!(Arc);
unpack_ptr!(RefCell);
impl<T: Pack> Pack for Option<T> {
const FIXED_SIZE: u32 = 4;
const VARIABLE_SIZE: bool = true;
const IS_OPTIONAL: bool = true;
fn pack(&self, dest: &mut Vec<u8>) {
let fixed_pos = dest.len() as u32;
Self::embedded_fixed_pack(self, dest);
let heap_pos = dest.len() as u32;
Self::embedded_fixed_repack(self, fixed_pos, heap_pos, dest);
Self::embedded_variable_pack(self, dest);
}
fn embedded_fixed_pack(&self, dest: &mut Vec<u8>) {
if T::IS_OPTIONAL || !T::VARIABLE_SIZE {
dest.extend_from_slice(&1u32.to_le_bytes())
} else {
match self {
Some(x) => x.embedded_fixed_pack(dest),
None => dest.extend_from_slice(&1u32.to_le_bytes()),
}
}
}
fn embedded_fixed_repack(&self, fixed_pos: u32, heap_pos: u32, dest: &mut Vec<u8>) {
if let Some(x) = self {
if T::IS_OPTIONAL || !T::VARIABLE_SIZE {
dest[fixed_pos as usize..fixed_pos as usize + 4]
.copy_from_slice(&(heap_pos - fixed_pos).to_le_bytes())
} else {
x.embedded_fixed_repack(fixed_pos, heap_pos, dest)
}
}
}
fn embedded_variable_pack(&self, dest: &mut Vec<u8>) {
if let Some(x) = self {
if !x.is_empty_container() {
x.pack(dest)
}
}
}
}
impl<'a, T: Unpack<'a>> Unpack<'a> for Option<T> {
const FIXED_SIZE: u32 = 4;
const VARIABLE_SIZE: bool = true;
const IS_OPTIONAL: bool = true;
fn unpack(src: &'a [u8], pos: &mut u32) -> Result<Self> {
let mut fixed_pos = *pos;
*pos += 4;
Self::embedded_unpack(src, &mut fixed_pos, pos)
}
fn verify(src: &'a [u8], pos: &mut u32) -> Result<()> {
let mut fixed_pos = *pos;
*pos += 4;
Self::embedded_verify(src, &mut fixed_pos, pos)
}
fn embedded_unpack(src: &'a [u8], fixed_pos: &mut u32, heap_pos: &mut u32) -> Result<Self> {
let orig_pos = *fixed_pos;
let offset = u32::unpack(src, fixed_pos)?;
if offset == 1 {
return Ok(None);
}
*fixed_pos = orig_pos;
Ok(Some(<T>::embedded_variable_unpack(
src, fixed_pos, heap_pos,
)?))
}
fn embedded_verify(src: &'a [u8], fixed_pos: &mut u32, heap_pos: &mut u32) -> Result<()> {
let orig_pos = *fixed_pos;
let offset = u32::unpack(src, fixed_pos)?;
if offset == 1 {
return Ok(());
}
*fixed_pos = orig_pos;
T::embedded_variable_verify(src, fixed_pos, heap_pos)
}
}
trait BytesConversion<'a>: Sized {
fn fracpack_verify_if_str(bytes: &'a [u8]) -> Result<()>;
fn fracpack_from_bytes(bytes: &'a [u8]) -> Result<Self>;
fn fracpack_as_bytes(&'a self) -> &'a [u8];
}
impl<'a> BytesConversion<'a> for String {
fn fracpack_verify_if_str(bytes: &'a [u8]) -> Result<()> {
std::str::from_utf8(bytes).or(Err(Error::BadUTF8))?;
Ok(())
}
fn fracpack_from_bytes(bytes: &'a [u8]) -> Result<Self> {
Self::from_utf8(bytes.to_vec()).or(Err(Error::BadUTF8))
}
fn fracpack_as_bytes(&'a self) -> &'a [u8] {
self.as_bytes()
}
}
impl<'a> BytesConversion<'a> for &'a str {
fn fracpack_verify_if_str(bytes: &'a [u8]) -> Result<()> {
std::str::from_utf8(bytes).or(Err(Error::BadUTF8))?;
Ok(())
}
fn fracpack_from_bytes(bytes: &'a [u8]) -> Result<Self> {
std::str::from_utf8(bytes).or(Err(Error::BadUTF8))
}
fn fracpack_as_bytes(&self) -> &'a [u8] {
self.as_bytes()
}
}
impl<'a> BytesConversion<'a> for &'a [u8] {
fn fracpack_verify_if_str(_bytes: &'a [u8]) -> Result<()> {
Ok(())
}
fn fracpack_from_bytes(bytes: &'a [u8]) -> Result<Self> {
Ok(bytes)
}
fn fracpack_as_bytes(&self) -> &'a [u8] {
self
}
}
macro_rules! bytes_impl {
($t:ty) => {
impl<'a> Pack for $t {
const FIXED_SIZE: u32 = 4;
const VARIABLE_SIZE: bool = true;
fn pack(&self, dest: &mut Vec<u8>) {
dest.extend_from_slice(&(self.len() as u32).to_le_bytes());
dest.extend_from_slice(self.fracpack_as_bytes());
}
fn is_empty_container(&self) -> bool {
self.is_empty()
}
}
impl<'a> Unpack<'a> for $t {
const FIXED_SIZE: u32 = 4;
const VARIABLE_SIZE: bool = true;
fn unpack(src: &'a [u8], pos: &mut u32) -> Result<$t> {
let len = u32::unpack(src, pos)?;
let bytes = src
.get(*pos as usize..(*pos + len) as usize)
.ok_or(Error::ReadPastEnd)?;
*pos += len;
<$t>::fracpack_from_bytes(bytes)
}
fn verify(src: &'a [u8], pos: &mut u32) -> Result<()> {
let len = u32::unpack(src, pos)?;
let bytes = src
.get(*pos as usize..(*pos + len) as usize)
.ok_or(Error::ReadPastEnd)?;
*pos += len;
<$t>::fracpack_verify_if_str(bytes)?;
Ok(())
}
fn new_empty_container() -> Result<Self> {
Ok(Default::default())
}
}
};
} bytes_impl! {String}
bytes_impl! {&'a str}
bytes_impl! {&'a [u8]}
impl<T: Pack> Pack for Vec<T> {
const FIXED_SIZE: u32 = 4;
const VARIABLE_SIZE: bool = true;
fn pack(&self, dest: &mut Vec<u8>) {
let num_bytes = self.len() as u32 * T::FIXED_SIZE;
dest.extend_from_slice(&num_bytes.to_le_bytes());
dest.reserve(num_bytes as usize);
let start = dest.len();
for x in self {
T::embedded_fixed_pack(x, dest);
}
for (i, x) in self.iter().enumerate() {
let heap_pos = dest.len() as u32;
T::embedded_fixed_repack(x, start as u32 + (i as u32) * T::FIXED_SIZE, heap_pos, dest);
T::embedded_variable_pack(x, dest);
}
}
fn is_empty_container(&self) -> bool {
self.is_empty()
}
}
impl<'a, T: Unpack<'a>> Unpack<'a> for Vec<T> {
const FIXED_SIZE: u32 = 4;
const VARIABLE_SIZE: bool = true;
fn unpack(src: &'a [u8], pos: &mut u32) -> Result<Self> {
let num_bytes = u32::unpack(src, pos)?;
if num_bytes % T::FIXED_SIZE != 0 {
return Err(Error::BadSize);
}
let hp = *pos as u64 + num_bytes as u64;
let mut heap_pos = hp as u32;
if heap_pos as u64 != hp {
return Err(Error::ReadPastEnd);
}
let len = (num_bytes / T::FIXED_SIZE) as usize;
let mut result = Self::with_capacity(len);
for _ in 0..len {
result.push(T::embedded_unpack(src, pos, &mut heap_pos)?);
}
*pos = heap_pos;
Ok(result)
}
fn verify(src: &'a [u8], pos: &mut u32) -> Result<()> {
let num_bytes = u32::unpack(src, pos)?;
if num_bytes % T::FIXED_SIZE != 0 {
return Err(Error::BadSize);
}
let hp = *pos as u64 + num_bytes as u64;
let mut heap_pos = hp as u32;
if heap_pos as u64 != hp {
return Err(Error::ReadPastEnd);
}
for _ in 0..num_bytes / T::FIXED_SIZE {
T::embedded_verify(src, pos, &mut heap_pos)?;
}
*pos = heap_pos;
Ok(())
}
fn new_empty_container() -> Result<Self> {
Ok(Default::default())
}
}
impl<T: Pack, const N: usize> Pack for [T; N] {
const VARIABLE_SIZE: bool = T::VARIABLE_SIZE;
const FIXED_SIZE: u32 = if T::VARIABLE_SIZE {
4
} else {
T::FIXED_SIZE * N as u32
};
fn pack(&self, dest: &mut Vec<u8>) {
let start = dest.len();
for item in self {
item.embedded_fixed_pack(dest);
}
for (i, item) in self.iter().enumerate() {
let heap_pos = dest.len() as u32;
item.embedded_fixed_repack(start as u32 + (i as u32) * T::FIXED_SIZE, heap_pos, dest);
item.embedded_variable_pack(dest);
}
}
}
impl<'a, T: Unpack<'a>, const N: usize> Unpack<'a> for [T; N] {
const VARIABLE_SIZE: bool = T::VARIABLE_SIZE;
const FIXED_SIZE: u32 = if T::VARIABLE_SIZE {
4
} else {
T::FIXED_SIZE * N as u32
};
fn unpack(src: &'a [u8], pos: &mut u32) -> Result<Self> {
let hp = *pos as u64 + T::FIXED_SIZE as u64 * N as u64;
let mut heap_pos = hp as u32;
if heap_pos as u64 != hp {
return Err(Error::ReadPastEnd);
}
let mut items: Vec<T> = Vec::with_capacity(N);
for _ in 0..N {
items.push(T::embedded_unpack(src, pos, &mut heap_pos)?);
}
let result: [T; N] = items.try_into().unwrap_or_else(|v: Vec<T>| {
panic!(
"Expected a fixed array of length {} but it was {}",
N,
v.len()
)
});
*pos = heap_pos;
Ok(result)
}
fn verify(src: &'a [u8], pos: &mut u32) -> Result<()> {
let hp = *pos as u64 + T::FIXED_SIZE as u64 * N as u64;
let mut heap_pos = hp as u32;
if heap_pos as u64 != hp {
return Err(Error::ReadPastEnd);
}
for _ in 0..N {
T::embedded_verify(src, pos, &mut heap_pos)?;
}
*pos = heap_pos;
Ok(())
}
}
macro_rules! tuple_impls {
($($len:expr => ($($n:tt $name:ident)*))+) => {
$(
impl<$($name: Pack),*> Pack for ($($name,)*)
{
const VARIABLE_SIZE: bool = true;
const FIXED_SIZE: u32 = 4;
#[allow(non_snake_case)]
fn pack(&self, dest: &mut Vec<u8>) {
let heap: u32 = $($name::FIXED_SIZE +)* 0;
assert!(heap as u16 as u32 == heap); (heap as u16).pack(dest);
$(
let $name = dest.len() as u32;
self.$n.embedded_fixed_pack(dest);
)*
$(
let heap_pos = dest.len() as u32;
self.$n.embedded_fixed_repack($name, heap_pos, dest);
self.$n.embedded_variable_pack(dest);
)*
}
}
impl<'a, $($name: Unpack<'a>),*> Unpack<'a> for ($($name,)*)
{
const VARIABLE_SIZE: bool = true;
const FIXED_SIZE: u32 = 4;
#[allow(non_snake_case,unused_mut)]
fn unpack(src: &'a [u8], pos: &mut u32) -> Result<Self> {
let fixed_size = u16::unpack(src, pos)?;
let mut heap_pos = *pos + fixed_size as u32;
if heap_pos < *pos {
return Err(Error::BadOffset);
}
$(
let $name = $name::embedded_unpack(src, pos, &mut heap_pos)?;
)*
*pos = heap_pos;
Ok(($($name,)*))
}
#[allow(unused_mut)]
fn verify(src: &'a [u8], pos: &mut u32) -> Result<()> {
let fixed_size = u16::unpack(src, pos)?;
let mut heap_pos = *pos + fixed_size as u32;
if heap_pos < *pos {
return Err(Error::BadOffset);
}
$(
$name::embedded_unpack(src, pos, &mut heap_pos)?;
)*
*pos = heap_pos;
Ok(())
}
}
)+
}
}
tuple_impls! {
0 => ()
1 => (0 T0)
2 => (0 T0 1 T1)
3 => (0 T0 1 T1 2 T2)
4 => (0 T0 1 T1 2 T2 3 T3)
5 => (0 T0 1 T1 2 T2 3 T3 4 T4)
6 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5)
7 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6)
8 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7)
9 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8)
10 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9)
11 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10)
12 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11)
13 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12)
14 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13)
15 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14)
16 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15)
}