vortex_dtype/
ptype.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4//! Physical type definitions and behavior.
5
6use std::cmp::Ordering;
7use std::fmt::{Debug, Display, Formatter};
8use std::hash::Hash;
9use std::panic::RefUnwindSafe;
10
11use num_traits::bounds::UpperBounded;
12use num_traits::{FromPrimitive, Num, NumCast, ToPrimitive, Unsigned};
13use vortex_error::{VortexError, VortexResult, vortex_err};
14
15use crate::DType;
16use crate::half::f16;
17use crate::nullability::Nullability::NonNullable;
18
19/// Physical type enum, represents the in-memory physical layout but might represent a different logical type.
20#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Hash, prost::Enumeration)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
23#[repr(u8)]
24pub enum PType {
25    /// An 8-bit unsigned integer
26    U8 = 0,
27    /// A 16-bit unsigned integer
28    U16 = 1,
29    /// A 32-bit unsigned integer
30    U32 = 2,
31    /// A 64-bit unsigned integer
32    U64 = 3,
33    /// An 8-bit signed integer
34    I8 = 4,
35    /// A 16-bit signed integer
36    I16 = 5,
37    /// A 32-bit signed integer
38    I32 = 6,
39    /// A 64-bit signed integer
40    I64 = 7,
41    /// A 16-bit floating point number
42    F16 = 8,
43    /// A 32-bit floating point number
44    F32 = 9,
45    /// A 64-bit floating point number
46    F64 = 10,
47}
48
49/// A trait for native Rust types that correspond 1:1 to a PType
50pub trait NativePType:
51    Send
52    + Sync
53    + Clone
54    + Copy
55    + Debug
56    + Display
57    + Default
58    + RefUnwindSafe
59    + Num
60    + NumCast
61    + FromPrimitive
62    + ToBytes
63    + TryFromBytes
64    + 'static
65{
66    /// The PType that corresponds to this native type
67    const PTYPE: PType;
68
69    /// Whether this instance (`self`) is NaN
70    /// For integer types, this is always `false`
71    fn is_nan(self) -> bool;
72
73    /// Whether this instance (`self`) is Infinite
74    /// For integer types, this is always `false`
75    fn is_infinite(self) -> bool;
76
77    /// Compare another instance of this type to `self`, providing a total ordering
78    fn total_compare(self, other: Self) -> Ordering;
79
80    /// Test whether self is less than or equal to the other
81    #[inline]
82    fn is_le(self, other: Self) -> bool {
83        self.total_compare(other).is_le()
84    }
85
86    /// Test whether self is less than the other
87    #[inline]
88    fn is_lt(self, other: Self) -> bool {
89        self.total_compare(other).is_lt()
90    }
91
92    /// Test whether self is greater than or equal to the other
93    #[inline]
94    fn is_ge(self, other: Self) -> bool {
95        self.total_compare(other).is_ge()
96    }
97
98    /// Test whether self is greater than the other
99    #[inline]
100    fn is_gt(self, other: Self) -> bool {
101        self.total_compare(other).is_gt()
102    }
103
104    /// Whether another instance of this type (`other`) is bitwise equal to `self`
105    fn is_eq(self, other: Self) -> bool;
106
107    /// Downcast the provided object to a type-specific instance.
108    fn downcast<V: PTypeVisitor + ?Sized>(visitor: &V) -> V::Output<Self>;
109
110    /// Downcast the provided object to a type-specific instance.
111    fn downcast_mut<V: PTypeVisitorMut + ?Sized>(visitor: &mut V) -> V::Output<Self>;
112}
113
114/// A visitor trait for converting a `NativePType` to another parameterized type.
115#[allow(missing_docs)] // Kind of obvious..
116pub trait PTypeVisitor {
117    type Output<T: NativePType>;
118
119    fn as_u8(&self) -> Self::Output<u8>;
120    fn as_u16(&self) -> Self::Output<u16>;
121    fn as_u32(&self) -> Self::Output<u32>;
122    fn as_u64(&self) -> Self::Output<u64>;
123    fn as_i8(&self) -> Self::Output<i8>;
124    fn as_i16(&self) -> Self::Output<i16>;
125    fn as_i32(&self) -> Self::Output<i32>;
126    fn as_i64(&self) -> Self::Output<i64>;
127    fn as_f16(&self) -> Self::Output<f16>;
128    fn as_f32(&self) -> Self::Output<f32>;
129    fn as_f64(&self) -> Self::Output<f64>;
130}
131
132/// Extension trait to provide generic downcasting for [`PTypeVisitor`].
133pub trait PTypeVisitorExt: PTypeVisitor {
134    /// Downcast the object to a specific primitive type.
135    fn as_primitive<T: NativePType>(&self) -> Self::Output<T> {
136        T::downcast(self)
137    }
138}
139
140impl<T: PTypeVisitor + ?Sized> PTypeVisitorExt for T {}
141
142/// A visitor trait for converting a `NativePType` to another mutable parameterized type.
143#[allow(missing_docs)] // Kind of obvious..
144pub trait PTypeVisitorMut {
145    type Output<T: NativePType>;
146
147    fn as_u8(&mut self) -> Self::Output<u8>;
148    fn as_u16(&mut self) -> Self::Output<u16>;
149    fn as_u32(&mut self) -> Self::Output<u32>;
150    fn as_u64(&mut self) -> Self::Output<u64>;
151    fn as_i8(&mut self) -> Self::Output<i8>;
152    fn as_i16(&mut self) -> Self::Output<i16>;
153    fn as_i32(&mut self) -> Self::Output<i32>;
154    fn as_i64(&mut self) -> Self::Output<i64>;
155    fn as_f16(&mut self) -> Self::Output<f16>;
156    fn as_f32(&mut self) -> Self::Output<f32>;
157    fn as_f64(&mut self) -> Self::Output<f64>;
158}
159
160/// Extension trait to provide generic downcasting for [`PTypeVisitorMut`].
161pub trait PTypeVisitorMutExt: PTypeVisitorMut {
162    /// Downcast the object to a specific primitive type.
163    fn as_primitive_mut<T: NativePType>(&mut self) -> Self::Output<T> {
164        T::downcast_mut(self)
165    }
166}
167
168macro_rules! impl_ptype_downcast {
169    ($T:ty) => {
170        #[inline]
171        fn downcast<V: PTypeVisitor + ?Sized>(visitor: &V) -> V::Output<Self> {
172            paste::paste! { visitor.[<as_ $T>]() }
173        }
174
175        #[inline]
176        fn downcast_mut<V: PTypeVisitorMut + ?Sized>(visitor: &mut V) -> V::Output<Self> {
177            paste::paste! { visitor.[<as_ $T>]() }
178        }
179    };
180}
181
182macro_rules! native_ptype {
183    ($T:ty, $ptype:tt) => {
184        impl NativePType for $T {
185            const PTYPE: PType = PType::$ptype;
186
187            #[inline]
188            fn is_nan(self) -> bool {
189                false
190            }
191
192            #[inline]
193            fn is_infinite(self) -> bool {
194                false
195            }
196
197            #[inline]
198            fn total_compare(self, other: Self) -> Ordering {
199                self.cmp(&other)
200            }
201
202            #[inline]
203            fn is_eq(self, other: Self) -> bool {
204                self == other
205            }
206
207            impl_ptype_downcast!($T);
208        }
209    };
210}
211
212impl<T: PTypeVisitorMut + ?Sized> PTypeVisitorMutExt for T {}
213
214macro_rules! native_float_ptype {
215    ($T:ty, $ptype:tt) => {
216        impl NativePType for $T {
217            const PTYPE: PType = PType::$ptype;
218
219            #[inline]
220            fn is_nan(self) -> bool {
221                <$T>::is_nan(self)
222            }
223
224            #[inline]
225            fn is_infinite(self) -> bool {
226                <$T>::is_infinite(self)
227            }
228
229            #[inline]
230            fn total_compare(self, other: Self) -> Ordering {
231                self.total_cmp(&other)
232            }
233
234            #[inline]
235            fn is_eq(self, other: Self) -> bool {
236                self.to_bits() == other.to_bits()
237            }
238
239            impl_ptype_downcast!($T);
240        }
241    };
242}
243
244native_ptype!(u8, U8);
245native_ptype!(u16, U16);
246native_ptype!(u32, U32);
247native_ptype!(u64, U64);
248native_ptype!(i8, I8);
249native_ptype!(i16, I16);
250native_ptype!(i32, I32);
251native_ptype!(i64, I64);
252native_float_ptype!(f16, F16);
253native_float_ptype!(f32, F32);
254native_float_ptype!(f64, F64);
255
256/// Macro to match over each PType, binding the corresponding native type (from `NativePType`)
257#[macro_export]
258macro_rules! match_each_native_ptype {
259    (
260        $self:expr,integral: |
261        $integral_enc:ident |
262        $intbody:block,floating: |
263        $floating_point_enc:ident |
264        $floatbody:block
265    ) => {{
266        use $crate::PType;
267        use $crate::half::f16;
268        match $self {
269            PType::I8 => {
270                type $integral_enc = i8;
271                $intbody
272            }
273            PType::I16 => {
274                type $integral_enc = i16;
275                $intbody
276            }
277            PType::I32 => {
278                type $integral_enc = i32;
279                $intbody
280            }
281            PType::I64 => {
282                type $integral_enc = i64;
283                $intbody
284            }
285            PType::U8 => {
286                type $integral_enc = u8;
287                $intbody
288            }
289            PType::U16 => {
290                type $integral_enc = u16;
291                $intbody
292            }
293            PType::U32 => {
294                type $integral_enc = u32;
295                $intbody
296            }
297            PType::U64 => {
298                type $integral_enc = u64;
299                $intbody
300            }
301            PType::F16 => {
302                type $floating_point_enc = f16;
303                $floatbody
304            }
305            PType::F32 => {
306                type $floating_point_enc = f32;
307                $floatbody
308            }
309            PType::F64 => {
310                type $floating_point_enc = f64;
311                $floatbody
312            }
313        }
314    }};
315    (
316        $self:expr,unsigned: |
317        $unsigned_enc:ident |
318        $unsigned_body:block,signed: |
319        $signed_enc:ident |
320        $signed_body:block,floating: |
321        $floating_point_enc:ident |
322        $floating_point_body:block
323    ) => {{
324        use $crate::PType;
325        use $crate::half::f16;
326        match $self {
327            PType::U8 => {
328                type $unsigned_enc = u8;
329                $unsigned_body
330            }
331            PType::U16 => {
332                type $unsigned_enc = u16;
333                $unsigned_body
334            }
335            PType::U32 => {
336                type $unsigned_enc = u32;
337                $unsigned_body
338            }
339            PType::U64 => {
340                type $unsigned_enc = u64;
341                $unsigned_body
342            }
343            PType::I8 => {
344                type $signed_enc = i8;
345                $signed_body
346            }
347            PType::I16 => {
348                type $signed_enc = i16;
349                $signed_body
350            }
351            PType::I32 => {
352                type $signed_enc = i32;
353                $signed_body
354            }
355            PType::I64 => {
356                type $signed_enc = i64;
357                $signed_body
358            }
359            PType::F16 => {
360                type $floating_point_enc = f16;
361                $floating_point_body
362            }
363            PType::F32 => {
364                type $floating_point_enc = f32;
365                $floating_point_body
366            }
367            PType::F64 => {
368                type $floating_point_enc = f64;
369                $floating_point_body
370            }
371        }
372    }};
373    ($self:expr, | $tname:ident | $body:block) => {{
374        use $crate::PType;
375        use $crate::half::f16;
376        match $self {
377            PType::I8 => {
378                type $tname = i8;
379                $body
380            }
381            PType::I16 => {
382                type $tname = i16;
383                $body
384            }
385            PType::I32 => {
386                type $tname = i32;
387                $body
388            }
389            PType::I64 => {
390                type $tname = i64;
391                $body
392            }
393            PType::U8 => {
394                type $tname = u8;
395                $body
396            }
397            PType::U16 => {
398                type $tname = u16;
399                $body
400            }
401            PType::U32 => {
402                type $tname = u32;
403                $body
404            }
405            PType::U64 => {
406                type $tname = u64;
407                $body
408            }
409            PType::F16 => {
410                type $tname = f16;
411                $body
412            }
413            PType::F32 => {
414                type $tname = f32;
415                $body
416            }
417            PType::F64 => {
418                type $tname = f64;
419                $body
420            }
421        }
422    }};
423}
424
425/// Macro to match over each integer PType, binding the corresponding native type (from `NativePType`)
426#[macro_export]
427macro_rules! match_each_integer_ptype {
428    ($self:expr, | $enc:ident | $body:block) => {{
429        use $crate::PType;
430        match $self {
431            PType::I8 => {
432                type $enc = i8;
433                $body
434            }
435            PType::I16 => {
436                type $enc = i16;
437                $body
438            }
439            PType::I32 => {
440                type $enc = i32;
441                $body
442            }
443            PType::I64 => {
444                type $enc = i64;
445                $body
446            }
447            PType::U8 => {
448                type $enc = u8;
449                $body
450            }
451            PType::U16 => {
452                type $enc = u16;
453                $body
454            }
455            PType::U32 => {
456                type $enc = u32;
457                $body
458            }
459            PType::U64 => {
460                type $enc = u64;
461                $body
462            }
463            other => panic!("Unsupported ptype {other}"),
464        }
465    }};
466}
467
468/// Macro to match over each unsigned integer type, binding the corresponding native type (from `NativePType`)
469#[macro_export]
470macro_rules! match_each_unsigned_integer_ptype {
471    ($self:expr, | $enc:ident | $body:block) => {{
472        use $crate::PType;
473        match $self {
474            PType::U8 => {
475                type $enc = u8;
476                $body
477            }
478            PType::U16 => {
479                type $enc = u16;
480                $body
481            }
482            PType::U32 => {
483                type $enc = u32;
484                $body
485            }
486            PType::U64 => {
487                type $enc = u64;
488                $body
489            }
490            other => panic!("Unsupported ptype {other}"),
491        }
492    }};
493}
494
495/// Macro to match over each signed integer type, binding the corresponding native type (from `NativePType`)
496#[macro_export]
497macro_rules! match_each_signed_integer_ptype {
498    ($self:expr, | $enc:ident | $body:block) => {{
499        use $crate::PType;
500        match $self {
501            PType::I8 => {
502                type $enc = i8;
503                $body
504            }
505            PType::I16 => {
506                type $enc = i16;
507                $body
508            }
509            PType::I32 => {
510                type $enc = i32;
511                $body
512            }
513            PType::I64 => {
514                type $enc = i64;
515                $body
516            }
517            other => panic!("Unsupported ptype {other}"),
518        }
519    }};
520}
521
522/// Macro to match over each floating point type, binding the corresponding native type (from `NativePType`)
523#[macro_export]
524macro_rules! match_each_float_ptype {
525    ($self:expr, | $enc:ident | $body:block) => {{
526        use vortex_dtype::half::f16;
527        use $crate::PType;
528        match $self {
529            PType::F16 => {
530                type $enc = f16;
531                $body
532            }
533            PType::F32 => {
534                type $enc = f32;
535                $body
536            }
537            PType::F64 => {
538                type $enc = f64;
539                $body
540            }
541            other => panic!("Unsupported ptype {other}"),
542        }
543    }};
544}
545
546/// Macro to match over each SIMD capable `PType`, binding the corresponding native type (from `NativePType`)
547///
548/// Note: The match will panic in case of `PType::F16`.
549#[macro_export]
550macro_rules! match_each_native_simd_ptype {
551    ($self:expr, | $enc:ident | $body:block) => {{
552        use $crate::PType;
553        match $self {
554            PType::I8 => {
555                type $enc = i8;
556                $body
557            }
558            PType::I16 => {
559                type $enc = i16;
560                $body
561            }
562            PType::I32 => {
563                type $enc = i32;
564                $body
565            }
566            PType::I64 => {
567                type $enc = i64;
568                $body
569            }
570            PType::U8 => {
571                type $enc = u8;
572                $body
573            }
574            PType::U16 => {
575                type $enc = u16;
576                $body
577            }
578            PType::U32 => {
579                type $enc = u32;
580                $body
581            }
582            PType::U64 => {
583                type $enc = u64;
584                $body
585            }
586            PType::F16 => panic!("f16 does not implement simd::SimdElement"),
587            PType::F32 => {
588                type $enc = f32;
589                $body
590            }
591            PType::F64 => {
592                type $enc = f64;
593                $body
594            }
595        }
596    }};
597}
598
599impl PType {
600    /// Returns `true` iff this PType is an unsigned integer type
601    #[inline]
602    pub const fn is_unsigned_int(self) -> bool {
603        matches!(self, Self::U8 | Self::U16 | Self::U32 | Self::U64)
604    }
605
606    /// Returns `true` iff this PType is a signed integer type
607    #[inline]
608    pub const fn is_signed_int(self) -> bool {
609        matches!(self, Self::I8 | Self::I16 | Self::I32 | Self::I64)
610    }
611
612    /// Returns `true` iff this PType is an integer type
613    /// Equivalent to `self.is_unsigned_int() || self.is_signed_int()`
614    #[inline]
615    pub const fn is_int(self) -> bool {
616        self.is_unsigned_int() || self.is_signed_int()
617    }
618
619    /// Returns `true` iff this PType is a floating point type
620    #[inline]
621    pub const fn is_float(self) -> bool {
622        matches!(self, Self::F16 | Self::F32 | Self::F64)
623    }
624
625    /// Returns the number of bytes in this PType
626    #[inline]
627    pub const fn byte_width(&self) -> usize {
628        match_each_native_ptype!(self, |T| { size_of::<T>() })
629    }
630
631    /// Returns the number of bits in this PType
632    #[inline]
633    pub const fn bit_width(&self) -> usize {
634        self.byte_width() * 8
635    }
636
637    /// Returns the maximum value of this PType if it is an integer type
638    /// Returns `u64::MAX` if the value is too large to fit in a `u64`
639    #[inline]
640    pub fn max_value_as_u64(&self) -> u64 {
641        match_each_native_ptype!(self, |T| {
642            <T as UpperBounded>::max_value()
643                .to_u64()
644                .unwrap_or(u64::MAX)
645        })
646    }
647
648    /// Returns the PType that corresponds to the signed version of this PType
649    #[inline]
650    pub const fn to_signed(self) -> Self {
651        match self {
652            Self::U8 => Self::I8,
653            Self::U16 => Self::I16,
654            Self::U32 => Self::I32,
655            Self::U64 => Self::I64,
656            Self::I8 | Self::I16 | Self::I32 | Self::I64 | Self::F16 | Self::F32 | Self::F64 => {
657                self
658            }
659        }
660    }
661
662    /// Returns the PType that corresponds to the unsigned version of this PType
663    /// For floating point types, this will simply return `self`
664    #[inline]
665    pub const fn to_unsigned(self) -> Self {
666        match self {
667            Self::I8 => Self::U8,
668            Self::I16 => Self::U16,
669            Self::I32 => Self::U32,
670            Self::I64 => Self::U64,
671            Self::U8 | Self::U16 | Self::U32 | Self::U64 | Self::F16 | Self::F32 | Self::F64 => {
672                self
673            }
674        }
675    }
676}
677
678impl Display for PType {
679    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
680        match self {
681            Self::U8 => write!(f, "u8"),
682            Self::U16 => write!(f, "u16"),
683            Self::U32 => write!(f, "u32"),
684            Self::U64 => write!(f, "u64"),
685            Self::I8 => write!(f, "i8"),
686            Self::I16 => write!(f, "i16"),
687            Self::I32 => write!(f, "i32"),
688            Self::I64 => write!(f, "i64"),
689            Self::F16 => write!(f, "f16"),
690            Self::F32 => write!(f, "f32"),
691            Self::F64 => write!(f, "f64"),
692        }
693    }
694}
695
696impl TryFrom<&DType> for PType {
697    type Error = VortexError;
698
699    #[inline]
700    fn try_from(value: &DType) -> VortexResult<Self> {
701        if let DType::Primitive(p, _) = value {
702            Ok(*p)
703        } else {
704            Err(vortex_err!("Cannot convert DType {} into PType", value))
705        }
706    }
707}
708
709impl From<PType> for &DType {
710    fn from(item: PType) -> Self {
711        // We expand this match statement so that we can return a static reference.
712        match item {
713            PType::I8 => &DType::Primitive(PType::I8, NonNullable),
714            PType::I16 => &DType::Primitive(PType::I16, NonNullable),
715            PType::I32 => &DType::Primitive(PType::I32, NonNullable),
716            PType::I64 => &DType::Primitive(PType::I64, NonNullable),
717            PType::U8 => &DType::Primitive(PType::U8, NonNullable),
718            PType::U16 => &DType::Primitive(PType::U16, NonNullable),
719            PType::U32 => &DType::Primitive(PType::U32, NonNullable),
720            PType::U64 => &DType::Primitive(PType::U64, NonNullable),
721            PType::F16 => &DType::Primitive(PType::F16, NonNullable),
722            PType::F32 => &DType::Primitive(PType::F32, NonNullable),
723            PType::F64 => &DType::Primitive(PType::F64, NonNullable),
724        }
725    }
726}
727
728impl From<PType> for DType {
729    fn from(item: PType) -> Self {
730        DType::Primitive(item, NonNullable)
731    }
732}
733
734/// A trait for types that can be converted to a little-endian byte slice
735pub trait ToBytes: Sized {
736    /// Returns a slice of this type's bytes in little-endian order
737    fn to_le_bytes(&self) -> &[u8];
738}
739
740/// A trait for types that can be converted from a little-endian byte slice
741pub trait TryFromBytes: Sized {
742    /// Attempts to convert a slice of bytes in little-endian order to this type
743    fn try_from_le_bytes(bytes: &[u8]) -> VortexResult<Self>;
744}
745
746macro_rules! try_from_bytes {
747    ($T:ty) => {
748        impl ToBytes for $T {
749            #[inline]
750            #[allow(clippy::size_of_in_element_count)]
751            fn to_le_bytes(&self) -> &[u8] {
752                // NOTE(ngates): this assumes the platform is little-endian. Currently enforced
753                //  with a flag cfg(target_endian = "little")
754                let raw_ptr = self as *const $T as *const u8;
755                unsafe { std::slice::from_raw_parts(raw_ptr, std::mem::size_of::<$T>()) }
756            }
757        }
758
759        impl TryFromBytes for $T {
760            fn try_from_le_bytes(bytes: &[u8]) -> VortexResult<Self> {
761                Ok(<$T>::from_le_bytes(bytes.try_into().map_err(|_| {
762                    vortex_err!("Failed to convert bytes into {}", stringify!($T))
763                })?))
764            }
765        }
766    };
767}
768
769try_from_bytes!(u8);
770try_from_bytes!(u16);
771try_from_bytes!(u32);
772try_from_bytes!(u64);
773try_from_bytes!(i8);
774try_from_bytes!(i16);
775try_from_bytes!(i32);
776try_from_bytes!(i64);
777try_from_bytes!(f16);
778try_from_bytes!(f32);
779try_from_bytes!(f64);
780
781/// A trait that allows conversion from a PType to its physical representation (i.e., unsigned)
782pub trait PhysicalPType: NativePType {
783    /// The physical type that corresponds to this native type.
784    type Physical: NativePType + Unsigned;
785}
786
787macro_rules! physical_ptype {
788    ($T:ty, $U:ty) => {
789        impl PhysicalPType for $T {
790            type Physical = $U;
791        }
792    };
793}
794
795physical_ptype!(i8, u8);
796physical_ptype!(i16, u16);
797physical_ptype!(i32, u32);
798physical_ptype!(i64, u64);
799physical_ptype!(u8, u8);
800physical_ptype!(u16, u16);
801physical_ptype!(u32, u32);
802physical_ptype!(u64, u64);
803
804#[cfg(test)]
805mod tests {
806    use super::*;
807
808    #[test]
809    fn try_from_bytes() {
810        assert_eq!(u8::try_from_le_bytes(&[0x01]).unwrap(), 0x01);
811        assert_eq!(u16::try_from_le_bytes(&[0x01, 0x02]).unwrap(), 0x0201);
812        assert_eq!(
813            u32::try_from_le_bytes(&[0x01, 0x02, 0x03, 0x04]).unwrap(),
814            0x04030201
815        );
816        assert_eq!(
817            u64::try_from_le_bytes(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]).unwrap(),
818            0x0807060504030201
819        );
820    }
821
822    #[test]
823    fn to_bytes_rt() {
824        assert_eq!(&0x01u8.to_le_bytes(), &[0x01]);
825        assert_eq!(&0x0201u16.to_le_bytes(), &[0x01, 0x02]);
826
827        assert_eq!(u8::try_from_le_bytes(&42_u8.to_le_bytes()).unwrap(), 42);
828        assert_eq!(u16::try_from_le_bytes(&42_u16.to_le_bytes()).unwrap(), 42);
829        assert_eq!(u32::try_from_le_bytes(&42_u32.to_le_bytes()).unwrap(), 42);
830        assert_eq!(u64::try_from_le_bytes(&42_u64.to_le_bytes()).unwrap(), 42);
831        assert_eq!(i8::try_from_le_bytes(&42_i8.to_le_bytes()).unwrap(), 42);
832        assert_eq!(i16::try_from_le_bytes(&42_i16.to_le_bytes()).unwrap(), 42);
833        assert_eq!(i32::try_from_le_bytes(&42_i32.to_le_bytes()).unwrap(), 42);
834        assert_eq!(i64::try_from_le_bytes(&42_i64.to_le_bytes()).unwrap(), 42);
835        assert_eq!(
836            f16::try_from_le_bytes(&f16::from_f32(42.0).to_le_bytes()).unwrap(),
837            f16::from_f32(42.0)
838        );
839        assert_eq!(
840            f32::try_from_le_bytes(&42.0_f32.to_le_bytes()).unwrap(),
841            42.0
842        );
843        assert_eq!(
844            f64::try_from_le_bytes(&42.0_f64.to_le_bytes()).unwrap(),
845            42.0
846        );
847    }
848
849    #[test]
850    fn max_value_u64() {
851        assert_eq!(PType::U8.max_value_as_u64(), u8::MAX as u64);
852        assert_eq!(PType::U16.max_value_as_u64(), u16::MAX as u64);
853        assert_eq!(PType::U32.max_value_as_u64(), u32::MAX as u64);
854        assert_eq!(PType::U64.max_value_as_u64(), u64::MAX);
855        assert_eq!(PType::I8.max_value_as_u64(), i8::MAX as u64);
856        assert_eq!(PType::I16.max_value_as_u64(), i16::MAX as u64);
857        assert_eq!(PType::I32.max_value_as_u64(), i32::MAX as u64);
858        assert_eq!(PType::I64.max_value_as_u64(), i64::MAX as u64);
859        assert_eq!(PType::F16.max_value_as_u64(), 65504); // f16 is a weird type...
860        assert_eq!(PType::F32.max_value_as_u64(), u64::MAX);
861        assert_eq!(PType::F64.max_value_as_u64(), u64::MAX);
862    }
863
864    #[test]
865    fn widths() {
866        assert_eq!(PType::U8.byte_width(), 1);
867        assert_eq!(PType::U16.byte_width(), 2);
868        assert_eq!(PType::U32.byte_width(), 4);
869        assert_eq!(PType::U64.byte_width(), 8);
870        assert_eq!(PType::I8.byte_width(), 1);
871        assert_eq!(PType::I16.byte_width(), 2);
872        assert_eq!(PType::I32.byte_width(), 4);
873        assert_eq!(PType::I64.byte_width(), 8);
874        assert_eq!(PType::F16.byte_width(), 2);
875        assert_eq!(PType::F32.byte_width(), 4);
876        assert_eq!(PType::F64.byte_width(), 8);
877
878        assert_eq!(PType::U8.bit_width(), 8);
879        assert_eq!(PType::U16.bit_width(), 16);
880        assert_eq!(PType::U32.bit_width(), 32);
881        assert_eq!(PType::U64.bit_width(), 64);
882        assert_eq!(PType::I8.bit_width(), 8);
883        assert_eq!(PType::I16.bit_width(), 16);
884        assert_eq!(PType::I32.bit_width(), 32);
885        assert_eq!(PType::I64.bit_width(), 64);
886        assert_eq!(PType::F16.bit_width(), 16);
887        assert_eq!(PType::F32.bit_width(), 32);
888        assert_eq!(PType::F64.bit_width(), 64);
889    }
890
891    #[test]
892    fn native_ptype_nan_handling() {
893        let a = f32::NAN;
894        let b = f32::NAN;
895        assert_ne!(a, b);
896        assert!(<f32 as NativePType>::is_nan(a));
897        assert!(<f32 as NativePType>::is_nan(b));
898        assert!(<f32 as NativePType>::is_eq(a, b));
899        assert!(<f32 as NativePType>::total_compare(a, b) == Ordering::Equal);
900    }
901
902    #[test]
903    fn to_signed() {
904        assert_eq!(PType::U8.to_signed(), PType::I8);
905        assert_eq!(PType::U16.to_signed(), PType::I16);
906        assert_eq!(PType::U32.to_signed(), PType::I32);
907        assert_eq!(PType::U64.to_signed(), PType::I64);
908        assert_eq!(PType::I8.to_signed(), PType::I8);
909        assert_eq!(PType::I16.to_signed(), PType::I16);
910        assert_eq!(PType::I32.to_signed(), PType::I32);
911        assert_eq!(PType::I64.to_signed(), PType::I64);
912        assert_eq!(PType::F16.to_signed(), PType::F16);
913        assert_eq!(PType::F32.to_signed(), PType::F32);
914        assert_eq!(PType::F64.to_signed(), PType::F64);
915    }
916
917    #[test]
918    fn to_unsigned() {
919        assert_eq!(PType::U8.to_unsigned(), PType::U8);
920        assert_eq!(PType::U16.to_unsigned(), PType::U16);
921        assert_eq!(PType::U32.to_unsigned(), PType::U32);
922        assert_eq!(PType::U64.to_unsigned(), PType::U64);
923        assert_eq!(PType::I8.to_unsigned(), PType::U8);
924        assert_eq!(PType::I16.to_unsigned(), PType::U16);
925        assert_eq!(PType::I32.to_unsigned(), PType::U32);
926        assert_eq!(PType::I64.to_unsigned(), PType::U64);
927        assert_eq!(PType::F16.to_unsigned(), PType::F16);
928        assert_eq!(PType::F32.to_unsigned(), PType::F32);
929        assert_eq!(PType::F64.to_unsigned(), PType::F64);
930    }
931
932    #[test]
933    fn to_dtype() {
934        assert_eq!(
935            DType::from(PType::U8),
936            DType::Primitive(PType::U8, NonNullable)
937        );
938        assert_eq!(
939            DType::from(PType::U16),
940            DType::Primitive(PType::U16, NonNullable)
941        );
942        assert_eq!(
943            DType::from(PType::U32),
944            DType::Primitive(PType::U32, NonNullable)
945        );
946        assert_eq!(
947            DType::from(PType::U64),
948            DType::Primitive(PType::U64, NonNullable)
949        );
950        assert_eq!(
951            DType::from(PType::I8),
952            DType::Primitive(PType::I8, NonNullable)
953        );
954        assert_eq!(
955            DType::from(PType::I16),
956            DType::Primitive(PType::I16, NonNullable)
957        );
958        assert_eq!(
959            DType::from(PType::I32),
960            DType::Primitive(PType::I32, NonNullable)
961        );
962        assert_eq!(
963            DType::from(PType::I64),
964            DType::Primitive(PType::I64, NonNullable)
965        );
966        assert_eq!(
967            DType::from(PType::F16),
968            DType::Primitive(PType::F16, NonNullable)
969        );
970        assert_eq!(
971            DType::from(PType::F32),
972            DType::Primitive(PType::F32, NonNullable)
973        );
974        assert_eq!(
975            DType::from(PType::F64),
976            DType::Primitive(PType::F64, NonNullable)
977        );
978    }
979}