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

vortex-data / vortex / 16204612549

10 Jul 2025 07:50PM UTC coverage: 81.152% (+2.9%) from 78.263%
16204612549

Pull #3825

github

web-flow
Merge d0d2717da into be9c2fd3e
Pull Request #3825: feat: Add optimize ArrayOp with VBView implementation

178 of 211 new or added lines in 4 files covered. (84.36%)

330 existing lines in 34 files now uncovered.

45433 of 55985 relevant lines covered (81.15%)

145951.87 hits per line

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

77.7
/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 statistics;
6
mod visitor;
7

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

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

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

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

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

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

45
    /// Returns whether the array is empty (has zero rows).
46
    fn is_empty(&self) -> bool {
182,949✔
47
        self.len() == 0
182,949✔
48
    }
182,949✔
49

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

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

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

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

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

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

70
    /// Returns whether the array is of the given encoding.
71
    fn is_encoding(&self, encoding: EncodingId) -> bool {
333,541✔
72
        self.encoding_id() == encoding
333,541✔
73
    }
333,541✔
74

75
    /// Returns whether this array is an arrow encoding.
76
    // TODO(ngates): this shouldn't live here.
77
    fn is_arrow(&self) -> bool {
8,535✔
78
        self.is_encoding(NullEncoding.id())
8,535✔
79
            || self.is_encoding(BoolEncoding.id())
8,535✔
80
            || self.is_encoding(PrimitiveEncoding.id())
7,404✔
81
            || self.is_encoding(VarBinEncoding.id())
5,385✔
82
            || self.is_encoding(VarBinViewEncoding.id())
5,385✔
83
    }
8,535✔
84

85
    /// Whether the array is of a canonical encoding.
86
    // TODO(ngates): this shouldn't live here.
87
    fn is_canonical(&self) -> bool {
48,305✔
88
        self.is_encoding(NullEncoding.id())
48,305✔
89
            || self.is_encoding(BoolEncoding.id())
48,305✔
90
            || self.is_encoding(PrimitiveEncoding.id())
46,795✔
91
            || self.is_encoding(DecimalEncoding.id())
31,667✔
92
            || self.is_encoding(StructEncoding.id())
30,428✔
93
            || self.is_encoding(ListEncoding.id())
29,157✔
94
            || self.is_encoding(VarBinViewEncoding.id())
29,156✔
95
            || self.is_encoding(ExtensionEncoding.id())
28,134✔
96
    }
48,305✔
97

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

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

104
    /// Returns whether all items in the array are valid.
105
    ///
106
    /// This is usually cheaper than computing a precise `valid_count`.
107
    fn all_valid(&self) -> VortexResult<bool>;
108

109
    /// Returns whether the array is all invalid.
110
    ///
111
    /// This is usually cheaper than computing a precise `invalid_count`.
112
    fn all_invalid(&self) -> VortexResult<bool>;
113

114
    /// Returns the number of valid elements in the array.
115
    fn valid_count(&self) -> VortexResult<usize>;
116

117
    /// Returns the number of invalid elements in the array.
118
    fn invalid_count(&self) -> VortexResult<usize>;
119

120
    /// Returns the canonical validity mask for the array.
121
    fn validity_mask(&self) -> VortexResult<Mask>;
122

123
    /// Returns the canonical representation of the array.
124
    fn to_canonical(&self) -> VortexResult<Canonical>;
125

126
    /// Writes the array into the canonical builder.
127
    ///
128
    /// The [`DType`] of the builder must match that of the array.
129
    fn append_to_builder(&self, builder: &mut dyn ArrayBuilder) -> VortexResult<()>;
130

131
    /// Returns the statistics of the array.
132
    // TODO(ngates): change how this works. It's weird.
133
    fn statistics(&self) -> StatsSetRef<'_>;
134

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

138
    /// Optionally invoke a kernel for the given compute function.
139
    ///
140
    /// These encoding-specific kernels are independent of kernels registered directly with
141
    /// compute functions using [`ComputeFn::register_kernel`], and are attempted only if none of
142
    /// the function-specific kernels returns a result.
143
    ///
144
    /// This allows encodings the opportunity to generically implement many compute functions
145
    /// that share some property, for example [`ComputeFn::is_elementwise`], without prior
146
    /// knowledge of the function itself, while still allowing users to override the implementation
147
    /// of compute functions for built-in encodings. For an example, see the implementation for
148
    /// chunked arrays.
149
    ///
150
    /// The first input in the [`InvocationArgs`] is always the array itself.
151
    ///
152
    /// Warning: do not call `compute_fn.invoke(args)` directly, as this will result in a recursive
153
    /// call.
154
    fn invoke(&self, compute_fn: &ComputeFn, args: &InvocationArgs)
155
    -> VortexResult<Option<Output>>;
156
}
157

158
impl Array for Arc<dyn Array> {
159
    fn as_any(&self) -> &dyn Any {
543,052✔
160
        self.as_ref().as_any()
543,052✔
161
    }
543,052✔
162

163
    fn to_array(&self) -> ArrayRef {
124,825✔
164
        self.clone()
124,825✔
165
    }
124,825✔
166

167
    fn len(&self) -> usize {
1,031,519✔
168
        self.as_ref().len()
1,031,519✔
169
    }
1,031,519✔
170

171
    fn dtype(&self) -> &DType {
814,741✔
172
        self.as_ref().dtype()
814,741✔
173
    }
814,741✔
174

175
    fn encoding(&self) -> EncodingRef {
18,178✔
176
        self.as_ref().encoding()
18,178✔
177
    }
18,178✔
178

179
    fn encoding_id(&self) -> EncodingId {
299,724✔
180
        self.as_ref().encoding_id()
299,724✔
181
    }
299,724✔
182

183
    fn slice(&self, start: usize, end: usize) -> VortexResult<ArrayRef> {
34,079✔
184
        self.as_ref().slice(start, end)
34,079✔
185
    }
34,079✔
186

187
    fn scalar_at(&self, index: usize) -> VortexResult<Scalar> {
494,790✔
188
        self.as_ref().scalar_at(index)
494,790✔
189
    }
494,790✔
190

NEW
191
    fn optimize(&self) -> VortexResult<ArrayRef> {
×
NEW
192
        self.as_ref().optimize()
×
NEW
193
    }
×
194

195
    fn is_valid(&self, index: usize) -> VortexResult<bool> {
29,850✔
196
        self.as_ref().is_valid(index)
29,850✔
197
    }
29,850✔
198

199
    fn is_invalid(&self, index: usize) -> VortexResult<bool> {
364✔
200
        self.as_ref().is_invalid(index)
364✔
201
    }
364✔
202

203
    fn all_valid(&self) -> VortexResult<bool> {
24,287✔
204
        self.as_ref().all_valid()
24,287✔
205
    }
24,287✔
206

207
    fn all_invalid(&self) -> VortexResult<bool> {
41,753✔
208
        self.as_ref().all_invalid()
41,753✔
209
    }
41,753✔
210

211
    fn valid_count(&self) -> VortexResult<usize> {
2,640✔
212
        self.as_ref().valid_count()
2,640✔
213
    }
2,640✔
214

215
    fn invalid_count(&self) -> VortexResult<usize> {
444✔
216
        self.as_ref().invalid_count()
444✔
217
    }
444✔
218

219
    fn validity_mask(&self) -> VortexResult<Mask> {
5,668✔
220
        self.as_ref().validity_mask()
5,668✔
221
    }
5,668✔
222

223
    fn to_canonical(&self) -> VortexResult<Canonical> {
217,828✔
224
        self.as_ref().to_canonical()
217,828✔
225
    }
217,828✔
226

227
    fn append_to_builder(&self, builder: &mut dyn ArrayBuilder) -> VortexResult<()> {
39,640✔
228
        self.as_ref().append_to_builder(builder)
39,640✔
229
    }
39,640✔
230

231
    fn statistics(&self) -> StatsSetRef<'_> {
392,355✔
232
        self.as_ref().statistics()
392,355✔
233
    }
392,355✔
234

235
    fn with_children(&self, children: &[ArrayRef]) -> VortexResult<ArrayRef> {
×
236
        self.as_ref().with_children(children)
×
237
    }
×
238

239
    fn invoke(
31,921✔
240
        &self,
31,921✔
241
        compute_fn: &ComputeFn,
31,921✔
242
        args: &InvocationArgs,
31,921✔
243
    ) -> VortexResult<Option<Output>> {
31,921✔
244
        self.as_ref().invoke(compute_fn, args)
31,921✔
245
    }
31,921✔
246
}
247

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

251
impl ToOwned for dyn Array {
252
    type Owned = ArrayRef;
253

254
    fn to_owned(&self) -> Self::Owned {
×
255
        self.to_array()
×
256
    }
×
257
}
258

259
impl dyn Array + '_ {
260
    /// Returns the array downcast to the given `A`.
261
    pub fn as_<V: VTable>(&self) -> &V::Array {
301✔
262
        self.as_opt::<V>().vortex_expect("Failed to downcast")
301✔
263
    }
301✔
264

265
    /// Returns the array downcast to the given `A`.
266
    pub fn as_opt<V: VTable>(&self) -> Option<&V::Array> {
1,605,504✔
267
        self.as_any()
1,605,504✔
268
            .downcast_ref::<ArrayAdapter<V>>()
1,605,504✔
269
            .map(|array_adapter| &array_adapter.0)
1,605,504✔
270
    }
1,605,504✔
271

272
    /// Is self an array with encoding from vtable `V`.
273
    pub fn is<V: VTable>(&self) -> bool {
9,015✔
274
        self.as_opt::<V>().is_some()
9,015✔
275
    }
9,015✔
276
}
277

278
impl Display for dyn Array {
279
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
830✔
280
        write!(
830✔
281
            f,
830✔
282
            "{}({}, len={})",
830✔
283
            self.encoding_id(),
830✔
284
            self.dtype(),
830✔
285
            self.len()
830✔
286
        )
830✔
287
    }
830✔
288
}
289

290
impl dyn Array + '_ {
291
    /// Total size of the array in bytes, including all children and buffers.
292
    // TODO(ngates): this should return u64
293
    pub fn nbytes(&self) -> usize {
149,523✔
294
        let mut nbytes = 0;
149,523✔
295
        for array in self.depth_first_traversal() {
209,839✔
296
            for buffer in array.buffers() {
269,862✔
297
                nbytes += buffer.len();
269,862✔
298
            }
269,862✔
299
        }
300
        nbytes
149,523✔
301
    }
149,523✔
302
}
303

304
mod private {
305
    use super::*;
306

307
    pub trait Sealed {}
308

309
    impl<V: VTable> Sealed for ArrayAdapter<V> {}
310
    impl Sealed for Arc<dyn Array> {}
311
}
312

313
/// Adapter struct used to lift the [`VTable`] trait into an object-safe [`Array`]
314
/// implementation.
315
///
316
/// Since this is a unit struct with `repr(transparent)`, we are able to turn un-adapted array
317
/// structs into [`dyn Array`] using some cheeky casting inside [`std::ops::Deref`] and
318
/// [`AsRef`]. See the `vtable!` macro for more details.
319
#[repr(transparent)]
320
pub struct ArrayAdapter<V: VTable>(V::Array);
321

322
impl<V: VTable> Debug for ArrayAdapter<V> {
323
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
×
324
        self.0.fmt(f)
×
325
    }
×
326
}
327

328
impl<V: VTable> Array for ArrayAdapter<V> {
329
    fn as_any(&self) -> &dyn Any {
1,521,921✔
330
        self
1,521,921✔
331
    }
1,521,921✔
332

333
    fn to_array(&self) -> ArrayRef {
291,577✔
334
        Arc::new(ArrayAdapter::<V>(self.0.clone()))
291,577✔
335
    }
291,577✔
336

337
    fn len(&self) -> usize {
10,832,967✔
338
        <V::ArrayVTable as ArrayVTable<V>>::len(&self.0)
10,832,967✔
339
    }
10,832,967✔
340

341
    fn dtype(&self) -> &DType {
27,263,067✔
342
        <V::ArrayVTable as ArrayVTable<V>>::dtype(&self.0)
27,263,067✔
343
    }
27,263,067✔
344

345
    fn encoding(&self) -> EncodingRef {
24,424✔
346
        V::encoding(&self.0)
24,424✔
347
    }
24,424✔
348

349
    fn encoding_id(&self) -> EncodingId {
328,570✔
350
        V::encoding(&self.0).id()
328,570✔
351
    }
328,570✔
352

353
    fn slice(&self, start: usize, stop: usize) -> VortexResult<ArrayRef> {
67,499✔
354
        if start == 0 && stop == self.len() {
67,499✔
355
            return Ok(self.to_array());
9,681✔
356
        }
57,818✔
357

57,818✔
358
        if start > self.len() {
57,818✔
359
            vortex_bail!(OutOfBounds: start, 0, self.len());
×
360
        }
57,818✔
361
        if stop > self.len() {
57,818✔
362
            vortex_bail!(OutOfBounds: stop, 0, self.len());
×
363
        }
57,818✔
364
        if start > stop {
57,818✔
365
            vortex_bail!("start ({start}) must be <= stop ({stop})");
×
366
        }
57,818✔
367

57,818✔
368
        if start == stop {
57,818✔
369
            return Ok(Canonical::empty(self.dtype()).into_array());
12✔
370
        }
57,806✔
371

57,806✔
372
        // We know that constant array don't need stats propagation, so we can avoid the overhead of
57,806✔
373
        // computing derived stats and merging them in.
57,806✔
374
        // TODO(ngates): skip the is_constant check here, it can force an expensive compute.
57,806✔
375
        // TODO(ngates): provide a means to slice an array _without_ propagating stats.
57,806✔
376
        let derived_stats = (!self.0.is_constant_opts(Cost::Negligible)).then(|| {
57,806✔
377
            let stats = self.statistics().to_owned();
48,812✔
378

48,812✔
379
            // an array that is not constant can become constant after slicing
48,812✔
380
            let is_constant = stats.get_as::<bool>(Stat::IsConstant);
48,812✔
381
            let is_sorted = stats.get_as::<bool>(Stat::IsSorted);
48,812✔
382
            let is_strict_sorted = stats.get_as::<bool>(Stat::IsStrictSorted);
48,812✔
383

48,812✔
384
            let mut stats = stats.keep_inexact_stats(&[
48,812✔
385
                Stat::Max,
48,812✔
386
                Stat::Min,
48,812✔
387
                Stat::NullCount,
48,812✔
388
                Stat::UncompressedSizeInBytes,
48,812✔
389
            ]);
48,812✔
390

48,812✔
391
            if is_constant == Some(Precision::Exact(true)) {
48,812✔
392
                stats.set(Stat::IsConstant, Precision::exact(true));
×
393
            }
48,812✔
394
            if is_sorted == Some(Precision::Exact(true)) {
48,812✔
395
                stats.set(Stat::IsSorted, Precision::exact(true));
690✔
396
            }
48,122✔
397
            if is_strict_sorted == Some(Precision::Exact(true)) {
48,812✔
398
                stats.set(Stat::IsStrictSorted, Precision::exact(true));
380✔
399
            }
48,432✔
400

401
            stats
48,812✔
402
        });
57,806✔
403

404
        let sliced = <V::OperationsVTable as OperationsVTable<V>>::slice(&self.0, start, stop)?;
57,806✔
405

406
        assert_eq!(
57,806✔
407
            sliced.len(),
57,806✔
408
            stop - start,
57,806✔
409
            "Slice length mismatch {}",
×
410
            self.encoding_id()
×
411
        );
412
        assert_eq!(
57,806✔
413
            sliced.dtype(),
57,806✔
414
            self.dtype(),
57,806✔
415
            "Slice dtype mismatch {}",
×
416
            self.encoding_id()
×
417
        );
418

419
        if let Some(derived_stats) = derived_stats {
57,806✔
420
            let mut stats = sliced.statistics().to_owned();
48,812✔
421
            stats.combine_sets(&derived_stats, self.dtype())?;
48,812✔
422
            for (stat, val) in stats.into_iter() {
100,030✔
423
                sliced.statistics().set(stat, val)
99,695✔
424
            }
425
        }
8,994✔
426

427
        Ok(sliced)
57,806✔
428
    }
67,499✔
429

430
    fn scalar_at(&self, index: usize) -> VortexResult<Scalar> {
3,531,876✔
431
        if index >= self.len() {
3,531,876✔
432
            vortex_bail!(OutOfBounds: index, 0, self.len());
8✔
433
        }
3,531,868✔
434
        if self.is_invalid(index)? {
3,531,868✔
435
            return Ok(Scalar::null(self.dtype().clone()));
2,659✔
436
        }
3,529,209✔
437
        let scalar = <V::OperationsVTable as OperationsVTable<V>>::scalar_at(&self.0, index)?;
3,529,209✔
438
        assert_eq!(self.dtype(), scalar.dtype(), "Scalar dtype mismatch");
3,529,209✔
439
        Ok(scalar)
3,529,209✔
440
    }
3,531,876✔
441

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

4✔
445
        #[cfg(debug_assertions)]
4✔
446
        {
4✔
447
            let nbytes = self.0.nbytes();
4✔
448
            let result_nbytes = result.nbytes();
4✔
449
            assert!(
4✔
450
                result_nbytes <= nbytes,
4✔
NEW
451
                "optimize() made the array larger: {} bytes -> {} bytes",
×
452
                nbytes,
453
                result_nbytes
454
            );
455
        }
456

457
        assert_eq!(
4✔
458
            self.dtype(),
4✔
459
            result.dtype(),
4✔
NEW
460
            "optimize() changed DType from {} to {}",
×
NEW
461
            self.dtype(),
×
NEW
462
            result.dtype()
×
463
        );
464
        assert_eq!(
4✔
465
            result.len(),
4✔
466
            self.len(),
4✔
NEW
467
            "optimize() changed len from {} to {}",
×
NEW
468
            self.len(),
×
NEW
469
            result.len()
×
470
        );
471

472
        Ok(result)
4✔
473
    }
4✔
474

475
    fn is_valid(&self, index: usize) -> VortexResult<bool> {
3,571,774✔
476
        if index >= self.len() {
3,571,774✔
477
            vortex_bail!(OutOfBounds: index, 0, self.len());
×
478
        }
3,571,774✔
479
        <V::ValidityVTable as ValidityVTable<V>>::is_valid(&self.0, index)
3,571,774✔
480
    }
3,571,774✔
481

482
    fn is_invalid(&self, index: usize) -> VortexResult<bool> {
3,532,614✔
483
        self.is_valid(index).map(|valid| !valid)
3,532,614✔
484
    }
3,532,614✔
485

486
    fn all_valid(&self) -> VortexResult<bool> {
3,130,520✔
487
        <V::ValidityVTable as ValidityVTable<V>>::all_valid(&self.0)
3,130,520✔
488
    }
3,130,520✔
489

490
    fn all_invalid(&self) -> VortexResult<bool> {
159,608✔
491
        <V::ValidityVTable as ValidityVTable<V>>::all_invalid(&self.0)
159,608✔
492
    }
159,608✔
493

494
    fn valid_count(&self) -> VortexResult<usize> {
54,296✔
495
        if let Some(Precision::Exact(invalid_count)) =
4,276✔
496
            self.statistics().get_as::<usize>(Stat::NullCount)
54,296✔
497
        {
498
            return Ok(self.len() - invalid_count);
4,276✔
499
        }
50,020✔
500

501
        let count = <V::ValidityVTable as ValidityVTable<V>>::valid_count(&self.0)?;
50,020✔
502
        assert!(count <= self.len(), "Valid count exceeds array length");
50,020✔
503

504
        self.statistics()
50,020✔
505
            .set(Stat::NullCount, Precision::exact(self.len() - count));
50,020✔
506

50,020✔
507
        Ok(count)
50,020✔
508
    }
54,296✔
509

510
    fn invalid_count(&self) -> VortexResult<usize> {
11,401✔
511
        if let Some(Precision::Exact(invalid_count)) =
3,457✔
512
            self.statistics().get_as::<usize>(Stat::NullCount)
11,401✔
513
        {
514
            return Ok(invalid_count);
3,457✔
515
        }
7,944✔
516

517
        let count = <V::ValidityVTable as ValidityVTable<V>>::invalid_count(&self.0)?;
7,944✔
518
        assert!(count <= self.len(), "Invalid count exceeds array length");
7,944✔
519

520
        self.statistics()
7,944✔
521
            .set(Stat::NullCount, Precision::exact(count));
7,944✔
522

7,944✔
523
        Ok(count)
7,944✔
524
    }
11,401✔
525

526
    fn validity_mask(&self) -> VortexResult<Mask> {
239,334✔
527
        let mask = <V::ValidityVTable as ValidityVTable<V>>::validity_mask(&self.0)?;
239,334✔
528
        assert_eq!(mask.len(), self.len(), "Validity mask length mismatch");
239,334✔
529
        Ok(mask)
239,334✔
530
    }
239,334✔
531

532
    fn to_canonical(&self) -> VortexResult<Canonical> {
285,467✔
533
        let canonical = <V::CanonicalVTable as CanonicalVTable<V>>::canonicalize(&self.0)?;
285,467✔
534
        assert_eq!(
285,467✔
535
            self.len(),
285,467✔
536
            canonical.as_ref().len(),
285,467✔
537
            "Canonical length mismatch {}. Expected {} but encoded into {}.",
×
538
            self.encoding_id(),
×
539
            self.len(),
×
540
            canonical.as_ref().len()
×
541
        );
542
        assert_eq!(
285,467✔
543
            self.dtype(),
285,467✔
544
            canonical.as_ref().dtype(),
285,467✔
545
            "Canonical dtype mismatch {}. Expected {} but encoded into {}.",
×
546
            self.encoding_id(),
×
547
            self.dtype(),
×
548
            canonical.as_ref().dtype()
×
549
        );
550
        canonical.as_ref().statistics().inherit(self.statistics());
285,467✔
551
        Ok(canonical)
285,467✔
552
    }
285,467✔
553

554
    fn append_to_builder(&self, builder: &mut dyn ArrayBuilder) -> VortexResult<()> {
41,285✔
555
        if builder.dtype() != self.dtype() {
41,285✔
556
            vortex_bail!(
×
557
                "Builder dtype mismatch: expected {}, got {}",
×
558
                self.dtype(),
×
559
                builder.dtype(),
×
560
            );
×
561
        }
41,285✔
562
        let len = builder.len();
41,285✔
563

41,285✔
564
        <V::CanonicalVTable as CanonicalVTable<V>>::append_to_builder(&self.0, builder)?;
41,285✔
565
        assert_eq!(
41,285✔
566
            len + self.len(),
41,285✔
567
            builder.len(),
41,285✔
568
            "Builder length mismatch after writing array for encoding {}",
×
569
            self.encoding_id(),
×
570
        );
571
        Ok(())
41,285✔
572
    }
41,285✔
573

574
    fn statistics(&self) -> StatsSetRef<'_> {
1,733,331✔
575
        <V::ArrayVTable as ArrayVTable<V>>::stats(&self.0)
1,733,331✔
576
    }
1,733,331✔
577

578
    fn with_children(&self, children: &[ArrayRef]) -> VortexResult<ArrayRef> {
×
579
        struct ReplacementChildren<'a> {
580
            children: &'a [ArrayRef],
581
        }
582

583
        impl ArrayChildren for ReplacementChildren<'_> {
584
            fn get(&self, index: usize, dtype: &DType, len: usize) -> VortexResult<ArrayRef> {
×
585
                if index >= self.children.len() {
×
586
                    vortex_bail!(OutOfBounds: index, 0, self.children.len());
×
587
                }
×
588
                let child = &self.children[index];
×
589
                if child.len() != len {
×
590
                    vortex_bail!(
×
591
                        "Child length mismatch: expected {}, got {}",
×
592
                        len,
×
593
                        child.len()
×
594
                    );
×
595
                }
×
596
                if child.dtype() != dtype {
×
597
                    vortex_bail!(
×
598
                        "Child dtype mismatch: expected {}, got {}",
×
599
                        dtype,
×
600
                        child.dtype()
×
601
                    );
×
602
                }
×
603
                Ok(child.clone())
×
604
            }
×
605

606
            fn len(&self) -> usize {
×
607
                self.children.len()
×
608
            }
×
609
        }
610

611
        let metadata = self.metadata()?.ok_or_else(|| {
×
612
            vortex_err!("Cannot replace children for arrays that do not support serialization")
×
613
        })?;
×
614

615
        // Replace the children of the array by re-building the array from parts.
616
        self.encoding().build(
×
617
            self.dtype(),
×
618
            self.len(),
×
619
            &metadata,
×
620
            &self.buffers(),
×
621
            &ReplacementChildren { children },
×
622
        )
×
623
    }
×
624

625
    fn invoke(
34,712✔
626
        &self,
34,712✔
627
        compute_fn: &ComputeFn,
34,712✔
628
        args: &InvocationArgs,
34,712✔
629
    ) -> VortexResult<Option<Output>> {
34,712✔
630
        <V::ComputeVTable as ComputeVTable<V>>::invoke(&self.0, compute_fn, args)
34,712✔
631
    }
34,712✔
632
}
633

634
impl<V: VTable> ArrayVisitor for ArrayAdapter<V> {
635
    fn children(&self) -> Vec<ArrayRef> {
289,812✔
636
        struct ChildrenCollector {
637
            children: Vec<ArrayRef>,
638
        }
639

640
        impl ArrayChildVisitor for ChildrenCollector {
641
            fn visit_child(&mut self, _name: &str, array: &dyn Array) {
118,189✔
642
                self.children.push(array.to_array());
118,189✔
643
            }
118,189✔
644
        }
645

646
        let mut collector = ChildrenCollector {
289,812✔
647
            children: Vec::new(),
289,812✔
648
        };
289,812✔
649
        <V::VisitorVTable as VisitorVTable<V>>::visit_children(&self.0, &mut collector);
289,812✔
650
        collector.children
289,812✔
651
    }
289,812✔
652

653
    fn nchildren(&self) -> usize {
×
654
        <V::VisitorVTable as VisitorVTable<V>>::nchildren(&self.0)
×
655
    }
×
656

657
    fn children_names(&self) -> Vec<String> {
683✔
658
        struct ChildNameCollector {
659
            names: Vec<String>,
660
        }
661

662
        impl ArrayChildVisitor for ChildNameCollector {
663
            fn visit_child(&mut self, name: &str, _array: &dyn Array) {
469✔
664
                self.names.push(name.to_string());
469✔
665
            }
469✔
666
        }
667

668
        let mut collector = ChildNameCollector { names: Vec::new() };
683✔
669
        <V::VisitorVTable as VisitorVTable<V>>::visit_children(&self.0, &mut collector);
683✔
670
        collector.names
683✔
671
    }
683✔
672

673
    fn named_children(&self) -> Vec<(String, ArrayRef)> {
×
674
        struct NamedChildrenCollector {
675
            children: Vec<(String, ArrayRef)>,
676
        }
677

678
        impl ArrayChildVisitor for NamedChildrenCollector {
679
            fn visit_child(&mut self, name: &str, array: &dyn Array) {
×
680
                self.children.push((name.to_string(), array.to_array()));
×
681
            }
×
682
        }
683

684
        let mut collector = NamedChildrenCollector {
×
685
            children: Vec::new(),
×
686
        };
×
687

×
688
        <V::VisitorVTable as VisitorVTable<V>>::visit_children(&self.0, &mut collector);
×
689
        collector.children
×
690
    }
×
691

692
    fn buffers(&self) -> Vec<ByteBuffer> {
220,281✔
693
        struct BufferCollector {
694
            buffers: Vec<ByteBuffer>,
695
        }
696

697
        impl ArrayBufferVisitor for BufferCollector {
698
            fn visit_buffer(&mut self, buffer: &ByteBuffer) {
293,029✔
699
                self.buffers.push(buffer.clone());
293,029✔
700
            }
293,029✔
701
        }
702

703
        let mut collector = BufferCollector {
220,281✔
704
            buffers: Vec::new(),
220,281✔
705
        };
220,281✔
706
        <V::VisitorVTable as VisitorVTable<V>>::visit_buffers(&self.0, &mut collector);
220,281✔
707
        collector.buffers
220,281✔
708
    }
220,281✔
709

710
    fn nbuffers(&self) -> usize {
45,043✔
711
        <V::VisitorVTable as VisitorVTable<V>>::nbuffers(&self.0)
45,043✔
712
    }
45,043✔
713

714
    fn metadata(&self) -> VortexResult<Option<Vec<u8>>> {
48,848✔
715
        Ok(<V::SerdeVTable as SerdeVTable<V>>::metadata(&self.0)?.map(|m| m.serialize()))
48,848✔
716
    }
48,848✔
717

718
    fn metadata_fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
683✔
719
        match <V::SerdeVTable as SerdeVTable<V>>::metadata(&self.0) {
683✔
720
            Err(e) => write!(f, "<serde error: {e}>"),
×
721
            Ok(None) => write!(f, "<serde not supported>"),
×
722
            Ok(Some(metadata)) => Debug::fmt(&metadata, f),
683✔
723
        }
724
    }
683✔
725
}
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