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