#![allow(unsafe_code)]
use core::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
use core::marker::PhantomData;
use core::{fmt, hash, mem, num, ptr, slice};
use crate::buf::buf;
pub struct Texel<P: ?Sized>(PhantomData<P>);
pub struct IsTransparentWrapper<P, O>(PhantomData<(P, O)>);
pub trait AsTexel {
fn texel() -> Texel<Self>;
}
macro_rules! def_max_align {
(
$($($arch:literal),* = $num:literal),*
) => {
$(
/// A byte-like-type that is aligned to the required max alignment.
///
/// This type does not contain padding and implements `Pod`. Generally, the alignment and size
/// requirement is kept small to avoid overhead.
#[derive(Clone, Copy)]
#[cfg(
any($(target_arch = $arch),*),
)]
#[repr(align($num))]
#[repr(C)]
pub struct MaxAligned(pub(crate) [u8; $num]);
#[cfg(
any($(target_arch = $arch),*),
)]
pub(crate) const MAX_ALIGN: usize = $num;
)*
#[cfg(
not(any(
$(any($(target_arch = $arch),*)),*
)),
)]
#[repr(align(8))]
#[repr(C)]
pub struct MaxAligned(pub(crate) [u8; 8]);
#[cfg(
not(any(
$(any($(target_arch = $arch),*)),*
)),
)]
pub(crate) const MAX_ALIGN: usize = 8;
}
}
def_max_align! {
"x86", "x86_64" = 32,
"arm" = 16,
"aarch64" = 16,
"wasm32" = 16
}
unsafe impl bytemuck::Zeroable for MaxAligned {}
unsafe impl bytemuck::Pod for MaxAligned {}
macro_rules! builtin_texel {
( $name:ty ) => {
impl AsTexel for $name {
fn texel() -> Texel<Self> {
const _: () = {
assert!(Texel::<$name>::check_invariants());
};
unsafe { Texel::new_unchecked() }
}
}
};
}
pub(crate) mod constants {
use super::{AsTexel, MaxAligned, Texel};
macro_rules! constant_texel {
($(($name:ident, $type:ty)),*) => {
$(pub const $name: Texel<$type> = Texel(core::marker::PhantomData) ;
impl AsTexel for $type {
fn texel() -> Texel<Self> {
const _: () = {
assert!(Texel::<$type>::check_invariants());
};
$name
}
}
)*
}
}
constant_texel!(
(I8, i8),
(U8, u8),
(I16, i16),
(U16, u16),
(I32, i32),
(U32, u32),
(F32, f32),
(I64, i64),
(U64, u64),
(F64, f64),
(USIZE, usize),
(ISIZE, isize),
(MAX, MaxAligned)
);
impl<T: AsTexel> AsTexel for [T; 1] {
fn texel() -> Texel<[T; 1]> {
T::texel().array::<1>()
}
}
impl<T: AsTexel> AsTexel for [T; 2] {
fn texel() -> Texel<[T; 2]> {
T::texel().array::<2>()
}
}
impl<T: AsTexel> AsTexel for [T; 3] {
fn texel() -> Texel<[T; 3]> {
T::texel().array::<3>()
}
}
impl<T: AsTexel> AsTexel for [T; 4] {
fn texel() -> Texel<[T; 4]> {
T::texel().array::<4>()
}
}
impl<T: AsTexel> AsTexel for [T; 5] {
fn texel() -> Texel<[T; 5]> {
T::texel().array::<5>()
}
}
impl<T: AsTexel> AsTexel for [T; 6] {
fn texel() -> Texel<[T; 6]> {
T::texel().array::<6>()
}
}
impl<T: AsTexel> AsTexel for [T; 7] {
fn texel() -> Texel<[T; 7]> {
T::texel().array::<7>()
}
}
impl<T: AsTexel> AsTexel for [T; 8] {
fn texel() -> Texel<[T; 8]> {
T::texel().array::<8>()
}
}
impl<T: AsTexel> AsTexel for ::core::num::Wrapping<T> {
fn texel() -> Texel<::core::num::Wrapping<T>> {
T::texel().num_wrapping()
}
}
}
#[cfg(target_arch = "x86")]
mod x64 {
use super::{AsTexel, Texel};
use core::arch::x86;
builtin_texel!(x86::__m128);
builtin_texel!(x86::__m128);
builtin_texel!(x86::__m128d);
builtin_texel!(x86::__m128i);
builtin_texel!(x86::__m256);
builtin_texel!(x86::__m256d);
builtin_texel!(x86::__m256i);
}
#[cfg(target_arch = "x86_64")]
mod x64_64 {
use super::{AsTexel, Texel};
use core::arch::x86_64;
builtin_texel!(x86_64::__m128);
builtin_texel!(x86_64::__m128d);
builtin_texel!(x86_64::__m128i);
builtin_texel!(x86_64::__m256);
builtin_texel!(x86_64::__m256d);
builtin_texel!(x86_64::__m256i);
}
#[cfg(target_arch = "arm")]
mod arm {
}
#[cfg(target_arch = "aarch64")]
mod arm {
use super::{AsTexel, Texel};
use core::arch::aarch64;
builtin_texel!(aarch64::float64x1_t);
builtin_texel!(aarch64::float64x1x2_t);
builtin_texel!(aarch64::float64x1x3_t);
builtin_texel!(aarch64::float64x1x4_t);
builtin_texel!(aarch64::float64x2_t);
builtin_texel!(aarch64::float64x2x2_t);
builtin_texel!(aarch64::float64x2x3_t);
builtin_texel!(aarch64::float64x2x4_t);
}
#[cfg(target_arch = "wasm32")]
mod arm {
use super::{AsTexel, Texel};
use core::arch::wasm32;
builtin_texel!(wasm32::v128);
}
impl<P: bytemuck::Pod> Texel<P> {
pub const fn for_type() -> Option<Self> {
if Texel::<P>::check_invariants() {
Some(Texel(PhantomData))
} else {
None
}
}
}
impl<P, O: bytemuck::TransparentWrapper<P>> IsTransparentWrapper<P, O> {
pub const CONST: Self = IsTransparentWrapper(PhantomData);
}
impl buf {
pub const ALIGNMENT: usize = MAX_ALIGN;
pub fn from_bytes(bytes: &[u8]) -> Option<&Self> {
if bytes.as_ptr() as usize % Self::ALIGNMENT == 0 {
Some(unsafe { &*(bytes as *const [u8] as *const Self) })
} else {
None
}
}
pub fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut Self> {
if bytes.as_ptr() as usize % Self::ALIGNMENT == 0 {
Some(unsafe { &mut *(bytes as *mut [u8] as *mut Self) })
} else {
None
}
}
}
impl<P> Texel<P> {
pub const unsafe fn new_unchecked() -> Self {
debug_assert!(Self::check_invariants());
Texel(PhantomData)
}
pub(crate) const fn check_invariants() -> bool {
mem::align_of::<P>() <= MAX_ALIGN && mem::size_of::<P>() > 0 && !mem::needs_drop::<P>()
}
pub const fn align(self) -> usize {
mem::align_of::<P>()
}
pub const fn size(self) -> usize {
mem::size_of::<P>()
}
pub const fn size_nz(self) -> core::num::NonZeroUsize {
match core::num::NonZeroUsize::new(self.size()) {
None => panic!(""),
Some(num) => num,
}
}
pub const fn array<const N: usize>(self) -> Texel<[P; N]> {
if N == 0 {
panic!()
}
unsafe { Texel::new_unchecked() }
}
pub const fn transparent_wrap<O>(self, _: IsTransparentWrapper<P, O>) -> Texel<O> {
unsafe { Texel::new_unchecked() }
}
pub const fn transparent_unwrap<O>(self, _: IsTransparentWrapper<O, P>) -> Texel<O> {
unsafe { Texel::new_unchecked() }
}
pub const fn num_wrapping(self) -> Texel<num::Wrapping<P>> {
unsafe { Texel::new_unchecked() }
}
}
impl<T, const N: usize> Texel<[T; N]> {
pub const fn array_element(self) -> Texel<T> {
unsafe { Texel::new_unchecked() }
}
}
impl<P> Texel<P> {
pub fn copy_val(self, val: &P) -> P {
unsafe { ptr::read(val) }
}
pub fn to_slice<'buf>(self, buffer: &'buf [MaxAligned]) -> &'buf [P] {
self.cast_buf(buf::new(buffer))
}
pub fn to_mut_slice<'buf>(self, buffer: &'buf mut [MaxAligned]) -> &'buf mut [P] {
self.cast_mut_buf(buf::new_mut(buffer))
}
pub fn try_to_slice<'buf>(self, bytes: &'buf [u8]) -> Option<&'buf [P]> {
if bytes.as_ptr() as usize % mem::align_of::<P>() == 0 {
let len = bytes.len() / mem::size_of::<P>();
Some(unsafe { &*ptr::slice_from_raw_parts(bytes.as_ptr() as *const P, len) })
} else {
None
}
}
pub fn try_to_slice_mut<'buf>(self, bytes: &'buf mut [u8]) -> Option<&'buf [P]> {
if let Some(slice) = self.try_to_slice(bytes) {
let len = slice.len();
Some(unsafe { &*ptr::slice_from_raw_parts_mut(bytes.as_ptr() as *mut P, len) })
} else {
None
}
}
pub fn to_bytes<'buf>(self, texel: &'buf [P]) -> &'buf [u8] {
self.cast_bytes(texel)
}
pub fn to_mut_bytes<'buf>(self, texel: &'buf mut [P]) -> &'buf mut [u8] {
self.cast_mut_bytes(texel)
}
pub(crate) fn cast_buf<'buf>(self, buffer: &'buf buf) -> &'buf [P] {
debug_assert_eq!(buffer.as_ptr() as usize % mem::align_of::<MaxAligned>(), 0);
debug_assert_eq!(buffer.as_ptr() as usize % mem::align_of::<P>(), 0);
unsafe {
slice::from_raw_parts(
buffer.as_ptr() as *const P,
buffer.len() / self.size_nz().get(),
)
}
}
pub(crate) fn cast_mut_buf<'buf>(self, buffer: &'buf mut buf) -> &'buf mut [P] {
debug_assert_eq!(buffer.as_ptr() as usize % mem::align_of::<MaxAligned>(), 0);
debug_assert_eq!(buffer.as_ptr() as usize % mem::align_of::<P>(), 0);
unsafe {
slice::from_raw_parts_mut(
buffer.as_mut_ptr() as *mut P,
buffer.len() / self.size_nz().get(),
)
}
}
pub(crate) fn cast_bytes<'buf>(self, texel: &'buf [P]) -> &'buf [u8] {
unsafe { slice::from_raw_parts(texel.as_ptr() as *const u8, mem::size_of_val(texel)) }
}
pub(crate) fn cast_mut_bytes<'buf>(self, texel: &'buf mut [P]) -> &'buf mut [u8] {
unsafe { slice::from_raw_parts_mut(texel.as_ptr() as *mut u8, mem::size_of_val(texel)) }
}
}
impl<P> Clone for Texel<P> {
fn clone(&self) -> Self {
Texel(PhantomData)
}
}
impl<P> PartialEq for Texel<P> {
fn eq(&self, _: &Self) -> bool {
true
}
}
impl<P> Eq for Texel<P> {}
impl<P> PartialOrd for Texel<P> {
fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
Some(Ordering::Equal)
}
}
impl<P> Ord for Texel<P> {
fn cmp(&self, _: &Self) -> Ordering {
Ordering::Equal
}
}
impl<P> Copy for Texel<P> {}
impl<P> fmt::Debug for Texel<P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Texel")
.field("size", &self.size())
.field("align", &self.align())
.finish()
}
}
impl<P> hash::Hash for Texel<P> {
fn hash<H: hash::Hasher>(&self, _: &mut H) {}
}