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