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::{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. reserved_value() -> Self19 fn reserved_value() -> Self; 20 /// Checks whether value is the reserved one. is_reserved_value(&self) -> bool21 fn is_reserved_value(&self) -> bool; 22 } 23 24 /// Packed representation of `Option<T>`. 25 #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)] 26 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] 27 pub struct PackedOption<T: ReservedValue>(T); 28 29 impl<T: ReservedValue> PackedOption<T> { 30 /// Returns `true` if the packed option is a `None` value. is_none(&self) -> bool31 pub fn is_none(&self) -> bool { 32 self.0.is_reserved_value() 33 } 34 35 /// Returns `true` if the packed option is a `Some` value. is_some(&self) -> bool36 pub fn is_some(&self) -> bool { 37 !self.0.is_reserved_value() 38 } 39 40 /// Expand the packed option into a normal `Option`. expand(self) -> Option<T>41 pub fn expand(self) -> Option<T> { 42 if self.is_none() { 43 None 44 } else { 45 Some(self.0) 46 } 47 } 48 49 /// Maps a `PackedOption<T>` to `Option<U>` by applying a function to a contained value. map<U, F>(self, f: F) -> Option<U> where F: FnOnce(T) -> U,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. unwrap(self) -> T58 pub fn unwrap(self) -> T { 59 self.expand().unwrap() 60 } 61 62 /// Unwrap a packed `Some` value or panic. expect(self, msg: &str) -> T63 pub fn expect(self, msg: &str) -> T { 64 self.expand().expect(msg) 65 } 66 67 /// Takes the value out of the packed option, leaving a `None` in its place. take(&mut self) -> Option<T>68 pub fn take(&mut self) -> Option<T> { 69 mem::replace(self, None.into()).expand() 70 } 71 } 72 73 impl<T: ReservedValue> Default for PackedOption<T> { 74 /// Create a default packed option representing `None`. default() -> Self75 fn default() -> Self { 76 Self(T::reserved_value()) 77 } 78 } 79 80 impl<T: ReservedValue> From<T> for PackedOption<T> { 81 /// Convert `t` into a packed `Some(x)`. from(t: T) -> Self82 fn from(t: T) -> Self { 83 debug_assert!( 84 !t.is_reserved_value(), 85 "Can't make a PackedOption from the reserved value." 86 ); 87 Self(t) 88 } 89 } 90 91 impl<T: ReservedValue> From<Option<T>> for PackedOption<T> { 92 /// Convert an option into its packed equivalent. from(opt: Option<T>) -> Self93 fn from(opt: Option<T>) -> Self { 94 match opt { 95 None => Self::default(), 96 Some(t) => t.into(), 97 } 98 } 99 } 100 101 impl<T: ReservedValue> Into<Option<T>> for PackedOption<T> { into(self) -> Option<T>102 fn into(self) -> Option<T> { 103 self.expand() 104 } 105 } 106 107 impl<T> fmt::Debug for PackedOption<T> 108 where 109 T: ReservedValue + fmt::Debug, 110 { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result111 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 112 if self.is_none() { 113 write!(f, "None") 114 } else { 115 write!(f, "Some({:?})", self.0) 116 } 117 } 118 } 119 120 #[cfg(test)] 121 mod tests { 122 use super::*; 123 124 // Dummy entity class, with no Copy or Clone. 125 #[derive(Debug, PartialEq, Eq)] 126 struct NoC(u32); 127 128 impl ReservedValue for NoC { reserved_value() -> Self129 fn reserved_value() -> Self { 130 NoC(13) 131 } 132 is_reserved_value(&self) -> bool133 fn is_reserved_value(&self) -> bool { 134 self.0 == 13 135 } 136 } 137 138 #[test] moves()139 fn moves() { 140 let x = NoC(3); 141 let somex: PackedOption<NoC> = x.into(); 142 assert!(!somex.is_none()); 143 assert_eq!(somex.expand(), Some(NoC(3))); 144 145 let none: PackedOption<NoC> = None.into(); 146 assert!(none.is_none()); 147 assert_eq!(none.expand(), None); 148 } 149 150 // Dummy entity class, with Copy. 151 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 152 struct Ent(u32); 153 154 impl ReservedValue for Ent { reserved_value() -> Self155 fn reserved_value() -> Self { 156 Ent(13) 157 } 158 is_reserved_value(&self) -> bool159 fn is_reserved_value(&self) -> bool { 160 self.0 == 13 161 } 162 } 163 164 #[test] copies()165 fn copies() { 166 let x = Ent(2); 167 let some: PackedOption<Ent> = x.into(); 168 assert_eq!(some.expand(), x.into()); 169 assert_eq!(some, x.into()); 170 } 171 } 172