Skip to main content

vortex_buffer/bit/
buf_mut.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::ops::Not;
5
6use bitvec::view::BitView;
7
8use crate::BitBuffer;
9use crate::BufferAllocatorRef;
10use crate::BufferMut;
11use crate::ByteBufferMut;
12use crate::bit::collect_bool_words;
13use crate::bit::get_bit_unchecked;
14use crate::bit::ops;
15use crate::bit::pack::collect_bool_words_multiversioned;
16use crate::bit::set_bit_unchecked;
17use crate::bit::unset_bit_unchecked;
18use crate::buffer_mut;
19
20/// Sets all bits in the bit-range `[start_bit, end_bit)` of `slice` to `value`.
21///
22/// Bits within the slice's whole 64-bit words are filled a word at a time, while bits in the
23/// trailing bytes that do not make up a whole word, where appends usually land, are filled a byte
24/// at a time.
25#[allow(clippy::inline_always)]
26#[inline(always)]
27pub(crate) fn fill_bits(slice: &mut [u8], start_bit: usize, end_bit: usize, value: bool) {
28    if start_bit >= end_bit {
29        return;
30    }
31    debug_assert!(end_bit <= slice.len() * 8);
32
33    let word_bits = slice.len() / 8 * 64;
34    if end_bit <= word_bits {
35        fill_word_bits(slice, start_bit, end_bit, value);
36        return;
37    }
38    if start_bit < word_bits {
39        fill_word_bits(slice, start_bit, word_bits, value);
40    }
41    fill_byte_bits(slice, start_bit.max(word_bits), end_bit, value);
42}
43
44/// Fills the non-empty bit-range `[start_bit, end_bit)`, which must lie within whole 64-bit words
45/// of `slice`. The partial words at either end get one masked read-modify-write each and the
46/// whole words between them are filled directly.
47#[allow(clippy::inline_always)]
48#[inline(always)]
49fn fill_word_bits(slice: &mut [u8], start_bit: usize, end_bit: usize, value: bool) {
50    let words = slice.as_chunks_mut::<8>().0;
51    let (start_word, start_rem) = (start_bit / 64, start_bit % 64);
52    let (end_word, end_rem) = (end_bit / 64, end_bit % 64);
53
54    if start_word == end_word {
55        // `start_rem < end_rem` because the range is non-empty and within one word.
56        fill_word_mask(
57            &mut words[start_word],
58            low_bits(end_rem) & !low_bits(start_rem),
59            value,
60        );
61        return;
62    }
63
64    fill_word_mask(&mut words[start_word], !low_bits(start_rem), value);
65    words[start_word + 1..end_word].fill(if value { [0xFF; 8] } else { [0x00; 8] });
66    if end_rem != 0 {
67        fill_word_mask(&mut words[end_word], low_bits(end_rem), value);
68    }
69}
70
71/// A `u64` with the low `n < 64` bits set.
72#[allow(clippy::inline_always)]
73#[inline(always)]
74const fn low_bits(n: usize) -> u64 {
75    (1u64 << n) - 1
76}
77
78/// Sets the bits of the little-endian `word` selected by `mask` to `value`.
79#[allow(clippy::inline_always)]
80#[inline(always)]
81fn fill_word_mask(word: &mut [u8; 8], mask: u64, value: bool) {
82    let current = u64::from_le_bytes(*word);
83    let updated = if value {
84        current | mask
85    } else {
86        current & !mask
87    };
88    *word = updated.to_le_bytes();
89}
90
91/// Fills the non-empty bit-range `[start_bit, end_bit)` of `slice` a byte at a time.
92#[allow(clippy::inline_always)]
93#[inline(always)]
94fn fill_byte_bits(slice: &mut [u8], start_bit: usize, end_bit: usize, value: bool) {
95    let fill_byte: u8 = if value { 0xFF } else { 0x00 };
96
97    let start_byte = start_bit / 8;
98    let start_rem = start_bit % 8;
99    let end_byte = end_bit / 8;
100    let end_rem = end_bit % 8;
101
102    if start_byte == end_byte {
103        // All bits are in the same byte
104        let mask = ((1u8 << (end_rem - start_rem)) - 1) << start_rem;
105        if value {
106            slice[start_byte] |= mask;
107        } else {
108            slice[start_byte] &= !mask;
109        }
110    } else {
111        // First partial byte
112        if start_rem != 0 {
113            let mask = !((1u8 << start_rem) - 1);
114            if value {
115                slice[start_byte] |= mask;
116            } else {
117                slice[start_byte] &= !mask;
118            }
119        }
120
121        // Middle bytes
122        let fill_start = if start_rem != 0 {
123            start_byte + 1
124        } else {
125            start_byte
126        };
127        if fill_start < end_byte {
128            slice[fill_start..end_byte].fill(fill_byte);
129        }
130
131        // Last partial byte
132        if end_rem != 0 {
133            let mask = (1u8 << end_rem) - 1;
134            if value {
135                slice[end_byte] |= mask;
136            } else {
137                slice[end_byte] &= !mask;
138            }
139        }
140    }
141}
142
143/// A mutable bitset buffer that allows random access to individual bits for set and get.
144///
145///
146/// # Example
147/// ```
148/// use vortex_buffer::BitBufferMut;
149///
150/// let mut bools = BitBufferMut::new_unset(10);
151/// bools.set_to(9, true);
152/// for i in 0..9 {
153///    assert!(!bools.value(i));
154/// }
155/// assert!(bools.value(9));
156///
157/// // Freeze into a new bools vector.
158/// let bools = bools.freeze();
159/// ```
160///
161/// See also: [`BitBuffer`].
162#[derive(Debug, Clone)]
163pub struct BitBufferMut {
164    buffer: ByteBufferMut,
165    /// Represents the offset in bits at which the bit buffer starts.
166    ///
167    /// Unlike [`BitBuffer`], this is not normalised into the first byte: [`Self::from_buffer`]
168    /// stores what the caller passed, and `BitPackedArray` passes a whole-chunk offset.
169    offset: usize,
170    len: usize,
171}
172
173impl BitBufferMut {
174    /// Create new bit buffer from given byte buffer and logical bit length
175    ///
176    /// Panics if the buffer is not large enough to hold `len` bits after the offset.
177    #[inline]
178    pub fn from_buffer(buffer: ByteBufferMut, offset: usize, len: usize) -> Self {
179        assert!(
180            len.saturating_add(offset) <= buffer.len().saturating_mul(8),
181            "Buffer len {} is too short for offset {offset} and length {len}",
182            buffer.len()
183        );
184        Self {
185            buffer,
186            offset,
187            len,
188        }
189    }
190
191    /// Creates a `BitBufferMut` from a [`BitBuffer`] by copying all of the data over.
192    pub fn copy_from(bit_buffer: &BitBuffer) -> Self {
193        Self::copy_from_in(bit_buffer, bit_buffer.inner().allocator().clone())
194    }
195
196    /// Copies a bit buffer with the provided allocator.
197    pub fn copy_from_in(bit_buffer: &BitBuffer, allocator: BufferAllocatorRef) -> Self {
198        Self {
199            buffer: ByteBufferMut::copy_from_in(bit_buffer.inner(), allocator),
200            offset: bit_buffer.offset(),
201            len: bit_buffer.len(),
202        }
203    }
204
205    /// Create a new empty mutable bit buffer with requested capacity (in bits).
206    #[inline]
207    pub fn with_capacity(capacity: usize) -> Self {
208        Self::with_capacity_in(capacity, BufferAllocatorRef::statically_allocated())
209    }
210
211    /// Create a mutable bit buffer with the provided allocator.
212    #[inline]
213    pub fn with_capacity_in(capacity: usize, allocator: BufferAllocatorRef) -> Self {
214        Self {
215            buffer: BufferMut::with_capacity_in(capacity.div_ceil(8), allocator),
216            offset: 0,
217            len: 0,
218        }
219    }
220
221    /// Create a new mutable buffer with requested `len` and all bits set to `true`.
222    #[inline]
223    pub fn new_set(len: usize) -> Self {
224        Self {
225            buffer: buffer_mut![0xFF; len.div_ceil(8)],
226            offset: 0,
227            len,
228        }
229    }
230
231    /// Create a new mutable buffer with requested `len` and all bits set to `false`.
232    #[inline]
233    pub fn new_unset(len: usize) -> Self {
234        Self {
235            buffer: BufferMut::zeroed(len.div_ceil(8)),
236            offset: 0,
237            len,
238        }
239    }
240
241    /// Create a new empty `BitBufferMut`.
242    #[allow(clippy::inline_always)]
243    #[inline(always)]
244    pub fn empty() -> Self {
245        Self::with_capacity(0)
246    }
247
248    /// Create an empty mutable bit buffer with the provided allocator.
249    pub fn empty_in(allocator: BufferAllocatorRef) -> Self {
250        Self::with_capacity_in(0, allocator)
251    }
252
253    #[cfg(test)]
254    pub(crate) fn allocator(&self) -> &BufferAllocatorRef {
255        self.buffer.allocator()
256    }
257
258    /// Takes the buffer, leaving an empty buffer with the same allocator.
259    pub fn take(&mut self) -> Self {
260        Self {
261            buffer: self.buffer.take(),
262            offset: std::mem::take(&mut self.offset),
263            len: std::mem::take(&mut self.len),
264        }
265    }
266
267    /// Create a new mutable buffer with requested `len` and all bits set to `value`.
268    #[inline]
269    pub fn full(value: bool, len: usize) -> Self {
270        if value {
271            Self::new_set(len)
272        } else {
273            Self::new_unset(len)
274        }
275    }
276
277    /// Create a bit buffer of `len` with `indices` set as true.
278    pub fn from_indices(len: usize, indices: impl IntoIterator<Item = usize>) -> BitBufferMut {
279        let mut buffer = BufferMut::<u64>::zeroed(len.div_ceil(64));
280        for idx in indices {
281            assert!(idx < len, "index {idx} exceeds len {len}");
282            buffer.as_mut_slice()[idx / 64] |= 1 << (idx % 64);
283        }
284
285        let mut buffer = buffer.into_byte_buffer();
286        buffer.truncate(len.div_ceil(8));
287
288        Self {
289            buffer,
290            offset: 0,
291            len,
292        }
293    }
294
295    /// Mutable-buffer form of [`BitBuffer::collect_bool`].
296    ///
297    /// Calls `f` in the same order and uses the same packing path.
298    #[inline]
299    pub fn collect_bool<F: FnMut(usize) -> bool>(len: usize, f: F) -> Self {
300        Self::collect_bool_in(len, f, BufferAllocatorRef::statically_allocated())
301    }
302
303    /// Collects predicate results with the provided allocator.
304    #[inline]
305    pub fn collect_bool_in<F: FnMut(usize) -> bool>(
306        len: usize,
307        f: F,
308        allocator: BufferAllocatorRef,
309    ) -> Self {
310        Self::collect_words_in(len, allocator, |words| collect_bool_words(words, len, f))
311    }
312
313    /// Mutable-buffer form of [`BitBuffer::collect_bool_multiversioned`].
314    ///
315    /// Calls `f` in the same order and uses the same packing path.
316    #[inline]
317    pub fn collect_bool_multiversioned<F: FnMut(usize) -> bool>(len: usize, f: F) -> Self {
318        Self::collect_bool_multiversioned_in(len, f, BufferAllocatorRef::statically_allocated())
319    }
320
321    /// Collects multiversioned predicate results with the provided allocator.
322    #[inline]
323    pub fn collect_bool_multiversioned_in<F: FnMut(usize) -> bool>(
324        len: usize,
325        f: F,
326        allocator: BufferAllocatorRef,
327    ) -> Self {
328        Self::collect_words_in(len, allocator, |words| {
329            collect_bool_words_multiversioned(words, len, f)
330        })
331    }
332
333    #[inline]
334    fn collect_words_in(
335        len: usize,
336        allocator: BufferAllocatorRef,
337        fill: impl FnOnce(&mut [u64]),
338    ) -> Self {
339        let num_words = len.div_ceil(64);
340        // Allocate the words already initialised. Calling `set_len` on a `with_capacity`
341        // allocation and then passing `as_mut_slice()` to `fill` would materialise a `&mut [u64]`
342        // over uninitialised memory, which is Undefined Behaviour regardless of `u64` having no
343        // invalid bit patterns. `fill` overwrites every word, so the zeroing costs one memset.
344        let mut buffer = BufferMut::<u64>::zeroed_in(num_words, allocator);
345        fill(buffer.as_mut_slice());
346
347        let mut bytes = buffer.into_byte_buffer();
348        bytes.truncate(len.div_ceil(8));
349
350        Self {
351            buffer: bytes,
352            offset: 0,
353            len,
354        }
355    }
356
357    /// Return the underlying byte buffer.
358    #[inline]
359    pub fn inner(&self) -> &ByteBufferMut {
360        &self.buffer
361    }
362
363    /// Consumes the buffer and return the underlying byte buffer.
364    #[inline]
365    pub fn into_inner(self) -> ByteBufferMut {
366        self.buffer
367    }
368
369    /// Get the current populated length of the buffer.
370    #[allow(clippy::inline_always)]
371    #[inline(always)]
372    pub fn len(&self) -> usize {
373        self.len
374    }
375
376    /// True if the buffer has length 0.
377    #[allow(clippy::inline_always)]
378    #[inline(always)]
379    pub fn is_empty(&self) -> bool {
380        self.len == 0
381    }
382
383    /// Get the current bit offset of the buffer.
384    #[allow(clippy::inline_always)]
385    #[inline(always)]
386    pub fn offset(&self) -> usize {
387        self.offset
388    }
389
390    /// Get the value at the requested index.
391    #[allow(clippy::inline_always)]
392    #[inline(always)]
393    pub fn value(&self, index: usize) -> bool {
394        assert!(index < self.len);
395        // SAFETY: checked by assertion
396        unsafe { self.value_unchecked(index) }
397    }
398
399    /// Get the value at the requested index without bounds checking.
400    ///
401    /// # Safety
402    ///
403    /// The caller must ensure that `index` is less than the length of the buffer.
404    #[allow(clippy::inline_always)]
405    #[inline(always)]
406    pub unsafe fn value_unchecked(&self, index: usize) -> bool {
407        unsafe { get_bit_unchecked(self.buffer.as_ptr(), self.offset + index) }
408    }
409
410    /// Get the bit capacity of the buffer.
411    #[allow(clippy::inline_always)]
412    #[inline(always)]
413    pub fn capacity(&self) -> usize {
414        (self.buffer.capacity() * 8) - self.offset
415    }
416
417    /// Reserve additional bit capacity for the buffer.
418    #[inline]
419    pub fn reserve(&mut self, additional: usize) {
420        let required_bits = self.offset + self.len + additional;
421        let required_bytes = required_bits.div_ceil(8); // Rounds up.
422
423        let additional_bytes = required_bytes.saturating_sub(self.buffer.len());
424        self.buffer.reserve(additional_bytes);
425    }
426
427    /// Clears the bit buffer (but keeps any allocated memory).
428    #[inline]
429    pub fn clear(&mut self) {
430        // Also clear the byte buffer (not just `len`) so the "bits beyond len are zero"
431        // invariant holds; `append_false` and `append_buffer` rely on it.
432        self.buffer.clear();
433        self.len = 0;
434        self.offset = 0;
435    }
436
437    /// Set the bit at `index` to the given boolean value.
438    ///
439    /// This operation is checked so if `index` exceeds the buffer length, this will panic.
440    #[inline]
441    pub fn set_to(&mut self, index: usize, value: bool) {
442        if value {
443            self.set(index);
444        } else {
445            self.unset(index);
446        }
447    }
448
449    /// Set the bit at `index` to the given boolean value without checking bounds.
450    ///
451    /// # Safety
452    ///
453    /// The caller must ensure that `index` does not exceed the largest bit index in the backing buffer.
454    #[inline]
455    pub unsafe fn set_to_unchecked(&mut self, index: usize, value: bool) {
456        if value {
457            // SAFETY: checked by caller
458            unsafe { self.set_unchecked(index) }
459        } else {
460            // SAFETY: checked by caller
461            unsafe { self.unset_unchecked(index) }
462        }
463    }
464
465    /// Set a position to `true`.
466    ///
467    /// This operation is checked so if `index` exceeds the buffer length, this will panic.
468    #[inline]
469    pub fn set(&mut self, index: usize) {
470        assert!(index < self.len, "index {index} exceeds len {}", self.len);
471
472        // SAFETY: checked by assertion
473        unsafe { self.set_unchecked(index) };
474    }
475
476    /// Set a position to `false`.
477    ///
478    /// This operation is checked so if `index` exceeds the buffer length, this will panic.
479    #[inline]
480    pub fn unset(&mut self, index: usize) {
481        assert!(index < self.len, "index {index} exceeds len {}", self.len);
482
483        // SAFETY: checked by assertion
484        unsafe { self.unset_unchecked(index) };
485    }
486
487    /// Set the bit at `index` to `true` without checking bounds.
488    ///
489    /// Note: Do not call this in a tight loop. Prefer to use [`set_bit_unchecked`].
490    ///
491    /// # Safety
492    ///
493    /// The caller must ensure that `index` does not exceed the largest bit index in the backing buffer.
494    #[inline]
495    pub unsafe fn set_unchecked(&mut self, index: usize) {
496        // SAFETY: checked by caller
497        unsafe { set_bit_unchecked(self.buffer.as_mut_ptr(), self.offset + index) }
498    }
499
500    /// Unset the bit at `index` without checking bounds.
501    ///
502    /// Note: Do not call this in a tight loop. Prefer to use [`unset_bit_unchecked`].
503    ///
504    /// # Safety
505    ///
506    /// The caller must ensure that `index` does not exceed the largest bit index in the backing buffer.
507    #[inline]
508    pub unsafe fn unset_unchecked(&mut self, index: usize) {
509        // SAFETY: checked by caller
510        unsafe { unset_bit_unchecked(self.buffer.as_mut_ptr(), self.offset + index) }
511    }
512
513    /// Foces the length of the `BitBufferMut` to `new_len`.
514    ///
515    /// # Safety
516    ///
517    /// - `new_len` must be less than or equal to [`capacity()`](Self::capacity)
518    /// - The elements at `old_len..new_len` must be initialized
519    #[allow(clippy::inline_always)]
520    #[inline(always)]
521    pub unsafe fn set_len(&mut self, new_len: usize) {
522        debug_assert!(
523            new_len <= self.capacity(),
524            "`set_len` requires that new_len <= capacity()"
525        );
526
527        // Calculate the new byte length required to hold the bits
528        let bytes_len = (self.offset + new_len).div_ceil(8);
529        unsafe { self.buffer.set_len(bytes_len) };
530
531        self.len = new_len;
532    }
533
534    /// Truncate the buffer to the given length.
535    ///
536    /// If the given length is greater than the current length, this is a no-op.
537    #[inline]
538    pub fn truncate(&mut self, len: usize) {
539        if len > self.len {
540            return;
541        }
542
543        assert!(
544            self.offset <= usize::MAX - len,
545            "Truncate on BitBufferMut overflowed"
546        );
547        let end_bit = self.offset + len;
548        let new_len_bytes = end_bit.div_ceil(8);
549        self.buffer.truncate(new_len_bytes);
550        self.len = len;
551
552        // Clear stale bits in the final partial byte so the "bits beyond len are zero" invariant
553        // holds. `append_false` (and `append_buffer`) rely on it to avoid a read-modify-write.
554        if !end_bit.is_multiple_of(8) {
555            let keep = (1u8 << (end_bit % 8)) - 1;
556            self.buffer.as_mut_slice()[new_len_bytes - 1] &= keep;
557        }
558    }
559
560    /// Append a new boolean into the bit buffer, incrementing the length.
561    #[inline]
562    pub fn append(&mut self, value: bool) {
563        if value {
564            self.append_true()
565        } else {
566            self.append_false()
567        }
568    }
569
570    /// Append a new true value to the buffer.
571    #[inline]
572    pub fn append_true(&mut self) {
573        let bit_pos = self.offset + self.len;
574        let byte_pos = bit_pos / 8;
575        let bit_in_byte = bit_pos % 8;
576
577        // Ensure buffer has enough bytes
578        if byte_pos >= self.buffer.len() {
579            self.buffer.push(0u8);
580        }
581
582        // Set the bit
583        self.buffer.as_mut_slice()[byte_pos] |= 1 << bit_in_byte;
584        self.len += 1;
585    }
586
587    /// Append a new false value to the buffer.
588    #[inline]
589    pub fn append_false(&mut self) {
590        let bit_pos = self.offset + self.len;
591        let byte_pos = bit_pos / 8;
592
593        // Ensure buffer has enough bytes (pushed as 0x00, so bit is already unset).
594        if byte_pos >= self.buffer.len() {
595            self.buffer.push(0u8);
596        }
597
598        // The bit is guaranteed to be 0: new bytes are zero-initialized, and
599        // existing bytes have this bit unset (it's beyond the current length).
600        self.len += 1;
601    }
602
603    /// Append several boolean values into the bit buffer. After this operation,
604    /// the length will be incremented by `n`.
605    ///
606    /// Panics if the buffer does not have `n` slots left.
607    #[inline]
608    pub fn append_n(&mut self, value: bool, n: usize) {
609        if n == 0 {
610            return;
611        }
612
613        assert!(
614            self.offset
615                .checked_add(self.len)
616                .and_then(|v| v.checked_add(n))
617                .is_some(),
618            "Append on BitBufferMut overflowed"
619        );
620        let end_bit_pos = self.offset + self.len + n;
621        let required_bytes = end_bit_pos.div_ceil(8);
622
623        // Ensure buffer has enough bytes
624        if required_bytes > self.buffer.len() {
625            self.buffer.push_n(0x00, required_bytes - self.buffer.len());
626        }
627
628        let start = self.offset + self.len;
629        self.len += n;
630        // Appends write the trailing bytes, which rarely form whole words, so fill bytewise
631        // rather than splitting the range between the word and byte paths of `fill_bits`.
632        fill_byte_bits(self.buffer.as_mut_slice(), start, end_bit_pos, value);
633    }
634
635    /// Sets all bits in the range `[start, end)` to `value`.
636    ///
637    /// This operates on an arbitrary range within the existing length of the buffer.
638    /// Panics if `end > self.len` or `start > end`.
639    #[allow(clippy::inline_always)]
640    #[inline(always)]
641    pub fn fill_range(&mut self, start: usize, end: usize, value: bool) {
642        assert!(end <= self.len, "end {end} exceeds len {}", self.len);
643        assert!(start <= end, "start {start} exceeds end {end}");
644
645        // SAFETY: assertions above guarantee start <= end <= self.len,
646        // so offset + end fits within the buffer.
647        unsafe { self.fill_range_unchecked(start, end, value) }
648    }
649
650    /// Sets all bits in the range `[start, end)` to `value` without bounds checking.
651    ///
652    /// # Safety
653    ///
654    /// The caller must ensure that `start <= end <= self.len`.
655    #[allow(clippy::inline_always)]
656    #[inline(always)]
657    pub unsafe fn fill_range_unchecked(&mut self, start: usize, end: usize, value: bool) {
658        fill_bits(
659            self.buffer.as_mut_slice(),
660            self.offset + start,
661            self.offset + end,
662            value,
663        );
664    }
665
666    /// Append a [`BitBuffer`] to this [`BitBufferMut`]
667    ///
668    /// This efficiently copies all bits from the source buffer to the end of this buffer.
669    pub fn append_buffer(&mut self, buffer: &BitBuffer) {
670        let bit_len = buffer.len();
671        if bit_len == 0 {
672            return;
673        }
674
675        let start_bit_pos = self.offset + self.len;
676        let end_bit_pos = start_bit_pos + bit_len;
677        let required_bytes = end_bit_pos.div_ceil(8);
678
679        // Ensure buffer has enough bytes, zero-initialized for OR-based writes.
680        if required_bytes > self.buffer.len() {
681            self.buffer.push_n(0x00, required_bytes - self.buffer.len());
682        }
683
684        let dst_bit_offset = start_bit_pos % 8;
685        let src_bit_offset = buffer.offset();
686
687        if dst_bit_offset == 0 && src_bit_offset == 0 {
688            // Both byte-aligned: use memcpy for full bytes, then mask the tail.
689            let dst_byte = start_bit_pos / 8;
690            let src_bytes = buffer.inner().as_slice();
691            let full_bytes = bit_len / 8;
692            self.buffer.as_mut_slice()[dst_byte..dst_byte + full_bytes]
693                .copy_from_slice(&src_bytes[..full_bytes]);
694            let rem = bit_len % 8;
695            if rem != 0 {
696                let mask = (1u8 << rem) - 1;
697                self.buffer.as_mut_slice()[dst_byte + full_bytes] |= src_bytes[full_bytes] & mask;
698            }
699        } else {
700            // Use bitvec for unaligned bit copying.
701            let self_slice = self
702                .buffer
703                .as_mut_slice()
704                .view_bits_mut::<bitvec::prelude::Lsb0>();
705            let other_slice = buffer
706                .inner()
707                .as_slice()
708                .view_bits::<bitvec::prelude::Lsb0>();
709            let source_range = src_bit_offset..src_bit_offset + bit_len;
710            self_slice[start_bit_pos..end_bit_pos].copy_from_bitslice(&other_slice[source_range]);
711        }
712
713        self.len += bit_len;
714    }
715
716    /// Freeze the buffer in its current state into an immutable `BoolBuffer`.
717    #[inline]
718    pub fn freeze(self) -> BitBuffer {
719        BitBuffer::new_with_offset(self.buffer.freeze(), self.len, self.offset)
720    }
721
722    /// Get the underlying bytes as a slice
723    #[inline]
724    pub fn as_slice(&self) -> &[u8] {
725        self.buffer.as_slice()
726    }
727
728    /// Get the underlying bytes as a mutable slice
729    #[inline]
730    pub fn as_mut_slice(&mut self) -> &mut [u8] {
731        self.buffer.as_mut_slice()
732    }
733}
734
735impl Default for BitBufferMut {
736    fn default() -> Self {
737        Self::with_capacity(0)
738    }
739}
740
741// Mutate-in-place implementation of bitwise NOT.
742impl Not for BitBufferMut {
743    type Output = BitBufferMut;
744
745    #[inline]
746    fn not(mut self) -> Self::Output {
747        ops::bitwise_unary_op_mut(&mut self, |b| !b);
748        self
749    }
750}
751
752impl From<&[bool]> for BitBufferMut {
753    fn from(value: &[bool]) -> Self {
754        // SAFETY: the predicate is invoked with indices `0..value.len()` only.
755        // Skipping the bounds check lets the gather loop vectorize.
756        BitBufferMut::collect_bool_multiversioned(value.len(), |i| unsafe {
757            *value.get_unchecked(i)
758        })
759    }
760}
761
762// allow building a buffer from a set of truthy byte values.
763impl From<&[u8]> for BitBufferMut {
764    fn from(value: &[u8]) -> Self {
765        // SAFETY: the predicate is invoked with indices `0..value.len()` only.
766        // Skipping the bounds check lets the gather loop vectorize.
767        BitBufferMut::collect_bool_multiversioned(
768            value.len(),
769            |i| unsafe { *value.get_unchecked(i) } > 0,
770        )
771    }
772}
773
774impl From<Vec<bool>> for BitBufferMut {
775    fn from(value: Vec<bool>) -> Self {
776        value.as_slice().into()
777    }
778}
779
780impl FromIterator<bool> for BitBufferMut {
781    #[inline]
782    fn from_iter<T: IntoIterator<Item = bool>>(iter: T) -> Self {
783        let mut iter = iter.into_iter();
784
785        // Since we do not know the length of the iterator, we can only guess how much memory we
786        // need to reserve. Note that these hints may be inaccurate.
787        let (lower_bound, _) = iter.size_hint();
788
789        // We choose not to use the optional upper bound size hint to match the standard library.
790
791        // Initialize all bits to 0 with the given length. By doing this, we only need to set bits
792        // that are true (and this is faster from benchmarks).
793        let mut buf = BitBufferMut::new_unset(lower_bound);
794        assert_eq!(buf.offset, 0);
795
796        // Directly write within our known capacity.
797        let ptr = buf.buffer.as_mut_ptr();
798        for i in 0..lower_bound {
799            let Some(v) = iter.next() else {
800                // SAFETY: We are definitely under the capacity and all values are already
801                // initialized from `new_unset`.
802                unsafe { buf.set_len(i) };
803                return buf;
804            };
805
806            if v {
807                // SAFETY: We have ensured that we are within the capacity.
808                unsafe { set_bit_unchecked(ptr, i) }
809            }
810        }
811
812        // Append any remaining items one at a time, as we do not know how many more there are.
813        // (`append` is already a single branch + bit set, see `append_true`/`append_false`.)
814        for v in iter {
815            buf.append(v);
816        }
817
818        buf
819    }
820}
821
822#[cfg(test)]
823mod tests {
824    use allocator_api2::alloc::Global;
825    use rstest::rstest;
826
827    use crate::BufferAllocatorRef;
828    use crate::BufferMut;
829    use crate::bit::buf_mut::BitBufferMut;
830    use crate::bit::buf_mut::fill_bits;
831    use crate::bitbuffer;
832    use crate::bitbuffer_mut;
833    use crate::buffer_mut;
834
835    /// Checks every range of slices that end on and off a word boundary against a bit-by-bit
836    /// fill, starting from a pattern so that bits outside the range must be preserved.
837    #[rstest]
838    fn fill_bits_matches_bitwise_fill(
839        #[values(1, 7, 8, 9, 16, 17)] slice_len: usize,
840        #[values(false, true)] value: bool,
841    ) {
842        let pattern: Vec<u8> = (0u8..)
843            .take(slice_len)
844            .map(|i| i.wrapping_mul(0x5B) ^ 0xA6)
845            .collect();
846        for start in 0..=slice_len * 8 {
847            for end in start..=slice_len * 8 {
848                let mut actual = pattern.clone();
849                fill_bits(&mut actual, start, end, value);
850
851                let mut expected = pattern.clone();
852                for bit in start..end {
853                    if value {
854                        expected[bit / 8] |= 1 << (bit % 8);
855                    } else {
856                        expected[bit / 8] &= !(1 << (bit % 8));
857                    }
858                }
859                assert_eq!(actual, expected, "range {start}..{end}");
860            }
861        }
862    }
863
864    #[test]
865    fn test_bits_mut() {
866        let mut bools = bitbuffer_mut![false; 10];
867        bools.set_to(0, true);
868        bools.set_to(9, true);
869
870        let bools = bools.freeze();
871        assert!(bools.value(0));
872        for i in 1..=8 {
873            assert!(!bools.value(i));
874        }
875        assert!(bools.value(9));
876    }
877
878    #[test]
879    fn take_preserves_allocator() {
880        let allocator = BufferAllocatorRef::new(Global);
881        let mut buffer = BitBufferMut::with_capacity_in(4, allocator.clone());
882        buffer.append(true);
883        buffer.append(false);
884        buffer.append(true);
885
886        let taken = buffer.take();
887
888        assert_eq!(
889            (0..taken.len())
890                .map(|index| taken.value(index))
891                .collect::<Vec<_>>(),
892            [true, false, true]
893        );
894        assert!(taken.allocator().ptr_eq(&allocator));
895        assert!(buffer.is_empty());
896        assert!(buffer.allocator().ptr_eq(&allocator));
897    }
898
899    #[test]
900    fn test_append_n() {
901        let mut bools = BitBufferMut::with_capacity(10);
902        assert_eq!(bools.len(), 0);
903        assert!(bools.is_empty());
904
905        bools.append(true);
906        bools.append_n(false, 8);
907        bools.append_n(true, 1);
908
909        let bools = bools.freeze();
910
911        assert_eq!(bools.true_count(), 2);
912        assert!(bools.value(0));
913        assert!(bools.value(9));
914    }
915
916    #[test]
917    fn append_false_after_truncate_reads_back_false() {
918        // `truncate` leaves stale bits in the final partial byte; a subsequent `append_false`
919        // must still read back as false. Regression test for the `append_false` fast path.
920        let mut bools = BitBufferMut::new_set(16);
921        bools.truncate(12);
922        bools.append_false();
923        bools.append_true();
924
925        let bools = bools.freeze();
926        assert_eq!(bools.len(), 14);
927        assert!(
928            !bools.value(12),
929            "appended false must read back false after truncate"
930        );
931        assert!(bools.value(13));
932    }
933
934    #[test]
935    fn test_reserve_ensures_len_plus_additional() {
936        // This test documents the fix for the bug where reserve was incorrectly
937        // calculating additional bytes from capacity instead of len.
938
939        let mut bits = BitBufferMut::with_capacity(10);
940        assert_eq!(bits.len(), 0);
941
942        bits.reserve(100);
943
944        // Should have capacity for at least len + 100 = 0 + 100 = 100 bits.
945        assert!(bits.capacity() >= 100);
946
947        bits.append_n(true, 50);
948        assert_eq!(bits.len(), 50);
949
950        bits.reserve(100);
951
952        // Should have capacity for at least len + 100 = 50 + 100 = 150 bits.
953        assert!(bits.capacity() >= 150);
954    }
955
956    #[test]
957    fn test_with_offset_zero() {
958        // Test basic operations when offset is 0
959        let buf = BufferMut::zeroed(2);
960        let mut bit_buf = BitBufferMut::from_buffer(buf, 0, 16);
961
962        // Set some bits
963        bit_buf.set(0);
964        bit_buf.set(7);
965        bit_buf.set(8);
966        bit_buf.set(15);
967
968        // Verify values
969        assert!(bit_buf.value(0));
970        assert!(bit_buf.value(7));
971        assert!(bit_buf.value(8));
972        assert!(bit_buf.value(15));
973        assert!(!bit_buf.value(1));
974        assert!(!bit_buf.value(9));
975
976        // Verify underlying bytes
977        assert_eq!(bit_buf.as_slice()[0], 0b10000001);
978        assert_eq!(bit_buf.as_slice()[1], 0b10000001);
979    }
980
981    #[test]
982    fn test_with_offset_within_byte() {
983        // Test operations with offset=3 (within first byte)
984        let buf = buffer_mut![0b11111111, 0b00000000, 0b00000000];
985        let mut bit_buf = BitBufferMut::from_buffer(buf, 3, 10);
986
987        // Initially, bits 3-7 from first byte are set (5 bits)
988        // and bits 0-4 from second byte are unset (5 bits more)
989        assert!(bit_buf.value(0)); // bit 3 of byte 0
990        assert!(bit_buf.value(4)); // bit 7 of byte 0
991        assert!(!bit_buf.value(5)); // bit 0 of byte 1
992
993        // Set a bit in the second byte's range
994        bit_buf.set(7);
995        assert!(bit_buf.value(7));
996
997        // Unset a bit in the first byte's range
998        bit_buf.unset(0);
999        assert!(!bit_buf.value(0));
1000    }
1001
1002    #[test]
1003    fn test_with_offset_byte_boundary() {
1004        // Test operations with offset=8 (exactly one byte)
1005        let buf = buffer_mut![0xFF, 0x00, 0xFF];
1006        let mut bit_buf = BitBufferMut::from_buffer(buf, 8, 16);
1007
1008        // Buffer starts at byte 1, so all bits should be unset initially
1009        for i in 0..8 {
1010            assert!(!bit_buf.value(i));
1011        }
1012        // Next byte has all bits set
1013        for i in 8..16 {
1014            assert!(bit_buf.value(i));
1015        }
1016
1017        // Set some bits
1018        bit_buf.set(0);
1019        bit_buf.set(3);
1020        assert!(bit_buf.value(0));
1021        assert!(bit_buf.value(3));
1022    }
1023
1024    #[test]
1025    fn test_with_large_offset() {
1026        // Test with offset=13 (one byte + 5 bits)
1027        let buf = buffer_mut![0xFF, 0xFF, 0xFF, 0xFF];
1028        let mut bit_buf = BitBufferMut::from_buffer(buf, 13, 10);
1029
1030        // All bits should initially be set
1031        for i in 0..10 {
1032            assert!(bit_buf.value(i));
1033        }
1034
1035        // Unset some bits
1036        bit_buf.unset(0);
1037        bit_buf.unset(5);
1038        bit_buf.unset(9);
1039
1040        assert!(!bit_buf.value(0));
1041        assert!(bit_buf.value(1));
1042        assert!(!bit_buf.value(5));
1043        assert!(!bit_buf.value(9));
1044    }
1045
1046    #[test]
1047    fn test_append_with_offset() {
1048        // Create buffer with offset
1049        let buf = buffer_mut![0b11100000]; // First 3 bits unset, last 5 set
1050        let mut bit_buf = BitBufferMut::from_buffer(buf, 3, 0); // Start at bit 3, len=0
1051
1052        // Append some bits
1053        bit_buf.append(false); // Should use bit 3
1054        bit_buf.append(true); // Should use bit 4
1055        bit_buf.append(true); // Should use bit 5
1056
1057        assert_eq!(bit_buf.len(), 3);
1058        assert!(!bit_buf.value(0));
1059        assert!(bit_buf.value(1));
1060        assert!(bit_buf.value(2));
1061    }
1062
1063    #[test]
1064    fn test_append_n_with_offset_crossing_boundary() {
1065        // Create buffer with offset that will cross byte boundary when appending
1066        let buf = BufferMut::zeroed(4);
1067        let mut bit_buf = BitBufferMut::from_buffer(buf, 5, 0);
1068
1069        // Append enough bits to cross into next byte
1070        bit_buf.append_n(true, 10); // 5 bits left in first byte, then 5 in second
1071
1072        assert_eq!(bit_buf.len(), 10);
1073        for i in 0..10 {
1074            assert!(bit_buf.value(i));
1075        }
1076
1077        // Verify the underlying bytes
1078        // Bits 5-7 of byte 0 should be set (3 bits)
1079        // Bits 0-6 of byte 1 should be set (7 bits)
1080        assert_eq!(bit_buf.as_slice()[0], 0b11100000);
1081        assert_eq!(bit_buf.as_slice()[1], 0b01111111);
1082    }
1083
1084    #[test]
1085    fn test_truncate_with_offset() {
1086        let buf = buffer_mut![0xFF, 0xFF];
1087        let mut bit_buf = BitBufferMut::from_buffer(buf, 4, 12);
1088
1089        assert_eq!(bit_buf.len(), 12);
1090
1091        // Truncate to 8 bits
1092        bit_buf.truncate(8);
1093        assert_eq!(bit_buf.len(), 8);
1094
1095        // Truncate to 3 bits
1096        bit_buf.truncate(3);
1097        assert_eq!(bit_buf.len(), 3);
1098
1099        // Truncating to larger length should be no-op
1100        bit_buf.truncate(10);
1101        assert_eq!(bit_buf.len(), 3);
1102    }
1103
1104    #[test]
1105    fn test_capacity_with_offset() {
1106        // Use exact buffer size to test capacity calculation
1107        let buf = buffer_mut![0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // Exactly 10 bytes = 80 bits
1108        let bit_buf = BitBufferMut::from_buffer(buf, 5, 0);
1109
1110        // Capacity should be at least buffer length minus offset
1111        // (may be more due to allocator rounding)
1112        assert!(bit_buf.capacity() >= 75);
1113        // And should account for offset
1114        assert_eq!(bit_buf.capacity() % 8, (80 - 5) % 8);
1115    }
1116
1117    #[test]
1118    fn test_reserve_with_offset() {
1119        // Use exact buffer to test reserve
1120        let buf = buffer_mut![0, 0]; // Exactly 2 bytes = 16 bits
1121        let mut bit_buf = BitBufferMut::from_buffer(buf, 3, 0);
1122
1123        // Current capacity should be at least 13 bits (16 - 3)
1124        let initial_capacity = bit_buf.capacity();
1125        assert!(initial_capacity >= 13);
1126
1127        // Reserve 20 more bits (need total of offset 3 + len 0 + additional 20 = 23 bits)
1128        bit_buf.reserve(20);
1129
1130        // Should now have at least 20 bits of capacity
1131        assert!(bit_buf.capacity() >= 20);
1132    }
1133
1134    #[test]
1135    fn test_freeze_with_offset() {
1136        let buf = buffer_mut![0b11110000, 0b00001111];
1137        let mut bit_buf = BitBufferMut::from_buffer(buf, 4, 8);
1138
1139        // Set some bits
1140        bit_buf.set(0);
1141        bit_buf.set(7);
1142
1143        // Freeze and verify offset is preserved
1144        let frozen = bit_buf.freeze();
1145        assert_eq!(frozen.offset(), 4);
1146        assert_eq!(frozen.len(), 8);
1147
1148        // Verify values through frozen buffer
1149        assert!(frozen.value(0));
1150        assert!(frozen.value(7));
1151    }
1152
1153    #[cfg_attr(miri, ignore)] // bitvec crate uses a ptr cast that Miri doesn't support
1154    #[test]
1155    fn append_after_clear_reads_back_false() {
1156        // `clear` must not leave stale set bits behind: `append_false` and `append_buffer`
1157        // rely on bits beyond `len` being zero.
1158        let mut bools = BitBufferMut::new_set(16);
1159        bools.clear();
1160        bools.append_false();
1161        bools.append_buffer(&crate::BitBuffer::new_unset(8));
1162
1163        let bools = bools.freeze();
1164        assert_eq!(bools.len(), 9);
1165        assert_eq!(bools.true_count(), 0);
1166    }
1167
1168    #[cfg_attr(miri, ignore)] // bitvec crate uses a ptr cast that Miri doesn't support
1169    #[test]
1170    fn test_append_buffer_after_truncate() {
1171        // Truncating leaves stale set bits in the last partial byte; an append after that
1172        // must overwrite them rather than OR into them.
1173        let mut buf = BitBufferMut::new_set(16);
1174        buf.truncate(3);
1175        buf.append_buffer(&crate::BitBuffer::new_unset(8));
1176
1177        let frozen = buf.freeze();
1178        assert_eq!(frozen.len(), 11);
1179        for i in 0..3 {
1180            assert!(frozen.value(i), "bit {i} should be set");
1181        }
1182        for i in 3..11 {
1183            assert!(!frozen.value(i), "bit {i} should be unset");
1184        }
1185    }
1186
1187    #[rstest]
1188    #[case::both_aligned(0, 0)]
1189    #[case::dst_unaligned(3, 0)]
1190    #[case::src_unaligned(0, 5)]
1191    #[case::mismatched(3, 5)]
1192    #[case::equal_nonzero(5, 5)]
1193    #[cfg_attr(miri, ignore)] // bitvec crate uses a ptr cast that Miri doesn't support
1194    fn test_append_buffer_long(#[case] dst_prefix: usize, #[case] src_start: usize) {
1195        // Exercise every alignment combination across many words.
1196        let source = crate::BitBuffer::from_iter((0..301).map(|i| i % 3 == 0));
1197        let source = source.slice(src_start..301);
1198
1199        let mut dest = BitBufferMut::with_capacity(512);
1200        dest.append_n(true, dst_prefix);
1201        dest.append_buffer(&source);
1202
1203        assert_eq!(dest.len(), dst_prefix + source.len());
1204        for i in 0..dst_prefix {
1205            assert!(dest.value(i), "prefix bit {i}");
1206        }
1207        for i in 0..source.len() {
1208            assert_eq!(dest.value(dst_prefix + i), source.value(i), "bit {i}");
1209        }
1210    }
1211
1212    #[cfg_attr(miri, ignore)] // bitvec crate uses a ptr cast that Miri doesn't support
1213    #[test]
1214    fn test_append_buffer_with_offsets() {
1215        // Create source buffer with offset
1216        let source = bitbuffer![false, false, true, true, false, true];
1217
1218        // Create destination buffer with offset
1219        let buf = BufferMut::zeroed(4);
1220        let mut dest = BitBufferMut::from_buffer(buf, 3, 0);
1221
1222        // Append 2 initial bits
1223        dest.append(true);
1224        dest.append(false);
1225
1226        // Append the source buffer
1227        dest.append_buffer(&source);
1228
1229        assert_eq!(dest.len(), 8);
1230        assert!(dest.value(0)); // Our first append
1231        assert!(!dest.value(1)); // Our second append
1232        assert!(!dest.value(2)); // From source[0]
1233        assert!(!dest.value(3)); // From source[1]
1234        assert!(dest.value(4)); // From source[2]
1235        assert!(dest.value(5)); // From source[3]
1236        assert!(!dest.value(6)); // From source[4]
1237        assert!(dest.value(7)); // From source[5]
1238    }
1239
1240    #[test]
1241    fn test_set_unset_unchecked_with_offset() {
1242        let buf = BufferMut::zeroed(3);
1243        let mut bit_buf = BitBufferMut::from_buffer(buf, 7, 10);
1244
1245        unsafe {
1246            bit_buf.set_unchecked(0);
1247            bit_buf.set_unchecked(5);
1248            bit_buf.set_unchecked(9);
1249        }
1250
1251        assert!(bit_buf.value(0));
1252        assert!(bit_buf.value(5));
1253        assert!(bit_buf.value(9));
1254
1255        unsafe {
1256            bit_buf.unset_unchecked(5);
1257        }
1258
1259        assert!(!bit_buf.value(5));
1260    }
1261
1262    #[test]
1263    fn test_value_unchecked_with_offset() {
1264        let buf = buffer_mut![0b11110000, 0b00001111];
1265        let bit_buf = BitBufferMut::from_buffer(buf, 4, 8);
1266
1267        unsafe {
1268            // First 4 bits of logical buffer come from bits 4-7 of first byte (all 1s)
1269            assert!(bit_buf.value_unchecked(0));
1270            assert!(bit_buf.value_unchecked(3));
1271
1272            // Next 4 bits come from bits 0-3 of second byte (all 1s)
1273            assert!(bit_buf.value_unchecked(4));
1274            assert!(bit_buf.value_unchecked(7));
1275        }
1276    }
1277
1278    #[test]
1279    fn test_append_alternating_with_offset() {
1280        let buf = BufferMut::zeroed(4);
1281        let mut bit_buf = BitBufferMut::from_buffer(buf, 2, 0);
1282
1283        // Append alternating pattern across byte boundaries
1284        for i in 0..20 {
1285            bit_buf.append(i % 2 == 0);
1286        }
1287
1288        assert_eq!(bit_buf.len(), 20);
1289        for i in 0..20 {
1290            assert_eq!(bit_buf.value(i), i % 2 == 0);
1291        }
1292    }
1293
1294    #[test]
1295    fn test_new_set_new_unset() {
1296        let set_buf = bitbuffer_mut![true; 10];
1297        let unset_buf = bitbuffer_mut![false; 10];
1298
1299        for i in 0..10 {
1300            assert!(set_buf.value(i));
1301            assert!(!unset_buf.value(i));
1302        }
1303
1304        assert_eq!(set_buf.len(), 10);
1305        assert_eq!(unset_buf.len(), 10);
1306    }
1307
1308    #[test]
1309    fn test_append_n_false_with_offset() {
1310        let buf = BufferMut::zeroed(4);
1311        let mut bit_buf = BitBufferMut::from_buffer(buf, 5, 0);
1312
1313        bit_buf.append_n(false, 15);
1314
1315        assert_eq!(bit_buf.len(), 15);
1316        for i in 0..15 {
1317            assert!(!bit_buf.value(i));
1318        }
1319    }
1320
1321    #[test]
1322    fn test_append_n_true_with_offset() {
1323        let buf = BufferMut::zeroed(4);
1324        let mut bit_buf = BitBufferMut::from_buffer(buf, 5, 0);
1325
1326        bit_buf.append_n(true, 15);
1327
1328        assert_eq!(bit_buf.len(), 15);
1329        for i in 0..15 {
1330            assert!(bit_buf.value(i));
1331        }
1332    }
1333
1334    #[test]
1335    fn test_mixed_operations_with_offset() {
1336        // Complex test combining multiple operations with offset
1337        let buf = BufferMut::zeroed(5);
1338        let mut bit_buf = BitBufferMut::from_buffer(buf, 3, 0);
1339
1340        // Append some bits
1341        bit_buf.append_n(true, 5);
1342        bit_buf.append_n(false, 3);
1343        bit_buf.append(true);
1344
1345        assert_eq!(bit_buf.len(), 9);
1346
1347        // Set and unset
1348        bit_buf.set(6); // Was false, now true
1349        bit_buf.unset(2); // Was true, now false
1350
1351        // Verify
1352        assert!(bit_buf.value(0));
1353        assert!(bit_buf.value(1));
1354        assert!(!bit_buf.value(2)); // Unset
1355        assert!(bit_buf.value(3));
1356        assert!(bit_buf.value(4));
1357        assert!(!bit_buf.value(5));
1358        assert!(bit_buf.value(6)); // Set
1359        assert!(!bit_buf.value(7));
1360        assert!(bit_buf.value(8));
1361
1362        // Truncate
1363        bit_buf.truncate(6);
1364        assert_eq!(bit_buf.len(), 6);
1365
1366        // Freeze and verify offset preserved
1367        let frozen = bit_buf.freeze();
1368        assert_eq!(frozen.offset(), 3);
1369        assert_eq!(frozen.len(), 6);
1370    }
1371
1372    #[test]
1373    fn test_from_iterator_with_incorrect_size_hint() {
1374        // This test catches a bug where FromIterator assumed the upper bound
1375        // from size_hint was accurate. The iterator contract allows the actual
1376        // count to exceed the upper bound, which could cause UB if we used
1377        // append_unchecked beyond the allocated capacity.
1378
1379        // Custom iterator that lies about its size hint.
1380        struct LyingIterator {
1381            values: Vec<bool>,
1382            index: usize,
1383        }
1384
1385        impl Iterator for LyingIterator {
1386            type Item = bool;
1387
1388            fn next(&mut self) -> Option<Self::Item> {
1389                (self.index < self.values.len()).then(|| {
1390                    let val = self.values[self.index];
1391                    self.index += 1;
1392                    val
1393                })
1394            }
1395
1396            fn size_hint(&self) -> (usize, Option<usize>) {
1397                // Deliberately return an incorrect upper bound that's smaller
1398                // than the actual number of elements we'll yield.
1399                let remaining = self.values.len() - self.index;
1400                let lower = remaining.min(5); // Correct lower bound (but capped).
1401                let upper = Some(5); // Incorrect upper bound - we actually have more!
1402                (lower, upper)
1403            }
1404        }
1405
1406        // Create an iterator that claims to have at most 5 elements but actually has 10.
1407        let lying_iter = LyingIterator {
1408            values: vec![
1409                true, false, true, false, true, false, true, false, true, false,
1410            ],
1411            index: 0,
1412        };
1413
1414        // Collect the iterator. This would cause UB in the old implementation
1415        // if it trusted the upper bound and used append_unchecked beyond capacity.
1416        let bit_buf: BitBufferMut = lying_iter.collect();
1417
1418        // Verify all 10 elements were collected correctly.
1419        assert_eq!(bit_buf.len(), 10);
1420        for i in 0..10 {
1421            assert_eq!(bit_buf.value(i), i % 2 == 0);
1422        }
1423    }
1424
1425    /// The bound has to cover the offset as well as the length, matching
1426    /// `BitBuffer::new_with_offset`. One byte backs eight bits, so an offset of one leaves room
1427    /// for seven.
1428    #[rstest]
1429    #[case(0, 8)]
1430    #[case(1, 7)]
1431    #[case(7, 1)]
1432    #[case(8, 0)]
1433    fn from_buffer_accepts_offset_plus_len_within_the_buffer(
1434        #[case] offset: usize,
1435        #[case] len: usize,
1436    ) {
1437        let bits = BitBufferMut::from_buffer(buffer_mut![0u8; 1], offset, len);
1438        assert_eq!(bits.len(), len);
1439    }
1440
1441    #[rstest]
1442    #[case::offset_pushes_past_the_end(1, 8)]
1443    #[case::offset_alone_past_the_end(9, 0)]
1444    #[case::len_alone_past_the_end(0, 9)]
1445    #[should_panic(expected = "is too short for offset")]
1446    fn from_buffer_rejects_offset_plus_len_past_the_buffer(
1447        #[case] offset: usize,
1448        #[case] len: usize,
1449    ) {
1450        BitBufferMut::from_buffer(buffer_mut![0u8; 1], offset, len);
1451    }
1452
1453    /// `BitPackedArray` passes a whole-chunk offset, so an offset far above eight is legitimate
1454    /// as long as the buffer backs it.
1455    #[test]
1456    fn from_buffer_allows_offsets_beyond_one_byte() {
1457        let bits = BitBufferMut::from_buffer(buffer_mut![0u8; 128], 1000, 24);
1458        assert_eq!(bits.len(), 24);
1459    }
1460
1461    /// Saturating arithmetic keeps a huge offset from wrapping into an accepted bound.
1462    #[test]
1463    #[should_panic(expected = "is too short for offset")]
1464    fn from_buffer_does_not_wrap_on_a_huge_offset() {
1465        BitBufferMut::from_buffer(buffer_mut![0u8; 1], usize::MAX, 8);
1466    }
1467}