#![deny(missing_docs)]
pub unsafe trait EnumLike: Copy {
const NUM_VARIANTS: usize;
fn to_discr(self) -> usize;
fn from_discr(x: usize) -> Self;
}
unsafe impl EnumLike for () {
const NUM_VARIANTS: usize = 1;
#[inline(always)]
fn to_discr(self) -> usize {
0
}
#[inline(always)]
fn from_discr(_x: usize) -> Self {
()
}
}
unsafe impl EnumLike for bool {
const NUM_VARIANTS: usize = 2;
#[inline(always)]
fn to_discr(self) -> usize {
if self {
1
} else {
0
}
}
#[inline(always)]
fn from_discr(x: usize) -> Self {
x != 0
}
}
unsafe impl<T: EnumLike> EnumLike for Option<T> {
const NUM_VARIANTS: usize = 1 + T::NUM_VARIANTS;
#[inline(always)]
fn to_discr(self) -> usize {
match self {
None => T::NUM_VARIANTS,
Some(x) => x.to_discr(),
}
}
#[inline(always)]
fn from_discr(x: usize) -> Self {
match x {
x if x == T::NUM_VARIANTS => None,
x => Some(T::from_discr(x)),
}
}
}
unsafe impl<T: EnumLike, S: EnumLike> EnumLike for Result<T, S> {
const NUM_VARIANTS: usize = T::NUM_VARIANTS + S::NUM_VARIANTS;
#[inline(always)]
fn to_discr(self) -> usize {
match self {
Ok(x) => x.to_discr(),
Err(x) => T::NUM_VARIANTS + x.to_discr(),
}
}
#[inline(always)]
fn from_discr(x: usize) -> Self {
match x {
x if x < T::NUM_VARIANTS => Ok(T::from_discr(x)),
x => Err(S::from_discr(x - T::NUM_VARIANTS)),
}
}
}
unsafe impl<T: EnumLike> EnumLike for (T,) {
const NUM_VARIANTS: usize = T::NUM_VARIANTS;
#[inline(always)]
fn to_discr(self) -> usize {
self.0.to_discr()
}
#[inline(always)]
fn from_discr(x: usize) -> Self {
(T::from_discr(x),)
}
}
unsafe impl<T: EnumLike, S: EnumLike> EnumLike for (T, S) {
const NUM_VARIANTS: usize = T::NUM_VARIANTS * S::NUM_VARIANTS;
fn to_discr(self) -> usize {
self.0.to_discr() + self.1.to_discr() * T::NUM_VARIANTS
}
fn from_discr(x: usize) -> Self {
(
T::from_discr(x.wrapping_rem(T::NUM_VARIANTS)),
S::from_discr(x.wrapping_div(T::NUM_VARIANTS)),
)
}
}
macro_rules! tuple_impls {
{
($idx0:tt) -> $T0:ident
($idx1:tt) -> $T1:ident
} => {
};
{
($last_idx:tt) -> $last_T:ident
$(($idx:tt) -> $T:ident)+
} => {
unsafe impl<$($T:EnumLike,)+ $last_T:EnumLike> EnumLike for ($($T,)+ $last_T) {
const NUM_VARIANTS: usize = <(($($T,)+), $last_T)>::NUM_VARIANTS;
fn to_discr(self) -> usize {
(reverse_idx_b!(self [$($idx)+]), self.$last_idx).to_discr()
}
fn from_discr(x: usize) -> Self {
let a = <(($($T,)+), $last_T)>::from_discr(x);
reverse_idx_a!(a [$($idx)+])
}
}
tuple_impls! {
$(($idx) -> $T)+
}
};
}
macro_rules! reverse_idx_b {
($a:ident [] $($reversed:tt)*) => {
($($a.$reversed,)+)
};
($a:ident [$first:tt $($rest:tt)*] $($reversed:tt)*) => {
reverse_idx_b!($a [$($rest)*] $first $($reversed)*)
};
}
macro_rules! reverse_idx_a {
($a:ident [] $($reversed:tt)*) => {
($(($a.0).$reversed,)+ $a.1)
};
($a:ident [$first:tt $($rest:tt)*] $($reversed:tt)*) => {
reverse_idx_a!($a [$($rest)*] $first $($reversed)*)
};
}
tuple_impls! {
(31) -> A31
(30) -> A30
(29) -> A29
(28) -> A28
(27) -> A27
(26) -> A26
(25) -> A25
(24) -> A24
(23) -> A23
(22) -> A22
(21) -> A21
(20) -> A20
(19) -> A19
(18) -> A18
(17) -> A17
(16) -> A16
(15) -> A15
(14) -> A14
(13) -> A13
(12) -> A12
(11) -> A11
(10) -> A10
(9) -> A9
(8) -> A8
(7) -> A7
(6) -> A6
(5) -> A5
(4) -> A4
(3) -> A3
(2) -> A2
(1) -> A1
(0) -> A0
}
unsafe impl<T: EnumLike> EnumLike for [T; 0] {
const NUM_VARIANTS: usize = <()>::NUM_VARIANTS;
#[inline(always)]
fn to_discr(self) -> usize {
0
}
#[inline(always)]
fn from_discr(_x: usize) -> Self {
[]
}
}
macro_rules! array_impls {
{
($idx0:tt) -> $T0:ident
} => {
};
{
($N:tt) -> $T0:ident
$(($idx:tt) -> $T:ident)+
} => {
unsafe impl<$T0: EnumLike> EnumLike for [$T0; $N] {
const NUM_VARIANTS: usize = <($($T,)+)>::NUM_VARIANTS;
fn to_discr(mut self) -> usize {
use std::mem;
unsafe {
(
$(
mem::replace(&mut self[$N - 1 - $idx], mem::uninitialized()),
)+
).to_discr()
}
}
fn from_discr(x: usize) -> Self {
let t = <($($T,)+)>::from_discr(x);
reverse_idx_c!(t [$($idx)+] )
}
}
array_impls! {
$(($idx) -> $T)+
}
};
}
macro_rules! reverse_idx_c {
($a:ident [] $($reversed:tt)*) => {
[$($a.$reversed,)+]
};
($a:ident [$first:tt $($rest:tt)*] $($reversed:tt)*) => {
reverse_idx_c!($a [$($rest)*] $first $($reversed)*)
};
}
array_impls! {
(32) -> A
(31) -> A
(30) -> A
(29) -> A
(28) -> A
(27) -> A
(26) -> A
(25) -> A
(24) -> A
(23) -> A
(22) -> A
(21) -> A
(20) -> A
(19) -> A
(18) -> A
(17) -> A
(16) -> A
(15) -> A
(14) -> A
(13) -> A
(12) -> A
(11) -> A
(10) -> A
(9) -> A
(8) -> A
(7) -> A
(6) -> A
(5) -> A
(4) -> A
(3) -> A
(2) -> A
(1) -> A
(0) -> A
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct PackedU8<T> {
discr: u8,
_phantom: PhantomData<T>,
}
impl<T: EnumLike> PackedU8<T> {
const CHECK_LIMIT_VARIANTS: usize = (1 << 8) - T::NUM_VARIANTS;
pub fn new(a: T) -> Self {
assert!(Self::CHECK_LIMIT_VARIANTS <= (1 << 8));
Self {
discr: T::to_discr(a) as u8,
_phantom: PhantomData,
}
}
pub fn get(self) -> T {
T::from_discr(self.discr as usize)
}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct PackedU16<T> {
discr: u16,
_phantom: PhantomData<T>,
}
impl<T: EnumLike> PackedU16<T> {
const CHECK_LIMIT_VARIANTS: usize = (1 << 16) - T::NUM_VARIANTS;
pub fn new(a: T) -> Self {
assert!(Self::CHECK_LIMIT_VARIANTS <= (1 << 16));
Self {
discr: T::to_discr(a) as u16,
_phantom: PhantomData,
}
}
pub fn get(self) -> T {
T::from_discr(self.discr as usize)
}
}
pub trait EnumValues: EnumLike {
fn values() -> Values<Self>
where
Self: Sized,
{
Values::of()
}
}
impl<T: EnumLike> EnumValues for T {}
use std::marker::PhantomData;
#[derive(Copy, Clone, Debug)]
pub struct Values<T: EnumLike> {
current: usize,
max: usize,
_p: PhantomData<T>,
}
impl<T: EnumLike> Values<T> {
fn of() -> Self {
Self {
current: 0,
max: T::NUM_VARIANTS,
_p: PhantomData,
}
}
}
impl<T: EnumLike> Iterator for Values<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.current < self.max {
let x = T::from_discr(self.current);
self.current += 1;
Some(x)
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining_elements = (self.max - self.current) as usize;
(remaining_elements, Some(remaining_elements))
}
fn count(self) -> usize {
let remaining_elements = (self.max - self.current) as usize;
remaining_elements
}
fn last(mut self) -> Option<Self::Item> {
if self.max == 0 || self.current == self.max {
None
} else {
self.current = self.max - 1;
self.next()
}
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
if self.current + n < self.max {
self.current += n;
self.next()
} else {
self.current = self.max;
None
}
}
}
impl<T: EnumLike> ExactSizeIterator for Values<T> {}
#[cfg(test)]
mod tests {
use super::*;
fn check_values_of<T: Clone + ::std::fmt::Debug + PartialEq + EnumLike>(
x: usize,
) {
let mut seen = vec![];
let mut counter = 0;
for i in T::values() {
seen.push(i.clone());
let idx = i.clone().to_discr();
assert_eq!(i, T::from_discr(idx));
assert_eq!(idx, T::from_discr(idx).to_discr());
counter += 1;
}
assert_eq!(counter, T::NUM_VARIANTS);
for i in 0..counter {
for j in i + 1..counter {
if seen[i] == seen[j] {
panic!("Duplicate entry for {:?}", seen[i]);
}
}
}
assert_eq!(x, T::NUM_VARIANTS);
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum ABC {
A,
B,
C,
}
unsafe impl EnumLike for ABC {
const NUM_VARIANTS: usize = 3;
fn to_discr(self) -> usize {
match self {
ABC::A => 0,
ABC::B => 1,
ABC::C => 2,
}
}
fn from_discr(x: usize) -> Self {
match x {
0 => ABC::A,
1 => ABC::B,
2 => ABC::C,
_ => panic!("Enum ABC has no variant {}", x),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
struct Digit {
x: u8,
}
unsafe impl EnumLike for Digit {
const NUM_VARIANTS: usize = 10;
fn to_discr(self) -> usize {
self.x as usize
}
fn from_discr(x: usize) -> Self {
let x = x as u8;
Self { x }
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
struct TwoDigits {
tens: Digit,
ones: Digit,
}
unsafe impl EnumLike for TwoDigits {
const NUM_VARIANTS: usize = Digit::NUM_VARIANTS * Digit::NUM_VARIANTS;
fn to_discr(self) -> usize {
self.tens.to_discr() * Digit::NUM_VARIANTS + self.ones.to_discr()
}
fn from_discr(x: usize) -> Self {
let tens = Digit::from_discr(x / Digit::NUM_VARIANTS);
let ones = Digit::from_discr(x % Digit::NUM_VARIANTS);
Self { tens, ones }
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
struct ThreeDigits {
hundreds: Digit,
tens: Digit,
ones: Digit,
}
unsafe impl EnumLike for ThreeDigits {
const NUM_VARIANTS: usize =
Digit::NUM_VARIANTS * Digit::NUM_VARIANTS * Digit::NUM_VARIANTS;
fn to_discr(self) -> usize {
self.hundreds.to_discr() * Digit::NUM_VARIANTS * Digit::NUM_VARIANTS
+ self.tens.to_discr() * Digit::NUM_VARIANTS
+ self.ones.to_discr()
}
fn from_discr(x: usize) -> Self {
let hundreds = Digit::from_discr(
x / (Digit::NUM_VARIANTS * Digit::NUM_VARIANTS),
);
let tens = Digit::from_discr(
(x / Digit::NUM_VARIANTS) % Digit::NUM_VARIANTS,
);
let ones = Digit::from_discr(x % Digit::NUM_VARIANTS);
Self {
hundreds,
tens,
ones,
}
}
}
#[test]
fn values_of() {
let mut a = ABC::values();
assert_eq!(a.next(), Some(ABC::A));
assert_eq!(a.next(), Some(ABC::B));
assert_eq!(a.next(), Some(ABC::C));
assert_eq!(a.next(), None);
let b = Digit::values();
assert_eq!(b.count(), 10);
let c = TwoDigits::values();
assert_eq!(c.count(), 100);
}
#[test]
fn check_builtin_impls() {
check_values_of::<()>(1);
check_values_of::<bool>(2);
check_values_of::<Option<()>>(2);
check_values_of::<Option<bool>>(3);
check_values_of::<Result<(), ()>>(2);
check_values_of::<Result<bool, ()>>(3);
check_values_of::<Result<(), bool>>(3);
check_values_of::<Result<bool, bool>>(4);
check_values_of::<((),)>(1);
check_values_of::<((), ())>(1);
check_values_of::<((), (), ())>(1);
check_values_of::<(bool,)>(2);
check_values_of::<(bool, bool)>(2 * 2);
check_values_of::<(bool, bool, bool)>(2 * 2 * 2);
}
#[test]
fn check_array_impls() {
fn as_bool_vec(mut x: u32, n: usize) -> Vec<bool> {
let mut v = vec![];
while x != 0 {
v.push((x & 1) == 1);
x = x >> 1;
}
while v.len() < n {
v.push(false);
}
v
}
macro_rules! test_array_impl_n {
( $( $n:expr ),* ) => {
$(
let mut a = <[bool; $n]>::values();
for i in 0..(1 << $n) {
assert_eq!(a.next().unwrap(), *as_bool_vec(i, $n));
}
assert_eq!(a.next(), None);
)*
};
}
test_array_impl_n!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
macro_rules! test_array_impl_n_short {
( $( $n:expr ),* ) => {
$(
let mut a = <[bool; $n]>::values();
for i in 0..100 {
assert_eq!(
a.next().unwrap(),
*as_bool_vec(i as u32, $n)
);
}
let mut a = <[bool; $n]>::values().skip((1 << $n) - 10);
for i in ((1 << $n) - 10)..(1u64 << $n) {
assert_eq!(
a.next().unwrap(),
*as_bool_vec(i as u32, $n)
);
}
assert_eq!(a.next(), None);
)*
};
}
test_array_impl_n_short!(13, 14, 15, 16, 17, 18, 19, 20, 21, 22);
test_array_impl_n_short!(23, 24, 25, 26, 27, 28, 29, 30, 31, 32);
}
#[test]
fn check_tuple_array_equivalency() {
let mut a = <(bool, bool, bool, bool)>::values();
let mut b = <[[bool; 2]; 2]>::values();
for _ in 0..(1 << 4) {
let va = a.next().unwrap();
let vb = b.next().unwrap();
assert_eq!(va.0, vb[0][0]);
assert_eq!(va.1, vb[0][1]);
assert_eq!(va.2, vb[1][0]);
assert_eq!(va.3, vb[1][1]);
}
assert_eq!(a.next(), None);
assert_eq!(b.next(), None);
}
#[test]
fn check_test_impls() {
check_values_of::<ABC>(3);
check_values_of::<Digit>(10);
check_values_of::<TwoDigits>(100);
}
#[test]
fn nested_option_bool() {
type NestedOptionBool = Option<Option<Option<bool>>>;
check_values_of::<NestedOptionBool>(5);
let mut a = NestedOptionBool::values();
assert_eq!(a.next().unwrap(), Some(Some(Some(false))));
assert_eq!(a.next().unwrap(), Some(Some(Some(true))));
assert_eq!(a.next().unwrap(), Some(Some(None)));
assert_eq!(a.next().unwrap(), Some(None));
assert_eq!(a.next().unwrap(), None);
assert_eq!(a.next(), None);
}
#[test]
fn result_option_unit() {
type Abomination =
Result<Result<Option<()>, bool>, Result<(), Option<bool>>>;
check_values_of::<Abomination>(8);
assert_eq!(Abomination::values().nth(1).unwrap(), Ok(Ok(None)));
assert_eq!(Abomination::values().last().unwrap(), Err(Err(None)));
}
#[test]
fn packed_u8_u16() {
let a = false;
let a_p8 = PackedU8::new(a.clone());
assert_eq!(a, a_p8.get());
let b = ThreeDigits::values().nth(123);
let b_p16 = PackedU16::new(b.clone());
assert_eq!(b, b_p16.get());
}
}