1use std::cmp::Ordering;
4use std::fmt::{Debug, Display, Formatter};
5use std::hash::Hash;
6use std::panic::RefUnwindSafe;
7
8use num_traits::bounds::UpperBounded;
9use num_traits::{FromPrimitive, Num, NumCast, ToPrimitive};
10use vortex_error::{VortexError, VortexResult, vortex_err};
11
12use crate::DType;
13use crate::DType::*;
14use crate::half::f16;
15use crate::nullability::Nullability::NonNullable;
16
17#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Hash, prost::Enumeration)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
21#[repr(u8)]
22pub enum PType {
23 U8 = 0,
25 U16 = 1,
27 U32 = 2,
29 U64 = 3,
31 I8 = 4,
33 I16 = 5,
35 I32 = 6,
37 I64 = 7,
39 F16 = 8,
41 F32 = 9,
43 F64 = 10,
45}
46
47pub trait NativePType:
49 Send
50 + Sync
51 + Clone
52 + Copy
53 + Debug
54 + Display
55 + Default
56 + RefUnwindSafe
57 + Num
58 + NumCast
59 + FromPrimitive
60 + ToBytes
61 + TryFromBytes
62 + 'static
63{
64 const PTYPE: PType;
66
67 fn is_nan(self) -> bool;
70
71 fn is_infinite(self) -> bool;
74
75 fn total_compare(self, other: Self) -> Ordering;
77
78 #[inline]
80 fn is_le(self, other: Self) -> bool {
81 self.total_compare(other).is_le()
82 }
83
84 #[inline]
86 fn is_lt(self, other: Self) -> bool {
87 self.total_compare(other).is_lt()
88 }
89
90 #[inline]
92 fn is_ge(self, other: Self) -> bool {
93 self.total_compare(other).is_ge()
94 }
95
96 #[inline]
98 fn is_gt(self, other: Self) -> bool {
99 self.total_compare(other).is_gt()
100 }
101
102 fn is_eq(self, other: Self) -> bool;
104}
105
106macro_rules! native_ptype {
107 ($T:ty, $ptype:tt) => {
108 impl NativePType for $T {
109 const PTYPE: PType = PType::$ptype;
110
111 #[inline]
112 fn is_nan(self) -> bool {
113 false
114 }
115
116 #[inline]
117 fn is_infinite(self) -> bool {
118 false
119 }
120
121 #[inline]
122 fn total_compare(self, other: Self) -> Ordering {
123 self.cmp(&other)
124 }
125
126 #[inline]
127 fn is_eq(self, other: Self) -> bool {
128 self == other
129 }
130 }
131 };
132}
133
134macro_rules! native_float_ptype {
135 ($T:ty, $ptype:tt) => {
136 impl NativePType for $T {
137 const PTYPE: PType = PType::$ptype;
138
139 #[inline]
140 fn is_nan(self) -> bool {
141 <$T>::is_nan(self)
142 }
143
144 #[inline]
145 fn is_infinite(self) -> bool {
146 <$T>::is_infinite(self)
147 }
148
149 #[inline]
150 fn total_compare(self, other: Self) -> Ordering {
151 self.total_cmp(&other)
152 }
153
154 #[inline]
155 fn is_eq(self, other: Self) -> bool {
156 self.to_bits() == other.to_bits()
157 }
158 }
159 };
160}
161
162native_ptype!(u8, U8);
163native_ptype!(u16, U16);
164native_ptype!(u32, U32);
165native_ptype!(u64, U64);
166native_ptype!(i8, I8);
167native_ptype!(i16, I16);
168native_ptype!(i32, I32);
169native_ptype!(i64, I64);
170native_float_ptype!(f16, F16);
171native_float_ptype!(f32, F32);
172native_float_ptype!(f64, F64);
173
174#[macro_export]
176macro_rules! match_each_native_ptype {
177 ($self:expr, | $_:tt $enc:ident | $($body:tt)*) => ({
178 macro_rules! __with__ {( $_ $enc:ident ) => ( $($body)* )}
179 use $crate::PType;
180 use $crate::half::f16;
181 match $self {
182 PType::I8 => __with__! { i8 },
183 PType::I16 => __with__! { i16 },
184 PType::I32 => __with__! { i32 },
185 PType::I64 => __with__! { i64 },
186 PType::U8 => __with__! { u8 },
187 PType::U16 => __with__! { u16 },
188 PType::U32 => __with__! { u32 },
189 PType::U64 => __with__! { u64 },
190 PType::F16 => __with__! { f16 },
191 PType::F32 => __with__! { f32 },
192 PType::F64 => __with__! { f64 },
193 }
194 });
195 ($self:expr,
196 integral: | $_:tt $integral_enc:ident | { $($integral_body:tt)* }
197 floating_point: | $_2:tt $floating_point_enc:ident | { $($floating_point_body:tt)* }
198 ) => ({
199 macro_rules! __with_integer__ {( $_ $integral_enc:ident ) => ( { $($integral_body)* } )}
200 macro_rules! __with_floating_point__ {( $_ $floating_point_enc:ident ) => ( { $($floating_point_body)* } )}
201 use $crate::PType;
202 use $crate::half::f16;
203 match $self {
204 PType::I8 => __with_integer__! { i8 },
205 PType::I16 => __with_integer__! { i16 },
206 PType::I32 => __with_integer__! { i32 },
207 PType::I64 => __with_integer__! { i64 },
208 PType::U8 => __with_integer__! { u8 },
209 PType::U16 => __with_integer__! { u16 },
210 PType::U32 => __with_integer__! { u32 },
211 PType::U64 => __with_integer__! { u64 },
212 PType::F16 => __with_floating_point__! { f16 },
213 PType::F32 => __with_floating_point__! { f32 },
214 PType::F64 => __with_floating_point__! { f64 },
215 }
216 });
217 ($self:expr,
218 unsigned: | $_:tt $unsigned_enc:ident | { $($unsigned_body:tt)* }
219 signed: | $_1:tt $signed_enc:ident | { $($signed_body:tt)* }
220 floating: | $_2:tt $floating_point_enc:ident | { $($floating_point_body:tt)* }
221 ) => ({
222 macro_rules! __with_unsigned__ {( $_ $unsigned_enc:ident ) => ( { $($unsigned_body)* } )}
223 macro_rules! __with_signed__ {( $_ $signed_enc:ident ) => ( { $($signed_body)* } )}
224 macro_rules! __with_floating_point__ {( $_ $floating_point_enc:ident ) => ( { $($floating_point_body)* } )}
225 use $crate::PType;
226 use $crate::half::f16;
227 match $self {
228 PType::U8 => __with_unsigned__! { u8 },
229 PType::U16 => __with_unsigned__! { u16 },
230 PType::U32 => __with_unsigned__! { u32 },
231 PType::U64 => __with_unsigned__! { u64 },
232 PType::I8 => __with_signed__! { i8 },
233 PType::I16 => __with_signed__! { i16 },
234 PType::I32 => __with_signed__! { i32 },
235 PType::I64 => __with_signed__! { i64 },
236 PType::F16 => __with_floating_point__! { f16 },
237 PType::F32 => __with_floating_point__! { f32 },
238 PType::F64 => __with_floating_point__! { f64 },
239 }
240 })
241}
242
243#[macro_export]
245macro_rules! match_each_integer_ptype {
246 ($self:expr, | $_:tt $enc:ident | $($body:tt)*) => ({
247 macro_rules! __with__ {( $_ $enc:ident ) => ( $($body)* )}
248 use $crate::PType;
249 match $self {
250 PType::I8 => __with__! { i8 },
251 PType::I16 => __with__! { i16 },
252 PType::I32 => __with__! { i32 },
253 PType::I64 => __with__! { i64 },
254 PType::U8 => __with__! { u8 },
255 PType::U16 => __with__! { u16 },
256 PType::U32 => __with__! { u32 },
257 PType::U64 => __with__! { u64 },
258 other => panic!("Unsupported ptype {other}")
259 }
260 })
261}
262
263#[macro_export]
265macro_rules! match_each_unsigned_integer_ptype {
266 ($self:expr, | $_:tt $enc:ident | $($body:tt)*) => ({
267 macro_rules! __with__ {( $_ $enc:ident ) => ( $($body)* )}
268 use $crate::PType;
269 match $self {
270 PType::U8 => __with__! { u8 },
271 PType::U16 => __with__! { u16 },
272 PType::U32 => __with__! { u32 },
273 PType::U64 => __with__! { u64 },
274 other => panic!("Unsupported ptype {other}"),
275 }
276 })
277}
278
279#[macro_export]
281macro_rules! match_each_signed_integer_ptype {
282 ($self:expr, | $_:tt $enc:ident | $($body:tt)*) => ({
283 macro_rules! __with__ {( $_ $enc:ident ) => ( $($body)* )}
284 use $crate::PType;
285 match $self {
286 PType::I8 => __with__! { i8 },
287 PType::I16 => __with__! { i16 },
288 PType::I32 => __with__! { i32 },
289 PType::I64 => __with__! { i64 },
290 other => panic!("Unsupported ptype {other}"),
291 }
292 })
293}
294
295#[macro_export]
297macro_rules! match_each_float_ptype {
298 ($self:expr, | $_:tt $enc:ident | $($body:tt)*) => ({
299 macro_rules! __with__ {( $_ $enc:ident ) => ( $($body)* )}
300 use $crate::PType;
301 use vortex_dtype::half::f16;
302 match $self {
303 PType::F16 => __with__! { f16 },
304 PType::F32 => __with__! { f32 },
305 PType::F64 => __with__! { f64 },
306 other => panic!("Unsupported ptype {other}"),
307 }
308 })
309}
310
311#[macro_export]
315macro_rules! match_each_native_simd_ptype {
316 ($self:expr, | $_:tt $enc:ident | $($body:tt)*) => ({
317 macro_rules! __with__ {( $_ $enc:ident ) => ( $($body)* )}
318 use $crate::PType;
319 match $self {
320 PType::I8 => __with__! { i8 },
321 PType::I16 => __with__! { i16 },
322 PType::I32 => __with__! { i32 },
323 PType::I64 => __with__! { i64 },
324 PType::U8 => __with__! { u8 },
325 PType::U16 => __with__! { u16 },
326 PType::U32 => __with__! { u32 },
327 PType::U64 => __with__! { u64 },
328 PType::F16 => panic!("f16 does not implement simd::SimdElement"),
329 PType::F32 => __with__! { f32 },
330 PType::F64 => __with__! { f64 },
331 }
332 })
333}
334
335impl PType {
336 pub const fn is_unsigned_int(self) -> bool {
338 matches!(self, Self::U8 | Self::U16 | Self::U32 | Self::U64)
339 }
340
341 pub const fn is_signed_int(self) -> bool {
343 matches!(self, Self::I8 | Self::I16 | Self::I32 | Self::I64)
344 }
345
346 pub const fn is_int(self) -> bool {
349 self.is_unsigned_int() || self.is_signed_int()
350 }
351
352 pub const fn is_float(self) -> bool {
354 matches!(self, Self::F16 | Self::F32 | Self::F64)
355 }
356
357 pub const fn byte_width(&self) -> usize {
359 match_each_native_ptype!(self, |$T| std::mem::size_of::<$T>())
360 }
361
362 pub const fn bit_width(&self) -> usize {
364 self.byte_width() * 8
365 }
366
367 pub fn max_value_as_u64(&self) -> u64 {
370 match_each_native_ptype!(self, |$T| <$T as UpperBounded>::max_value().to_u64().unwrap_or(u64::MAX))
371 }
372
373 pub const fn to_signed(self) -> Self {
375 match self {
376 Self::U8 => Self::I8,
377 Self::U16 => Self::I16,
378 Self::U32 => Self::I32,
379 Self::U64 => Self::I64,
380 _ => self,
381 }
382 }
383
384 pub const fn to_unsigned(self) -> Self {
387 match self {
388 Self::I8 => Self::U8,
389 Self::I16 => Self::U16,
390 Self::I32 => Self::U32,
391 Self::I64 => Self::U64,
392 _ => self,
393 }
394 }
395}
396
397impl Display for PType {
398 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
399 match self {
400 Self::U8 => write!(f, "u8"),
401 Self::U16 => write!(f, "u16"),
402 Self::U32 => write!(f, "u32"),
403 Self::U64 => write!(f, "u64"),
404 Self::I8 => write!(f, "i8"),
405 Self::I16 => write!(f, "i16"),
406 Self::I32 => write!(f, "i32"),
407 Self::I64 => write!(f, "i64"),
408 Self::F16 => write!(f, "f16"),
409 Self::F32 => write!(f, "f32"),
410 Self::F64 => write!(f, "f64"),
411 }
412 }
413}
414
415impl TryFrom<&DType> for PType {
416 type Error = VortexError;
417
418 #[inline]
419 fn try_from(value: &DType) -> VortexResult<Self> {
420 match value {
421 Primitive(p, _) => Ok(*p),
422 _ => Err(vortex_err!("Cannot convert DType {} into PType", value)),
423 }
424 }
425}
426
427impl From<PType> for &DType {
428 fn from(item: PType) -> Self {
429 match item {
431 PType::I8 => &Primitive(PType::I8, NonNullable),
432 PType::I16 => &Primitive(PType::I16, NonNullable),
433 PType::I32 => &Primitive(PType::I32, NonNullable),
434 PType::I64 => &Primitive(PType::I64, NonNullable),
435 PType::U8 => &Primitive(PType::U8, NonNullable),
436 PType::U16 => &Primitive(PType::U16, NonNullable),
437 PType::U32 => &Primitive(PType::U32, NonNullable),
438 PType::U64 => &Primitive(PType::U64, NonNullable),
439 PType::F16 => &Primitive(PType::F16, NonNullable),
440 PType::F32 => &Primitive(PType::F32, NonNullable),
441 PType::F64 => &Primitive(PType::F64, NonNullable),
442 }
443 }
444}
445
446impl From<PType> for DType {
447 fn from(item: PType) -> Self {
448 Primitive(item, NonNullable)
449 }
450}
451
452pub trait ToBytes: Sized {
454 fn to_le_bytes(&self) -> &[u8];
456}
457
458pub trait TryFromBytes: Sized {
460 fn try_from_le_bytes(bytes: &[u8]) -> VortexResult<Self>;
462}
463
464macro_rules! try_from_bytes {
465 ($T:ty) => {
466 impl ToBytes for $T {
467 #[inline]
468 #[allow(clippy::size_of_in_element_count)]
469 fn to_le_bytes(&self) -> &[u8] {
470 let raw_ptr = self as *const $T as *const u8;
473 unsafe { std::slice::from_raw_parts(raw_ptr, std::mem::size_of::<$T>()) }
474 }
475 }
476
477 impl TryFromBytes for $T {
478 fn try_from_le_bytes(bytes: &[u8]) -> VortexResult<Self> {
479 Ok(<$T>::from_le_bytes(bytes.try_into().map_err(|_| {
480 vortex_err!("Failed to convert bytes into {}", stringify!($T))
481 })?))
482 }
483 }
484 };
485}
486
487try_from_bytes!(u8);
488try_from_bytes!(u16);
489try_from_bytes!(u32);
490try_from_bytes!(u64);
491try_from_bytes!(i8);
492try_from_bytes!(i16);
493try_from_bytes!(i32);
494try_from_bytes!(i64);
495try_from_bytes!(f16);
496try_from_bytes!(f32);
497try_from_bytes!(f64);
498
499#[cfg(test)]
500mod tests {
501 use super::*;
502
503 #[test]
504 fn try_from_bytes() {
505 assert_eq!(u8::try_from_le_bytes(&[0x01]).unwrap(), 0x01);
506 assert_eq!(u16::try_from_le_bytes(&[0x01, 0x02]).unwrap(), 0x0201);
507 assert_eq!(
508 u32::try_from_le_bytes(&[0x01, 0x02, 0x03, 0x04]).unwrap(),
509 0x04030201
510 );
511 assert_eq!(
512 u64::try_from_le_bytes(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]).unwrap(),
513 0x0807060504030201
514 );
515 }
516
517 #[test]
518 fn to_bytes_rt() {
519 assert_eq!(&0x01u8.to_le_bytes(), &[0x01]);
520 assert_eq!(&0x0201u16.to_le_bytes(), &[0x01, 0x02]);
521
522 assert_eq!(u8::try_from_le_bytes(&42_u8.to_le_bytes()).unwrap(), 42);
523 assert_eq!(u16::try_from_le_bytes(&42_u16.to_le_bytes()).unwrap(), 42);
524 assert_eq!(u32::try_from_le_bytes(&42_u32.to_le_bytes()).unwrap(), 42);
525 assert_eq!(u64::try_from_le_bytes(&42_u64.to_le_bytes()).unwrap(), 42);
526 assert_eq!(i8::try_from_le_bytes(&42_i8.to_le_bytes()).unwrap(), 42);
527 assert_eq!(i16::try_from_le_bytes(&42_i16.to_le_bytes()).unwrap(), 42);
528 assert_eq!(i32::try_from_le_bytes(&42_i32.to_le_bytes()).unwrap(), 42);
529 assert_eq!(i64::try_from_le_bytes(&42_i64.to_le_bytes()).unwrap(), 42);
530 assert_eq!(
531 f16::try_from_le_bytes(&f16::from_f32(42.0).to_le_bytes()).unwrap(),
532 f16::from_f32(42.0)
533 );
534 assert_eq!(
535 f32::try_from_le_bytes(&42.0_f32.to_le_bytes()).unwrap(),
536 42.0
537 );
538 assert_eq!(
539 f64::try_from_le_bytes(&42.0_f64.to_le_bytes()).unwrap(),
540 42.0
541 );
542 }
543
544 #[test]
545 fn max_value_u64() {
546 assert_eq!(PType::U8.max_value_as_u64(), u8::MAX as u64);
547 assert_eq!(PType::U16.max_value_as_u64(), u16::MAX as u64);
548 assert_eq!(PType::U32.max_value_as_u64(), u32::MAX as u64);
549 assert_eq!(PType::U64.max_value_as_u64(), u64::MAX);
550 assert_eq!(PType::I8.max_value_as_u64(), i8::MAX as u64);
551 assert_eq!(PType::I16.max_value_as_u64(), i16::MAX as u64);
552 assert_eq!(PType::I32.max_value_as_u64(), i32::MAX as u64);
553 assert_eq!(PType::I64.max_value_as_u64(), i64::MAX as u64);
554 assert_eq!(PType::F16.max_value_as_u64(), 65504); assert_eq!(PType::F32.max_value_as_u64(), u64::MAX);
556 assert_eq!(PType::F64.max_value_as_u64(), u64::MAX);
557 }
558
559 #[test]
560 fn widths() {
561 assert_eq!(PType::U8.byte_width(), 1);
562 assert_eq!(PType::U16.byte_width(), 2);
563 assert_eq!(PType::U32.byte_width(), 4);
564 assert_eq!(PType::U64.byte_width(), 8);
565 assert_eq!(PType::I8.byte_width(), 1);
566 assert_eq!(PType::I16.byte_width(), 2);
567 assert_eq!(PType::I32.byte_width(), 4);
568 assert_eq!(PType::I64.byte_width(), 8);
569 assert_eq!(PType::F16.byte_width(), 2);
570 assert_eq!(PType::F32.byte_width(), 4);
571 assert_eq!(PType::F64.byte_width(), 8);
572
573 assert_eq!(PType::U8.bit_width(), 8);
574 assert_eq!(PType::U16.bit_width(), 16);
575 assert_eq!(PType::U32.bit_width(), 32);
576 assert_eq!(PType::U64.bit_width(), 64);
577 assert_eq!(PType::I8.bit_width(), 8);
578 assert_eq!(PType::I16.bit_width(), 16);
579 assert_eq!(PType::I32.bit_width(), 32);
580 assert_eq!(PType::I64.bit_width(), 64);
581 assert_eq!(PType::F16.bit_width(), 16);
582 assert_eq!(PType::F32.bit_width(), 32);
583 assert_eq!(PType::F64.bit_width(), 64);
584 }
585
586 #[test]
587 fn native_ptype_nan_handling() {
588 let a = f32::NAN;
589 let b = f32::NAN;
590 assert_ne!(a, b);
591 assert!(<f32 as NativePType>::is_nan(a));
592 assert!(<f32 as NativePType>::is_nan(b));
593 assert!(<f32 as NativePType>::is_eq(a, b));
594 assert!(<f32 as NativePType>::total_compare(a, b) == Ordering::Equal);
595 }
596
597 #[test]
598 fn to_signed() {
599 assert_eq!(PType::U8.to_signed(), PType::I8);
600 assert_eq!(PType::U16.to_signed(), PType::I16);
601 assert_eq!(PType::U32.to_signed(), PType::I32);
602 assert_eq!(PType::U64.to_signed(), PType::I64);
603 assert_eq!(PType::I8.to_signed(), PType::I8);
604 assert_eq!(PType::I16.to_signed(), PType::I16);
605 assert_eq!(PType::I32.to_signed(), PType::I32);
606 assert_eq!(PType::I64.to_signed(), PType::I64);
607 assert_eq!(PType::F16.to_signed(), PType::F16);
608 assert_eq!(PType::F32.to_signed(), PType::F32);
609 assert_eq!(PType::F64.to_signed(), PType::F64);
610 }
611
612 #[test]
613 fn to_unsigned() {
614 assert_eq!(PType::U8.to_unsigned(), PType::U8);
615 assert_eq!(PType::U16.to_unsigned(), PType::U16);
616 assert_eq!(PType::U32.to_unsigned(), PType::U32);
617 assert_eq!(PType::U64.to_unsigned(), PType::U64);
618 assert_eq!(PType::I8.to_unsigned(), PType::U8);
619 assert_eq!(PType::I16.to_unsigned(), PType::U16);
620 assert_eq!(PType::I32.to_unsigned(), PType::U32);
621 assert_eq!(PType::I64.to_unsigned(), PType::U64);
622 assert_eq!(PType::F16.to_unsigned(), PType::F16);
623 assert_eq!(PType::F32.to_unsigned(), PType::F32);
624 assert_eq!(PType::F64.to_unsigned(), PType::F64);
625 }
626
627 #[test]
628 fn to_dtype() {
629 assert_eq!(DType::from(PType::U8), Primitive(PType::U8, NonNullable));
630 assert_eq!(DType::from(PType::U16), Primitive(PType::U16, NonNullable));
631 assert_eq!(DType::from(PType::U32), Primitive(PType::U32, NonNullable));
632 assert_eq!(DType::from(PType::U64), Primitive(PType::U64, NonNullable));
633 assert_eq!(DType::from(PType::I8), Primitive(PType::I8, NonNullable));
634 assert_eq!(DType::from(PType::I16), Primitive(PType::I16, NonNullable));
635 assert_eq!(DType::from(PType::I32), Primitive(PType::I32, NonNullable));
636 assert_eq!(DType::from(PType::I64), Primitive(PType::I64, NonNullable));
637 assert_eq!(DType::from(PType::F16), Primitive(PType::F16, NonNullable));
638 assert_eq!(DType::from(PType::F32), Primitive(PType::F32, NonNullable));
639 assert_eq!(DType::from(PType::F64), Primitive(PType::F64, NonNullable));
640 }
641}