Implement PackedOption to address #19.
The PackedOption<T> struct uses the same amount of memory as T, but can represent None via a reserved value.
This commit is contained in:
@@ -24,3 +24,4 @@ mod constant_hash;
|
|||||||
mod predicates;
|
mod predicates;
|
||||||
mod legalizer;
|
mod legalizer;
|
||||||
mod ref_slice;
|
mod ref_slice;
|
||||||
|
mod packed_option;
|
||||||
|
|||||||
121
lib/cretonne/src/packed_option.rs
Normal file
121
lib/cretonne/src/packed_option.rs
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
//! Compact representation of `Option<T>` for types with a reserved value.
|
||||||
|
//!
|
||||||
|
//! Small Cretonne 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 std::fmt;
|
||||||
|
|
||||||
|
/// 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expand the packed option into a normal `Option`.
|
||||||
|
pub fn expand(self) -> Option<T> {
|
||||||
|
if self.is_none() { None } else { Some(self.0) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ReservedValue> Default for PackedOption<T> {
|
||||||
|
/// Create a default packed option representing `None`.
|
||||||
|
fn default() -> PackedOption<T> {
|
||||||
|
PackedOption(T::reserved_value())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ReservedValue> From<T> for PackedOption<T> {
|
||||||
|
/// Convert `t` into a packed `Some(x)`.
|
||||||
|
fn from(t: T) -> PackedOption<T> {
|
||||||
|
debug_assert!(t != T::reserved_value(),
|
||||||
|
"Can't make a PackedOption from the reserved value.");
|
||||||
|
PackedOption(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ReservedValue> From<Option<T>> for PackedOption<T> {
|
||||||
|
/// Convert an option into its packed equivalent.
|
||||||
|
fn from(opt: Option<T>) -> PackedOption<T> {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user