Path: blob/main/cranelift/entity/src/packed_option.rs
1691 views
//! Compact representation of `Option<T>` for types with a reserved value.1//!2//! Small Cranelift types like the 32-bit entity references are often used in tables and linked3//! lists where an `Option<T>` is needed. Unfortunately, that would double the size of the tables4//! because `Option<T>` is twice as big as `T`.5//!6//! This module provides a `PackedOption<T>` for types that have a reserved value that can be used7//! to represent `None`.89use core::fmt;10use core::mem;1112#[cfg(feature = "enable-serde")]13use serde_derive::{Deserialize, Serialize};1415/// Types that have a reserved value which can't be created any other way.16pub trait ReservedValue {17/// Create an instance of the reserved value.18fn reserved_value() -> Self;19/// Checks whether value is the reserved one.20fn is_reserved_value(&self) -> bool;21}2223/// Packed representation of `Option<T>`.24///25/// This is a wrapper around a `T`, using `T::reserved_value` to represent26/// `None`.27#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]28#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]29#[repr(transparent)]30pub struct PackedOption<T: ReservedValue>(T);3132impl<T: ReservedValue> PackedOption<T> {33/// Returns `true` if the packed option is a `None` value.34pub fn is_none(&self) -> bool {35self.0.is_reserved_value()36}3738/// Returns `true` if the packed option is a `Some` value.39pub fn is_some(&self) -> bool {40!self.0.is_reserved_value()41}4243/// Expand the packed option into a normal `Option`.44pub fn expand(self) -> Option<T> {45if self.is_none() { None } else { Some(self.0) }46}4748/// Maps a `PackedOption<T>` to `Option<U>` by applying a function to a contained value.49pub fn map<U, F>(self, f: F) -> Option<U>50where51F: FnOnce(T) -> U,52{53self.expand().map(f)54}5556/// Unwrap a packed `Some` value or panic.57#[track_caller]58pub fn unwrap(self) -> T {59self.expand().unwrap()60}6162/// Unwrap a packed `Some` value or panic.63#[track_caller]64pub fn expect(self, msg: &str) -> T {65self.expand().expect(msg)66}6768/// Takes the value out of the packed option, leaving a `None` in its place.69pub fn take(&mut self) -> Option<T> {70mem::replace(self, None.into()).expand()71}72}7374impl<T: ReservedValue> Default for PackedOption<T> {75/// Create a default packed option representing `None`.76fn default() -> Self {77Self(T::reserved_value())78}79}8081impl<T: ReservedValue> From<T> for PackedOption<T> {82/// Convert `t` into a packed `Some(x)`.83fn from(t: T) -> Self {84debug_assert!(85!t.is_reserved_value(),86"Can't make a PackedOption from the reserved value."87);88Self(t)89}90}9192impl<T: ReservedValue> From<Option<T>> for PackedOption<T> {93/// Convert an option into its packed equivalent.94fn from(opt: Option<T>) -> Self {95match opt {96None => Self::default(),97Some(t) => t.into(),98}99}100}101102impl<T: ReservedValue> From<PackedOption<T>> for Option<T> {103fn from(packed: PackedOption<T>) -> Option<T> {104packed.expand()105}106}107108impl<T> fmt::Debug for PackedOption<T>109where110T: ReservedValue + fmt::Debug,111{112fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {113if self.is_none() {114write!(f, "None")115} else {116write!(f, "Some({:?})", self.0)117}118}119}120121#[cfg(test)]122mod tests {123use super::*;124125// Dummy entity class, with no Copy or Clone.126#[derive(Debug, PartialEq, Eq)]127struct NoC(u32);128129impl ReservedValue for NoC {130fn reserved_value() -> Self {131NoC(13)132}133134fn is_reserved_value(&self) -> bool {135self.0 == 13136}137}138139#[test]140fn moves() {141let x = NoC(3);142let somex: PackedOption<NoC> = x.into();143assert!(!somex.is_none());144assert_eq!(somex.expand(), Some(NoC(3)));145146let none: PackedOption<NoC> = None.into();147assert!(none.is_none());148assert_eq!(none.expand(), None);149}150151// Dummy entity class, with Copy.152#[derive(Clone, Copy, Debug, PartialEq, Eq)]153struct Ent(u32);154155impl ReservedValue for Ent {156fn reserved_value() -> Self {157Ent(13)158}159160fn is_reserved_value(&self) -> bool {161self.0 == 13162}163}164165#[test]166fn copies() {167let x = Ent(2);168let some: PackedOption<Ent> = x.into();169assert_eq!(some.expand(), x.into());170assert_eq!(some, x.into());171}172}173174175