append_only/
lib.rs

1#![deny(missing_docs)]
2//! append-only
3//!
4//! append-only contains versions of data structures that ... only allow appends.
5//! Data structures where values cannot be modified once added, combined with
6//! Boxing, allow immutable references to one key's value to be handed out while
7//! preserving the ability to append other key/value pairs to the structure.
8
9use std::borrow::Borrow;
10use std::collections::{hash_map, HashMap};
11use std::hash::Hash;
12use std::mem;
13use std::ops::Deref;
14use std::sync::RwLock;
15
16/// The sync module contains append-only data structures that can be used from
17/// multiple threads.
18pub mod sync {
19    use super::*;
20
21    /// Like `std::collections::HashMap`, but append-only.
22    #[derive(Debug)]
23    pub struct AppendOnlyHashMap<K, V>(RwLock<HashMap<K, Box<V>>>);
24
25    impl<K, V> AppendOnlyHashMap<K, V> {
26        /// Creates an empty `AppendOnlyHashMap`.
27        pub fn new() -> Self {
28            AppendOnlyHashMap(RwLock::new(HashMap::new()))
29        }
30
31        /// Creates an empty `AppendOnlyHashMap` with the specified capacity.
32        pub fn with_capacity(capacity: usize) -> Self {
33            AppendOnlyHashMap(RwLock::new(HashMap::with_capacity(capacity)))
34        }
35    }
36
37    impl<K, V> AppendOnlyHashMap<K, V>
38    where
39        K: Eq + Hash,
40    {
41        /// Returns a reference to the value corresponding to the key.
42        pub fn get<'map, Q: ?Sized>(&'map self, key: &Q) -> Option<&'map V>
43        where
44            Q: Borrow<K>
45        {
46            let locked = self.0.read().unwrap();
47            let value_ptr = locked.get(key.borrow())?.deref() as *const V;
48            mem::drop(locked);
49            // Safe because this value can never be removed or mutated as long
50            // as this map survives.
51            Some(unsafe {
52                &*value_ptr
53            })
54        }
55
56        /// Returns a reference to the value corresponding to the key, invoking
57        /// the provided callback if it is not already present.
58        pub fn get_or_insert_with<'map, F>(&'map self, key: K, f: F) -> &'map V
59        where
60            F: FnOnce(&K) -> V
61        {
62            self.get_or_insert_with_fallible::<(), _>(key, |k| Ok(f(k))).unwrap()
63        }
64
65        /// Returns a reference to the value corresponding to the key, invoking
66        /// the provided callback if it is not already present.
67        pub fn get_or_insert_with_fallible<'map, E, F>(&'map self, key: K, f: F) -> Result<&'map V, E>
68        where
69            F: FnOnce(&K) -> Result<V, E>
70        {
71            let mut locked = self.0.write().unwrap();
72            let value_ptr = match locked.entry(key) {
73                hash_map::Entry::Occupied(o) => o.get().deref() as *const V,
74                hash_map::Entry::Vacant(v) => {
75                    let value = Box::new(f(v.key())?);
76                    (*v.insert(value)).deref() as *const V
77                }
78            };
79            mem::drop(locked);
80            // Safe because this value can never be removed or mutated as long
81            // as this map survives.
82            Ok(unsafe {
83                &*value_ptr
84            })
85        }
86    }
87}