lending_library/
lib.rs

1/* Notice
2lib.rs: lending-library
3
4Copyright 2018 Thomas Bytheway <thomas.bytheway@cl.cam.ac.uk>
5
6This file is part of the lending-library open-source project: github.com/harkonenbade/lending-library;
7Its licensing is governed by the LICENSE file at the root of the project.
8*/
9#![warn(missing_docs)]
10
11//! A data store that lends temporary ownership of stored values.
12//!
13//! # Example
14//! ```
15//! use lending_library::*;
16//!
17//! struct Processor;
18//! struct Item(String);
19//!
20//! impl Item {
21//!     fn gen(dat: &str) -> Self { Item(dat.to_string()) }
22//! }
23//!
24//! impl Processor {
25//!     fn link(&self, _first: &Item, _second: &Item) {}
26//! }
27//!
28//! enum Event {
29//!     Foo {
30//!         id: i64,
31//!         dat: &'static str,
32//!     },
33//!     Bar {
34//!         id: i64,
35//!         o_id: i64,
36//!         o_dat: &'static str,
37//!     }
38//! }
39//!
40//! const EVTS: &[Event] = &[Event::Foo {id:1, dat:"a_val"},
41//!                          Event::Foo {id:2, dat:"b_val"},
42//!                          Event::Bar {id:1, o_id: 2, o_dat:"B_val"},
43//!                          Event::Bar {id:1, o_id: 3, o_dat:"c_val"}];
44//!
45//! struct Store {
46//!     id_gen: Box<Iterator<Item = i64>>,
47//!     id_to_dat: LendingLibrary<i64, Item>,
48//! }
49//!
50//! impl Store {
51//!     fn new() -> Self {
52//!         Store {
53//!             id_gen: Box::new(0..),
54//!             id_to_dat: LendingLibrary::new(),
55//!         }
56//!     }
57//!
58//!     pub fn declare(&mut self, uid: i64, dat: &str) -> Loan<i64, Item> {
59//!         if !self.id_to_dat.contains_key(&uid) {
60//!             self.id_to_dat.insert(uid, Item::gen(dat));
61//!         }
62//!         self.id_to_dat.lend(&uid).unwrap()
63//!     }
64//! }
65//!
66//! fn main() {
67//!     let mut store = Store::new();
68//!     let pro = Processor;
69//!     for evt in EVTS {
70//!         match *evt {
71//!             Event::Foo { id, dat } => {
72//!                 store.declare(id, dat);
73//!             }
74//!             Event::Bar { id, o_id, o_dat } => {
75//!                 let i = store.declare(id, "");
76//!                 let o = store.declare(o_id, o_dat);
77//!                 pro.link(&i, &o);
78//!             }
79//!         }
80//!     }
81//! }
82//! ```
83
84pub mod iter;
85mod loan;
86#[cfg(test)]
87mod tests;
88
89pub use loan::Loan;
90
91use std::{
92    collections::{hash_map::DefaultHasher, HashMap},
93    hash::{Hash, Hasher},
94    sync::atomic::{AtomicUsize, Ordering},
95    thread,
96};
97
98enum State<K, V> {
99    Present(K, V),
100    Loaned(K),
101    AwaitingDrop(K),
102}
103
104use self::State::{AwaitingDrop, Loaned, Present};
105
106/// A key-value data store that allows you to loan temporary ownership of values.
107///
108/// # Assumptions
109/// The store does it's best to ensure that no unsafe behaviour occurs, however as a result it may
110/// trigger several panics rather than allow an unsafe condition to arise.
111///
112/// The main panic condition is that a `Loan` object derived from the `lend` method on a store may
113/// never outlive the store it originated from. If this condition happens the store will generate a
114/// panic as it goes out of scope, noting the number of outstanding `Loan` objects.
115pub struct LendingLibrary<K, V>
116where
117    K: Hash,
118{
119    store: HashMap<u64, State<K, V>>,
120    outstanding: AtomicUsize,
121}
122
123fn _hash<K: Hash>(val: &K) -> u64 {
124    let mut hasher = DefaultHasher::new();
125    (*val).hash(&mut hasher);
126    hasher.finish()
127}
128
129impl<K, V> LendingLibrary<K, V>
130where
131    K: Hash,
132{
133    /// Creates a new empty `LendingLibrary`.
134    /// # Example
135    /// ```
136    /// use lending_library::LendingLibrary;
137    /// let mut lib: LendingLibrary<i32, i32> = LendingLibrary::new();
138    /// ```
139    pub fn new() -> LendingLibrary<K, V> {
140        LendingLibrary {
141            store: HashMap::new(),
142            outstanding: AtomicUsize::new(0),
143        }
144    }
145
146    /// Creates an empty `LendingLibrary` with at least the specified capacity.
147    /// The library will be able to hold at least `capacity` elements without reallocating.
148    /// # Example
149    /// ```
150    /// use lending_library::LendingLibrary;
151    /// let mut lib: LendingLibrary<i32, i32> = LendingLibrary::with_capacity(100);
152    /// ```
153    pub fn with_capacity(capacity: usize) -> LendingLibrary<K, V> {
154        LendingLibrary {
155            store: HashMap::with_capacity(capacity),
156            outstanding: AtomicUsize::new(0),
157        }
158    }
159
160    /// Returns the number of elements the library can store without reallocating.
161    /// The same bounds as [`HashMap::capacity()`] apply.
162    ///
163    /// [`HashMap::capacity()`]: https://doc.rust-lang.org/stable/std/collections/struct.HashMap.html#method.capacity
164    /// # Example
165    /// ```
166    /// use lending_library::LendingLibrary;
167    /// let mut lib: LendingLibrary<i32, i32> = LendingLibrary::with_capacity(100);
168    /// assert!(lib.capacity() >= 100);
169    /// ```
170    pub fn capacity(&self) -> usize {
171        self.store.capacity()
172    }
173
174    /// Reserves space such that the library can store at least `additional` new records without reallocating.
175    /// # Example
176    /// ```
177    /// use lending_library::LendingLibrary;
178    /// let mut lib: LendingLibrary<i32, i32> = LendingLibrary::with_capacity(0);
179    /// assert_eq!(lib.capacity(), 0);
180    /// lib.reserve(10);
181    /// assert!(lib.capacity() >= 10);
182    /// ```
183    pub fn reserve(&mut self, additional: usize) {
184        self.store.reserve(additional)
185    }
186
187    /// Reduces the stores capacity to the minimum currently required.
188    /// # Example
189    /// ```
190    /// use lending_library::LendingLibrary;
191    /// let mut lib: LendingLibrary<i32, i32> = LendingLibrary::with_capacity(10);
192    /// assert!(lib.capacity() >= 10);
193    /// lib.shrink_to_fit();
194    /// assert_eq!(lib.capacity(), 0);
195    /// ```
196    pub fn shrink_to_fit(&mut self) {
197        self.store.shrink_to_fit()
198    }
199
200    /// An iterator visiting all key/value pairs in arbitary order.
201    /// The item type is `(&'a K, &'a V)`
202    /// # Panics
203    /// The iterator will panic if it encounters an item that is currently loaned from the store,
204    /// so this should only be used where you are sure you have returned all loaned items.
205    pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
206        self.into_iter()
207    }
208
209    /// An iterator visiting all key/value pairs in arbitary order, with mutable references to the
210    /// values. The item type is `(&'a K, &'a mut V)`
211    /// # Panics
212    /// The iterator will panic if it encounters an item that is currently loaned from the store,
213    /// so this should only be used where you are sure you have returned all loaned items.
214    pub fn iter_mut(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
215        self.into_iter()
216    }
217
218    /// Returns the number of items in the store.
219    /// # Example
220    /// ```
221    /// use lending_library::LendingLibrary;
222    /// let mut lib: LendingLibrary<i32, i32> = LendingLibrary::new();
223    /// lib.insert(1, 1);
224    /// lib.insert(2, 1);
225    /// assert_eq!(lib.len(), 2);
226    /// ```
227    pub fn len(&self) -> usize {
228        self.store
229            .iter()
230            .map(|(_k, v)| match v {
231                Present(..) | Loaned(_) => 1,
232                AwaitingDrop(_) => 0,
233            })
234            .sum()
235    }
236
237    /// Returns true if the store is empty and false otherwise.
238    /// # Example
239    /// ```
240    /// use lending_library::LendingLibrary;
241    /// let mut lib: LendingLibrary<i32, i32> = LendingLibrary::new();
242    /// assert!(lib.is_empty());
243    /// lib.insert(1, 1);
244    /// lib.insert(2, 1);
245    /// assert!(!lib.is_empty());
246    /// ```
247    pub fn is_empty(&self) -> bool {
248        self.len() == 0
249    }
250
251    /// Removes all items from the store.
252    /// # Example
253    /// ```
254    /// use lending_library::LendingLibrary;
255    /// let mut lib: LendingLibrary<i32, i32> = LendingLibrary::new();
256    /// lib.insert(1, 1);
257    /// lib.insert(2, 1);
258    /// {
259    ///     let v = lib.lend(&2).unwrap();
260    ///     assert_eq!(*v, 1);
261    /// }
262    /// lib.clear();
263    /// assert_eq!(lib.lend(&1), None);
264    /// ```
265    pub fn clear(&mut self) {
266        let new_store = self
267            .store
268            .drain()
269            .filter(|&(_k, ref v)| match v {
270                Present(..) => false,
271                Loaned(_) | AwaitingDrop(_) => true,
272            })
273            .map(|(h, v)| match v {
274                Loaned(k) | AwaitingDrop(k) => (h, AwaitingDrop(k)),
275                Present(..) => unreachable!(),
276            })
277            .collect();
278        self.store = new_store;
279    }
280
281    /// Returns true if a record with key `key` exists in the store, and false otherwise.
282    /// # Example
283    /// ```
284    /// use lending_library::LendingLibrary;
285    /// let mut lib: LendingLibrary<i32, i32> = LendingLibrary::new();
286    /// assert!(!lib.contains_key(&1));
287    /// lib.insert(1, 1);
288    /// assert!(lib.contains_key(&1));
289    /// ```
290    pub fn contains_key(&self, key: &K) -> bool {
291        let h = _hash(key);
292        match self.store.get(&h) {
293            Some(v) => match v {
294                Present(..) | Loaned(_) => true,
295                AwaitingDrop(_) => false,
296            },
297            None => false,
298        }
299    }
300
301    /// Inserts a new key/value pair into the store. If a pair with that key already exists, the
302    /// previous values will be returned as `Some(V)`, otherwise the method returns `None`.
303    /// # Panics
304    /// The method will panic if you attempt to overwrite a key/value pair that is currently loaned.
305    /// # Example
306    /// ```
307    /// use lending_library::LendingLibrary;
308    /// let mut lib: LendingLibrary<i32, i32> = LendingLibrary::new();
309    /// lib.insert(1, 1);
310    /// lib.insert(2, 1);
311    /// ```
312    pub fn insert(&mut self, key: K, val: V) -> Option<V> {
313        let h = _hash(&key);
314        match self.store.insert(h, Present(key, val)) {
315            Some(v) => match v {
316                Present(_, v) => Some(v),
317                Loaned(_) => panic!("Cannot overwrite loaned value"),
318                AwaitingDrop(_) => panic!("Cannot overwrite value awaiting drop"),
319            },
320            None => None,
321        }
322    }
323
324    /// Removes a key/value pair from the store. Returning true if the key was present in the store
325    /// and false otherwise.
326    /// # Example
327    /// ```
328    /// use lending_library::LendingLibrary;
329    /// let mut lib: LendingLibrary<i32, i32> = LendingLibrary::new();
330    /// assert!(!lib.remove(&1));
331    /// lib.insert(1, 1);
332    /// assert!(lib.contains_key(&1));
333    /// assert!(lib.remove(&1));
334    /// assert!(!lib.contains_key(&1));
335    /// assert!(!lib.remove(&1));
336    /// ```
337    pub fn remove(&mut self, key: &K) -> bool {
338        let h = _hash(key);
339        match self.store.remove(&h) {
340            Some(v) => match v {
341                Present(..) => true,
342                Loaned(k) => {
343                    self.store.insert(h, AwaitingDrop(k));
344                    true
345                }
346                AwaitingDrop(k) => {
347                    self.store.insert(h, AwaitingDrop(k));
348                    false
349                }
350            },
351            None => false,
352        }
353    }
354
355    /// Loans a value from the library, returning `Some(Loan<K, V>)` if the value is present, and `None` if it is not.
356    /// # Panics
357    /// Will panic if you try and loan a value that still has an outstanding loan.
358    /// # Examples
359    /// ```
360    /// use lending_library::LendingLibrary;
361    /// let mut lib: LendingLibrary<i32, i32> = LendingLibrary::with_capacity(0);
362    /// lib.insert(1, 1);
363    /// {
364    ///     let mut v = lib.lend(&1).unwrap();
365    ///     *v += 5;
366    /// }
367    /// ```
368    pub fn lend(&mut self, key: &K) -> Option<Loan<K, V>> {
369        let h = _hash(key);
370        let ptr: *mut Self = self;
371        match self.store.remove(&h) {
372            Some(v) => match v {
373                Present(k, v) => {
374                    self.outstanding.fetch_add(1, Ordering::Relaxed);
375                    self.store.insert(h, Loaned(k));
376                    Some(Loan {
377                        owner: ptr,
378                        key: h,
379                        inner: Some(v),
380                    })
381                }
382                Loaned(_) => panic!("Lending already loaned value"),
383                AwaitingDrop(_) => panic!("Lending value awaiting drop"),
384            },
385            None => None,
386        }
387    }
388
389    fn checkin(&mut self, key: u64, val: V) {
390        match self.store.remove(&key) {
391            Some(v) => {
392                self.outstanding.fetch_sub(1, Ordering::Relaxed);
393                match v {
394                    Present(..) => panic!("Returning replaced item"),
395                    Loaned(k) => {
396                        self.store.insert(key, Present(k, val));
397                    }
398                    AwaitingDrop(_) => {}
399                }
400            }
401            None => panic!("Returning item not from store"),
402        }
403    }
404}
405
406impl<K, V> Drop for LendingLibrary<K, V>
407where
408    K: Hash,
409{
410    fn drop(&mut self) {
411        if !thread::panicking() {
412            let count = self.outstanding.load(Ordering::SeqCst);
413            if count != 0 {
414                panic!("{} value loans outlived store.", count)
415            }
416        }
417    }
418}
419
420impl<K, V> Default for LendingLibrary<K, V>
421where
422    K: Hash,
423{
424    fn default() -> Self {
425        LendingLibrary::new()
426    }
427}