use alloc::{string::String, vec::Vec};
use core::marker::PhantomData;
use alloy_primitives::{Address, FixedBytes, Signed, Uint, B256, U160, U256};
use stylus_core::HostAccess;
use super::{Erase, SimpleStorageType, StorageGuard, StorageGuardMut, StorageType};
use crate::{crypto, host::VM};
pub struct StorageMap<K: StorageKey, V: StorageType> {
slot: U256,
marker: PhantomData<(K, V)>,
__stylus_host: VM,
}
impl<K, V> StorageType for StorageMap<K, V>
where
K: StorageKey,
V: StorageType,
{
type Wraps<'a>
= StorageGuard<'a, StorageMap<K, V>>
where
Self: 'a;
type WrapsMut<'a>
= StorageGuardMut<'a, StorageMap<K, V>>
where
Self: 'a;
unsafe fn new(slot: U256, offset: u8, host: VM) -> Self {
debug_assert!(offset == 0);
Self {
slot,
marker: PhantomData,
__stylus_host: host,
}
}
fn load<'s>(self) -> Self::Wraps<'s> {
StorageGuard::new(self)
}
fn load_mut<'s>(self) -> Self::WrapsMut<'s> {
StorageGuardMut::new(self)
}
}
impl<K, V> HostAccess for StorageMap<K, V>
where
K: StorageKey,
V: StorageType,
{
type Host = VM;
#[inline]
fn vm(&self) -> &Self::Host {
&self.__stylus_host
}
}
#[cfg(feature = "stylus-test")]
impl<K, V, T> From<&T> for StorageMap<K, V>
where
K: StorageKey,
V: StorageType,
T: stylus_core::Host + Clone + 'static,
{
fn from(host: &T) -> Self {
unsafe {
Self::new(
U256::ZERO,
0,
crate::host::VM {
host: alloc::boxed::Box::new(host.clone()),
},
)
}
}
}
impl<K, V> StorageMap<K, V>
where
K: StorageKey,
V: StorageType,
{
const CHILD_OFFSET: u8 = 32 - V::SLOT_BYTES as u8;
pub fn getter(&self, key: K) -> StorageGuard<'_, V> {
let slot = key.to_slot(self.slot.into());
unsafe { StorageGuard::new(V::new(slot, Self::CHILD_OFFSET, self.__stylus_host.clone())) }
}
pub fn setter(&mut self, key: K) -> StorageGuardMut<'_, V> {
let slot = key.to_slot(self.slot.into());
unsafe {
StorageGuardMut::new(V::new(slot, Self::CHILD_OFFSET, self.__stylus_host.clone()))
}
}
pub fn get(&self, key: K) -> V::Wraps<'_> {
let store = self.getter(key);
unsafe { store.into_raw().load() }
}
}
impl<'a, K, V> StorageMap<K, V>
where
K: StorageKey,
V: SimpleStorageType<'a>,
{
pub fn insert(&mut self, key: K, value: V::Wraps<'a>) {
let mut store = self.setter(key);
store.set_by_wrapped(value);
}
pub fn replace(&mut self, key: K, value: V::Wraps<'a>) -> V::Wraps<'a> {
let slot = key.to_slot(self.slot.into());
unsafe {
let store = V::new(slot, Self::CHILD_OFFSET, self.__stylus_host.clone());
let mut alias = V::new(slot, Self::CHILD_OFFSET, self.__stylus_host.clone());
let prior = store.load();
alias.set_by_wrapped(value);
prior
}
}
pub fn take(&mut self, key: K) -> V::Wraps<'a> {
let slot = key.to_slot(self.slot.into());
unsafe {
let store = V::new(slot, Self::CHILD_OFFSET, self.__stylus_host.clone());
let mut alias = V::new(slot, Self::CHILD_OFFSET, self.__stylus_host.clone());
let value = store.load();
alias.erase();
value
}
}
}
impl<K, V> StorageMap<K, V>
where
K: StorageKey,
V: Erase,
{
pub fn delete(&mut self, key: K) {
let mut store = self.setter(key);
store.erase();
}
}
pub trait StorageKey {
fn to_slot(&self, root: B256) -> U256;
}
impl<const B: usize, const L: usize> StorageKey for Uint<B, L> {
fn to_slot(&self, root: B256) -> U256 {
let data = B256::from(U256::from(*self));
let data = data.concat_const::<32, 64>(root);
crypto::keccak(data).into()
}
}
impl<const B: usize, const L: usize> StorageKey for Signed<B, L> {
fn to_slot(&self, root: B256) -> U256 {
let data = B256::from(U256::from(self.into_raw()));
let data = data.concat_const::<32, 64>(root);
crypto::keccak(data).into()
}
}
impl<const N: usize> StorageKey for FixedBytes<N> {
fn to_slot(&self, root: B256) -> U256 {
let mut pad = [0; 32];
pad[..N].copy_from_slice(&self.0);
let data = B256::from(pad);
let data = data.concat_const::<32, 64>(root);
crypto::keccak(data).into()
}
}
impl StorageKey for &[u8] {
fn to_slot(&self, root: B256) -> U256 {
let mut vec = self.to_vec();
vec.extend(root);
crypto::keccak(vec).into()
}
}
impl StorageKey for Vec<u8> {
fn to_slot(&self, root: B256) -> U256 {
let bytes: &[u8] = self.as_ref();
bytes.to_slot(root)
}
}
impl StorageKey for &str {
fn to_slot(&self, root: B256) -> U256 {
self.as_bytes().to_slot(root)
}
}
impl StorageKey for String {
fn to_slot(&self, root: B256) -> U256 {
self.as_bytes().to_slot(root)
}
}
impl StorageKey for Address {
fn to_slot(&self, root: B256) -> U256 {
let int: U160 = self.0.into();
int.to_slot(root)
}
}
impl StorageKey for bool {
fn to_slot(&self, root: B256) -> U256 {
let value = self.then_some(1_u8).unwrap_or_default();
value.to_slot(root)
}
}
macro_rules! impl_key {
($($uint:ident $int:ident)+) => {
$(
impl StorageKey for $uint {
fn to_slot(&self, root: B256) -> U256 {
let data = B256::from(U256::from(*self));
let data = data.concat_const::<32, 64>(root.into());
crypto::keccak(data).into()
}
}
impl StorageKey for $int {
fn to_slot(&self, root: B256) -> U256 {
let data = B256::from(U256::from(*self as $uint)); let data = data.concat_const::<32, 64>(root.into());
crypto::keccak(data).into()
}
}
)+
};
}
impl_key!(u8 i8 u16 i16 u32 i32 u64 i64 u128 i128 usize isize);