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, 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::#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::#unsigned_type::MAX) {
143 return Err(crate::error::TempoPrecompileError::under_overflow());
144 }
145 let unsigned_val = word.to::<::alloy::primitives::#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::#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::#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 && 32 % byte_count == 0
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 = if *elem_is_packable {
351 (*array_size * elem_byte_count).div_ceil(32)
353 } else {
354 *array_size
356 };
357
358 let load_impl = if *elem_is_packable {
359 gen_packed_array_load(array_size, elem_byte_count)
360 } else {
361 gen_unpacked_array_load(array_size)
362 };
363
364 let store_impl = if *elem_is_packable {
365 gen_packed_array_store(array_size, elem_byte_count)
366 } else {
367 gen_unpacked_array_store()
368 };
369
370 quote! {
371 impl crate::storage::StorableType for [#elem_type; #array_size] {
373 const LAYOUT: crate::storage::Layout = crate::storage::Layout::Slots(#slot_count);
375
376 type Handler = crate::storage::types::array::ArrayHandler<#elem_type, #array_size>;
377
378 fn handle(slot: ::alloy::primitives::U256, ctx: crate::storage::LayoutCtx, address: ::alloy::primitives::Address) -> Self::Handler {
379 debug_assert_eq!(ctx, crate::storage::LayoutCtx::FULL, "Arrays cannot be packed");
380 Self::Handler::new(slot, address)
381 }
382 }
383
384 impl crate::storage::Storable for [#elem_type; #array_size] {
386 #[inline]
387 fn load<S: crate::storage::StorageOps>(storage: &S, slot: ::alloy::primitives::U256, ctx: crate::storage::LayoutCtx) -> crate::error::Result<Self> {
388 debug_assert_eq!(
389 ctx, crate::storage::LayoutCtx::FULL,
390 "Arrays can only be loaded with LayoutCtx::FULL"
391 );
392
393 use crate::storage::packing::{calc_element_slot, calc_element_offset, extract_from_word};
394 let base_slot = slot;
395 #load_impl
396 }
397
398 #[inline]
399 fn store<S: crate::storage::StorageOps>(&self, storage: &mut S, slot: ::alloy::primitives::U256, ctx: crate::storage::LayoutCtx) -> crate::error::Result<()> {
400 debug_assert_eq!(
401 ctx, crate::storage::LayoutCtx::FULL,
402 "Arrays can only be stored with LayoutCtx::FULL"
403 );
404
405 use crate::storage::packing::{calc_element_slot, calc_element_offset, insert_into_word};
406 let base_slot = slot;
407 #store_impl
408 }
409
410 }
412
413 }
414}
415
416fn gen_packed_array_load(array_size: &usize, elem_byte_count: &usize) -> TokenStream {
418 quote! {
419 let mut result = [Default::default(); #array_size];
420 for i in 0..#array_size {
421 let slot_idx = calc_element_slot(i, #elem_byte_count);
422 let offset = calc_element_offset(i, #elem_byte_count);
423 let slot_addr = base_slot + U256::from(slot_idx);
424 let slot_value = storage.load(slot_addr)?;
425 result[i] = extract_from_word(slot_value, offset, #elem_byte_count)?;
426 }
427 Ok(result)
428 }
429}
430
431fn gen_packed_array_store(array_size: &usize, elem_byte_count: &usize) -> TokenStream {
433 quote! {
434 let slot_count = (#array_size * #elem_byte_count).div_ceil(32);
436
437 for slot_idx in 0..slot_count {
439 let slot_addr = base_slot + U256::from(slot_idx);
440 let mut slot_value = U256::ZERO;
441
442 for i in 0..#array_size {
444 let elem_slot = calc_element_slot(i, #elem_byte_count);
445 if elem_slot == slot_idx {
446 let offset = calc_element_offset(i, #elem_byte_count);
447 slot_value = insert_into_word(slot_value, &self[i], offset, #elem_byte_count)?;
448 }
449 }
450
451 storage.store(slot_addr, slot_value)?;
452 }
453 Ok(())
454 }
455}
456
457fn gen_unpacked_array_load(array_size: &usize) -> TokenStream {
459 quote! {
460 let mut result = [Default::default(); #array_size];
461 for i in 0..#array_size {
462 let elem_slot = base_slot + ::alloy::primitives::U256::from(i);
463 result[i] = crate::storage::Storable::load(storage, elem_slot, crate::storage::LayoutCtx::FULL)?;
464 }
465 Ok(result)
466 }
467}
468
469fn gen_unpacked_array_store() -> TokenStream {
471 quote! {
472 for (i, elem) in self.iter().enumerate() {
473 let elem_slot = base_slot + ::alloy::primitives::U256::from(i);
474 crate::storage::Storable::store(elem, storage, elem_slot, crate::storage::LayoutCtx::FULL)?;
475 }
476 Ok(())
477 }
478}
479
480fn gen_arrays_for_type(
482 elem_type: TokenStream,
483 elem_byte_count: usize,
484 sizes: &[usize],
485) -> Vec<TokenStream> {
486 let elem_is_packable = is_packable(elem_byte_count);
487
488 sizes
489 .iter()
490 .map(|&size| {
491 let config = ArrayConfig {
492 elem_type: elem_type.clone(),
493 array_size: size,
494 elem_byte_count,
495 elem_is_packable,
496 };
497 gen_array_impl(&config)
498 })
499 .collect()
500}
501
502pub(crate) fn gen_storable_arrays() -> TokenStream {
504 let mut all_impls = Vec::new();
505 let sizes: Vec<usize> = (1..=32).collect();
506
507 for &bit_size in RUST_INT_SIZES {
509 let type_ident = quote::format_ident!("u{}", bit_size);
510 let byte_count = bit_size / 8;
511 all_impls.extend(gen_arrays_for_type(
512 quote! { #type_ident },
513 byte_count,
514 &sizes,
515 ));
516 }
517
518 for &bit_size in RUST_INT_SIZES {
520 let type_ident = quote::format_ident!("i{}", bit_size);
521 let byte_count = bit_size / 8;
522 all_impls.extend(gen_arrays_for_type(
523 quote! { #type_ident },
524 byte_count,
525 &sizes,
526 ));
527 }
528
529 for &bit_size in ALLOY_INT_SIZES {
531 let type_ident = quote::format_ident!("U{}", bit_size);
532 let byte_count = bit_size / 8;
533 all_impls.extend(gen_arrays_for_type(
534 quote! { ::alloy::primitives::#type_ident },
535 byte_count,
536 &sizes,
537 ));
538 }
539
540 for &bit_size in ALLOY_INT_SIZES {
542 let type_ident = quote::format_ident!("I{}", bit_size);
543 let byte_count = bit_size / 8;
544 all_impls.extend(gen_arrays_for_type(
545 quote! { ::alloy::primitives::#type_ident },
546 byte_count,
547 &sizes,
548 ));
549 }
550
551 all_impls.extend(gen_arrays_for_type(
553 quote! { ::alloy::primitives::Address },
554 20,
555 &sizes,
556 ));
557
558 for &byte_size in &[20, 32] {
560 all_impls.extend(gen_arrays_for_type(
561 quote! { ::alloy::primitives::FixedBytes<#byte_size> },
562 byte_size,
563 &sizes,
564 ));
565 }
566
567 quote! {
568 #(#all_impls)*
569 }
570}
571
572pub(crate) fn gen_nested_arrays() -> TokenStream {
574 let mut all_impls = Vec::new();
575
576 for inner in &[2usize, 4, 8, 16] {
579 let inner_slots = inner.div_ceil(32); let max_outer = 32 / inner_slots.max(1);
581
582 for outer in 1..=max_outer.min(32) {
583 all_impls.extend(gen_arrays_for_type(
584 quote! { [u8; #inner] },
585 inner_slots * 32, &[outer],
587 ));
588 }
589 }
590
591 for inner in &[2usize, 4, 8] {
593 let inner_slots = (inner * 2).div_ceil(32);
594 let max_outer = 32 / inner_slots.max(1);
595
596 for outer in 1..=max_outer.min(16) {
597 all_impls.extend(gen_arrays_for_type(
598 quote! { [u16; #inner] },
599 inner_slots * 32,
600 &[outer],
601 ));
602 }
603 }
604
605 quote! {
606 #(#all_impls)*
607 }
608}
609
610pub(crate) fn gen_struct_arrays(struct_type: TokenStream, array_sizes: &[usize]) -> TokenStream {
628 let impls: Vec<_> = array_sizes
629 .iter()
630 .map(|&size| gen_struct_array_impl(&struct_type, size))
631 .collect();
632
633 quote! {
634 #(#impls)*
635 }
636}
637
638fn gen_struct_array_impl(struct_type: &TokenStream, array_size: usize) -> TokenStream {
640 let struct_type_str = struct_type
642 .to_string()
643 .replace("::", "_")
644 .replace(['<', '>', ' ', '[', ']', ';'], "_");
645 let mod_ident = quote::format_ident!("__array_{}_{}", struct_type_str, array_size);
646
647 let load_impl = gen_struct_array_load(struct_type, array_size);
649 let store_impl = gen_struct_array_store(struct_type);
650
651 quote! {
652 mod #mod_ident {
654 use super::*;
655 pub const ELEM_SLOTS: usize = <#struct_type as crate::storage::StorableType>::SLOTS;
656 pub const ARRAY_LEN: usize = #array_size;
657 pub const SLOT_COUNT: usize = ARRAY_LEN * ELEM_SLOTS;
658 }
659
660 impl crate::storage::StorableType for [#struct_type; #array_size] {
662 const LAYOUT: crate::storage::Layout = crate::storage::Layout::Slots(#mod_ident::SLOT_COUNT);
663
664 type Handler = crate::storage::Slot<Self>;
665
666 fn handle(slot: ::alloy::primitives::U256, ctx: crate::storage::LayoutCtx, address: ::alloy::primitives::Address) -> Self::Handler {
667 crate::storage::Slot::new_with_ctx(slot, ctx, address)
668 }
669 }
670
671 impl crate::storage::Storable for [#struct_type; #array_size] {
673 #[inline]
674 fn load<S: crate::storage::StorageOps>(storage: &S, slot: ::alloy::primitives::U256, ctx: crate::storage::LayoutCtx) -> crate::error::Result<Self> {
675 debug_assert_eq!(
676 ctx, crate::storage::LayoutCtx::FULL,
677 "Struct arrays can only be loaded with LayoutCtx::FULL"
678 );
679 let base_slot = slot;
680 #load_impl
681 }
682
683 #[inline]
684 fn store<S: crate::storage::StorageOps>(&self, storage: &mut S, slot: ::alloy::primitives::U256, ctx: crate::storage::LayoutCtx) -> crate::error::Result<()> {
685 debug_assert_eq!(
686 ctx, crate::storage::LayoutCtx::FULL,
687 "Struct arrays can only be stored with LayoutCtx::FULL"
688 );
689 let base_slot = slot;
690 #store_impl
691 }
692
693 }
695
696 }
697}
698
699fn gen_struct_array_load(struct_type: &TokenStream, array_size: usize) -> TokenStream {
703 quote! {
704 let mut result = [Default::default(); #array_size];
705 for i in 0..#array_size {
706 let elem_slot = base_slot.checked_add(
708 ::alloy::primitives::U256::from(i).checked_mul(
709 ::alloy::primitives::U256::from(<#struct_type as crate::storage::StorableType>::SLOTS)
710 ).ok_or(crate::error::TempoError::SlotOverflow)?
711 ).ok_or(crate::error::TempoError::SlotOverflow)?;
712
713 result[i] = <#struct_type as crate::storage::Storable>::load(storage, elem_slot, crate::storage::LayoutCtx::FULL)?;
714 }
715 Ok(result)
716 }
717}
718
719fn gen_struct_array_store(struct_type: &TokenStream) -> TokenStream {
721 quote! {
722 for (i, elem) in self.iter().enumerate() {
723 let elem_slot = base_slot.checked_add(
725 ::alloy::primitives::U256::from(i).checked_mul(
726 ::alloy::primitives::U256::from(<#struct_type as crate::storage::StorableType>::SLOTS)
727 ).ok_or(crate::error::TempoError::SlotOverflow)?
728 ).ok_or(crate::error::TempoError::SlotOverflow)?;
729
730 <#struct_type as crate::storage::Storable>::store(elem, storage, elem_slot, crate::storage::LayoutCtx::FULL)?;
731 }
732 Ok(())
733 }
734}