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

vortex-data / vortex / 16992684502

15 Aug 2025 02:56PM UTC coverage: 87.875% (+0.2%) from 87.72%
16992684502

Pull #2456

github

web-flow
Merge 2d540e578 into 4a23f65b3
Pull Request #2456: feat: basic BoolBuffer / BoolBufferMut

1275 of 1428 new or added lines in 110 files covered. (89.29%)

334 existing lines in 31 files now uncovered.

57169 of 65057 relevant lines covered (87.88%)

658056.52 hits per line

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

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

4
//! Array validity and nullability behavior, used by arrays and compute functions.
5

6
use std::fmt::Debug;
7
use std::ops::{BitAnd, Not};
8

9
use arrow_buffer::NullBuffer;
10
use vortex_buffer::BitBuffer;
11
use vortex_dtype::{DType, Nullability};
12
use vortex_error::{VortexExpect as _, VortexResult, vortex_bail, vortex_err, vortex_panic};
13
use vortex_mask::{AllOr, Mask, MaskValues};
14
use vortex_scalar::Scalar;
15

16
use crate::arrays::{BoolArray, ConstantArray};
17
use crate::compute::{fill_null, filter, sum, take};
18
use crate::patches::Patches;
19
use crate::{Array, ArrayRef, IntoArray, ToCanonical};
20

21
/// Validity information for an array
22
#[derive(Clone, Debug)]
23
pub enum Validity {
24
    /// Items *can't* be null
25
    NonNullable,
26
    /// All items are valid
27
    AllValid,
28
    /// All items are null
29
    AllInvalid,
30
    /// Specified items are null
31
    Array(ArrayRef),
32
}
33

34
impl Validity {
35
    /// The [`DType`] of the underlying validity array (if it exists).
36
    pub const DTYPE: DType = DType::Bool(Nullability::NonNullable);
37

38
    pub fn null_count(&self, length: usize) -> VortexResult<usize> {
×
39
        match self {
×
40
            Self::NonNullable | Self::AllValid => Ok(0),
×
41
            Self::AllInvalid => Ok(length),
×
42
            Self::Array(a) => {
×
43
                let validity_len = a.len();
×
44
                if validity_len != length {
×
45
                    vortex_bail!(
×
46
                        "Validity array length {} doesn't match array length {}",
×
47
                        validity_len,
48
                        length
49
                    )
50
                }
×
51
                let true_count = sum(a)?
×
52
                    .as_primitive()
×
53
                    .as_::<usize>()?
×
54
                    .ok_or_else(|| vortex_err!("Failed to compute true count"))?;
×
55
                Ok(length - true_count)
×
56
            }
57
        }
58
    }
×
59

60
    /// If Validity is [`Validity::Array`], returns the array, otherwise returns `None`.
61
    pub fn into_array(self) -> Option<ArrayRef> {
×
62
        match self {
×
63
            Self::Array(a) => Some(a),
×
64
            _ => None,
×
65
        }
66
    }
×
67

68
    /// If Validity is [`Validity::Array`], returns a reference to the array array, otherwise returns `None`.
69
    pub fn as_array(&self) -> Option<&ArrayRef> {
×
70
        match self {
×
71
            Self::Array(a) => Some(a),
×
72
            _ => None,
×
73
        }
74
    }
×
75

76
    pub fn nullability(&self) -> Nullability {
5,096,386✔
77
        match self {
5,096,386✔
78
            Self::NonNullable => Nullability::NonNullable,
4,102,181✔
79
            _ => Nullability::Nullable,
994,205✔
80
        }
81
    }
5,096,386✔
82

83
    /// The union nullability and validity.
84
    pub fn union_nullability(self, nullability: Nullability) -> Self {
907✔
85
        match nullability {
907✔
86
            Nullability::NonNullable => self,
406✔
87
            Nullability::Nullable => self.into_nullable(),
501✔
88
        }
89
    }
907✔
90

91
    pub fn all_valid(&self) -> VortexResult<bool> {
19,413,053✔
92
        Ok(match self {
19,413,053✔
93
            Validity::NonNullable | Validity::AllValid => true,
19,400,103✔
94
            Validity::AllInvalid => false,
46✔
95
            Validity::Array(array) => {
12,904✔
96
                u64::try_from(&sum(array)?).vortex_expect("bool sum has to be u64")
12,904✔
97
                    == array.len() as u64
12,904✔
98
            }
99
        })
100
    }
19,413,053✔
101

102
    pub fn all_invalid(&self) -> VortexResult<bool> {
521,000✔
103
        Ok(match self {
521,000✔
104
            Validity::NonNullable | Validity::AllValid => false,
488,878✔
105
            Validity::AllInvalid => true,
765✔
106
            Validity::Array(array) => {
31,357✔
107
                u64::try_from(sum(array)?).vortex_expect("bool sum has to be u64") == 0
31,357✔
108
            }
109
        })
110
    }
521,000✔
111

112
    /// Returns whether the `index` item is valid.
113
    #[inline]
114
    pub fn is_valid(&self, index: usize) -> VortexResult<bool> {
74,628,028✔
115
        Ok(match self {
74,628,028✔
116
            Self::NonNullable | Self::AllValid => true,
68,242,390✔
117
            Self::AllInvalid => false,
173,882✔
118
            Self::Array(a) => {
6,211,756✔
119
                let scalar = a.scalar_at(index)?;
6,211,756✔
120
                scalar
6,211,756✔
121
                    .as_bool()
6,211,756✔
122
                    .value()
6,211,756✔
123
                    .vortex_expect("Validity must be non-nullable")
6,211,756✔
124
            }
125
        })
126
    }
74,628,028✔
127

128
    #[inline]
129
    pub fn is_null(&self, index: usize) -> VortexResult<bool> {
147,615✔
130
        Ok(!self.is_valid(index)?)
147,615✔
131
    }
147,615✔
132

133
    pub fn slice(&self, start: usize, stop: usize) -> VortexResult<Self> {
3,943,151✔
134
        match self {
3,943,151✔
135
            Self::Array(a) => Ok(Self::Array(a.slice(start, stop)?)),
9,885✔
136
            _ => Ok(self.clone()),
3,933,266✔
137
        }
138
    }
3,943,151✔
139

140
    pub fn take(&self, indices: &dyn Array) -> VortexResult<Self> {
75,925✔
141
        match self {
75,925✔
142
            Self::NonNullable => match indices.validity_mask()?.bit_buffer() {
59,587✔
143
                AllOr::All => {
144
                    if indices.dtype().is_nullable() {
52,645✔
145
                        Ok(Self::AllValid)
277✔
146
                    } else {
147
                        Ok(Self::NonNullable)
52,368✔
148
                    }
149
                }
150
                AllOr::None => Ok(Self::AllInvalid),
1✔
151
                AllOr::Some(buf) => Ok(Validity::from(buf.clone())),
6,941✔
152
            },
153
            Self::AllValid => match indices.validity_mask()?.bit_buffer() {
6,698✔
154
                AllOr::All => Ok(Self::AllValid),
3,262✔
155
                AllOr::None => Ok(Self::AllInvalid),
1✔
156
                AllOr::Some(buf) => Ok(Validity::from(buf.clone())),
3,435✔
157
            },
158
            Self::AllInvalid => Ok(Self::AllInvalid),
123✔
159
            Self::Array(is_valid) => {
9,517✔
160
                let maybe_is_valid = take(is_valid, indices)?;
9,517✔
161
                // Null indices invalidate that position.
162
                let is_valid = fill_null(&maybe_is_valid, &Scalar::from(false))?;
9,517✔
163
                Ok(Self::Array(is_valid))
9,517✔
164
            }
165
        }
166
    }
75,925✔
167

168
    /// Keep only the entries for which the mask is true.
169
    ///
170
    /// The result has length equal to the number of true values in mask.
171
    pub fn filter(&self, mask: &Mask) -> VortexResult<Self> {
58,728✔
172
        // NOTE(ngates): we take the mask as a reference to avoid the caller cloning unnecessarily
173
        //  if we happen to be NonNullable, AllValid, or AllInvalid.
174
        match self {
58,728✔
175
            v @ (Validity::NonNullable | Validity::AllValid | Validity::AllInvalid) => {
46,881✔
176
                Ok(v.clone())
46,881✔
177
            }
178
            Validity::Array(arr) => Ok(Validity::Array(filter(arr, mask)?)),
11,847✔
179
        }
180
    }
58,728✔
181

182
    /// Set to false any entries for which the mask is true.
183
    ///
184
    /// The result is always nullable. The result has the same length as self.
185
    pub fn mask(&self, mask: &Mask) -> VortexResult<Self> {
11,585✔
186
        match mask.bit_buffer() {
11,585✔
187
            AllOr::All => Ok(Validity::AllInvalid),
×
188
            AllOr::None => Ok(self.clone()),
×
189
            AllOr::Some(make_invalid) => Ok(match self {
11,585✔
190
                Validity::NonNullable | Validity::AllValid => {
191
                    Validity::Array(BoolArray::from(make_invalid.not()).into_array())
4,748✔
192
                }
193
                Validity::AllInvalid => Validity::AllInvalid,
55✔
194
                Validity::Array(is_valid) => {
6,782✔
195
                    let is_valid = is_valid.to_bool()?;
6,782✔
196
                    let keep_valid = make_invalid.not();
6,782✔
197
                    Validity::from(is_valid.bit_buffer().bitand(&keep_valid))
6,782✔
198
                }
199
            }),
200
        }
201
    }
11,585✔
202

203
    pub fn to_mask(&self, length: usize) -> VortexResult<Mask> {
1,480,920✔
204
        Ok(match self {
1,480,920✔
205
            Self::NonNullable | Self::AllValid => Mask::AllTrue(length),
1,409,419✔
206
            Self::AllInvalid => Mask::AllFalse(length),
499✔
207
            Self::Array(is_valid) => {
71,002✔
208
                assert_eq!(
71,002✔
209
                    is_valid.len(),
71,002✔
210
                    length,
211
                    "Validity::Array length must equal to_logical's argument: {}, {}.",
×
212
                    is_valid.len(),
×
213
                    length,
214
                );
215
                Mask::try_from(&is_valid.to_bool()?)?
71,002✔
216
            }
217
        })
218
    }
1,480,920✔
219

220
    /// Logically & two Validity values of the same length
221
    pub fn and(self, rhs: Validity) -> VortexResult<Validity> {
55✔
222
        let validity = match (self, rhs) {
55✔
223
            // Should be pretty clear
224
            (Validity::NonNullable, Validity::NonNullable) => Validity::NonNullable,
55✔
225
            // Any `AllInvalid` makes the output all invalid values
226
            (Validity::AllInvalid, _) | (_, Validity::AllInvalid) => Validity::AllInvalid,
×
227
            // All truthy values on one side, which makes no effect on an `Array` variant
228
            (Validity::Array(a), Validity::AllValid)
×
229
            | (Validity::Array(a), Validity::NonNullable)
×
230
            | (Validity::NonNullable, Validity::Array(a))
×
231
            | (Validity::AllValid, Validity::Array(a)) => Validity::Array(a),
×
232
            // Both sides are all valid
233
            (Validity::NonNullable, Validity::AllValid)
234
            | (Validity::AllValid, Validity::NonNullable)
235
            | (Validity::AllValid, Validity::AllValid) => Validity::AllValid,
×
236
            // Here we actually have to do some work
237
            (Validity::Array(lhs), Validity::Array(rhs)) => {
×
238
                let lhs = lhs.to_bool()?;
×
239
                let rhs = rhs.to_bool()?;
×
240

NEW
241
                let lhs = lhs.bit_buffer();
×
NEW
242
                let rhs = rhs.bit_buffer();
×
243

244
                Validity::from(lhs.bitand(rhs))
×
245
            }
246
        };
247

248
        Ok(validity)
55✔
249
    }
55✔
250

251
    pub fn patch(
12,004✔
252
        self,
12,004✔
253
        len: usize,
12,004✔
254
        indices_offset: usize,
12,004✔
255
        indices: &dyn Array,
12,004✔
256
        patches: &Validity,
12,004✔
257
    ) -> VortexResult<Self> {
12,004✔
258
        match (&self, patches) {
12,004✔
259
            (Validity::NonNullable, Validity::NonNullable) => return Ok(Validity::NonNullable),
9,217✔
260
            (Validity::NonNullable, _) => {
261
                vortex_bail!("Can't patch a non-nullable validity with nullable validity")
1✔
262
            }
263
            (_, Validity::NonNullable) => {
264
                vortex_bail!("Can't patch a nullable validity with non-nullable validity")
×
265
            }
266
            (Validity::AllValid, Validity::AllValid) => return Ok(Validity::AllValid),
1✔
267
            (Validity::AllInvalid, Validity::AllInvalid) => return Ok(Validity::AllInvalid),
1✔
268
            _ => {}
2,784✔
269
        };
270

271
        let own_nullability = if self == Validity::NonNullable {
2,784✔
272
            Nullability::NonNullable
×
273
        } else {
274
            Nullability::Nullable
2,784✔
275
        };
276

277
        let source = match self {
2,784✔
NEW
278
            Validity::NonNullable => BoolArray::from(BitBuffer::new_set(len)),
×
279
            Validity::AllValid => BoolArray::from(BitBuffer::new_set(len)),
392✔
280
            Validity::AllInvalid => BoolArray::from(BitBuffer::new_unset(len)),
2,311✔
281
            Validity::Array(a) => a.to_bool()?,
81✔
282
        };
283

284
        let patch_values = match patches {
2,784✔
NEW
285
            Validity::NonNullable => BoolArray::from(BitBuffer::new_set(indices.len())),
×
286
            Validity::AllValid => BoolArray::from(BitBuffer::new_set(indices.len())),
1,804✔
287
            Validity::AllInvalid => BoolArray::from(BitBuffer::new_unset(indices.len())),
2✔
288
            Validity::Array(a) => a.to_bool()?,
978✔
289
        };
290

291
        let patches = Patches::new(
2,784✔
292
            len,
2,784✔
293
            indices_offset,
2,784✔
294
            indices.to_array(),
2,784✔
295
            patch_values.into_array(),
2,784✔
296
        );
297

298
        Ok(Self::from_array(
2,784✔
299
            source.patch(&patches)?.into_array(),
2,784✔
300
            own_nullability,
2,784✔
301
        ))
302
    }
12,004✔
303

304
    /// Convert into a nullable variant
305
    pub fn into_nullable(self) -> Validity {
19,228✔
306
        match self {
19,228✔
307
            Self::NonNullable => Self::AllValid,
18,991✔
308
            _ => self,
237✔
309
        }
310
    }
19,228✔
311

312
    /// Convert into a non-nullable variant
313
    pub fn into_non_nullable(self) -> Option<Validity> {
2,555✔
314
        match self {
2,555✔
315
            Self::NonNullable => Some(Self::NonNullable),
508✔
316
            Self::AllValid => Some(Self::NonNullable),
1,282✔
317
            Self::AllInvalid => None,
×
318
            Self::Array(is_valid) => {
765✔
319
                is_valid
765✔
320
                    .statistics()
765✔
321
                    .compute_min::<bool>()
765✔
322
                    .vortex_expect("validity array must support min")
765✔
323
                    .then(|| {
765✔
324
                        // min true => all true
325
                        Self::NonNullable
238✔
326
                    })
238✔
327
            }
328
        }
329
    }
2,555✔
330

331
    /// Convert into a variant compatible with the given nullability, if possible.
332
    pub fn cast_nullability(self, nullability: Nullability) -> VortexResult<Validity> {
9,010✔
333
        match nullability {
9,010✔
334
            Nullability::NonNullable => self.into_non_nullable().ok_or_else(|| {
2,555✔
335
                vortex_err!("Cannot cast array with invalid values to non-nullable type.")
527✔
336
            }),
527✔
337
            Nullability::Nullable => Ok(self.into_nullable()),
6,455✔
338
        }
339
    }
9,010✔
340

341
    /// Create Validity by copying the given array's validity.
342
    pub fn copy_from_array(array: &dyn Array) -> VortexResult<Self> {
23,516✔
343
        Ok(Validity::from_mask(
23,516✔
344
            array.validity_mask()?,
23,516✔
345
            array.dtype().nullability(),
23,516✔
346
        ))
347
    }
23,516✔
348

349
    /// Create Validity from boolean array with given nullability of the array.
350
    ///
351
    /// Note: You want to pass the nullability of parent array and not the nullability of the validity array itself
352
    ///     as that is always nonnullable
353
    fn from_array(value: ArrayRef, nullability: Nullability) -> Self {
2,784✔
354
        if !matches!(value.dtype(), DType::Bool(Nullability::NonNullable)) {
2,784✔
355
            vortex_panic!("Expected a non-nullable boolean array")
×
356
        }
2,784✔
357
        match nullability {
2,784✔
358
            Nullability::NonNullable => Self::NonNullable,
×
359
            Nullability::Nullable => Self::Array(value),
2,784✔
360
        }
361
    }
2,784✔
362

363
    /// Returns the length of the validity array, if it exists.
364
    pub fn maybe_len(&self) -> Option<usize> {
6,223,412✔
365
        match self {
6,223,412✔
366
            Self::NonNullable | Self::AllValid | Self::AllInvalid => None,
6,094,763✔
367
            Self::Array(a) => Some(a.len()),
128,649✔
368
        }
369
    }
6,223,412✔
370

371
    pub fn uncompressed_size(&self) -> usize {
×
372
        if let Validity::Array(a) = self {
×
373
            a.len().div_ceil(8)
×
374
        } else {
375
            0
×
376
        }
377
    }
×
378

379
    pub fn is_array(&self) -> bool {
×
380
        matches!(self, Validity::Array(_))
×
381
    }
×
382
}
383

384
impl PartialEq for Validity {
385
    fn eq(&self, other: &Self) -> bool {
63,769✔
386
        match (self, other) {
63,769✔
387
            (Self::NonNullable, Self::NonNullable) => true,
36,448✔
388
            (Self::AllValid, Self::AllValid) => true,
126✔
389
            (Self::AllInvalid, Self::AllInvalid) => true,
123✔
390
            (Self::Array(a), Self::Array(b)) => {
526✔
391
                let a = a
526✔
392
                    .to_bool()
526✔
393
                    .vortex_expect("Failed to get Validity Array as BoolArray");
526✔
394
                let b = b
526✔
395
                    .to_bool()
526✔
396
                    .vortex_expect("Failed to get Validity Array as BoolArray");
526✔
397
                a.bit_buffer() == b.bit_buffer()
526✔
398
            }
399
            _ => false,
26,546✔
400
        }
401
    }
63,769✔
402
}
403

404
impl From<BitBuffer> for Validity {
405
    fn from(value: BitBuffer) -> Self {
68,659✔
406
        let true_count = value.true_count();
68,659✔
407
        if true_count == value.len() {
68,659✔
408
            Self::AllValid
374✔
409
        } else if true_count == 0 {
68,285✔
410
            Self::AllInvalid
2,235✔
411
        } else {
412
            Self::Array(BoolArray::from(value).into_array())
66,050✔
413
        }
414
    }
68,659✔
415
}
416

417
impl From<NullBuffer> for Validity {
418
    fn from(value: NullBuffer) -> Self {
24,608✔
419
        let bitbuffer: BitBuffer = value.into_inner().into();
24,608✔
420
        bitbuffer.into()
24,608✔
421
    }
24,608✔
422
}
423

424
impl FromIterator<Mask> for Validity {
425
    fn from_iter<T: IntoIterator<Item = Mask>>(iter: T) -> Self {
×
426
        Validity::from_mask(iter.into_iter().collect(), Nullability::Nullable)
×
427
    }
×
428
}
429

430
impl FromIterator<bool> for Validity {
431
    fn from_iter<T: IntoIterator<Item = bool>>(iter: T) -> Self {
3,391✔
432
        Validity::from(BitBuffer::from_iter(iter))
3,391✔
433
    }
3,391✔
434
}
435

436
impl From<Nullability> for Validity {
437
    fn from(value: Nullability) -> Self {
49,812✔
438
        match value {
49,812✔
439
            Nullability::NonNullable => Validity::NonNullable,
43,823✔
440
            Nullability::Nullable => Validity::AllValid,
5,989✔
441
        }
442
    }
49,812✔
443
}
444

445
impl Validity {
446
    pub fn from_mask(mask: Mask, nullability: Nullability) -> Self {
24,282✔
447
        assert!(
24,282✔
448
            nullability == Nullability::Nullable || matches!(mask, Mask::AllTrue(_)),
24,282✔
449
            "NonNullable validity must be AllValid",
2✔
450
        );
451
        match mask {
24,280✔
452
            Mask::AllTrue(_) => match nullability {
20,057✔
453
                Nullability::NonNullable => Validity::NonNullable,
19,740✔
454
                Nullability::Nullable => Validity::AllValid,
317✔
455
            },
456
            Mask::AllFalse(_) => Validity::AllInvalid,
78✔
457
            Mask::Values(values) => Validity::Array(values.into_array()),
4,145✔
458
        }
459
    }
24,280✔
460
}
461

462
impl IntoArray for Mask {
463
    fn into_array(self) -> ArrayRef {
40✔
464
        match self {
40✔
465
            Self::AllTrue(len) => ConstantArray::new(true, len).into_array(),
×
466
            Self::AllFalse(len) => ConstantArray::new(false, len).into_array(),
×
467
            Self::Values(a) => a.into_array(),
40✔
468
        }
469
    }
40✔
470
}
471

472
impl IntoArray for &MaskValues {
473
    fn into_array(self) -> ArrayRef {
4,224✔
474
        BoolArray::new(self.bit_buffer().clone(), Validity::NonNullable).into_array()
4,224✔
475
    }
4,224✔
476
}
477

478
#[cfg(test)]
479
mod tests {
480
    use rstest::rstest;
481
    use vortex_buffer::{Buffer, buffer};
482
    use vortex_dtype::Nullability;
483
    use vortex_mask::Mask;
484

485
    use crate::arrays::{BoolArray, PrimitiveArray};
486
    use crate::validity::Validity;
487
    use crate::{ArrayRef, IntoArray};
488

489
    #[rstest]
490
    #[case(Validity::AllValid, 5, &[2, 4], Validity::AllValid, Validity::AllValid)]
491
    #[case(Validity::AllValid, 5, &[2, 4], Validity::AllInvalid, Validity::Array(BoolArray::from_iter([true, true, false, true, false]).into_array())
492
    )]
493
    #[case(Validity::AllValid, 5, &[2, 4], Validity::Array(BoolArray::from_iter([true, false]).into_array()), Validity::Array(BoolArray::from_iter([true, true, true, true, false]).into_array())
494
    )]
495
    #[case(Validity::AllInvalid, 5, &[2, 4], Validity::AllValid, Validity::Array(BoolArray::from_iter([false, false, true, false, true]).into_array())
496
    )]
497
    #[case(Validity::AllInvalid, 5, &[2, 4], Validity::AllInvalid, Validity::AllInvalid)]
498
    #[case(Validity::AllInvalid, 5, &[2, 4], Validity::Array(BoolArray::from_iter([true, false]).into_array()), Validity::Array(BoolArray::from_iter([false, false, true, false, false]).into_array())
499
    )]
500
    #[case(Validity::Array(BoolArray::from_iter([false, true, false, true, false]).into_array()), 5, &[2, 4], Validity::AllValid, Validity::Array(BoolArray::from_iter([false, true, true, true, true]).into_array())
501
    )]
502
    #[case(Validity::Array(BoolArray::from_iter([false, true, false, true, false]).into_array()), 5, &[2, 4], Validity::AllInvalid, Validity::Array(BoolArray::from_iter([false, true, false, true, false]).into_array())
503
    )]
504
    #[case(Validity::Array(BoolArray::from_iter([false, true, false, true, false]).into_array()), 5, &[2, 4], Validity::Array(BoolArray::from_iter([true, false]).into_array()), Validity::Array(BoolArray::from_iter([false, true, true, true, false]).into_array())
505
    )]
506
    fn patch_validity(
507
        #[case] validity: Validity,
508
        #[case] len: usize,
509
        #[case] positions: &[u64],
510
        #[case] patches: Validity,
511
        #[case] expected: Validity,
512
    ) {
513
        let indices =
514
            PrimitiveArray::new(Buffer::copy_from(positions), Validity::NonNullable).into_array();
515
        assert_eq!(
516
            validity.patch(len, 0, &indices, &patches).unwrap(),
517
            expected
518
        );
519
    }
520

521
    #[test]
522
    #[should_panic]
523
    fn out_of_bounds_patch() {
1✔
524
        Validity::NonNullable
1✔
525
            .patch(2, 0, &buffer![4].into_array(), &Validity::AllInvalid)
1✔
526
            .unwrap();
1✔
527
    }
1✔
528

529
    #[test]
530
    #[should_panic]
531
    fn into_validity_nullable() {
1✔
532
        Validity::from_mask(Mask::AllFalse(10), Nullability::NonNullable);
1✔
533
    }
1✔
534

535
    #[test]
536
    #[should_panic]
537
    fn into_validity_nullable_array() {
1✔
538
        Validity::from_mask(Mask::from_iter(vec![true, false]), Nullability::NonNullable);
1✔
539
    }
1✔
540

541
    #[rstest]
542
    #[case(Validity::AllValid, PrimitiveArray::new(buffer![0, 1], Validity::from_iter(vec![true, false])).into_array(), Validity::from_iter(vec![true, false]))]
543
    #[case(Validity::AllValid, buffer![0, 1].into_array(), Validity::AllValid)]
544
    #[case(Validity::AllValid, PrimitiveArray::new(buffer![0, 1], Validity::AllInvalid).into_array(), Validity::AllInvalid)]
545
    #[case(Validity::NonNullable, PrimitiveArray::new(buffer![0, 1], Validity::from_iter(vec![true, false])).into_array(), Validity::from_iter(vec![true, false]))]
546
    #[case(Validity::NonNullable, buffer![0, 1].into_array(), Validity::NonNullable)]
547
    #[case(Validity::NonNullable, PrimitiveArray::new(buffer![0, 1], Validity::AllInvalid).into_array(), Validity::AllInvalid)]
548
    fn validity_take(
549
        #[case] validity: Validity,
550
        #[case] indices: ArrayRef,
551
        #[case] expected: Validity,
552
    ) {
553
        assert_eq!(validity.take(&indices).unwrap(), expected);
554
    }
555
}
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

© 2025 Coveralls, Inc