1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
//! Trivial type-map implementation
//!
//! Implementation uses type erased values with type as index.
//!
//! ## Hash implementation
//!
//! The map uses simplified `Hasher` that relies on fact that `Type::id` is unique.
//! In fact there is no hashing under hood, and type's id is returned as it is.
//!
//! ## Usage
//!
//! ```rust
//! use ttmap::TypeMap;
//!
//! let mut map = TypeMap::new();
//!
//! map.insert("string");
//!
//! assert_eq!(*map.get::<&'static str>().unwrap(), "string");
//!
//! map.insert(1u8);
//!
//! assert_eq!(*map.get::<u8>().unwrap(), 1);
//!
//! assert_eq!(map.get_or_default::<String>(), "");
//! ```

#![warn(missing_docs)]
#![allow(private_interfaces)]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::style))]

#[cfg(not(debug_assertions))]
macro_rules! unreach {
    () => ({
        unsafe {
            core::hint::unreachable_unchecked();
        }
    })
}

#[cfg(debug_assertions)]
macro_rules! unreach {
    () => ({
        unreachable!()
    })
}

mod typ;
pub use typ::{Type, RawType};
mod value;
pub use value::Value;
mod hash;

type Key = core::any::TypeId;
///Boxed [Type]
pub type ValueBox = Box<dyn core::any::Any + Send + Sync>;

#[cold]
#[inline(never)]
fn unlikely_vacant_insert(this: std::collections::hash_map::VacantEntry<'_, Key, ValueBox>, val: ValueBox) -> &'_ mut ValueBox {
    this.insert(val)
}

type HashMap = std::collections::HashMap<Key, ValueBox, hash::UniqueHasherBuilder>;

///Type-safe store, indexed by types.
pub struct TypeMap {
    inner: HashMap,
}

impl TypeMap {
    #[inline]
    ///Creates new instance
    pub fn new() -> Self {
        Self {
            inner: HashMap::with_capacity_and_hasher(0, hash::UniqueHasherBuilder),
        }
    }

    #[inline]
    ///Returns number of key & value pairs inside.
    pub fn len(&self) -> usize {
        self.inner.len()
    }

    #[inline]
    ///Returns number of key & value pairs inside.
    pub fn capacity(&self) -> usize {
        self.inner.capacity()
    }

    #[inline]
    ///Returns whether map is empty
    pub fn is_empty(&self) -> bool {
        self.inner.is_empty()
    }

    #[inline]
    ///Removes all pairs of key & value from the map.
    pub fn clear(&mut self) {
        self.inner.clear()
    }

    #[inline]
    ///Returns whether element is present in the map.
    pub fn has<T: Type>(&self) -> bool {
        self.inner.contains_key(&T::id())
    }

    #[inline]
    ///Returns whether element is present in the map.
    pub fn contains_key<T: Type>(&self) -> bool {
        self.inner.contains_key(&T::id())
    }

    #[inline]
    ///Access element in the map, returning reference to it, if present
    pub fn get<T: Type>(&self) -> Option<&T> {
        self.inner.get(&T::id()).map(|raw| Value::<T>::new_inner_ref(raw).downcast_ref())
    }

    #[inline]
    ///Access element in the map, returning reference to it, if present
    pub fn get_raw(&self, id: &Key) -> Option<&Value<RawType>> {
        self.inner.get(id).map(Value::new_inner_ref)
    }

    #[inline]
    ///Access element in the map, returning mutable reference to it, if present
    pub fn get_mut<T: Type>(&mut self) -> Option<&mut T> {
        self.inner.get_mut(&T::id()).map(|raw| Value::<T>::new_inner_mut(raw).downcast_mut())
    }

    #[inline]
    ///Access element in the map, returning mutable reference to it, if present
    pub fn get_mut_raw(&mut self, id: &Key) -> Option<&mut Value<RawType>> {
        self.inner.get_mut(id).map(Value::new_inner_mut)
    }

    #[inline]
    ///Access element in the map, if not present, constructs it using default value.
    pub fn get_or_default<T: Type + Default>(&mut self) -> &mut T {
        use std::collections::hash_map::Entry;

        match self.inner.entry(T::id()) {
            Entry::Occupied(occupied) => {
                match occupied.into_mut().downcast_mut() {
                    Some(res) => res,
                    None => unreach!(),
                }
            },
            Entry::Vacant(vacant) => {
                let ptr = unlikely_vacant_insert(vacant, Box::<T>::default());
                match ptr.downcast_mut() {
                    Some(res) => res,
                    None => unreach!(),
                }
            }
        }
    }

    #[inline]
    ///Insert element inside the map, returning heap-allocated old one if any
    ///
    ///## Note
    ///
    ///Be careful when inserting without explicitly specifying type.
    ///Some special types like function pointers are impossible to infer as non-anonymous type.
    ///You should manually specify type when in doubt.
    pub fn insert<T: Type>(&mut self, value: T) -> Option<Box<T>> {
        self.insert_raw(Value::new_inner(Box::new(value))).map(Value::downcast)
    }

    ///Insert raw element inside the map, returning heap-allocated old one if any
    pub fn insert_raw<T: Type>(&mut self, value: Value<T>) -> Option<Value<T>> {
        use std::collections::hash_map::Entry;

        match self.inner.entry(T::id()) {
            Entry::Occupied(mut occupied) => Some(
                Value::<T>::new_inner(
                    occupied.insert(value.into_raw())
                )
            ),
            Entry::Vacant(vacant) => {
                vacant.insert(value.into_raw());
                None
            }
        }
    }

    #[inline]
    ///Attempts to remove element from the map, returning boxed `Some` if it is present.
    pub fn remove_raw(&mut self, id: &Key) -> Option<Value<RawType>> {
        self.inner.remove(id).map(Value::new_inner)
    }

    #[inline]
    ///Attempts to remove element from the map, returning boxed `Some` if it is present.
    pub fn remove<T: Type>(&mut self) -> Option<Box<T>> {
        self.inner.remove(&T::id()).map(|val| Value::<T>::new_inner(val).downcast())
    }
}

impl core::default::Default for TypeMap {
    #[inline]
    fn default() -> Self {
        Self::new()
    }
}

impl core::fmt::Debug for TypeMap {
    #[inline]
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
        writeln!(f, "TypeMap {{ size={}, capacity={} }}", self.len(), self.capacity())
    }
}