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///
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: 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() {
47            None
48        } else {
49            Some(self.0)
50        }
51    }
52
53    /// Maps a `PackedOption<T>` to `Option<U>` by applying a function to a contained value.
54    pub fn map<U, F>(self, f: F) -> Option<U>
55    where
56        F: FnOnce(T) -> U,
57    {
58        self.expand().map(f)
59    }
60
61    /// Unwrap a packed `Some` value or panic.
62    #[track_caller]
63    pub fn unwrap(self) -> T {
64        self.expand().unwrap()
65    }
66
67    /// Unwrap a packed `Some` value or panic.
68    #[track_caller]
69    pub fn expect(self, msg: &str) -> T {
70        self.expand().expect(msg)
71    }
72
73    /// Takes the value out of the packed option, leaving a `None` in its place.
74    pub fn take(&mut self) -> Option<T> {
75        mem::replace(self, None.into()).expand()
76    }
77}
78
79impl<T: ReservedValue> Default for PackedOption<T> {
80    /// Create a default packed option representing `None`.
81    fn default() -> Self {
82        Self(T::reserved_value())
83    }
84}
85
86impl<T: ReservedValue> From<T> for PackedOption<T> {
87    /// Convert `t` into a packed `Some(x)`.
88    fn from(t: T) -> Self {
89        debug_assert!(
90            !t.is_reserved_value(),
91            "Can't make a PackedOption from the reserved value."
92        );
93        Self(t)
94    }
95}
96
97impl<T: ReservedValue> From<Option<T>> for PackedOption<T> {
98    /// Convert an option into its packed equivalent.
99    fn from(opt: Option<T>) -> Self {
100        match opt {
101            None => Self::default(),
102            Some(t) => t.into(),
103        }
104    }
105}
106
107impl<T: ReservedValue> Into<Option<T>> for PackedOption<T> {
108    fn into(self) -> Option<T> {
109        self.expand()
110    }
111}
112
113impl<T> fmt::Debug for PackedOption<T>
114where
115    T: ReservedValue + fmt::Debug,
116{
117    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118        if self.is_none() {
119            write!(f, "None")
120        } else {
121            write!(f, "Some({:?})", self.0)
122        }
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    // Dummy entity class, with no Copy or Clone.
131    #[derive(Debug, PartialEq, Eq)]
132    struct NoC(u32);
133
134    impl ReservedValue for NoC {
135        fn reserved_value() -> Self {
136            NoC(13)
137        }
138
139        fn is_reserved_value(&self) -> bool {
140            self.0 == 13
141        }
142    }
143
144    #[test]
145    fn moves() {
146        let x = NoC(3);
147        let somex: PackedOption<NoC> = x.into();
148        assert!(!somex.is_none());
149        assert_eq!(somex.expand(), Some(NoC(3)));
150
151        let none: PackedOption<NoC> = None.into();
152        assert!(none.is_none());
153        assert_eq!(none.expand(), None);
154    }
155
156    // Dummy entity class, with Copy.
157    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
158    struct Ent(u32);
159
160    impl ReservedValue for Ent {
161        fn reserved_value() -> Self {
162            Ent(13)
163        }
164
165        fn is_reserved_value(&self) -> bool {
166            self.0 == 13
167        }
168    }
169
170    #[test]
171    fn copies() {
172        let x = Ent(2);
173        let some: PackedOption<Ent> = x.into();
174        assert_eq!(some.expand(), x.into());
175        assert_eq!(some, x.into());
176    }
177}