vortex_buffer/bit/
take.rs1use num_traits::AsPrimitive;
5use num_traits::Zero;
6use vortex_error::VortexExpect;
7
8use crate::BitBuffer;
9use crate::BitBufferView;
10use crate::bit::get_bit;
11use crate::bit::get_bit_unchecked;
12
13const COLLECT_TO_VEC: usize = 4096;
16
17pub fn take_bits<I>(
19 bits: BitBufferView<'_>,
20 indices: &[I],
21 validity: Option<BitBufferView<'_>>,
22) -> BitBuffer
23where
24 I: AsPrimitive<usize> + Ord + Zero,
25{
26 if let Some(validity) = validity {
27 assert_eq!(validity.len(), indices.len());
28 }
29
30 if indices.is_empty() || bits.is_empty() {
31 return BitBuffer::new_unset(indices.len());
32 }
33
34 if bits.len() <= indices.len() * 64 {
37 match bits.true_count() {
38 0 => return BitBuffer::new_unset(indices.len()),
39 count if count == bits.len() => return BitBuffer::new_set(indices.len()),
40 1 => {
41 let target = bits.select(0).vortex_expect("one set bit");
42 return BitBuffer::collect_bool_multiversioned(indices.len(), |i| {
43 unsafe { indices.get_unchecked(i) }.as_() == target
46 });
47 }
48 count if count == bits.len() - 1 => {
49 let target = first_unset(bits).vortex_expect("one unset bit");
50 return BitBuffer::collect_bool_multiversioned(indices.len(), |i| {
51 unsafe { indices.get_unchecked(i) }.as_() != target
54 });
55 }
56 _ => {}
57 }
58 }
59
60 let Some(validity) = validity else {
61 let first = indices[0];
64 let (min, max) = indices
65 .iter()
66 .fold((first, first), |(lo, hi), &x| (lo.min(x), hi.max(x)));
67 assert!(
68 min >= I::zero() && max.as_() < bits.len(),
69 "take index out of bounds"
70 );
71
72 return if bits.len() <= COLLECT_TO_VEC {
73 let bools: Vec<bool> = bits.iter().collect();
74 BitBuffer::collect_bool_multiversioned(indices.len(), |i| unsafe {
75 let idx = indices.get_unchecked(i).as_();
78 *bools.get_unchecked(idx)
80 })
81 } else {
82 let ptr = bits.inner().as_ptr();
83 let offset = bits.offset();
84 BitBuffer::collect_bool(indices.len(), |i| unsafe {
85 let idx = indices.get_unchecked(i).as_();
88 get_bit_unchecked(ptr, offset + idx)
90 })
91 };
92 };
93
94 let ptr = validity.inner().as_ptr();
95 if bits.len() <= COLLECT_TO_VEC {
96 let offset = validity.offset();
97 let bools: Vec<bool> = bits.iter().collect();
98 return BitBuffer::collect_bool(indices.len(), |i| {
99 let idx = unsafe { indices.get_unchecked(i).as_() };
102 let mask_idx: usize = unsafe { get_bit_unchecked(ptr, offset + i) }.as_();
104 bools[idx & mask_idx.wrapping_neg()]
105 });
106 }
107
108 let validity_offset = validity.offset();
109 let buf = bits.inner();
110 let bits_offset = bits.offset();
111 BitBuffer::collect_bool(indices.len(), |i| {
112 let idx = unsafe { indices.get_unchecked(i).as_() };
115 let mask_idx: usize = unsafe { get_bit_unchecked(ptr, validity_offset + i) }.as_();
117 let masked_idx = idx & mask_idx.wrapping_neg();
118 get_bit(buf, bits_offset + masked_idx)
119 })
120}
121
122fn first_unset(bits: BitBufferView<'_>) -> Option<usize> {
123 let chunks = bits.chunks();
124 for (word_idx, word) in chunks.iter().enumerate() {
125 if word != u64::MAX {
126 return Some(word_idx * 64 + (!word).trailing_zeros() as usize);
127 }
128 }
129
130 let remainder_start = bits.len() / 64 * 64;
131 let target = remainder_start + (!chunks.remainder_bits()).trailing_zeros() as usize;
132 (target < bits.len()).then_some(target)
133}
134
135#[cfg(test)]
136mod tests {
137 use rand::RngExt;
138 use rand::SeedableRng;
139 use rand::rngs::StdRng;
140 use rstest::rstest;
141
142 use super::take_bits;
143 use crate::BitBuffer;
144
145 fn random_bits(len: usize) -> BitBuffer {
146 let mut rng = StdRng::seed_from_u64(42);
147 BitBuffer::collect_bool(len, |_| rng.random())
148 }
149
150 fn random_indices(len: usize, bound: usize) -> Vec<usize> {
151 let mut rng = StdRng::seed_from_u64(43);
152 (0..len).map(|_| rng.random_range(0..bound)).collect()
153 }
154
155 #[rstest]
156 #[case(300)]
157 #[case(5000)]
158 fn gathers(#[case] bits_len: usize) {
159 let bits = random_bits(bits_len + 11);
160 let view = bits.as_view().slice(11..);
161 let len = 400;
162 let indices: Vec<i64> = random_indices(len, bits_len)
163 .into_iter()
164 .map(|idx| i64::try_from(idx).unwrap())
165 .collect();
166
167 let taken = take_bits(view, &indices, None);
168 assert_eq!(taken.len(), len);
169 for (i, &idx) in indices.iter().enumerate() {
170 assert_eq!(
171 taken.value(i),
172 bits.value(usize::try_from(idx).unwrap() + 11)
173 );
174 }
175
176 let validity = BitBuffer::collect_bool(len, |i| i % 3 != 0);
177 let with_garbage: Vec<i64> = indices
178 .iter()
179 .enumerate()
180 .map(|(i, &idx)| match i % 6 {
181 0 => i64::MAX,
182 3 => -1,
183 _ => idx,
184 })
185 .collect();
186
187 let taken = take_bits(view, &with_garbage, Some(validity.as_view()));
188 assert_eq!(taken.len(), len);
189 for (i, &idx) in with_garbage.iter().enumerate() {
190 if validity.value(i) {
191 assert_eq!(
192 taken.value(i),
193 bits.value(usize::try_from(idx).unwrap() + 11)
194 );
195 }
196 }
197 }
198
199 #[rstest]
200 #[case(BitBuffer::new_set(100), |_: usize| true)]
201 #[case(BitBuffer::new_unset(100), |_: usize| false)]
202 #[case(BitBuffer::collect_bool(100, |i| i == 37), |idx: usize| idx == 37)]
203 #[case(BitBuffer::collect_bool(100, |i| i != 37), |idx: usize| idx != 37)]
204 fn nearly_full(#[case] bits: BitBuffer, #[case] expected: fn(usize) -> bool) {
205 let indices: Vec<u8> = random_indices(300, bits.len())
206 .into_iter()
207 .map(|idx| u8::try_from(idx).unwrap())
208 .collect();
209
210 let taken = take_bits(bits.as_view(), &indices, None);
211 for (i, &idx) in indices.iter().enumerate() {
212 assert_eq!(taken.value(i), expected(usize::from(idx)));
213 }
214 }
215
216 #[test]
217 fn small_indices() {
218 let bits = BitBuffer::collect_bool(10_000, |i| i != 37);
219 let taken = take_bits(bits.as_view(), &[36u32, 37, 38], None);
220 assert!(taken.value(0));
221 assert!(!taken.value(1));
222 assert!(taken.value(2));
223 }
224
225 #[test]
226 fn empty() {
227 let bits = random_bits(64);
228 assert_eq!(take_bits::<u16>(bits.as_view(), &[], None).len(), 0);
229
230 let bits = BitBuffer::new_unset(0);
231 let validity = BitBuffer::new_unset(3);
232 let taken = take_bits(bits.as_view(), &[7u32, 8, 9], Some(validity.as_view()));
233 assert_eq!(taken, BitBuffer::new_unset(3));
234 }
235}