1use std::mem::MaybeUninit;
8use std::ops::BitOrAssign;
9
10use vortex_buffer::BitBuffer;
11
12use crate::lane_kernels::CHUNK_LEN;
13use crate::lane_kernels::source::IndexedSource;
14
15pub trait IndexedSourceExt: IndexedSource + Sized {
22 #[inline]
47 fn try_map_masked_into<R, F>(
48 self,
49 mask: &BitBuffer,
50 out: &mut [MaybeUninit<R>],
51 mut f: F,
52 ) -> Result<(), usize>
53 where
54 R: Copy + Default,
55 F: FnMut(Self::Item) -> Option<R>,
56 {
57 #[inline(always)]
58 fn chunk<S, R, F>(
59 values: &S,
60 out: &mut [MaybeUninit<R>],
61 f: &mut F,
62 src_chunk: u64,
63 base: usize,
64 count: usize,
65 ) -> Option<usize>
66 where
67 S: IndexedSource,
68 R: Copy + Default,
69 F: FnMut(S::Item) -> Option<R>,
70 {
71 let mut fail_bits: u64 = 0;
72 for bit_idx in 0..count {
73 let idx = base + bit_idx;
74 let val = unsafe { values.get_unchecked(idx) };
76 let opt = f(val);
77 fail_bits |= (opt.is_none() as u64) << bit_idx;
78 let result = opt.unwrap_or_default();
79 unsafe { out.get_unchecked_mut(idx).write(result) };
80 }
81 let valid_failures = fail_bits & src_chunk;
82 (valid_failures != 0).then_some(base + valid_failures.trailing_zeros() as usize)
83 }
84
85 let values = self;
86 let len = values.len();
87 assert_eq!(len, mask.len(), "values and mask must have the same length");
88 assert_eq!(out.len(), len, "out must have the same length as values");
89
90 let chunks = mask.chunks();
91 let chunks_count = len / 64;
92 let remainder = len % 64;
93
94 for (chunk_idx, src_chunk) in chunks.iter().enumerate() {
95 if let Some(idx) = chunk(&values, out, &mut f, src_chunk, chunk_idx * 64, 64) {
96 return Err(idx);
97 }
98 }
99 if remainder != 0
100 && let Some(idx) = chunk(
101 &values,
102 out,
103 &mut f,
104 chunks.remainder_bits(),
105 chunks_count * 64,
106 remainder,
107 )
108 {
109 return Err(idx);
110 }
111 Ok(())
112 }
113
114 #[inline]
122 fn map_into<R, F>(self, out: &mut [MaybeUninit<R>], mut f: F)
123 where
124 F: FnMut(Self::Item) -> R,
125 {
126 #[inline(always)]
127 fn chunk<S, R, F>(
128 values: &S,
129 out: &mut [MaybeUninit<R>],
130 f: &mut F,
131 base: usize,
132 count: usize,
133 ) where
134 S: IndexedSource,
135 F: FnMut(S::Item) -> R,
136 {
137 for bit_idx in 0..count {
138 let idx = base + bit_idx;
139 let val = unsafe { values.get_unchecked(idx) };
141 unsafe { out.get_unchecked_mut(idx).write(f(val)) };
142 }
143 }
144
145 let values = self;
146 let len = values.len();
147 assert_eq!(out.len(), len, "out must have the same length as values");
148
149 let chunks_count = len / CHUNK_LEN;
150 let remainder = len % CHUNK_LEN;
151
152 for chunk_idx in 0..chunks_count {
153 chunk(&values, out, &mut f, chunk_idx * CHUNK_LEN, CHUNK_LEN);
154 }
155 if remainder != 0 {
156 chunk(&values, out, &mut f, chunks_count * CHUNK_LEN, remainder);
157 }
158 }
159
160 #[inline]
182 fn map_bits_into<F>(self, words: &mut [u64], mut f: F)
183 where
184 F: FnMut(Self::Item) -> bool,
185 {
186 #[inline(always)]
187 fn chunk<S, F>(values: &S, f: &mut F, base: usize, count: usize) -> u64
188 where
189 S: IndexedSource,
190 F: FnMut(S::Item) -> bool,
191 {
192 let mut packed: u64 = 0;
193 for bit_idx in 0..count {
194 let val = unsafe { values.get_unchecked(base + bit_idx) };
196 packed |= (f(val) as u64) << bit_idx;
197 }
198 packed
199 }
200
201 let values = self;
202 let len = values.len();
203 let num_words = len.div_ceil(64);
204 assert!(
205 words.len() >= num_words,
206 "words slice has {} entries, need at least {num_words}",
207 words.len(),
208 );
209
210 let full = len / 64;
211 let remainder = len % 64;
212
213 for word_idx in 0..full {
214 words[word_idx] = chunk(&values, &mut f, word_idx * 64, 64);
215 }
216 if remainder != 0 {
217 words[full] = chunk(&values, &mut f, full * 64, remainder);
218 }
219 }
220
221 #[inline]
243 fn map_checked_into<R, Fail, Apply>(self, out: &mut [MaybeUninit<R>], mut apply: Apply) -> Fail
244 where
245 Fail: Copy + Default + BitOrAssign,
246 Apply: FnMut(Self::Item) -> (R, Fail),
247 {
248 const {
249 assert!(
250 size_of::<Fail>() <= size_of::<R>(),
251 "failure evidence must be no wider than the value, or it bounds the vector width"
252 )
253 };
254
255 let values = self;
256 let len = values.len();
257 assert_eq!(out.len(), len, "out must have the same length as values");
258
259 let mut failed = Fail::default();
260 for idx in 0..len {
261 let val = unsafe { values.get_unchecked(idx) };
263
264 let (result, failure) = apply(val);
265 failed |= failure;
266
267 unsafe { out.get_unchecked_mut(idx).write(result) };
269 }
270 failed
271 }
272
273 #[inline]
296 fn try_map_into<R, F>(self, out: &mut [MaybeUninit<R>], mut f: F) -> Result<(), usize>
297 where
298 R: Copy + Default,
299 F: FnMut(Self::Item) -> Option<R>,
300 {
301 #[inline(always)]
305 fn chunk<S, R, F>(
306 values: &S,
307 out: &mut [MaybeUninit<R>],
308 f: &mut F,
309 base: usize,
310 count: usize,
311 ) -> bool
312 where
313 S: IndexedSource,
314 R: Copy + Default,
315 F: FnMut(S::Item) -> Option<R>,
316 {
317 let mut fail_acc: u64 = 0;
318 for bit_idx in 0..count {
319 let idx = base + bit_idx;
320 let val = unsafe { values.get_unchecked(idx) };
322 let opt = f(val);
323 fail_acc |= opt.is_none() as u64;
324 let result = opt.unwrap_or_default();
325 unsafe { out.get_unchecked_mut(idx).write(result) };
326 }
327 fail_acc != 0
328 }
329
330 let values = self;
331 let len = values.len();
332 assert_eq!(out.len(), len, "out must have the same length as values");
333
334 let chunks_count = len / CHUNK_LEN;
335 let remainder = len % CHUNK_LEN;
336
337 for chunk_idx in 0..chunks_count {
338 let base = chunk_idx * CHUNK_LEN;
339 if chunk(&values, out, &mut f, base, CHUNK_LEN) {
340 return Err(attribute_failure_no_mask(&values, base, CHUNK_LEN, &mut f));
341 }
342 }
343 if remainder != 0 {
344 let base = chunks_count * CHUNK_LEN;
345 if chunk(&values, out, &mut f, base, remainder) {
346 return Err(attribute_failure_no_mask(&values, base, remainder, &mut f));
347 }
348 }
349 Ok(())
350 }
351}
352
353impl<S: IndexedSource> IndexedSourceExt for S {}
354
355#[cold]
361#[inline(never)]
362fn cold_scan<S>(
363 values: &S,
364 base: usize,
365 chunk_len: usize,
366 mut lane_fails: impl FnMut(usize , S::Item) -> bool,
367) -> usize
368where
369 S: IndexedSource,
370{
371 for bit_idx in 0..chunk_len {
372 let idx = base + bit_idx;
373 let val = unsafe { values.get_unchecked(idx) };
375 if lane_fails(bit_idx, val) {
376 return idx;
377 }
378 }
379 unreachable!("cold_scan called without a failing lane")
380}
381
382#[inline]
385fn attribute_failure_no_mask<S, R, F>(values: &S, base: usize, chunk_len: usize, f: &mut F) -> usize
386where
387 S: IndexedSource,
388 F: FnMut(S::Item) -> Option<R>,
389{
390 cold_scan(values, base, chunk_len, |_bit_idx, val| f(val).is_none())
391}
392
393#[cfg(test)]
394#[allow(clippy::cast_possible_truncation)]
395mod tests {
396 use vortex_buffer::BitBuffer;
397 use vortex_buffer::BitBufferMut;
398
399 use super::*;
400
401 fn write_t<T: Copy>(out: Vec<MaybeUninit<T>>) -> Vec<T> {
402 unsafe { std::mem::transmute(out) }
404 }
405
406 #[test]
407 fn try_map_masked_into_all_ok() {
408 let values: Vec<u64> = (0..200).collect();
409 let mask = BitBuffer::new_set(200);
410 let mut out = vec![MaybeUninit::<u32>::uninit(); 200];
411 let res = values.as_slice().try_map_masked_into(&mask, &mut out, |v| {
412 (v <= u32::MAX as u64).then_some(v as u32)
413 });
414 assert!(res.is_ok());
415 let got = write_t(out);
416 assert_eq!(got, (0..200u32).collect::<Vec<_>>());
417 }
418
419 #[test]
420 fn try_map_masked_into_overflow_fails() {
421 let mut values: Vec<u64> = (0..200).collect();
422 values[137] = (u32::MAX as u64) + 1;
423 let mask = BitBuffer::new_set(200);
424 let mut out = vec![MaybeUninit::<u32>::uninit(); 200];
425 let res = values.as_slice().try_map_masked_into(&mask, &mut out, |v| {
426 (v <= u32::MAX as u64).then_some(v as u32)
427 });
428 assert_eq!(res, Err(137));
429 }
430
431 #[test]
432 fn try_map_masked_into_overflow_reports_first_failing_lane() {
433 let mut values: Vec<u64> = (0..200).collect();
434 values[50] = u64::MAX;
435 values[51] = u64::MAX;
436 values[137] = u64::MAX;
437 let mask = BitBuffer::new_set(200);
438 let mut out = vec![MaybeUninit::<u32>::uninit(); 200];
439 let res = values.as_slice().try_map_masked_into(&mask, &mut out, |v| {
440 (v <= u32::MAX as u64).then_some(v as u32)
441 });
442 assert_eq!(res, Err(50));
443 }
444
445 #[test]
446 fn try_map_masked_into_value_only_closure_filters_null_overflow() {
447 let mut values: Vec<u64> = (0..200).collect();
448 values[5] = u64::MAX;
449 values[42] = u64::MAX;
450 let mask = {
451 let mut m = BitBufferMut::with_capacity(200);
452 for i in 0..200 {
453 m.append(i != 5 && i != 42);
454 }
455 m.freeze()
456 };
457 let mut out = vec![MaybeUninit::<u32>::uninit(); 200];
458 let res = values.as_slice().try_map_masked_into(&mask, &mut out, |v| {
459 (v <= u32::MAX as u64).then_some(v as u32)
460 });
461 assert!(
462 res.is_ok(),
463 "null-lane overflow should be filtered by the cold path"
464 );
465 }
466
467 #[test]
468 fn try_map_masked_into_value_only_closure_reports_first_valid_failure() {
469 let mut values: Vec<u64> = (0..200).collect();
470 values[5] = u64::MAX;
471 values[42] = u64::MAX;
472 values[77] = u64::MAX;
473 values[100] = u64::MAX;
474 let mask = {
475 let mut m = BitBufferMut::with_capacity(200);
476 for i in 0..200 {
477 m.append(i != 5 && i != 42);
478 }
479 m.freeze()
480 };
481 let mut out = vec![MaybeUninit::<u32>::uninit(); 200];
482 let res = values.as_slice().try_map_masked_into(&mask, &mut out, |v| {
483 (v <= u32::MAX as u64).then_some(v as u32)
484 });
485 assert_eq!(res, Err(77));
486 }
487
488 #[test]
489 fn try_map_masked_into_null_lane_bypasses_check() {
490 let mut values: Vec<u64> = (0..200).collect();
491 values[5] = u64::MAX;
492 let mask = {
493 let mut m = BitBufferMut::with_capacity(200);
494 for i in 0..200 {
495 m.append(i != 5);
496 }
497 m.freeze()
498 };
499 let mut out = vec![MaybeUninit::<u32>::uninit(); 200];
500 let res = values.as_slice().try_map_masked_into(&mask, &mut out, |v| {
501 (v <= u32::MAX as u64).then_some(v as u32)
502 });
503 assert!(res.is_ok());
504 let got = write_t(out);
505 assert_eq!(got[5], 0);
506 assert_eq!(got[6], 6);
507 }
508
509 #[test]
510 fn try_map_masked_into_branchful_matches_branchless() {
511 let mut values: Vec<u64> = (0..130).map(|i| i as u64 * 7).collect();
512 values[2] = u64::MAX;
513 values[65] = u32::MAX as u64;
514 let mask = {
515 let mut m = BitBufferMut::with_capacity(130);
516 for i in 0..130 {
517 m.append(!matches!(i, 2 | 17 | 99));
518 }
519 m.freeze()
520 };
521
522 let mut branchless = vec![MaybeUninit::<u32>::uninit(); 130];
523 let mut branchful = vec![MaybeUninit::<u32>::uninit(); 130];
524 values
525 .as_slice()
526 .try_map_masked_into(&mask, &mut branchless, |v| {
527 (v <= u32::MAX as u64).then_some(v as u32)
528 })
529 .unwrap();
530 values
531 .as_slice()
532 .try_map_masked_into(&mask, &mut branchful, |v| u32::try_from(v).ok())
533 .unwrap();
534
535 assert_eq!(write_t(branchful), write_t(branchless));
536 }
537
538 #[test]
539 fn try_map_masked_into_partial_chunk() {
540 let values: Vec<u64> = (0..130).collect();
541 let mask = BitBuffer::new_set(130);
542 let mut out = vec![MaybeUninit::<u32>::uninit(); 130];
543 let res = values.as_slice().try_map_masked_into(&mask, &mut out, |v| {
544 (v <= u32::MAX as u64).then_some(v as u32)
545 });
546 assert!(res.is_ok());
547 let got = write_t(out);
548 assert_eq!(got.len(), 130);
549 assert_eq!(got[129], 129);
550 }
551
552 #[test]
553 fn try_map_masked_into_sliced_mask_unaligned_offset() {
554 let big = BitBuffer::new_set(256);
555 let mask = big.slice(13..143);
556 assert_eq!(mask.len(), 130);
557
558 let values: Vec<u64> = (0..130).collect();
559 let mut out = vec![MaybeUninit::<u32>::uninit(); 130];
560 let res = values.as_slice().try_map_masked_into(&mask, &mut out, |v| {
561 (v <= u32::MAX as u64).then_some(v as u32)
562 });
563 assert!(res.is_ok());
564 let got = write_t(out);
565 assert_eq!(got, (0..130u32).collect::<Vec<_>>());
566 }
567
568 #[test]
569 fn try_map_masked_into_sliced_mask_with_overflow() {
570 let big = BitBuffer::new_set(256);
571 let mask = big.slice(13..143);
572 assert_eq!(mask.len(), 130);
573
574 let mut values: Vec<u64> = (0..130).collect();
575 values[77] = u64::MAX;
576 let mut out = vec![MaybeUninit::<u32>::uninit(); 130];
577 let res = values.as_slice().try_map_masked_into(&mask, &mut out, |v| {
578 (v <= u32::MAX as u64).then_some(v as u32)
579 });
580 assert_eq!(res, Err(77));
581 }
582
583 #[test]
584 fn try_map_masked_into_sliced_mask_null_lanes() {
585 let mut m = BitBufferMut::with_capacity(256);
586 for i in 0..256 {
587 m.append(i % 3 != 0);
588 }
589 let big = m.freeze();
590 let mask = big.slice(13..143);
591 assert_eq!(mask.len(), 130);
592
593 let mut values: Vec<u64> = (0..130).collect();
594 values[2] = u64::MAX;
595 let mut out = vec![MaybeUninit::<u32>::uninit(); 130];
596 let res = values.as_slice().try_map_masked_into(&mask, &mut out, |v| {
597 (v <= u32::MAX as u64).then_some(v as u32)
598 });
599 assert!(res.is_ok(), "null lane should bypass the range check");
600 }
601
602 #[test]
603 fn map_checked_into_writes_all_lanes_and_reduces_flag() {
604 let mut values: Vec<u64> = (0..130).collect();
605 let mut out = vec![MaybeUninit::<u32>::uninit(); 130];
606 let failed = values
607 .as_slice()
608 .map_checked_into(&mut out, |v| (v as u32, v > u32::MAX as u64));
609 assert!(!failed);
610 assert_eq!(write_t(out), (0..130u32).collect::<Vec<_>>());
611
612 values[77] = (u32::MAX as u64) + 1;
613 let mut out = vec![MaybeUninit::<u32>::uninit(); 130];
614 let failed = values
615 .as_slice()
616 .map_checked_into(&mut out, |v| (v as u32, v > u32::MAX as u64));
617 assert!(failed);
618 assert_eq!(write_t(out)[76], 76);
620 }
621
622 #[test]
623 fn map_bits_into_packs_full_and_remainder_words() {
624 let values: Vec<u32> = (0..130).collect();
625 let mut words = vec![u64::MAX; 3];
626 values.as_slice().map_bits_into(&mut words, |v| v % 2 == 0);
627
628 for idx in 0..130 {
629 let bit = (words[idx / 64] >> (idx % 64)) & 1;
630 assert_eq!(bit == 1, idx % 2 == 0, "lane {idx}");
631 }
632 assert_eq!(words[2] >> 2, 0);
634 }
635
636 #[test]
637 fn map_bits_into_lane_zip_compare() {
638 use crate::lane_kernels::LaneZip;
639
640 let lhs: Vec<i64> = (0..100).collect();
641 let rhs: Vec<i64> = (0..100).rev().collect();
642 let mut words = vec![0u64; 2];
643 LaneZip::new(lhs.as_slice(), rhs.as_slice()).map_bits_into(&mut words, |(a, b)| a >= b);
644
645 for idx in 0..100 {
646 let bit = (words[idx / 64] >> (idx % 64)) & 1;
647 assert_eq!(bit == 1, lhs[idx] >= rhs[idx], "lane {idx}");
648 }
649 }
650
651 #[test]
652 #[should_panic(expected = "words slice has 1 entries")]
653 fn map_bits_into_words_too_short_panics() {
654 let values: Vec<u32> = (0..65).collect();
655 let mut words = vec![0u64; 1];
656 values.as_slice().map_bits_into(&mut words, |v| v > 0);
657 }
658
659 #[test]
660 fn try_map_masked_into_overflow_in_remainder() {
661 let mut values: Vec<u64> = (0..130).collect();
662 values[129] = (u32::MAX as u64) + 1;
663 let mask = BitBuffer::new_set(130);
664 let mut out = vec![MaybeUninit::<u32>::uninit(); 130];
665 let res = values.as_slice().try_map_masked_into(&mask, &mut out, |v| {
666 (v <= u32::MAX as u64).then_some(v as u32)
667 });
668 assert_eq!(res, Err(129));
669 }
670}