Skip to main content

vortex_buffer/bit/
meta.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 vortex_error::VortexExpect;
8
9/// In-memory metadata describing a packed bitset: a normalized bit `offset` (always `< 8`) and a
10/// logical bit `len`.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub struct BitBufferMeta {
13    offset: usize,
14    len: usize,
15}
16
17impl BitBufferMeta {
18    /// Create metadata for a bitset starting at bit `offset` with `len` bits.
19    ///
20    /// Panics if `offset >= 8`. Use [`from_raw_offset`](Self::from_raw_offset) to normalize a
21    /// larger offset.
22    pub fn new(offset: usize, len: usize) -> Self {
23        assert!(offset < 8, "BitBufferMeta offset must be < 8, got {offset}");
24        Self { offset, len }
25    }
26
27    /// Normalize a raw bit `offset` into a whole-byte offset plus metadata whose `offset` is
28    /// `< 8`.
29    ///
30    /// Returns `(byte_offset, meta)` so the caller can slice its backing buffer by `byte_offset`
31    /// and store the remaining sub-byte offset in `meta`.
32    pub fn from_raw_offset(offset: usize, len: usize) -> (usize, Self) {
33        (
34            offset / 8,
35            Self {
36                offset: offset % 8,
37                len,
38            },
39        )
40    }
41
42    /// Return the leading byte offset and normalized metadata for a logical slice.
43    ///
44    /// # Panics
45    ///
46    /// Panics if the range is out of bounds or its end precedes its start.
47    pub fn slice(&self, range: impl RangeBounds<usize>) -> (usize, Self) {
48        let start = match range.start_bound() {
49            Bound::Included(&start) => start,
50            Bound::Excluded(&start) => start
51                .checked_add(1)
52                .vortex_expect("excluded slice start must not overflow"),
53            Bound::Unbounded => 0,
54        };
55        let end = match range.end_bound() {
56            Bound::Included(&end) => end
57                .checked_add(1)
58                .vortex_expect("included slice end must not overflow"),
59            Bound::Excluded(&end) => end,
60            Bound::Unbounded => self.len,
61        };
62
63        assert!(start <= end);
64        assert!(start <= self.len);
65        assert!(end <= self.len);
66
67        Self::from_raw_offset(self.offset + start, end - start)
68    }
69
70    /// The sub-byte bit offset. Always `< 8`.
71    #[inline(always)]
72    pub fn offset(&self) -> usize {
73        self.offset
74    }
75
76    /// The logical length of the bitset in bits.
77    #[inline(always)]
78    pub fn len(&self) -> usize {
79        self.len
80    }
81
82    /// Returns `true` if the bitset is empty.
83    #[inline(always)]
84    pub fn is_empty(&self) -> bool {
85        self.len == 0
86    }
87
88    /// The number of backing bytes required to hold `offset + len` bits.
89    #[inline]
90    pub fn byte_len(&self) -> usize {
91        (self.offset + self.len).div_ceil(8)
92    }
93}