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