1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! Compact representation of `Option<T>` for types with a reserved value.
//!
//! Small Cranelift types like the 32-bit entity references are often used in tables and linked
//! lists where an `Option<T>` is needed. Unfortunately, that would double the size of the tables
//! because `Option<T>` is twice as big as `T`.
//!
//! This module provides a `PackedOption<T>` for types that have a reserved value that can be used
//! to represent `None`.

use core::fmt;
use core::mem;

/// Types that have a reserved value which can't be created any other way.
pub trait ReservedValue: Eq {
    /// Create an instance of the reserved value.
    fn reserved_value() -> Self;
}

/// Packed representation of `Option<T>`.
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct PackedOption<T: ReservedValue>(T);

impl<T: ReservedValue> PackedOption<T> {
    /// Returns `true` if the packed option is a `None` value.
    pub fn is_none(&self) -> bool {
        self.0 == T::reserved_value()
    }

    /// Returns `true` if the packed option is a `Some` value.
    pub fn is_some(&self) -> bool {
        self.0 != T::reserved_value()
    }

    /// Expand the packed option into a normal `Option`.
    pub fn expand(self) -> Option<T> {
        if self.is_none() {
            None
        } else {
            Some(self.0)
        }
    }

    /// Maps a `PackedOption<T>` to `Option<U>` by applying a function to a contained value.
    pub fn map<U, F>(self, f: F) -> Option<U>
    where
        F: FnOnce(T) -> U,
    {
        self.expand().map(f)
    }

    /// Unwrap a packed `Some` value or panic.
    pub fn unwrap(self) -> T {
        self.expand().unwrap()
    }

    /// Unwrap a packed `Some` value or panic.
    pub fn expect(self, msg: &str) -> T {
        self.expand().expect(msg)
    }

    /// Takes the value out of the packed option, leaving a `None` in its place.
    pub fn take(&mut self) -> Option<T> {
        mem::replace(self, None.into()).expand()
    }
}

impl<T: ReservedValue> Default for PackedOption<T> {
    /// Create a default packed option representing `None`.
    fn default() -> Self {
        Self(T::reserved_value())
    }
}

impl<T: ReservedValue> From<T> for PackedOption<T> {
    /// Convert `t` into a packed `Some(x)`.
    fn from(t: T) -> Self {
        debug_assert!(
            t != T::reserved_value(),
            "Can't make a PackedOption from the reserved value."
        );
        Self(t)
    }
}

impl<T: ReservedValue> From<Option<T>> for PackedOption<T> {
    /// Convert an option into its packed equivalent.
    fn from(opt: Option<T>) -> Self {
        match opt {
            None => Self::default(),
            Some(t) => t.into(),
        }
    }
}

impl<T: ReservedValue> Into<Option<T>> for PackedOption<T> {
    fn into(self) -> Option<T> {
        self.expand()
    }
}

impl<T> fmt::Debug for PackedOption<T>
where
    T: ReservedValue + fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.is_none() {
            write!(f, "None")
        } else {
            write!(f, "Some({:?})", self.0)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    // Dummy entity class, with no Copy or Clone.
    #[derive(Debug, PartialEq, Eq)]
    struct NoC(u32);

    impl ReservedValue for NoC {
        fn reserved_value() -> Self {
            NoC(13)
        }
    }

    #[test]
    fn moves() {
        let x = NoC(3);
        let somex: PackedOption<NoC> = x.into();
        assert!(!somex.is_none());
        assert_eq!(somex.expand(), Some(NoC(3)));

        let none: PackedOption<NoC> = None.into();
        assert!(none.is_none());
        assert_eq!(none.expand(), None);
    }

    // Dummy entity class, with Copy.
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    struct Ent(u32);

    impl ReservedValue for Ent {
        fn reserved_value() -> Self {
            Ent(13)
        }
    }

    #[test]
    fn copies() {
        let x = Ent(2);
        let some: PackedOption<Ent> = x.into();
        assert_eq!(some.expand(), x.into());
        assert_eq!(some, x.into());
    }
}