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

vortex-data / vortex / 16728097825

04 Aug 2025 04:00PM UTC coverage: 48.355% (-35.1%) from 83.429%
16728097825

Pull #4108

github

web-flow
Merge 1b2d27fd8 into 649ba9576
Pull Request #4108: perf[vortex-array]: use all_valid instead of `invalid_count() == 0`

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

11596 existing lines in 378 files now uncovered.

18635 of 38538 relevant lines covered (48.35%)

151786.4 hits per line

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

46.52
/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 {
324,098✔
29
        self.0.len()
324,098✔
30
    }
324,098✔
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> {
50,088✔
39
        FieldNamesIter {
50,088✔
40
            inner: self,
50,088✔
41
            idx: 0,
50,088✔
42
        }
50,088✔
43
    }
50,088✔
44

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

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

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

60
    fn index(&self, index: usize) -> &Self::Output {
12,644✔
61
        &self.0[index]
12,644✔
62
    }
12,644✔
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> {
175,082✔
75
        if self.idx >= self.inner.len() {
175,082✔
76
            return None;
4,340✔
77
        }
170,742✔
78

79
        let i = &self.inner.0[self.idx];
170,742✔
80
        self.idx += 1;
170,742✔
81
        Some(i)
170,742✔
82
    }
175,082✔
83

84
    fn size_hint(&self) -> (usize, Option<usize>) {
2,718✔
85
        let len = self.inner.len() - self.idx;
2,718✔
86
        (len, Some(len))
2,718✔
87
    }
2,718✔
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

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

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

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

117
impl ExactSizeIterator for FieldNamesIntoIter {}
118

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

122
    type IntoIter = FieldNamesIntoIter;
123

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

132
impl From<Vec<FieldName>> for FieldNames {
133
    fn from(value: Vec<FieldName>) -> Self {
9,692✔
134
        Self(value.into())
9,692✔
135
    }
9,692✔
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 {
UNCOV
145
    fn from(value: &[FieldName]) -> Self {
×
UNCOV
146
        Self(Arc::from(value))
×
UNCOV
147
    }
×
148
}
149

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

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

162
impl<F: Into<FieldName>> FromIterator<F> for FieldNames {
163
    fn from_iter<T: IntoIterator<Item = F>>(iter: T) -> Self {
2,936✔
164
        Self(iter.into_iter().map(|v| v.into()).collect())
4,998✔
165
    }
2,936✔
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 {
159,426✔
207
        self.is_nullable().into()
159,426✔
208
    }
159,426✔
209

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

214
        match self {
222,570✔
UNCOV
215
            Null => true,
×
216
            Extension(ext_dtype) => ext_dtype.storage_dtype().is_nullable(),
9,352✔
217
            Bool(n)
23,586✔
218
            | Primitive(_, n)
154,430✔
219
            | Decimal(_, n)
8,536✔
220
            | Utf8(n)
19,330✔
221
            | Binary(n)
4,144✔
222
            | Struct(_, n)
2,966✔
223
            | List(_, n) => matches!(n, Nullable),
213,218✔
224
        }
225
    }
222,570✔
226

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

232
    /// Get a new DType with `Nullability::Nullable` (but otherwise the same as `self`)
233
    pub fn as_nullable(&self) -> Self {
95,854✔
234
        self.with_nullability(Nullability::Nullable)
95,854✔
235
    }
95,854✔
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 {
107,262✔
239
        match self {
107,262✔
UNCOV
240
            Null => Null,
×
241
            Bool(_) => Bool(nullability),
84,944✔
242
            Primitive(p, _) => Primitive(*p, nullability),
19,454✔
243
            Decimal(d, _) => Decimal(*d, nullability),
932✔
244
            Utf8(_) => Utf8(nullability),
1,484✔
UNCOV
245
            Binary(_) => Binary(nullability),
×
UNCOV
246
            Struct(st, _) => Struct(st.clone(), nullability),
×
UNCOV
247
            List(c, _) => List(c.clone(), nullability),
×
248
            Extension(ext) => Extension(Arc::new(ext.with_nullability(nullability))),
448✔
249
        }
250
    }
107,262✔
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 {
516✔
254
        let nullability = self.nullability() | other;
516✔
255
        self.with_nullability(nullability)
516✔
256
    }
516✔
257

258
    /// Check if `self` and `other` are equal, ignoring nullability
259
    pub fn eq_ignore_nullability(&self, other: &Self) -> bool {
136,602✔
260
        match (self, other) {
136,602✔
UNCOV
261
            (Null, Null) => true,
×
262
            (Bool(_), Bool(_)) => true,
7,774✔
263
            (Primitive(lhs_ptype, _), Primitive(rhs_ptype, _)) => lhs_ptype == rhs_ptype,
104,670✔
264
            (Decimal(lhs, _), Decimal(rhs, _)) => lhs == rhs,
2,572✔
265
            (Utf8(_), Utf8(_)) => true,
10,888✔
266
            (Binary(_), Binary(_)) => true,
44✔
267
            (List(lhs_dtype, _), List(rhs_dtype, _)) => lhs_dtype.eq_ignore_nullability(rhs_dtype),
1,376✔
UNCOV
268
            (Struct(lhs_dtype, _), Struct(rhs_dtype, _)) => {
×
UNCOV
269
                (lhs_dtype.names() == rhs_dtype.names())
×
UNCOV
270
                    && (lhs_dtype
×
UNCOV
271
                        .fields()
×
UNCOV
272
                        .zip_eq(rhs_dtype.fields())
×
UNCOV
273
                        .all(|(l, r)| l.eq_ignore_nullability(&r)))
×
274
            }
275
            (Extension(lhs_extdtype), Extension(rhs_extdtype)) => {
9,264✔
276
                lhs_extdtype.as_ref().eq_ignore_nullability(rhs_extdtype)
9,264✔
277
            }
278
            _ => false,
14✔
279
        }
280
    }
136,602✔
281

282
    /// Check if `self` is a `StructDType`
283
    pub fn is_struct(&self) -> bool {
20,016✔
284
        matches!(self, Struct(_, _))
20,016✔
285
    }
20,016✔
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
UNCOV
293
    pub fn is_primitive(&self) -> bool {
×
UNCOV
294
        matches!(self, Primitive(_, _))
×
UNCOV
295
    }
×
296

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

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

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

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

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

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

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

347
    /// Check if `self` is a utf8
348
    pub fn is_utf8(&self) -> bool {
468✔
349
        matches!(self, Utf8(_))
468✔
350
    }
468✔
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> {
5,780✔
364
        match self {
5,780✔
365
            Decimal(decimal, _) => Some(decimal),
5,780✔
366
            _ => None,
×
367
        }
368
    }
5,780✔
369

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

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

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

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

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

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

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

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

© 2026 Coveralls, Inc