alloy_dyn_abi/dynamic/
token.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
use crate::{Decoder, DynSolValue, Error, Result, Word};
use alloc::{borrow::Cow, boxed::Box, vec::Vec};
use alloy_primitives::try_vec;
use alloy_sol_types::abi::token::{PackedSeqToken, Token, WordToken};

/// A dynamic token.
///
/// Equivalent to an enum over all types implementing [`Token`].
// NOTE: do not derive `Hash` for this type. The derived version is not
// compatible with the current `PartialEq` implementation. If manually
// implementing `Hash`, ignore the `template` prop in the `DynSeq` variant
#[derive(Clone, Debug)]
pub enum DynToken<'a> {
    /// A single word.
    Word(Word),
    /// A Fixed Sequence.
    FixedSeq(Cow<'a, [DynToken<'a>]>, usize),
    /// A dynamic-length sequence.
    DynSeq {
        /// The contents of the dynamic sequence.
        contents: Cow<'a, [DynToken<'a>]>,
        /// The type template of the dynamic sequence.
        /// This is used only when decoding. It indicates what the token type
        /// of the sequence is. During tokenization of data, the type of the
        /// contents is known, so this is not needed.
        #[doc(hidden)]
        template: Option<Box<DynToken<'a>>>,
    },
    /// A packed sequence (string or bytes).
    PackedSeq(&'a [u8]),
}

impl<T: Into<Word>> From<T> for DynToken<'_> {
    #[inline]
    fn from(value: T) -> Self {
        Self::Word(value.into())
    }
}

impl PartialEq<DynToken<'_>> for DynToken<'_> {
    #[inline]
    fn eq(&self, other: &DynToken<'_>) -> bool {
        match (self, other) {
            (Self::Word(l0), DynToken::Word(r0)) => l0 == r0,
            (Self::FixedSeq(l0, l1), DynToken::FixedSeq(r0, r1)) => l0 == r0 && l1 == r1,
            (
                Self::DynSeq { contents: l_contents, .. },
                DynToken::DynSeq { contents: r_contents, .. },
            ) => l_contents == r_contents,
            (Self::PackedSeq(l0), DynToken::PackedSeq(r0)) => l0 == r0,
            _ => false,
        }
    }
}

impl Eq for DynToken<'_> {}

impl<'a> DynToken<'a> {
    /// Calculate the minimum number of words required to encode this token.
    pub fn minimum_words(&self) -> usize {
        match self {
            DynToken::Word(_) => 1,
            DynToken::PackedSeq(_) => 1,
            DynToken::FixedSeq(contents, _) => {
                contents.iter().map(Self::minimum_words).sum::<usize>()
            }
            DynToken::DynSeq { .. } => 1,
        }
    }

    /// Instantiate a DynToken from a fixed sequence of values.
    #[inline]
    pub fn from_fixed_seq(seq: &'a [DynSolValue]) -> Self {
        let tokens = seq.iter().map(DynSolValue::tokenize).collect();
        Self::FixedSeq(Cow::Owned(tokens), seq.len())
    }

    /// Instantiate a DynToken from a dynamic sequence of values.
    #[inline]
    pub fn from_dyn_seq(seq: &'a [DynSolValue]) -> Self {
        let tokens = seq.iter().map(DynSolValue::tokenize).collect();
        Self::DynSeq { contents: Cow::Owned(tokens), template: None }
    }

    /// Attempt to cast to a word.
    #[inline]
    pub const fn as_word(&self) -> Option<Word> {
        match self {
            Self::Word(word) => Some(*word),
            _ => None,
        }
    }

    /// Fallible cast into a fixed sequence.
    #[inline]
    pub fn as_fixed_seq(&self) -> Option<(&[Self], usize)> {
        match self {
            Self::FixedSeq(tokens, size) => Some((tokens, *size)),
            _ => None,
        }
    }

    /// Fallible cast into a dynamic sequence.
    #[inline]
    pub fn as_dynamic_seq(&self) -> Option<&[Self]> {
        match self {
            Self::DynSeq { contents, .. } => Some(contents),
            _ => None,
        }
    }

    /// Fallible cast into a sequence, dynamic or fixed-size
    #[inline]
    pub fn as_token_seq(&self) -> Option<&[Self]> {
        match self {
            Self::FixedSeq(contents, _) | Self::DynSeq { contents, .. } => Some(contents),
            _ => None,
        }
    }

    /// Fallible cast into a packed sequence.
    #[inline]
    pub const fn as_packed_seq(&self) -> Option<&[u8]> {
        match self {
            Self::PackedSeq(bytes) => Some(bytes),
            _ => None,
        }
    }

    /// True if the type is dynamic, else false.
    #[inline]
    pub fn is_dynamic(&self) -> bool {
        match self {
            Self::Word(_) => false,
            Self::FixedSeq(inner, _) => inner.iter().any(Self::is_dynamic),
            Self::DynSeq { .. } | Self::PackedSeq(_) => true,
        }
    }

    /// Decodes from a decoder, populating the structure with the decoded data.
    #[inline]
    pub(crate) fn decode_populate(&mut self, dec: &mut Decoder<'a>) -> Result<()> {
        match self {
            Self::Word(w) => *w = WordToken::decode_from(dec)?.0,
            Self::FixedSeq(..) => {
                let dynamic = self.is_dynamic();
                let mut child = if dynamic { dec.take_indirection() } else { dec.raw_child() }?;

                self.decode_sequence_populate(&mut child)?;

                if !dynamic {
                    dec.take_offset_from(&child);
                }
            }
            Self::DynSeq { contents, template } => {
                let mut child = dec.take_indirection()?;
                let size = child.take_offset()?;
                if size == 0 {
                    // should already be empty from `empty_dyn_token`
                    debug_assert!(contents.is_empty());
                    return Ok(());
                }

                // This expect is safe because this is only invoked after
                // `empty_dyn_token()` which always sets template
                let template = template.take().expect("no template for dynamic sequence");

                // This appears to be an unclarity in the Solidity spec. The
                // spec specifies that offsets are relative to the beginning of
                // `enc(X)`. But known-good test vectors have it relative to the
                // word AFTER the array size
                let mut child = child.raw_child()?;

                // Check that the decoder contains enough words to decode the
                // sequence. Each item in the sequence is at least one word, so
                // the remaining words must be at least the size of the sequence
                if child.remaining_words() < template.minimum_words() * size {
                    return Err(alloy_sol_types::Error::Overrun.into());
                }

                let mut new_tokens = if size == 1 {
                    // re-use the box allocation
                    unsafe { Vec::from_raw_parts(Box::into_raw(template), 1, 1) }
                } else {
                    try_vec![*template; size]?
                };

                for t in &mut new_tokens {
                    t.decode_populate(&mut child)?;
                }

                *contents = new_tokens.into();
            }
            Self::PackedSeq(buf) => *buf = PackedSeqToken::decode_from(dec)?.0,
        }
        Ok(())
    }

    /// Decode a sequence from the decoder, populating the data by consuming
    /// decoder words.
    #[inline]
    pub(crate) fn decode_sequence_populate(&mut self, dec: &mut Decoder<'a>) -> Result<()> {
        match self {
            Self::FixedSeq(buf, size) => {
                buf.to_mut().iter_mut().take(*size).try_for_each(|item| item.decode_populate(dec))
            }
            Self::DynSeq { .. } => self.decode_populate(dec),
            _ => Err(Error::custom("Called decode_sequence_populate on non-sequence token")),
        }
    }

    /// Decode a single item of this type, as a sequence of length 1.
    #[inline]
    pub(crate) fn decode_single_populate(&mut self, dec: &mut Decoder<'a>) -> Result<()> {
        // This is what
        // `Self::FixedSeq(vec![self.clone()], 1).decode_populate()`
        // would do, so we skip the allocation.
        self.decode_populate(dec)
    }
}