Skip to main content

arrow_buffer/buffer/
boolean.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_chunk_iterator::{BitChunks, UnalignedBitChunk};
19use crate::bit_iterator::{BitIndexIterator, BitIndexU32Iterator, BitIterator, BitSliceIterator};
20use crate::bit_util::read_u64;
21use crate::{
22    BooleanBufferBuilder, Buffer, MutableBuffer, bit_util, buffer_bin_and, buffer_bin_or,
23    buffer_bin_xor,
24};
25
26use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not};
27
28/// A slice-able [`Buffer`] containing bit-packed booleans
29///
30/// This structure represents a sequence of boolean values packed into a
31/// byte-aligned [`Buffer`]. Both the offset and length are represented in bits.
32///
33/// # Layout
34///
35/// The values are represented as little endian bit-packed values, where the
36/// least significant bit of each byte represents the first boolean value and
37/// then proceeding to the most significant bit.
38///
39/// For example, the 10 bit bitmask `0b0111001101` has length 10, and is
40/// represented using 2 bytes with offset 0 like this:
41///
42/// ```text
43///        ┌─────────────────────────────────┐    ┌─────────────────────────────────┐
44///        │┌───┬───┬───┬───┬───┬───┬───┬───┐│    │┌───┬───┬───┬───┬───┬───┬───┬───┐│
45///        ││ 1 │ 0 │ 1 │ 1 │ 0 │ 0 │ 1 │ 1 ││    ││ 1 │ 0 │ ? │ ? │ ? │ ? │ ? │ ? ││
46///        │└───┴───┴───┴───┴───┴───┴───┴───┘│    │└───┴───┴───┴───┴───┴───┴───┴───┘│
47/// bit    └─────────────────────────────────┘    └─────────────────────────────────┘
48/// offset  0             Byte 0             7    0              Byte 1            7
49///
50///         length = 10 bits, offset = 0
51/// ```
52///
53/// The same bitmask with length 10 and offset 3 would be represented using 2
54/// bytes like this:
55///
56/// ```text
57///       ┌─────────────────────────────────┐    ┌─────────────────────────────────┐
58///       │┌───┬───┬───┬───┬───┬───┬───┬───┐│    │┌───┬───┬───┬───┬───┬───┬───┬───┐│
59///       ││ ? │ ? │ ? │ 1 │ 0 │ 1 │ 1 │ 0 ││    ││ 0 │ 1 │ 1 │ 1 │ 0 │ ? │ ? │ ? ││
60///       │└───┴───┴───┴───┴───┴───┴───┴───┘│    │└───┴───┴───┴───┴───┴───┴───┴───┘│
61/// bit   └─────────────────────────────────┘    └─────────────────────────────────┘
62/// offset 0             Byte 0             7    0              Byte 1            7
63///
64///        length = 10 bits, offset = 3
65/// ```
66///
67/// Note that the bits marked `?` are not logically part of the mask and may
68/// contain either `0` or `1`
69///
70/// # Bitwise Operations
71///
72/// `BooleanBuffer` implements the standard bitwise traits for creating a new
73/// buffer ([`BitAnd`], [`BitOr`], [`BitXor`], [`Not`]) as well as the assign variants
74/// for updating an existing buffer in place when possible ([`BitAndAssign`],
75/// [`BitOrAssign`], [`BitXorAssign`]).
76///
77/// ```
78/// # use arrow_buffer::BooleanBuffer;
79/// let mut left = BooleanBuffer::from(&[true, false, true, true] as &[bool]);
80/// let right = BooleanBuffer::from(&[true, true, false, true] as &[bool]);
81///
82/// // Create a new buffer by applying bitwise AND
83/// let anded = &left & &right;
84/// assert_eq!(anded, BooleanBuffer::from(&[true, false, false, true] as &[bool]));
85///
86/// // Update `left` in place by applying bitwise AND in place
87/// left &= &right;
88/// assert_eq!(left, BooleanBuffer::from(&[true, false, false, true] as &[bool]));
89/// ```
90///
91/// # See Also
92/// * [`BooleanBufferBuilder`] for building [`BooleanBuffer`] instances
93/// * [`NullBuffer`] for representing null values in Arrow arrays
94///
95/// [`NullBuffer`]: crate::NullBuffer
96#[derive(Debug, Clone, Eq)]
97pub struct BooleanBuffer {
98    /// Underlying buffer (byte aligned)
99    buffer: Buffer,
100    /// Offset in bits (not bytes)
101    bit_offset: usize,
102    /// Length in bits (not bytes)
103    bit_len: usize,
104}
105
106impl PartialEq for BooleanBuffer {
107    fn eq(&self, other: &Self) -> bool {
108        if self.bit_len != other.bit_len {
109            return false;
110        }
111
112        let lhs = self.bit_chunks().iter_padded();
113        let rhs = other.bit_chunks().iter_padded();
114        lhs.zip(rhs).all(|(a, b)| a == b)
115    }
116}
117
118impl BooleanBuffer {
119    /// Create a new [`BooleanBuffer`] from a [`Buffer`], `bit_offset` offset and `bit_len` length
120    ///
121    /// # Panics
122    ///
123    /// This method will panic if `buffer` is not large enough
124    pub fn new(buffer: Buffer, bit_offset: usize, bit_len: usize) -> Self {
125        let total_len = bit_offset.saturating_add(bit_len);
126        let buffer_len = buffer.len();
127        let buffer_bit_len = buffer_len.saturating_mul(8);
128        assert!(
129            total_len <= buffer_bit_len,
130            "buffer not large enough (bit_offset: {bit_offset}, bit_len: {bit_len}, buffer_len: {buffer_len})"
131        );
132        Self {
133            buffer,
134            bit_offset,
135            bit_len,
136        }
137    }
138
139    /// Create a new [`BooleanBuffer`] of `length` bits (not bytes) where all values are `true`
140    pub fn new_set(length: usize) -> Self {
141        let mut builder = BooleanBufferBuilder::new(length);
142        builder.append_n(length, true);
143        builder.finish()
144    }
145
146    /// Create a new [`BooleanBuffer`] of `length` bits (not bytes) where all values are `false`
147    pub fn new_unset(length: usize) -> Self {
148        let buffer = MutableBuffer::new_null(length).into_buffer();
149        Self {
150            buffer,
151            bit_offset: 0,
152            bit_len: length,
153        }
154    }
155
156    /// Invokes `f` with indexes `0..len` collecting the boolean results into a new `BooleanBuffer`
157    pub fn collect_bool<F: FnMut(usize) -> bool>(len: usize, f: F) -> Self {
158        let buffer = MutableBuffer::collect_bool(len, f);
159        Self::new(buffer.into(), 0, len)
160    }
161
162    /// Create a new [`BooleanBuffer`] by copying the relevant bits from an
163    /// input buffer.
164    ///
165    /// # Notes:
166    /// * The new `BooleanBuffer` may have non zero offset
167    ///   and/or padding bits outside the logical range.
168    ///
169    /// # Example: Create a new [`BooleanBuffer`] copying a bit slice from in input slice
170    /// ```
171    /// # use arrow_buffer::BooleanBuffer;
172    /// let input = [0b11001100u8, 0b10111010u8];
173    /// // // Copy bits 4..16 from input
174    /// let result = BooleanBuffer::from_bits(&input, 4, 12);
175    /// // output is 12 bits long starting from bit offset 4
176    /// assert_eq!(result.len(), 12);
177    /// assert_eq!(result.offset(), 4);
178    /// // the expected 12 bits are 0b101110101100 (bits 4..16 of the input)
179    /// let expected_bits = [false, false, true, true, false, true, false, true, true, true, false, true];
180    /// for (i, v) in expected_bits.into_iter().enumerate() {
181    ///    assert_eq!(result.value(i), v);
182    /// }
183    /// // However, underlying buffer has (ignored) bits set outside the requested range
184    /// assert_eq!(result.values(), &[0b11001100u8, 0b10111010, 0, 0, 0, 0, 0, 0]);
185    /// ```
186    pub fn from_bits(src: impl AsRef<[u8]>, offset_in_bits: usize, len_in_bits: usize) -> Self {
187        Self::from_bitwise_unary_op(src, offset_in_bits, len_in_bits, |a| a)
188    }
189
190    /// Create a new [`BooleanBuffer`] by applying the bitwise operation to `op`
191    /// to an input buffer.
192    ///
193    /// This function is faster than applying the operation bit by bit as
194    /// it processes input buffers in chunks of 64 bits (8 bytes) at a time
195    ///
196    /// # Notes:
197    /// * `op` takes a single `u64` inputs and produces one `u64` output.
198    /// * `op` must only apply bitwise operations
199    ///   on the relevant bits; the input `u64` may contain irrelevant bits
200    ///   and may be processed differently on different endian architectures.
201    /// * `op` may be called with input bits outside the requested range
202    /// * Returned `BooleanBuffer` may have non zero offset
203    /// * Returned `BooleanBuffer` may have bits set outside the requested range
204    ///
205    /// # See Also
206    /// - [`BooleanBuffer::from_bitwise_binary_op`] to create a new buffer from a binary operation
207    /// - [`apply_bitwise_unary_op`](bit_util::apply_bitwise_unary_op) for in-place unary bitwise operations
208    ///
209    /// # Example: Create new [`BooleanBuffer`] from bitwise `NOT`
210    /// ```
211    /// # use arrow_buffer::BooleanBuffer;
212    /// let input = [0b11001100u8, 0b10111010u8]; // 2 bytes = 16 bits
213    /// // NOT of bits 4..16
214    /// let result = BooleanBuffer::from_bitwise_unary_op(
215    ///  &input, 4, 12, |a| !a
216    /// );
217    /// // output is 12 bits long starting from bit offset 4
218    /// assert_eq!(result.len(), 12);
219    /// assert_eq!(result.offset(), 4);
220    /// // the expected 12 bits are 0b001100110101, (NOT of the requested bits)
221    /// let expected_bits = [true, true, false, false, true, false, true, false, false, false, true, false];
222    /// for (i, v) in expected_bits.into_iter().enumerate() {
223    ///     assert_eq!(result.value(i), v);
224    /// }
225    /// // However, underlying buffer has (ignored) bits set outside the requested range
226    /// let expected = [0b00110011u8, 0b01000101u8, 255, 255, 255, 255, 255, 255];
227    /// assert_eq!(result.values(), &expected);
228    /// ```
229    pub fn from_bitwise_unary_op<F>(
230        src: impl AsRef<[u8]>,
231        offset_in_bits: usize,
232        len_in_bits: usize,
233        mut op: F,
234    ) -> Self
235    where
236        F: FnMut(u64) -> u64,
237    {
238        let end = offset_in_bits + len_in_bits;
239        // Align start and end to 64 bit (8 byte) boundaries if possible to allow using the
240        // optimized code path as much as possible.
241        let aligned_offset = offset_in_bits & !63;
242        let aligned_end_bytes = bit_util::ceil(end, 64) * 8;
243        let src_len = src.as_ref().len();
244        let slice_end = aligned_end_bytes.min(src_len);
245
246        let aligned_start = &src.as_ref()[aligned_offset / 8..slice_end];
247
248        let (prefix, aligned_u64s, suffix) = unsafe { aligned_start.as_ref().align_to::<u64>() };
249        match (prefix, suffix) {
250            ([], []) => {
251                // the buffer is word (64 bit) aligned, so use optimized Vec code.
252                let result_u64s: Vec<u64> = aligned_u64s.iter().map(|l| op(*l)).collect();
253                return BooleanBuffer::new(result_u64s.into(), offset_in_bits % 64, len_in_bits);
254            }
255            ([], suffix) => {
256                let suffix = read_u64(suffix);
257                let result_u64s: Vec<u64> = aligned_u64s
258                    .iter()
259                    .copied()
260                    .chain(std::iter::once(suffix))
261                    .map(&mut op)
262                    .collect();
263                return BooleanBuffer::new(result_u64s.into(), offset_in_bits % 64, len_in_bits);
264            }
265            _ => {}
266        }
267
268        // align to byte boundaries
269        // Use unaligned code path, handle remainder bytes
270        let (chunks, remainder) = aligned_start.as_chunks::<8>();
271        let iter = chunks.iter().map(|c| u64::from_le_bytes(*c));
272        let vec_u64s: Vec<u64> = if remainder.is_empty() {
273            iter.map(&mut op).collect()
274        } else {
275            iter.chain(Some(read_u64(remainder))).map(&mut op).collect()
276        };
277
278        BooleanBuffer::new(vec_u64s.into(), offset_in_bits % 64, len_in_bits)
279    }
280
281    /// Create a new [`BooleanBuffer`] by applying the bitwise operation `op` to
282    /// the relevant bits from two input buffers.
283    ///
284    /// This function is faster than applying the operation bit by bit as
285    /// it processes input buffers in chunks of 64 bits (8 bytes) at a time
286    ///
287    /// # Notes:
288    /// * `op` takes two `u64` inputs and produces one `u64` output.
289    /// * `op` must only apply bitwise operations
290    ///   on the relevant bits; the input `u64` values may contain irrelevant bits
291    ///   and may be processed differently on different endian architectures.
292    /// * `op` may be called with input bits outside the requested range.
293    /// * Returned `BooleanBuffer` may have non zero offset
294    /// * Returned `BooleanBuffer` may have bits set outside the requested range
295    ///
296    /// # See Also
297    /// - [`BooleanBuffer::from_bitwise_unary_op`] for unary operations on a single input buffer.
298    /// - [`apply_bitwise_binary_op`](bit_util::apply_bitwise_binary_op) for in-place binary bitwise operations
299    ///
300    /// # Example: Create new [`BooleanBuffer`] from bitwise `AND` of two [`Buffer`]s
301    /// ```
302    /// # use arrow_buffer::{Buffer, BooleanBuffer};
303    /// let left = Buffer::from(vec![0b11001100u8, 0b10111010u8]); // 2 bytes = 16 bits
304    /// let right = Buffer::from(vec![0b10101010u8, 0b11011100u8, 0b11110000u8]); // 3 bytes = 24 bits
305    /// // AND of the first 12 bits
306    /// let result = BooleanBuffer::from_bitwise_binary_op(
307    ///   &left, 0, &right, 0, 12, |a, b| a & b
308    /// );
309    /// assert_eq!(result.len(), 12);
310    /// for i in 0..12 {
311    ///     assert_eq!(result.value(i), left.as_slice()[i / 8] >> (i % 8) & 1 == 1
312    ///         && right.as_slice()[i / 8] >> (i % 8) & 1 == 1);
313    /// }
314    /// ```
315    ///
316    /// # Example: Create new [`BooleanBuffer`] from bitwise `OR` of two byte slices
317    /// ```
318    /// # use arrow_buffer::{BooleanBuffer, bit_util};
319    /// let left = [0b11001100u8, 0b10111010u8];
320    /// let right = [0b10101010u8, 0b11011100u8];
321    /// // OR of bits 4..16 from left and bits 0..12 from right
322    /// let result = BooleanBuffer::from_bitwise_binary_op(
323    ///  &left, 4, &right, 0, 12, |a, b| a | b
324    /// );
325    /// assert_eq!(result.len(), 12);
326    /// for i in 0..12 {
327    ///     let l = bit_util::get_bit(&left, 4 + i);
328    ///     let r = bit_util::get_bit(&right, i);
329    ///     assert_eq!(result.value(i), l | r);
330    /// }
331    /// ```
332    pub fn from_bitwise_binary_op<F>(
333        left: impl AsRef<[u8]>,
334        left_offset_in_bits: usize,
335        right: impl AsRef<[u8]>,
336        right_offset_in_bits: usize,
337        len_in_bits: usize,
338        mut op: F,
339    ) -> Self
340    where
341        F: FnMut(u64, u64) -> u64,
342    {
343        let left = left.as_ref();
344        let right = right.as_ref();
345
346        // When both offsets share the same sub-64-bit alignment, we can
347        // align both to 64-bit boundaries and zip u64s directly,
348        // avoiding BitChunks bit-shifting entirely.
349        if left_offset_in_bits % 64 == right_offset_in_bits % 64 {
350            let bit_offset = left_offset_in_bits % 64;
351            let left_end = left_offset_in_bits + len_in_bits;
352            let right_end = right_offset_in_bits + len_in_bits;
353
354            let left_aligned = left_offset_in_bits & !63;
355            let right_aligned = right_offset_in_bits & !63;
356
357            let left_end_bytes = (bit_util::ceil(left_end, 64) * 8).min(left.len());
358            let right_end_bytes = (bit_util::ceil(right_end, 64) * 8).min(right.len());
359
360            let left_slice = &left[left_aligned / 8..left_end_bytes];
361            let right_slice = &right[right_aligned / 8..right_end_bytes];
362
363            let (lp, left_u64s, ls) = unsafe { left_slice.align_to::<u64>() };
364            let (rp, right_u64s, rs) = unsafe { right_slice.align_to::<u64>() };
365
366            match (lp, ls, rp, rs) {
367                ([], [], [], []) => {
368                    let result_u64s: Vec<u64> = left_u64s
369                        .iter()
370                        .zip(right_u64s.iter())
371                        .map(|(l, r)| op(*l, *r))
372                        .collect();
373                    return BooleanBuffer::new(result_u64s.into(), bit_offset, len_in_bits);
374                }
375                ([], left_suf, [], right_suf) => {
376                    let left_iter = left_u64s
377                        .iter()
378                        .copied()
379                        .chain((!left_suf.is_empty()).then(|| read_u64(left_suf)));
380                    let right_iter = right_u64s
381                        .iter()
382                        .copied()
383                        .chain((!right_suf.is_empty()).then(|| read_u64(right_suf)));
384                    let result_u64s: Vec<u64> =
385                        left_iter.zip(right_iter).map(|(l, r)| op(l, r)).collect();
386                    return BooleanBuffer::new(result_u64s.into(), bit_offset, len_in_bits);
387                }
388                _ => {}
389            }
390
391            // Memory not u64-aligned, use chunks_exact fallback
392            let (left_chunks, left_rem) = left_slice.as_chunks::<8>();
393            let (right_chunks, right_rem) = right_slice.as_chunks::<8>();
394
395            let left_iter = left_chunks.iter().map(|c| u64::from_le_bytes(*c));
396            let right_iter = right_chunks.iter().map(|c| u64::from_le_bytes(*c));
397
398            let result_u64s: Vec<u64> = if left_rem.is_empty() && right_rem.is_empty() {
399                left_iter.zip(right_iter).map(|(l, r)| op(l, r)).collect()
400            } else {
401                left_iter
402                    .chain(Some(read_u64(left_rem)))
403                    .zip(right_iter.chain(Some(read_u64(right_rem))))
404                    .map(|(l, r)| op(l, r))
405                    .collect()
406            };
407            return BooleanBuffer::new(result_u64s.into(), bit_offset, len_in_bits);
408        }
409
410        // Different sub-64-bit alignments: bit-shifting unavoidable
411        let left_chunks = BitChunks::new(left, left_offset_in_bits, len_in_bits);
412        let right_chunks = BitChunks::new(right, right_offset_in_bits, len_in_bits);
413
414        let chunks = left_chunks
415            .iter()
416            .zip(right_chunks.iter())
417            .map(|(left, right)| op(left, right));
418        // Soundness: `BitChunks` is a `BitChunks` trusted length iterator which
419        // correctly reports its upper bound
420        let mut buffer = unsafe { MutableBuffer::from_trusted_len_iter(chunks) };
421
422        let remainder_bytes = bit_util::ceil(left_chunks.remainder_len(), 8);
423        let rem = op(left_chunks.remainder_bits(), right_chunks.remainder_bits());
424        // we are counting its starting from the least significant bit, to to_le_bytes should be correct
425        let rem = &rem.to_le_bytes()[0..remainder_bytes];
426        buffer.extend_from_slice(rem);
427
428        BooleanBuffer {
429            buffer: Buffer::from(buffer),
430            bit_offset: 0,
431            bit_len: len_in_bits,
432        }
433    }
434
435    /// Returns the number of set bits in this buffer
436    pub fn count_set_bits(&self) -> usize {
437        self.buffer
438            .count_set_bits_offset(self.bit_offset, self.bit_len)
439    }
440
441    /// Finds the position of the n-th set bit (1-based) starting from `start` index.
442    /// If fewer than `n` set bits are found, returns the length of the buffer.
443    pub fn find_nth_set_bit_position(&self, start: usize, n: usize) -> usize {
444        if n == 0 {
445            return start;
446        }
447
448        self.slice(start, self.bit_len - start)
449            .set_indices()
450            .nth(n - 1)
451            .map(|idx| start + idx + 1)
452            .unwrap_or(self.bit_len)
453    }
454
455    /// Returns a [`BitChunks`] instance which can be used to iterate over
456    /// this buffer's bits in `u64` chunks
457    #[inline]
458    pub fn bit_chunks(&self) -> BitChunks<'_> {
459        BitChunks::new(self.values(), self.bit_offset, self.bit_len)
460    }
461
462    /// Returns the offset of this [`BooleanBuffer`] in bits (not bytes)
463    #[inline]
464    pub fn offset(&self) -> usize {
465        self.bit_offset
466    }
467
468    /// Returns the length of this [`BooleanBuffer`] in bits (not bytes)
469    #[inline]
470    pub fn len(&self) -> usize {
471        self.bit_len
472    }
473
474    /// Returns true if this [`BooleanBuffer`] is empty
475    #[inline]
476    pub fn is_empty(&self) -> bool {
477        self.bit_len == 0
478    }
479
480    /// Free up unused memory.
481    pub fn shrink_to_fit(&mut self) {
482        // TODO(emilk): we could shrink even more in the case where we are a small sub-slice of the full buffer
483        self.buffer.shrink_to_fit();
484    }
485
486    /// Returns the boolean value at index `i`.
487    ///
488    /// # Panics
489    ///
490    /// Panics if `i >= self.len()`
491    #[inline]
492    pub fn value(&self, idx: usize) -> bool {
493        assert!(idx < self.bit_len);
494        unsafe { self.value_unchecked(idx) }
495    }
496
497    /// Returns the boolean value at index `i`.
498    ///
499    /// # Safety
500    /// This doesn't check bounds, the caller must ensure that index < self.len()
501    #[inline]
502    pub unsafe fn value_unchecked(&self, i: usize) -> bool {
503        unsafe { bit_util::get_bit_raw(self.buffer.as_ptr(), i + self.bit_offset) }
504    }
505
506    /// Returns the packed values of this [`BooleanBuffer`] not including any offset
507    #[inline]
508    pub fn values(&self) -> &[u8] {
509        &self.buffer
510    }
511
512    /// Slices this [`BooleanBuffer`] by the provided `offset` and `length`
513    ///
514    /// # Panics
515    ///
516    /// Panics if `offset + len > self.len()`
517    pub fn slice(&self, offset: usize, len: usize) -> Self {
518        assert!(
519            offset.saturating_add(len) <= self.bit_len,
520            "the length + offset of the sliced BooleanBuffer cannot exceed the existing length"
521        );
522        Self {
523            buffer: self.buffer.clone(),
524            bit_offset: self.bit_offset + offset,
525            bit_len: len,
526        }
527    }
528
529    /// Returns a new [`Buffer`] containing the sliced contents of this [`BooleanBuffer`]
530    ///
531    /// Equivalent to `self.buffer.bit_slice(self.offset, self.len)`
532    pub fn sliced(&self) -> Buffer {
533        self.buffer.bit_slice(self.bit_offset, self.bit_len)
534    }
535
536    /// Returns true if this [`BooleanBuffer`] is equal to `other`, using pointer comparisons
537    /// to determine buffer equality. This is cheaper than `PartialEq::eq` but may
538    /// return false when the arrays are logically equal
539    pub fn ptr_eq(&self, other: &Self) -> bool {
540        self.buffer.as_ptr() == other.buffer.as_ptr()
541            && self.bit_offset == other.bit_offset
542            && self.bit_len == other.bit_len
543    }
544
545    /// Returns the inner [`Buffer`]
546    ///
547    /// Note: this does not account for offset and length of this [`BooleanBuffer`]
548    #[inline]
549    pub fn inner(&self) -> &Buffer {
550        &self.buffer
551    }
552
553    /// Returns the inner [`Buffer`], consuming self
554    ///
555    /// Note: this does not account for offset and length of this [`BooleanBuffer`]
556    pub fn into_inner(self) -> Buffer {
557        self.buffer
558    }
559
560    /// Claim memory used by this buffer in the provided memory pool.
561    ///
562    /// See [`Buffer::claim`] for details.
563    #[cfg(feature = "pool")]
564    pub fn claim(&self, pool: &dyn crate::MemoryPool) {
565        self.buffer.claim(pool);
566    }
567
568    /// Apply a bitwise binary operation to `self`.
569    ///
570    /// If the underlying buffer is uniquely owned, reuses the allocation
571    /// and updates the bytes in place. If the underlying buffer is shared,
572    /// returns a newly allocated buffer.
573    ///
574    /// # API Notes
575    ///
576    /// If the buffer is reused, the result preserves the existing offset, which
577    /// may be non-zero.
578    fn bitwise_bin_op_assign<F>(&mut self, rhs: &BooleanBuffer, op: F)
579    where
580        F: FnMut(u64, u64) -> u64,
581    {
582        assert_eq!(self.bit_len, rhs.bit_len);
583        // Try to mutate in place if the buffer is uniquely owned
584        let buffer = std::mem::take(&mut self.buffer);
585        match buffer.into_mutable() {
586            Ok(mut buf) => {
587                bit_util::apply_bitwise_binary_op(
588                    &mut buf,
589                    self.bit_offset,
590                    &rhs.buffer,
591                    rhs.bit_offset,
592                    self.bit_len,
593                    op,
594                );
595                self.buffer = buf.into();
596            }
597            Err(buf) => {
598                self.buffer = buf;
599                *self = BooleanBuffer::from_bitwise_binary_op(
600                    self.values(),
601                    self.bit_offset,
602                    rhs.values(),
603                    rhs.bit_offset,
604                    self.bit_len,
605                    op,
606                );
607            }
608        }
609    }
610
611    /// Returns an iterator over the bits in this [`BooleanBuffer`]
612    pub fn iter(&self) -> BitIterator<'_> {
613        self.into_iter()
614    }
615
616    /// Returns an [`UnalignedBitChunk`] over this buffer's values.
617    fn unaligned_bit_chunks(&self) -> UnalignedBitChunk<'_> {
618        UnalignedBitChunk::new(self.values(), self.offset(), self.len())
619    }
620
621    /// Returns an iterator over the set bit positions in this [`BooleanBuffer`]
622    pub fn set_indices(&self) -> BitIndexIterator<'_> {
623        BitIndexIterator::new(self.values(), self.bit_offset, self.bit_len)
624    }
625
626    /// Returns a `u32` iterator over set bit positions without any usize->u32 conversion
627    pub fn set_indices_u32(&self) -> BitIndexU32Iterator<'_> {
628        BitIndexU32Iterator::new(self.values(), self.bit_offset, self.bit_len)
629    }
630
631    /// Returns a [`BitSliceIterator`] yielding contiguous ranges of set bits
632    pub fn set_slices(&self) -> BitSliceIterator<'_> {
633        BitSliceIterator::new(self.values(), self.bit_offset, self.bit_len)
634    }
635
636    /// Block size for chunked fold operations in [`Self::has_true`] and [`Self::has_false`].
637    /// Using `chunks_exact` with this size lets the compiler fully unroll the inner
638    /// fold (no inner branch/loop), enabling short-circuit exits every N chunks.
639    const CHUNK_FOLD_BLOCK_SIZE: usize = 16;
640
641    /// Returns whether there is at least one `true` value in this buffer.
642    ///
643    /// This is more efficient than `count_set_bits() > 0` because it can short-circuit
644    /// as soon as a `true` value is found, without counting all set bits.
645    ///
646    /// Returns `false` for empty buffer.
647    pub fn has_true(&self) -> bool {
648        let bit_chunks = self.unaligned_bit_chunks();
649        let chunks = bit_chunks.chunks();
650        let (exact, remainder) = chunks.as_chunks::<{ Self::CHUNK_FOLD_BLOCK_SIZE }>();
651        let found = bit_chunks.prefix().unwrap_or(0) != 0
652            || exact
653                .iter()
654                .any(|block| block.iter().fold(0u64, |acc, &c| acc | c) != 0);
655        found || remainder.iter().any(|&c| c != 0) || bit_chunks.suffix().unwrap_or(0) != 0
656    }
657
658    /// Returns whether there is at least one `false` value in this buffer.
659    ///
660    /// This is more efficient than `len() > count_set_bits()` because it can short-circuit
661    /// as soon as a `false` value is found, without counting all set bits.
662    ///
663    /// Returns `false` for empty buffer.
664    pub fn has_false(&self) -> bool {
665        let bit_chunks = self.unaligned_bit_chunks();
666        // UnalignedBitChunk zeros padding bits; fill them with 1s so
667        // they don't appear as false values.
668        let lead_mask = !((1u64 << bit_chunks.lead_padding()) - 1);
669        let trail_mask = if bit_chunks.trailing_padding() == 0 {
670            u64::MAX
671        } else {
672            (1u64 << (64 - bit_chunks.trailing_padding())) - 1
673        };
674        let (prefix_fill, suffix_fill) = match (bit_chunks.prefix(), bit_chunks.suffix()) {
675            (Some(_), Some(_)) => (!lead_mask, !trail_mask),
676            (Some(_), None) => (!lead_mask | !trail_mask, 0),
677            (None, Some(_)) => (0, !trail_mask),
678            (None, None) => (0, 0),
679        };
680        let chunks = bit_chunks.chunks();
681        let (exact, remainder) = chunks.as_chunks::<{ Self::CHUNK_FOLD_BLOCK_SIZE }>();
682        let found = bit_chunks
683            .prefix()
684            .is_some_and(|v| (v | prefix_fill) != u64::MAX)
685            || exact
686                .iter()
687                .any(|block| block.iter().fold(u64::MAX, |acc, &c| acc & c) != u64::MAX);
688        found
689            || remainder.iter().any(|&c| c != u64::MAX)
690            || bit_chunks
691                .suffix()
692                .is_some_and(|v| (v | suffix_fill) != u64::MAX)
693    }
694}
695
696impl Not for &BooleanBuffer {
697    type Output = BooleanBuffer;
698
699    fn not(self) -> Self::Output {
700        BooleanBuffer::from_bitwise_unary_op(&self.buffer, self.bit_offset, self.bit_len, |a| !a)
701    }
702}
703
704impl BitAnd<&BooleanBuffer> for &BooleanBuffer {
705    type Output = BooleanBuffer;
706
707    fn bitand(self, rhs: &BooleanBuffer) -> Self::Output {
708        assert_eq!(self.bit_len, rhs.bit_len);
709        BooleanBuffer {
710            buffer: buffer_bin_and(
711                &self.buffer,
712                self.bit_offset,
713                &rhs.buffer,
714                rhs.bit_offset,
715                self.bit_len,
716            ),
717            bit_offset: 0,
718            bit_len: self.bit_len,
719        }
720    }
721}
722
723impl BitOr<&BooleanBuffer> for &BooleanBuffer {
724    type Output = BooleanBuffer;
725
726    fn bitor(self, rhs: &BooleanBuffer) -> Self::Output {
727        assert_eq!(self.bit_len, rhs.bit_len);
728        BooleanBuffer {
729            buffer: buffer_bin_or(
730                &self.buffer,
731                self.bit_offset,
732                &rhs.buffer,
733                rhs.bit_offset,
734                self.bit_len,
735            ),
736            bit_offset: 0,
737            bit_len: self.bit_len,
738        }
739    }
740}
741
742impl BitXor<&BooleanBuffer> for &BooleanBuffer {
743    type Output = BooleanBuffer;
744
745    fn bitxor(self, rhs: &BooleanBuffer) -> Self::Output {
746        assert_eq!(self.bit_len, rhs.bit_len);
747        BooleanBuffer {
748            buffer: buffer_bin_xor(
749                &self.buffer,
750                self.bit_offset,
751                &rhs.buffer,
752                rhs.bit_offset,
753                self.bit_len,
754            ),
755            bit_offset: 0,
756            bit_len: self.bit_len,
757        }
758    }
759}
760
761impl BitAndAssign<&BooleanBuffer> for BooleanBuffer {
762    fn bitand_assign(&mut self, rhs: &BooleanBuffer) {
763        self.bitwise_bin_op_assign(rhs, |a, b| a & b);
764    }
765}
766
767impl BitOrAssign<&BooleanBuffer> for BooleanBuffer {
768    fn bitor_assign(&mut self, rhs: &BooleanBuffer) {
769        self.bitwise_bin_op_assign(rhs, |a, b| a | b);
770    }
771}
772
773impl BitXorAssign<&BooleanBuffer> for BooleanBuffer {
774    fn bitxor_assign(&mut self, rhs: &BooleanBuffer) {
775        self.bitwise_bin_op_assign(rhs, |a, b| a ^ b);
776    }
777}
778
779impl<'a> IntoIterator for &'a BooleanBuffer {
780    type Item = bool;
781    type IntoIter = BitIterator<'a>;
782
783    fn into_iter(self) -> Self::IntoIter {
784        BitIterator::new(self.values(), self.bit_offset, self.bit_len)
785    }
786}
787
788impl From<&[bool]> for BooleanBuffer {
789    fn from(value: &[bool]) -> Self {
790        let mut builder = BooleanBufferBuilder::new(value.len());
791        builder.append_slice(value);
792        builder.finish()
793    }
794}
795
796impl From<Vec<bool>> for BooleanBuffer {
797    fn from(value: Vec<bool>) -> Self {
798        value.as_slice().into()
799    }
800}
801
802impl FromIterator<bool> for BooleanBuffer {
803    fn from_iter<T: IntoIterator<Item = bool>>(iter: T) -> Self {
804        let iter = iter.into_iter();
805        let (hint, _) = iter.size_hint();
806        let mut builder = BooleanBufferBuilder::new(hint);
807        iter.for_each(|b| builder.append(b));
808        builder.finish()
809    }
810}
811
812#[cfg(test)]
813mod tests {
814    use super::*;
815
816    #[test]
817    fn test_boolean_new() {
818        let bytes = &[0, 1, 2, 3, 4];
819        let buf = Buffer::from(bytes);
820        let offset = 0;
821        let len = 24;
822
823        let boolean_buf = BooleanBuffer::new(buf.clone(), offset, len);
824        assert_eq!(bytes, boolean_buf.values());
825        assert_eq!(offset, boolean_buf.offset());
826        assert_eq!(len, boolean_buf.len());
827
828        assert_eq!(2, boolean_buf.count_set_bits());
829        assert_eq!(&buf, boolean_buf.inner());
830        assert_eq!(buf, boolean_buf.clone().into_inner());
831
832        assert!(!boolean_buf.is_empty())
833    }
834
835    #[test]
836    fn test_boolean_data_equality() {
837        let boolean_buf1 = BooleanBuffer::new(Buffer::from(&[0, 1, 4, 3, 5]), 0, 32);
838        let boolean_buf2 = BooleanBuffer::new(Buffer::from(&[0, 1, 4, 3, 5]), 0, 32);
839        assert_eq!(boolean_buf1, boolean_buf2);
840
841        // slice with same offset and same length should still preserve equality
842        let boolean_buf3 = boolean_buf1.slice(8, 16);
843        assert_ne!(boolean_buf1, boolean_buf3);
844        let boolean_buf4 = boolean_buf1.slice(0, 32);
845        assert_eq!(boolean_buf1, boolean_buf4);
846
847        // unequal because of different elements
848        let boolean_buf2 = BooleanBuffer::new(Buffer::from(&[0, 0, 2, 3, 4]), 0, 32);
849        assert_ne!(boolean_buf1, boolean_buf2);
850
851        // unequal because of different length
852        let boolean_buf2 = BooleanBuffer::new(Buffer::from(&[0, 1, 4, 3, 5]), 0, 24);
853        assert_ne!(boolean_buf1, boolean_buf2);
854
855        // ptr_eq
856        assert!(boolean_buf1.ptr_eq(&boolean_buf1));
857        assert!(boolean_buf2.ptr_eq(&boolean_buf2));
858        assert!(!boolean_buf1.ptr_eq(&boolean_buf2));
859    }
860
861    #[test]
862    fn test_boolean_slice() {
863        let bytes = &[0, 3, 2, 6, 2];
864        let boolean_buf1 = BooleanBuffer::new(Buffer::from(bytes), 0, 32);
865        let boolean_buf2 = BooleanBuffer::new(Buffer::from(bytes), 0, 32);
866
867        let boolean_slice1 = boolean_buf1.slice(16, 16);
868        let boolean_slice2 = boolean_buf2.slice(0, 16);
869        assert_eq!(boolean_slice1.values(), boolean_slice2.values());
870
871        assert_eq!(bytes, boolean_slice1.values());
872        assert_eq!(16, boolean_slice1.bit_offset);
873        assert_eq!(16, boolean_slice1.bit_len);
874
875        assert_eq!(bytes, boolean_slice2.values());
876        assert_eq!(0, boolean_slice2.bit_offset);
877        assert_eq!(16, boolean_slice2.bit_len);
878    }
879
880    #[test]
881    fn test_boolean_bitand() {
882        let offset = 0;
883        let len = 40;
884
885        let buf1 = Buffer::from(&[0, 1, 1, 0, 0]);
886        let boolean_buf1 = &BooleanBuffer::new(buf1, offset, len);
887
888        let buf2 = Buffer::from(&[0, 1, 1, 1, 0]);
889        let boolean_buf2 = &BooleanBuffer::new(buf2, offset, len);
890
891        let expected = BooleanBuffer::new(Buffer::from(&[0, 1, 1, 0, 0]), offset, len);
892        assert_eq!(boolean_buf1 & boolean_buf2, expected);
893    }
894
895    #[test]
896    fn test_boolean_bitor() {
897        let offset = 0;
898        let len = 40;
899
900        let buf1 = Buffer::from(&[0, 1, 1, 0, 0]);
901        let boolean_buf1 = &BooleanBuffer::new(buf1, offset, len);
902
903        let buf2 = Buffer::from(&[0, 1, 1, 1, 0]);
904        let boolean_buf2 = &BooleanBuffer::new(buf2, offset, len);
905
906        let expected = BooleanBuffer::new(Buffer::from(&[0, 1, 1, 1, 0]), offset, len);
907        assert_eq!(boolean_buf1 | boolean_buf2, expected);
908    }
909
910    #[test]
911    fn test_boolean_bitxor() {
912        let offset = 0;
913        let len = 40;
914
915        let buf1 = Buffer::from(&[0, 1, 1, 0, 0]);
916        let boolean_buf1 = &BooleanBuffer::new(buf1, offset, len);
917
918        let buf2 = Buffer::from(&[0, 1, 1, 1, 0]);
919        let boolean_buf2 = &BooleanBuffer::new(buf2, offset, len);
920
921        let expected = BooleanBuffer::new(Buffer::from(&[0, 0, 0, 1, 0]), offset, len);
922        assert_eq!(boolean_buf1 ^ boolean_buf2, expected);
923    }
924
925    #[test]
926    fn test_boolean_bitand_assign_shared_and_unshared() {
927        let rhs = BooleanBuffer::from(&[true, true, false, true, false, true][..]);
928        let original = BooleanBuffer::from(&[true, false, true, true, true, false][..]);
929
930        let mut unshared = BooleanBuffer::from(&[true, false, true, true, true, false][..]);
931        unshared &= &rhs;
932
933        let mut shared = original.clone();
934        let _shared_owner = shared.clone();
935        shared &= &rhs;
936
937        let expected = &original & &rhs;
938        assert_eq!(unshared, expected);
939        assert_eq!(shared, expected);
940    }
941
942    #[test]
943    fn test_boolean_bitor_assign() {
944        let rhs = BooleanBuffer::from(&[true, true, false, true, false, true][..]);
945        let original = BooleanBuffer::from(&[true, false, true, true, true, false][..]);
946
947        let mut actual = original.clone();
948        actual |= &rhs;
949
950        let expected = &original | &rhs;
951        assert_eq!(actual, expected);
952    }
953
954    #[test]
955    fn test_boolean_bitxor_assign() {
956        let rhs = BooleanBuffer::from(&[true, true, false, true, false, true][..]);
957        let original = BooleanBuffer::from(&[true, false, true, true, true, false][..]);
958
959        let mut actual = original.clone();
960        actual ^= &rhs;
961
962        let expected = &original ^ &rhs;
963        assert_eq!(actual, expected);
964    }
965
966    #[test]
967    fn test_boolean_not() {
968        let offset = 0;
969        let len = 40;
970
971        let buf = Buffer::from(&[0, 1, 1, 0, 0]);
972        let boolean_buf = &BooleanBuffer::new(buf, offset, len);
973
974        let expected = BooleanBuffer::new(Buffer::from(&[255, 254, 254, 255, 255]), offset, len);
975        assert_eq!(!boolean_buf, expected);
976
977        // Demonstrate that Non-zero offsets are preserved
978        let sliced = boolean_buf.slice(3, 20);
979        let result = !&sliced;
980        assert_eq!(result.offset(), 3);
981        assert_eq!(result.len(), sliced.len());
982        for i in 0..sliced.len() {
983            assert_eq!(result.value(i), !sliced.value(i));
984        }
985    }
986
987    #[test]
988    fn test_boolean_from_slice_bool() {
989        let v = [true, false, false];
990        let buf = BooleanBuffer::from(&v[..]);
991        assert_eq!(buf.offset(), 0);
992        assert_eq!(buf.len(), 3);
993        assert_eq!(buf.values().len(), 1);
994        assert!(buf.value(0));
995    }
996
997    #[test]
998    fn test_from_bitwise_unary_op() {
999        // Use 1024 boolean values so that at least some of the tests cover multiple u64 chunks and
1000        // perfect alignment
1001        let input_bools = (0..1024)
1002            .map(|_| rand::random::<bool>())
1003            .collect::<Vec<bool>>();
1004        let input_buffer = BooleanBuffer::from(&input_bools[..]);
1005
1006        // Note ensure we test offsets over 100 to cover multiple u64 chunks
1007        for offset in 0..1024 {
1008            let result = BooleanBuffer::from_bitwise_unary_op(
1009                input_buffer.values(),
1010                offset,
1011                input_buffer.len() - offset,
1012                |a| !a,
1013            );
1014            let expected = input_bools[offset..]
1015                .iter()
1016                .map(|b| !*b)
1017                .collect::<BooleanBuffer>();
1018            assert_eq!(result, expected);
1019        }
1020
1021        // Also test when the input doesn't cover the entire buffer
1022        for offset in 0..512 {
1023            let len = 512 - offset; // fixed length less than total
1024            let result =
1025                BooleanBuffer::from_bitwise_unary_op(input_buffer.values(), offset, len, |a| !a);
1026            let expected = input_bools[offset..]
1027                .iter()
1028                .take(len)
1029                .map(|b| !*b)
1030                .collect::<BooleanBuffer>();
1031            assert_eq!(result, expected);
1032        }
1033    }
1034
1035    #[test]
1036    fn test_from_bitwise_unary_op_unaligned_fallback() {
1037        // Deterministic affine sequence over u8: b[i] = 37*i + 11 (mod 256).
1038        // This yields a non-trivial mix of bits (prefix: 11, 48, 85, 122, 159, 196, 233, 14, ...)
1039        // so unary bit operations are exercised on varied input patterns.
1040        let bytes = (0..80)
1041            .map(|i| (i as u8).wrapping_mul(37).wrapping_add(11))
1042            .collect::<Vec<_>>();
1043        let base = bytes.as_ptr() as usize;
1044        let shift = (0..8).find(|s| !(base + s).is_multiple_of(8)).unwrap();
1045        let misaligned = &bytes[shift..];
1046
1047        // Case 1: fallback path with `remainder.is_empty() == true`
1048        let src = &misaligned[..24];
1049        let offset = 7;
1050        let len = 96;
1051        let result = BooleanBuffer::from_bitwise_unary_op(src, offset, len, |a| !a);
1052        let expected = (0..len)
1053            .map(|i| !bit_util::get_bit(src, offset + i))
1054            .collect::<BooleanBuffer>();
1055        assert_eq!(result, expected);
1056        assert_eq!(result.offset(), offset % 64);
1057
1058        // Case 2: fallback path with `remainder.is_empty() == false`
1059        let src = &misaligned[..13];
1060        let offset = 3;
1061        let len = 100;
1062        let result = BooleanBuffer::from_bitwise_unary_op(src, offset, len, |a| !a);
1063        let expected = (0..len)
1064            .map(|i| !bit_util::get_bit(src, offset + i))
1065            .collect::<BooleanBuffer>();
1066        assert_eq!(result, expected);
1067        assert_eq!(result.offset(), offset % 64);
1068    }
1069
1070    #[test]
1071    fn test_from_bitwise_binary_op() {
1072        // pick random boolean inputs
1073        let input_bools_left = (0..1024)
1074            .map(|_| rand::random::<bool>())
1075            .collect::<Vec<bool>>();
1076        let input_bools_right = (0..1024)
1077            .map(|_| rand::random::<bool>())
1078            .collect::<Vec<bool>>();
1079        let input_buffer_left = BooleanBuffer::from(&input_bools_left[..]);
1080        let input_buffer_right = BooleanBuffer::from(&input_bools_right[..]);
1081
1082        #[cfg(miri)] // Takes too long otherwise
1083        let left_offsets = [0, 1, 7, 8, 63, 64, 65];
1084        #[cfg(not(miri))]
1085        let left_offsets = 0..200;
1086
1087        for left_offset in left_offsets {
1088            for right_offset in [0, 4, 5, 17, 33, 24, 45, 64, 65, 100, 200] {
1089                for len_offset in [0, 1, 44, 100, 256, 300, 512] {
1090                    let len = 1024 - len_offset - left_offset.max(right_offset); // ensure we don't go out of bounds
1091                    // compute with AND
1092                    let result = BooleanBuffer::from_bitwise_binary_op(
1093                        input_buffer_left.values(),
1094                        left_offset,
1095                        input_buffer_right.values(),
1096                        right_offset,
1097                        len,
1098                        |a, b| a & b,
1099                    );
1100                    // compute directly from bools
1101                    let expected = input_bools_left[left_offset..]
1102                        .iter()
1103                        .zip(&input_bools_right[right_offset..])
1104                        .take(len)
1105                        .map(|(a, b)| *a & *b)
1106                        .collect::<BooleanBuffer>();
1107                    assert_eq!(result, expected);
1108                }
1109            }
1110        }
1111    }
1112
1113    #[test]
1114    fn test_from_bitwise_binary_op_same_mod_64_unaligned_fallback() {
1115        // Exercise the shared-alignment fast path when both inputs are misaligned in memory,
1116        // forcing the chunks_exact fallback instead of align_to::<u64>().
1117        let left_bytes = [
1118            0,           // dropped so `&left_bytes[1..]` is not u64-aligned in memory
1119            0b1101_0010, // logical left bits start at bit 3 of this byte
1120            0b0110_1101,
1121            0b1010_0111,
1122            0b0001_1110,
1123            0b1110_0001,
1124            0b0101_1010,
1125            0b1001_0110,
1126            0b0011_1100,
1127            0b1011_0001,
1128            0b0100_1110,
1129            0b1100_0011,
1130            0b0111_1000,
1131        ];
1132        let right_bytes = [
1133            0,           // dropped so `&right_bytes[1..]` is not u64-aligned in memory
1134            0b1010_1100, // logical right bits start at bit 67 == bit 3 of the second 64-bit block
1135            0b0101_0011,
1136            0b1111_0000,
1137            0b0011_1010,
1138            0b1000_1111,
1139            0b0110_0101,
1140            0b1101_1000,
1141            0b0001_0111,
1142            0b1110_0100,
1143            0b0010_1101,
1144            0b1001_1010,
1145            0b0111_0001,
1146        ];
1147
1148        let left = &left_bytes[1..];
1149        let right = &right_bytes[1..];
1150
1151        let left_offset = 3;
1152        let right_offset = 67; // same mod 64 as left_offset, so this takes the shared-alignment path
1153        let len = 24; // leaves a partial trailing chunk, so this covers the non-empty remainder branch
1154
1155        let result = BooleanBuffer::from_bitwise_binary_op(
1156            left,
1157            left_offset,
1158            right,
1159            right_offset,
1160            len,
1161            |a, b| a & b,
1162        );
1163        let expected = (0..len)
1164            .map(|i| {
1165                bit_util::get_bit(left, left_offset + i)
1166                    & bit_util::get_bit(right, right_offset + i)
1167            })
1168            .collect::<BooleanBuffer>();
1169
1170        assert_eq!(result, expected);
1171        assert_eq!(result.offset(), left_offset % 64);
1172    }
1173
1174    #[test]
1175    fn test_from_bitwise_binary_op_same_mod_64_unaligned_fallback_no_remainder() {
1176        // Force the chunks_exact fallback with an exact 8-byte chunk so both remainders are empty.
1177        let left_bytes = [
1178            0,           // dropped so `&left_bytes[1..]` is not u64-aligned in memory
1179            0b1010_1100, // logical left bits start at bit 3 of this byte
1180            0b0110_1001,
1181            0b1101_0011,
1182            0b0001_1110,
1183            0b1110_0101,
1184            0b0101_1000,
1185            0b1001_0111,
1186            0b0011_1101,
1187        ];
1188        let right_bytes = [
1189            0,           // dropped so `&right_bytes[1..]` is not u64-aligned in memory
1190            0b0111_0010, // logical right bits start at bit 67 == bit 3 of the second 64-bit block
1191            0b1010_1001,
1192            0b0101_1110,
1193            0b1100_0011,
1194            0b0011_1011,
1195            0b1000_1110,
1196            0b1111_0001,
1197            0b0100_1101,
1198            0b1011_0110,
1199            0b0001_1011,
1200            0b1101_0100,
1201            0b0110_0011,
1202            0b1001_1110,
1203            0b0010_1001,
1204            0b1110_0110,
1205            0b0101_0001,
1206        ];
1207
1208        let left = &left_bytes[1..];
1209        let right = &right_bytes[1..];
1210
1211        let left_offset = 3;
1212        let right_offset = 67; // same mod 64 as left_offset, so this takes the shared-alignment path
1213        let len = 61; // 3 + 61 = 64, so the aligned slices are exactly one 8-byte chunk with empty remainders
1214
1215        let result = BooleanBuffer::from_bitwise_binary_op(
1216            left,
1217            left_offset,
1218            right,
1219            right_offset,
1220            len,
1221            |a, b| a | b,
1222        );
1223        let expected = (0..len)
1224            .map(|i| {
1225                bit_util::get_bit(left, left_offset + i)
1226                    | bit_util::get_bit(right, right_offset + i)
1227            })
1228            .collect::<BooleanBuffer>();
1229
1230        assert_eq!(result, expected);
1231        assert_eq!(result.offset(), left_offset % 64);
1232    }
1233
1234    #[test]
1235    fn test_extend_trusted_len_sets_byte_len() {
1236        // Ensures extend_trusted_len keeps the underlying byte length in sync with bit length.
1237        let mut builder = BooleanBufferBuilder::new(0);
1238        let bools: Vec<_> = (0..10).map(|i| i % 2 == 0).collect();
1239        unsafe { builder.extend_trusted_len(bools.into_iter()) };
1240        assert_eq!(builder.as_slice().len(), bit_util::ceil(builder.len(), 8));
1241    }
1242
1243    #[test]
1244    fn test_extend_trusted_len_then_append() {
1245        // Exercises append after extend_trusted_len to validate byte length and values.
1246        let mut builder = BooleanBufferBuilder::new(0);
1247        let bools: Vec<_> = (0..9).map(|i| i % 3 == 0).collect();
1248        unsafe { builder.extend_trusted_len(bools.clone().into_iter()) };
1249        builder.append(true);
1250        assert_eq!(builder.as_slice().len(), bit_util::ceil(builder.len(), 8));
1251        let finished = builder.finish();
1252        for (i, v) in bools.into_iter().chain(std::iter::once(true)).enumerate() {
1253            assert_eq!(finished.value(i), v, "at index {i}");
1254        }
1255    }
1256
1257    #[test]
1258    fn test_find_nth_set_bit_position() {
1259        let bools = vec![true, false, true, true, false, true];
1260        let buffer = BooleanBuffer::from(bools);
1261
1262        assert_eq!(buffer.clone().find_nth_set_bit_position(0, 1), 1);
1263        assert_eq!(buffer.clone().find_nth_set_bit_position(0, 2), 3);
1264        assert_eq!(buffer.clone().find_nth_set_bit_position(0, 3), 4);
1265        assert_eq!(buffer.clone().find_nth_set_bit_position(0, 4), 6);
1266        assert_eq!(buffer.clone().find_nth_set_bit_position(0, 5), 6);
1267
1268        assert_eq!(buffer.clone().find_nth_set_bit_position(1, 1), 3);
1269        assert_eq!(buffer.clone().find_nth_set_bit_position(3, 1), 4);
1270        assert_eq!(buffer.clone().find_nth_set_bit_position(3, 2), 6);
1271    }
1272
1273    #[test]
1274    fn test_find_nth_set_bit_position_large() {
1275        let mut bools = vec![false; 1000];
1276        bools[100] = true;
1277        bools[500] = true;
1278        bools[999] = true;
1279        let buffer = BooleanBuffer::from(bools);
1280
1281        assert_eq!(buffer.clone().find_nth_set_bit_position(0, 1), 101);
1282        assert_eq!(buffer.clone().find_nth_set_bit_position(0, 2), 501);
1283        assert_eq!(buffer.clone().find_nth_set_bit_position(0, 3), 1000);
1284        assert_eq!(buffer.clone().find_nth_set_bit_position(0, 4), 1000);
1285
1286        assert_eq!(buffer.clone().find_nth_set_bit_position(101, 1), 501);
1287    }
1288
1289    #[test]
1290    fn test_find_nth_set_bit_position_sliced() {
1291        let bools = vec![false, true, false, true, true, false, true]; // [F, T, F, T, T, F, T]
1292        let buffer = BooleanBuffer::from(bools);
1293        let slice = buffer.slice(1, 6); // [T, F, T, T, F, T]
1294
1295        assert_eq!(slice.len(), 6);
1296        // Logical indices: 0, 1, 2, 3, 4, 5
1297        // Logical values: T, F, T, T, F, T
1298
1299        assert_eq!(slice.clone().find_nth_set_bit_position(0, 1), 1);
1300        assert_eq!(slice.clone().find_nth_set_bit_position(0, 2), 3);
1301        assert_eq!(slice.clone().find_nth_set_bit_position(0, 3), 4);
1302        assert_eq!(slice.clone().find_nth_set_bit_position(0, 4), 6);
1303    }
1304
1305    #[test]
1306    fn test_find_nth_set_bit_position_all_set() {
1307        let buffer = BooleanBuffer::new_set(100);
1308        for i in 1..=100 {
1309            assert_eq!(buffer.clone().find_nth_set_bit_position(0, i), i);
1310        }
1311        assert_eq!(buffer.clone().find_nth_set_bit_position(0, 101), 100);
1312    }
1313
1314    #[test]
1315    fn test_find_nth_set_bit_position_none_set() {
1316        let buffer = BooleanBuffer::new_unset(100);
1317        assert_eq!(buffer.clone().find_nth_set_bit_position(0, 1), 100);
1318    }
1319
1320    #[test]
1321    fn test_has_true_has_false_all_true() {
1322        let arr = BooleanBuffer::from(vec![true, true, true]);
1323        assert!(arr.has_true());
1324        assert!(!arr.has_false());
1325    }
1326
1327    #[test]
1328    fn test_has_true_has_false_all_false() {
1329        let arr = BooleanBuffer::from(vec![false, false, false]);
1330        assert!(!arr.has_true());
1331        assert!(arr.has_false());
1332    }
1333
1334    #[test]
1335    fn test_has_true_has_false_mixed() {
1336        let arr = BooleanBuffer::from(vec![true, false, true]);
1337        assert!(arr.has_true());
1338        assert!(arr.has_false());
1339    }
1340
1341    #[test]
1342    fn test_has_true_has_false_empty() {
1343        let arr = BooleanBuffer::from(Vec::<bool>::new());
1344        assert!(!arr.has_true());
1345        assert!(!arr.has_false());
1346    }
1347
1348    #[test]
1349    fn test_has_false_aligned_suffix_all_true() {
1350        let arr = BooleanBuffer::from(vec![true; 129]);
1351        assert!(arr.has_true());
1352        assert!(!arr.has_false());
1353    }
1354
1355    #[test]
1356    fn test_has_false_non_aligned_all_true() {
1357        // 65 elements: exercises the remainder path in has_false
1358        let arr = BooleanBuffer::from(vec![true; 65]);
1359        assert!(arr.has_true());
1360        assert!(!arr.has_false());
1361    }
1362
1363    #[test]
1364    fn test_has_false_non_aligned_last_false() {
1365        // 64 trues + 1 false: remainder path should find the false
1366        let mut values = vec![true; 64];
1367        values.push(false);
1368        let arr = BooleanBuffer::from(values);
1369        assert!(arr.has_true());
1370        assert!(arr.has_false());
1371    }
1372
1373    #[test]
1374    fn test_has_false_exact_64_all_true() {
1375        // Exactly 64 elements, no remainder
1376        let arr = BooleanBuffer::from(vec![true; 64]);
1377        assert!(arr.has_true());
1378        assert!(!arr.has_false());
1379    }
1380
1381    #[test]
1382    fn test_has_true_has_false_unaligned_slices() {
1383        let cases = [
1384            (1, 129, true, false),
1385            (3, 130, true, false),
1386            (5, 65, true, false),
1387            (7, 64, true, false),
1388        ];
1389
1390        let base = BooleanBuffer::from(vec![true; 300]);
1391
1392        for (offset, len, expected_has_true, expected_has_false) in cases {
1393            let arr = base.slice(offset, len);
1394            assert_eq!(
1395                arr.has_true(),
1396                expected_has_true,
1397                "offset={offset} len={len}"
1398            );
1399            assert_eq!(
1400                arr.has_false(),
1401                expected_has_false,
1402                "offset={offset} len={len}"
1403            );
1404        }
1405    }
1406
1407    #[test]
1408    fn test_has_true_has_false_exact_multiples_of_64() {
1409        let cases = [
1410            (64, true, false),
1411            (128, true, false),
1412            (192, true, false),
1413            (256, true, false),
1414        ];
1415
1416        for (len, expected_has_true, expected_has_false) in cases {
1417            let arr = BooleanBuffer::from(vec![true; len]);
1418            assert_eq!(arr.has_true(), expected_has_true, "len={len}");
1419            assert_eq!(arr.has_false(), expected_has_false, "len={len}");
1420        }
1421    }
1422}