#[cfg(any(test, feature = "cmc-host"))]
use ic_platform::candid_codec::{Encode, encode_one};
use ic_platform::candid_codec::{
Error as EncodeError,
arguments::{EncodeArgs, encode_args},
decoder::{Arity, Decode, Error as PlatformDecodeError, Limits, decode_args},
};
const MAX_ENCODED_BYTES: usize = 8 * 1024 * 1024;
const BASE_WORK: usize = 64 * 1024;
const MAX_WORK: usize = 256_000_000;
const BASE_ALLOCATION: usize = 1024 * 1024;
const MAX_ALLOCATION: usize = 256 * 1024 * 1024;
const MAX_TYPE_ENTRIES: usize = 4_096;
const MAX_HEADER_BYTES: usize = 64 * 1024;
#[derive(Debug, thiserror::Error)]
pub enum CandidReplyError {
#[error("Candid reply decoding quota exceeded")]
Budget,
#[error("invalid Candid reply: {reason:?}")]
Protocol { reason: PlatformDecodeError },
}
pub fn encode_arguments<T: EncodeArgs>(args: &T) -> Result<Vec<u8>, EncodeError> {
encode_args(args, MAX_ENCODED_BYTES)
}
#[cfg(any(test, feature = "cmc-host"))]
pub fn encode_value<T: Encode + ?Sized>(value: &T) -> Result<Vec<u8>, EncodeError> {
encode_one(value, MAX_ENCODED_BYTES)
}
pub fn decode_reply<'a, T: Decode<'a>>(bytes: &'a [u8]) -> Result<T, CandidReplyError> {
let limits = Limits {
bytes: bytes.len(),
header: MAX_HEADER_BYTES,
types: MAX_TYPE_ENTRIES,
work: bytes
.len()
.saturating_mul(32)
.saturating_add(BASE_WORK)
.min(MAX_WORK),
allocation: bytes
.len()
.saturating_mul(16)
.saturating_add(BASE_ALLOCATION)
.min(MAX_ALLOCATION),
};
decode_args::<(T,)>(bytes, limits, Arity::Compatible)
.map(|(value,)| value)
.map_err(|reason| match reason {
PlatformDecodeError::Limit => CandidReplyError::Budget,
reason => CandidReplyError::Protocol { reason },
})
}
#[cfg(test)]
mod tests;