alloy_dyn_abi/dynamic/
token.rs1use crate::{Decoder, DynSolValue, Error, Result, Word};
2use alloc::{borrow::Cow, boxed::Box, vec::Vec};
3use alloy_primitives::try_vec;
4use alloy_sol_types::abi::token::{PackedSeqToken, Token, WordToken};
5
6#[derive(Clone, Debug)]
13pub enum DynToken<'a> {
14    Word(Word),
16    FixedSeq(Cow<'a, [DynToken<'a>]>, usize),
18    DynSeq {
20        contents: Cow<'a, [DynToken<'a>]>,
22        #[doc(hidden)]
27        template: Option<Box<DynToken<'a>>>,
28    },
29    PackedSeq(&'a [u8]),
31}
32
33impl<T: Into<Word>> From<T> for DynToken<'_> {
34    #[inline]
35    fn from(value: T) -> Self {
36        Self::Word(value.into())
37    }
38}
39
40impl PartialEq<DynToken<'_>> for DynToken<'_> {
41    #[inline]
42    fn eq(&self, other: &DynToken<'_>) -> bool {
43        match (self, other) {
44            (Self::Word(l0), DynToken::Word(r0)) => l0 == r0,
45            (Self::FixedSeq(l0, l1), DynToken::FixedSeq(r0, r1)) => l0 == r0 && l1 == r1,
46            (
47                Self::DynSeq { contents: l_contents, .. },
48                DynToken::DynSeq { contents: r_contents, .. },
49            ) => l_contents == r_contents,
50            (Self::PackedSeq(l0), DynToken::PackedSeq(r0)) => l0 == r0,
51            _ => false,
52        }
53    }
54}
55
56impl Eq for DynToken<'_> {}
57
58impl<'a> DynToken<'a> {
59    pub fn minimum_words(&self) -> usize {
61        match self {
62            DynToken::Word(_) => 1,
63            DynToken::PackedSeq(_) => 1,
64            DynToken::FixedSeq(contents, _) => {
65                contents.iter().map(Self::minimum_words).sum::<usize>()
66            }
67            DynToken::DynSeq { .. } => 1,
68        }
69    }
70
71    #[inline]
73    pub fn from_fixed_seq(seq: &'a [DynSolValue]) -> Self {
74        let tokens = seq.iter().map(DynSolValue::tokenize).collect();
75        Self::FixedSeq(Cow::Owned(tokens), seq.len())
76    }
77
78    #[inline]
80    pub fn from_dyn_seq(seq: &'a [DynSolValue]) -> Self {
81        let tokens = seq.iter().map(DynSolValue::tokenize).collect();
82        Self::DynSeq { contents: Cow::Owned(tokens), template: None }
83    }
84
85    #[inline]
87    pub const fn as_word(&self) -> Option<Word> {
88        match self {
89            Self::Word(word) => Some(*word),
90            _ => None,
91        }
92    }
93
94    #[inline]
96    pub fn as_fixed_seq(&self) -> Option<(&[Self], usize)> {
97        match self {
98            Self::FixedSeq(tokens, size) => Some((tokens, *size)),
99            _ => None,
100        }
101    }
102
103    #[inline]
105    pub fn as_dynamic_seq(&self) -> Option<&[Self]> {
106        match self {
107            Self::DynSeq { contents, .. } => Some(contents),
108            _ => None,
109        }
110    }
111
112    #[inline]
114    pub fn as_token_seq(&self) -> Option<&[Self]> {
115        match self {
116            Self::FixedSeq(contents, _) | Self::DynSeq { contents, .. } => Some(contents),
117            _ => None,
118        }
119    }
120
121    #[inline]
123    pub const fn as_packed_seq(&self) -> Option<&[u8]> {
124        match self {
125            Self::PackedSeq(bytes) => Some(bytes),
126            _ => None,
127        }
128    }
129
130    #[inline]
132    pub fn is_dynamic(&self) -> bool {
133        match self {
134            Self::Word(_) => false,
135            Self::FixedSeq(inner, _) => inner.iter().any(Self::is_dynamic),
136            Self::DynSeq { .. } | Self::PackedSeq(_) => true,
137        }
138    }
139
140    #[inline]
142    pub(crate) fn decode_populate(&mut self, dec: &mut Decoder<'a>) -> Result<()> {
143        match self {
144            Self::Word(w) => *w = WordToken::decode_from(dec)?.0,
145            Self::FixedSeq(..) => {
146                let dynamic = self.is_dynamic();
147                let mut child = if dynamic { dec.take_indirection() } else { dec.raw_child() }?;
148
149                self.decode_sequence_populate(&mut child)?;
150
151                if !dynamic {
152                    dec.take_offset_from(&child);
153                }
154            }
155            Self::DynSeq { contents, template } => {
156                let mut child = dec.take_indirection()?;
157                let size = child.take_offset()?;
158                if size == 0 {
159                    debug_assert!(contents.is_empty());
161                    return Ok(());
162                }
163
164                let template = template.take().expect("no template for dynamic sequence");
167
168                let mut child = child.raw_child()?;
173
174                if child.remaining_words() < template.minimum_words() * size {
178                    return Err(alloy_sol_types::Error::Overrun.into());
179                }
180
181                let mut new_tokens = if size == 1 {
182                    unsafe { Vec::from_raw_parts(Box::into_raw(template), 1, 1) }
184                } else {
185                    try_vec![*template; size]?
186                };
187
188                for t in &mut new_tokens {
189                    t.decode_populate(&mut child)?;
190                }
191
192                *contents = new_tokens.into();
193            }
194            Self::PackedSeq(buf) => *buf = PackedSeqToken::decode_from(dec)?.0,
195        }
196        Ok(())
197    }
198
199    #[inline]
202    pub(crate) fn decode_sequence_populate(&mut self, dec: &mut Decoder<'a>) -> Result<()> {
203        match self {
204            Self::FixedSeq(buf, size) => {
205                buf.to_mut().iter_mut().take(*size).try_for_each(|item| item.decode_populate(dec))
206            }
207            Self::DynSeq { .. } => self.decode_populate(dec),
208            _ => Err(Error::custom("Called decode_sequence_populate on non-sequence token")),
209        }
210    }
211
212    #[inline]
214    pub(crate) fn decode_single_populate(&mut self, dec: &mut Decoder<'a>) -> Result<()> {
215        self.decode_populate(dec)
219    }
220}