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(µ).unwrap(); 437 } 438 } 439