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

vortex-data / vortex / 17075133033

19 Aug 2025 04:01PM UTC coverage: 87.949% (+0.09%) from 87.856%
17075133033

push

github

web-flow
feat: ArrayOperations infallible, eager validation + new_unchecked (#4177)

ArrayOperations currently return VortexResult<>, but they really should
just be infallible. A failed array op is generally indicative of
programmer or encoding error. There's really nothing interesting we can
do to handle an out-of-bounds slice() or scalar_at.

There's a lot that falls out of this, like fixing a bunch of tests,
tweaking our scalar value casting to return Option instead of Result,
etc.

---------

Signed-off-by: Andrew Duffy <andrew@a10y.dev>

1744 of 1985 new or added lines in 195 files covered. (87.86%)

36 existing lines in 27 files now uncovered.

56745 of 64520 relevant lines covered (87.95%)

624082.56 hits per line

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

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

4
pub mod display;
5
mod visitor;
6

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

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

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

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

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

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

43
    /// Returns whether the array is empty (has zero rows).
44
    fn is_empty(&self) -> bool {
523,285✔
45
        self.len() == 0
523,285✔
46
    }
523,285✔
47

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

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

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

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

60
    /// Fetch the scalar at the given index.
61
    ///
62
    /// This method panics if the index is out of bounds for the array.
63
    fn scalar_at(&self, index: usize) -> Scalar;
64

65
    /// Returns whether the array is of the given encoding.
66
    fn is_encoding(&self, encoding: EncodingId) -> bool {
4,838,702✔
67
        self.encoding_id() == encoding
4,838,702✔
68
    }
4,838,702✔
69

70
    /// Returns whether this array is an arrow encoding.
71
    // TODO(ngates): this shouldn't live here.
72
    fn is_arrow(&self) -> bool {
48,201✔
73
        self.is_encoding(NullEncoding.id())
48,201✔
74
            || self.is_encoding(BoolEncoding.id())
48,201✔
75
            || self.is_encoding(PrimitiveEncoding.id())
38,872✔
76
            || self.is_encoding(VarBinEncoding.id())
25,437✔
77
            || self.is_encoding(VarBinViewEncoding.id())
25,437✔
78
    }
48,201✔
79

80
    /// Whether the array is of a canonical encoding.
81
    // TODO(ngates): this shouldn't live here.
82
    fn is_canonical(&self) -> bool {
763,650✔
83
        self.is_encoding(NullEncoding.id())
763,650✔
84
            || self.is_encoding(BoolEncoding.id())
763,650✔
85
            || self.is_encoding(PrimitiveEncoding.id())
748,488✔
86
            || self.is_encoding(DecimalEncoding.id())
477,666✔
87
            || self.is_encoding(StructEncoding.id())
475,675✔
88
            || self.is_encoding(ListEncoding.id())
473,688✔
89
            || self.is_encoding(VarBinViewEncoding.id())
473,366✔
90
            || self.is_encoding(ExtensionEncoding.id())
466,122✔
91
    }
763,650✔
92

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

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

99
    /// Returns whether all items in the array are valid.
100
    ///
101
    /// This is usually cheaper than computing a precise `valid_count`.
102
    fn all_valid(&self) -> VortexResult<bool>;
103

104
    /// Returns whether the array is all invalid.
105
    ///
106
    /// This is usually cheaper than computing a precise `invalid_count`.
107
    fn all_invalid(&self) -> VortexResult<bool>;
108

109
    /// Returns the number of valid elements in the array.
110
    fn valid_count(&self) -> VortexResult<usize>;
111

112
    /// Returns the number of invalid elements in the array.
113
    fn invalid_count(&self) -> VortexResult<usize>;
114

115
    /// Returns the canonical validity mask for the array.
116
    fn validity_mask(&self) -> VortexResult<Mask>;
117

118
    /// Returns the canonical representation of the array.
119
    fn to_canonical(&self) -> VortexResult<Canonical>;
120

121
    /// Writes the array into the canonical builder.
122
    ///
123
    /// The [`DType`] of the builder must match that of the array.
124
    fn append_to_builder(&self, builder: &mut dyn ArrayBuilder) -> VortexResult<()>;
125

126
    /// Returns the statistics of the array.
127
    // TODO(ngates): change how this works. It's weird.
128
    fn statistics(&self) -> StatsSetRef<'_>;
129

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

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

153
impl Array for Arc<dyn Array> {
154
    fn as_any(&self) -> &dyn Any {
3,612,878✔
155
        self.as_ref().as_any()
3,612,878✔
156
    }
3,612,878✔
157

158
    fn to_array(&self) -> ArrayRef {
288,151✔
159
        self.clone()
288,151✔
160
    }
288,151✔
161

162
    fn len(&self) -> usize {
32,223,048✔
163
        self.as_ref().len()
32,223,048✔
164
    }
32,223,048✔
165

166
    fn dtype(&self) -> &DType {
10,404,111✔
167
        self.as_ref().dtype()
10,404,111✔
168
    }
10,404,111✔
169

170
    fn encoding(&self) -> EncodingRef {
24,908✔
171
        self.as_ref().encoding()
24,908✔
172
    }
24,908✔
173

174
    fn encoding_id(&self) -> EncodingId {
4,103,270✔
175
        self.as_ref().encoding_id()
4,103,270✔
176
    }
4,103,270✔
177

178
    fn slice(&self, start: usize, end: usize) -> ArrayRef {
1,886,754✔
179
        self.as_ref().slice(start, end)
1,886,754✔
180
    }
1,886,754✔
181

182
    fn scalar_at(&self, index: usize) -> Scalar {
50,961,514✔
183
        self.as_ref().scalar_at(index)
50,961,514✔
184
    }
50,961,514✔
185

186
    fn is_valid(&self, index: usize) -> VortexResult<bool> {
4,526,788✔
187
        self.as_ref().is_valid(index)
4,526,788✔
188
    }
4,526,788✔
189

190
    fn is_invalid(&self, index: usize) -> VortexResult<bool> {
2,964✔
191
        self.as_ref().is_invalid(index)
2,964✔
192
    }
2,964✔
193

194
    fn all_valid(&self) -> VortexResult<bool> {
190,345✔
195
        self.as_ref().all_valid()
190,345✔
196
    }
190,345✔
197

198
    fn all_invalid(&self) -> VortexResult<bool> {
286,485✔
199
        self.as_ref().all_invalid()
286,485✔
200
    }
286,485✔
201

202
    fn valid_count(&self) -> VortexResult<usize> {
13,987✔
203
        self.as_ref().valid_count()
13,987✔
204
    }
13,987✔
205

206
    fn invalid_count(&self) -> VortexResult<usize> {
8,458✔
207
        self.as_ref().invalid_count()
8,458✔
208
    }
8,458✔
209

210
    fn validity_mask(&self) -> VortexResult<Mask> {
36,865✔
211
        self.as_ref().validity_mask()
36,865✔
212
    }
36,865✔
213

214
    fn to_canonical(&self) -> VortexResult<Canonical> {
4,307,890✔
215
        self.as_ref().to_canonical()
4,307,890✔
216
    }
4,307,890✔
217

218
    fn append_to_builder(&self, builder: &mut dyn ArrayBuilder) -> VortexResult<()> {
51,906✔
219
        self.as_ref().append_to_builder(builder)
51,906✔
220
    }
51,906✔
221

222
    fn statistics(&self) -> StatsSetRef<'_> {
4,777,707✔
223
        self.as_ref().statistics()
4,777,707✔
224
    }
4,777,707✔
225

226
    fn with_children(&self, children: &[ArrayRef]) -> VortexResult<ArrayRef> {
×
227
        self.as_ref().with_children(children)
×
228
    }
×
229

230
    fn invoke(
575,519✔
231
        &self,
575,519✔
232
        compute_fn: &ComputeFn,
575,519✔
233
        args: &InvocationArgs,
575,519✔
234
    ) -> VortexResult<Option<Output>> {
575,519✔
235
        self.as_ref().invoke(compute_fn, args)
575,519✔
236
    }
575,519✔
237
}
238

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

242
impl ToOwned for dyn Array {
243
    type Owned = ArrayRef;
244

245
    fn to_owned(&self) -> Self::Owned {
×
246
        self.to_array()
×
247
    }
×
248
}
249

250
impl dyn Array + '_ {
251
    /// Returns the array downcast to the given `A`.
252
    pub fn as_<V: VTable>(&self) -> &V::Array {
442✔
253
        self.as_opt::<V>().vortex_expect("Failed to downcast")
442✔
254
    }
442✔
255

256
    /// Returns the array downcast to the given `A`.
257
    pub fn as_opt<V: VTable>(&self) -> Option<&V::Array> {
12,386,421✔
258
        self.as_any()
12,386,421✔
259
            .downcast_ref::<ArrayAdapter<V>>()
12,386,421✔
260
            .map(|array_adapter| &array_adapter.0)
12,386,421✔
261
    }
12,386,421✔
262

263
    /// Is self an array with encoding from vtable `V`.
264
    pub fn is<V: VTable>(&self) -> bool {
4,423,513✔
265
        self.as_opt::<V>().is_some()
4,423,513✔
266
    }
4,423,513✔
267

268
    pub fn is_constant(&self) -> bool {
1,024,184✔
269
        let opts = IsConstantOpts {
1,024,184✔
270
            cost: Cost::Specialized,
1,024,184✔
271
        };
1,024,184✔
272
        is_constant_opts(self, &opts)
1,024,184✔
273
            .inspect_err(|e| log::warn!("Failed to compute IsConstant: {e}"))
1,024,184✔
274
            .ok()
1,024,184✔
275
            .flatten()
1,024,184✔
276
            .unwrap_or_default()
1,024,184✔
277
    }
1,024,184✔
278

279
    pub fn is_constant_opts(&self, cost: Cost) -> bool {
×
280
        let opts = IsConstantOpts { cost };
×
281
        is_constant_opts(self, &opts)
×
282
            .inspect_err(|e| log::warn!("Failed to compute IsConstant: {e}"))
×
283
            .ok()
×
284
            .flatten()
×
285
            .unwrap_or_default()
×
286
    }
×
287

288
    pub fn as_constant(&self) -> Option<Scalar> {
546,451✔
289
        self.is_constant().then(|| self.scalar_at(0))
546,451✔
290
    }
546,451✔
291

292
    /// Total size of the array in bytes, including all children and buffers.
293
    pub fn nbytes(&self) -> u64 {
192,564✔
294
        let mut nbytes = 0;
192,564✔
295
        for array in self.depth_first_traversal() {
269,241✔
296
            for buffer in array.buffers() {
317,487✔
297
                nbytes += buffer.len() as u64;
317,170✔
298
            }
317,170✔
299
        }
300
        nbytes
192,564✔
301
    }
192,564✔
302
}
303

304
/// Trait for converting a type into a Vortex [`ArrayRef`].
305
pub trait IntoArray {
306
    fn into_array(self) -> ArrayRef;
307
}
308

309
impl IntoArray for ArrayRef {
310
    fn into_array(self) -> ArrayRef {
5,110✔
311
        self
5,110✔
312
    }
5,110✔
313
}
314

315
mod private {
316
    use super::*;
317

318
    pub trait Sealed {}
319

320
    impl<V: VTable> Sealed for ArrayAdapter<V> {}
321
    impl Sealed for Arc<dyn Array> {}
322
}
323

324
/// Adapter struct used to lift the [`VTable`] trait into an object-safe [`Array`]
325
/// implementation.
326
///
327
/// Since this is a unit struct with `repr(transparent)`, we are able to turn un-adapted array
328
/// structs into [`dyn Array`] using some cheeky casting inside [`std::ops::Deref`] and
329
/// [`AsRef`]. See the `vtable!` macro for more details.
330
#[repr(transparent)]
331
pub struct ArrayAdapter<V: VTable>(V::Array);
332

333
impl<V: VTable> ArrayAdapter<V> {
334
    /// Provide a reference to the underlying array held within the adapter.
335
    pub fn as_inner(&self) -> &V::Array {
×
336
        &self.0
×
337
    }
×
338

339
    /// Unwrap into the inner array type, consuming the adapter.
340
    pub fn into_inner(self) -> V::Array {
×
341
        self.0
×
342
    }
×
343
}
344

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

351
impl<V: VTable> Array for ArrayAdapter<V> {
352
    fn as_any(&self) -> &dyn Any {
7,723,441✔
353
        self
7,723,441✔
354
    }
7,723,441✔
355

356
    fn to_array(&self) -> ArrayRef {
756,349✔
357
        Arc::new(ArrayAdapter::<V>(self.0.clone()))
756,349✔
358
    }
756,349✔
359

360
    fn len(&self) -> usize {
196,453,023✔
361
        <V::ArrayVTable as ArrayVTable<V>>::len(&self.0)
196,453,023✔
362
    }
196,453,023✔
363

364
    fn dtype(&self) -> &DType {
368,850,907✔
365
        <V::ArrayVTable as ArrayVTable<V>>::dtype(&self.0)
368,850,907✔
366
    }
368,850,907✔
367

368
    fn encoding(&self) -> EncodingRef {
30,660✔
369
        V::encoding(&self.0)
30,660✔
370
    }
30,660✔
371

372
    fn encoding_id(&self) -> EncodingId {
3,492,913✔
373
        V::encoding(&self.0).id()
3,492,913✔
374
    }
3,492,913✔
375

376
    fn slice(&self, start: usize, stop: usize) -> ArrayRef {
2,856,384✔
377
        if start == 0 && stop == self.len() {
2,856,384✔
378
            return self.to_array();
135,788✔
379
        }
2,720,596✔
380

381
        assert!(
2,720,596✔
382
            start <= self.len(),
2,720,596✔
NEW
383
            "OutOfBounds: start {start} > length {}",
×
NEW
384
            self.len()
×
385
        );
386
        assert!(
2,720,596✔
387
            stop <= self.len(),
2,720,596✔
NEW
388
            "OutOfBounds: stop {stop} > length {}",
×
NEW
389
            self.len()
×
390
        );
391

392
        assert!(start <= stop, "start ({start}) must be <= stop ({stop})");
2,720,596✔
393

394
        if start == stop {
2,720,596✔
395
            return Canonical::empty(self.dtype()).into_array();
454✔
396
        }
2,720,142✔
397

398
        let sliced = <V::OperationsVTable as OperationsVTable<V>>::slice(&self.0, start, stop);
2,720,142✔
399

400
        assert_eq!(
2,720,142✔
401
            sliced.len(),
2,720,142✔
402
            stop - start,
2,720,142✔
403
            "Slice length mismatch {}",
×
404
            self.encoding_id()
×
405
        );
406

407
        // Slightly more expensive, so only do this in debug builds.
408
        debug_assert_eq!(
2,720,142✔
409
            sliced.dtype(),
2,720,142✔
410
            self.dtype(),
2,720,142✔
411
            "Slice dtype mismatch {}",
×
412
            self.encoding_id()
×
413
        );
414

415
        // Propagate some stats from the original array to the sliced array.
416
        if !sliced.is::<ConstantVTable>() {
2,720,142✔
417
            self.statistics().with_iter(|iter| {
2,586,302✔
418
                sliced.statistics().inherit(iter.filter(|(stat, value)| {
2,642,652✔
419
                    matches!(
151,362✔
420
                        stat,
183,837✔
421
                        Stat::IsConstant | Stat::IsSorted | Stat::IsStrictSorted
422
                    ) && value.as_ref().as_exact().is_some_and(|v| {
32,475✔
423
                        Scalar::new(DType::Bool(Nullability::NonNullable), v.clone())
32,475✔
424
                            .as_bool()
32,475✔
425
                            .value()
32,475✔
426
                            .unwrap_or_default()
32,475✔
427
                    })
32,475✔
428
                }));
183,837✔
429
            });
2,586,302✔
430
        }
133,840✔
431

432
        sliced
2,720,142✔
433
    }
2,856,384✔
434

435
    fn scalar_at(&self, index: usize) -> Scalar {
64,226,309✔
436
        assert!(index < self.len(), "index {index} out of bounds");
64,226,309✔
437
        if self.is_invalid(index).vortex_expect("index out of bounds") {
64,226,306✔
438
            return Scalar::null(self.dtype().clone());
3,899,757✔
439
        }
60,326,549✔
440
        let scalar = <V::OperationsVTable as OperationsVTable<V>>::scalar_at(&self.0, index);
60,326,549✔
441
        assert_eq!(self.dtype(), scalar.dtype(), "Scalar dtype mismatch");
60,326,549✔
442
        scalar
60,326,549✔
443
    }
64,226,306✔
444

445
    fn is_valid(&self, index: usize) -> VortexResult<bool> {
76,409,953✔
446
        if index >= self.len() {
76,409,953✔
447
            vortex_bail!(OutOfBounds: index, 0, self.len());
×
448
        }
76,409,953✔
449
        <V::ValidityVTable as ValidityVTable<V>>::is_valid(&self.0, index)
76,409,953✔
450
    }
76,409,953✔
451

452
    fn is_invalid(&self, index: usize) -> VortexResult<bool> {
64,229,682✔
453
        self.is_valid(index).map(|valid| !valid)
64,229,682✔
454
    }
64,229,682✔
455

456
    fn all_valid(&self) -> VortexResult<bool> {
19,367,104✔
457
        <V::ValidityVTable as ValidityVTable<V>>::all_valid(&self.0)
19,367,104✔
458
    }
19,367,104✔
459

460
    fn all_invalid(&self) -> VortexResult<bool> {
447,370✔
461
        <V::ValidityVTable as ValidityVTable<V>>::all_invalid(&self.0)
447,370✔
462
    }
447,370✔
463

464
    fn valid_count(&self) -> VortexResult<usize> {
84,537✔
465
        if let Some(Precision::Exact(invalid_count)) =
11,645✔
466
            self.statistics().get_as::<usize>(Stat::NullCount)
84,537✔
467
        {
468
            return Ok(self.len() - invalid_count);
11,645✔
469
        }
72,892✔
470

471
        let count = <V::ValidityVTable as ValidityVTable<V>>::valid_count(&self.0)?;
72,892✔
472
        assert!(count <= self.len(), "Valid count exceeds array length");
72,892✔
473

474
        self.statistics()
72,892✔
475
            .set(Stat::NullCount, Precision::exact(self.len() - count));
72,892✔
476

477
        Ok(count)
72,892✔
478
    }
84,537✔
479

480
    fn invalid_count(&self) -> VortexResult<usize> {
15,120✔
481
        if let Some(Precision::Exact(invalid_count)) =
994✔
482
            self.statistics().get_as::<usize>(Stat::NullCount)
15,120✔
483
        {
484
            return Ok(invalid_count);
994✔
485
        }
14,126✔
486

487
        let count = <V::ValidityVTable as ValidityVTable<V>>::invalid_count(&self.0)?;
14,126✔
488
        assert!(count <= self.len(), "Invalid count exceeds array length");
14,126✔
489

490
        self.statistics()
14,126✔
491
            .set(Stat::NullCount, Precision::exact(count));
14,126✔
492

493
        Ok(count)
14,126✔
494
    }
15,120✔
495

496
    fn validity_mask(&self) -> VortexResult<Mask> {
859,509✔
497
        let mask = <V::ValidityVTable as ValidityVTable<V>>::validity_mask(&self.0)?;
859,509✔
498
        assert_eq!(mask.len(), self.len(), "Validity mask length mismatch");
859,509✔
499
        Ok(mask)
859,509✔
500
    }
859,509✔
501

502
    fn to_canonical(&self) -> VortexResult<Canonical> {
3,720,301✔
503
        let canonical = <V::CanonicalVTable as CanonicalVTable<V>>::canonicalize(&self.0)?;
3,720,301✔
504
        assert_eq!(
3,720,301✔
505
            self.len(),
3,720,301✔
506
            canonical.as_ref().len(),
3,720,301✔
507
            "Canonical length mismatch {}. Expected {} but encoded into {}.",
×
508
            self.encoding_id(),
×
509
            self.len(),
×
510
            canonical.as_ref().len()
×
511
        );
512
        assert_eq!(
3,720,301✔
513
            self.dtype(),
3,720,301✔
514
            canonical.as_ref().dtype(),
3,720,301✔
515
            "Canonical dtype mismatch {}. Expected {} but encoded into {}.",
×
516
            self.encoding_id(),
×
517
            self.dtype(),
×
518
            canonical.as_ref().dtype()
×
519
        );
520
        canonical
3,720,301✔
521
            .as_ref()
3,720,301✔
522
            .statistics()
3,720,301✔
523
            .inherit_from(self.statistics());
3,720,301✔
524
        Ok(canonical)
3,720,301✔
525
    }
3,720,301✔
526

527
    fn append_to_builder(&self, builder: &mut dyn ArrayBuilder) -> VortexResult<()> {
54,618✔
528
        if builder.dtype() != self.dtype() {
54,618✔
529
            vortex_bail!(
×
530
                "Builder dtype mismatch: expected {}, got {}",
×
531
                self.dtype(),
×
532
                builder.dtype(),
×
533
            );
534
        }
54,618✔
535
        let len = builder.len();
54,618✔
536

537
        <V::CanonicalVTable as CanonicalVTable<V>>::append_to_builder(&self.0, builder)?;
54,618✔
538
        assert_eq!(
54,618✔
539
            len + self.len(),
54,618✔
540
            builder.len(),
54,618✔
541
            "Builder length mismatch after writing array for encoding {}",
×
542
            self.encoding_id(),
×
543
        );
544
        Ok(())
54,618✔
545
    }
54,618✔
546

547
    fn statistics(&self) -> StatsSetRef<'_> {
16,083,406✔
548
        <V::ArrayVTable as ArrayVTable<V>>::stats(&self.0)
16,083,406✔
549
    }
16,083,406✔
550

551
    fn with_children(&self, children: &[ArrayRef]) -> VortexResult<ArrayRef> {
×
552
        struct ReplacementChildren<'a> {
553
            children: &'a [ArrayRef],
554
        }
555

556
        impl ArrayChildren for ReplacementChildren<'_> {
557
            fn get(&self, index: usize, dtype: &DType, len: usize) -> VortexResult<ArrayRef> {
×
558
                if index >= self.children.len() {
×
559
                    vortex_bail!(OutOfBounds: index, 0, self.children.len());
×
560
                }
×
561
                let child = &self.children[index];
×
562
                if child.len() != len {
×
563
                    vortex_bail!(
×
564
                        "Child length mismatch: expected {}, got {}",
×
565
                        len,
566
                        child.len()
×
567
                    );
568
                }
×
569
                if child.dtype() != dtype {
×
570
                    vortex_bail!(
×
571
                        "Child dtype mismatch: expected {}, got {}",
×
572
                        dtype,
573
                        child.dtype()
×
574
                    );
575
                }
×
576
                Ok(child.clone())
×
577
            }
×
578

579
            fn len(&self) -> usize {
×
580
                self.children.len()
×
581
            }
×
582
        }
583

584
        let metadata = self.metadata()?.ok_or_else(|| {
×
585
            vortex_err!("Cannot replace children for arrays that do not support serialization")
×
586
        })?;
×
587

588
        // Replace the children of the array by re-building the array from parts.
589
        self.encoding().build(
×
590
            self.dtype(),
×
591
            self.len(),
×
592
            &metadata,
×
593
            &self.buffers(),
×
594
            &ReplacementChildren { children },
×
595
        )
×
596
    }
×
597

598
    fn invoke(
485,336✔
599
        &self,
485,336✔
600
        compute_fn: &ComputeFn,
485,336✔
601
        args: &InvocationArgs,
485,336✔
602
    ) -> VortexResult<Option<Output>> {
485,336✔
603
        <V::ComputeVTable as ComputeVTable<V>>::invoke(&self.0, compute_fn, args)
485,336✔
604
    }
485,336✔
605
}
606

607
impl<V: VTable> ArrayVisitor for ArrayAdapter<V> {
608
    fn children(&self) -> Vec<ArrayRef> {
368,748✔
609
        struct ChildrenCollector {
610
            children: Vec<ArrayRef>,
611
        }
612

613
        impl ArrayChildVisitor for ChildrenCollector {
614
            fn visit_child(&mut self, _name: &str, array: &dyn Array) {
149,459✔
615
                self.children.push(array.to_array());
149,459✔
616
            }
149,459✔
617
        }
618

619
        let mut collector = ChildrenCollector {
368,748✔
620
            children: Vec::new(),
368,748✔
621
        };
368,748✔
622
        <V::VisitorVTable as VisitorVTable<V>>::visit_children(&self.0, &mut collector);
368,748✔
623
        collector.children
368,748✔
624
    }
368,748✔
625

626
    fn nchildren(&self) -> usize {
×
627
        <V::VisitorVTable as VisitorVTable<V>>::nchildren(&self.0)
×
628
    }
×
629

630
    fn children_names(&self) -> Vec<String> {
741✔
631
        struct ChildNameCollector {
632
            names: Vec<String>,
633
        }
634

635
        impl ArrayChildVisitor for ChildNameCollector {
636
            fn visit_child(&mut self, name: &str, _array: &dyn Array) {
508✔
637
                self.names.push(name.to_string());
508✔
638
            }
508✔
639
        }
640

641
        let mut collector = ChildNameCollector { names: Vec::new() };
741✔
642
        <V::VisitorVTable as VisitorVTable<V>>::visit_children(&self.0, &mut collector);
741✔
643
        collector.names
741✔
644
    }
741✔
645

646
    fn named_children(&self) -> Vec<(String, ArrayRef)> {
×
647
        struct NamedChildrenCollector {
648
            children: Vec<(String, ArrayRef)>,
649
        }
650

651
        impl ArrayChildVisitor for NamedChildrenCollector {
652
            fn visit_child(&mut self, name: &str, array: &dyn Array) {
×
653
                self.children.push((name.to_string(), array.to_array()));
×
654
            }
×
655
        }
656

657
        let mut collector = NamedChildrenCollector {
×
658
            children: Vec::new(),
×
659
        };
×
660

661
        <V::VisitorVTable as VisitorVTable<V>>::visit_children(&self.0, &mut collector);
×
662
        collector.children
×
663
    }
×
664

665
    fn buffers(&self) -> Vec<ByteBuffer> {
281,935✔
666
        struct BufferCollector {
667
            buffers: Vec<ByteBuffer>,
668
        }
669

670
        impl ArrayBufferVisitor for BufferCollector {
671
            fn visit_buffer(&mut self, buffer: &ByteBuffer) {
345,567✔
672
                self.buffers.push(buffer.clone());
345,567✔
673
            }
345,567✔
674
        }
675

676
        let mut collector = BufferCollector {
281,935✔
677
            buffers: Vec::new(),
281,935✔
678
        };
281,935✔
679
        <V::VisitorVTable as VisitorVTable<V>>::visit_buffers(&self.0, &mut collector);
281,935✔
680
        collector.buffers
281,935✔
681
    }
281,935✔
682

683
    fn nbuffers(&self) -> usize {
56,205✔
684
        <V::VisitorVTable as VisitorVTable<V>>::nbuffers(&self.0)
56,205✔
685
    }
56,205✔
686

687
    fn metadata(&self) -> VortexResult<Option<Vec<u8>>> {
60,930✔
688
        Ok(<V::SerdeVTable as SerdeVTable<V>>::metadata(&self.0)?.map(|m| m.serialize()))
60,930✔
689
    }
60,930✔
690

691
    fn metadata_fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
741✔
692
        match <V::SerdeVTable as SerdeVTable<V>>::metadata(&self.0) {
741✔
693
            Err(e) => write!(f, "<serde error: {e}>"),
×
694
            Ok(None) => write!(f, "<serde not supported>"),
×
695
            Ok(Some(metadata)) => Debug::fmt(&metadata, f),
741✔
696
        }
697
    }
741✔
698
}
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