1 //! Compact representation of `Option<T>` for types with a reserved value. 2 //! 3 //! Small Cranelift types like the 32-bit entity references are often used in tables and linked 4 //! lists where an `Option<T>` is needed. Unfortunately, that would double the size of the tables 5 //! because `Option<T>` is twice as big as `T`. 6 //! 7 //! This module provides a `PackedOption<T>` for types that have a reserved value that can be used 8 //! to represent `None`. 9 10 use core::fmt; 11 use core::mem; 12 13 #[cfg(feature = "enable-serde")] 14 use serde_derive::{Deserialize, Serialize}; 15 16 /// Types that have a reserved value which can't be created any other way. 17 pub trait ReservedValue { 18 /// Create an instance of the reserved value. 19 fn reserved_value() -> Self; 20 /// Checks whether value is the reserved one. 21 fn is_reserved_value(&self) -> bool; 22 } 23 24 /// Packed representation of `Option<T>`. 25 /// 26 /// This is a wrapper around a `T`, using `T::reserved_value` to represent 27 /// `None`. 28 #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)] 29 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] 30 #[repr(transparent)] 31 pub struct PackedOption<T: ReservedValue>(T); 32 33 impl<T: ReservedValue> PackedOption<T> { 34 /// Returns `true` if the packed option is a `None` value. 35 pub fn is_none(&self) -> bool { 36 self.0.is_reserved_value() 37 } 38 39 /// Returns `true` if the packed option is a `Some` value. 40 pub fn is_some(&self) -> bool { 41 !self.0.is_reserved_value() 42 } 43 44 /// Expand the packed option into a normal `Option`. 45 pub fn expand(self) -> Option<T> { 46 if self.is_none() { None } else { Some(self.0) } 47 } 48 49 /// Maps a `PackedOption<T>` to `Option<U>` by applying a function to a contained value. 50 pub fn map<U, F>(self, f: F) -> Option<U> 51 where 52 F: FnOnce(T) -> U, 53 { 54 self.expand().map(f) 55 } 56 57 /// Unwrap a packed `Some` value or panic. 58 #[track_caller] 59 pub fn unwrap(self) -> T { 60 self.expand().unwrap() 61 } 62 63 /// Unwrap a packed `Some` value or panic. 64 #[track_caller] 65 pub fn expect(self, msg: &str) -> T { 66 self.expand().expect(msg) 67 } 68 69 /// Takes the value out of the packed option, leaving a `None` in its place. 70 pub fn take(&mut self) -> Option<T> { 71 mem::replace(self, None.into()).expand() 72 } 73 } 74 75 impl<T: ReservedValue> Default for PackedOption<T> { 76 /// Create a default packed option representing `None`. 77 fn default() -> Self { 78 Self(T::reserved_value()) 79 } 80 } 81 82 impl<T: ReservedValue> From<T> for PackedOption<T> { 83 /// Convert `t` into a packed `Some(x)`. 84 fn from(t: T) -> Self { 85 debug_assert!( 86 !t.is_reserved_value(), 87 "Can't make a PackedOption from the reserved value." 88 ); 89 Self(t) 90 } 91 } 92 93 impl<T: ReservedValue> From<Option<T>> for PackedOption<T> { 94 /// Convert an option into its packed equivalent. 95 fn from(opt: Option<T>) -> Self { 96 match opt { 97 None => Self::default(), 98 Some(t) => t.into(), 99 } 100 } 101 } 102 103 impl<T: ReservedValue> From<PackedOption<T>> for Option<T> { 104 fn from(packed: PackedOption<T>) -> Option<T> { 105 packed.expand() 106 } 107 } 108 109 impl<T> fmt::Debug for PackedOption<T> 110 where 111 T: ReservedValue + fmt::Debug, 112 { 113 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 114 if self.is_none() { 115 write!(f, "None") 116 } else { 117 write!(f, "Some({:?})", self.0) 118 } 119 } 120 } 121 122 #[cfg(test)] 123 mod tests { 124 use super::*; 125 126 // Dummy entity class, with no Copy or Clone. 127 #[derive(Debug, PartialEq, Eq)] 128 struct NoC(u32); 129 130 impl ReservedValue for NoC { 131 fn reserved_value() -> Self { 132 NoC(13) 133 } 134 135 fn is_reserved_value(&self) -> bool { 136 self.0 == 13 137 } 138 } 139 140 #[test] 141 fn moves() { 142 let x = NoC(3); 143 let somex: PackedOption<NoC> = x.into(); 144 assert!(!somex.is_none()); 145 assert_eq!(somex.expand(), Some(NoC(3))); 146 147 let none: PackedOption<NoC> = None.into(); 148 assert!(none.is_none()); 149 assert_eq!(none.expand(), None); 150 } 151 152 // Dummy entity class, with Copy. 153 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 154 struct Ent(u32); 155 156 impl ReservedValue for Ent { 157 fn reserved_value() -> Self { 158 Ent(13) 159 } 160 161 fn is_reserved_value(&self) -> bool { 162 self.0 == 13 163 } 164 } 165 166 #[test] 167 fn copies() { 168 let x = Ent(2); 169 let some: PackedOption<Ent> = x.into(); 170 assert_eq!(some.expand(), x.into()); 171 assert_eq!(some, x.into()); 172 } 173 } 174