Skip to main content

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, mem};
11use wasmtime_core::{alloc::TryClone, error::OutOfMemory};
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///
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)]
31pub struct PackedOption<T: ReservedValue>(T);
32
33impl<T> TryClone for PackedOption<T>
34where
35    T: ReservedValue + TryClone,
36{
37    fn try_clone(&self) -> Result<Self, OutOfMemory> {
38        Ok(Self(self.0.try_clone()?))
39    }
40}
41
42impl<T: ReservedValue> PackedOption<T> {
43    /// Returns `true` if the packed option is a `None` value.
44    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.
49    pub fn is_some(&self) -> bool {
50        !self.0.is_reserved_value()
51    }
52
53    /// Expand the packed option into a normal `Option`.
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.
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]
68    pub fn unwrap(self) -> T {
69        self.expand().unwrap()
70    }
71
72    /// Unwrap a packed `Some` value or panic.
73    #[track_caller]
74    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.
79    pub fn take(&mut self) -> Option<T> {
80        mem::replace(self, None.into()).expand()
81    }
82}
83
84impl<T: ReservedValue> Default for PackedOption<T> {
85    /// Create a default packed option representing `None`.
86    fn default() -> Self {
87        Self(T::reserved_value())
88    }
89}
90
91impl<T: ReservedValue> From<T> for PackedOption<T> {
92    /// Convert `t` into a packed `Some(x)`.
93    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
102impl<T: ReservedValue> From<Option<T>> for PackedOption<T> {
103    /// Convert an option into its packed equivalent.
104    fn from(opt: Option<T>) -> Self {
105        match opt {
106            None => Self::default(),
107            Some(t) => t.into(),
108        }
109    }
110}
111
112impl<T: ReservedValue> From<PackedOption<T>> for Option<T> {
113    fn from(packed: PackedOption<T>) -> Option<T> {
114        packed.expand()
115    }
116}
117
118impl<T> fmt::Debug for PackedOption<T>
119where
120    T: ReservedValue + fmt::Debug,
121{
122    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)]
132mod 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 {
140        fn reserved_value() -> Self {
141            NoC(13)
142        }
143
144        fn is_reserved_value(&self) -> bool {
145            self.0 == 13
146        }
147    }
148
149    #[test]
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 {
166        fn reserved_value() -> Self {
167            Ent(13)
168        }
169
170        fn is_reserved_value(&self) -> bool {
171            self.0 == 13
172        }
173    }
174
175    #[test]
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}