| 1 | //! Server-side handles and storage for per-handle data. | 
| 2 |  | 
|---|
| 3 | use std::collections::BTreeMap; | 
|---|
| 4 | use std::hash::Hash; | 
|---|
| 5 | use std::num::NonZero; | 
|---|
| 6 | use std::ops::{Index, IndexMut}; | 
|---|
| 7 | use std::sync::atomic::{AtomicU32, Ordering}; | 
|---|
| 8 |  | 
|---|
| 9 | use super::fxhash::FxHashMap; | 
|---|
| 10 |  | 
|---|
| 11 | pub(super) type Handle = NonZero<u32>; | 
|---|
| 12 |  | 
|---|
| 13 | /// A store that associates values of type `T` with numeric handles. A value can | 
|---|
| 14 | /// be looked up using its handle. | 
|---|
| 15 | pub(super) struct OwnedStore<T: 'static> { | 
|---|
| 16 | counter: &'static AtomicU32, | 
|---|
| 17 | data: BTreeMap<Handle, T>, | 
|---|
| 18 | } | 
|---|
| 19 |  | 
|---|
| 20 | impl<T> OwnedStore<T> { | 
|---|
| 21 | pub(super) fn new(counter: &'static AtomicU32) -> Self { | 
|---|
| 22 | // Ensure the handle counter isn't 0, which would panic later, | 
|---|
| 23 | // when `NonZero::new` (aka `Handle::new`) is called in `alloc`. | 
|---|
| 24 | assert_ne!(counter.load(Ordering::Relaxed), 0); | 
|---|
| 25 |  | 
|---|
| 26 | OwnedStore { counter, data: BTreeMap::new() } | 
|---|
| 27 | } | 
|---|
| 28 | } | 
|---|
| 29 |  | 
|---|
| 30 | impl<T> OwnedStore<T> { | 
|---|
| 31 | pub(super) fn alloc(&mut self, x: T) -> Handle { | 
|---|
| 32 | let counter: u32 = self.counter.fetch_add(val:1, order:Ordering::Relaxed); | 
|---|
| 33 | let handle: NonZero = Handle::new(counter).expect(msg: "`proc_macro` handle counter overflowed"); | 
|---|
| 34 | assert!(self.data.insert(handle, x).is_none()); | 
|---|
| 35 | handle | 
|---|
| 36 | } | 
|---|
| 37 |  | 
|---|
| 38 | pub(super) fn take(&mut self, h: Handle) -> T { | 
|---|
| 39 | self.data.remove(&h).expect(msg: "use-after-free in `proc_macro` handle") | 
|---|
| 40 | } | 
|---|
| 41 | } | 
|---|
| 42 |  | 
|---|
| 43 | impl<T> Index<Handle> for OwnedStore<T> { | 
|---|
| 44 | type Output = T; | 
|---|
| 45 | fn index(&self, h: Handle) -> &T { | 
|---|
| 46 | self.data.get(&h).expect(msg: "use-after-free in `proc_macro` handle") | 
|---|
| 47 | } | 
|---|
| 48 | } | 
|---|
| 49 |  | 
|---|
| 50 | impl<T> IndexMut<Handle> for OwnedStore<T> { | 
|---|
| 51 | fn index_mut(&mut self, h: Handle) -> &mut T { | 
|---|
| 52 | self.data.get_mut(&h).expect(msg: "use-after-free in `proc_macro` handle") | 
|---|
| 53 | } | 
|---|
| 54 | } | 
|---|
| 55 |  | 
|---|
| 56 | /// Like `OwnedStore`, but avoids storing any value more than once. | 
|---|
| 57 | pub(super) struct InternedStore<T: 'static> { | 
|---|
| 58 | owned: OwnedStore<T>, | 
|---|
| 59 | interner: FxHashMap<T, Handle>, | 
|---|
| 60 | } | 
|---|
| 61 |  | 
|---|
| 62 | impl<T: Copy + Eq + Hash> InternedStore<T> { | 
|---|
| 63 | pub(super) fn new(counter: &'static AtomicU32) -> Self { | 
|---|
| 64 | InternedStore { owned: OwnedStore::new(counter), interner: FxHashMap::default() } | 
|---|
| 65 | } | 
|---|
| 66 |  | 
|---|
| 67 | pub(super) fn alloc(&mut self, x: T) -> Handle { | 
|---|
| 68 | let owned: &mut OwnedStore = &mut self.owned; | 
|---|
| 69 | *self.interner.entry(x).or_insert_with(|| owned.alloc(x)) | 
|---|
| 70 | } | 
|---|
| 71 |  | 
|---|
| 72 | pub(super) fn copy(&mut self, h: Handle) -> T { | 
|---|
| 73 | self.owned[h] | 
|---|
| 74 | } | 
|---|
| 75 | } | 
|---|
| 76 |  | 
|---|