Skip to main content

arrow_buffer/builder/
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::{BooleanBufferBuilder, MutableBuffer, NullBuffer};
19
20/// Builder for creating [`NullBuffer`]s (bitmaps indicating validity/nulls).
21///
22/// # See also
23/// * [`BooleanBufferBuilder`] for a lower-level bitmap builder.
24/// * [`Self::allocated_size`] for the current memory allocated by the builder.
25///
26/// # Performance
27///
28/// This builder only materializes the buffer when null values (`false`) are
29/// appended. If you only append non-null, (`true`) to the builder, no buffer is
30/// allocated and [`build`](#method.build) or [`finish`](#method.finish) return
31/// `None`.
32///
33/// This optimization is **very** important for the performance as it avoids
34/// allocating memory for the null buffer when there are no nulls.
35///
36/// # Example
37/// ```
38/// # use arrow_buffer::NullBufferBuilder;
39/// let mut builder = NullBufferBuilder::new(8);
40/// builder.append_n_non_nulls(8);
41/// // If no non null values are appended, the null buffer is not created
42/// let buffer = builder.finish();
43/// assert!(buffer.is_none());
44/// // however, if a null value is appended, the null buffer is created
45/// let mut builder = NullBufferBuilder::new(8);
46/// builder.append_n_non_nulls(7);
47/// builder.append_null();
48/// let buffer = builder.finish().unwrap();
49/// assert_eq!(buffer.len(), 8);
50/// assert_eq!(buffer.iter().collect::<Vec<_>>(), vec![true, true, true, true, true, true, true, false]);
51/// ```
52#[derive(Debug)]
53pub struct NullBufferBuilder {
54    /// The bitmap builder to store the null buffer:
55    /// * `Some` if any nulls have been appended ("materialized")
56    /// * `None` if no nulls have been appended.
57    bitmap_builder: Option<BooleanBufferBuilder>,
58    /// Length of the buffer before materializing.
59    ///
60    /// if `bitmap_buffer` buffer is `Some`, this value is not used.
61    len: usize,
62    /// Initial capacity of the `bitmap_builder`, when it is materialized.
63    capacity: usize,
64}
65
66impl NullBufferBuilder {
67    /// Creates a new empty builder.
68    ///
69    /// Note that this method does not allocate any memory, regardless of the
70    /// `capacity` parameter. If an allocation is required, `capacity` is the
71    /// size in bits (not bytes) that will be allocated at minimum.
72    pub fn new(capacity: usize) -> Self {
73        Self {
74            bitmap_builder: None,
75            len: 0,
76            capacity,
77        }
78    }
79
80    /// Creates a new builder with given length.
81    pub fn new_with_len(len: usize) -> Self {
82        Self {
83            bitmap_builder: None,
84            len,
85            capacity: len,
86        }
87    }
88
89    /// Creates a new builder from a `MutableBuffer`.
90    ///
91    /// # Panics
92    ///
93    /// Panics if `len > buffer.len() * 8`
94    pub fn new_from_buffer(buffer: MutableBuffer, len: usize) -> Self {
95        let capacity = buffer.len() * 8;
96        assert!(len <= capacity);
97
98        let bitmap_builder = Some(BooleanBufferBuilder::new_from_buffer(buffer, len));
99        Self {
100            bitmap_builder,
101            len,
102            capacity,
103        }
104    }
105
106    /// Appends `n` `true`s into the builder
107    /// to indicate that these `n` items are not nulls.
108    #[inline]
109    pub fn append_n_non_nulls(&mut self, n: usize) {
110        if let Some(buf) = self.bitmap_builder.as_mut() {
111            buf.append_n(n, true)
112        } else {
113            self.len += n;
114        }
115    }
116
117    /// Appends a `true` into the builder
118    /// to indicate that this item is not null.
119    #[inline]
120    pub fn append_non_null(&mut self) {
121        if let Some(buf) = self.bitmap_builder.as_mut() {
122            buf.append(true)
123        } else {
124            self.len += 1;
125        }
126    }
127
128    /// Appends `n` `false`s into the builder
129    /// to indicate that these `n` items are nulls.
130    #[inline]
131    pub fn append_n_nulls(&mut self, n: usize) {
132        self.materialize_if_needed().append_n(n, false);
133    }
134
135    /// Appends a `false` into the builder
136    /// to indicate that this item is null.
137    #[inline]
138    pub fn append_null(&mut self) {
139        self.materialize_if_needed().append(false);
140    }
141
142    /// Appends a boolean value into the builder.
143    #[inline]
144    pub fn append(&mut self, not_null: bool) {
145        if not_null {
146            self.append_non_null()
147        } else {
148            self.append_null()
149        }
150    }
151
152    /// Appends the low `count` bits from `word` as validity values.
153    ///
154    /// `word` is treated as a packed LSB-first bitmap, where set bits indicate
155    /// non-null values. Only the lowest `count` bits are appended; higher bits
156    /// are ignored.
157    ///
158    /// This does not materialize the null buffer when all appended bits are set.
159    ///
160    /// # Panics
161    ///
162    /// Panics if `count` is greater than 64.
163    #[inline]
164    pub fn append_word(&mut self, word: u64, count: usize) {
165        assert!(count <= 64, "count must be less than or equal to 64");
166        if word.trailing_ones() as usize >= count {
167            self.append_n_non_nulls(count);
168        } else {
169            self.materialize_if_needed().append_word(word, count)
170        }
171    }
172
173    /// Sets a bit in the builder at `index`
174    ///
175    /// # Panics
176    ///
177    /// Panics for the same reasons as [`BooleanBufferBuilder::set_bit`]
178    #[inline]
179    pub fn set_bit(&mut self, index: usize, v: bool) {
180        self.materialize_if_needed().set_bit(index, v);
181    }
182
183    /// Gets a bit in the buffer at `index`
184    ///
185    /// # Panics
186    ///
187    /// Panics for the same reasons as [`BooleanBufferBuilder::get_bit`], but only if
188    /// a bitmap has been materialized (i.e. a null was appended)
189    #[inline]
190    pub fn is_valid(&self, index: usize) -> bool {
191        if let Some(ref buf) = self.bitmap_builder {
192            buf.get_bit(index)
193        } else {
194            true
195        }
196    }
197
198    /// Truncates the builder to the given length
199    ///
200    /// If `len` is greater than the buffer's current length, this has no effect
201    #[inline]
202    pub fn truncate(&mut self, len: usize) {
203        if let Some(buf) = self.bitmap_builder.as_mut() {
204            buf.truncate(len);
205        } else if len <= self.len {
206            self.len = len
207        }
208    }
209
210    /// Appends a boolean slice into the builder
211    /// to indicate the validations of these items.
212    pub fn append_slice(&mut self, slice: &[bool]) {
213        // First check if not already materialized before checking if there are any nulls
214        if self.bitmap_builder.is_none() && slice.iter().any(|v| !v) {
215            self.materialize_if_needed();
216        }
217        if let Some(buf) = self.bitmap_builder.as_mut() {
218            buf.append_slice(slice)
219        } else {
220            self.len += slice.len();
221        }
222    }
223
224    /// Append [`NullBuffer`] to this [`NullBufferBuilder`]
225    ///
226    /// This is useful when you want to concatenate two null buffers.
227    pub fn append_buffer(&mut self, buffer: &NullBuffer) {
228        if buffer.null_count() > 0 {
229            self.materialize_if_needed();
230        }
231        if let Some(buf) = self.bitmap_builder.as_mut() {
232            buf.append_buffer(buffer.inner())
233        } else {
234            self.len += buffer.len();
235        }
236    }
237
238    /// Builds the [`NullBuffer`] and resets the builder.
239    ///
240    /// Returns `None` if the builder only contains `true`s. Use [`Self::build`]
241    /// when you don't need to reuse this builder.
242    pub fn finish(&mut self) -> Option<NullBuffer> {
243        self.len = 0;
244        Some(NullBuffer::new(self.bitmap_builder.take()?.build()))
245    }
246
247    /// Builds the [`NullBuffer`] without resetting the builder.
248    ///
249    /// This consumes the builder. Use [`Self::finish`] to reuse it.
250    pub fn build(self) -> Option<NullBuffer> {
251        self.bitmap_builder.map(NullBuffer::from)
252    }
253
254    /// Builds the [NullBuffer] without resetting the builder.
255    pub fn finish_cloned(&self) -> Option<NullBuffer> {
256        let buffer = self.bitmap_builder.as_ref()?.finish_cloned();
257        Some(NullBuffer::new(buffer))
258    }
259
260    /// Returns the inner bitmap builder as slice
261    pub fn as_slice(&self) -> Option<&[u8]> {
262        Some(self.bitmap_builder.as_ref()?.as_slice())
263    }
264
265    fn materialize_if_needed(&mut self) -> &mut BooleanBufferBuilder {
266        let (len, capacity) = (self.len, self.capacity);
267        self.bitmap_builder
268            .get_or_insert_with(|| Self::materialize(len, capacity))
269    }
270
271    #[cold]
272    fn materialize(len: usize, capacity: usize) -> BooleanBufferBuilder {
273        let mut b = BooleanBufferBuilder::new(len.max(capacity));
274        b.append_n(len, true);
275        b
276    }
277
278    /// Return a mutable reference to the inner bitmap slice.
279    pub fn as_slice_mut(&mut self) -> Option<&mut [u8]> {
280        self.bitmap_builder.as_mut().map(|b| b.as_slice_mut())
281    }
282
283    /// Return the allocated size of this builder, in bytes, useful for memory accounting.
284    pub fn allocated_size(&self) -> usize {
285        self.bitmap_builder
286            .as_ref()
287            .map(|b| b.capacity() / 8)
288            .unwrap_or(0)
289    }
290
291    /// Return the number of bits in the buffer.
292    pub fn len(&self) -> usize {
293        self.bitmap_builder.as_ref().map_or(self.len, |b| b.len())
294    }
295
296    /// Check if the builder is empty.
297    pub fn is_empty(&self) -> bool {
298        self.len() == 0
299    }
300}
301
302#[cfg(test)]
303mod tests {
304    use super::*;
305
306    #[test]
307    fn test_null_buffer_builder() {
308        let mut builder = NullBufferBuilder::new(0);
309        builder.append_null();
310        builder.append_non_null();
311        builder.append_n_nulls(2);
312        builder.append_n_non_nulls(2);
313        assert_eq!(6, builder.len());
314        assert_eq!(64, builder.allocated_size());
315
316        let buf = builder.finish().unwrap();
317        assert_eq!(&[0b110010_u8], buf.validity());
318    }
319
320    #[test]
321    fn test_null_buffer_builder_all_nulls() {
322        let mut builder = NullBufferBuilder::new(0);
323        builder.append_null();
324        builder.append_n_nulls(2);
325        builder.append_slice(&[false, false, false]);
326        assert_eq!(6, builder.len());
327        assert_eq!(64, builder.allocated_size());
328
329        let buf = builder.finish().unwrap();
330        assert_eq!(&[0b0_u8], buf.validity());
331    }
332
333    #[test]
334    fn test_null_buffer_builder_no_null() {
335        let mut builder = NullBufferBuilder::new(0);
336        builder.append_non_null();
337        builder.append_n_non_nulls(2);
338        builder.append_slice(&[true, true, true]);
339        assert_eq!(6, builder.len());
340        assert_eq!(0, builder.allocated_size());
341
342        let buf = builder.finish();
343        assert!(buf.is_none());
344    }
345
346    #[test]
347    fn test_null_buffer_builder_reset() {
348        let mut builder = NullBufferBuilder::new(0);
349        builder.append_slice(&[true, false, true]);
350        builder.finish();
351        assert!(builder.is_empty());
352
353        builder.append_slice(&[true, true, true]);
354        assert!(builder.finish().is_none());
355        assert!(builder.is_empty());
356
357        builder.append_slice(&[true, true, false, true]);
358
359        let buf = builder.finish().unwrap();
360        assert_eq!(&[0b1011_u8], buf.validity());
361    }
362
363    #[test]
364    fn test_append_word_all_valid() {
365        let mut builder = NullBufferBuilder::new(0);
366        builder.append_word(u64::MAX, 64);
367        builder.append_word(0b0111, 3);
368
369        assert_eq!(builder.len(), 67);
370        assert_eq!(builder.allocated_size(), 0);
371        assert!(builder.finish().is_none());
372    }
373
374    #[test]
375    fn test_append_word_mixed() {
376        let mut builder = NullBufferBuilder::new(0);
377        builder.append_n_non_nulls(3);
378        builder.append_word(0b1100_1010, 6);
379        builder.append_word(0b1111, 4);
380
381        let buffer = builder.finish().unwrap();
382        assert_eq!(&[0b0101_0111, 0b0001_1110], buffer.validity());
383    }
384
385    #[test]
386    #[should_panic(expected = "count must be less than or equal to 64")]
387    fn test_append_word_count_too_large() {
388        let mut builder = NullBufferBuilder::new(0);
389        builder.append_word(u64::MAX, 65);
390    }
391
392    #[test]
393    fn test_null_buffer_builder_is_valid() {
394        let mut builder = NullBufferBuilder::new(0);
395        builder.append_n_non_nulls(6);
396        assert!(builder.is_valid(0));
397
398        builder.append_null();
399        assert!(!builder.is_valid(6));
400
401        builder.append_non_null();
402        assert!(builder.is_valid(7));
403    }
404
405    #[test]
406    fn test_null_buffer_builder_truncate() {
407        let mut builder = NullBufferBuilder::new(10);
408        builder.append_n_non_nulls(16);
409        assert_eq!(builder.as_slice(), None);
410        builder.truncate(20);
411        assert_eq!(builder.as_slice(), None);
412        assert_eq!(builder.len(), 16);
413        assert_eq!(builder.allocated_size(), 0);
414        builder.truncate(14);
415        assert_eq!(builder.as_slice(), None);
416        assert_eq!(builder.len(), 14);
417        builder.append_null();
418        builder.append_non_null();
419        assert_eq!(builder.as_slice().unwrap(), &[0xFF, 0b10111111]);
420        assert_eq!(builder.allocated_size(), 64);
421    }
422
423    #[test]
424    fn test_null_buffer_builder_truncate_never_materialized() {
425        let mut builder = NullBufferBuilder::new(0);
426        assert_eq!(builder.len(), 0);
427        builder.append_n_nulls(2); // doesn't materialize
428        assert_eq!(builder.len(), 2);
429        builder.truncate(1);
430        assert_eq!(builder.len(), 1);
431    }
432
433    #[test]
434    fn test_append_buffers() {
435        let mut builder = NullBufferBuilder::new(0);
436        let buffer1 = NullBuffer::from(&[true, true]);
437        let buffer2 = NullBuffer::from(&[true, true, false]);
438
439        builder.append_buffer(&buffer1);
440        builder.append_buffer(&buffer2);
441
442        assert_eq!(builder.as_slice().unwrap(), &[0b01111_u8]);
443    }
444
445    #[test]
446    fn test_append_buffers_with_unaligned_length() {
447        let mut builder = NullBufferBuilder::new(0);
448        let buffer = NullBuffer::from(&[true, true, false, true, false]);
449        builder.append_buffer(&buffer);
450        assert_eq!(builder.as_slice().unwrap(), &[0b01011_u8]);
451
452        let buffer = NullBuffer::from(&[false, false, true, true, true, false, false]);
453        builder.append_buffer(&buffer);
454        assert_eq!(builder.as_slice().unwrap(), &[0b10001011_u8, 0b0011_u8]);
455    }
456
457    #[test]
458    fn test_append_empty_buffer() {
459        let mut builder = NullBufferBuilder::new(0);
460        let buffer = NullBuffer::from(&[true, true, false, true]);
461        builder.append_buffer(&buffer);
462        assert_eq!(builder.as_slice().unwrap(), &[0b1011_u8]);
463
464        let buffer = NullBuffer::from(&[]);
465        builder.append_buffer(&buffer);
466
467        assert_eq!(builder.as_slice().unwrap(), &[0b1011_u8]);
468    }
469
470    #[test]
471    fn test_should_not_materialize_when_appending_all_valid_buffers() {
472        let mut builder = NullBufferBuilder::new(0);
473        let buffer = NullBuffer::from(&[true; 10]);
474        builder.append_buffer(&buffer);
475
476        let buffer = NullBuffer::from(&[true; 2]);
477        builder.append_buffer(&buffer);
478
479        assert_eq!(builder.finish(), None);
480    }
481}