Path: blob/main/crates/polars-arrow/src/bitmap/iterator.rs
6939 views
use polars_utils::slice::load_padded_le_u64;12use super::Bitmap;3use super::bitmask::BitMask;4use crate::trusted_len::TrustedLen;56/// Calculates how many iterations are remaining, assuming:7/// - We have length elements left.8/// - We need max(consume, min_length_for_iter) elements to start a new iteration.9/// - On each iteration we consume the given amount of elements.10fn calc_iters_remaining(length: usize, min_length_for_iter: usize, consume: usize) -> usize {11let min_length_for_iter = min_length_for_iter.max(consume);12if length < min_length_for_iter {13return 0;14}1516let obvious_part = length - min_length_for_iter;17let obvious_iters = obvious_part / consume;18// let obvious_part_remaining = obvious_part % consume;19// let total_remaining = min_length_for_iter + obvious_part_remaining;20// assert!(total_remaining >= min_length_for_iter); // We have at least 1 more iter.21// assert!(obvious_part_remaining < consume); // Basic modulo property.22// assert!(total_remaining < min_length_for_iter + consume); // Add min_length_for_iter to both sides.23// assert!(total_remaining - consume < min_length_for_iter); // Not enough remaining after 1 iter.241 + obvious_iters // Thus always exactly 1 more iter.25}2627#[derive(Clone)]28pub struct TrueIdxIter<'a> {29mask: BitMask<'a>,30first_unknown: usize,31i: usize,32len: usize,33remaining: usize,34}3536impl<'a> TrueIdxIter<'a> {37#[inline]38pub fn new(len: usize, validity: Option<&'a Bitmap>) -> Self {39if let Some(bitmap) = validity {40assert!(len == bitmap.len());41Self {42mask: BitMask::from_bitmap(bitmap),43first_unknown: 0,44i: 0,45remaining: bitmap.len() - bitmap.unset_bits(),46len,47}48} else {49Self {50mask: BitMask::default(),51first_unknown: len,52i: 0,53remaining: len,54len,55}56}57}58}5960impl Iterator for TrueIdxIter<'_> {61type Item = usize;6263#[inline]64fn next(&mut self) -> Option<Self::Item> {65// Fast path for many non-nulls in a row.66if self.i < self.first_unknown {67let ret = self.i;68self.i += 1;69self.remaining -= 1;70return Some(ret);71}7273while self.i < self.len {74let mask = self.mask.get_u32(self.i);75let num_null = mask.trailing_zeros();76self.i += num_null as usize;77if num_null < 32 {78self.first_unknown = self.i + (mask >> num_null).trailing_ones() as usize;79let ret = self.i;80self.i += 1;81self.remaining -= 1;82return Some(ret);83}84}8586None87}8889#[inline]90fn size_hint(&self) -> (usize, Option<usize>) {91(self.remaining, Some(self.remaining))92}93}9495unsafe impl TrustedLen for TrueIdxIter<'_> {}9697pub struct FastU32BitmapIter<'a> {98bytes: &'a [u8],99shift: u32,100bits_left: usize,101}102103impl<'a> FastU32BitmapIter<'a> {104pub fn new(bytes: &'a [u8], offset: usize, len: usize) -> Self {105assert!(bytes.len() * 8 >= offset + len);106let shift = (offset % 8) as u32;107let bytes = &bytes[offset / 8..];108Self {109bytes,110shift,111bits_left: len,112}113}114115// The iteration logic that would normally follow the fast-path.116fn next_remainder(&mut self) -> Option<u32> {117if self.bits_left > 0 {118let word = load_padded_le_u64(self.bytes);119let mask;120if self.bits_left >= 32 {121mask = u32::MAX;122self.bits_left -= 32;123self.bytes = unsafe { self.bytes.get_unchecked(4..) };124} else {125mask = (1 << self.bits_left) - 1;126self.bits_left = 0;127}128129return Some((word >> self.shift) as u32 & mask);130}131132None133}134135/// Returns the remainder bits and how many there are,136/// assuming the iterator was fully consumed.137pub fn remainder(mut self) -> (u64, usize) {138let bits_left = self.bits_left;139let lo = self.next_remainder().unwrap_or(0);140let hi = self.next_remainder().unwrap_or(0);141(((hi as u64) << 32) | (lo as u64), bits_left)142}143}144145impl Iterator for FastU32BitmapIter<'_> {146type Item = u32;147148#[inline]149fn next(&mut self) -> Option<Self::Item> {150// Fast path, can load a whole u64.151if self.bits_left >= 64 {152let chunk;153unsafe {154// SAFETY: bits_left ensures this is in-bounds.155chunk = self.bytes.get_unchecked(0..8);156self.bytes = self.bytes.get_unchecked(4..);157}158self.bits_left -= 32;159let word = u64::from_le_bytes(chunk.try_into().unwrap());160return Some((word >> self.shift) as u32);161}162163None164}165166#[inline]167fn size_hint(&self) -> (usize, Option<usize>) {168let hint = calc_iters_remaining(self.bits_left, 64, 32);169(hint, Some(hint))170}171}172173unsafe impl TrustedLen for FastU32BitmapIter<'_> {}174175#[derive(Clone)]176pub struct FastU56BitmapIter<'a> {177bytes: &'a [u8],178shift: u32,179bits_left: usize,180}181182impl<'a> FastU56BitmapIter<'a> {183pub fn new(bytes: &'a [u8], offset: usize, len: usize) -> Self {184assert!(bytes.len() * 8 >= offset + len);185let shift = (offset % 8) as u32;186let bytes = &bytes[offset / 8..];187Self {188bytes,189shift,190bits_left: len,191}192}193194// The iteration logic that would normally follow the fast-path.195fn next_remainder(&mut self) -> Option<u64> {196if self.bits_left > 0 {197let word = load_padded_le_u64(self.bytes);198let mask;199if self.bits_left >= 56 {200mask = (1 << 56) - 1;201self.bits_left -= 56;202self.bytes = unsafe { self.bytes.get_unchecked(7..) };203} else {204mask = (1 << self.bits_left) - 1;205self.bits_left = 0;206};207208return Some((word >> self.shift) & mask);209}210211None212}213214/// Returns the remainder bits and how many there are,215/// assuming the iterator was fully consumed. Output is safe but216/// not specified if the iterator wasn't fully consumed.217pub fn remainder(mut self) -> (u64, usize) {218let bits_left = self.bits_left;219let lo = self.next_remainder().unwrap_or(0);220let hi = self.next_remainder().unwrap_or(0);221((hi << 56) | lo, bits_left)222}223}224225impl Iterator for FastU56BitmapIter<'_> {226type Item = u64;227228#[inline]229fn next(&mut self) -> Option<Self::Item> {230// Fast path, can load a whole u64.231if self.bits_left >= 64 {232let chunk;233unsafe {234// SAFETY: bits_left ensures this is in-bounds.235chunk = self.bytes.get_unchecked(0..8);236self.bytes = self.bytes.get_unchecked(7..);237self.bits_left -= 56;238}239240let word = u64::from_le_bytes(chunk.try_into().unwrap());241let mask = (1 << 56) - 1;242return Some((word >> self.shift) & mask);243}244245None246}247248#[inline]249fn size_hint(&self) -> (usize, Option<usize>) {250let hint = calc_iters_remaining(self.bits_left, 64, 56);251(hint, Some(hint))252}253}254255unsafe impl TrustedLen for FastU56BitmapIter<'_> {}256257pub struct FastU64BitmapIter<'a> {258bytes: &'a [u8],259shift: u32,260bits_left: usize,261next_word: u64,262}263264impl<'a> FastU64BitmapIter<'a> {265pub fn new(bytes: &'a [u8], offset: usize, len: usize) -> Self {266assert!(bytes.len() * 8 >= offset + len);267let shift = (offset % 8) as u32;268let bytes = &bytes[offset / 8..];269let next_word = load_padded_le_u64(bytes);270let bytes = bytes.get(8..).unwrap_or(&[]);271Self {272bytes,273shift,274bits_left: len,275next_word,276}277}278279#[inline]280fn combine(&self, lo: u64, hi: u64) -> u64 {281// Compiles to 128-bit SHRD instruction on x86-64.282// Yes, the % 64 is important for the compiler to generate optimal code.283let wide = ((hi as u128) << 64) | lo as u128;284(wide >> (self.shift % 64)) as u64285}286287// The iteration logic that would normally follow the fast-path.288fn next_remainder(&mut self) -> Option<u64> {289if self.bits_left > 0 {290let lo = self.next_word;291let hi = load_padded_le_u64(self.bytes);292let mask;293if self.bits_left >= 64 {294mask = u64::MAX;295self.bits_left -= 64;296self.bytes = self.bytes.get(8..).unwrap_or(&[]);297} else {298mask = (1 << self.bits_left) - 1;299self.bits_left = 0;300};301self.next_word = hi;302303return Some(self.combine(lo, hi) & mask);304}305306None307}308309/// Returns the remainder bits and how many there are,310/// assuming the iterator was fully consumed. Output is safe but311/// not specified if the iterator wasn't fully consumed.312pub fn remainder(mut self) -> ([u64; 2], usize) {313let bits_left = self.bits_left;314let lo = self.next_remainder().unwrap_or(0);315let hi = self.next_remainder().unwrap_or(0);316([lo, hi], bits_left)317}318}319320impl Iterator for FastU64BitmapIter<'_> {321type Item = u64;322323#[inline]324fn next(&mut self) -> Option<Self::Item> {325// Fast path: can load two u64s in a row.326// (Note that we already loaded one in the form of self.next_word).327if self.bits_left >= 128 {328let chunk;329unsafe {330// SAFETY: bits_left ensures this is in-bounds.331chunk = self.bytes.get_unchecked(0..8);332self.bytes = self.bytes.get_unchecked(8..);333}334let lo = self.next_word;335let hi = u64::from_le_bytes(chunk.try_into().unwrap());336self.next_word = hi;337self.bits_left -= 64;338339return Some(self.combine(lo, hi));340}341342None343}344345#[inline]346fn size_hint(&self) -> (usize, Option<usize>) {347let hint = calc_iters_remaining(self.bits_left, 128, 64);348(hint, Some(hint))349}350}351352unsafe impl TrustedLen for FastU64BitmapIter<'_> {}353354/// This crates' equivalent of [`std::vec::IntoIter`] for [`Bitmap`].355#[derive(Debug, Clone)]356pub struct IntoIter {357values: Bitmap,358index: usize,359end: usize,360}361362impl IntoIter {363/// Creates a new [`IntoIter`] from a [`Bitmap`]364#[inline]365pub fn new(values: Bitmap) -> Self {366let end = values.len();367Self {368values,369index: 0,370end,371}372}373}374375impl Iterator for IntoIter {376type Item = bool;377378#[inline]379fn next(&mut self) -> Option<Self::Item> {380if self.index == self.end {381return None;382}383let old = self.index;384self.index += 1;385Some(unsafe { self.values.get_bit_unchecked(old) })386}387388#[inline]389fn size_hint(&self) -> (usize, Option<usize>) {390(self.end - self.index, Some(self.end - self.index))391}392393#[inline]394fn nth(&mut self, n: usize) -> Option<Self::Item> {395let new_index = self.index + n;396if new_index > self.end {397self.index = self.end;398None399} else {400self.index = new_index;401self.next()402}403}404}405406impl DoubleEndedIterator for IntoIter {407#[inline]408fn next_back(&mut self) -> Option<Self::Item> {409if self.index == self.end {410None411} else {412self.end -= 1;413Some(unsafe { self.values.get_bit_unchecked(self.end) })414}415}416}417418unsafe impl TrustedLen for IntoIter {}419420421