use core::{
fmt::Debug,
mem::{MaybeUninit, transmute, transmute_copy},
};
use bytemuck::{Pod, TransparentWrapper, Zeroable};
use const_default::ConstDefault;
use crate::{
Array,
int::{CInt, PrimaryUInt},
};
pub use self::iter::{EnumDiscriminantsIterator, EnumDiscriminantsIteratorExt, IterEnumDiscriminants};
pub trait MapEnum {
type Discriminant: MapEnum<Discriminant = Self::Discriminant, Array = Self::Array>;
type Array: Array<E = ()>;
fn iter() -> impl ExactSizeIterator<Item = Self::Discriminant>;
fn get_index(&self) -> usize;
}
pub unsafe trait PlainMapEnum: MapEnum {
type NatureInt: PrimaryUInt;
}
#[repr(transparent)]
pub struct EnumMap<Ids: MapEnum, T>(<Ids::Array as Array>::Map<T>);
impl<Ids: MapEnum, T> EnumMap<Ids, T> {
pub const fn from_array(data: <Ids::Array as Array>::Map<T>) -> Self {
Self(data)
}
pub const fn as_array(&self) -> &<Ids::Array as Array>::Map<T> {
&self.0
}
pub const fn as_array_mut(&mut self) -> &mut <Ids::Array as Array>::Map<T> {
&mut self.0
}
pub const fn into_array(self) -> <Ids::Array as Array>::Map<T> {
let out = unsafe { core::ptr::from_ref(&self.0).read() };
core::mem::forget(self);
out
}
pub const fn remap<TIds: MapEnum<Array = Ids::Array>>(&self) -> &EnumMap<TIds, T> {
unsafe { transmute(self) }
}
pub const fn remap_mut<TIds: MapEnum<Array = Ids::Array>>(&mut self) -> &mut EnumMap<TIds, T> {
unsafe { transmute(self) }
}
pub fn map_new(mut map: impl FnMut(Ids::Discriminant) -> T) -> Self {
let mut out = MaybeUninit::<Self>::uninit();
let buf = unsafe { out.assume_init_mut() }.0.as_mut();
for ids in Ids::iter() {
unsafe { core::ptr::write(buf.get_unchecked_mut(ids.get_index()), map(ids)) };
}
unsafe { out.assume_init() }
}
pub const fn map_as_ref<'a>(&'a self) -> EnumMap<Ids, &'a T> {
let mut out = MaybeUninit::<EnumMap<Ids, &'a T>>::uninit();
let mut src = (&raw const self.0).cast::<T>();
let mut tgt = (unsafe { &raw mut out.assume_init_mut().0 }).cast::<&'a T>();
let mut i = 0;
while i < Ids::Array::LEN {
unsafe { tgt.write(&*src) };
i += 1;
src = unsafe { src.add(1) };
tgt = unsafe { tgt.add(1) };
}
unsafe { out.assume_init() }
}
pub const fn map_as_mut<'a>(&'a mut self) -> EnumMap<Ids, &'a mut T> {
let mut out = MaybeUninit::<EnumMap<Ids, &'a mut T>>::uninit();
let mut src = (&raw mut self.0).cast::<T>();
let mut tgt = (unsafe { &raw mut out.assume_init_mut().0 }).cast::<&'a mut T>();
let mut i = 0;
while i < Ids::Array::LEN {
unsafe { tgt.write(&mut *src) };
i += 1;
src = unsafe { src.add(1) };
tgt = unsafe { tgt.add(1) };
}
unsafe { out.assume_init() }
}
pub const fn transparent_wrap<U: TransparentWrapper<T>>(self) -> EnumMap<Ids, U> {
let out = unsafe { transmute_copy(&self) };
core::mem::forget(self);
out
}
pub const fn transparent_peel<U>(self) -> EnumMap<Ids, U>
where
T: TransparentWrapper<U>,
{
let out = unsafe { transmute_copy(&self) };
core::mem::forget(self);
out
}
}
impl<Ids: MapEnum + PlainMapEnum, T> EnumMap<Ids, T> {
const ASSERT: () = assert!(size_of::<Ids>() == size_of::<Ids::NatureInt>() && size_of::<Ids>() <= size_of::<usize>());
pub const fn cget(&self, index: &Ids) -> &T {
let () = Self::ASSERT;
let index: Ids::NatureInt = unsafe { transmute_copy(index) };
unsafe { &*(&raw const self.0 as *const T).add(CInt::cast_as(index)) }
}
pub const fn cget_mut(&mut self, index: &Ids) -> &mut T {
let () = Self::ASSERT;
let index: Ids::NatureInt = unsafe { transmute_copy(index) };
unsafe { &mut *(&raw mut self.0 as *mut T).add(CInt::cast_as(index)) }
}
}
impl<Ids0: MapEnum<Discriminant: Clone>, Ids1: MapEnum, T> EnumMap<Ids0, EnumMap<Ids1, T>> {
pub fn map_new2(mut map: impl FnMut(Ids0::Discriminant, Ids1::Discriminant) -> T) -> Self {
let mut out = MaybeUninit::<Self>::uninit();
let buf = unsafe { out.assume_init_mut() }.0.as_mut();
for ids0 in Ids0::iter() {
for ids1 in Ids1::iter() {
let ids0 = ids0.clone();
unsafe { core::ptr::write(buf.get_unchecked_mut(ids0.get_index()).0.as_mut().get_unchecked_mut(ids1.get_index()), map(ids0, ids1)) };
}
}
unsafe { out.assume_init() }
}
}
impl<Ids: MapEnum, T> AsRef<<Ids::Array as Array>::Map<T>> for EnumMap<Ids, T> {
fn as_ref(&self) -> &<Ids::Array as Array>::Map<T> {
&self.0
}
}
impl<Ids: MapEnum, T> AsMut<<Ids::Array as Array>::Map<T>> for EnumMap<Ids, T> {
fn as_mut(&mut self) -> &mut <Ids::Array as Array>::Map<T> {
&mut self.0
}
}
impl<Ids: MapEnum, T> core::ops::Index<Ids> for EnumMap<Ids, T> {
type Output = T;
fn index(&self, index: Ids) -> &Self::Output {
&self.0.as_ref()[index.get_index()]
}
}
impl<Ids: MapEnum, T> core::ops::IndexMut<Ids> for EnumMap<Ids, T> {
fn index_mut(&mut self, index: Ids) -> &mut Self::Output {
&mut self.0.as_mut()[index.get_index()]
}
}
impl<'i, Ids: MapEnum, T> core::ops::Index<&'i Ids> for EnumMap<Ids, T> {
type Output = T;
fn index(&self, index: &'i Ids) -> &Self::Output {
&self.0.as_ref()[index.get_index()]
}
}
impl<'i, Ids: MapEnum, T> core::ops::IndexMut<&'i Ids> for EnumMap<Ids, T> {
fn index_mut(&mut self, index: &'i Ids) -> &mut Self::Output {
&mut self.0.as_mut()[index.get_index()]
}
}
impl<Ids: MapEnum<Array: Array<Map<T>: Debug>>, T> Debug for EnumMap<Ids, T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "EnumMap<{}>(", core::any::type_name::<Ids>())?;
self.0.fmt(f)?;
write!(f, ")")
}
}
impl<Ids: MapEnum<Array: Array<Map<T>: Copy>>, T> Copy for EnumMap<Ids, T> {}
impl<Ids: MapEnum<Array: Array<Map<T>: Clone>>, T> Clone for EnumMap<Ids, T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<Ids: MapEnum<Array: Array<Map<T>: Default>>, T> Default for EnumMap<Ids, T> {
fn default() -> Self {
Self(Default::default())
}
}
impl<Ids: MapEnum<Array: Array<Map<T>: ConstDefault>>, T> ConstDefault for EnumMap<Ids, T> {
const DEFAULT: Self = Self(ConstDefault::DEFAULT);
}
unsafe impl<Ids: MapEnum<Array: Array<Map<T>: Zeroable>>, T> Zeroable for EnumMap<Ids, T> {}
unsafe impl<Ids: MapEnum<Array: Array<Map<T>: Pod>> + 'static, T: 'static> Pod for EnumMap<Ids, T> {}
unsafe impl<Ids: MapEnum, T: TransparentWrapper<U>, U> TransparentWrapper<EnumMap<Ids, U>> for EnumMap<Ids, T> {}
impl<Ids: MapEnum<Array: Array<Map<T>: Eq>>, T> Eq for EnumMap<Ids, T> {}
impl<Ids: MapEnum<Array: Array<Map<T>: PartialEq>>, T> PartialEq for EnumMap<Ids, T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<Ids: MapEnum<Array: Array<Map<T>: Ord>>, T> Ord for EnumMap<Ids, T> {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.0.cmp(&other.0)
}
}
impl<Ids: MapEnum<Array: Array<Map<T>: PartialOrd>>, T> PartialOrd for EnumMap<Ids, T> {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.0.partial_cmp(&other.0)
}
}
mod iter;
#[cfg(test)]
mod test {
use crate::{MapEnum, PlainMapEnum, int::PrimaryInt};
#[derive(MapEnum)]
enum Nature {
V0,
V1,
}
#[derive(MapEnum)]
enum Alter {
V0 = 2,
V1,
}
#[derive(MapEnum)]
#[discriminant]
enum Discriminant {
V0,
V1(#[allow(dead_code)] char),
}
#[derive(MapEnum)]
#[discriminant]
enum Generic<T> {
V0(T),
V1,
}
#[derive(MapEnum)]
#[discriminant]
enum GenericWhere<T>
where
T: PrimaryInt,
{
V0(T),
V1,
}
#[test]
fn nature() {
assert_eq!(Nature::V0.get_index(), 0);
assert_eq!(Nature::V1.get_index(), 1);
}
#[test]
fn alter() {
assert_eq!(Alter::V0.get_index(), 0);
assert_eq!(Alter::V1.get_index(), 1);
}
#[test]
fn discriminant() {
assert_eq!(Discriminant::V0.get_index(), 0);
assert_eq!(Discriminant::V1('d').get_index(), 1);
}
#[test]
fn generic() {
assert_eq!(Generic::V0('d').get_index(), 0);
assert_eq!(Generic::<char>::V1.get_index(), 1);
}
#[test]
fn generic_where() {
assert_eq!(GenericWhere::V0(3).get_index(), 0);
assert_eq!(GenericWhere::<u8>::V1.get_index(), 1);
}
}