1#[cfg(feature = "arrow")]
10mod arrow;
11mod buf;
12mod buf_mut;
13mod count_ones;
14mod macros;
15mod meta;
16mod ops;
17mod pack;
18mod select;
19mod take;
20mod view;
21
22pub use arrow_buffer::bit_chunk_iterator::BitChunkIterator;
23pub use arrow_buffer::bit_chunk_iterator::BitChunks;
24pub use arrow_buffer::bit_chunk_iterator::UnalignedBitChunk;
25pub use arrow_buffer::bit_chunk_iterator::UnalignedBitChunkIterator;
26pub use arrow_buffer::bit_iterator::BitIndexIterator;
27pub use arrow_buffer::bit_iterator::BitIterator;
28pub use arrow_buffer::bit_iterator::BitSliceIterator;
29pub use buf::*;
30pub use buf_mut::*;
31pub use meta::*;
32pub use pack::*;
33pub use take::*;
34pub use view::*;
35
36#[inline]
42pub fn collect_bool_word<F>(len: usize, f: F) -> u64
43where
44 F: FnMut(usize) -> bool,
45{
46 assert!(len <= 64, "cannot pack {len} bits into a u64 word");
47
48 let mut word = [0u64; 1];
49 collect_bool_words_inline(&mut word, len, f);
50 word[0]
51}
52
53#[inline]
71pub fn collect_bool_words<F>(words: &mut [u64], len: usize, f: F)
72where
73 F: FnMut(usize) -> bool,
74{
75 let num_words = len.div_ceil(64);
76 assert!(
77 words.len() >= num_words,
78 "words slice has {} entries, need at least {num_words}",
79 words.len(),
80 );
81
82 collect_bool_words_inline(words, len, f)
83}
84
85#[inline]
91pub fn read_u64_le(bytes: &[u8]) -> u64 {
92 debug_assert!(bytes.len() <= 8);
93 let mut buf = [0u8; 8];
94 buf[..bytes.len()].copy_from_slice(bytes);
95 u64::from_le_bytes(buf)
96}
97
98#[inline]
110pub fn splice_word_at_bit(words: &mut [u64], bit_offset: usize, word: u64) {
111 let dest_word = bit_offset / 64;
112 let bit_in_word = bit_offset % 64;
113 words[dest_word] |= word << bit_in_word;
114 if bit_in_word != 0 {
115 let high = word >> (64 - bit_in_word);
116 if high != 0 {
117 words[dest_word + 1] = high;
118 }
119 }
120}
121
122#[inline]
131pub fn pack_bools_into_words<F>(words: &mut [u64], bit_offset: usize, len: usize, mut f: F)
132where
133 F: FnMut(usize) -> bool,
134{
135 if len == 0 {
136 return;
137 }
138 let num_words = (bit_offset + len).div_ceil(64);
139 assert!(
140 words.len() >= num_words,
141 "words slice has {} entries, need at least {num_words}",
142 words.len(),
143 );
144
145 let mut done = 0;
146 while len - done >= 64 {
147 let word = collect_bool_word(64, |bit| f(done + bit));
148 splice_word_at_bit(words, bit_offset + done, word);
149 done += 64;
150 }
151 let tail = len - done;
152 if tail > 0 {
153 let word = collect_bool_word(tail, |bit| f(done + bit));
154 splice_word_at_bit(words, bit_offset + done, word);
155 }
156}
157
158#[allow(clippy::inline_always)]
164#[inline(always)]
165pub fn get_bit(buf: &[u8], index: usize) -> bool {
166 buf[index / 8] & (1 << (index % 8)) != 0
167}
168
169#[allow(clippy::inline_always)]
175#[inline(always)]
176pub unsafe fn get_bit_unchecked(buf: *const u8, index: usize) -> bool {
177 (unsafe { *buf.add(index / 8) } & (1 << (index % 8))) != 0
178}
179
180#[allow(clippy::inline_always)]
186#[inline(always)]
187pub unsafe fn set_bit_unchecked(buf: *mut u8, index: usize) {
188 unsafe { *buf.add(index / 8) |= 1 << (index % 8) };
189}
190
191#[allow(clippy::inline_always)]
197#[inline(always)]
198pub unsafe fn unset_bit_unchecked(buf: *mut u8, index: usize) {
199 unsafe { *buf.add(index / 8) &= !(1 << (index % 8)) };
200}
201
202#[cfg(test)]
203mod tests {
204 use super::collect_bool_word;
205 use super::pack_bools_into_words;
206 use super::read_u64_le;
207
208 #[test]
209 fn collect_bool_word_packs_lsb_first() {
210 let word = collect_bool_word(5, |idx| idx.is_multiple_of(2));
211 assert_eq!(word, 0b10101);
212 }
213
214 #[test]
215 fn collect_bool_word_empty() {
216 assert_eq!(collect_bool_word(0, |_| true), 0);
217 }
218
219 #[test]
220 fn read_u64_le_zero_pads_tail() {
221 assert_eq!(read_u64_le(&[0x34, 0x12]), 0x1234);
222 assert_eq!(read_u64_le(&[0xff; 8]), u64::MAX);
223 }
224
225 #[test]
226 #[should_panic(expected = "cannot pack 65 bits into a u64 word")]
227 fn collect_bool_word_rejects_too_many_bits() {
228 let _ = collect_bool_word(65, |_| true);
229 }
230
231 fn pack(bit_offset: usize, len: usize, f: impl Fn(usize) -> bool) -> Vec<bool> {
232 let num_words = (bit_offset + len).div_ceil(64);
233 let mut words = vec![0u64; num_words];
234 pack_bools_into_words(&mut words, bit_offset, len, &f);
235 (0..bit_offset + len)
236 .map(|i| (words[i / 64] >> (i % 64)) & 1 == 1)
237 .collect()
238 }
239
240 #[test]
241 fn pack_bools_aligned_multi_word_with_tail() {
242 let bits = pack(0, 130, |i| i.is_multiple_of(3));
243 for i in 0..130 {
244 assert_eq!(bits[i], i.is_multiple_of(3), "bit {i}");
245 }
246 }
247
248 #[test]
249 fn pack_bools_unaligned_crossing_words() {
250 let bits = pack(40, 200, |i| i.is_multiple_of(7));
251 assert!(bits[..40].iter().all(|&b| !b));
252 for i in 0..200 {
253 assert_eq!(bits[40 + i], i.is_multiple_of(7), "bit {}", 40 + i);
254 }
255 }
256
257 #[test]
258 fn pack_bools_preserves_low_bits_of_leading_word() {
259 let mut words = vec![0u64; 2];
260 words[0] = 0b11111;
261 pack_bools_into_words(&mut words, 5, 70, |_| true);
262 for i in 0..5 {
263 assert_eq!((words[0] >> i) & 1, 1, "preserved bit {i}");
264 }
265 for i in 5..75 {
266 assert_eq!((words[i / 64] >> (i % 64)) & 1, 1, "extended bit {i}");
267 }
268 }
269}