Skip to main content

arrow_buffer/util/
bit_util.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
18//! Utils for working with bits
19
20use crate::bit_chunk_iterator::BitChunks;
21
22/// Returns the nearest number that is `>=` than `num` and is a multiple of 64
23///
24/// # Panics
25///
26/// Panics if rounding `num` up overflows `usize`
27#[inline]
28pub fn round_upto_multiple_of_64(num: usize) -> usize {
29    num.checked_next_multiple_of(64)
30        .expect("failed to round upto multiple of 64")
31}
32
33/// Returns the nearest multiple of `factor` that is `>=` than `num`. Here `factor` must
34/// be a power of 2.
35///
36/// # Panics
37///
38/// Panics if rounding `num` up overflows `usize`
39pub fn round_upto_power_of_2(num: usize, factor: usize) -> usize {
40    debug_assert!(factor > 0 && factor.is_power_of_two());
41    num.checked_add(factor - 1)
42        .expect("failed to round to next highest power of 2")
43        & !(factor - 1)
44}
45
46/// Returns whether bit at position `i` in `data` is set or not
47///
48/// # Panics
49///
50/// Panics if `i / 8 >= data.len()`
51#[inline]
52pub fn get_bit(data: &[u8], i: usize) -> bool {
53    data[i / 8] & (1 << (i % 8)) != 0
54}
55
56/// Returns whether bit at position `i` in `data` is set or not.
57///
58/// # Safety
59///
60/// Note this doesn't do any bound checking, for performance reason. The caller is
61/// responsible to guarantee that `i` is within bounds.
62#[inline]
63pub unsafe fn get_bit_raw(data: *const u8, i: usize) -> bool {
64    unsafe { (*data.add(i / 8) & (1 << (i % 8))) != 0 }
65}
66
67/// Sets bit at position `i` for `data` to 1
68///
69/// # Panics
70///
71/// Panics if `i / 8 >= data.len()`
72#[inline]
73pub fn set_bit(data: &mut [u8], i: usize) {
74    data[i / 8] |= 1 << (i % 8);
75}
76
77/// Sets bit at position `i` for `data`
78///
79/// # Safety
80///
81/// Note this doesn't do any bound checking, for performance reason. The caller is
82/// responsible to guarantee that `i` is within bounds.
83#[inline]
84pub unsafe fn set_bit_raw(data: *mut u8, i: usize) {
85    unsafe {
86        *data.add(i / 8) |= 1 << (i % 8);
87    }
88}
89
90/// Sets bit at position `i` for `data` to 0
91///
92/// # Panics
93///
94/// Panics if `i / 8 >= data.len()`
95#[inline]
96pub fn unset_bit(data: &mut [u8], i: usize) {
97    data[i / 8] &= !(1 << (i % 8));
98}
99
100/// Sets bit at position `i` for `data` to 0
101///
102/// # Safety
103///
104/// Note this doesn't do any bound checking, for performance reason. The caller is
105/// responsible to guarantee that `i` is within bounds.
106#[inline]
107pub unsafe fn unset_bit_raw(data: *mut u8, i: usize) {
108    unsafe {
109        *data.add(i / 8) &= !(1 << (i % 8));
110    }
111}
112
113/// Returns the ceil of `value`/`divisor`
114#[inline]
115pub fn ceil(value: usize, divisor: usize) -> usize {
116    value.div_ceil(divisor)
117}
118
119/// Read a u64 from a byte slice, padding with zeros if necessary
120#[inline]
121pub(crate) fn read_u64(input: &[u8]) -> u64 {
122    let len = input.len().min(8);
123    let mut buf = [0_u8; 8];
124    buf[..len].copy_from_slice(input);
125    u64::from_le_bytes(buf)
126}
127
128/// Read up to 8 bits from a byte slice starting at a given bit offset.
129///
130/// # Arguments
131///
132/// * `slice` - The byte slice to read from
133/// * `number_of_bits_to_read` - Number of bits to read (must be < 8)
134/// * `bit_offset` - Starting bit offset within the first byte (must be < 8)
135///
136/// # Returns
137///
138/// A `u8` containing the requested bits in the least significant positions
139///
140/// # Panics
141/// - Panics if `number_of_bits_to_read` is 0 or >= 8
142/// - Panics if `bit_offset` is >= 8
143/// - Panics if `slice` is empty or too small to read the requested bits
144///
145#[inline]
146pub(crate) fn read_up_to_byte_from_offset(
147    slice: &[u8],
148    number_of_bits_to_read: usize,
149    bit_offset: usize,
150) -> u8 {
151    assert!(number_of_bits_to_read < 8, "can read up to 8 bits only");
152    assert!(bit_offset < 8, "bit offset must be less than 8");
153    assert_ne!(
154        number_of_bits_to_read, 0,
155        "number of bits to read must be greater than 0"
156    );
157    assert_ne!(slice.len(), 0, "slice must not be empty");
158
159    let number_of_bytes_to_read = ceil(number_of_bits_to_read + bit_offset, 8);
160
161    // number of bytes to read
162    assert!(slice.len() >= number_of_bytes_to_read, "slice is too small");
163
164    let mut bits = slice[0] >> bit_offset;
165    for (i, &byte) in slice
166        .iter()
167        .take(number_of_bytes_to_read)
168        .enumerate()
169        .skip(1)
170    {
171        bits |= byte << (i * 8 - bit_offset);
172    }
173
174    bits & ((1 << number_of_bits_to_read) - 1)
175}
176
177/// Applies a bitwise operation relative to another bit-packed byte slice
178/// (right) in place
179///
180/// Note: applies the operation 64-bits (u64) at a time.
181///
182/// # Arguments
183///
184/// * `left` - The mutable buffer to be modified in-place
185/// * `offset_in_bits` - Starting bit offset in Self buffer
186/// * `right` - slice of bit-packed bytes in LSB order
187/// * `right_offset_in_bits` - Starting bit offset in the right buffer
188/// * `len_in_bits` - Number of bits to process
189/// * `op` - Binary operation to apply (e.g., `|a, b| a & b`). Applied a word at a time
190///
191/// Only the bits in `left_offset_in_bits..left_offset_in_bits + len_in_bits` are
192/// modified. Bits of `left` outside that range are left unchanged, including the
193/// bits sharing a byte with either end of the range.
194///
195/// # Example: Modify entire buffer
196/// ```
197/// # use arrow_buffer::MutableBuffer;
198/// # use arrow_buffer::bit_util::apply_bitwise_binary_op;
199/// let mut left = MutableBuffer::new(2);
200/// left.extend_from_slice(&[0b11110000u8, 0b00110011u8]);
201/// let right = &[0b10101010u8, 0b10101010u8];
202/// // apply bitwise AND between left and right buffers, updating left in place
203/// apply_bitwise_binary_op(left.as_slice_mut(), 0, right, 0, 16, |a, b| a & b);
204/// assert_eq!(left.as_slice(), &[0b10100000u8, 0b00100010u8]);
205/// ```
206///
207/// # Example: Modify buffer with offsets
208/// ```
209/// # use arrow_buffer::MutableBuffer;
210/// # use arrow_buffer::bit_util::apply_bitwise_binary_op;
211/// let mut left = MutableBuffer::new(2);
212/// left.extend_from_slice(&[0b00000000u8, 0b00000000u8]);
213/// let right = &[0b10110011u8, 0b11111110u8];
214/// // apply bitwise OR between left and right buffers,
215/// // Apply only 8 bits starting from bit offset 3 in left and bit offset 2 in right
216/// apply_bitwise_binary_op(left.as_slice_mut(), 3, right, 2, 8, |a, b| a | b);
217/// assert_eq!(left.as_slice(), &[0b01100000, 0b00000101u8]);
218/// ```
219///
220/// # Panics
221///
222/// If the offset or lengths exceed the buffer or slice size.
223pub fn apply_bitwise_binary_op<F>(
224    left: &mut [u8],
225    left_offset_in_bits: usize,
226    right: impl AsRef<[u8]>,
227    right_offset_in_bits: usize,
228    len_in_bits: usize,
229    mut op: F,
230) where
231    F: FnMut(u64, u64) -> u64,
232{
233    if len_in_bits == 0 {
234        return;
235    }
236
237    // offset inside a byte
238    let bit_offset = left_offset_in_bits % 8;
239
240    let is_mutable_buffer_byte_aligned = bit_offset == 0;
241
242    if is_mutable_buffer_byte_aligned {
243        byte_aligned_bitwise_bin_op_helper(
244            left,
245            left_offset_in_bits,
246            right,
247            right_offset_in_bits,
248            len_in_bits,
249            op,
250        );
251    } else {
252        // If we are not byte aligned, run `op` on the first few bits to reach byte alignment
253        let bits_to_next_byte = (8 - bit_offset)
254            // Minimum with the amount of bits we need to process
255            // to avoid reading out of bounds
256            .min(len_in_bits);
257
258        {
259            let right_byte_offset = right_offset_in_bits / 8;
260
261            // Read the same amount of bits from the right buffer
262            let right_first_byte = crate::util::bit_util::read_up_to_byte_from_offset(
263                &right.as_ref()[right_byte_offset..],
264                bits_to_next_byte,
265                // Right bit offset
266                right_offset_in_bits % 8,
267            );
268
269            align_to_byte(
270                left,
271                // Hope it gets inlined
272                &mut |left| op(left, right_first_byte as u64),
273                left_offset_in_bits,
274                bits_to_next_byte,
275            );
276        }
277
278        let offset_in_bits = left_offset_in_bits + bits_to_next_byte;
279        let right_offset_in_bits = right_offset_in_bits + bits_to_next_byte;
280        let len_in_bits = len_in_bits.saturating_sub(bits_to_next_byte);
281
282        if len_in_bits == 0 {
283            return;
284        }
285
286        // We are now byte aligned
287        byte_aligned_bitwise_bin_op_helper(
288            left,
289            offset_in_bits,
290            right,
291            right_offset_in_bits,
292            len_in_bits,
293            op,
294        );
295    }
296}
297
298/// Apply a bitwise operation to a mutable buffer, updating it in place.
299///
300/// Note: applies the operation 64-bits (u64) at a time.
301///
302/// # Arguments
303///
304/// * `offset_in_bits` - Starting bit offset for the current buffer
305/// * `len_in_bits` - Number of bits to process
306/// * `op` - Unary operation to apply (e.g., `|a| !a`). Applied a word at a time
307///
308/// Only the bits in `offset_in_bits..offset_in_bits + len_in_bits` are modified.
309/// Bits outside that range are left unchanged, including the bits sharing a byte
310/// with either end of the range.
311///
312/// # Example: Modify entire buffer
313/// ```
314/// # use arrow_buffer::MutableBuffer;
315/// # use arrow_buffer::bit_util::apply_bitwise_unary_op;
316/// let mut buffer = MutableBuffer::new(2);
317/// buffer.extend_from_slice(&[0b11110000u8, 0b00110011u8]);
318/// // apply bitwise NOT to the buffer in place
319/// apply_bitwise_unary_op(buffer.as_slice_mut(), 0, 16, |a| !a);
320/// assert_eq!(buffer.as_slice(), &[0b00001111u8, 0b11001100u8]);
321/// ```
322///
323/// # Example: Modify buffer with offsets
324/// ```
325/// # use arrow_buffer::MutableBuffer;
326/// # use arrow_buffer::bit_util::apply_bitwise_unary_op;
327/// let mut buffer = MutableBuffer::new(2);
328/// buffer.extend_from_slice(&[0b00000000u8, 0b00000000u8]);
329/// // apply bitwise NOT to 8 bits starting from bit offset 3
330/// apply_bitwise_unary_op(buffer.as_slice_mut(), 3, 8, |a| !a);
331/// assert_eq!(buffer.as_slice(), &[0b11111000u8, 0b00000111u8]);
332/// ```
333///
334/// # Panics
335///
336/// If the offset and length exceed the buffer size.
337pub fn apply_bitwise_unary_op<F>(
338    buffer: &mut [u8],
339    offset_in_bits: usize,
340    len_in_bits: usize,
341    mut op: F,
342) where
343    F: FnMut(u64) -> u64,
344{
345    if len_in_bits == 0 {
346        return;
347    }
348
349    // offset inside a byte
350    let left_bit_offset = offset_in_bits % 8;
351
352    let is_mutable_buffer_byte_aligned = left_bit_offset == 0;
353
354    if is_mutable_buffer_byte_aligned {
355        byte_aligned_bitwise_unary_op_helper(buffer, offset_in_bits, len_in_bits, op);
356    } else {
357        align_to_byte(buffer, &mut op, offset_in_bits, len_in_bits);
358
359        // If we are not byte aligned we will read the first few bits
360        let bits_to_next_byte = 8 - left_bit_offset;
361
362        let offset_in_bits = offset_in_bits + bits_to_next_byte;
363        let len_in_bits = len_in_bits.saturating_sub(bits_to_next_byte);
364
365        if len_in_bits == 0 {
366            return;
367        }
368
369        // We are now byte aligned
370        byte_aligned_bitwise_unary_op_helper(buffer, offset_in_bits, len_in_bits, op);
371    }
372}
373
374/// Perform bitwise binary operation on byte-aligned buffers (i.e. not offsetting into a middle of a byte).
375///
376/// This is the optimized path for byte-aligned operations. It processes data in
377/// u64 chunks for maximum efficiency, then handles any remainder bits.
378///
379/// # Arguments
380///
381/// * `left` - The left mutable buffer (must be byte-aligned)
382/// * `left_offset_in_bits` - Starting bit offset in the left buffer (must be multiple of 8)
383/// * `right` - The right buffer as byte slice
384/// * `right_offset_in_bits` - Starting bit offset in the right buffer
385/// * `len_in_bits` - Number of bits to process
386/// * `op` - Binary operation to apply
387#[inline]
388fn byte_aligned_bitwise_bin_op_helper<F>(
389    left: &mut [u8],
390    left_offset_in_bits: usize,
391    right: impl AsRef<[u8]>,
392    right_offset_in_bits: usize,
393    len_in_bits: usize,
394    mut op: F,
395) where
396    F: FnMut(u64, u64) -> u64,
397{
398    // Must not reach here if we not byte aligned
399    assert_eq!(
400        left_offset_in_bits % 8,
401        0,
402        "offset_in_bits must be byte aligned"
403    );
404
405    // 1. Prepare the buffers
406    let (complete_u64_chunks, remainder_bytes) =
407        U64UnalignedSlice::split(left, left_offset_in_bits, len_in_bits);
408
409    let right_chunks = BitChunks::new(right.as_ref(), right_offset_in_bits, len_in_bits);
410    assert_eq!(
411        self::ceil(right_chunks.remainder_len(), 8),
412        remainder_bytes.len()
413    );
414
415    let right_chunks_iter = right_chunks.iter();
416    assert_eq!(right_chunks_iter.len(), complete_u64_chunks.len());
417
418    // 2. Process complete u64 chunks
419    complete_u64_chunks.zip_modify(right_chunks_iter, &mut op);
420
421    // Handle remainder bits if any
422    if right_chunks.remainder_len() > 0 {
423        handle_mutable_buffer_remainder(
424            &mut op,
425            remainder_bytes,
426            right_chunks.remainder_bits(),
427            right_chunks.remainder_len(),
428        )
429    }
430}
431
432/// Perform bitwise unary operation on byte-aligned buffer.
433///
434/// This is the optimized path for byte-aligned unary operations. It processes data in
435/// u64 chunks for maximum efficiency, then handles any remainder bits.
436///
437/// # Arguments
438///
439/// * `buffer` - The mutable buffer (must be byte-aligned)
440/// * `offset_in_bits` - Starting bit offset (must be multiple of 8)
441/// * `len_in_bits` - Number of bits to process
442/// * `op` - Unary operation to apply (e.g., `|a| !a`)
443#[inline]
444fn byte_aligned_bitwise_unary_op_helper<F>(
445    buffer: &mut [u8],
446    offset_in_bits: usize,
447    len_in_bits: usize,
448    mut op: F,
449) where
450    F: FnMut(u64) -> u64,
451{
452    // Must not reach here if we not byte aligned
453    assert_eq!(offset_in_bits % 8, 0, "offset_in_bits must be byte aligned");
454
455    let remainder_len = len_in_bits % 64;
456
457    let (complete_u64_chunks, remainder_bytes) =
458        U64UnalignedSlice::split(buffer, offset_in_bits, len_in_bits);
459
460    assert_eq!(self::ceil(remainder_len, 8), remainder_bytes.len());
461
462    // 2. Process complete u64 chunks
463    complete_u64_chunks.apply_unary_op(&mut op);
464
465    // Handle remainder bits if any
466    if remainder_len > 0 {
467        handle_mutable_buffer_remainder_unary(&mut op, remainder_bytes, remainder_len)
468    }
469}
470
471/// Align to byte boundary by applying operation to bits before the next byte boundary.
472///
473/// This function handles non-byte-aligned operations by processing bits from the current
474/// position up to the next byte boundary, while preserving all other bits in the byte.
475///
476/// # Arguments
477///
478/// * `op` - Unary operation to apply
479/// * `buffer` - The mutable buffer to modify
480/// * `offset_in_bits` - Starting bit offset (not byte-aligned)
481/// * `remaining_len_in_bits` - Number of bits still to process starting at `offset_in_bits`.
482///   When this is smaller than the number of bits left in the byte, the trailing bits of
483///   the byte are left untouched.
484fn align_to_byte<F>(
485    buffer: &mut [u8],
486    op: &mut F,
487    offset_in_bits: usize,
488    remaining_len_in_bits: usize,
489) where
490    F: FnMut(u64) -> u64,
491{
492    let byte_offset = offset_in_bits / 8;
493    let bit_offset = offset_in_bits % 8;
494
495    // Byte aligned offsets must take the byte aligned path instead
496    debug_assert_ne!(bit_offset, 0, "offset_in_bits must not be byte aligned");
497
498    // 1. read the first byte from the buffer
499    let first_byte: u8 = buffer[byte_offset];
500
501    // 2. Shift byte by the bit offset, keeping only the relevant bits
502    let relevant_first_byte = first_byte >> bit_offset;
503
504    // 3. run the op on the first byte only
505    let result_first_byte = op(relevant_first_byte as u64) as u8;
506
507    // 4. Shift back the result to the original position
508    let result_first_byte = result_first_byte << bit_offset;
509
510    // 5. Mask in only the bits the caller asked to process, i.e. the bits in
511    //    `bit_offset..bit_offset + bits_in_this_byte`. The request may end before the
512    //    byte boundary, in which case the trailing bits must be preserved as well.
513    //
514    //    `bit_offset` is in `1..=7` per the assert above, so `bits_in_this_byte` is at
515    //    most 7 and `bits_in_this_byte + bit_offset <= 8`, keeping the mask within a `u8`.
516    let bits_in_this_byte = (8 - bit_offset).min(remaining_len_in_bits);
517    let write_mask = ((1u8 << bits_in_this_byte) - 1) << bit_offset;
518
519    let result_first_byte = (first_byte & !write_mask) | (result_first_byte & write_mask);
520
521    // 6. write back the result to the buffer
522    buffer[byte_offset] = result_first_byte;
523}
524
525/// Centralized structure to handle a mutable u8 slice as a mutable u64 pointer.
526///
527/// Handle the following:
528/// 1. the lifetime is correct
529/// 2. we read/write within the bounds
530/// 3. We read and write using unaligned
531///
532/// This does not deallocate the underlying pointer when dropped
533///
534/// This is the only place that uses unsafe code to read and write unaligned
535///
536struct U64UnalignedSlice<'a> {
537    /// Pointer to the start of the u64 data
538    ///
539    /// We are using raw pointer as the data came from a u8 slice so we need to read and write unaligned
540    ptr: *mut u64,
541
542    /// Number of u64 elements
543    len: usize,
544
545    /// Marker to tie the lifetime of the pointer to the lifetime of the u8 slice
546    _marker: std::marker::PhantomData<&'a u8>,
547}
548
549impl<'a> U64UnalignedSlice<'a> {
550    /// Create a new [`U64UnalignedSlice`] from a `&mut [u8]` buffer
551    ///
552    /// return the [`U64UnalignedSlice`] and slice of bytes that are not part of the u64 chunks (guaranteed to be less than 8 bytes)
553    ///
554    fn split(
555        buffer: &'a mut [u8],
556        offset_in_bits: usize,
557        len_in_bits: usize,
558    ) -> (Self, &'a mut [u8]) {
559        // 1. Prepare the buffers
560        let left_buffer_mut: &mut [u8] = {
561            let last_offset = self::ceil(offset_in_bits + len_in_bits, 8);
562            assert!(last_offset <= buffer.len());
563
564            let byte_offset = offset_in_bits / 8;
565
566            &mut buffer[byte_offset..last_offset]
567        };
568
569        let number_of_u64_we_can_fit = len_in_bits / (u64::BITS as usize);
570
571        // 2. Split
572        let u64_len_in_bytes = number_of_u64_we_can_fit * size_of::<u64>();
573
574        assert!(u64_len_in_bytes <= left_buffer_mut.len());
575        let (bytes_for_u64, remainder) = left_buffer_mut.split_at_mut(u64_len_in_bytes);
576
577        #[expect(
578            clippy::cast_ptr_alignment,
579            reason = "`U64UnalignedSlice` only reads and writes through the unaligned methods"
580        )]
581        let ptr = bytes_for_u64.as_mut_ptr().cast::<u64>();
582
583        let this = Self {
584            ptr,
585            len: number_of_u64_we_can_fit,
586            _marker: std::marker::PhantomData,
587        };
588
589        (this, remainder)
590    }
591
592    fn len(&self) -> usize {
593        self.len
594    }
595
596    /// Modify the underlying u64 data in place using a binary operation
597    /// with another iterator.
598    fn zip_modify(
599        mut self,
600        mut zip_iter: impl ExactSizeIterator<Item = u64>,
601        mut map: impl FnMut(u64, u64) -> u64,
602    ) {
603        assert_eq!(self.len, zip_iter.len());
604
605        // In order to avoid advancing the pointer at the end of the loop which will
606        // make the last pointer invalid, we handle the first element outside the loop
607        // and then advance the pointer at the start of the loop
608        // making sure that the iterator is not empty
609        if let Some(right) = zip_iter.next() {
610            // SAFETY: We asserted that the iterator length and the current length are the same
611            // and the iterator is not empty, so the pointer is valid
612            unsafe {
613                self.apply_bin_op(right, &mut map);
614            }
615
616            // Because this consumes self we don't update the length
617        }
618
619        for right in zip_iter {
620            // Advance the pointer
621            //
622            // SAFETY: We asserted that the iterator length and the current length are the same
623            self.ptr = unsafe { self.ptr.add(1) };
624
625            // SAFETY: the pointer is valid as we are within the length
626            unsafe {
627                self.apply_bin_op(right, &mut map);
628            }
629
630            // Because this consumes self we don't update the length
631        }
632    }
633
634    /// Centralized function to correctly read the current u64 value and write back the result
635    ///
636    /// # SAFETY
637    /// the caller must ensure that the pointer is valid for reads and writes
638    ///
639    #[inline]
640    unsafe fn apply_bin_op(&mut self, right: u64, mut map: impl FnMut(u64, u64) -> u64) {
641        // SAFETY: The constructor ensures the pointer is valid,
642        // and as to all modifications in U64UnalignedSlice
643        let current_input = unsafe {
644            self.ptr
645                // Reading unaligned as we came from u8 slice
646                .read_unaligned()
647                // bit-packed buffers are stored starting with the least-significant byte first
648                // so when reading as u64 on a big-endian machine, the bytes need to be swapped
649                .to_le()
650        };
651
652        let combined = map(current_input, right);
653
654        // Write the result back
655        //
656        // The pointer came from mutable u8 slice so the pointer is valid for writes,
657        // and we need to write unaligned
658        unsafe { self.ptr.write_unaligned(combined) }
659    }
660
661    /// Modify the underlying u64 data in place using a unary operation.
662    fn apply_unary_op(mut self, mut map: impl FnMut(u64) -> u64) {
663        if self.len == 0 {
664            return;
665        }
666
667        // In order to avoid advancing the pointer at the end of the loop which will
668        // make the last pointer invalid, we handle the first element outside the loop
669        // and then advance the pointer at the start of the loop
670        // making sure that the iterator is not empty
671        // Safety: `self.len > 0` (checked above) and the pointer has not been advanced yet,
672        // so it is valid for reads and writes.
673        unsafe {
674            // I hope the function get inlined and the compiler remove the dead right parameter
675            self.apply_bin_op(0, &mut |left, _| map(left));
676
677            // Because this consumes self we don't update the length
678        }
679
680        for _ in 1..self.len {
681            // Advance the pointer
682            //
683            // SAFETY: we only advance the pointer within the length and not beyond
684            self.ptr = unsafe { self.ptr.add(1) };
685
686            // SAFETY: the pointer is valid as we are within the length
687            unsafe {
688                // I hope the function get inlined and the compiler remove the dead right parameter
689                self.apply_bin_op(0, &mut |left, _| map(left));
690            }
691
692            // Because this consumes self we don't update the length
693        }
694    }
695}
696
697/// Handle remainder bits (< 64 bits) for binary operations.
698///
699/// This function processes the bits that don't form a complete u64 chunk,
700/// ensuring that bits outside the operation range are preserved.
701///
702/// # Arguments
703///
704/// * `op` - Binary operation to apply
705/// * `start_remainder_mut_slice` - slice to the start of remainder bytes
706///   the length must be equal to `ceil(remainder_len, 8)`
707/// * `right_remainder_bits` - Right operand bits
708/// * `remainder_len` - Number of remainder bits
709#[inline]
710fn handle_mutable_buffer_remainder<F>(
711    op: &mut F,
712    start_remainder_mut_slice: &mut [u8],
713    right_remainder_bits: u64,
714    remainder_len: usize,
715) where
716    F: FnMut(u64, u64) -> u64,
717{
718    // Only read from slice the number of remainder bits
719    let left_remainder_bits = get_remainder_bits(start_remainder_mut_slice, remainder_len);
720
721    // Apply the operation
722    let rem = op(left_remainder_bits, right_remainder_bits);
723
724    // Write only the relevant bits back the result to the mutable slice
725    set_remainder_bits(start_remainder_mut_slice, rem, remainder_len);
726}
727
728/// Write remainder bits back to buffer while preserving bits outside the range.
729///
730/// This function carefully updates only the specified bits, leaving all other
731/// bits in the affected bytes unchanged.
732///
733/// # Arguments
734///
735/// * `start_remainder_mut_slice` - the slice of bytes to write the remainder bits to,
736///   the length must be equal to `ceil(remainder_len, 8)`
737/// * `rem` - The result bits to write
738/// * `remainder_len` - Number of bits to write
739#[inline]
740fn set_remainder_bits(start_remainder_mut_slice: &mut [u8], rem: u64, remainder_len: usize) {
741    assert_ne!(
742        start_remainder_mut_slice.len(),
743        0,
744        "start_remainder_mut_slice must not be empty"
745    );
746    assert!(remainder_len < 64, "remainder_len must be less than 64");
747
748    // This assertion is to make sure that the last byte in the slice is the boundary byte
749    // (i.e., the byte that contains both remainder bits and bits outside the remainder)
750    assert_eq!(
751        start_remainder_mut_slice.len(),
752        self::ceil(remainder_len, 8),
753        "start_remainder_mut_slice length must be equal to ceil(remainder_len, 8)"
754    );
755
756    // Need to update the remainder bytes in the mutable buffer
757    // but not override the bits outside the remainder
758
759    // Update `rem` end with the current bytes in the mutable buffer
760    // to preserve the bits outside the remainder
761    let rem = {
762        // 1. Read the byte that we will override
763        //    we only read the last byte as we verified that start_remainder_mut_slice length is
764        //    equal to ceil(remainder_len, 8), which means the last byte is the boundary byte
765        //    containing both remainder bits and bits outside the remainder
766        let current = start_remainder_mut_slice
767            .last()
768            // Unwrap as we already validated the slice is not empty
769            .unwrap();
770
771        // Shift the boundary byte to the position it occupies within `rem`, otherwise
772        // its bits would be compared against the wrong end of the mask below
773        let current = (*current as u64) << ((start_remainder_mut_slice.len() - 1) * 8);
774
775        // Mask where the bits that are inside the remainder are 1
776        // and the bits outside the remainder are 0
777        let inside_remainder_mask = (1 << remainder_len) - 1;
778        // Mask where the bits that are outside the remainder are 1
779        // and the bits inside the remainder are 0
780        let outside_remainder_mask = !inside_remainder_mask;
781
782        // 2. Only keep the bits that are outside the remainder for the value from the mutable buffer
783        let current = current & outside_remainder_mask;
784
785        // 3. Only keep the bits that are inside the remainder for the value from the operation
786        let rem = rem & inside_remainder_mask;
787
788        // 4. Combine the two values
789        current | rem
790    };
791
792    // Write back the result to the mutable slice
793    {
794        let remainder_bytes = start_remainder_mut_slice.len();
795
796        // we are counting starting from the least significant bit, so to_le_bytes should be correct
797        let rem = &rem.to_le_bytes()[0..remainder_bytes];
798
799        // this assumes that `[ToByteSlice]` can be copied directly
800        // without calling `to_byte_slice` for each element,
801        // which is correct for all ArrowNativeType implementations including u64.
802        let src = rem.as_ptr();
803        // Safety: `rem` has length `remainder_bytes`, `start_remainder_mut_slice` has length
804        // `remainder_bytes`, and the two slices are non-overlapping (rem is derived from a
805        // local `to_le_bytes()` call; start_remainder_mut_slice is the caller's mutable buffer).
806        unsafe {
807            std::ptr::copy_nonoverlapping(
808                src,
809                start_remainder_mut_slice.as_mut_ptr(),
810                remainder_bytes,
811            )
812        };
813    }
814}
815
816/// Read remainder bits from a slice.
817///
818/// Reads the specified number of bits from slice and returns them as a u64.
819///
820/// # Arguments
821///
822/// * `remainder` - slice to the start of the bits
823/// * `remainder_len` - Number of bits to read (must be < 64)
824///
825/// # Returns
826///
827/// A u64 containing the bits in the least significant positions
828#[inline]
829fn get_remainder_bits(remainder: &[u8], remainder_len: usize) -> u64 {
830    assert!(remainder.len() < 64, "remainder_len must be less than 64");
831    assert_eq!(
832        remainder.len(),
833        self::ceil(remainder_len, 8),
834        "remainder and remainder len ceil must be the same"
835    );
836
837    let bits = remainder
838        .iter()
839        .enumerate()
840        .fold(0_u64, |acc, (index, &byte)| {
841            acc | ((byte as u64) << (index * 8))
842        });
843
844    bits & ((1 << remainder_len) - 1)
845}
846
847/// Handle remainder bits (< 64 bits) for unary operations.
848///
849/// This function processes the bits that don't form a complete u64 chunk,
850/// ensuring that bits outside the operation range are preserved.
851///
852/// # Arguments
853///
854/// * `op` - Unary operation to apply
855/// * `start_remainder_mut` - Slice of bytes to write the remainder bits to
856/// * `remainder_len` - Number of remainder bits
857#[inline]
858fn handle_mutable_buffer_remainder_unary<F>(
859    op: &mut F,
860    start_remainder_mut: &mut [u8],
861    remainder_len: usize,
862) where
863    F: FnMut(u64) -> u64,
864{
865    // Only read from the slice the number of remainder bits
866    let left_remainder_bits = get_remainder_bits(start_remainder_mut, remainder_len);
867
868    // Apply the operation
869    let rem = op(left_remainder_bits);
870
871    // Write only the relevant bits back the result to the slice
872    set_remainder_bits(start_remainder_mut, rem, remainder_len);
873}
874
875#[cfg(test)]
876mod tests {
877    use std::collections::HashSet;
878
879    use super::*;
880    use crate::bit_iterator::BitIterator;
881    use crate::{BooleanBuffer, BooleanBufferBuilder, MutableBuffer};
882    use rand::rngs::StdRng;
883    use rand::{RngExt, SeedableRng};
884
885    #[test]
886    fn test_round_upto_multiple_of_64() {
887        assert_eq!(0, round_upto_multiple_of_64(0));
888        assert_eq!(64, round_upto_multiple_of_64(1));
889        assert_eq!(64, round_upto_multiple_of_64(63));
890        assert_eq!(64, round_upto_multiple_of_64(64));
891        assert_eq!(128, round_upto_multiple_of_64(65));
892        assert_eq!(192, round_upto_multiple_of_64(129));
893    }
894
895    #[test]
896    #[should_panic(expected = "failed to round upto multiple of 64")]
897    fn test_round_upto_multiple_of_64_panic() {
898        let _ = round_upto_multiple_of_64(usize::MAX);
899    }
900
901    #[test]
902    #[should_panic(expected = "failed to round to next highest power of 2")]
903    fn test_round_upto_panic() {
904        let _ = round_upto_power_of_2(usize::MAX, 2);
905    }
906
907    #[test]
908    fn test_get_bit() {
909        // 00001101
910        assert!(get_bit(&[0b00001101], 0));
911        assert!(!get_bit(&[0b00001101], 1));
912        assert!(get_bit(&[0b00001101], 2));
913        assert!(get_bit(&[0b00001101], 3));
914
915        // 01001001 01010010
916        assert!(get_bit(&[0b01001001, 0b01010010], 0));
917        assert!(!get_bit(&[0b01001001, 0b01010010], 1));
918        assert!(!get_bit(&[0b01001001, 0b01010010], 2));
919        assert!(get_bit(&[0b01001001, 0b01010010], 3));
920        assert!(!get_bit(&[0b01001001, 0b01010010], 4));
921        assert!(!get_bit(&[0b01001001, 0b01010010], 5));
922        assert!(get_bit(&[0b01001001, 0b01010010], 6));
923        assert!(!get_bit(&[0b01001001, 0b01010010], 7));
924        assert!(!get_bit(&[0b01001001, 0b01010010], 8));
925        assert!(get_bit(&[0b01001001, 0b01010010], 9));
926        assert!(!get_bit(&[0b01001001, 0b01010010], 10));
927        assert!(!get_bit(&[0b01001001, 0b01010010], 11));
928        assert!(get_bit(&[0b01001001, 0b01010010], 12));
929        assert!(!get_bit(&[0b01001001, 0b01010010], 13));
930        assert!(get_bit(&[0b01001001, 0b01010010], 14));
931        assert!(!get_bit(&[0b01001001, 0b01010010], 15));
932    }
933
934    pub fn seedable_rng() -> StdRng {
935        StdRng::seed_from_u64(42)
936    }
937
938    #[test]
939    fn test_get_bit_raw() {
940        const NUM_BYTE: usize = 10;
941        let mut buf = [0; NUM_BYTE];
942        let mut expected = vec![];
943        let mut rng = seedable_rng();
944        for i in 0..8 * NUM_BYTE {
945            let b = rng.random_bool(0.5);
946            expected.push(b);
947            if b {
948                set_bit(&mut buf[..], i)
949            }
950        }
951
952        let raw_ptr = buf.as_ptr();
953        for (i, b) in expected.iter().enumerate() {
954            unsafe {
955                assert_eq!(*b, get_bit_raw(raw_ptr, i));
956            }
957        }
958    }
959
960    #[test]
961    fn test_set_bit() {
962        let mut b = [0b00000010];
963        set_bit(&mut b, 0);
964        assert_eq!([0b00000011], b);
965        set_bit(&mut b, 1);
966        assert_eq!([0b00000011], b);
967        set_bit(&mut b, 7);
968        assert_eq!([0b10000011], b);
969    }
970
971    #[test]
972    fn test_unset_bit() {
973        let mut b = [0b11111101];
974        unset_bit(&mut b, 0);
975        assert_eq!([0b11111100], b);
976        unset_bit(&mut b, 1);
977        assert_eq!([0b11111100], b);
978        unset_bit(&mut b, 7);
979        assert_eq!([0b01111100], b);
980    }
981
982    #[test]
983    fn test_set_bit_raw() {
984        const NUM_BYTE: usize = 10;
985        let mut buf = vec![0; NUM_BYTE];
986        let mut expected = vec![];
987        let mut rng = seedable_rng();
988        for i in 0..8 * NUM_BYTE {
989            let b = rng.random_bool(0.5);
990            expected.push(b);
991            if b {
992                unsafe {
993                    set_bit_raw(buf.as_mut_ptr(), i);
994                }
995            }
996        }
997
998        let raw_ptr = buf.as_ptr();
999        for (i, b) in expected.iter().enumerate() {
1000            unsafe {
1001                assert_eq!(*b, get_bit_raw(raw_ptr, i));
1002            }
1003        }
1004    }
1005
1006    #[test]
1007    fn test_unset_bit_raw() {
1008        const NUM_BYTE: usize = 10;
1009        let mut buf = vec![255; NUM_BYTE];
1010        let mut expected = vec![];
1011        let mut rng = seedable_rng();
1012        for i in 0..8 * NUM_BYTE {
1013            let b = rng.random_bool(0.5);
1014            expected.push(b);
1015            if !b {
1016                unsafe {
1017                    unset_bit_raw(buf.as_mut_ptr(), i);
1018                }
1019            }
1020        }
1021
1022        let raw_ptr = buf.as_ptr();
1023        for (i, b) in expected.iter().enumerate() {
1024            unsafe {
1025                assert_eq!(*b, get_bit_raw(raw_ptr, i));
1026            }
1027        }
1028    }
1029
1030    #[test]
1031    fn test_get_set_bit_roundtrip() {
1032        const NUM_BYTES: usize = 10;
1033        const NUM_SETS: usize = 10;
1034
1035        let mut buffer: [u8; NUM_BYTES * 8] = [0; NUM_BYTES * 8];
1036        let mut v = HashSet::new();
1037        let mut rng = seedable_rng();
1038        for _ in 0..NUM_SETS {
1039            let offset = rng.random_range(0..8 * NUM_BYTES);
1040            v.insert(offset);
1041            set_bit(&mut buffer[..], offset);
1042        }
1043        for i in 0..NUM_BYTES * 8 {
1044            assert_eq!(v.contains(&i), get_bit(&buffer[..], i));
1045        }
1046    }
1047
1048    #[test]
1049    fn test_ceil() {
1050        assert_eq!(ceil(0, 1), 0);
1051        assert_eq!(ceil(1, 1), 1);
1052        assert_eq!(ceil(1, 2), 1);
1053        assert_eq!(ceil(1, 8), 1);
1054        assert_eq!(ceil(7, 8), 1);
1055        assert_eq!(ceil(8, 8), 1);
1056        assert_eq!(ceil(9, 8), 2);
1057        assert_eq!(ceil(9, 9), 1);
1058        assert_eq!(ceil(10000000000, 10), 1000000000);
1059        assert_eq!(ceil(10, 10000000000), 1);
1060        assert_eq!(ceil(10000000000, 1000000000), 10);
1061    }
1062
1063    #[test]
1064    fn test_read_up_to() {
1065        let all_ones = &[0b10111001, 0b10001100];
1066
1067        for (bit_offset, expected) in [
1068            (0, 0b00000001),
1069            (1, 0b00000000),
1070            (2, 0b00000000),
1071            (3, 0b00000001),
1072            (4, 0b00000001),
1073            (5, 0b00000001),
1074            (6, 0b00000000),
1075            (7, 0b00000001),
1076        ] {
1077            let result = read_up_to_byte_from_offset(all_ones, 1, bit_offset);
1078            assert_eq!(
1079                result, expected,
1080                "failed at bit_offset {bit_offset}. result, expected:\n{result:08b}\n{expected:08b}"
1081            );
1082        }
1083
1084        for (bit_offset, expected) in [
1085            (0, 0b00000001),
1086            (1, 0b00000000),
1087            (2, 0b00000010),
1088            (3, 0b00000011),
1089            (4, 0b00000011),
1090            (5, 0b00000001),
1091            (6, 0b00000010),
1092            (7, 0b00000001),
1093        ] {
1094            let result = read_up_to_byte_from_offset(all_ones, 2, bit_offset);
1095            assert_eq!(
1096                result, expected,
1097                "failed at bit_offset {bit_offset}. result, expected:\n{result:08b}\n{expected:08b}"
1098            );
1099        }
1100
1101        for (bit_offset, expected) in [
1102            (0, 0b00111001),
1103            (1, 0b00011100),
1104            (2, 0b00101110),
1105            (3, 0b00010111),
1106            (4, 0b00001011),
1107            (5, 0b00100101),
1108            (6, 0b00110010),
1109            (7, 0b00011001),
1110        ] {
1111            let result = read_up_to_byte_from_offset(all_ones, 6, bit_offset);
1112            assert_eq!(
1113                result, expected,
1114                "failed at bit_offset {bit_offset}. result, expected:\n{result:08b}\n{expected:08b}"
1115            );
1116        }
1117
1118        for (bit_offset, expected) in [
1119            (0, 0b00111001),
1120            (1, 0b01011100),
1121            (2, 0b00101110),
1122            (3, 0b00010111),
1123            (4, 0b01001011),
1124            (5, 0b01100101),
1125            (6, 0b00110010),
1126            (7, 0b00011001),
1127        ] {
1128            let result = read_up_to_byte_from_offset(all_ones, 7, bit_offset);
1129            assert_eq!(
1130                result, expected,
1131                "failed at bit_offset {bit_offset}. result, expected:\n{result:08b}\n{expected:08b}"
1132            );
1133        }
1134    }
1135
1136    /// Verifies that a unary operation applied to a buffer using u64 chunks
1137    /// is the same as applying the operation bit by bit.
1138    fn test_mutable_buffer_bin_op_helper<F, G>(
1139        left_data: &[bool],
1140        right_data: &[bool],
1141        left_offset_in_bits: usize,
1142        right_offset_in_bits: usize,
1143        len_in_bits: usize,
1144        op: F,
1145        mut expected_op: G,
1146    ) where
1147        F: FnMut(u64, u64) -> u64,
1148        G: FnMut(bool, bool) -> bool,
1149    {
1150        let mut left_buffer = BooleanBufferBuilder::new(len_in_bits);
1151        left_buffer.append_slice(left_data);
1152        let right_buffer = BooleanBuffer::from(right_data);
1153
1154        let expected: Vec<bool> = left_data
1155            .iter()
1156            .skip(left_offset_in_bits)
1157            .zip(right_data.iter().skip(right_offset_in_bits))
1158            .take(len_in_bits)
1159            .map(|(l, r)| expected_op(*l, *r))
1160            .collect();
1161
1162        let before = left_buffer.as_slice().to_vec();
1163
1164        apply_bitwise_binary_op(
1165            left_buffer.as_slice_mut(),
1166            left_offset_in_bits,
1167            right_buffer.inner(),
1168            right_offset_in_bits,
1169            len_in_bits,
1170            op,
1171        );
1172
1173        let result: Vec<bool> =
1174            BitIterator::new(left_buffer.as_slice(), left_offset_in_bits, len_in_bits).collect();
1175
1176        assert_eq!(
1177            result, expected,
1178            "Failed with left_offset={left_offset_in_bits}, right_offset={right_offset_in_bits}, len={len_in_bits}"
1179        );
1180
1181        assert_bits_outside_range_preserved(
1182            &before,
1183            left_buffer.as_slice(),
1184            left_offset_in_bits,
1185            len_in_bits,
1186            &format!(
1187                "left_offset={left_offset_in_bits}, right_offset={right_offset_in_bits}, len={len_in_bits}"
1188            ),
1189        );
1190    }
1191
1192    /// Asserts that every bit outside `offset_in_bits..offset_in_bits + len_in_bits`
1193    /// is identical in `before` and `after`.
1194    fn assert_bits_outside_range_preserved(
1195        before: &[u8],
1196        after: &[u8],
1197        offset_in_bits: usize,
1198        len_in_bits: usize,
1199        context: &str,
1200    ) {
1201        assert_eq!(before.len(), after.len());
1202        for i in 0..before.len() * 8 {
1203            if i >= offset_in_bits && i < offset_in_bits + len_in_bits {
1204                continue;
1205            }
1206            assert_eq!(
1207                get_bit(before, i),
1208                get_bit(after, i),
1209                "bit {i} outside the requested range was modified ({context})"
1210            );
1211        }
1212    }
1213
1214    /// Verifies that a unary operation applied to a buffer using u64 chunks
1215    /// is the same as applying the operation bit by bit.
1216    fn test_mutable_buffer_unary_op_helper<F, G>(
1217        data: &[bool],
1218        offset_in_bits: usize,
1219        len_in_bits: usize,
1220        op: F,
1221        mut expected_op: G,
1222    ) where
1223        F: FnMut(u64) -> u64,
1224        G: FnMut(bool) -> bool,
1225    {
1226        let mut buffer = BooleanBufferBuilder::new(len_in_bits);
1227        buffer.append_slice(data);
1228
1229        let expected: Vec<bool> = data
1230            .iter()
1231            .skip(offset_in_bits)
1232            .take(len_in_bits)
1233            .map(|b| expected_op(*b))
1234            .collect();
1235
1236        let before = buffer.as_slice().to_vec();
1237
1238        apply_bitwise_unary_op(buffer.as_slice_mut(), offset_in_bits, len_in_bits, op);
1239
1240        let result: Vec<bool> =
1241            BitIterator::new(buffer.as_slice(), offset_in_bits, len_in_bits).collect();
1242
1243        assert_eq!(
1244            result, expected,
1245            "Failed with offset={offset_in_bits}, len={len_in_bits}"
1246        );
1247
1248        assert_bits_outside_range_preserved(
1249            &before,
1250            buffer.as_slice(),
1251            offset_in_bits,
1252            len_in_bits,
1253            &format!("offset={offset_in_bits}, len={len_in_bits}"),
1254        );
1255    }
1256
1257    // Helper to create test data of specific length
1258    fn create_test_data(len: usize) -> (Vec<bool>, Vec<bool>) {
1259        let mut rng = rand::rng();
1260        let left: Vec<bool> = (0..len).map(|_| rng.random_bool(0.5)).collect();
1261        let right: Vec<bool> = (0..len).map(|_| rng.random_bool(0.5)).collect();
1262        (left, right)
1263    }
1264
1265    /// Test all binary operations (AND, OR, XOR) with the given parameters
1266    fn test_all_binary_ops(
1267        left_data: &[bool],
1268        right_data: &[bool],
1269        left_offset_in_bits: usize,
1270        right_offset_in_bits: usize,
1271        len_in_bits: usize,
1272    ) {
1273        // Test AND
1274        test_mutable_buffer_bin_op_helper(
1275            left_data,
1276            right_data,
1277            left_offset_in_bits,
1278            right_offset_in_bits,
1279            len_in_bits,
1280            |a, b| a & b,
1281            |a, b| a & b,
1282        );
1283
1284        // Test OR
1285        test_mutable_buffer_bin_op_helper(
1286            left_data,
1287            right_data,
1288            left_offset_in_bits,
1289            right_offset_in_bits,
1290            len_in_bits,
1291            |a, b| a | b,
1292            |a, b| a | b,
1293        );
1294
1295        // Test XOR
1296        test_mutable_buffer_bin_op_helper(
1297            left_data,
1298            right_data,
1299            left_offset_in_bits,
1300            right_offset_in_bits,
1301            len_in_bits,
1302            |a, b| a ^ b,
1303            |a, b| a ^ b,
1304        );
1305    }
1306
1307    // ===== Combined Binary Operation Tests =====
1308
1309    #[test]
1310    fn test_binary_ops_less_than_byte() {
1311        let (left, right) = create_test_data(4);
1312        test_all_binary_ops(&left, &right, 0, 0, 4);
1313    }
1314
1315    #[test]
1316    fn test_binary_ops_less_than_byte_across_boundary() {
1317        let (left, right) = create_test_data(16);
1318        test_all_binary_ops(&left, &right, 6, 6, 4);
1319    }
1320
1321    #[test]
1322    fn test_binary_ops_exactly_byte() {
1323        let (left, right) = create_test_data(16);
1324        test_all_binary_ops(&left, &right, 0, 0, 8);
1325    }
1326
1327    #[test]
1328    fn test_binary_ops_more_than_byte_less_than_u64() {
1329        let (left, right) = create_test_data(64);
1330        test_all_binary_ops(&left, &right, 0, 0, 32);
1331    }
1332
1333    #[test]
1334    fn test_binary_ops_exactly_u64() {
1335        let (left, right) = create_test_data(180);
1336        test_all_binary_ops(&left, &right, 0, 0, 64);
1337        test_all_binary_ops(&left, &right, 64, 9, 64);
1338        test_all_binary_ops(&left, &right, 8, 100, 64);
1339        test_all_binary_ops(&left, &right, 1, 15, 64);
1340        test_all_binary_ops(&left, &right, 12, 10, 64);
1341        test_all_binary_ops(&left, &right, 180 - 64, 2, 64);
1342    }
1343
1344    #[test]
1345    fn test_binary_ops_more_than_u64_not_multiple() {
1346        let (left, right) = create_test_data(200);
1347        test_all_binary_ops(&left, &right, 0, 0, 100);
1348    }
1349
1350    #[test]
1351    fn test_binary_ops_exactly_multiple_u64() {
1352        let (left, right) = create_test_data(256);
1353        test_all_binary_ops(&left, &right, 0, 0, 128);
1354    }
1355
1356    #[test]
1357    fn test_binary_ops_more_than_multiple_u64() {
1358        let (left, right) = create_test_data(300);
1359        test_all_binary_ops(&left, &right, 0, 0, 200);
1360    }
1361
1362    #[test]
1363    fn test_binary_ops_byte_aligned_no_remainder() {
1364        let (left, right) = create_test_data(200);
1365        test_all_binary_ops(&left, &right, 0, 0, 128);
1366    }
1367
1368    #[test]
1369    fn test_binary_ops_byte_aligned_with_remainder() {
1370        let (left, right) = create_test_data(200);
1371        test_all_binary_ops(&left, &right, 0, 0, 100);
1372    }
1373
1374    #[test]
1375    fn test_binary_ops_not_byte_aligned_no_remainder() {
1376        let (left, right) = create_test_data(200);
1377        test_all_binary_ops(&left, &right, 3, 3, 128);
1378    }
1379
1380    #[test]
1381    fn test_binary_ops_not_byte_aligned_with_remainder() {
1382        let (left, right) = create_test_data(200);
1383        test_all_binary_ops(&left, &right, 5, 5, 100);
1384    }
1385
1386    #[test]
1387    fn test_binary_ops_different_offsets() {
1388        let (left, right) = create_test_data(200);
1389        test_all_binary_ops(&left, &right, 3, 7, 50);
1390    }
1391
1392    #[test]
1393    fn test_binary_ops_offsets_greater_than_8_less_than_64() {
1394        let (left, right) = create_test_data(200);
1395        test_all_binary_ops(&left, &right, 13, 27, 100);
1396    }
1397
1398    // ===== NOT (Unary) Operation Tests =====
1399
1400    #[test]
1401    fn test_not_less_than_byte() {
1402        let data = vec![true, false, true, false];
1403        test_mutable_buffer_unary_op_helper(&data, 0, 4, |a| !a, |a| !a);
1404    }
1405
1406    #[test]
1407    fn test_not_less_than_byte_across_boundary() {
1408        let data: Vec<bool> = (0..16).map(|i| i % 2 == 0).collect();
1409        test_mutable_buffer_unary_op_helper(&data, 6, 4, |a| !a, |a| !a);
1410    }
1411
1412    #[test]
1413    fn test_not_exactly_byte() {
1414        let data: Vec<bool> = (0..16).map(|i| i % 2 == 0).collect();
1415        test_mutable_buffer_unary_op_helper(&data, 0, 8, |a| !a, |a| !a);
1416    }
1417
1418    #[test]
1419    fn test_not_more_than_byte_less_than_u64() {
1420        let data: Vec<bool> = (0..64).map(|i| i % 2 == 0).collect();
1421        test_mutable_buffer_unary_op_helper(&data, 0, 32, |a| !a, |a| !a);
1422    }
1423
1424    #[test]
1425    fn test_not_exactly_u64() {
1426        let data: Vec<bool> = (0..128).map(|i| i % 2 == 0).collect();
1427        test_mutable_buffer_unary_op_helper(&data, 0, 64, |a| !a, |a| !a);
1428    }
1429
1430    #[test]
1431    fn test_not_more_than_u64_not_multiple() {
1432        let data: Vec<bool> = (0..200).map(|i| i % 2 == 0).collect();
1433        test_mutable_buffer_unary_op_helper(&data, 0, 100, |a| !a, |a| !a);
1434    }
1435
1436    #[test]
1437    fn test_not_exactly_multiple_u64() {
1438        let data: Vec<bool> = (0..256).map(|i| i % 2 == 0).collect();
1439        test_mutable_buffer_unary_op_helper(&data, 0, 128, |a| !a, |a| !a);
1440    }
1441
1442    #[test]
1443    fn test_not_more_than_multiple_u64() {
1444        let data: Vec<bool> = (0..300).map(|i| i % 2 == 0).collect();
1445        test_mutable_buffer_unary_op_helper(&data, 0, 200, |a| !a, |a| !a);
1446    }
1447
1448    #[test]
1449    fn test_not_byte_aligned_no_remainder() {
1450        let data: Vec<bool> = (0..200).map(|i| i % 2 == 0).collect();
1451        test_mutable_buffer_unary_op_helper(&data, 0, 128, |a| !a, |a| !a);
1452    }
1453
1454    #[test]
1455    fn test_not_byte_aligned_with_remainder() {
1456        let data: Vec<bool> = (0..200).map(|i| i % 2 == 0).collect();
1457        test_mutable_buffer_unary_op_helper(&data, 0, 100, |a| !a, |a| !a);
1458    }
1459
1460    #[test]
1461    fn test_not_not_byte_aligned_no_remainder() {
1462        let data: Vec<bool> = (0..200).map(|i| i % 2 == 0).collect();
1463        test_mutable_buffer_unary_op_helper(&data, 3, 128, |a| !a, |a| !a);
1464    }
1465
1466    #[test]
1467    fn test_not_not_byte_aligned_with_remainder() {
1468        let data: Vec<bool> = (0..200).map(|i| i % 2 == 0).collect();
1469        test_mutable_buffer_unary_op_helper(&data, 5, 100, |a| !a, |a| !a);
1470    }
1471
1472    // ===== Edge Cases =====
1473
1474    #[test]
1475    fn test_empty_length() {
1476        let (left, right) = create_test_data(16);
1477        test_all_binary_ops(&left, &right, 0, 0, 0);
1478    }
1479
1480    #[test]
1481    fn test_single_bit() {
1482        let (left, right) = create_test_data(16);
1483        test_all_binary_ops(&left, &right, 0, 0, 1);
1484    }
1485
1486    #[test]
1487    fn test_single_bit_at_offset() {
1488        let (left, right) = create_test_data(16);
1489        test_all_binary_ops(&left, &right, 7, 7, 1);
1490    }
1491
1492    #[test]
1493    fn test_not_single_bit() {
1494        let data = vec![true, false, true, false];
1495        test_mutable_buffer_unary_op_helper(&data, 0, 1, |a| !a, |a| !a);
1496    }
1497
1498    #[test]
1499    fn test_not_empty_length() {
1500        let data = vec![true, false, true, false];
1501        test_mutable_buffer_unary_op_helper(&data, 0, 0, |a| !a, |a| !a);
1502    }
1503
1504    #[test]
1505    fn test_less_than_byte_unaligned_and_not_enough_bits() {
1506        let left_offset_in_bits = 2;
1507        let right_offset_in_bits = 4;
1508        let len_in_bits = 1;
1509
1510        // Single byte
1511        let right = (0..8).map(|i| (i / 2) % 2 == 0).collect::<Vec<_>>();
1512        // less than a byte
1513        let left = (0..3).map(|i| i % 2 == 0).collect::<Vec<_>>();
1514        test_all_binary_ops(
1515            &left,
1516            &right,
1517            left_offset_in_bits,
1518            right_offset_in_bits,
1519            len_in_bits,
1520        );
1521    }
1522
1523    /// Ranges that start and end inside the same non-byte-aligned byte must not
1524    /// touch the trailing bits of that byte.
1525    #[test]
1526    fn test_ops_ending_inside_the_first_partial_byte() {
1527        let (left, right) = create_test_data(32);
1528        for offset in 1..8 {
1529            // Inclusive so the range ending exactly on the byte boundary is covered too
1530            for len in 1..=(8 - offset) {
1531                test_all_binary_ops(&left, &right, offset, offset, len);
1532                test_all_binary_ops(&left, &right, offset, (offset + 3) % 8, len);
1533                test_mutable_buffer_unary_op_helper(&left, offset, len, |a| !a, |a| !a);
1534            }
1535        }
1536    }
1537
1538    #[test]
1539    fn test_and_within_first_partial_byte_preserves_trailing_bits() {
1540        let mut left = vec![0b11111111u8, 0b11111111u8];
1541        let right = vec![0b00000000u8, 0b00000000u8];
1542        // AND a single bit at bit offset 1: only bit 1 may be cleared
1543        apply_bitwise_binary_op(&mut left, 1, &right, 0, 1, |a, b| a & b);
1544        assert_eq!(left, vec![0b11111101u8, 0b11111111u8]);
1545    }
1546
1547    #[test]
1548    fn test_not_within_first_partial_byte_preserves_trailing_bits() {
1549        let mut buffer = vec![0b00000000u8];
1550        // NOT two bits at bit offset 3: only bits 3 and 4 may be flipped
1551        apply_bitwise_unary_op(&mut buffer, 3, 2, |a| !a);
1552        assert_eq!(buffer, vec![0b00011000u8]);
1553    }
1554
1555    /// When the remainder spans more than one byte, the byte holding the end of the
1556    /// range is the *last* byte of the remainder, not the first. Its bits above the
1557    /// remainder must survive.
1558    #[test]
1559    fn test_or_with_multi_byte_remainder_preserves_boundary_bits() {
1560        let mut left = vec![0b00000000u8, 0b00000000u8, 0b11110000u8];
1561        let right = vec![0b11111111u8, 0b11111111u8, 0b11111111u8];
1562        // OR over 20 bits: bits 20..24 of `left` are outside the range and must stay set
1563        apply_bitwise_binary_op(&mut left, 0, &right, 0, 20, |a, b| a | b);
1564        assert_eq!(
1565            left,
1566            vec![0b11111111u8, 0b11111111u8, 0b11111111u8],
1567            "the boundary byte lost its out-of-range bits"
1568        );
1569    }
1570
1571    #[test]
1572    fn test_not_with_multi_byte_remainder_preserves_boundary_bits() {
1573        let mut buffer = vec![0b00000000u8, 0b00000000u8, 0b11111111u8];
1574        // NOT over 20 bits: only bits 16..20 of the last byte may be flipped
1575        apply_bitwise_unary_op(&mut buffer, 0, 20, |a| !a);
1576        assert_eq!(
1577            buffer,
1578            vec![0b11111111u8, 0b11111111u8, 0b11110000u8],
1579            "the boundary byte lost its out-of-range bits"
1580        );
1581    }
1582
1583    #[test]
1584    fn test_bitwise_binary_op_offset_out_of_bounds() {
1585        let input = vec![0b10101010u8, 0b01010101u8];
1586        let mut buffer = MutableBuffer::new(2); // space for 16 bits
1587        buffer.extend_from_slice(&input); // only 2 bytes
1588        apply_bitwise_binary_op(
1589            buffer.as_slice_mut(),
1590            100, // exceeds buffer length, becomes a noop
1591            [0b11110000u8, 0b00001111u8],
1592            0,
1593            0,
1594            |a, b| a & b,
1595        );
1596        assert_eq!(buffer.as_slice(), &input);
1597    }
1598
1599    #[test]
1600    #[should_panic(expected = "assertion failed: last_offset <= buffer.len()")]
1601    fn test_bitwise_binary_op_length_out_of_bounds() {
1602        let mut buffer = MutableBuffer::new(2); // space for 16 bits
1603        buffer.extend_from_slice(&[0b10101010u8, 0b01010101u8]); // only 2 bytes
1604        apply_bitwise_binary_op(
1605            buffer.as_slice_mut(),
1606            0, // exceeds buffer length
1607            [0b11110000u8, 0b00001111u8],
1608            0,
1609            100,
1610            |a, b| a & b,
1611        );
1612        assert_eq!(buffer.as_slice(), &[0b10101010u8, 0b01010101u8]);
1613    }
1614
1615    #[test]
1616    #[should_panic(expected = "offset + len out of bounds")]
1617    fn test_bitwise_binary_op_right_len_out_of_bounds() {
1618        let mut buffer = MutableBuffer::new(2); // space for 16 bits
1619        buffer.extend_from_slice(&[0b10101010u8, 0b01010101u8]); // only 2 bytes
1620        apply_bitwise_binary_op(
1621            buffer.as_slice_mut(),
1622            0, // exceeds buffer length
1623            [0b11110000u8, 0b00001111u8],
1624            1000,
1625            16,
1626            |a, b| a & b,
1627        );
1628        assert_eq!(buffer.as_slice(), &[0b10101010u8, 0b01010101u8]);
1629    }
1630
1631    #[test]
1632    #[should_panic(expected = "the len is 2 but the index is 12")]
1633    fn test_bitwise_unary_op_offset_out_of_bounds() {
1634        let input = vec![0b10101010u8, 0b01010101u8];
1635        let mut buffer = MutableBuffer::new(2); // space for 16 bits
1636        buffer.extend_from_slice(&input); // only 2 bytes
1637        apply_bitwise_unary_op(
1638            buffer.as_slice_mut(),
1639            100, // exceeds buffer length, becomes a noop
1640            8,
1641            |a| !a,
1642        );
1643        assert_eq!(buffer.as_slice(), &input);
1644    }
1645
1646    #[test]
1647    #[should_panic(expected = "assertion failed: last_offset <= buffer.len()")]
1648    fn test_bitwise_unary_op_length_out_of_bounds2() {
1649        let input = vec![0b10101010u8, 0b01010101u8];
1650        let mut buffer = MutableBuffer::new(2); // space for 16 bits
1651        buffer.extend_from_slice(&input); // only 2 bytes
1652        apply_bitwise_unary_op(
1653            buffer.as_slice_mut(),
1654            3,   // start at bit 3, to exercise different path
1655            100, // exceeds buffer length
1656            |a| !a,
1657        );
1658        assert_eq!(buffer.as_slice(), &input);
1659    }
1660}