Skip to main content

vortex_buffer/bit/
view.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::ops::Bound;
5use std::ops::RangeBounds;
6
7use crate::BitBuffer;
8use crate::BitBufferMeta;
9use crate::BitBufferMut;
10use crate::ByteBuffer;
11use crate::bit::BitChunks;
12use crate::bit::BitIndexIterator;
13use crate::bit::BitIterator;
14use crate::bit::BitSliceIterator;
15use crate::bit::UnalignedBitChunk;
16use crate::bit::buf_mut::fill_bits;
17use crate::bit::count_ones::count_ones;
18use crate::bit::get_bit_unchecked;
19use crate::bit::select::bit_select;
20use crate::bit::set_bit_unchecked;
21use crate::bit::unset_bit_unchecked;
22
23/// Resolve `start..end` bounds against a logical length, panicking on invalid ranges.
24#[inline]
25fn resolve_range(range: impl RangeBounds<usize>, len: usize) -> (usize, usize) {
26    let start = match range.start_bound() {
27        Bound::Included(&s) => s,
28        Bound::Excluded(&s) => s + 1,
29        Bound::Unbounded => 0,
30    };
31    let end = match range.end_bound() {
32        Bound::Included(&e) => e + 1,
33        Bound::Excluded(&e) => e,
34        Bound::Unbounded => len,
35    };
36
37    assert!(start <= end);
38    assert!(start <= len);
39    assert!(end <= len);
40    (start, end)
41}
42
43/// Normalize a byte slice and bit offset so that the returned offset is `< 8`.
44#[inline]
45fn normalize(buffer: &[u8], offset: usize) -> (&[u8], usize) {
46    let byte_offset = offset / 8;
47    (&buffer[byte_offset..], offset % 8)
48}
49
50/// An immutable, borrowed view over a packed bitset.
51///
52/// This is the borrowing analogue of [`BitBuffer`]: it stores a byte slice together with a bit
53/// `offset` (always `< 8`) and a logical bit `len`, without owning or reference-counting the
54/// backing allocation. Use it to read a bitset without cloning the underlying [`ByteBuffer`].
55#[derive(Debug, Clone, Copy)]
56pub struct BitBufferView<'a> {
57    buffer: &'a [u8],
58    offset: usize,
59    len: usize,
60}
61
62impl<'a> BitBufferView<'a> {
63    /// Create a new view over `buffer` with `len` bits, starting at bit zero.
64    ///
65    /// Panics if the buffer is not large enough to hold `len` bits.
66    #[inline]
67    pub fn new(buffer: &'a [u8], len: usize) -> Self {
68        Self::new_with_offset(buffer, len, 0)
69    }
70
71    /// Create a new view over `buffer` with `len` bits, starting at the given bit `offset`.
72    ///
73    /// Panics if the buffer is not large enough to hold `len` bits after the offset.
74    #[inline]
75    pub fn new_with_offset(buffer: &'a [u8], len: usize, offset: usize) -> Self {
76        assert!(
77            len.saturating_add(offset) <= buffer.len().saturating_mul(8),
78            "provided slice (len={}) not large enough to back BitBufferView with offset {offset} len {len}",
79            buffer.len()
80        );
81
82        let (buffer, offset) = normalize(buffer, offset);
83        Self {
84            buffer,
85            offset,
86            len,
87        }
88    }
89
90    /// Create a new view over `buffer` described by `meta`.
91    #[inline]
92    pub fn from_meta(buffer: &'a [u8], meta: BitBufferMeta) -> Self {
93        Self::new_with_offset(buffer, meta.len(), meta.offset())
94    }
95
96    /// Returns the [`BitBufferMeta`] (offset and length) describing this view.
97    #[inline]
98    pub fn meta(&self) -> BitBufferMeta {
99        BitBufferMeta::new(self.offset, self.len)
100    }
101
102    /// Get the logical length of this view in bits.
103    #[inline]
104    pub fn len(&self) -> usize {
105        self.len
106    }
107
108    /// Returns `true` if the view is empty.
109    #[inline]
110    pub fn is_empty(&self) -> bool {
111        self.len == 0
112    }
113
114    /// Offset of the start of the view in bits. Always `< 8`.
115    #[allow(clippy::inline_always)]
116    #[inline(always)]
117    pub fn offset(&self) -> usize {
118        self.offset
119    }
120
121    /// Get a reference to the underlying byte slice.
122    #[allow(clippy::inline_always)]
123    #[inline(always)]
124    pub fn inner(&self) -> &'a [u8] {
125        self.buffer
126    }
127
128    /// Retrieve the value at the given index.
129    ///
130    /// Panics if the index is out of bounds.
131    #[inline]
132    pub fn value(&self, index: usize) -> bool {
133        assert!(index < self.len);
134        // SAFETY: checked by assertion
135        unsafe { self.value_unchecked(index) }
136    }
137
138    /// Retrieve the value at the given index without bounds checking.
139    ///
140    /// # Safety
141    ///
142    /// Caller must ensure that `index` is within the range of the view.
143    #[inline]
144    pub unsafe fn value_unchecked(&self, index: usize) -> bool {
145        unsafe { get_bit_unchecked(self.buffer.as_ptr(), index + self.offset) }
146    }
147
148    /// Create a new view over the range `[start, end)` of this view.
149    ///
150    /// Panics if the slice would extend beyond the end of the view.
151    #[inline]
152    pub fn slice(&self, range: impl RangeBounds<usize>) -> BitBufferView<'a> {
153        let (start, end) = resolve_range(range, self.len);
154        BitBufferView::new_with_offset(self.buffer, end - start, self.offset + start)
155    }
156
157    /// Access chunks of the buffer aligned to an 8 byte boundary as
158    /// `[prefix, <full chunks>, suffix]`.
159    #[inline]
160    pub fn unaligned_chunks(&self) -> UnalignedBitChunk<'a> {
161        UnalignedBitChunk::new(self.buffer, self.offset, self.len)
162    }
163
164    /// Access chunks of the underlying buffer as 8 byte chunks with a final trailer.
165    #[inline]
166    pub fn chunks(&self) -> BitChunks<'a> {
167        BitChunks::new(self.buffer, self.offset, self.len)
168    }
169
170    /// Get the number of set bits in the view.
171    #[inline]
172    pub fn true_count(&self) -> usize {
173        count_ones(self.buffer, self.offset, self.len)
174    }
175
176    /// Get the number of unset bits in the view.
177    #[inline]
178    pub fn false_count(&self) -> usize {
179        self.len - self.true_count()
180    }
181
182    /// Returns the position of the `nth` set bit (0-indexed), or `None` if out of range.
183    #[inline]
184    pub fn select(&self, nth: usize) -> Option<usize> {
185        bit_select(self.buffer, self.offset, self.len, nth)
186    }
187
188    /// Iterator over bits in the view.
189    #[inline]
190    pub fn iter(&self) -> BitIterator<'a> {
191        BitIterator::new(self.buffer, self.offset, self.len)
192    }
193
194    /// Iterator over set indices of the underlying buffer.
195    #[inline]
196    pub fn set_indices(&self) -> BitIndexIterator<'a> {
197        BitIndexIterator::new(self.buffer, self.offset, self.len)
198    }
199
200    /// Iterator over set slices of the underlying buffer.
201    #[inline]
202    pub fn set_slices(&self) -> BitSliceIterator<'a> {
203        BitSliceIterator::new(self.buffer, self.offset, self.len)
204    }
205
206    /// Copy this view into an owned [`BitBuffer`].
207    pub fn to_bit_buffer(&self) -> BitBuffer {
208        let bytes = (self.offset + self.len).div_ceil(8);
209        BitBuffer::new_with_offset(
210            ByteBuffer::copy_from(&self.buffer[..bytes]),
211            self.len,
212            self.offset,
213        )
214    }
215}
216
217impl<'a> IntoIterator for BitBufferView<'a> {
218    type Item = bool;
219    type IntoIter = BitIterator<'a>;
220
221    fn into_iter(self) -> Self::IntoIter {
222        self.iter()
223    }
224}
225
226impl PartialEq for BitBufferView<'_> {
227    fn eq(&self, other: &Self) -> bool {
228        if self.len != other.len {
229            return false;
230        }
231
232        self.chunks()
233            .iter_padded()
234            .zip(other.chunks().iter_padded())
235            .all(|(a, b)| a == b)
236    }
237}
238
239impl Eq for BitBufferView<'_> {}
240
241/// A mutable, borrowed view over a packed bitset.
242///
243/// This is the borrowing analogue of [`BitBufferMut`]: it stores a mutable byte slice together
244/// with a bit `offset` (always `< 8`) and a logical bit `len`. Unlike [`BitBufferMut`] it cannot
245/// grow or reallocate, so it only supports in-place reads and writes (such as
246/// [`set`](Self::set), [`unset`](Self::unset), and [`fill_range`](Self::fill_range)).
247#[derive(Debug)]
248pub struct BitBufferMutView<'a> {
249    buffer: &'a mut [u8],
250    offset: usize,
251    len: usize,
252}
253
254impl<'a> BitBufferMutView<'a> {
255    /// Create a new mutable view over `buffer` with `len` bits, starting at bit zero.
256    ///
257    /// Panics if the buffer is not large enough to hold `len` bits.
258    #[inline]
259    pub fn new(buffer: &'a mut [u8], len: usize) -> Self {
260        Self::new_with_offset(buffer, len, 0)
261    }
262
263    /// Create a new mutable view over `buffer` with `len` bits, starting at bit `offset`.
264    ///
265    /// Panics if the buffer is not large enough to hold `len` bits after the offset.
266    #[inline]
267    pub fn new_with_offset(buffer: &'a mut [u8], len: usize, offset: usize) -> Self {
268        assert!(
269            len.saturating_add(offset) <= buffer.len().saturating_mul(8),
270            "provided slice (len={}) not large enough to back BitBufferMutView with offset {offset} len {len}",
271            buffer.len()
272        );
273
274        let byte_offset = offset / 8;
275        let offset = offset % 8;
276        Self {
277            buffer: &mut buffer[byte_offset..],
278            offset,
279            len,
280        }
281    }
282
283    /// Borrow this mutable view as an immutable [`BitBufferView`].
284    #[inline]
285    pub fn as_view(&self) -> BitBufferView<'_> {
286        BitBufferView {
287            buffer: self.buffer,
288            offset: self.offset,
289            len: self.len,
290        }
291    }
292
293    /// Get the logical length of this view in bits.
294    #[inline]
295    pub fn len(&self) -> usize {
296        self.len
297    }
298
299    /// Returns `true` if the view is empty.
300    #[inline]
301    pub fn is_empty(&self) -> bool {
302        self.len == 0
303    }
304
305    /// Offset of the start of the view in bits. Always `< 8`.
306    #[allow(clippy::inline_always)]
307    #[inline(always)]
308    pub fn offset(&self) -> usize {
309        self.offset
310    }
311
312    /// Get the underlying bytes as a slice.
313    #[inline]
314    pub fn as_slice(&self) -> &[u8] {
315        self.buffer
316    }
317
318    /// Get the underlying bytes as a mutable slice.
319    #[inline]
320    pub fn as_mut_slice(&mut self) -> &mut [u8] {
321        self.buffer
322    }
323
324    /// Retrieve the value at the given index.
325    ///
326    /// Panics if the index is out of bounds.
327    #[inline]
328    pub fn value(&self, index: usize) -> bool {
329        assert!(index < self.len);
330        // SAFETY: checked by assertion
331        unsafe { self.value_unchecked(index) }
332    }
333
334    /// Retrieve the value at the given index without bounds checking.
335    ///
336    /// # Safety
337    ///
338    /// Caller must ensure that `index` is within the range of the view.
339    #[inline]
340    pub unsafe fn value_unchecked(&self, index: usize) -> bool {
341        unsafe { get_bit_unchecked(self.buffer.as_ptr(), index + self.offset) }
342    }
343
344    /// Get the number of set bits in the view.
345    #[inline]
346    pub fn true_count(&self) -> usize {
347        self.as_view().true_count()
348    }
349
350    /// Get the number of unset bits in the view.
351    #[inline]
352    pub fn false_count(&self) -> usize {
353        self.as_view().false_count()
354    }
355
356    /// Iterator over bits in the view.
357    #[inline]
358    pub fn iter(&self) -> BitIterator<'_> {
359        self.as_view().iter()
360    }
361
362    /// Set the bit at `index` to the given boolean value.
363    ///
364    /// Panics if `index` exceeds the view length.
365    #[inline]
366    pub fn set_to(&mut self, index: usize, value: bool) {
367        if value {
368            self.set(index);
369        } else {
370            self.unset(index);
371        }
372    }
373
374    /// Set the bit at `index` to the given boolean value without bounds checking.
375    ///
376    /// # Safety
377    ///
378    /// Caller must ensure that `index` is within the range of the view.
379    #[inline]
380    pub unsafe fn set_to_unchecked(&mut self, index: usize, value: bool) {
381        if value {
382            // SAFETY: checked by caller
383            unsafe { self.set_unchecked(index) }
384        } else {
385            // SAFETY: checked by caller
386            unsafe { self.unset_unchecked(index) }
387        }
388    }
389
390    /// Set the bit at `index` to `true`.
391    ///
392    /// Panics if `index` exceeds the view length.
393    #[inline]
394    pub fn set(&mut self, index: usize) {
395        assert!(index < self.len, "index {index} exceeds len {}", self.len);
396        // SAFETY: checked by assertion
397        unsafe { self.set_unchecked(index) };
398    }
399
400    /// Set the bit at `index` to `false`.
401    ///
402    /// Panics if `index` exceeds the view length.
403    #[inline]
404    pub fn unset(&mut self, index: usize) {
405        assert!(index < self.len, "index {index} exceeds len {}", self.len);
406        // SAFETY: checked by assertion
407        unsafe { self.unset_unchecked(index) };
408    }
409
410    /// Set the bit at `index` to `true` without bounds checking.
411    ///
412    /// # Safety
413    ///
414    /// Caller must ensure that `index` is within the range of the view.
415    #[inline]
416    pub unsafe fn set_unchecked(&mut self, index: usize) {
417        // SAFETY: checked by caller
418        unsafe { set_bit_unchecked(self.buffer.as_mut_ptr(), self.offset + index) }
419    }
420
421    /// Set the bit at `index` to `false` without bounds checking.
422    ///
423    /// # Safety
424    ///
425    /// Caller must ensure that `index` is within the range of the view.
426    #[inline]
427    pub unsafe fn unset_unchecked(&mut self, index: usize) {
428        // SAFETY: checked by caller
429        unsafe { unset_bit_unchecked(self.buffer.as_mut_ptr(), self.offset + index) }
430    }
431
432    /// Sets all bits in the range `[start, end)` to `value`.
433    ///
434    /// Panics if `end > self.len()` or `start > end`.
435    #[allow(clippy::inline_always)]
436    #[inline(always)]
437    pub fn fill_range(&mut self, start: usize, end: usize, value: bool) {
438        assert!(end <= self.len, "end {end} exceeds len {}", self.len);
439        assert!(start <= end, "start {start} exceeds end {end}");
440        // SAFETY: assertions guarantee start <= end <= self.len.
441        unsafe { self.fill_range_unchecked(start, end, value) }
442    }
443
444    /// Sets all bits in the range `[start, end)` to `value` without bounds checking.
445    ///
446    /// # Safety
447    ///
448    /// Caller must ensure that `start <= end <= self.len()`.
449    #[allow(clippy::inline_always)]
450    #[inline(always)]
451    pub unsafe fn fill_range_unchecked(&mut self, start: usize, end: usize, value: bool) {
452        fill_bits(self.buffer, self.offset + start, self.offset + end, value);
453    }
454
455    /// Copy this view into an owned [`BitBuffer`].
456    pub fn to_bit_buffer(&self) -> BitBuffer {
457        self.as_view().to_bit_buffer()
458    }
459}
460
461impl BitBuffer {
462    /// Borrow this buffer as a [`BitBufferView`] without cloning the backing allocation.
463    #[inline]
464    pub fn as_view(&self) -> BitBufferView<'_> {
465        BitBufferView {
466            buffer: self.inner().as_slice(),
467            offset: self.offset(),
468            len: self.len(),
469        }
470    }
471}
472
473impl BitBufferMut {
474    /// Borrow this buffer as an immutable [`BitBufferView`].
475    #[inline]
476    pub fn as_view(&self) -> BitBufferView<'_> {
477        BitBufferView {
478            buffer: self.as_slice(),
479            offset: self.offset(),
480            len: self.len(),
481        }
482    }
483
484    /// Borrow this buffer as a [`BitBufferMutView`].
485    #[inline]
486    pub fn as_mut_view(&mut self) -> BitBufferMutView<'_> {
487        let offset = self.offset();
488        let len = self.len();
489        BitBufferMutView {
490            buffer: self.as_mut_slice(),
491            offset,
492            len,
493        }
494    }
495}
496
497#[cfg(test)]
498mod tests {
499    use crate::BitBuffer;
500    use crate::BitBufferMut;
501    use crate::bitbuffer;
502
503    #[test]
504    fn view_reads_match_buffer() {
505        let buffer = bitbuffer![true, false, true, true, false, true, false, false];
506        let view = buffer.as_view();
507
508        assert_eq!(view.len(), buffer.len());
509        assert_eq!(view.true_count(), buffer.true_count());
510        assert_eq!(view.false_count(), buffer.false_count());
511        for i in 0..buffer.len() {
512            assert_eq!(view.value(i), buffer.value(i));
513        }
514        assert_eq!(
515            view.iter().collect::<Vec<_>>(),
516            buffer.iter().collect::<Vec<_>>()
517        );
518    }
519
520    #[test]
521    fn view_slice_preserves_offset() {
522        let buffer = BitBuffer::new_set(20);
523        let sliced = buffer.slice(5..17);
524        let view = buffer.as_view().slice(5..17);
525
526        assert_eq!(view.len(), sliced.len());
527        assert_eq!(view.true_count(), sliced.true_count());
528        assert_eq!(view.to_bit_buffer(), sliced);
529    }
530
531    #[test]
532    fn view_offset_buffer() {
533        let buffer = BitBuffer::new_set(64).slice(3..40);
534        let view = buffer.as_view();
535        assert_eq!(view.offset(), buffer.offset());
536        assert_eq!(view.len(), buffer.len());
537        assert_eq!(view.to_bit_buffer(), buffer);
538    }
539
540    #[test]
541    fn mut_view_set_unset() {
542        let mut buffer = BitBufferMut::new_unset(16);
543        {
544            let mut view = buffer.as_mut_view();
545            view.set(0);
546            view.set(15);
547            view.set_to(7, true);
548            view.fill_range(2, 5, true);
549            assert!(view.value(0));
550            assert_eq!(view.true_count(), 6);
551            view.unset(0);
552        }
553        let frozen = buffer.freeze();
554        assert!(!frozen.value(0));
555        assert!(frozen.value(2));
556        assert!(frozen.value(4));
557        assert!(frozen.value(7));
558        assert!(frozen.value(15));
559        assert_eq!(frozen.true_count(), 5);
560    }
561
562    #[test]
563    fn mut_view_with_offset() {
564        let mut buffer = BitBufferMut::from_buffer(crate::buffer_mut![0u8; 4], 3, 20);
565        {
566            let mut view = buffer.as_mut_view();
567            assert_eq!(view.offset(), 3);
568            view.fill_range(0, 20, true);
569        }
570        let frozen = buffer.freeze();
571        assert_eq!(frozen.true_count(), 20);
572    }
573}