unc_sdk/collections/
lookup_map.rs1use std::marker::PhantomData;
5
6use borsh::{to_vec, BorshDeserialize, BorshSerialize};
7
8use crate::collections::append_slice;
9use crate::{env, IntoStorageKey};
10use unc_sdk_macros::unc;
11
12const ERR_KEY_SERIALIZATION: &str = "Cannot serialize key with Borsh";
13const ERR_VALUE_DESERIALIZATION: &str = "Cannot deserialize value with Borsh";
14const ERR_VALUE_SERIALIZATION: &str = "Cannot serialize value with Borsh";
15
16#[unc(inside_uncsdk)]
18pub struct LookupMap<K, V> {
19 key_prefix: Vec<u8>,
20 #[borsh(skip)]
21 el: PhantomData<(K, V)>,
22}
23
24impl<K, V> LookupMap<K, V> {
25 pub fn new<S>(key_prefix: S) -> Self
34 where
35 S: IntoStorageKey,
36 {
37 Self { key_prefix: key_prefix.into_storage_key(), el: PhantomData }
38 }
39
40 fn raw_key_to_storage_key(&self, raw_key: &[u8]) -> Vec<u8> {
41 append_slice(&self.key_prefix, raw_key)
42 }
43
44 fn contains_key_raw(&self, key_raw: &[u8]) -> bool {
46 let storage_key = self.raw_key_to_storage_key(key_raw);
47 env::storage_has_key(&storage_key)
48 }
49
50 fn get_raw(&self, key_raw: &[u8]) -> Option<Vec<u8>> {
52 let storage_key = self.raw_key_to_storage_key(key_raw);
53 env::storage_read(&storage_key)
54 }
55
56 pub fn insert_raw(&mut self, key_raw: &[u8], value_raw: &[u8]) -> Option<Vec<u8>> {
61 let storage_key = self.raw_key_to_storage_key(key_raw);
62 if env::storage_write(&storage_key, value_raw) {
63 Some(env::storage_get_evicted().unwrap())
64 } else {
65 None
66 }
67 }
68
69 pub fn remove_raw(&mut self, key_raw: &[u8]) -> Option<Vec<u8>> {
72 let storage_key = self.raw_key_to_storage_key(key_raw);
73 if env::storage_remove(&storage_key) {
74 Some(env::storage_get_evicted().unwrap())
75 } else {
76 None
77 }
78 }
79}
80
81impl<K, V> LookupMap<K, V>
82where
83 K: BorshSerialize,
84 V: BorshSerialize + BorshDeserialize,
85{
86 fn serialize_key(key: &K) -> Vec<u8> {
87 match to_vec(key) {
88 Ok(x) => x,
89 Err(_) => env::panic_str(ERR_KEY_SERIALIZATION),
90 }
91 }
92
93 fn deserialize_value(raw_value: &[u8]) -> V {
94 match V::try_from_slice(raw_value) {
95 Ok(x) => x,
96 Err(_) => env::panic_str(ERR_VALUE_DESERIALIZATION),
97 }
98 }
99
100 fn serialize_value(value: &V) -> Vec<u8> {
101 match to_vec(value) {
102 Ok(x) => x,
103 Err(_) => env::panic_str(ERR_VALUE_SERIALIZATION),
104 }
105 }
106
107 pub fn contains_key(&self, key: &K) -> bool {
121 self.contains_key_raw(&Self::serialize_key(key))
122 }
123
124 pub fn get(&self, key: &K) -> Option<V> {
138 self.get_raw(&Self::serialize_key(key)).map(|value_raw| Self::deserialize_value(&value_raw))
139 }
140
141 pub fn remove(&mut self, key: &K) -> Option<V> {
156 self.remove_raw(&Self::serialize_key(key))
157 .map(|value_raw| Self::deserialize_value(&value_raw))
158 }
159
160 pub fn insert(&mut self, key: &K, value: &V) -> Option<V> {
175 self.insert_raw(&Self::serialize_key(key), &Self::serialize_value(value))
176 .map(|value_raw| Self::deserialize_value(&value_raw))
177 }
178
179 pub fn extend<IT: IntoIterator<Item = (K, V)>>(&mut self, iter: IT) {
197 for (el_key, el_value) in iter {
198 self.insert(&el_key, &el_value);
199 }
200 }
201}
202
203impl<K, V> std::fmt::Debug for LookupMap<K, V>
204where
205 K: std::fmt::Debug + BorshSerialize,
206 V: std::fmt::Debug + BorshSerialize + BorshDeserialize,
207{
208 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209 f.debug_struct("LookupMap").field("key_prefix", &self.key_prefix).finish()
210 }
211}
212
213#[cfg(not(target_arch = "wasm32"))]
214#[cfg(test)]
215mod tests {
216 use crate::collections::LookupMap;
217 use rand::seq::SliceRandom;
218 use rand::{Rng, SeedableRng};
219 use std::collections::HashMap;
220
221 #[test]
222 pub fn test_insert_one() {
223 let mut map = LookupMap::new(b"m");
224 assert_eq!(None, map.insert(&1, &2));
225 assert_eq!(2, map.insert(&1, &3).unwrap());
226 }
227
228 #[test]
229 pub fn test_insert() {
230 let mut map = LookupMap::new(b"m");
231 let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(0);
232 for _ in 0..500 {
233 let key = rng.gen::<u64>();
234 let value = rng.gen::<u64>();
235 map.insert(&key, &value);
236 }
237 }
238
239 #[test]
240 pub fn test_insert_has_key() {
241 let mut map = LookupMap::new(b"m");
242 let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(0);
243 let mut key_to_value = HashMap::new();
244 for _ in 0..100 {
245 let key = rng.gen::<u64>();
246 let value = rng.gen::<u64>();
247 map.insert(&key, &value);
248 key_to_value.insert(key, value);
249 }
250 for _ in 0..100 {
252 let key = rng.gen::<u64>();
253 assert_eq!(map.contains_key(&key), key_to_value.contains_key(&key));
254 }
255 for (key, _) in key_to_value.iter() {
257 assert!(map.contains_key(key));
258 }
259 }
260
261 #[test]
262 pub fn test_insert_remove() {
263 let mut map = LookupMap::new(b"m");
264 let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(1);
265 let mut keys = vec![];
266 let mut key_to_value = HashMap::new();
267 for _ in 0..100 {
268 let key = rng.gen::<u64>();
269 let value = rng.gen::<u64>();
270 keys.push(key);
271 key_to_value.insert(key, value);
272 map.insert(&key, &value);
273 }
274 keys.shuffle(&mut rng);
275 for key in keys {
276 let actual = map.remove(&key).unwrap();
277 assert_eq!(actual, key_to_value[&key]);
278 }
279 }
280
281 #[test]
282 pub fn test_remove_last_reinsert() {
283 let mut map = LookupMap::new(b"m");
284 let key1 = 1u64;
285 let value1 = 2u64;
286 map.insert(&key1, &value1);
287 let key2 = 3u64;
288 let value2 = 4u64;
289 map.insert(&key2, &value2);
290
291 let actual_value2 = map.remove(&key2).unwrap();
292 assert_eq!(actual_value2, value2);
293
294 let actual_insert_value2 = map.insert(&key2, &value2);
295 assert_eq!(actual_insert_value2, None);
296 }
297
298 #[test]
299 pub fn test_insert_override_remove() {
300 let mut map = LookupMap::new(b"m");
301 let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(2);
302 let mut keys = vec![];
303 let mut key_to_value = HashMap::new();
304 for _ in 0..100 {
305 let key = rng.gen::<u64>();
306 let value = rng.gen::<u64>();
307 keys.push(key);
308 key_to_value.insert(key, value);
309 map.insert(&key, &value);
310 }
311 keys.shuffle(&mut rng);
312 for key in &keys {
313 let value = rng.gen::<u64>();
314 let actual = map.insert(key, &value).unwrap();
315 assert_eq!(actual, key_to_value[key]);
316 key_to_value.insert(*key, value);
317 }
318 keys.shuffle(&mut rng);
319 for key in keys {
320 let actual = map.remove(&key).unwrap();
321 assert_eq!(actual, key_to_value[&key]);
322 }
323 }
324
325 #[test]
326 pub fn test_get_non_existent() {
327 let mut map = LookupMap::new(b"m");
328 let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(3);
329 let mut key_to_value = HashMap::new();
330 for _ in 0..500 {
331 let key = rng.gen::<u64>() % 20_000;
332 let value = rng.gen::<u64>();
333 key_to_value.insert(key, value);
334 map.insert(&key, &value);
335 }
336 for _ in 0..500 {
337 let key = rng.gen::<u64>() % 20_000;
338 assert_eq!(map.get(&key), key_to_value.get(&key).cloned());
339 }
340 }
341
342 #[test]
343 pub fn test_extend() {
344 let mut map = LookupMap::new(b"m");
345 let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(4);
346 let mut key_to_value = HashMap::new();
347 for _ in 0..100 {
348 let key = rng.gen::<u64>();
349 let value = rng.gen::<u64>();
350 key_to_value.insert(key, value);
351 map.insert(&key, &value);
352 }
353 for _ in 0..10 {
354 let mut tmp = vec![];
355 for _ in 0..=(rng.gen::<u64>() % 20 + 1) {
356 let key = rng.gen::<u64>();
357 let value = rng.gen::<u64>();
358 tmp.push((key, value));
359 }
360 key_to_value.extend(tmp.iter().cloned());
361 map.extend(tmp.iter().cloned());
362 }
363
364 for (key, value) in key_to_value {
365 assert_eq!(map.get(&key).unwrap(), value);
366 }
367 }
368
369 #[test]
370 fn test_debug() {
371 let map: LookupMap<u64, u64> = LookupMap::new(b"m");
372
373 assert_eq!(
374 format!("{:?}", map),
375 format!("LookupMap {{ key_prefix: {:?} }}", map.key_prefix)
376 );
377 }
378}