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