use crate::{
abi::{CasperABI, Declaration, Definition, StructField},
casper::{self, read_into_vec},
serializers::borsh::{BorshDeserialize, BorshSerialize},
};
use casper_executor_wasm_common::keyspace::Keyspace;
use const_fnv1a_hash::fnv1a_hash_str_64;
use crate::prelude::marker::PhantomData;
#[derive(BorshSerialize, BorshDeserialize, Debug, Clone)]
#[borsh(crate = "crate::serializers::borsh")]
pub struct Map<K, V> {
pub(crate) name: String,
pub(crate) _marker: PhantomData<(K, V)>,
}
#[allow(dead_code)]
pub(crate) const fn compute_prefix(input: &str) -> [u8; 8] {
let hash = fnv1a_hash_str_64(input);
hash.to_le_bytes()
}
impl<K, V> Map<K, V>
where
K: BorshSerialize,
V: BorshSerialize + BorshDeserialize,
{
pub fn new<S: Into<String>>(name: S) -> Self {
Self {
name: name.into(),
_marker: PhantomData,
}
}
pub fn insert(&mut self, key: &K, value: &V) {
let mut context_key = Vec::new();
context_key.extend(self.name.as_bytes());
key.serialize(&mut context_key).unwrap();
let prefix = Keyspace::Context(&context_key);
casper::write(prefix, &borsh::to_vec(value).unwrap()).unwrap();
}
pub fn remove(&mut self, key: &K) {
let prefix_bytes = self.compute_prefix_for_key(key);
let prefix = Keyspace::Context(&prefix_bytes);
casper::remove(prefix).unwrap();
}
pub fn get(&self, key: &K) -> Option<V> {
let mut key_bytes = self.name.as_bytes().to_owned();
key.serialize(&mut key_bytes).unwrap();
let prefix = Keyspace::Context(&key_bytes);
read_into_vec(prefix)
.unwrap()
.map(|vec| borsh::from_slice(&vec).unwrap())
}
fn compute_prefix_for_key(&self, key: &K) -> Vec<u8> {
let mut context_key = Vec::new();
context_key.extend(self.name.as_bytes());
key.serialize(&mut context_key).unwrap();
context_key
}
}
impl<K: CasperABI, V: CasperABI> CasperABI for Map<K, V> {
fn populate_definitions(definitions: &mut crate::abi::Definitions) {
definitions.populate_one::<K>();
definitions.populate_one::<V>();
}
fn declaration() -> Declaration {
format!("Map<{}, {}>", K::declaration(), V::declaration())
}
#[inline]
fn definition() -> Definition {
Definition::Struct {
items: vec![StructField {
name: "prefix".into(),
decl: u64::declaration(),
}],
}
}
}
#[cfg(test)]
pub(crate) mod tests {
use super::*;
#[test]
fn test_compute_prefix() {
let prefix = compute_prefix("hello");
assert_eq!(prefix.as_slice(), &[11, 189, 170, 128, 70, 216, 48, 164]);
let back = u64::from_le_bytes(prefix);
assert_eq!(fnv1a_hash_str_64("hello"), back);
}
#[ignore]
#[test]
fn test_map() {
let mut map = Map::<u64, u64>::new("test");
map.insert(&1, &2);
assert_eq!(map.get(&1), Some(2));
assert_eq!(map.get(&2), None);
map.insert(&2, &3);
assert_eq!(map.get(&1), Some(2));
assert_eq!(map.get(&2), Some(3));
let mut map = Map::<u64, u64>::new("test2");
assert_eq!(map.get(&1), None);
map.insert(&1, &22);
assert_eq!(map.get(&1), Some(22));
assert_eq!(map.get(&2), None);
map.insert(&2, &33);
assert_eq!(map.get(&1), Some(22));
assert_eq!(map.get(&2), Some(33));
}
}