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