cranelift_entity/
packed_option.rs

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
10use core::fmt;
11use core::mem;
12
13#[cfg(feature = "enable-serde")]
14use serde_derive::{Deserialize, Serialize};
15
16/// Types that have a reserved value which can't be created any other way.
17pub 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#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
26#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
27pub struct PackedOption<T: ReservedValue>(T);
28
29impl<T: ReservedValue> PackedOption<T> {
30    /// Returns `true` if the packed option is a `None` value.
31    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.
36    pub fn is_some(&self) -> bool {
37        !self.0.is_reserved_value()
38    }
39
40    /// Expand the packed option into a normal `Option`.
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.
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
75impl<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
82impl<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
93impl<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
103impl<T: ReservedValue> Into<Option<T>> for PackedOption<T> {
104    fn into(self) -> Option<T> {
105        self.expand()
106    }
107}
108
109impl<T> fmt::Debug for PackedOption<T>
110where
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)]
123mod 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}