Skip to main content

arrow_buffer/buffer/
null.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use crate::bit_iterator::{BitIndexIterator, BitIterator, BitSliceIterator};
19use crate::buffer::BooleanBuffer;
20use crate::{Buffer, MutableBuffer, OverflowError};
21
22/// A [`BooleanBuffer`] used to encode validity (null values) for Arrow arrays
23///
24/// In the [Arrow specification], array validity is encoded in a packed bitmask with a
25/// `true` value indicating the corresponding slot is not null, and `false` indicating
26/// that it is null.
27///
28/// # See also
29/// * [`NullBufferBuilder`] for creating `NullBuffer`s
30///
31/// [Arrow specification]: https://arrow.apache.org/docs/format/Columnar.html#validity-bitmaps
32/// [`NullBufferBuilder`]: crate::NullBufferBuilder
33#[derive(Debug, Clone, Eq, PartialEq)]
34pub struct NullBuffer {
35    buffer: BooleanBuffer,
36    null_count: usize,
37}
38
39impl NullBuffer {
40    /// Create a new [`NullBuffer`] computing the null count
41    pub fn new(buffer: BooleanBuffer) -> Self {
42        let null_count = buffer.len() - buffer.count_set_bits();
43        Self { buffer, null_count }
44    }
45
46    /// Create a new [`NullBuffer`] of length `len` where all values are null
47    pub fn new_null(len: usize) -> Self {
48        Self {
49            buffer: BooleanBuffer::new_unset(len),
50            null_count: len,
51        }
52    }
53
54    /// Create a new [`NullBuffer`] of length `len` where all values are valid
55    ///
56    /// Note: it is more efficient to not set the null buffer if it is known to
57    /// be all valid (aka all values are not null)
58    pub fn new_valid(len: usize) -> Self {
59        Self {
60            buffer: BooleanBuffer::new_set(len),
61            null_count: 0,
62        }
63    }
64
65    /// Create a new [`NullBuffer`] with the provided `buffer` and `null_count`
66    ///
67    /// # Safety
68    ///
69    /// `buffer` must contain `null_count` `0` bits
70    pub unsafe fn new_unchecked(buffer: BooleanBuffer, null_count: usize) -> Self {
71        Self { buffer, null_count }
72    }
73
74    /// Computes the union of the nulls in two optional [`NullBuffer`]
75    ///
76    /// This is commonly used by binary operations where the result is NULL if either
77    /// of the input values is NULL. Handling the null mask separately in this way
78    /// can yield significant performance improvements over an iterator approach
79    pub fn union(lhs: Option<&NullBuffer>, rhs: Option<&NullBuffer>) -> Option<NullBuffer> {
80        match (lhs, rhs) {
81            (Some(lhs), Some(rhs)) if lhs.null_count() > 0 || rhs.null_count() > 0 => {
82                Some(Self::new(lhs.inner() & rhs.inner()))
83            }
84            (Some(n), None) | (None, Some(n)) if n.null_count() > 0 => Some(n.clone()),
85            (_, _) => None,
86        }
87    }
88
89    /// Computes the union of the nulls in multiple optional [`NullBuffer`]s
90    ///
91    /// See [`union`](Self::union)
92    pub fn union_many<'a>(
93        nulls: impl IntoIterator<Item = Option<&'a NullBuffer>>,
94    ) -> Option<NullBuffer> {
95        // Unwrap to BooleanBuffer because BitAndAssign is not implemented for NullBuffer
96        let mut buffers = nulls.into_iter().filter_map(|nb| match nb {
97            Some(nb) if nb.null_count > 0 => Some(nb.inner()),
98            _ => None,
99        });
100        let first = buffers.next()?;
101        let mut result = first.clone();
102        for buf in buffers {
103            result &= buf;
104        }
105        Some(Self::new(result))
106    }
107
108    /// Returns true if all nulls in `other` also exist in self
109    pub fn contains(&self, other: &NullBuffer) -> bool {
110        if other.null_count == 0 {
111            return true;
112        }
113        let lhs = self.inner().bit_chunks().iter_padded();
114        let rhs = other.inner().bit_chunks().iter_padded();
115        lhs.zip(rhs).all(|(l, r)| (l & !r) == 0)
116    }
117
118    /// Returns a new [`NullBuffer`] where each bit in the current null buffer
119    /// is repeated `count` times. This is useful for masking the nulls of
120    /// the child of a FixedSizeListArray based on its parent
121    ///
122    /// # Panics
123    ///
124    /// Panics if `self.len() * count` overflows `usize`.
125    /// Use [`Self::try_expand`] for a fallible version.
126    pub fn expand(&self, count: usize) -> Self {
127        self.try_expand(count).unwrap_or_else(|err| panic!("{err}"))
128    }
129
130    /// Returns a new [`NullBuffer`] where each bit in the current null buffer
131    /// is repeated `count` times. This is useful for masking the nulls of
132    /// the child of a FixedSizeListArray based on its parent
133    ///
134    /// # Errors
135    ///
136    /// Errors if `self.len() * count` overflows `usize`
137    pub fn try_expand(&self, count: usize) -> Result<Self, OverflowError> {
138        let capacity = self
139            .buffer
140            .len()
141            .checked_mul(count)
142            .ok_or_else(|| OverflowError::new::<usize>("buffer length"))?;
143        let mut buffer = MutableBuffer::new_null(capacity);
144
145        if count.is_multiple_of(8) {
146            // When count is a multiple of 8 every expanded run starts on a byte
147            // boundary (bit i starts at bit i*count, which is divisible by 8),
148            // so we can fill count/8 bytes of 0xFF at a time instead of setting
149            // bits individually.
150            let bytes_per_bit = count / 8;
151            let buf = buffer.as_mut();
152            for (start, end) in BitSliceIterator::new(
153                self.buffer.values(),
154                self.buffer.offset(),
155                self.buffer.len(),
156            ) {
157                let byte_start = start * bytes_per_bit;
158                let byte_end = end * bytes_per_bit;
159                buf[byte_start..byte_end].fill(0xFF);
160            }
161        } else if count.is_multiple_of(4) {
162            // count is a multiple of 4 but not 8: each bit's range starts and ends
163            // on a nibble boundary. Fill any full bytes, then OR in the partial nibble
164            // (0x0F if the range ends mid-byte, 0xF0 if it starts mid-byte).
165            let buf = buffer.as_mut();
166            for i in 0..self.buffer.len() {
167                if self.is_null(i) {
168                    continue;
169                }
170                let start_bit = i * count;
171                let end_bit = start_bit + count;
172                if start_bit.is_multiple_of(8) {
173                    buf[start_bit / 8..end_bit / 8].fill(0xFF);
174                    buf[end_bit / 8] |= 0x0F;
175                } else {
176                    buf[start_bit / 8] |= 0xF0;
177                    buf[start_bit / 8 + 1..end_bit / 8].fill(0xFF);
178                }
179            }
180        } else {
181            // For each contiguous run of valid bits [start, end), the corresponding
182            // output bits [start*count, end*count) are set. Boundary bytes that are
183            // only partially covered are ORed with a mask; fully covered interior
184            // bytes are filled with 0xFF.
185            let buf = buffer.as_mut();
186            for (start, end) in BitSliceIterator::new(
187                self.buffer.values(),
188                self.buffer.offset(),
189                self.buffer.len(),
190            ) {
191                let start_bit = start * count;
192                let end_bit = end * count;
193                let start_byte = start_bit / 8;
194                let start_offset = (start_bit % 8) as u32; // first bit to set within start_byte
195                let end_byte = end_bit / 8;
196                let end_offset = (end_bit % 8) as u32; // one-past-last bit within end_byte
197
198                if start_byte == end_byte {
199                    // All bits land in one byte: mask from start_offset up to end_offset.
200                    // 0xFF << start_offset  → bits [start_offset, 7] set
201                    // (1 << end_offset) - 1 → bits [0, end_offset) set
202                    // AND of both           → bits [start_offset, end_offset) set
203                    buf[start_byte] |= (0xFFu8 << start_offset) & ((1u8 << end_offset) - 1);
204                } else {
205                    if start_offset != 0 {
206                        // Partial leading byte: set bits from start_offset to bit 7.
207                        buf[start_byte] |= 0xFFu8 << start_offset;
208                    }
209                    // Full interior bytes (skip start_byte if it was only partially covered).
210                    let full_start = start_byte + (start_offset != 0) as usize;
211                    buf[full_start..end_byte].fill(0xFF);
212                    if end_offset != 0 {
213                        // Partial trailing byte: set bits 0 up to end_offset.
214                        // (1 << end_offset) - 1 → bits [0, end_offset) set
215                        buf[end_byte] |= (1u8 << end_offset) - 1;
216                    }
217                }
218            }
219        }
220        Ok(Self {
221            buffer: BooleanBuffer::new(buffer.into(), 0, capacity),
222            null_count: self.null_count * count,
223        })
224    }
225
226    /// Returns the length of this [`NullBuffer`] in bits
227    #[inline]
228    pub fn len(&self) -> usize {
229        self.buffer.len()
230    }
231
232    /// Returns the offset of this [`NullBuffer`] in bits
233    #[inline]
234    pub fn offset(&self) -> usize {
235        self.buffer.offset()
236    }
237
238    /// Returns true if this [`NullBuffer`] is empty
239    #[inline]
240    pub fn is_empty(&self) -> bool {
241        self.buffer.is_empty()
242    }
243
244    /// Free up unused memory.
245    pub fn shrink_to_fit(&mut self) {
246        self.buffer.shrink_to_fit();
247    }
248
249    /// Returns the null count for this [`NullBuffer`]
250    #[inline]
251    pub fn null_count(&self) -> usize {
252        self.null_count
253    }
254
255    /// Returns `true` if the value at `idx` is not null
256    ///
257    /// # Panics
258    ///
259    /// Panics if `idx >= self.len()`
260    #[inline]
261    pub fn is_valid(&self, idx: usize) -> bool {
262        self.buffer.value(idx)
263    }
264
265    /// Returns `true` if the value at `idx` is null
266    ///
267    /// # Panics
268    ///
269    /// Panics if `idx >= self.len()`
270    #[inline]
271    pub fn is_null(&self, idx: usize) -> bool {
272        !self.is_valid(idx)
273    }
274
275    /// Returns the packed validity of this [`NullBuffer`] not including any offset
276    #[inline]
277    pub fn validity(&self) -> &[u8] {
278        self.buffer.values()
279    }
280
281    /// Slices this [`NullBuffer`] by the provided `offset` and `length`
282    ///
283    /// # Panics
284    ///
285    /// Panics if `offset + len > self.len()`
286    pub fn slice(&self, offset: usize, len: usize) -> Self {
287        Self::new(self.buffer.slice(offset, len))
288    }
289
290    /// Returns an iterator over the bits in this [`NullBuffer`]
291    ///
292    /// * `true` indicates that the corresponding value is not NULL
293    /// * `false` indicates that the corresponding value is NULL
294    ///
295    /// Note: [`Self::valid_indices`] will be significantly faster for most use-cases
296    pub fn iter(&self) -> BitIterator<'_> {
297        self.buffer.iter()
298    }
299
300    /// Returns a [`BitIndexIterator`] over the valid indices in this [`NullBuffer`]
301    ///
302    /// Valid indices indicate the corresponding value is not NULL
303    pub fn valid_indices(&self) -> BitIndexIterator<'_> {
304        self.buffer.set_indices()
305    }
306
307    /// Returns a [`BitSliceIterator`] yielding contiguous ranges of valid indices
308    ///
309    /// Valid indices indicate the corresponding value is not NULL
310    pub fn valid_slices(&self) -> BitSliceIterator<'_> {
311        self.buffer.set_slices()
312    }
313
314    /// Calls the provided closure for each index in this null mask that is set
315    #[inline]
316    pub fn try_for_each_valid_idx<E, F: FnMut(usize) -> Result<(), E>>(
317        &self,
318        f: F,
319    ) -> Result<(), E> {
320        if self.null_count == self.len() {
321            return Ok(());
322        }
323        self.valid_indices().try_for_each(f)
324    }
325
326    /// Returns the inner [`BooleanBuffer`]
327    #[inline]
328    pub fn inner(&self) -> &BooleanBuffer {
329        &self.buffer
330    }
331
332    /// Returns the inner [`BooleanBuffer`]
333    #[inline]
334    pub fn into_inner(self) -> BooleanBuffer {
335        self.buffer
336    }
337
338    /// Returns the underlying [`Buffer`]
339    #[inline]
340    pub fn buffer(&self) -> &Buffer {
341        self.buffer.inner()
342    }
343
344    /// Create a [`NullBuffer`] from an *unsliced* validity bitmap (`offset = 0` **bits**) of length `len`.
345    ///
346    /// Returns `None` if there are no nulls (all values valid).
347    pub fn from_unsliced_buffer(buffer: impl Into<Buffer>, len: usize) -> Option<Self> {
348        let bb = BooleanBuffer::new(buffer.into(), 0, len);
349        let nb = NullBuffer::new(bb);
350        (nb.null_count() > 0).then_some(nb)
351    }
352
353    /// Claim memory used by this null buffer in the provided memory pool.
354    #[cfg(feature = "pool")]
355    pub fn claim(&self, pool: &dyn crate::MemoryPool) {
356        // NullBuffer wraps a BooleanBuffer which wraps a Buffer
357        self.buffer.inner().claim(pool);
358    }
359}
360
361impl<'a> IntoIterator for &'a NullBuffer {
362    type Item = bool;
363    type IntoIter = BitIterator<'a>;
364
365    fn into_iter(self) -> Self::IntoIter {
366        self.buffer.iter()
367    }
368}
369
370impl From<BooleanBuffer> for NullBuffer {
371    fn from(value: BooleanBuffer) -> Self {
372        Self::new(value)
373    }
374}
375
376impl From<&[bool]> for NullBuffer {
377    fn from(value: &[bool]) -> Self {
378        BooleanBuffer::from(value).into()
379    }
380}
381
382impl<const N: usize> From<&[bool; N]> for NullBuffer {
383    fn from(value: &[bool; N]) -> Self {
384        value[..].into()
385    }
386}
387
388impl From<Vec<bool>> for NullBuffer {
389    fn from(value: Vec<bool>) -> Self {
390        BooleanBuffer::from(value).into()
391    }
392}
393
394impl FromIterator<bool> for NullBuffer {
395    fn from_iter<T: IntoIterator<Item = bool>>(iter: T) -> Self {
396        BooleanBuffer::from_iter(iter).into()
397    }
398}
399
400#[cfg(test)]
401mod tests {
402    use super::*;
403
404    #[test]
405    fn test_size() {
406        // This tests that the niche optimisation eliminates the overhead of an option
407        assert_eq!(
408            std::mem::size_of::<NullBuffer>(),
409            std::mem::size_of::<Option<NullBuffer>>()
410        );
411    }
412
413    #[test]
414    fn test_from_unsliced_buffer_with_nulls() {
415        // 0b10110010 → null(0), valid(1), null(2), null(3), valid(4), valid(5), null(6), valid(7)
416        let buf = Buffer::from([0b10110010u8]);
417        let result = NullBuffer::from_unsliced_buffer(buf, 8);
418        assert!(result.is_some());
419        let nb = result.unwrap();
420        assert_eq!(nb.len(), 8);
421        assert_eq!(nb.null_count(), 4);
422        assert!(nb.is_null(0));
423        assert!(nb.is_valid(1));
424        assert!(nb.is_null(2));
425        assert!(nb.is_null(3));
426        assert!(nb.is_valid(4));
427        assert!(nb.is_valid(5));
428        assert!(nb.is_null(6));
429        assert!(nb.is_valid(7));
430    }
431
432    #[test]
433    fn test_from_unsliced_buffer_all_valid() {
434        // All bits set = all valid, no nulls
435        let buf = Buffer::from([0b11111111u8]);
436        let result = NullBuffer::from_unsliced_buffer(buf, 8);
437        assert!(result.is_none());
438    }
439
440    #[test]
441    fn test_from_unsliced_buffer_all_null() {
442        // No bits set = all null
443        let buf = Buffer::from([0b00000000u8]);
444        let result = NullBuffer::from_unsliced_buffer(buf, 8);
445        assert!(result.is_some());
446        let nb = result.unwrap();
447        assert_eq!(nb.len(), 8);
448        assert_eq!(nb.null_count(), 8);
449    }
450
451    #[test]
452    fn test_from_unsliced_buffer_empty() {
453        let buf = Buffer::from([]);
454        let result = NullBuffer::from_unsliced_buffer(buf, 0);
455        assert!(result.is_none());
456    }
457
458    #[test]
459    fn test_union_many_all_none() {
460        let result = NullBuffer::union_many([None, None, None]);
461        assert!(result.is_none());
462    }
463
464    #[test]
465    fn test_union_many_single_some() {
466        let a = NullBuffer::from(&[true, false, true, true]);
467        let result = NullBuffer::union_many([Some(&a)]);
468        assert_eq!(result, Some(a));
469    }
470
471    #[test]
472    fn test_union_many_two_inputs() {
473        let a = NullBuffer::from(&[true, false, true, true]);
474        let b = NullBuffer::from(&[true, true, false, true]);
475        let result = NullBuffer::union_many([Some(&a), Some(&b)]);
476        let expected = NullBuffer::union(Some(&a), Some(&b));
477        assert_eq!(result, expected);
478    }
479
480    #[test]
481    fn test_union_many_three_inputs() {
482        let a = NullBuffer::from(&[true, false, true, true]);
483        let b = NullBuffer::from(&[true, true, false, true]);
484        let c = NullBuffer::from(&[false, true, true, true]);
485        let result = NullBuffer::union_many([Some(&a), Some(&b), Some(&c)]);
486        let expected = NullBuffer::from(&[false, false, false, true]);
487        assert_eq!(result, Some(expected));
488    }
489
490    #[test]
491    fn test_union_many_mixed_none() {
492        let a = NullBuffer::from(&[true, false, true, true]);
493        let b = NullBuffer::from(&[false, true, true, true]);
494        let result = NullBuffer::union_many([Some(&a), None, Some(&b)]);
495        let expected = NullBuffer::union(Some(&a), Some(&b));
496        assert_eq!(result, expected);
497    }
498
499    #[test]
500    fn test_union_many_empty_slice() {
501        let result = NullBuffer::union_many([] as [Option<&NullBuffer>; 0]);
502        assert!(result.is_none());
503    }
504
505    #[test]
506    fn test_union_many_no_nulls() {
507        let a = NullBuffer::from(&[true, true, true, true]);
508
509        let result = NullBuffer::union_many([Some(&a), Some(&a), Some(&a)]);
510        assert_eq!(result, None);
511    }
512
513    #[test]
514    fn test_union_no_nulls() {
515        let a = NullBuffer::from(&[true, true, true, true]);
516
517        let result = NullBuffer::union(Some(&a), Some(&a));
518        assert_eq!(result, None);
519
520        let result = NullBuffer::union(Some(&a), None);
521        assert_eq!(result, None);
522
523        let result = NullBuffer::union(None, Some(&a));
524        assert_eq!(result, None);
525    }
526
527    #[test]
528    fn test_union_nulls_one_side() {
529        let all_valid = NullBuffer::from(&[true, true, true, true]);
530        let all_null = NullBuffer::from(&[false, false, false, false]);
531
532        let result = NullBuffer::union(Some(&all_valid), Some(&all_null));
533        assert_eq!(result, Some(all_null.clone()));
534
535        let result = NullBuffer::union(Some(&all_null), Some(&all_valid));
536        assert_eq!(result, Some(all_null.clone()));
537    }
538
539    #[test]
540    fn test_expand_code_paths() {
541        let source = NullBuffer::from(&[true, false, true] as &[bool]);
542
543        for count in [8, 4, 3] {
544            let expanded = source.expand(count);
545            assert_eq!(expanded.len(), 3 * count);
546            assert_eq!(expanded.null_count(), count);
547            assert!((0..count).all(|i| expanded.is_valid(i)));
548            assert!((count..2 * count).all(|i| expanded.is_null(i)));
549            assert!((2 * count..3 * count).all(|i| expanded.is_valid(i)));
550        }
551    }
552}