evm_coder/
custom_signature.rs1pub const SIGNATURE_SIZE_LIMIT: usize = 256;
69
70#[derive(Debug)]
72pub struct SignatureUnit {
73 pub data: [u8; SIGNATURE_SIZE_LIMIT],
75 pub len: usize,
77}
78
79impl SignatureUnit {
80 #[must_use]
82 pub const fn new(name: &'static str) -> SignatureUnit {
83 let mut signature = [0_u8; SIGNATURE_SIZE_LIMIT];
84 let name = name.as_bytes();
85 let name_len = name.len();
86 let mut dst_offset = 0;
87 crate::make_signature!(@copy(name, signature, name_len, dst_offset));
88 SignatureUnit {
89 data: signature,
90 len: name_len,
91 }
92 }
93 #[must_use]
95 pub fn as_str(&self) -> Option<&str> {
96 core::str::from_utf8(&self.data[0..self.len]).ok()
97 }
98}
99
100#[macro_export]
108macro_rules! make_signature {
109 (new $($tt:tt)*) => {
110 ($crate::custom_signature::SignatureUnit {
111 data: {
112 let mut out = [0u8; $crate::custom_signature::SIGNATURE_SIZE_LIMIT];
113 let mut dst_offset = 0;
114 $crate::make_signature!(@data(out, dst_offset); $($tt)*);
115 out
116 },
117 len: {0 + $crate::make_signature!(@size; $($tt)*)},
118 })
119 };
120
121 (@size;) => {
122 0
123 };
124 (@size; fixed($expr:expr) $($tt:tt)*) => {
125 $expr.len() + $crate::make_signature!(@size; $($tt)*)
126 };
127 (@size; nameof($expr:expr) $($tt:tt)*) => {
128 $expr.len + $crate::make_signature!(@size; $($tt)*)
129 };
130 (@size; numof($expr:expr) $($tt:tt)*) => {
131 {
132 let mut out = 0;
133 let mut v = $expr;
134 if v == 0 {
135 out = 1;
136 } else {
137 while v > 0 {
138 out += 1;
139 v /= 10;
140 }
141 }
142 out
143 } + $crate::make_signature!(@size; $($tt)*)
144 };
145 (@size; shift_left($expr:expr) $($tt:tt)*) => {
146 $crate::make_signature!(@size; $($tt)*) - $expr
147 };
148
149 (@data($dst:ident, $dst_offset:ident);) => {};
150 (@data($dst:ident, $dst_offset:ident); fixed($expr:expr) $($tt:tt)*) => {
151 {
152 let data = $expr.as_bytes();
153 let data_len = data.len();
154 $crate::make_signature!(@copy(data, $dst, data_len, $dst_offset));
155 }
156 $crate::make_signature!(@data($dst, $dst_offset); $($tt)*)
157 };
158 (@data($dst:ident, $dst_offset:ident); nameof($expr:expr) $($tt:tt)*) => {
159 {
160 $crate::make_signature!(@copy(&$expr.data, $dst, $expr.len, $dst_offset));
161 }
162 $crate::make_signature!(@data($dst, $dst_offset); $($tt)*)
163 };
164 (@data($dst:ident, $dst_offset:ident); numof($expr:expr) $($tt:tt)*) => {
165 {
166 let mut v = $expr;
167 let mut need_to_swap = 0;
168 if v == 0 {
169 $dst[$dst_offset] = b'0';
170 $dst_offset += 1;
171 } else {
172 while v > 0 {
173 let n = (v % 10) as u8;
174 $dst[$dst_offset] = b'0' + n;
175 v /= 10;
176 need_to_swap += 1;
177 $dst_offset += 1;
178 }
179 }
180 let mut i = 0;
181 #[allow(clippy::manual_swap)]
182 while i < need_to_swap / 2 {
183 let a = $dst_offset - i - 1;
184 let b = $dst_offset - need_to_swap + i;
185 let v = $dst[a];
186 $dst[a] = $dst[b];
187 $dst[b] = v;
188 i += 1;
189 }
190 }
191 $crate::make_signature!(@data($dst, $dst_offset); $($tt)*)
192 };
193 (@data($dst:ident, $dst_offset:ident); shift_left($expr:expr) $($tt:tt)*) => {
194 $dst_offset -= $expr;
195 $crate::make_signature!(@data($dst, $dst_offset); $($tt)*)
196 };
197
198 (@copy($src:expr, $dst:expr, $src_len:expr, $dst_offset:ident)) => {
199 {
200 let mut src_offset = 0;
201 let src_len: usize = $src_len;
202 while src_offset < src_len {
203 $dst[$dst_offset] = $src[src_offset];
204 $dst_offset += 1;
205 src_offset += 1;
206 }
207 }
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use core::str::from_utf8;
214
215 use super::{SignatureUnit, SIGNATURE_SIZE_LIMIT};
216
217 trait Name {
218 const NAME: SignatureUnit;
219
220 fn name() -> &'static str {
221 from_utf8(&Self::NAME.data[..Self::NAME.len]).expect("bad utf-8")
222 }
223 }
224
225 impl Name for u8 {
226 const NAME: SignatureUnit = make_signature!(new fixed("uint8"));
227 }
228 impl Name for u32 {
229 const NAME: SignatureUnit = make_signature!(new fixed("uint32"));
230 }
231 impl<T: Name> Name for Vec<T> {
232 const NAME: SignatureUnit = make_signature!(new nameof(T::NAME) fixed("[]"));
233 }
234 impl<A: Name, B: Name> Name for (A, B) {
235 const NAME: SignatureUnit =
236 make_signature!(new fixed("(") nameof(A::NAME) fixed(",") nameof(B::NAME) fixed(")"));
237 }
238 impl<A: Name> Name for (A,) {
239 const NAME: SignatureUnit =
240 make_signature!(new fixed("(") nameof(A::NAME) fixed(",") shift_left(1) fixed(")"));
241 }
242 impl<A: Name, const SIZE: usize> Name for [A; SIZE] {
243 const NAME: SignatureUnit =
244 make_signature!(new nameof(A::NAME) fixed("[") numof(SIZE) fixed("]"));
245 }
246
247 struct MaxSize();
248 impl Name for MaxSize {
249 const NAME: SignatureUnit = SignatureUnit {
250 data: [b'!'; SIGNATURE_SIZE_LIMIT],
251 len: SIGNATURE_SIZE_LIMIT,
252 };
253 }
254
255 #[test]
256 fn simple() {
257 assert_eq!(u8::name(), "uint8");
258 assert_eq!(u32::name(), "uint32");
259 }
260
261 #[test]
262 fn vector_of_simple() {
263 assert_eq!(<Vec<u8>>::name(), "uint8[]");
264 assert_eq!(<Vec<u32>>::name(), "uint32[]");
265 }
266
267 #[test]
268 fn vector_of_vector() {
269 assert_eq!(<Vec<Vec<u8>>>::name(), "uint8[][]");
270 }
271
272 #[test]
273 fn tuple_of_simple() {
274 assert_eq!(<(u32, u8)>::name(), "(uint32,uint8)");
275 }
276
277 #[test]
278 fn tuple_of_tuple() {
279 assert_eq!(
280 <((u32, u8), (u8, u32))>::name(),
281 "((uint32,uint8),(uint8,uint32))"
282 );
283 }
284
285 #[test]
286 fn vector_of_tuple() {
287 assert_eq!(<Vec<(u32, u8)>>::name(), "(uint32,uint8)[]");
288 }
289
290 #[test]
291 fn tuple_of_vector() {
292 assert_eq!(<(Vec<u32>, u8)>::name(), "(uint32[],uint8)");
293 }
294
295 #[test]
296 fn complex() {
297 assert_eq!(
298 <(Vec<u32>, (u32, Vec<u8>))>::name(),
299 "(uint32[],(uint32,uint8[]))"
300 );
301 }
302
303 #[test]
304 fn max_size() {
305 assert_eq!(<MaxSize>::name(), "!".repeat(SIGNATURE_SIZE_LIMIT));
306 }
307
308 #[test]
309 fn shift() {
310 assert_eq!(<(u32,)>::name(), "(uint32)");
311 }
312
313 #[test]
314 fn num() {
315 assert_eq!(<[u8; 0]>::name(), "uint8[0]");
316 assert_eq!(<[u8; 1234]>::name(), "uint8[1234]");
317 assert_eq!(<[u8; 12345]>::name(), "uint8[12345]");
318 }
319
320 #[test]
321 #[ignore = "fails in ci due to rust version"]
322 fn over_max_size() {
323 let t = trybuild::TestCases::new();
324 t.compile_fail("tests/build_failed/custom_signature_over_max_size.rs");
325 }
326}