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

vortex-data / vortex / 16269921526

14 Jul 2025 02:40PM UTC coverage: 81.518% (+0.05%) from 81.464%
16269921526

Pull #3866

github

web-flow
Merge c0af6ecae into b1a8d1d4f
Pull Request #3866: feat: display

139 of 148 new or added lines in 3 files covered. (93.92%)

87 existing lines in 1 file now uncovered.

46256 of 56743 relevant lines covered (81.52%)

146241.12 hits per line

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

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

4
mod convert;
5
mod display;
6
mod statistics;
7
mod visitor;
8

9
use std::any::Any;
10
use std::fmt::{Debug, Display, Formatter};
11
use std::sync::Arc;
12

13
pub use convert::*;
14
use display::{DisplayArray, DisplayArrayAs, DisplayOptions};
15
pub use visitor::*;
16
use vortex_buffer::ByteBuffer;
17
use vortex_dtype::DType;
18
use vortex_error::{VortexExpect, VortexResult, vortex_bail, vortex_err};
19
use vortex_mask::Mask;
20
use vortex_scalar::Scalar;
21

22
use crate::arrays::{
23
    BoolEncoding, DecimalEncoding, ExtensionEncoding, ListEncoding, NullEncoding,
24
    PrimitiveEncoding, StructEncoding, VarBinEncoding, VarBinViewEncoding,
25
};
26
use crate::builders::ArrayBuilder;
27
use crate::compute::{ComputeFn, Cost, InvocationArgs, Output};
28
use crate::serde::ArrayChildren;
29
use crate::stats::{Precision, Stat, StatsProviderExt, StatsSetRef};
30
use crate::vtable::{
31
    ArrayVTable, CanonicalVTable, ComputeVTable, OperationsVTable, SerdeVTable, VTable,
32
    ValidityVTable, VisitorVTable,
33
};
34
use crate::{Canonical, EncodingId, EncodingRef, SerializeMetadata};
35

36
/// The public API trait for all Vortex arrays.
37
pub trait Array: 'static + private::Sealed + Send + Sync + Debug + ArrayVisitor {
38
    /// Returns the array as a reference to a generic [`Any`] trait object.
39
    fn as_any(&self) -> &dyn Any;
40

41
    /// Returns the array as an [`ArrayRef`].
42
    fn to_array(&self) -> ArrayRef;
43

44
    /// Returns the length of the array.
45
    fn len(&self) -> usize;
46

47
    /// Returns whether the array is empty (has zero rows).
48
    fn is_empty(&self) -> bool {
212,648✔
49
        self.len() == 0
212,648✔
50
    }
212,648✔
51

52
    /// Returns the logical Vortex [`DType`] of the array.
53
    fn dtype(&self) -> &DType;
54

55
    /// Returns the encoding of the array.
56
    fn encoding(&self) -> EncodingRef;
57

58
    /// Returns the encoding ID of the array.
59
    fn encoding_id(&self) -> EncodingId;
60

61
    /// Performs a constant-time slice of the array.
62
    fn slice(&self, start: usize, end: usize) -> VortexResult<ArrayRef>;
63

64
    /// Fetch the scalar at the given index.
65
    fn scalar_at(&self, index: usize) -> VortexResult<Scalar>;
66

67
    /// Return an optimized version of the same array.
68
    ///
69
    /// See [`OperationsVTable::optimize`] for more details.
70
    fn optimize(&self) -> VortexResult<ArrayRef>;
71

72
    /// Display.
73
    fn display(&self) -> DisplayArray
10✔
74
    where
10✔
75
        Self: Sized,
10✔
76
    {
10✔
77
        DisplayArray(self)
10✔
78
    }
10✔
79

80
    /// Display with options.
NEW
UNCOV
81
    fn display_as<'a>(&'a self, options: &'a DisplayOptions) -> DisplayArrayAs<'a>
×
NEW
UNCOV
82
    where
×
NEW
UNCOV
83
        Self: Sized,
×
NEW
UNCOV
84
    {
×
NEW
UNCOV
85
        DisplayArrayAs(self, options)
×
NEW
UNCOV
86
    }
×
87

88
    // /// Format.
89
    // fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result;
90

91
    // /// Format with options.
92
    // fn fmt_as(&self, f: &mut Formatter<'_>, options: &DisplayOptions) -> std::fmt::Result;
93

94
    /// Returns whether the array is of the given encoding.
95
    fn is_encoding(&self, encoding: EncodingId) -> bool {
347,705✔
96
        self.encoding_id() == encoding
347,705✔
97
    }
347,705✔
98

99
    /// Returns whether this array is an arrow encoding.
100
    // TODO(ngates): this shouldn't live here.
101
    fn is_arrow(&self) -> bool {
8,437✔
102
        self.is_encoding(NullEncoding.id())
8,437✔
103
            || self.is_encoding(BoolEncoding.id())
8,437✔
104
            || self.is_encoding(PrimitiveEncoding.id())
7,306✔
105
            || self.is_encoding(VarBinEncoding.id())
5,381✔
106
            || self.is_encoding(VarBinViewEncoding.id())
5,381✔
107
    }
8,437✔
108

109
    /// Whether the array is of a canonical encoding.
110
    // TODO(ngates): this shouldn't live here.
111
    fn is_canonical(&self) -> bool {
50,351✔
112
        self.is_encoding(NullEncoding.id())
50,351✔
113
            || self.is_encoding(BoolEncoding.id())
50,351✔
114
            || self.is_encoding(PrimitiveEncoding.id())
48,841✔
115
            || self.is_encoding(DecimalEncoding.id())
33,429✔
116
            || self.is_encoding(StructEncoding.id())
32,118✔
117
            || self.is_encoding(ListEncoding.id())
30,851✔
118
            || self.is_encoding(VarBinViewEncoding.id())
30,849✔
119
            || self.is_encoding(ExtensionEncoding.id())
29,559✔
120
    }
50,351✔
121

122
    /// Returns whether the item at `index` is valid.
123
    fn is_valid(&self, index: usize) -> VortexResult<bool>;
124

125
    /// Returns whether the item at `index` is invalid.
126
    fn is_invalid(&self, index: usize) -> VortexResult<bool>;
127

128
    /// Returns whether all items in the array are valid.
129
    ///
130
    /// This is usually cheaper than computing a precise `valid_count`.
131
    fn all_valid(&self) -> VortexResult<bool>;
132

133
    /// Returns whether the array is all invalid.
134
    ///
135
    /// This is usually cheaper than computing a precise `invalid_count`.
136
    fn all_invalid(&self) -> VortexResult<bool>;
137

138
    /// Returns the number of valid elements in the array.
139
    fn valid_count(&self) -> VortexResult<usize>;
140

141
    /// Returns the number of invalid elements in the array.
142
    fn invalid_count(&self) -> VortexResult<usize>;
143

144
    /// Returns the canonical validity mask for the array.
145
    fn validity_mask(&self) -> VortexResult<Mask>;
146

147
    /// Returns the canonical representation of the array.
148
    fn to_canonical(&self) -> VortexResult<Canonical>;
149

150
    /// Writes the array into the canonical builder.
151
    ///
152
    /// The [`DType`] of the builder must match that of the array.
153
    fn append_to_builder(&self, builder: &mut dyn ArrayBuilder) -> VortexResult<()>;
154

155
    /// Returns the statistics of the array.
156
    // TODO(ngates): change how this works. It's weird.
157
    fn statistics(&self) -> StatsSetRef<'_>;
158

159
    /// Replaces the children of the array with the given array references.
160
    fn with_children(&self, children: &[ArrayRef]) -> VortexResult<ArrayRef>;
161

162
    /// Optionally invoke a kernel for the given compute function.
163
    ///
164
    /// These encoding-specific kernels are independent of kernels registered directly with
165
    /// compute functions using [`ComputeFn::register_kernel`], and are attempted only if none of
166
    /// the function-specific kernels returns a result.
167
    ///
168
    /// This allows encodings the opportunity to generically implement many compute functions
169
    /// that share some property, for example [`ComputeFn::is_elementwise`], without prior
170
    /// knowledge of the function itself, while still allowing users to override the implementation
171
    /// of compute functions for built-in encodings. For an example, see the implementation for
172
    /// chunked arrays.
173
    ///
174
    /// The first input in the [`InvocationArgs`] is always the array itself.
175
    ///
176
    /// Warning: do not call `compute_fn.invoke(args)` directly, as this will result in a recursive
177
    /// call.
178
    fn invoke(&self, compute_fn: &ComputeFn, args: &InvocationArgs)
179
    -> VortexResult<Option<Output>>;
180
}
181

182
impl Array for Arc<dyn Array> {
183
    fn as_any(&self) -> &dyn Any {
557,934✔
184
        self.as_ref().as_any()
557,934✔
185
    }
557,934✔
186

187
    fn to_array(&self) -> ArrayRef {
126,283✔
188
        self.clone()
126,283✔
189
    }
126,283✔
190

191
    fn len(&self) -> usize {
1,054,111✔
192
        self.as_ref().len()
1,054,111✔
193
    }
1,054,111✔
194

195
    fn dtype(&self) -> &DType {
834,373✔
196
        self.as_ref().dtype()
834,373✔
197
    }
834,373✔
198

199
    fn encoding(&self) -> EncodingRef {
18,196✔
200
        self.as_ref().encoding()
18,196✔
201
    }
18,196✔
202

203
    fn encoding_id(&self) -> EncodingId {
308,130✔
204
        self.as_ref().encoding_id()
308,130✔
205
    }
308,130✔
206

207
    fn slice(&self, start: usize, end: usize) -> VortexResult<ArrayRef> {
34,719✔
208
        self.as_ref().slice(start, end)
34,719✔
209
    }
34,719✔
210

211
    fn scalar_at(&self, index: usize) -> VortexResult<Scalar> {
504,637✔
212
        self.as_ref().scalar_at(index)
504,637✔
213
    }
504,637✔
214

UNCOV
215
    fn optimize(&self) -> VortexResult<ArrayRef> {
×
UNCOV
216
        self.as_ref().optimize()
×
UNCOV
217
    }
×
218

219
    // fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
220
    //     self.as_ref().fmt(f)
221
    // }
222

223
    // fn fmt_as(&self, f: &mut Formatter<'_>, options: &DisplayOptions) -> std::fmt::Result {
224
    //     self.as_ref().fmt_as(f, options)
225
    // }
226

227
    fn is_valid(&self, index: usize) -> VortexResult<bool> {
31,110✔
228
        self.as_ref().is_valid(index)
31,110✔
229
    }
31,110✔
230

231
    fn is_invalid(&self, index: usize) -> VortexResult<bool> {
364✔
232
        self.as_ref().is_invalid(index)
364✔
233
    }
364✔
234

235
    fn all_valid(&self) -> VortexResult<bool> {
25,227✔
236
        self.as_ref().all_valid()
25,227✔
237
    }
25,227✔
238

239
    fn all_invalid(&self) -> VortexResult<bool> {
42,831✔
240
        self.as_ref().all_invalid()
42,831✔
241
    }
42,831✔
242

243
    fn valid_count(&self) -> VortexResult<usize> {
2,668✔
244
        self.as_ref().valid_count()
2,668✔
245
    }
2,668✔
246

247
    fn invalid_count(&self) -> VortexResult<usize> {
444✔
248
        self.as_ref().invalid_count()
444✔
249
    }
444✔
250

251
    fn validity_mask(&self) -> VortexResult<Mask> {
6,166✔
252
        self.as_ref().validity_mask()
6,166✔
253
    }
6,166✔
254

255
    fn to_canonical(&self) -> VortexResult<Canonical> {
221,515✔
256
        self.as_ref().to_canonical()
221,515✔
257
    }
221,515✔
258

259
    fn append_to_builder(&self, builder: &mut dyn ArrayBuilder) -> VortexResult<()> {
40,062✔
260
        self.as_ref().append_to_builder(builder)
40,062✔
261
    }
40,062✔
262

263
    fn statistics(&self) -> StatsSetRef<'_> {
396,996✔
264
        self.as_ref().statistics()
396,996✔
265
    }
396,996✔
266

UNCOV
267
    fn with_children(&self, children: &[ArrayRef]) -> VortexResult<ArrayRef> {
×
UNCOV
268
        self.as_ref().with_children(children)
×
UNCOV
269
    }
×
270

271
    fn invoke(
33,465✔
272
        &self,
33,465✔
273
        compute_fn: &ComputeFn,
33,465✔
274
        args: &InvocationArgs,
33,465✔
275
    ) -> VortexResult<Option<Output>> {
33,465✔
276
        self.as_ref().invoke(compute_fn, args)
33,465✔
277
    }
33,465✔
278
}
279

280
/// A reference counted pointer to a dynamic [`Array`] trait object.
281
pub type ArrayRef = Arc<dyn Array>;
282

283
impl ToOwned for dyn Array {
284
    type Owned = ArrayRef;
285

UNCOV
286
    fn to_owned(&self) -> Self::Owned {
×
UNCOV
287
        self.to_array()
×
UNCOV
288
    }
×
289
}
290

291
impl dyn Array + '_ {
292
    /// Returns the array downcast to the given `A`.
293
    pub fn as_<V: VTable>(&self) -> &V::Array {
269✔
294
        self.as_opt::<V>().vortex_expect("Failed to downcast")
269✔
295
    }
269✔
296

297
    /// Returns the array downcast to the given `A`.
298
    pub fn as_opt<V: VTable>(&self) -> Option<&V::Array> {
1,540,473✔
299
        self.as_any()
1,540,473✔
300
            .downcast_ref::<ArrayAdapter<V>>()
1,540,473✔
301
            .map(|array_adapter| &array_adapter.0)
1,540,473✔
302
    }
1,540,473✔
303

304
    /// Is self an array with encoding from vtable `V`.
305
    pub fn is<V: VTable>(&self) -> bool {
9,307✔
306
        self.as_opt::<V>().is_some()
9,307✔
307
    }
9,307✔
308
}
309

310
impl Display for dyn Array {
311
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
830✔
312
        self.fmt_as(f, &DisplayOptions::MetadataOnly)
830✔
313
    }
830✔
314
}
315

316
impl dyn Array + '_ {
317
    /// Total size of the array in bytes, including all children and buffers.
318
    // TODO(ngates): this should return u64
319
    pub fn nbytes(&self) -> usize {
151,321✔
320
        let mut nbytes = 0;
151,321✔
321
        for array in self.depth_first_traversal() {
212,733✔
322
            for buffer in array.buffers() {
269,406✔
323
                nbytes += buffer.len();
269,406✔
324
            }
269,406✔
325
        }
326
        nbytes
151,321✔
327
    }
151,321✔
328
}
329

330
mod private {
331
    use super::*;
332

333
    pub trait Sealed {}
334

335
    impl<V: VTable> Sealed for ArrayAdapter<V> {}
336
    impl Sealed for Arc<dyn Array> {}
337
}
338

339
/// Adapter struct used to lift the [`VTable`] trait into an object-safe [`Array`]
340
/// implementation.
341
///
342
/// Since this is a unit struct with `repr(transparent)`, we are able to turn un-adapted array
343
/// structs into [`dyn Array`] using some cheeky casting inside [`std::ops::Deref`] and
344
/// [`AsRef`]. See the `vtable!` macro for more details.
345
#[repr(transparent)]
346
pub struct ArrayAdapter<V: VTable>(V::Array);
347

348
impl<V: VTable> Debug for ArrayAdapter<V> {
UNCOV
349
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
×
UNCOV
350
        self.0.fmt(f)
×
351
    }
×
352
}
353

354
impl<V: VTable> Array for ArrayAdapter<V> {
355
    fn as_any(&self) -> &dyn Any {
1,455,523✔
356
        self
1,455,523✔
357
    }
1,455,523✔
358

359
    fn to_array(&self) -> ArrayRef {
309,103✔
360
        Arc::new(ArrayAdapter::<V>(self.0.clone()))
309,103✔
361
    }
309,103✔
362

363
    fn len(&self) -> usize {
10,945,872✔
364
        <V::ArrayVTable as ArrayVTable<V>>::len(&self.0)
10,945,872✔
365
    }
10,945,872✔
366

367
    fn dtype(&self) -> &DType {
27,545,173✔
368
        <V::ArrayVTable as ArrayVTable<V>>::dtype(&self.0)
27,545,173✔
369
    }
27,545,173✔
370

371
    fn encoding(&self) -> EncodingRef {
24,352✔
372
        V::encoding(&self.0)
24,352✔
373
    }
24,352✔
374

375
    fn encoding_id(&self) -> EncodingId {
341,248✔
376
        V::encoding(&self.0).id()
341,248✔
377
    }
341,248✔
378

379
    fn slice(&self, start: usize, stop: usize) -> VortexResult<ArrayRef> {
68,222✔
380
        if start == 0 && stop == self.len() {
68,222✔
381
            return Ok(self.to_array());
9,681✔
382
        }
58,541✔
383

58,541✔
384
        if start > self.len() {
58,541✔
UNCOV
385
            vortex_bail!(OutOfBounds: start, 0, self.len());
×
386
        }
58,541✔
387
        if stop > self.len() {
58,541✔
UNCOV
388
            vortex_bail!(OutOfBounds: stop, 0, self.len());
×
389
        }
58,541✔
390
        if start > stop {
58,541✔
UNCOV
391
            vortex_bail!("start ({start}) must be <= stop ({stop})");
×
392
        }
58,541✔
393

58,541✔
394
        if start == stop {
58,541✔
395
            return Ok(Canonical::empty(self.dtype()).into_array());
15✔
396
        }
58,526✔
397

58,526✔
398
        // We know that constant array don't need stats propagation, so we can avoid the overhead of
58,526✔
399
        // computing derived stats and merging them in.
58,526✔
400
        // TODO(ngates): skip the is_constant check here, it can force an expensive compute.
58,526✔
401
        // TODO(ngates): provide a means to slice an array _without_ propagating stats.
58,526✔
402
        let derived_stats = (!self.0.is_constant_opts(Cost::Negligible)).then(|| {
58,526✔
403
            let stats = self.statistics().to_owned();
49,414✔
404

49,414✔
405
            // an array that is not constant can become constant after slicing
49,414✔
406
            let is_constant = stats.get_as::<bool>(Stat::IsConstant);
49,414✔
407
            let is_sorted = stats.get_as::<bool>(Stat::IsSorted);
49,414✔
408
            let is_strict_sorted = stats.get_as::<bool>(Stat::IsStrictSorted);
49,414✔
409

49,414✔
410
            let mut stats = stats.keep_inexact_stats(&[
49,414✔
411
                Stat::Max,
49,414✔
412
                Stat::Min,
49,414✔
413
                Stat::NullCount,
49,414✔
414
                Stat::UncompressedSizeInBytes,
49,414✔
415
            ]);
49,414✔
416

49,414✔
417
            if is_constant == Some(Precision::Exact(true)) {
49,414✔
UNCOV
418
                stats.set(Stat::IsConstant, Precision::exact(true));
×
419
            }
49,414✔
420
            if is_sorted == Some(Precision::Exact(true)) {
49,414✔
421
                stats.set(Stat::IsSorted, Precision::exact(true));
790✔
422
            }
48,624✔
423
            if is_strict_sorted == Some(Precision::Exact(true)) {
49,414✔
424
                stats.set(Stat::IsStrictSorted, Precision::exact(true));
480✔
425
            }
48,934✔
426

427
            stats
49,414✔
428
        });
58,526✔
429

430
        let sliced = <V::OperationsVTable as OperationsVTable<V>>::slice(&self.0, start, stop)?;
58,526✔
431

432
        assert_eq!(
58,526✔
433
            sliced.len(),
58,526✔
434
            stop - start,
58,526✔
UNCOV
435
            "Slice length mismatch {}",
×
UNCOV
436
            self.encoding_id()
×
437
        );
438
        assert_eq!(
58,526✔
439
            sliced.dtype(),
58,526✔
440
            self.dtype(),
58,526✔
UNCOV
441
            "Slice dtype mismatch {}",
×
UNCOV
442
            self.encoding_id()
×
443
        );
444

445
        if let Some(derived_stats) = derived_stats {
58,526✔
446
            let mut stats = sliced.statistics().to_owned();
49,414✔
447
            stats.combine_sets(&derived_stats, self.dtype())?;
49,414✔
448
            for (stat, val) in stats.into_iter() {
101,153✔
449
                sliced.statistics().set(stat, val)
100,815✔
450
            }
451
        }
9,112✔
452

453
        Ok(sliced)
58,526✔
454
    }
68,222✔
455

456
    fn scalar_at(&self, index: usize) -> VortexResult<Scalar> {
3,541,628✔
457
        if index >= self.len() {
3,541,628✔
458
            vortex_bail!(OutOfBounds: index, 0, self.len());
8✔
459
        }
3,541,620✔
460
        if self.is_invalid(index)? {
3,541,620✔
461
            return Ok(Scalar::null(self.dtype().clone()));
2,738✔
462
        }
3,538,882✔
463
        let scalar = <V::OperationsVTable as OperationsVTable<V>>::scalar_at(&self.0, index)?;
3,538,882✔
464
        assert_eq!(self.dtype(), scalar.dtype(), "Scalar dtype mismatch");
3,538,882✔
465
        Ok(scalar)
3,538,882✔
466
    }
3,541,628✔
467

468
    fn optimize(&self) -> VortexResult<ArrayRef> {
4✔
469
        let result = <V::OperationsVTable as OperationsVTable<V>>::optimize(&self.0)?.into_array();
4✔
470

4✔
471
        #[cfg(debug_assertions)]
4✔
472
        {
4✔
473
            let nbytes = self.0.nbytes();
4✔
474
            let result_nbytes = result.nbytes();
4✔
475
            assert!(
4✔
476
                result_nbytes <= nbytes,
4✔
UNCOV
477
                "optimize() made the array larger: {} bytes -> {} bytes",
×
478
                nbytes,
479
                result_nbytes
480
            );
481
        }
482

483
        assert_eq!(
4✔
484
            self.dtype(),
4✔
485
            result.dtype(),
4✔
UNCOV
486
            "optimize() changed DType from {} to {}",
×
UNCOV
487
            self.dtype(),
×
UNCOV
488
            result.dtype()
×
489
        );
490
        assert_eq!(
4✔
491
            result.len(),
4✔
492
            self.len(),
4✔
UNCOV
493
            "optimize() changed len from {} to {}",
×
UNCOV
494
            self.len(),
×
UNCOV
495
            result.len()
×
496
        );
497

498
        Ok(result)
4✔
499
    }
4✔
500

501
    // fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
502
    //     <V::DisplayVTable as DisplayVTable<V>>::fmt(&self.0, f)
503
    // }
504

505
    // fn fmt_as(&self, f: &mut Formatter<'_>, options: &DisplayOptions) -> std::fmt::Result {
506
    //     <V::DisplayVTable as DisplayVTable<V>>::fmt_as(&self.0, f, options)
507
    // }
508

509
    fn is_valid(&self, index: usize) -> VortexResult<bool> {
3,582,786✔
510
        if index >= self.len() {
3,582,786✔
UNCOV
511
            vortex_bail!(OutOfBounds: index, 0, self.len());
×
512
        }
3,582,786✔
513
        <V::ValidityVTable as ValidityVTable<V>>::is_valid(&self.0, index)
3,582,786✔
514
    }
3,582,786✔
515

516
    fn is_invalid(&self, index: usize) -> VortexResult<bool> {
3,542,366✔
517
        self.is_valid(index).map(|valid| !valid)
3,542,366✔
518
    }
3,542,366✔
519

520
    fn all_valid(&self) -> VortexResult<bool> {
3,160,614✔
521
        <V::ValidityVTable as ValidityVTable<V>>::all_valid(&self.0)
3,160,614✔
522
    }
3,160,614✔
523

524
    fn all_invalid(&self) -> VortexResult<bool> {
162,230✔
525
        <V::ValidityVTable as ValidityVTable<V>>::all_invalid(&self.0)
162,230✔
526
    }
162,230✔
527

528
    fn valid_count(&self) -> VortexResult<usize> {
54,874✔
529
        if let Some(Precision::Exact(invalid_count)) =
4,290✔
530
            self.statistics().get_as::<usize>(Stat::NullCount)
54,874✔
531
        {
532
            return Ok(self.len() - invalid_count);
4,290✔
533
        }
50,584✔
534

535
        let count = <V::ValidityVTable as ValidityVTable<V>>::valid_count(&self.0)?;
50,584✔
536
        assert!(count <= self.len(), "Valid count exceeds array length");
50,584✔
537

538
        self.statistics()
50,584✔
539
            .set(Stat::NullCount, Precision::exact(self.len() - count));
50,584✔
540

50,584✔
541
        Ok(count)
50,584✔
542
    }
54,874✔
543

544
    fn invalid_count(&self) -> VortexResult<usize> {
11,449✔
545
        if let Some(Precision::Exact(invalid_count)) =
3,461✔
546
            self.statistics().get_as::<usize>(Stat::NullCount)
11,449✔
547
        {
548
            return Ok(invalid_count);
3,461✔
549
        }
7,988✔
550

551
        let count = <V::ValidityVTable as ValidityVTable<V>>::invalid_count(&self.0)?;
7,988✔
552
        assert!(count <= self.len(), "Invalid count exceeds array length");
7,988✔
553

554
        self.statistics()
7,988✔
555
            .set(Stat::NullCount, Precision::exact(count));
7,988✔
556

7,988✔
557
        Ok(count)
7,988✔
558
    }
11,449✔
559

560
    fn validity_mask(&self) -> VortexResult<Mask> {
242,783✔
561
        let mask = <V::ValidityVTable as ValidityVTable<V>>::validity_mask(&self.0)?;
242,783✔
562
        assert_eq!(mask.len(), self.len(), "Validity mask length mismatch");
242,783✔
563
        Ok(mask)
242,783✔
564
    }
242,783✔
565

566
    fn to_canonical(&self) -> VortexResult<Canonical> {
290,027✔
567
        let canonical = <V::CanonicalVTable as CanonicalVTable<V>>::canonicalize(&self.0)?;
290,027✔
568
        assert_eq!(
290,027✔
569
            self.len(),
290,027✔
570
            canonical.as_ref().len(),
290,027✔
UNCOV
571
            "Canonical length mismatch {}. Expected {} but encoded into {}.",
×
UNCOV
572
            self.encoding_id(),
×
573
            self.len(),
×
574
            canonical.as_ref().len()
×
575
        );
576
        assert_eq!(
290,027✔
577
            self.dtype(),
290,027✔
578
            canonical.as_ref().dtype(),
290,027✔
579
            "Canonical dtype mismatch {}. Expected {} but encoded into {}.",
×
580
            self.encoding_id(),
×
581
            self.dtype(),
×
582
            canonical.as_ref().dtype()
×
583
        );
584
        canonical.as_ref().statistics().inherit(self.statistics());
290,027✔
585
        Ok(canonical)
290,027✔
586
    }
290,027✔
587

588
    fn append_to_builder(&self, builder: &mut dyn ArrayBuilder) -> VortexResult<()> {
41,749✔
589
        if builder.dtype() != self.dtype() {
41,749✔
590
            vortex_bail!(
×
591
                "Builder dtype mismatch: expected {}, got {}",
×
592
                self.dtype(),
×
593
                builder.dtype(),
×
UNCOV
594
            );
×
595
        }
41,749✔
596
        let len = builder.len();
41,749✔
597

41,749✔
598
        <V::CanonicalVTable as CanonicalVTable<V>>::append_to_builder(&self.0, builder)?;
41,749✔
599
        assert_eq!(
41,749✔
600
            len + self.len(),
41,749✔
601
            builder.len(),
41,749✔
602
            "Builder length mismatch after writing array for encoding {}",
×
UNCOV
603
            self.encoding_id(),
×
604
        );
605
        Ok(())
41,749✔
606
    }
41,749✔
607

608
    fn statistics(&self) -> StatsSetRef<'_> {
1,756,935✔
609
        <V::ArrayVTable as ArrayVTable<V>>::stats(&self.0)
1,756,935✔
610
    }
1,756,935✔
611

612
    fn with_children(&self, children: &[ArrayRef]) -> VortexResult<ArrayRef> {
×
613
        struct ReplacementChildren<'a> {
614
            children: &'a [ArrayRef],
615
        }
616

617
        impl ArrayChildren for ReplacementChildren<'_> {
UNCOV
618
            fn get(&self, index: usize, dtype: &DType, len: usize) -> VortexResult<ArrayRef> {
×
UNCOV
619
                if index >= self.children.len() {
×
UNCOV
620
                    vortex_bail!(OutOfBounds: index, 0, self.children.len());
×
UNCOV
621
                }
×
UNCOV
622
                let child = &self.children[index];
×
UNCOV
623
                if child.len() != len {
×
UNCOV
624
                    vortex_bail!(
×
UNCOV
625
                        "Child length mismatch: expected {}, got {}",
×
UNCOV
626
                        len,
×
UNCOV
627
                        child.len()
×
UNCOV
628
                    );
×
UNCOV
629
                }
×
UNCOV
630
                if child.dtype() != dtype {
×
UNCOV
631
                    vortex_bail!(
×
UNCOV
632
                        "Child dtype mismatch: expected {}, got {}",
×
UNCOV
633
                        dtype,
×
UNCOV
634
                        child.dtype()
×
UNCOV
635
                    );
×
UNCOV
636
                }
×
UNCOV
637
                Ok(child.clone())
×
UNCOV
638
            }
×
639

UNCOV
640
            fn len(&self) -> usize {
×
UNCOV
641
                self.children.len()
×
642
            }
×
643
        }
644

UNCOV
645
        let metadata = self.metadata()?.ok_or_else(|| {
×
UNCOV
646
            vortex_err!("Cannot replace children for arrays that do not support serialization")
×
UNCOV
647
        })?;
×
648

649
        // Replace the children of the array by re-building the array from parts.
UNCOV
650
        self.encoding().build(
×
UNCOV
651
            self.dtype(),
×
UNCOV
652
            self.len(),
×
UNCOV
653
            &metadata,
×
UNCOV
654
            &self.buffers(),
×
UNCOV
655
            &ReplacementChildren { children },
×
UNCOV
656
        )
×
UNCOV
657
    }
×
658

659
    fn invoke(
36,662✔
660
        &self,
36,662✔
661
        compute_fn: &ComputeFn,
36,662✔
662
        args: &InvocationArgs,
36,662✔
663
    ) -> VortexResult<Option<Output>> {
36,662✔
664
        <V::ComputeVTable as ComputeVTable<V>>::invoke(&self.0, compute_fn, args)
36,662✔
665
    }
36,662✔
666
}
667

668
impl<V: VTable> ArrayVisitor for ArrayAdapter<V> {
669
    fn children(&self) -> Vec<ArrayRef> {
291,746✔
670
        struct ChildrenCollector {
671
            children: Vec<ArrayRef>,
672
        }
673

674
        impl ArrayChildVisitor for ChildrenCollector {
675
            fn visit_child(&mut self, _name: &str, array: &dyn Array) {
119,387✔
676
                self.children.push(array.to_array());
119,387✔
677
            }
119,387✔
678
        }
679

680
        let mut collector = ChildrenCollector {
291,746✔
681
            children: Vec::new(),
291,746✔
682
        };
291,746✔
683
        <V::VisitorVTable as VisitorVTable<V>>::visit_children(&self.0, &mut collector);
291,746✔
684
        collector.children
291,746✔
685
    }
291,746✔
686

UNCOV
687
    fn nchildren(&self) -> usize {
×
UNCOV
688
        <V::VisitorVTable as VisitorVTable<V>>::nchildren(&self.0)
×
UNCOV
689
    }
×
690

691
    fn children_names(&self) -> Vec<String> {
683✔
692
        struct ChildNameCollector {
693
            names: Vec<String>,
694
        }
695

696
        impl ArrayChildVisitor for ChildNameCollector {
697
            fn visit_child(&mut self, name: &str, _array: &dyn Array) {
469✔
698
                self.names.push(name.to_string());
469✔
699
            }
469✔
700
        }
701

702
        let mut collector = ChildNameCollector { names: Vec::new() };
683✔
703
        <V::VisitorVTable as VisitorVTable<V>>::visit_children(&self.0, &mut collector);
683✔
704
        collector.names
683✔
705
    }
683✔
706

UNCOV
707
    fn named_children(&self) -> Vec<(String, ArrayRef)> {
×
708
        struct NamedChildrenCollector {
709
            children: Vec<(String, ArrayRef)>,
710
        }
711

712
        impl ArrayChildVisitor for NamedChildrenCollector {
UNCOV
713
            fn visit_child(&mut self, name: &str, array: &dyn Array) {
×
UNCOV
714
                self.children.push((name.to_string(), array.to_array()));
×
UNCOV
715
            }
×
716
        }
717

UNCOV
718
        let mut collector = NamedChildrenCollector {
×
UNCOV
719
            children: Vec::new(),
×
UNCOV
720
        };
×
UNCOV
721

×
UNCOV
722
        <V::VisitorVTable as VisitorVTable<V>>::visit_children(&self.0, &mut collector);
×
UNCOV
723
        collector.children
×
UNCOV
724
    }
×
725

726
    fn buffers(&self) -> Vec<ByteBuffer> {
222,307✔
727
        struct BufferCollector {
728
            buffers: Vec<ByteBuffer>,
729
        }
730

731
        impl ArrayBufferVisitor for BufferCollector {
732
            fn visit_buffer(&mut self, buffer: &ByteBuffer) {
292,289✔
733
                self.buffers.push(buffer.clone());
292,289✔
734
            }
292,289✔
735
        }
736

737
        let mut collector = BufferCollector {
222,307✔
738
            buffers: Vec::new(),
222,307✔
739
        };
222,307✔
740
        <V::VisitorVTable as VisitorVTable<V>>::visit_buffers(&self.0, &mut collector);
222,307✔
741
        collector.buffers
222,307✔
742
    }
222,307✔
743

744
    fn nbuffers(&self) -> usize {
45,023✔
745
        <V::VisitorVTable as VisitorVTable<V>>::nbuffers(&self.0)
45,023✔
746
    }
45,023✔
747

748
    fn metadata(&self) -> VortexResult<Option<Vec<u8>>> {
48,704✔
749
        Ok(<V::SerdeVTable as SerdeVTable<V>>::metadata(&self.0)?.map(|m| m.serialize()))
48,704✔
750
    }
48,704✔
751

752
    fn metadata_fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
683✔
753
        match <V::SerdeVTable as SerdeVTable<V>>::metadata(&self.0) {
683✔
UNCOV
754
            Err(e) => write!(f, "<serde error: {e}>"),
×
UNCOV
755
            Ok(None) => write!(f, "<serde not supported>"),
×
756
            Ok(Some(metadata)) => Debug::fmt(&metadata, f),
683✔
757
        }
758
    }
683✔
759
}
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