• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vortex-data / vortex / 16729059657

04 Aug 2025 04:45PM UTC coverage: 83.417% (-0.3%) from 83.688%
16729059657

Pull #4109

github

web-flow
Merge 824cda705 into 7a35bb974
Pull Request #4109: Revert "feat: implement Cast Kernel everywhere"

13 of 13 new or added lines in 1 file covered. (100.0%)

45 existing lines in 12 files now uncovered.

46430 of 55660 relevant lines covered (83.42%)

449843.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.11
/vortex-dtype/src/dtype.rs
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3

4
use std::fmt::{Debug, Display, Formatter};
5
use std::hash::Hash;
6
use std::ops::Index;
7
use std::sync::Arc;
8

9
use DType::*;
10
use itertools::Itertools;
11
use static_assertions::const_assert_eq;
12
use vortex_error::vortex_panic;
13

14
use crate::decimal::DecimalDType;
15
use crate::nullability::Nullability;
16
use crate::{ExtDType, FieldDType, PType, StructFields};
17

18
/// A name for a field in a struct
19
pub type FieldName = Arc<str>;
20

21
/// An ordered list of field names in a struct
22
#[derive(Clone, PartialEq, Eq, Debug, Default, Hash)]
23
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24
pub struct FieldNames(Arc<[FieldName]>);
25

26
impl FieldNames {
27
    /// Returns the number of elements.
28
    pub fn len(&self) -> usize {
1,008,777✔
29
        self.0.len()
1,008,777✔
30
    }
1,008,777✔
31

32
    /// Returns true if the number of elements is 0.
33
    pub fn is_empty(&self) -> bool {
×
34
        self.len() == 0
×
35
    }
×
36

37
    /// Returns a borrowed iterator over the field names.
38
    pub fn iter(&self) -> impl ExactSizeIterator<Item = &FieldName> {
116,276✔
39
        FieldNamesIter {
116,276✔
40
            inner: self,
116,276✔
41
            idx: 0,
116,276✔
42
        }
116,276✔
43
    }
116,276✔
44

45
    /// Returns a reference to a field name, or None if `index` is out of bounds.
46
    pub fn get(&self, index: usize) -> Option<&FieldName> {
546✔
47
        self.0.get(index)
546✔
48
    }
546✔
49
}
50

51
impl AsRef<[FieldName]> for FieldNames {
52
    fn as_ref(&self) -> &[FieldName] {
1,014✔
53
        &self.0
1,014✔
54
    }
1,014✔
55
}
56

57
impl Index<usize> for FieldNames {
58
    type Output = FieldName;
59

60
    fn index(&self, index: usize) -> &Self::Output {
21,481✔
61
        &self.0[index]
21,481✔
62
    }
21,481✔
63
}
64

65
/// Iterator of references to field names
66
pub struct FieldNamesIter<'a> {
67
    inner: &'a FieldNames,
68
    idx: usize,
69
}
70

71
impl<'a> Iterator for FieldNamesIter<'a> {
72
    type Item = &'a FieldName;
73

74
    fn next(&mut self) -> Option<Self::Item> {
378,324✔
75
        if self.idx >= self.inner.len() {
378,324✔
76
            return None;
31,273✔
77
        }
347,051✔
78

79
        let i = &self.inner.0[self.idx];
347,051✔
80
        self.idx += 1;
347,051✔
81
        Some(i)
347,051✔
82
    }
378,324✔
83

84
    fn size_hint(&self) -> (usize, Option<usize>) {
11,315✔
85
        let len = self.inner.len() - self.idx;
11,315✔
86
        (len, Some(len))
11,315✔
87
    }
11,315✔
88
}
89

90
impl ExactSizeIterator for FieldNamesIter<'_> {}
91

92
/// Owned iterator of field names.
93
pub struct FieldNamesIntoIter {
94
    inner: FieldNames,
95
    idx: usize,
96
}
97

98
impl Iterator for FieldNamesIntoIter {
99
    type Item = FieldName;
100

101
    fn next(&mut self) -> Option<Self::Item> {
3✔
102
        if self.idx >= self.inner.len() {
3✔
103
            return None;
1✔
104
        }
2✔
105

106
        let i = self.inner.0[self.idx].clone();
2✔
107
        self.idx += 1;
2✔
108
        Some(i)
2✔
109
    }
3✔
110

111
    fn size_hint(&self) -> (usize, Option<usize>) {
1✔
112
        let len = self.inner.len() - self.idx;
1✔
113
        (len, Some(len))
1✔
114
    }
1✔
115
}
116

117
impl ExactSizeIterator for FieldNamesIntoIter {}
118

119
impl IntoIterator for FieldNames {
120
    type Item = FieldName;
121

122
    type IntoIter = FieldNamesIntoIter;
123

124
    fn into_iter(self) -> Self::IntoIter {
2✔
125
        FieldNamesIntoIter {
2✔
126
            inner: self,
2✔
127
            idx: 0,
2✔
128
        }
2✔
129
    }
2✔
130
}
131

132
impl From<Vec<FieldName>> for FieldNames {
133
    fn from(value: Vec<FieldName>) -> Self {
36,089✔
134
        Self(value.into())
36,089✔
135
    }
36,089✔
136
}
137

138
impl From<&[&'static str]> for FieldNames {
139
    fn from(value: &[&'static str]) -> Self {
×
140
        Self(value.iter().cloned().map(Arc::from).collect())
×
141
    }
×
142
}
143

144
impl From<&[FieldName]> for FieldNames {
145
    fn from(value: &[FieldName]) -> Self {
117✔
146
        Self(Arc::from(value))
117✔
147
    }
117✔
148
}
149

150
impl<const N: usize> From<[&'static str; N]> for FieldNames {
151
    fn from(value: [&'static str; N]) -> Self {
204,441✔
152
        Self(value.into_iter().map(Arc::from).collect())
204,441✔
153
    }
204,441✔
154
}
155

156
impl<const N: usize> From<[FieldName; N]> for FieldNames {
157
    fn from(value: [FieldName; N]) -> Self {
13✔
158
        Self(value.into())
13✔
159
    }
13✔
160
}
161

162
impl<F: Into<FieldName>> FromIterator<F> for FieldNames {
163
    fn from_iter<T: IntoIterator<Item = F>>(iter: T) -> Self {
7,672✔
164
        Self(iter.into_iter().map(|v| v.into()).collect())
12,844✔
165
    }
7,672✔
166
}
167

168
/// The logical types of elements in Vortex arrays.
169
///
170
/// Vortex arrays preserve a single logical type, while the encodings allow for multiple
171
/// physical ways to encode that type.
172
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
173
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
174
pub enum DType {
175
    /// The logical null type (only has a single value, `null`)
176
    Null,
177
    /// The logical boolean type (`true` or `false` if non-nullable; `true`, `false`, or `null` if nullable)
178
    Bool(Nullability),
179
    /// Primitive, fixed-width numeric types (e.g., `u8`, `i8`, `u16`, `i16`, `u32`, `i32`, `u64`, `i64`, `f32`, `f64`)
180
    Primitive(PType, Nullability),
181
    /// Real numbers with fixed exact precision and scale.
182
    Decimal(DecimalDType, Nullability),
183
    /// UTF-8 strings
184
    Utf8(Nullability),
185
    /// Binary data
186
    Binary(Nullability),
187
    /// A struct is composed of an ordered list of fields, each with a corresponding name and DType
188
    Struct(StructFields, Nullability),
189
    /// A variable-length list type, parameterized by a single element DType
190
    List(Arc<DType>, Nullability),
191
    /// User-defined extension types
192
    Extension(Arc<ExtDType>),
193
}
194

195
#[cfg(not(target_arch = "wasm32"))]
196
const_assert_eq!(size_of::<DType>(), 16);
197

198
#[cfg(target_arch = "wasm32")]
199
const_assert_eq!(size_of::<DType>(), 8);
200

201
impl DType {
202
    /// The default DType for bytes
203
    pub const BYTES: Self = Primitive(PType::U8, Nullability::NonNullable);
204

205
    /// Get the nullability of the DType
206
    pub fn nullability(&self) -> Nullability {
55,522,118✔
207
        self.is_nullable().into()
55,522,118✔
208
    }
55,522,118✔
209

210
    /// Check if the DType is nullable
211
    pub fn is_nullable(&self) -> bool {
66,388,066✔
212
        use crate::nullability::Nullability::*;
213

214
        match self {
66,388,066✔
215
            Null => true,
473,304✔
216
            Extension(ext_dtype) => ext_dtype.storage_dtype().is_nullable(),
129,528✔
217
            Bool(n)
11,929,509✔
218
            | Primitive(_, n)
51,772,991✔
219
            | Decimal(_, n)
399,188✔
220
            | Utf8(n)
1,052,325✔
221
            | Binary(n)
501,823✔
222
            | Struct(_, n)
31,828✔
223
            | List(_, n) => matches!(n, Nullable),
65,785,234✔
224
        }
225
    }
66,388,066✔
226

227
    /// Get a new DType with `Nullability::NonNullable` (but otherwise the same as `self`)
228
    pub fn as_nonnullable(&self) -> Self {
197✔
229
        self.with_nullability(Nullability::NonNullable)
197✔
230
    }
197✔
231

232
    /// Get a new DType with `Nullability::Nullable` (but otherwise the same as `self`)
233
    pub fn as_nullable(&self) -> Self {
3,611,346✔
234
        self.with_nullability(Nullability::Nullable)
3,611,346✔
235
    }
3,611,346✔
236

237
    /// Get a new DType with the given nullability (but otherwise the same as `self`)
238
    pub fn with_nullability(&self, nullability: Nullability) -> Self {
4,153,758✔
239
        match self {
4,153,758✔
240
            Null => Null,
47,853✔
241
            Bool(_) => Bool(nullability),
1,379,884✔
242
            Primitive(p, _) => Primitive(*p, nullability),
2,310,948✔
243
            Decimal(d, _) => Decimal(*d, nullability),
152,995✔
244
            Utf8(_) => Utf8(nullability),
151,634✔
245
            Binary(_) => Binary(nullability),
10,023✔
246
            Struct(st, _) => Struct(st.clone(), nullability),
13,221✔
247
            List(c, _) => List(c.clone(), nullability),
11,583✔
248
            Extension(ext) => Extension(Arc::new(ext.with_nullability(nullability))),
75,617✔
249
        }
250
    }
4,153,758✔
251

252
    /// Union the nullability of this dtype with the other nullability, returning a new dtype.
253
    pub fn union_nullability(&self, other: Nullability) -> Self {
408,339✔
254
        let nullability = self.nullability() | other;
408,339✔
255
        self.with_nullability(nullability)
408,339✔
256
    }
408,339✔
257

258
    /// Check if `self` and `other` are equal, ignoring nullability
259
    pub fn eq_ignore_nullability(&self, other: &Self) -> bool {
44,720,239✔
260
        match (self, other) {
44,720,239✔
261
            (Null, Null) => true,
235,014✔
262
            (Bool(_), Bool(_)) => true,
2,873,479✔
263
            (Primitive(lhs_ptype, _), Primitive(rhs_ptype, _)) => lhs_ptype == rhs_ptype,
38,184,992✔
264
            (Decimal(lhs, _), Decimal(rhs, _)) => lhs == rhs,
1,518,346✔
265
            (Utf8(_), Utf8(_)) => true,
1,315,555✔
266
            (Binary(_), Binary(_)) => true,
15,956✔
267
            (List(lhs_dtype, _), List(rhs_dtype, _)) => lhs_dtype.eq_ignore_nullability(rhs_dtype),
98,720✔
268
            (Struct(lhs_dtype, _), Struct(rhs_dtype, _)) => {
104,052✔
269
                (lhs_dtype.names() == rhs_dtype.names())
104,052✔
270
                    && (lhs_dtype
104,052✔
271
                        .fields()
104,052✔
272
                        .zip_eq(rhs_dtype.fields())
104,052✔
273
                        .all(|(l, r)| l.eq_ignore_nullability(&r)))
215,670✔
274
            }
275
            (Extension(lhs_extdtype), Extension(rhs_extdtype)) => {
373,696✔
276
                lhs_extdtype.as_ref().eq_ignore_nullability(rhs_extdtype)
373,696✔
277
            }
278
            _ => false,
429✔
279
        }
280
    }
44,720,239✔
281

282
    /// Check if `self` is a `StructDType`
283
    pub fn is_struct(&self) -> bool {
55,928✔
284
        matches!(self, Struct(_, _))
55,928✔
285
    }
55,928✔
286

287
    /// Check if `self` is a `ListDType`
288
    pub fn is_list(&self) -> bool {
×
289
        matches!(self, List(_, _))
×
290
    }
×
291

292
    /// Check if `self` is a primitive tpye
293
    pub fn is_primitive(&self) -> bool {
1,326✔
294
        matches!(self, Primitive(_, _))
1,326✔
295
    }
1,326✔
296

297
    /// Returns this DType's `PType` if it is a primitive type, otherwise panics.
298
    pub fn as_ptype(&self) -> PType {
227,309,918✔
299
        match self {
227,309,918✔
300
            Primitive(ptype, _) => *ptype,
227,309,918✔
301
            _ => vortex_panic!("DType is not a primitive type"),
×
302
        }
303
    }
227,309,918✔
304

305
    /// Check if `self` is an unsigned integer
306
    pub fn is_unsigned_int(&self) -> bool {
102,071✔
307
        if let Primitive(ptype, _) = self {
102,071✔
308
            return ptype.is_unsigned_int();
102,071✔
309
        }
×
310
        false
×
311
    }
102,071✔
312

313
    /// Check if `self` is a signed integer
314
    pub fn is_signed_int(&self) -> bool {
11,772✔
315
        if let Primitive(ptype, _) = self {
11,772✔
316
            return ptype.is_signed_int();
11,772✔
317
        }
×
318
        false
×
319
    }
11,772✔
320

321
    /// Check if `self` is an integer (signed or unsigned)
322
    pub fn is_int(&self) -> bool {
1,000,858✔
323
        if let Primitive(ptype, _) = self {
1,000,858✔
324
            return ptype.is_int();
1,000,858✔
325
        }
×
326
        false
×
327
    }
1,000,858✔
328

329
    /// Check if `self` is a floating point number
330
    pub fn is_float(&self) -> bool {
9,906✔
331
        if let Primitive(ptype, _) = self {
9,906✔
332
            return ptype.is_float();
9,906✔
333
        }
×
334
        false
×
335
    }
9,906✔
336

337
    /// Check if `self` is a boolean
338
    pub fn is_boolean(&self) -> bool {
11,926✔
339
        matches!(self, Bool(_))
11,926✔
340
    }
11,926✔
341

342
    /// Check if `self` is a binary
343
    pub fn is_binary(&self) -> bool {
585✔
344
        matches!(self, Binary(_))
585✔
345
    }
585✔
346

347
    /// Check if `self` is a utf8
348
    pub fn is_utf8(&self) -> bool {
5,343✔
349
        matches!(self, Utf8(_))
5,343✔
350
    }
5,343✔
351

352
    /// Check if `self` is an extension type
353
    pub fn is_extension(&self) -> bool {
×
354
        matches!(self, Extension(_))
×
355
    }
×
356

357
    /// Check if `self` is a decimal type
358
    pub fn is_decimal(&self) -> bool {
×
359
        matches!(self, Decimal(..))
×
360
    }
×
361

362
    /// Check returns the inner decimal type if the dtype is a decimal
363
    pub fn as_decimal(&self) -> Option<&DecimalDType> {
12,711✔
364
        match self {
12,711✔
365
            Decimal(decimal, _) => Some(decimal),
12,711✔
366
            _ => None,
×
367
        }
368
    }
12,711✔
369

370
    /// Get the `StructDType` if `self` is a `StructDType`, otherwise `None`
371
    pub fn as_struct(&self) -> Option<&StructFields> {
550,226✔
372
        match self {
550,226✔
373
            Struct(s, _) => Some(s),
549,562✔
374
            _ => None,
664✔
375
        }
376
    }
550,226✔
377

378
    /// Get the inner dtype if `self` is a `ListDType`, otherwise `None`
379
    pub fn as_list_element(&self) -> Option<&Arc<DType>> {
624✔
380
        match self {
624✔
381
            List(s, _) => Some(s),
585✔
382
            _ => None,
39✔
383
        }
384
    }
624✔
385

386
    /// Convenience method for creating a struct dtype
387
    pub fn struct_<I: IntoIterator<Item = (impl Into<FieldName>, impl Into<FieldDType>)>>(
17✔
388
        iter: I,
17✔
389
        nullability: Nullability,
17✔
390
    ) -> Self {
17✔
391
        Struct(StructFields::from_iter(iter), nullability)
17✔
392
    }
17✔
393

394
    /// Convenience method for creating a list dtype
395
    pub fn list(dtype: impl Into<DType>, nullability: Nullability) -> Self {
2✔
396
        List(Arc::new(dtype.into()), nullability)
2✔
397
    }
2✔
398
}
399

400
impl Display for DType {
401
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2,847✔
402
        match self {
2,847✔
403
            Null => write!(f, "null"),
×
404
            Bool(n) => write!(f, "bool{n}"),
117✔
405
            Primitive(pt, n) => write!(f, "{pt}{n}"),
2,184✔
406
            Decimal(dt, n) => write!(f, "{dt}{n}"),
39✔
407
            Utf8(n) => write!(f, "utf8{n}"),
195✔
UNCOV
408
            Binary(n) => write!(f, "binary{n}"),
×
409
            Struct(sdt, n) => write!(
117✔
410
                f,
117✔
411
                "{{{}}}{}",
117✔
412
                sdt.names()
117✔
413
                    .iter()
117✔
414
                    .zip(sdt.fields())
117✔
415
                    .map(|(n, dt)| format!("{n}={dt}"))
273✔
416
                    .join(", "),
117✔
417
                n
418
            ),
419
            List(edt, n) => write!(f, "list({edt}){n}"),
39✔
420
            Extension(ext) => write!(
156✔
421
                f,
156✔
422
                "ext({}, {}{}){}",
156✔
423
                ext.id(),
156✔
424
                ext.storage_dtype()
156✔
425
                    .with_nullability(Nullability::NonNullable),
156✔
426
                ext.metadata()
156✔
427
                    .map(|m| format!(", {m:?}"))
156✔
428
                    .unwrap_or_else(|| "".to_string()),
156✔
429
                ext.storage_dtype().nullability(),
156✔
430
            ),
431
        }
432
    }
2,847✔
433
}
434

435
#[cfg(test)]
436
mod tests {
437
    use super::*;
438

439
    #[test]
440
    fn test_field_names_iter() {
1✔
441
        let names = ["a", "b"];
1✔
442
        let field_names = FieldNames::from(names);
1✔
443
        assert_eq!(field_names.iter().len(), names.len());
1✔
444
        let mut iter = field_names.iter();
1✔
445
        assert_eq!(iter.next(), Some(&"a".into()));
1✔
446
        assert_eq!(iter.next(), Some(&"b".into()));
1✔
447
        assert_eq!(iter.next(), None);
1✔
448
    }
1✔
449

450
    #[test]
451
    fn test_field_names_owned_iter() {
1✔
452
        let names = ["a", "b"];
1✔
453
        let field_names = FieldNames::from(names);
1✔
454
        assert_eq!(field_names.clone().into_iter().len(), names.len());
1✔
455
        let mut iter = field_names.into_iter();
1✔
456
        assert_eq!(iter.next(), Some("a".into()));
1✔
457
        assert_eq!(iter.next(), Some("b".into()));
1✔
458
        assert_eq!(iter.next(), None);
1✔
459
    }
1✔
460
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc