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, mem}; 11 use wasmtime_core::{alloc::TryClone, error::OutOfMemory}; 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. 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 /// 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> TryClone for PackedOption<T> 34 where 35 T: ReservedValue + TryClone, 36 { try_clone(&self) -> Result<Self, OutOfMemory>37 fn try_clone(&self) -> Result<Self, OutOfMemory> { 38 Ok(Self(self.0.try_clone()?)) 39 } 40 } 41 42 impl<T: ReservedValue> PackedOption<T> { 43 /// Returns `true` if the packed option is a `None` value. is_none(&self) -> bool44 pub fn is_none(&self) -> bool { 45 self.0.is_reserved_value() 46 } 47 48 /// Returns `true` if the packed option is a `Some` value. is_some(&self) -> bool49 pub fn is_some(&self) -> bool { 50 !self.0.is_reserved_value() 51 } 52 53 /// Expand the packed option into a normal `Option`. expand(self) -> Option<T>54 pub fn expand(self) -> Option<T> { 55 if self.is_none() { None } else { Some(self.0) } 56 } 57 58 /// 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,59 pub fn map<U, F>(self, f: F) -> Option<U> 60 where 61 F: FnOnce(T) -> U, 62 { 63 self.expand().map(f) 64 } 65 66 /// Unwrap a packed `Some` value or panic. 67 #[track_caller] unwrap(self) -> T68 pub fn unwrap(self) -> T { 69 self.expand().unwrap() 70 } 71 72 /// Unwrap a packed `Some` value or panic. 73 #[track_caller] expect(self, msg: &str) -> T74 pub fn expect(self, msg: &str) -> T { 75 self.expand().expect(msg) 76 } 77 78 /// Takes the value out of the packed option, leaving a `None` in its place. take(&mut self) -> Option<T>79 pub fn take(&mut self) -> Option<T> { 80 mem::replace(self, None.into()).expand() 81 } 82 } 83 84 impl<T: ReservedValue> Default for PackedOption<T> { 85 /// Create a default packed option representing `None`. default() -> Self86 fn default() -> Self { 87 Self(T::reserved_value()) 88 } 89 } 90 91 impl<T: ReservedValue> From<T> for PackedOption<T> { 92 /// Convert `t` into a packed `Some(x)`. from(t: T) -> Self93 fn from(t: T) -> Self { 94 debug_assert!( 95 !t.is_reserved_value(), 96 "Can't make a PackedOption from the reserved value." 97 ); 98 Self(t) 99 } 100 } 101 102 impl<T: ReservedValue> From<Option<T>> for PackedOption<T> { 103 /// Convert an option into its packed equivalent. from(opt: Option<T>) -> Self104 fn from(opt: Option<T>) -> Self { 105 match opt { 106 None => Self::default(), 107 Some(t) => t.into(), 108 } 109 } 110 } 111 112 impl<T: ReservedValue> From<PackedOption<T>> for Option<T> { from(packed: PackedOption<T>) -> Option<T>113 fn from(packed: PackedOption<T>) -> Option<T> { 114 packed.expand() 115 } 116 } 117 118 impl<T> fmt::Debug for PackedOption<T> 119 where 120 T: ReservedValue + fmt::Debug, 121 { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result122 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 123 if self.is_none() { 124 write!(f, "None") 125 } else { 126 write!(f, "Some({:?})", self.0) 127 } 128 } 129 } 130 131 #[cfg(test)] 132 mod tests { 133 use super::*; 134 135 // Dummy entity class, with no Copy or Clone. 136 #[derive(Debug, PartialEq, Eq)] 137 struct NoC(u32); 138 139 impl ReservedValue for NoC { reserved_value() -> Self140 fn reserved_value() -> Self { 141 NoC(13) 142 } 143 is_reserved_value(&self) -> bool144 fn is_reserved_value(&self) -> bool { 145 self.0 == 13 146 } 147 } 148 149 #[test] moves()150 fn moves() { 151 let x = NoC(3); 152 let somex: PackedOption<NoC> = x.into(); 153 assert!(!somex.is_none()); 154 assert_eq!(somex.expand(), Some(NoC(3))); 155 156 let none: PackedOption<NoC> = None.into(); 157 assert!(none.is_none()); 158 assert_eq!(none.expand(), None); 159 } 160 161 // Dummy entity class, with Copy. 162 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 163 struct Ent(u32); 164 165 impl ReservedValue for Ent { reserved_value() -> Self166 fn reserved_value() -> Self { 167 Ent(13) 168 } 169 is_reserved_value(&self) -> bool170 fn is_reserved_value(&self) -> bool { 171 self.0 == 13 172 } 173 } 174 175 #[test] copies()176 fn copies() { 177 let x = Ent(2); 178 let some: PackedOption<Ent> = x.into(); 179 assert_eq!(some.expand(), x.into()); 180 assert_eq!(some, x.into()); 181 } 182 } 183