use crate::de::{DecoderConfig, IDLDeserialize};
use crate::ser::IDLBuilder;
use crate::{CandidType, Error, Result};
use serde::de::Deserialize;
fn check_recursion_depth(_depth: u16) -> Result<()> {
#[cfg(not(target_family = "wasm"))]
match stacker::remaining_stack() {
Some(size) if size < 32768 => {
return Err(Error::msg(format!(
"Recursion limit exceeded at depth {_depth}"
)))
}
None if _depth > 512 => {
return Err(Error::msg(format!(
"Recursion limit exceeded at depth {_depth}. Cannot detect stack size, use a conservative bound"
)))
}
_ => (),
}
Ok(())
}
#[derive(Debug, Clone)]
pub(crate) struct RecursionDepth(std::rc::Rc<std::cell::Cell<u16>>);
impl RecursionDepth {
pub(crate) fn new() -> Self {
Self(std::rc::Rc::new(std::cell::Cell::new(0)))
}
pub(crate) fn guard(&self) -> Result<DepthGuard> {
let current = self.0.get();
let new_depth = current
.checked_add(1)
.ok_or_else(|| Error::msg("Recursion depth overflow"))?;
self.0.set(new_depth);
let guard = DepthGuard(self.0.clone());
check_recursion_depth(new_depth)?;
Ok(guard)
}
}
#[derive(Debug)]
pub(crate) struct DepthGuard(std::rc::Rc<std::cell::Cell<u16>>);
impl Drop for DepthGuard {
fn drop(&mut self) {
let current = self.0.get();
let new_depth = current
.checked_sub(1)
.expect("Recursion depth underflow - this is a bug");
self.0.set(new_depth);
}
}
pub fn check_unique<'a, I, T>(sorted: I) -> Result<()>
where
T: 'a + PartialEq + std::fmt::Display,
I: Iterator<Item = &'a T>,
{
let mut prev: Option<&T> = None;
for lab in sorted {
if let Some(prev) = prev {
if lab == prev {
return Err(Error::msg(format!(
"label '{lab}' hash collision with '{prev}'"
)));
}
}
prev = Some(lab);
}
Ok(())
}
pub fn pp_num_str(s: &str) -> String {
let mut groups = Vec::new();
for chunk in s.as_bytes().rchunks(3) {
let str = String::from_utf8_lossy(chunk);
groups.push(str);
}
groups.reverse();
if "-" == groups.first().unwrap() {
"-".to_string() + &groups[1..].join("_")
} else {
groups.join("_")
}
}
#[macro_export]
macro_rules! Encode {
( $($x:expr),* ) => {{
let mut builder = $crate::ser::IDLBuilder::new();
Encode!(@PutValue builder $($x,)*)
}};
( @PutValue $builder:ident $x:expr, $($tail:expr,)* ) => {{
$builder.arg($x).and_then(|builder| Encode!(@PutValue builder $($tail,)*))
}};
( @PutValue $builder:ident ) => {{
$builder.serialize_to_vec()
}};
}
#[macro_export]
macro_rules! Decode {
( $hex:expr $(,$ty:ty)* ) => {{
Decode!([$crate::de::DecoderConfig::new()]; $hex $(,$ty)*)
}};
( [ $config:expr ] ; $hex:expr $(,$ty:ty)* ) => {{
$crate::de::IDLDeserialize::new_with_config($hex, &$config)
.and_then(|mut de| Decode!(@GetValue [] de $($ty,)*)
.and_then(|res| de.done().and(Ok(res))))
}};
(@Debug [ $config:expr ] ; $hex:expr $(,$ty:ty)* ) => {{
$crate::de::IDLDeserialize::new_with_config($hex, &$config)
.and_then(|mut de| Decode!(@GetValue [] de $($ty,)*)
.and_then(|res| de.done().and(Ok((res, de.get_config().compute_cost(&$config))))))
}};
(@GetValue [$($ans:ident)*] $de:ident $ty:ty, $($tail:ty,)* ) => {{
$de.get_value::<$ty>()
.and_then(|val| Decode!(@GetValue [$($ans)* val] $de $($tail,)* ))
}};
(@GetValue [$ans:ident] $de:ident) => {{
Ok($ans)
}};
(@GetValue [$($ans:ident)*] $de:ident) => {{
Ok(($($ans),*))
}};
}
pub fn decode_args<'a, Tuple>(bytes: &'a [u8]) -> Result<Tuple>
where
Tuple: ArgumentDecoder<'a>,
{
let mut de = IDLDeserialize::new(bytes)?;
let res = ArgumentDecoder::decode(&mut de)?;
de.done()?;
Ok(res)
}
pub fn decode_args_with_config<'a, Tuple>(bytes: &'a [u8], config: &DecoderConfig) -> Result<Tuple>
where
Tuple: ArgumentDecoder<'a>,
{
let mut de = IDLDeserialize::new_with_config(bytes, config)?;
let res = ArgumentDecoder::decode(&mut de)?;
de.done()?;
Ok(res)
}
pub fn decode_args_with_config_debug<'a, Tuple>(
bytes: &'a [u8],
config: &DecoderConfig,
) -> Result<(Tuple, DecoderConfig)>
where
Tuple: ArgumentDecoder<'a>,
{
let mut de = IDLDeserialize::new_with_config(bytes, config)?;
let res = ArgumentDecoder::decode(&mut de)?;
de.done()?;
let cost = de.get_config().compute_cost(config);
Ok((res, cost))
}
pub fn decode_args_with_decoding_quota<const N: usize, Tuple>(byte_vec: Vec<u8>) -> Tuple
where
Tuple: for<'a> ArgumentDecoder<'a>,
{
let mut config = DecoderConfig::new();
config.set_decoding_quota(N);
decode_args_with_config(&byte_vec[..], &config).unwrap()
}
pub fn decode_args_with_skipping_quota<const M: usize, Tuple>(byte_vec: Vec<u8>) -> Tuple
where
Tuple: for<'a> ArgumentDecoder<'a>,
{
let mut config = DecoderConfig::new();
config.set_skipping_quota(M);
decode_args_with_config(&byte_vec[..], &config).unwrap()
}
pub fn decode_args_with_decoding_and_skipping_quota<const N: usize, const M: usize, Tuple>(
byte_vec: Vec<u8>,
) -> Tuple
where
Tuple: for<'a> ArgumentDecoder<'a>,
{
let mut config = DecoderConfig::new();
config.set_decoding_quota(N);
config.set_skipping_quota(M);
decode_args_with_config(&byte_vec[..], &config).unwrap()
}
pub fn decode_one<'a, T>(bytes: &'a [u8]) -> Result<T>
where
T: Deserialize<'a> + CandidType,
{
let (res,) = decode_args(bytes)?;
Ok(res)
}
pub fn decode_one_with_config<'a, T>(bytes: &'a [u8], config: &DecoderConfig) -> Result<T>
where
T: Deserialize<'a> + CandidType,
{
let (res,) = decode_args_with_config(bytes, config)?;
Ok(res)
}
pub fn decode_one_with_decoding_quota<const N: usize, T>(byte_vec: Vec<u8>) -> T
where
T: for<'a> Deserialize<'a> + CandidType,
{
let mut config = DecoderConfig::new();
config.set_decoding_quota(N);
decode_one_with_config(&byte_vec[..], &config).unwrap()
}
pub fn decode_one_with_skipping_quota<const M: usize, T>(byte_vec: Vec<u8>) -> T
where
T: for<'a> Deserialize<'a> + CandidType,
{
let mut config = DecoderConfig::new();
config.set_skipping_quota(M);
decode_one_with_config(&byte_vec[..], &config).unwrap()
}
pub fn decode_one_with_decoding_and_skipping_quota<const N: usize, const M: usize, T>(
byte_vec: Vec<u8>,
) -> T
where
T: for<'a> Deserialize<'a> + CandidType,
{
let mut config = DecoderConfig::new();
config.set_decoding_quota(N);
config.set_skipping_quota(M);
decode_one_with_config(&byte_vec[..], &config).unwrap()
}
pub fn write_args<Tuple: ArgumentEncoder, Writer: std::io::Write>(
writer: &mut Writer,
arguments: Tuple,
) -> Result<()> {
let mut ser = IDLBuilder::new();
arguments.encode(&mut ser)?;
ser.serialize(writer)
}
pub fn write_args_ref<Tuple: ArgumentEncoder, Writer: std::io::Write>(
writer: &mut Writer,
arguments: &Tuple,
) -> Result<()> {
let mut ser = IDLBuilder::new();
arguments.encode_ref(&mut ser)?;
ser.serialize(writer)
}
pub fn encode_args<Tuple: ArgumentEncoder>(arguments: Tuple) -> Result<Vec<u8>> {
let mut result = Vec::new();
write_args(&mut result, arguments)?;
Ok(result)
}
pub fn encode_args_ref<Tuple: ArgumentEncoder>(arguments: &Tuple) -> Result<Vec<u8>> {
let mut result = Vec::new();
write_args_ref(&mut result, arguments)?;
Ok(result)
}
pub fn encode_one<T: CandidType>(argument: T) -> Result<Vec<u8>> {
encode_args((argument,))
}
pub trait ArgumentDecoder<'a>: Sized {
fn decode(de: &mut IDLDeserialize<'a>) -> Result<Self>;
}
impl<'a> ArgumentDecoder<'a> for () {
fn decode(_de: &mut IDLDeserialize<'a>) -> Result<()> {
Ok(())
}
}
pub trait ArgumentEncoder {
fn encode(self, ser: &mut IDLBuilder) -> Result<()>;
fn encode_ref(&self, ser: &mut IDLBuilder) -> Result<()>;
}
impl ArgumentEncoder for () {
fn encode(self, _ser: &mut IDLBuilder) -> Result<()> {
Ok(())
}
fn encode_ref(&self, _ser: &mut IDLBuilder) -> Result<()> {
Ok(())
}
}
macro_rules! decode_impl {
( $($id: ident : $typename: ident),* ) => {
impl<'a, $( $typename ),*> ArgumentDecoder<'a> for ($($typename,)*)
where
$( $typename: Deserialize<'a> + CandidType ),*
{
fn decode(de: &mut IDLDeserialize<'a>) -> Result<Self> {
$(
let $id: $typename = de.get_value()?;
)*
Ok(($( $id, )*))
}
}
}
}
macro_rules! encode_impl {
( $($id: ident : $typename: ident),* ) => {
impl<$( $typename ),*> ArgumentEncoder for ($($typename,)*)
where
$( $typename: CandidType ),*
{
fn encode(self, ser: &mut IDLBuilder) -> Result<()> {
let ( $( $id, )* ) = self;
$(
ser.arg(&$id)?;
)*
Ok(())
}
fn encode_ref(&self, ser: &mut IDLBuilder) -> Result<()> {
let ( $( $id, )* ) = self;
$(
ser.arg(&$id)?;
)*
Ok(())
}
}
}
}
decode_impl!(a: A);
decode_impl!(a: A, b: B);
decode_impl!(a: A, b: B, c: C);
decode_impl!(a: A, b: B, c: C, d: D);
decode_impl!(a: A, b: B, c: C, d: D, e: E);
decode_impl!(a: A, b: B, c: C, d: D, e: E, f: F);
decode_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G);
decode_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
decode_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
decode_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
#[rustfmt::skip]
decode_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);
#[rustfmt::skip]
decode_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L);
#[rustfmt::skip]
decode_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M);
#[rustfmt::skip]
decode_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N);
#[rustfmt::skip]
decode_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O);
#[rustfmt::skip]
decode_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P);
encode_impl!(a: A);
encode_impl!(a: A, b: B);
encode_impl!(a: A, b: B, c: C);
encode_impl!(a: A, b: B, c: C, d: D);
encode_impl!(a: A, b: B, c: C, d: D, e: E);
encode_impl!(a: A, b: B, c: C, d: D, e: E, f: F);
encode_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G);
encode_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
encode_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
encode_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
#[rustfmt::skip]
encode_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);
#[rustfmt::skip]
encode_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L);
#[rustfmt::skip]
encode_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M);
#[rustfmt::skip]
encode_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N);
#[rustfmt::skip]
encode_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O);
#[rustfmt::skip]
encode_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P);
#[cfg(test)]
mod tests {
#[test]
fn test_decode_single_value() {
let bytes = Encode!(&"hello").unwrap();
let value = Decode!(&bytes, String).unwrap();
assert_eq!(value, "hello");
}
}