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