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