use crate::{
Result, Word,
abi::{Decoder, Encoder},
};
use alloc::vec::Vec;
use alloy_primitives::{Bytes, FixedBytes, I256, U256, hex, utils::vec_try_with_capacity};
use core::{fmt, mem, mem::MaybeUninit, ptr};
#[allow(unknown_lints, unnameable_types)]
mod sealed {
pub trait Sealed {}
impl Sealed for super::WordToken {}
impl Sealed for () {}
impl<T, const N: usize> Sealed for super::FixedSeqToken<T, N> {}
impl<T> Sealed for super::DynSeqToken<T> {}
impl Sealed for super::PackedSeqToken<'_> {}
}
use sealed::Sealed;
pub trait Token<'de>: Sealed + Sized {
const DYNAMIC: bool;
fn decode_from(dec: &mut Decoder<'de>) -> Result<Self>;
#[inline]
unsafe fn decode_many_from<'a>(
dec: &mut Decoder<'de>,
out: &'a mut [MaybeUninit<Self>],
) -> Result<&'a mut [Self]> {
try_init_each(out, || Self::decode_from(dec))
}
fn head_words(&self) -> usize;
fn tail_words(&self) -> usize;
#[inline]
fn total_words(&self) -> usize {
self.head_words() + self.tail_words()
}
fn head_append(&self, enc: &mut Encoder);
#[inline]
fn head_append_many(tokens: &[Self], enc: &mut Encoder) {
for token in tokens {
token.head_append(enc);
}
}
fn tail_append(&self, enc: &mut Encoder);
}
pub trait TokenSeq<'a>: Token<'a> {
const IS_TUPLE: bool = false;
fn encode_sequence(&self, enc: &mut Encoder);
fn decode_sequence(dec: &mut Decoder<'a>) -> Result<Self>;
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[repr(transparent)]
pub struct WordToken(pub Word);
impl<T> From<&T> for WordToken
where
T: Clone,
Self: From<T>,
{
#[inline]
fn from(value: &T) -> Self {
Self::from(value.clone())
}
}
impl<T> From<&mut T> for WordToken
where
T: Clone,
Self: From<T>,
{
#[inline]
fn from(value: &mut T) -> Self {
Self::from(value.clone())
}
}
impl From<Word> for WordToken {
#[inline]
fn from(value: Word) -> Self {
Self(value)
}
}
impl From<WordToken> for Word {
#[inline]
fn from(value: WordToken) -> Self {
value.0
}
}
impl From<bool> for WordToken {
#[inline]
fn from(value: bool) -> Self {
U256::from(value as u64).into()
}
}
impl From<U256> for WordToken {
#[inline]
fn from(value: U256) -> Self {
Self(value.into())
}
}
impl From<I256> for WordToken {
#[inline]
fn from(value: I256) -> Self {
Self(value.into())
}
}
impl From<WordToken> for [u8; 32] {
#[inline]
fn from(value: WordToken) -> [u8; 32] {
value.0.into()
}
}
impl From<[u8; 32]> for WordToken {
#[inline]
fn from(value: [u8; 32]) -> Self {
Self(value.into())
}
}
impl AsRef<Word> for WordToken {
#[inline]
fn as_ref(&self) -> &Word {
&self.0
}
}
impl AsRef<[u8]> for WordToken {
#[inline]
fn as_ref(&self) -> &[u8] {
&self.0.0
}
}
impl<'a> Token<'a> for WordToken {
const DYNAMIC: bool = false;
#[inline]
fn decode_from(dec: &mut Decoder<'a>) -> Result<Self> {
dec.take_word().copied().map(Self)
}
#[inline]
unsafe fn decode_many_from<'b>(
dec: &mut Decoder<'a>,
out: &'b mut [MaybeUninit<Self>],
) -> Result<&'b mut [Self]> {
let len = out.len();
let byte_len = len * Word::len_bytes();
let slice = dec.take_slice(byte_len)?;
unsafe {
core::ptr::copy_nonoverlapping(slice.as_ptr(), out.as_mut_ptr().cast::<u8>(), byte_len);
Ok(core::slice::from_raw_parts_mut(out.as_mut_ptr().cast::<Self>(), len))
}
}
#[inline]
fn head_words(&self) -> usize {
1
}
#[inline]
fn tail_words(&self) -> usize {
0
}
#[inline]
fn head_append(&self, enc: &mut Encoder) {
enc.append_word(self.0);
}
#[inline]
fn head_append_many(tokens: &[Self], enc: &mut Encoder) {
let words = unsafe { &*(tokens as *const [Self] as *const [Word]) };
enc.append_words(words);
}
#[inline]
fn tail_append(&self, _enc: &mut Encoder) {}
}
impl WordToken {
#[inline]
pub const fn new(array: [u8; 32]) -> Self {
Self(FixedBytes(array))
}
#[inline]
pub const fn as_slice(&self) -> &[u8] {
&self.0.0
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FixedSeqToken<T, const N: usize>(pub [T; N]);
impl<T, const N: usize> TryFrom<Vec<T>> for FixedSeqToken<T, N> {
type Error = <[T; N] as TryFrom<Vec<T>>>::Error;
#[inline]
fn try_from(value: Vec<T>) -> Result<Self, Self::Error> {
<[T; N]>::try_from(value).map(Self)
}
}
impl<T, const N: usize> From<[T; N]> for FixedSeqToken<T, N> {
#[inline]
fn from(value: [T; N]) -> Self {
Self(value)
}
}
impl<T, const N: usize> AsRef<[T; N]> for FixedSeqToken<T, N> {
#[inline]
fn as_ref(&self) -> &[T; N] {
&self.0
}
}
impl<'de, T: Token<'de>, const N: usize> Token<'de> for FixedSeqToken<T, N> {
const DYNAMIC: bool = T::DYNAMIC;
#[inline]
fn decode_from(dec: &mut Decoder<'de>) -> Result<Self> {
if Self::DYNAMIC {
dec.take_indirection().and_then(|mut child| Self::decode_sequence(&mut child))
} else {
Self::decode_sequence(dec)
}
}
#[inline]
fn head_words(&self) -> usize {
if Self::DYNAMIC {
1
} else {
self.0.iter().map(T::total_words).sum()
}
}
#[inline]
fn tail_words(&self) -> usize {
if Self::DYNAMIC {
self.0.iter().map(T::total_words).sum()
} else {
0
}
}
#[inline]
fn head_append(&self, enc: &mut Encoder) {
if Self::DYNAMIC {
enc.append_indirection();
} else {
T::head_append_many(&self.0, enc);
}
}
#[inline]
fn tail_append(&self, enc: &mut Encoder) {
if Self::DYNAMIC {
self.encode_sequence(enc);
}
}
}
impl<'de, T: Token<'de>, const N: usize> TokenSeq<'de> for FixedSeqToken<T, N> {
#[inline]
fn encode_sequence(&self, enc: &mut Encoder) {
encode_sequence_impl(&self.0, enc);
}
#[inline]
fn decode_sequence(dec: &mut Decoder<'de>) -> Result<Self> {
let mut arr = crate::impl_core::uninit_array::<T, N>();
unsafe {
T::decode_many_from(dec, &mut arr)?;
Ok(Self(crate::impl_core::array_assume_init(arr)))
}
}
}
impl<T, const N: usize> FixedSeqToken<T, N> {
#[allow(clippy::missing_const_for_fn)]
#[inline]
pub fn into_array(self) -> [T; N] {
self.0
}
#[inline]
pub const fn as_array(&self) -> &[T; N] {
&self.0
}
#[inline]
pub const fn as_slice(&self) -> &[T] {
&self.0
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DynSeqToken<T>(pub Vec<T>);
impl<T> From<Vec<T>> for DynSeqToken<T> {
#[inline]
fn from(value: Vec<T>) -> Self {
Self(value)
}
}
impl<T> AsRef<[T]> for DynSeqToken<T> {
#[inline]
fn as_ref(&self) -> &[T] {
self.0.as_ref()
}
}
impl<'de, T: Token<'de>> Token<'de> for DynSeqToken<T> {
const DYNAMIC: bool = true;
#[inline]
fn decode_from(dec: &mut Decoder<'de>) -> Result<Self> {
let mut child = dec.take_indirection()?;
let len = child.take_offset()?;
let mut child = child.raw_child()?;
let mut tokens = vec_try_with_capacity(len)?;
unsafe {
T::decode_many_from(&mut child, &mut tokens.spare_capacity_mut()[..len])?;
tokens.set_len(len);
}
Ok(Self(tokens))
}
#[inline]
fn head_words(&self) -> usize {
1
}
#[inline]
fn tail_words(&self) -> usize {
1 + self.0.iter().map(T::total_words).sum::<usize>()
}
#[inline]
fn head_append(&self, enc: &mut Encoder) {
enc.append_indirection();
}
#[inline]
fn tail_append(&self, enc: &mut Encoder) {
enc.append_seq_len(self.0.len());
self.encode_sequence(enc);
}
}
impl<'de, T: Token<'de>> TokenSeq<'de> for DynSeqToken<T> {
#[inline]
fn encode_sequence(&self, enc: &mut Encoder) {
encode_sequence_impl(&self.0, enc);
}
#[inline]
fn decode_sequence(dec: &mut Decoder<'de>) -> Result<Self> {
Self::decode_from(dec)
}
}
impl<T> DynSeqToken<T> {
#[inline]
pub fn as_slice(&self) -> &[T] {
&self.0
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct PackedSeqToken<'a>(pub &'a [u8]);
impl fmt::Debug for PackedSeqToken<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("PackedSeqToken").field(&hex::encode_prefixed(self.0)).finish()
}
}
impl<'a> From<&'a [u8]> for PackedSeqToken<'a> {
#[inline]
fn from(value: &'a [u8]) -> Self {
Self(value)
}
}
impl<'a> From<&'a Vec<u8>> for PackedSeqToken<'a> {
#[inline]
fn from(value: &'a Vec<u8>) -> Self {
Self(value.as_slice())
}
}
impl AsRef<[u8]> for PackedSeqToken<'_> {
#[inline]
fn as_ref(&self) -> &[u8] {
self.0
}
}
impl<'de: 'a, 'a> Token<'de> for PackedSeqToken<'a> {
const DYNAMIC: bool = true;
#[inline]
fn decode_from(dec: &mut Decoder<'de>) -> Result<Self> {
let mut child = dec.take_indirection()?;
let len = child.take_offset()?;
let bytes = child.peek_len(len)?;
Ok(PackedSeqToken(bytes))
}
#[inline]
fn head_words(&self) -> usize {
1
}
#[inline]
fn tail_words(&self) -> usize {
1 + crate::utils::words_for(self.0)
}
#[inline]
fn head_append(&self, enc: &mut Encoder) {
enc.append_indirection();
}
#[inline]
fn tail_append(&self, enc: &mut Encoder) {
enc.append_packed_seq(self.0);
}
}
impl PackedSeqToken<'_> {
#[allow(clippy::missing_const_for_fn)]
#[inline]
pub fn into_vec(self) -> Vec<u8> {
self.0.to_vec()
}
pub fn into_bytes(self) -> Bytes {
Bytes::copy_from_slice(self.0)
}
#[inline]
pub const fn as_slice(&self) -> &[u8] {
self.0
}
}
macro_rules! tuple_impls {
($count:literal $($ty:ident),+) => {
impl<'de, $($ty: Token<'de>,)+> Sealed for ($($ty,)+) {}
#[allow(non_snake_case)]
impl<'de, $($ty: Token<'de>,)+> Token<'de> for ($($ty,)+) {
const DYNAMIC: bool = $( <$ty as Token>::DYNAMIC )||+;
#[inline]
fn decode_from(dec: &mut Decoder<'de>) -> Result<Self> {
if Self::DYNAMIC {
dec.take_indirection().and_then(|mut child| Self::decode_sequence(&mut child))
} else {
Self::decode_sequence(dec)
}
}
#[inline]
fn head_words(&self) -> usize {
if Self::DYNAMIC {
1
} else {
let ($($ty,)+) = self;
0 $( + $ty.total_words() )+
}
}
#[inline]
fn tail_words(&self) -> usize {
if Self::DYNAMIC {
let ($($ty,)+) = self;
0 $( + $ty.total_words() )+
} else {
0
}
}
#[inline]
fn head_append(&self, enc: &mut Encoder) {
if Self::DYNAMIC {
enc.append_indirection();
} else {
let ($($ty,)+) = self;
$(
$ty.head_append(enc);
)+
}
}
#[inline]
fn tail_append(&self, enc: &mut Encoder) {
if Self::DYNAMIC {
self.encode_sequence(enc);
}
}
}
#[allow(non_snake_case)]
impl<'de, $($ty: Token<'de>,)+> TokenSeq<'de> for ($($ty,)+) {
const IS_TUPLE: bool = true;
fn encode_sequence(&self, enc: &mut Encoder) {
let ($($ty,)+) = self;
enc.push_offset(0 $( + $ty.head_words() )+);
$(
$ty.head_append(enc);
enc.bump_offset($ty.tail_words());
)+
$(
$ty.tail_append(enc);
)+
enc.pop_offset();
}
#[inline]
fn decode_sequence(dec: &mut Decoder<'de>) -> Result<Self> {
Ok(($(
match <$ty as Token>::decode_from(dec) {
Ok(t) => t,
Err(e) => return Err(e),
},
)+))
}
}
};
}
impl<'de> Token<'de> for () {
const DYNAMIC: bool = false;
#[inline]
fn decode_from(_dec: &mut Decoder<'de>) -> Result<Self> {
Ok(())
}
#[inline]
fn head_words(&self) -> usize {
0
}
#[inline]
fn tail_words(&self) -> usize {
0
}
#[inline]
fn head_append(&self, _enc: &mut Encoder) {}
#[inline]
fn tail_append(&self, _enc: &mut Encoder) {}
}
impl<'de> TokenSeq<'de> for () {
const IS_TUPLE: bool = true;
#[inline]
fn encode_sequence(&self, _enc: &mut Encoder) {}
#[inline]
fn decode_sequence(_dec: &mut Decoder<'de>) -> Result<Self> {
Ok(())
}
}
all_the_tuples!(tuple_impls);
fn encode_sequence_impl<'de, T: Token<'de>>(tokens: &[T], enc: &mut Encoder) {
if T::DYNAMIC {
enc.push_offset(tokens.iter().map(T::head_words).sum());
for inner in tokens {
inner.head_append(enc);
enc.bump_offset(inner.tail_words());
}
for inner in tokens {
inner.tail_append(enc);
}
enc.pop_offset();
} else {
T::head_append_many(tokens, enc);
}
}
#[inline]
fn try_init_each<T, E, F>(out: &mut [MaybeUninit<T>], mut f: F) -> core::result::Result<&mut [T], E>
where
F: FnMut() -> core::result::Result<T, E>,
{
struct Guard<'a, T> {
buf: &'a mut [MaybeUninit<T>],
initialized: usize,
}
impl<T> Drop for Guard<'_, T> {
fn drop(&mut self) {
unsafe {
let ptr = self.buf.as_mut_ptr().cast::<T>();
ptr::drop_in_place(ptr::slice_from_raw_parts_mut(ptr, self.initialized));
}
}
}
let mut guard = Guard { buf: out, initialized: 0 };
for x in guard.buf.iter_mut() {
x.write(f()?);
guard.initialized += 1;
}
let buf = guard.buf as *mut [MaybeUninit<T>] as *mut [T];
mem::forget(guard);
Ok(unsafe { &mut *buf })
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{SolType, sol_data};
use alloy_primitives::B256;
macro_rules! assert_type_check {
($sol:ty, $token:expr $(,)?) => {
assert!(<$sol>::type_check($token).is_ok())
};
}
macro_rules! assert_not_type_check {
($sol:ty, $token:expr $(,)?) => {
assert!(<$sol>::type_check($token).is_err())
};
}
#[test]
fn test_type_check() {
assert_type_check!(
(sol_data::Uint<256>, sol_data::Bool),
&(WordToken(B256::default()), WordToken(B256::default())),
);
assert_not_type_check!(sol_data::Uint<8>, &Word::repeat_byte(0x11).into());
assert_not_type_check!(sol_data::Bool, &B256::repeat_byte(0x11).into());
assert_not_type_check!(sol_data::FixedBytes<31>, &B256::repeat_byte(0x11).into());
assert_type_check!(
(sol_data::Uint<32>, sol_data::Bool),
&(WordToken(B256::default()), WordToken(B256::default())),
);
assert_type_check!(
sol_data::Array<sol_data::Bool>,
&DynSeqToken(vec![WordToken(B256::default()), WordToken(B256::default()),]),
);
assert_type_check!(
sol_data::Array<sol_data::Bool>,
&DynSeqToken(vec![WordToken(B256::default()), WordToken(B256::default()),]),
);
assert_type_check!(
sol_data::Array<sol_data::Address>,
&DynSeqToken(vec![WordToken(B256::default()), WordToken(B256::default()),]),
);
assert_type_check!(
sol_data::FixedArray<sol_data::Bool, 2>,
&FixedSeqToken::<_, 2>([
WordToken(B256::default()),
WordToken(B256::default()),
]),
);
assert_type_check!(
sol_data::FixedArray<sol_data::Address, 2>,
&FixedSeqToken::<_, 2>([
WordToken(B256::default()),
WordToken(B256::default()),
]),
);
}
}