#![doc = include_str!("../doc/mem.md")]
use core::{
	cell::Cell,
	mem,
};
use funty::Unsigned;
use radium::marker::BitOps;
#[doc = include_str!("../doc/mem/BitRegister.md")]
pub trait BitRegister: Unsigned + BitOps {
	const INDX: u8 = bits_of::<Self>().trailing_zeros() as u8;
	const MASK: u8 = bits_of::<Self>() as u8 - 1;
	const ALL: Self;
}
macro_rules! register {
	($($t:ty),+ $(,)?) => { $(
		impl BitRegister for $t {
			const ALL: Self = !0;
		}
	)+ };
}
register!(u8, u16, u32);
#[cfg(target_pointer_width = "64")]
impl BitRegister for u64 {
	const ALL: Self = !0;
}
register!(usize);
pub const fn bits_of<T>() -> usize {
	core::mem::size_of::<T>().saturating_mul(<u8>::BITS as usize)
}
#[doc = include_str!("../doc/mem/elts.md")]
pub const fn elts<T>(bits: usize) -> usize {
	let width = bits_of::<T>();
	if width == 0 {
		return 0;
	}
	bits / width + (bits % width != 0) as usize
}
#[doc(hidden)]
#[cfg(not(tarpaulin_include))]
pub const fn aligned_to_size<T>() -> bool {
	mem::align_of::<T>() == mem::size_of::<T>()
}
#[doc(hidden)]
#[cfg(not(tarpaulin_include))]
pub const fn layout_eq<T, U>() -> bool {
	mem::align_of::<T>() == mem::align_of::<U>()
		&& mem::size_of::<T>() == mem::size_of::<U>()
}
#[doc(hidden)]
#[repr(transparent)]
#[doc = include_str!("../doc/mem/BitElement.md")]
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct BitElement<T = usize> {
	pub elem: T,
}
macro_rules! element {
	($($size:tt, $bare:ty => $atom:ident);+ $(;)?) => { $(
		impl BitElement<$bare> {
			pub const fn new(elem: $bare) -> Self {
				Self {
					elem,
				}
			}
		}
		impl BitElement<Cell<$bare>> {
			pub const fn new(elem: $bare) -> Self {
				Self {
					elem: Cell::new(elem),
				}
			}
		}
		radium::if_atomic!( if atomic($size) {
			use core::sync::atomic::$atom;
			impl BitElement<$atom> {
				pub const fn new(elem: $bare) -> Self {
					Self {
						elem: <$atom>::new(elem),
					}
				}
			}
		});
	)+ };
}
element! {
	8, u8 => AtomicU8;
	16, u16 => AtomicU16;
	32, u32 => AtomicU32;
}
#[cfg(target_pointer_width = "64")]
element!(64, u64 => AtomicU64);
element!(size, usize => AtomicUsize);
#[cfg(test)]
mod tests {
	use super::*;
	use crate::access::*;
	#[test]
	fn integer_properties() {
		assert!(aligned_to_size::<u8>());
		assert!(aligned_to_size::<BitSafeU8>());
		assert!(layout_eq::<u8, BitSafeU8>());
		assert!(aligned_to_size::<u16>());
		assert!(aligned_to_size::<BitSafeU16>());
		assert!(layout_eq::<u16, BitSafeU16>());
		assert!(aligned_to_size::<u32>());
		assert!(aligned_to_size::<BitSafeU32>());
		assert!(layout_eq::<u32, BitSafeU32>());
		assert!(aligned_to_size::<usize>());
		assert!(aligned_to_size::<BitSafeUsize>());
		assert!(layout_eq::<usize, BitSafeUsize>());
		#[cfg(target_pointer_width = "64")]
		{
			assert!(aligned_to_size::<u64>());
			assert!(aligned_to_size::<BitSafeU64>());
			assert!(layout_eq::<u64, BitSafeU64>());
		}
	}
}