Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/entity/src/packed_option.rs
1691 views
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
175