1 use core::num::{NonZeroU16, NonZeroU32, NonZeroU8, NonZeroUsize};
2 
3 /// Types implementing this trait can be used as keys for all Rodeos
4 ///
5 /// # Safety
6 ///
7 /// into/from must be perfectly symmetrical, any key that goes on must be perfectly reproduced with the other
8 ///
9 /// [`ReadOnlyLasso`]: crate::ReadOnlyLasso
10 pub unsafe trait Key: Copy + Eq {
11     /// Returns the `usize` that represents the current key
into_usize(self) -> usize12     fn into_usize(self) -> usize;
13 
14     /// Attempts to create a key from a `usize`, returning `None` if it fails
try_from_usize(int: usize) -> Option<Self>15     fn try_from_usize(int: usize) -> Option<Self>;
16 }
17 
18 /// A key type taking up `size_of::<usize>()` bytes of space (generally 4 or 8 bytes)
19 ///
20 /// Internally is a `NonZeroUsize` to allow for space optimizations when stored inside of an [`Option`]
21 ///
22 /// [`ReadOnlyLasso`]: crate::ReadOnlyLasso
23 /// [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
24 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
25 #[repr(transparent)]
26 pub struct LargeSpur {
27     key: NonZeroUsize,
28 }
29 
30 unsafe impl Key for LargeSpur {
31     #[cfg_attr(feature = "inline-more", inline)]
into_usize(self) -> usize32     fn into_usize(self) -> usize {
33         self.key.get() - 1
34     }
35 
36     /// Returns `None` if `int` is greater than `usize::MAX - 1`
37     #[cfg_attr(feature = "inline-more", inline)]
try_from_usize(int: usize) -> Option<Self>38     fn try_from_usize(int: usize) -> Option<Self> {
39         if int < usize::max_value() {
40             // Safety: The integer is less than the max value and then incremented by one, meaning that
41             // is is impossible for a zero to inhabit the NonZeroUsize
42             unsafe {
43                 Some(Self {
44                     key: NonZeroUsize::new_unchecked(int + 1),
45                 })
46             }
47         } else {
48             None
49         }
50     }
51 }
52 
53 impl Default for LargeSpur {
54     #[cfg_attr(feature = "inline-more", inline)]
default() -> Self55     fn default() -> Self {
56         Self::try_from_usize(0).unwrap()
57     }
58 }
59 
60 /// The default key for every Rodeo, uses only 32 bits of space
61 ///
62 /// Internally is a `NonZeroU32` to allow for space optimizations when stored inside of an [`Option`]
63 ///
64 /// [`ReadOnlyLasso`]: crate::ReadOnlyLasso
65 /// [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
66 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
67 #[repr(transparent)]
68 pub struct Spur {
69     key: NonZeroU32,
70 }
71 
72 unsafe impl Key for Spur {
73     #[cfg_attr(feature = "inline-more", inline)]
into_usize(self) -> usize74     fn into_usize(self) -> usize {
75         self.key.get() as usize - 1
76     }
77 
78     /// Returns `None` if `int` is greater than `u32::MAX - 1`
79     #[cfg_attr(feature = "inline-more", inline)]
try_from_usize(int: usize) -> Option<Self>80     fn try_from_usize(int: usize) -> Option<Self> {
81         if int < u32::max_value() as usize {
82             // Safety: The integer is less than the max value and then incremented by one, meaning that
83             // is is impossible for a zero to inhabit the NonZeroU32
84             unsafe {
85                 Some(Self {
86                     key: NonZeroU32::new_unchecked(int as u32 + 1),
87                 })
88             }
89         } else {
90             None
91         }
92     }
93 }
94 
95 impl Default for Spur {
96     #[cfg_attr(feature = "inline-more", inline)]
default() -> Self97     fn default() -> Self {
98         Self::try_from_usize(0).unwrap()
99     }
100 }
101 
102 /// A miniature Key utilizing only 16 bits of space
103 ///
104 /// Internally is a `NonZeroU16` to allow for space optimizations when stored inside of an [`Option`]
105 ///
106 /// [`ReadOnlyLasso`]: crate::ReadOnlyLasso
107 /// [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
108 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
109 #[repr(transparent)]
110 pub struct MiniSpur {
111     key: NonZeroU16,
112 }
113 
114 unsafe impl Key for MiniSpur {
115     #[cfg_attr(feature = "inline-more", inline)]
into_usize(self) -> usize116     fn into_usize(self) -> usize {
117         self.key.get() as usize - 1
118     }
119 
120     /// Returns `None` if `int` is greater than `u16::MAX - 1`
121     #[cfg_attr(feature = "inline-more", inline)]
try_from_usize(int: usize) -> Option<Self>122     fn try_from_usize(int: usize) -> Option<Self> {
123         if int < u16::max_value() as usize {
124             // Safety: The integer is less than the max value and then incremented by one, meaning that
125             // is is impossible for a zero to inhabit the NonZeroU16
126             unsafe {
127                 Some(Self {
128                     key: NonZeroU16::new_unchecked(int as u16 + 1),
129                 })
130             }
131         } else {
132             None
133         }
134     }
135 }
136 
137 impl Default for MiniSpur {
138     #[cfg_attr(feature = "inline-more", inline)]
default() -> Self139     fn default() -> Self {
140         Self::try_from_usize(0).unwrap()
141     }
142 }
143 
144 /// A miniature Key utilizing only 8 bits of space
145 ///
146 /// Internally is a `NonZeroU8` to allow for space optimizations when stored inside of an [`Option`]
147 ///
148 /// [`ReadOnlyLasso`]: crate::ReadOnlyLasso
149 /// [`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
150 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
151 #[repr(transparent)]
152 pub struct MicroSpur {
153     key: NonZeroU8,
154 }
155 
156 unsafe impl Key for MicroSpur {
157     #[cfg_attr(feature = "inline-more", inline)]
into_usize(self) -> usize158     fn into_usize(self) -> usize {
159         self.key.get() as usize - 1
160     }
161 
162     /// Returns `None` if `int` is greater than `u8::MAX - 1`
163     #[cfg_attr(feature = "inline-more", inline)]
try_from_usize(int: usize) -> Option<Self>164     fn try_from_usize(int: usize) -> Option<Self> {
165         if int < u8::max_value() as usize {
166             // Safety: The integer is less than the max value and then incremented by one, meaning that
167             // is is impossible for a zero to inhabit the NonZeroU8
168             unsafe {
169                 Some(Self {
170                     key: NonZeroU8::new_unchecked(int as u8 + 1),
171                 })
172             }
173         } else {
174             None
175         }
176     }
177 }
178 
179 impl Default for MicroSpur {
180     #[cfg_attr(feature = "inline-more", inline)]
default() -> Self181     fn default() -> Self {
182         Self::try_from_usize(0).unwrap()
183     }
184 }
185 
186 macro_rules! impl_serde {
187     ($($key:ident => $ty:ident),* $(,)?) => {
188         #[cfg(feature = "serialize")]
189         mod __serde {
190             use super::{$($key),*};
191             use serde::{
192                 de::{Deserialize, Deserializer},
193                 ser::{Serialize, Serializer},
194             };
195             use core::num::{$($ty),*};
196 
197             $(
198                 impl Serialize for $key {
199                     #[cfg_attr(feature = "inline-more", inline)]
200                     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
201                     where
202                         S: Serializer,
203                     {
204                         self.key.serialize(serializer)
205                     }
206                 }
207 
208                 impl<'de> Deserialize<'de> for $key {
209                     #[cfg_attr(feature = "inline-more", inline)]
210                     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
211                     where
212                         D: Deserializer<'de>,
213                     {
214                         let key = <$ty>::deserialize(deserializer)?;
215                         Ok(Self { key })
216                     }
217                 }
218             )*
219         }
220     };
221 }
222 
223 // Implement `Serialize` and `Deserialize` when the `serde` feature is enabled
224 impl_serde! {
225     Spur => NonZeroU32,
226     MiniSpur => NonZeroU16,
227     MicroSpur => NonZeroU8,
228     LargeSpur => NonZeroUsize,
229 }
230 
231 macro_rules! impl_deepsize {
232     ($($type:ident),* $(,)?) => {
233         #[cfg(feature = "deepsize")]
234         mod __deepsize {
235             use super::{$($type),*};
236             #[cfg(test)]
237             use super::Key;
238             use deepsize::{DeepSizeOf, Context};
239             use core::mem;
240 
241             $(
242                 impl DeepSizeOf for $type {
243                     fn deep_size_of_children(&self, _context: &mut Context) -> usize {
244                         0
245                     }
246 
247                     fn deep_size_of(&self) -> usize {
248                         mem::size_of::<$type>()
249                     }
250                 }
251             )*
252 
253             #[test]
254             fn deepsize_implementations() {
255                 $(
256                     assert_eq!(
257                         mem::size_of::<$type>(),
258                         $type::try_from_usize(0).unwrap().deep_size_of(),
259                     );
260                 )*
261             }
262         }
263     };
264 }
265 
266 // Implement `DeepSizeOf` when the `deepsize` feature is enabled
267 impl_deepsize! {
268     Spur,
269     MiniSpur,
270     MicroSpur,
271     LargeSpur,
272 }
273 
274 macro_rules! impl_abomonation {
275     ($($type:ident),* $(,)?) => {
276         #[cfg(all(feature = "abomonation", not(feature = "no-std")))]
277         mod __abomonation {
278             use super::{$($type),*};
279             #[cfg(test)]
280             use super::Key;
281             use abomonation::Abomonation;
282             use std::io::{self, Write};
283 
284             $(
285                 impl Abomonation for $type {
286                     unsafe fn entomb<W: Write>(&self, write: &mut W) -> io::Result<()> {
287                         self.key.entomb(write)
288                     }
289 
290                     unsafe fn exhume<'a, 'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> {
291                         self.key.exhume(bytes)
292                     }
293 
294                     fn extent(&self) -> usize {
295                         self.key.extent()
296                     }
297                 }
298             )*
299 
300             #[test]
301             fn abomonation_implementations() {
302                 let mut buf = Vec::new();
303 
304                 $(
305                     unsafe {
306                         let base = $type::try_from_usize(0).unwrap();
307 
308                         abomonation::encode(&base, &mut buf).unwrap();
309                         assert_eq!(base, *abomonation::decode(&mut buf [..]).unwrap().0);
310                     }
311 
312                     buf.clear();
313                 )*
314             }
315         }
316     };
317 }
318 
319 // Implement `Abomonation` when the `abomonation` feature is enabled
320 impl_abomonation! {
321     Spur,
322     MiniSpur,
323     MicroSpur,
324     LargeSpur,
325 }
326 
327 #[cfg(test)]
328 mod tests {
329     use super::*;
330 
331     #[test]
large()332     fn large() {
333         let zero = LargeSpur::try_from_usize(0).unwrap();
334         let max = LargeSpur::try_from_usize(usize::max_value() - 1).unwrap();
335         let default = LargeSpur::default();
336 
337         assert_eq!(zero.into_usize(), 0);
338         assert_eq!(max.into_usize(), usize::max_value() - 1);
339         assert_eq!(default.into_usize(), 0);
340     }
341 
342     #[test]
large_max_returns_none()343     fn large_max_returns_none() {
344         assert_eq!(None, LargeSpur::try_from_usize(usize::max_value()));
345     }
346 
347     #[test]
348     #[should_panic]
349     #[cfg(not(miri))]
large_max_panics()350     fn large_max_panics() {
351         LargeSpur::try_from_usize(usize::max_value()).unwrap();
352     }
353 
354     #[test]
spur()355     fn spur() {
356         let zero = Spur::try_from_usize(0).unwrap();
357         let max = Spur::try_from_usize(u32::max_value() as usize - 1).unwrap();
358         let default = Spur::default();
359 
360         assert_eq!(zero.into_usize(), 0);
361         assert_eq!(max.into_usize(), u32::max_value() as usize - 1);
362         assert_eq!(default.into_usize(), 0);
363     }
364 
365     #[test]
spur_returns_none()366     fn spur_returns_none() {
367         assert_eq!(None, Spur::try_from_usize(u32::max_value() as usize));
368     }
369 
370     #[test]
371     #[should_panic]
372     #[cfg(not(miri))]
spur_panics()373     fn spur_panics() {
374         Spur::try_from_usize(u32::max_value() as usize).unwrap();
375     }
376 
377     #[test]
mini()378     fn mini() {
379         let zero = MiniSpur::try_from_usize(0).unwrap();
380         let max = MiniSpur::try_from_usize(u16::max_value() as usize - 1).unwrap();
381         let default = MiniSpur::default();
382 
383         assert_eq!(zero.into_usize(), 0);
384         assert_eq!(max.into_usize(), u16::max_value() as usize - 1);
385         assert_eq!(default.into_usize(), 0);
386     }
387 
388     #[test]
mini_returns_none()389     fn mini_returns_none() {
390         assert_eq!(None, MiniSpur::try_from_usize(u16::max_value() as usize));
391     }
392 
393     #[test]
394     #[should_panic]
395     #[cfg(not(miri))]
mini_panics()396     fn mini_panics() {
397         MiniSpur::try_from_usize(u16::max_value() as usize).unwrap();
398     }
399 
400     #[test]
micro()401     fn micro() {
402         let zero = MicroSpur::try_from_usize(0).unwrap();
403         let max = MicroSpur::try_from_usize(u8::max_value() as usize - 1).unwrap();
404         let default = MicroSpur::default();
405 
406         assert_eq!(zero.into_usize(), 0);
407         assert_eq!(max.into_usize(), u8::max_value() as usize - 1);
408         assert_eq!(default.into_usize(), 0);
409     }
410 
411     #[test]
micro_returns_none()412     fn micro_returns_none() {
413         assert_eq!(None, MicroSpur::try_from_usize(u8::max_value() as usize));
414     }
415 
416     #[test]
417     #[should_panic]
418     #[cfg(not(miri))]
micro_panics()419     fn micro_panics() {
420         MicroSpur::try_from_usize(u8::max_value() as usize).unwrap();
421     }
422 
423     #[test]
424     #[cfg(feature = "serialize")]
all_serialize()425     fn all_serialize() {
426         let large = LargeSpur::try_from_usize(0).unwrap();
427         let _ = serde_json::to_string(&large).unwrap();
428 
429         let normal = Spur::try_from_usize(0).unwrap();
430         let _ = serde_json::to_string(&normal).unwrap();
431 
432         let mini = MiniSpur::try_from_usize(0).unwrap();
433         let _ = serde_json::to_string(&mini).unwrap();
434 
435         let micro = MicroSpur::try_from_usize(0).unwrap();
436         let _ = serde_json::to_string(&micro).unwrap();
437     }
438 }
439