wasmer_types/entity/
packed_option.rs

1// This file contains code from external sources.
2// Attributions: https://github.com/wasmerio/wasmer/blob/main/docs/ATTRIBUTIONS.md
3
4//! Compact representation of `Option<T>` for types with a reserved value.
5//!
6//! Small types are often used in tables and linked lists where an
7//! `Option<T>` is needed. Unfortunately, that would double the size of the tables
8//! because `Option<T>` is twice as big as `T`.
9//!
10//! This module provides a `PackedOption<T>` for types that have a reserved value that can be used
11//! to represent `None`.
12
13use crate::lib::std::fmt;
14use crate::lib::std::mem;
15
16#[cfg(feature = "enable-serde")]
17use serde::{Deserialize, Serialize};
18
19/// Types that have a reserved value which can't be created any other way.
20pub trait ReservedValue {
21    /// Create an instance of the reserved value.
22    fn reserved_value() -> Self;
23    /// Checks whether value is the reserved one.
24    fn is_reserved_value(&self) -> bool;
25}
26
27/// Packed representation of `Option<T>`.
28#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
29#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
30pub struct PackedOption<T: ReservedValue>(T);
31
32impl<T: ReservedValue> PackedOption<T> {
33    /// Returns `true` if the packed option is a `None` value.
34    pub fn is_none(&self) -> bool {
35        self.0.is_reserved_value()
36    }
37
38    /// Returns `true` if the packed option is a `Some` value.
39    pub fn is_some(&self) -> bool {
40        !self.0.is_reserved_value()
41    }
42
43    /// Expand the packed option into a normal `Option`.
44    pub fn expand(self) -> Option<T> {
45        if self.is_none() {
46            None
47        } else {
48            Some(self.0)
49        }
50    }
51
52    /// Maps a `PackedOption<T>` to `Option<U>` by applying a function to a contained value.
53    pub fn map<U, F>(self, f: F) -> Option<U>
54    where
55        F: FnOnce(T) -> U,
56    {
57        self.expand().map(f)
58    }
59
60    /// Unwrap a packed `Some` value or panic.
61    pub fn unwrap(self) -> T {
62        self.expand().unwrap()
63    }
64
65    /// Unwrap a packed `Some` value or panic.
66    pub fn expect(self, msg: &str) -> T {
67        self.expand().expect(msg)
68    }
69
70    /// Takes the value out of the packed option, leaving a `None` in its place.
71    pub fn take(&mut self) -> Option<T> {
72        mem::replace(self, None.into()).expand()
73    }
74}
75
76impl<T: ReservedValue> Default for PackedOption<T> {
77    /// Create a default packed option representing `None`.
78    fn default() -> Self {
79        Self(T::reserved_value())
80    }
81}
82
83impl<T: ReservedValue> From<T> for PackedOption<T> {
84    /// Convert `t` into a packed `Some(x)`.
85    fn from(t: T) -> Self {
86        debug_assert!(
87            !t.is_reserved_value(),
88            "Can't make a PackedOption from the reserved value."
89        );
90        Self(t)
91    }
92}
93
94impl<T: ReservedValue> From<Option<T>> for PackedOption<T> {
95    /// Convert an option into its packed equivalent.
96    fn from(opt: Option<T>) -> Self {
97        match opt {
98            None => Self::default(),
99            Some(t) => t.into(),
100        }
101    }
102}
103
104impl<T: ReservedValue> From<PackedOption<T>> for Option<T> {
105    fn from(other: PackedOption<T>) -> Self {
106        other.expand()
107    }
108}
109
110impl<T> fmt::Debug for PackedOption<T>
111where
112    T: ReservedValue + fmt::Debug,
113{
114    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115        if self.is_none() {
116            write!(f, "None")
117        } else {
118            write!(f, "Some({:?})", self.0)
119        }
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126
127    // Dummy entity class, with no Copy or Clone.
128    #[derive(Debug, PartialEq, Eq)]
129    struct NoC(u32);
130
131    impl ReservedValue for NoC {
132        fn reserved_value() -> Self {
133            Self(13)
134        }
135
136        fn is_reserved_value(&self) -> bool {
137            self.0 == 13
138        }
139    }
140
141    #[test]
142    fn moves() {
143        let x = NoC(3);
144        let somex: PackedOption<NoC> = x.into();
145        assert!(!somex.is_none());
146        assert_eq!(somex.expand(), Some(NoC(3)));
147
148        let none: PackedOption<NoC> = None.into();
149        assert!(none.is_none());
150        assert_eq!(none.expand(), None);
151    }
152
153    // Dummy entity class, with Copy.
154    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
155    struct Ent(u32);
156
157    impl ReservedValue for Ent {
158        fn reserved_value() -> Self {
159            Self(13)
160        }
161
162        fn is_reserved_value(&self) -> bool {
163            self.0 == 13
164        }
165    }
166
167    #[test]
168    fn copies() {
169        let x = Ent(2);
170        let some: PackedOption<Ent> = x.into();
171        let some2: Option<Ent> = x.into();
172        assert_eq!(some.expand(), some2);
173        assert_eq!(some, x.into());
174    }
175}