1use proc_macro2::TokenStream;
4use quote::quote;
5
6pub(crate) const RUST_INT_SIZES: &[usize] = &[8, 16, 32, 64, 128];
7pub(crate) const ALLOY_INT_SIZES: &[usize] = &[8, 16, 32, 64, 96, 128, 256];
8
9#[derive(Debug, Clone)]
13enum StorableConversionStrategy {
14 UnsignedRust,
15 UnsignedAlloy(proc_macro2::Ident),
16 SignedRust(proc_macro2::Ident),
17 SignedAlloy(proc_macro2::Ident),
18 FixedBytes(usize),
19}
20
21#[derive(Debug, Clone)]
23enum StorageKeyStrategy {
24 Simple, WithSize(usize), SignedRaw(usize), AsSlice, }
29
30#[derive(Debug, Clone)]
32struct TypeConfig {
33 type_path: TokenStream,
34 byte_count: usize,
35 storable_strategy: StorableConversionStrategy,
36 storage_key_strategy: StorageKeyStrategy,
37}
38
39fn gen_storable_layout_impl(type_path: &TokenStream, byte_count: usize) -> TokenStream {
43 quote! {
44 impl StorableType for #type_path {
45 const LAYOUT: Layout = Layout::Bytes(#byte_count);
46 type Handler = crate::storage::Slot<Self>;
47
48 fn handle(slot: U256, ctx: LayoutCtx, address: ::alloy::primitives::Address) -> Self::Handler {
49 crate::storage::Slot::new_with_ctx(slot, ctx, address)
50 }
51 }
52 }
53}
54
55fn gen_storage_key_impl(type_path: &TokenStream, strategy: &StorageKeyStrategy) -> TokenStream {
57 let conversion = match strategy {
58 StorageKeyStrategy::Simple => quote! { self.to_be_bytes() },
59 StorageKeyStrategy::WithSize(size) => quote! { self.to_be_bytes::<#size>() },
60 StorageKeyStrategy::SignedRaw(size) => quote! { self.into_raw().to_be_bytes::<#size>() },
61 StorageKeyStrategy::AsSlice => quote! { self.as_slice() },
62 };
63
64 quote! {
65 impl StorageKey for #type_path {
66 #[inline]
67 fn as_storage_bytes(&self) -> impl AsRef<[u8]> {
68 #conversion
69 }
70 }
71 }
72}
73
74fn gen_to_word_impl(type_path: &TokenStream, strategy: &StorableConversionStrategy) -> TokenStream {
76 match strategy {
77 StorableConversionStrategy::UnsignedRust => {
78 quote! {
79 impl FromWord for #type_path {
80 #[inline]
81 fn to_word(&self) -> ::alloy::primitives::U256 {
82 ::alloy::primitives::U256::from(*self)
83 }
84
85 #[inline]
86 fn from_word(word: U256) -> crate::error::Result<Self> {
87 word.try_into().map_err(|_| crate::error::TempoPrecompileError::under_overflow())
88 }
89 }
90 }
91 }
92 StorableConversionStrategy::UnsignedAlloy(ty) => {
93 quote! {
94 impl FromWord for #type_path {
95 #[inline]
96 fn to_word(&self) -> ::alloy::primitives::U256 {
97 ::alloy::primitives::U256::from(*self)
98 }
99
100 #[inline]
101 fn from_word(word: ::alloy::primitives::U256) -> crate::error::Result<Self> {
102 if word > ::alloy::primitives::U256::from(::alloy::primitives::aliases::#ty::MAX) {
104 return Err(crate::error::TempoPrecompileError::under_overflow());
105 }
106 Ok(word.to::<Self>())
107 }
108 }
109 }
110 }
111 StorableConversionStrategy::SignedRust(unsigned_type) => {
112 quote! {
113 impl FromWord for #type_path {
114 #[inline]
115 fn to_word(&self) -> U256 {
116 ::alloy::primitives::U256::from(*self as #unsigned_type)
118 }
119
120 #[inline]
121 fn from_word(word: U256) -> crate::error::Result<Self> {
122 let unsigned: #unsigned_type = word.try_into()
124 .map_err(|_| crate::error::TempoPrecompileError::under_overflow())?;
125 Ok(unsigned as Self)
126 }
127 }
128 }
129 }
130 StorableConversionStrategy::SignedAlloy(unsigned_type) => {
131 quote! {
132 impl FromWord for #type_path {
133 #[inline]
134 fn to_word(&self) -> ::alloy::primitives::U256 {
135 ::alloy::primitives::U256::from(self.into_raw())
137 }
138
139 #[inline]
140 fn from_word(word: ::alloy::primitives::U256) -> crate::error::Result<Self> {
141 if word > ::alloy::primitives::U256::from(::alloy::primitives::aliases::#unsigned_type::MAX) {
143 return Err(crate::error::TempoPrecompileError::under_overflow());
144 }
145 let unsigned_val = word.to::<::alloy::primitives::aliases::#unsigned_type>();
147 Ok(Self::from_raw(unsigned_val))
148 }
149 }
150 }
151 }
152 StorableConversionStrategy::FixedBytes(size) => {
153 quote! {
154 impl FromWord for #type_path {
155 #[inline]
156 fn to_word(&self) -> ::alloy::primitives::U256 {
157 let mut bytes = [0u8; 32];
158 bytes[32 - #size..].copy_from_slice(&self[..]);
159 ::alloy::primitives::U256::from_be_bytes(bytes)
160 }
161
162 #[inline]
163 fn from_word(word: ::alloy::primitives::U256) -> crate::error::Result<Self> {
164 let bytes = word.to_be_bytes::<32>();
165 let mut fixed_bytes = [0u8; #size];
166 fixed_bytes.copy_from_slice(&bytes[32 - #size..]);
167 Ok(Self::from(fixed_bytes))
168 }
169 }
170 }
171 }
172 }
173}
174
175fn gen_complete_impl_set(config: &TypeConfig) -> TokenStream {
177 let type_path = &config.type_path;
178 let storable_type_impl = gen_storable_layout_impl(type_path, config.byte_count);
179 let storage_key_impl = gen_storage_key_impl(type_path, &config.storage_key_strategy);
180 let to_word_impl = gen_to_word_impl(type_path, &config.storable_strategy);
181
182 let full_word_storable_impl = if config.byte_count < 32 {
183 quote! {
185 impl crate::storage::types::sealed::OnlyPrimitives for #type_path {}
186 impl crate::storage::types::Packable for #type_path {}
187 }
188 } else {
189 quote! {
191 impl crate::storage::types::sealed::OnlyPrimitives for #type_path {}
192 impl crate::storage::Storable for #type_path {
193 #[inline]
194 fn load<S: crate::storage::StorageOps>(
195 storage: &S,
196 slot: ::alloy::primitives::U256,
197 _ctx: crate::storage::LayoutCtx
198 ) -> crate::error::Result<Self> {
199 storage.load(slot).and_then(<Self as crate::storage::types::FromWord>::from_word)
200 }
201
202 #[inline]
203 fn store<S: crate::storage::StorageOps>(
204 &self,
205 storage: &mut S,
206 slot: ::alloy::primitives::U256,
207 _ctx: crate::storage::LayoutCtx
208 ) -> crate::error::Result<()> {
209 storage.store(slot, <Self as crate::storage::types::FromWord>::to_word(self))
210 }
211 }
212 }
213 };
214
215 quote! {
216 #storable_type_impl
217 #to_word_impl
218 #storage_key_impl
219 #full_word_storable_impl
220 }
221}
222
223pub(crate) fn gen_storable_rust_ints() -> TokenStream {
225 let mut impls = Vec::with_capacity(RUST_INT_SIZES.len() * 2);
226
227 for size in RUST_INT_SIZES {
228 let unsigned_type = quote::format_ident!("u{}", size);
229 let signed_type = quote::format_ident!("i{}", size);
230 let byte_count = size / 8;
231
232 let unsigned_config = TypeConfig {
234 type_path: quote! { #unsigned_type },
235 byte_count,
236 storable_strategy: StorableConversionStrategy::UnsignedRust,
237 storage_key_strategy: StorageKeyStrategy::Simple,
238 };
239 impls.push(gen_complete_impl_set(&unsigned_config));
240
241 let signed_config = TypeConfig {
243 type_path: quote! { #signed_type },
244 byte_count,
245 storable_strategy: StorableConversionStrategy::SignedRust(unsigned_type.clone()),
246 storage_key_strategy: StorageKeyStrategy::Simple,
247 };
248 impls.push(gen_complete_impl_set(&signed_config));
249 }
250
251 quote! {
252 #(#impls)*
253 }
254}
255
256fn gen_alloy_integers() -> Vec<TokenStream> {
258 let mut impls = Vec::with_capacity(ALLOY_INT_SIZES.len() * 2);
259
260 for &size in ALLOY_INT_SIZES {
261 let unsigned_type = quote::format_ident!("U{}", size);
262 let signed_type = quote::format_ident!("I{}", size);
263 let byte_count = size / 8;
264
265 let unsigned_config = TypeConfig {
267 type_path: quote! { ::alloy::primitives::aliases::#unsigned_type },
268 byte_count,
269 storable_strategy: StorableConversionStrategy::UnsignedAlloy(unsigned_type.clone()),
270 storage_key_strategy: StorageKeyStrategy::WithSize(byte_count),
271 };
272 impls.push(gen_complete_impl_set(&unsigned_config));
273
274 let signed_config = TypeConfig {
276 type_path: quote! { ::alloy::primitives::aliases::#signed_type },
277 byte_count,
278 storable_strategy: StorableConversionStrategy::SignedAlloy(unsigned_type.clone()),
279 storage_key_strategy: StorageKeyStrategy::SignedRaw(byte_count),
280 };
281 impls.push(gen_complete_impl_set(&signed_config));
282 }
283
284 impls
285}
286
287fn gen_fixed_bytes(sizes: &[usize]) -> Vec<TokenStream> {
289 let mut impls = Vec::with_capacity(sizes.len());
290
291 for &size in sizes {
292 let config = TypeConfig {
294 type_path: quote! { ::alloy::primitives::FixedBytes<#size> },
295 byte_count: size,
296 storable_strategy: StorableConversionStrategy::FixedBytes(size),
297 storage_key_strategy: StorageKeyStrategy::AsSlice,
298 };
299 impls.push(gen_complete_impl_set(&config));
300 }
301
302 impls
303}
304
305pub(crate) fn gen_storable_alloy_bytes() -> TokenStream {
307 let sizes: Vec<usize> = (1..=32).collect();
308 let impls = gen_fixed_bytes(&sizes);
309
310 quote! {
311 #(#impls)*
312 }
313}
314
315pub(crate) fn gen_storable_alloy_ints() -> TokenStream {
317 let impls = gen_alloy_integers();
318
319 quote! {
320 #(#impls)*
321 }
322}
323
324#[derive(Debug, Clone)]
328struct ArrayConfig {
329 elem_type: TokenStream,
330 array_size: usize,
331 elem_byte_count: usize,
332 elem_is_packable: bool,
333}
334
335fn is_packable(byte_count: usize) -> bool {
337 byte_count < 32
338}
339
340fn gen_array_impl(config: &ArrayConfig) -> TokenStream {
342 let ArrayConfig {
343 elem_type,
344 array_size,
345 elem_byte_count,
346 elem_is_packable,
347 } = config;
348
349 let slot_count_expr = if *elem_is_packable {
351 quote! { crate::storage::packing::calc_packed_slot_count(#array_size, #elem_byte_count) }
352 } else {
353 quote! { #array_size }
355 };
356
357 let load_impl = if *elem_is_packable {
358 gen_packed_array_load(array_size, elem_byte_count)
359 } else {
360 gen_unpacked_array_load(array_size)
361 };
362
363 let store_impl = if *elem_is_packable {
364 gen_packed_array_store(array_size, elem_byte_count)
365 } else {
366 gen_unpacked_array_store()
367 };
368
369 quote! {
370 impl crate::storage::StorableType for [#elem_type; #array_size] {
372 const LAYOUT: crate::storage::Layout = crate::storage::Layout::Slots(#slot_count_expr);
374
375 type Handler = crate::storage::types::array::ArrayHandler<#elem_type, #array_size>;
376
377 fn handle(slot: ::alloy::primitives::U256, ctx: crate::storage::LayoutCtx, address: ::alloy::primitives::Address) -> Self::Handler {
378 debug_assert_eq!(ctx, crate::storage::LayoutCtx::FULL, "Arrays cannot be packed");
379 Self::Handler::new(slot, address)
380 }
381 }
382
383 impl crate::storage::Storable for [#elem_type; #array_size] {
385 #[inline]
386 fn load<S: crate::storage::StorageOps>(storage: &S, slot: ::alloy::primitives::U256, ctx: crate::storage::LayoutCtx) -> crate::error::Result<Self> {
387 debug_assert_eq!(
388 ctx, crate::storage::LayoutCtx::FULL,
389 "Arrays can only be loaded with LayoutCtx::FULL"
390 );
391
392 use crate::storage::packing::{calc_element_slot, calc_element_offset, extract_from_word};
393 let base_slot = slot;
394 #load_impl
395 }
396
397 #[inline]
398 fn store<S: crate::storage::StorageOps>(&self, storage: &mut S, slot: ::alloy::primitives::U256, ctx: crate::storage::LayoutCtx) -> crate::error::Result<()> {
399 debug_assert_eq!(
400 ctx, crate::storage::LayoutCtx::FULL,
401 "Arrays can only be stored with LayoutCtx::FULL"
402 );
403
404 use crate::storage::packing::{calc_element_slot, calc_element_offset, insert_into_word};
405 let base_slot = slot;
406 #store_impl
407 }
408
409 }
411
412 }
413}
414
415fn gen_packed_array_load(array_size: &usize, elem_byte_count: &usize) -> TokenStream {
417 quote! {
418 let mut result = [Default::default(); #array_size];
419 for i in 0..#array_size {
420 let slot_idx = calc_element_slot(i, #elem_byte_count);
421 let offset = calc_element_offset(i, #elem_byte_count);
422 let slot_addr = base_slot + U256::from(slot_idx);
423 let slot_value = storage.load(slot_addr)?;
424 result[i] = extract_from_word(slot_value, offset, #elem_byte_count)?;
425 }
426 Ok(result)
427 }
428}
429
430fn gen_packed_array_store(array_size: &usize, elem_byte_count: &usize) -> TokenStream {
432 quote! {
433 let slot_count = crate::storage::packing::calc_packed_slot_count(
435 #array_size,
436 #elem_byte_count,
437 );
438
439 for slot_idx in 0..slot_count {
441 let slot_addr = base_slot + U256::from(slot_idx);
442 let mut slot_value = U256::ZERO;
443
444 for i in 0..#array_size {
446 let elem_slot = calc_element_slot(i, #elem_byte_count);
447 if elem_slot == slot_idx {
448 let offset = calc_element_offset(i, #elem_byte_count);
449 slot_value = insert_into_word(slot_value, &self[i], offset, #elem_byte_count)?;
450 }
451 }
452
453 storage.store(slot_addr, slot_value)?;
454 }
455 Ok(())
456 }
457}
458
459fn gen_unpacked_array_load(array_size: &usize) -> TokenStream {
461 quote! {
462 let mut result = [Default::default(); #array_size];
463 for i in 0..#array_size {
464 let elem_slot = base_slot + ::alloy::primitives::U256::from(i);
465 result[i] = crate::storage::Storable::load(storage, elem_slot, crate::storage::LayoutCtx::FULL)?;
466 }
467 Ok(result)
468 }
469}
470
471fn gen_unpacked_array_store() -> TokenStream {
473 quote! {
474 for (i, elem) in self.iter().enumerate() {
475 let elem_slot = base_slot + ::alloy::primitives::U256::from(i);
476 crate::storage::Storable::store(elem, storage, elem_slot, crate::storage::LayoutCtx::FULL)?;
477 }
478 Ok(())
479 }
480}
481
482fn gen_arrays_for_type(
484 elem_type: TokenStream,
485 elem_byte_count: usize,
486 sizes: &[usize],
487) -> Vec<TokenStream> {
488 let elem_is_packable = is_packable(elem_byte_count);
489
490 sizes
491 .iter()
492 .map(|&size| {
493 let config = ArrayConfig {
494 elem_type: elem_type.clone(),
495 array_size: size,
496 elem_byte_count,
497 elem_is_packable,
498 };
499 gen_array_impl(&config)
500 })
501 .collect()
502}
503
504pub(crate) fn gen_storable_arrays() -> TokenStream {
506 let mut all_impls = Vec::new();
507 let sizes: Vec<usize> = (1..=32).collect();
508
509 for &bit_size in RUST_INT_SIZES {
511 let type_ident = quote::format_ident!("u{}", bit_size);
512 let byte_count = bit_size / 8;
513 all_impls.extend(gen_arrays_for_type(
514 quote! { #type_ident },
515 byte_count,
516 &sizes,
517 ));
518 }
519
520 for &bit_size in RUST_INT_SIZES {
522 let type_ident = quote::format_ident!("i{}", bit_size);
523 let byte_count = bit_size / 8;
524 all_impls.extend(gen_arrays_for_type(
525 quote! { #type_ident },
526 byte_count,
527 &sizes,
528 ));
529 }
530
531 for &bit_size in ALLOY_INT_SIZES {
533 let type_ident = quote::format_ident!("U{}", bit_size);
534 let byte_count = bit_size / 8;
535 all_impls.extend(gen_arrays_for_type(
536 quote! { ::alloy::primitives::aliases::#type_ident },
537 byte_count,
538 &sizes,
539 ));
540 }
541
542 for &bit_size in ALLOY_INT_SIZES {
544 let type_ident = quote::format_ident!("I{}", bit_size);
545 let byte_count = bit_size / 8;
546 all_impls.extend(gen_arrays_for_type(
547 quote! { ::alloy::primitives::aliases::#type_ident },
548 byte_count,
549 &sizes,
550 ));
551 }
552
553 all_impls.extend(gen_arrays_for_type(
555 quote! { ::alloy::primitives::Address },
556 20,
557 &sizes,
558 ));
559
560 for &byte_size in &[20, 32] {
562 all_impls.extend(gen_arrays_for_type(
563 quote! { ::alloy::primitives::FixedBytes<#byte_size> },
564 byte_size,
565 &sizes,
566 ));
567 }
568
569 quote! {
570 #(#all_impls)*
571 }
572}
573
574pub(crate) fn gen_nested_arrays() -> TokenStream {
576 let mut all_impls = Vec::new();
577
578 for inner in &[2usize, 4, 8, 16] {
581 let inner_slots = inner.div_ceil(32); let max_outer = 32 / inner_slots.max(1);
583
584 for outer in 1..=max_outer.min(32) {
585 all_impls.extend(gen_arrays_for_type(
586 quote! { [u8; #inner] },
587 inner_slots * 32, &[outer],
589 ));
590 }
591 }
592
593 for inner in &[2usize, 4, 8] {
595 let inner_slots = (inner * 2).div_ceil(32);
596 let max_outer = 32 / inner_slots.max(1);
597
598 for outer in 1..=max_outer.min(16) {
599 all_impls.extend(gen_arrays_for_type(
600 quote! { [u16; #inner] },
601 inner_slots * 32,
602 &[outer],
603 ));
604 }
605 }
606
607 quote! {
608 #(#all_impls)*
609 }
610}
611
612pub(crate) fn gen_struct_arrays(struct_type: TokenStream, array_sizes: &[usize]) -> TokenStream {
630 let impls: Vec<_> = array_sizes
631 .iter()
632 .map(|&size| gen_struct_array_impl(&struct_type, size))
633 .collect();
634
635 quote! {
636 #(#impls)*
637 }
638}
639
640fn gen_struct_array_impl(struct_type: &TokenStream, array_size: usize) -> TokenStream {
642 let struct_type_str = struct_type
644 .to_string()
645 .replace("::", "_")
646 .replace(['<', '>', ' ', '[', ']', ';'], "_");
647 let mod_ident = quote::format_ident!("__array_{}_{}", struct_type_str, array_size);
648
649 let load_impl = gen_struct_array_load(struct_type, array_size);
651 let store_impl = gen_struct_array_store(struct_type);
652
653 quote! {
654 mod #mod_ident {
656 use super::*;
657 pub const ELEM_SLOTS: usize = <#struct_type as crate::storage::StorableType>::SLOTS;
658 pub const ARRAY_LEN: usize = #array_size;
659 pub const SLOT_COUNT: usize = ARRAY_LEN * ELEM_SLOTS;
660 }
661
662 impl crate::storage::StorableType for [#struct_type; #array_size] {
664 const LAYOUT: crate::storage::Layout = crate::storage::Layout::Slots(#mod_ident::SLOT_COUNT);
665
666 type Handler = crate::storage::Slot<Self>;
667
668 fn handle(slot: ::alloy::primitives::U256, ctx: crate::storage::LayoutCtx, address: ::alloy::primitives::Address) -> Self::Handler {
669 crate::storage::Slot::new_with_ctx(slot, ctx, address)
670 }
671 }
672
673 impl crate::storage::Storable for [#struct_type; #array_size] {
675 #[inline]
676 fn load<S: crate::storage::StorageOps>(storage: &S, slot: ::alloy::primitives::U256, ctx: crate::storage::LayoutCtx) -> crate::error::Result<Self> {
677 debug_assert_eq!(
678 ctx, crate::storage::LayoutCtx::FULL,
679 "Struct arrays can only be loaded with LayoutCtx::FULL"
680 );
681 let base_slot = slot;
682 #load_impl
683 }
684
685 #[inline]
686 fn store<S: crate::storage::StorageOps>(&self, storage: &mut S, slot: ::alloy::primitives::U256, ctx: crate::storage::LayoutCtx) -> crate::error::Result<()> {
687 debug_assert_eq!(
688 ctx, crate::storage::LayoutCtx::FULL,
689 "Struct arrays can only be stored with LayoutCtx::FULL"
690 );
691 let base_slot = slot;
692 #store_impl
693 }
694
695 }
697
698 }
699}
700
701fn gen_struct_array_load(struct_type: &TokenStream, array_size: usize) -> TokenStream {
705 quote! {
706 let mut result = [Default::default(); #array_size];
707 for i in 0..#array_size {
708 let elem_slot = base_slot.checked_add(
710 ::alloy::primitives::U256::from(i).checked_mul(
711 ::alloy::primitives::U256::from(<#struct_type as crate::storage::StorableType>::SLOTS)
712 ).ok_or(crate::error::TempoError::SlotOverflow)?
713 ).ok_or(crate::error::TempoError::SlotOverflow)?;
714
715 result[i] = <#struct_type as crate::storage::Storable>::load(storage, elem_slot, crate::storage::LayoutCtx::FULL)?;
716 }
717 Ok(result)
718 }
719}
720
721fn gen_struct_array_store(struct_type: &TokenStream) -> TokenStream {
723 quote! {
724 for (i, elem) in self.iter().enumerate() {
725 let elem_slot = base_slot.checked_add(
727 ::alloy::primitives::U256::from(i).checked_mul(
728 ::alloy::primitives::U256::from(<#struct_type as crate::storage::StorableType>::SLOTS)
729 ).ok_or(crate::error::TempoError::SlotOverflow)?
730 ).ok_or(crate::error::TempoError::SlotOverflow)?;
731
732 <#struct_type as crate::storage::Storable>::store(elem, storage, elem_slot, crate::storage::LayoutCtx::FULL)?;
733 }
734 Ok(())
735 }
736}