Path: blob/main/crates/polars-arrow/src/bitmap/immutable.rs
6939 views
#![allow(unsafe_op_in_unsafe_fn)]1use std::ops::Deref;2use std::sync::LazyLock;34use either::Either;5use polars_error::{PolarsResult, polars_bail};6use polars_utils::relaxed_cell::RelaxedCell;78use super::utils::{self, BitChunk, BitChunks, BitmapIter, count_zeros, fmt, get_bit_unchecked};9use super::{IntoIter, MutableBitmap, chunk_iter_to_vec, num_intersections_with};10use crate::array::Splitable;11use crate::bitmap::aligned::AlignedBitmapSlice;12use crate::bitmap::iterator::{13FastU32BitmapIter, FastU56BitmapIter, FastU64BitmapIter, TrueIdxIter,14};15use crate::bitmap::utils::bytes_for;16use crate::legacy::utils::FromTrustedLenIterator;17use crate::storage::SharedStorage;18use crate::trusted_len::TrustedLen;1920const UNKNOWN_BIT_COUNT: u64 = u64::MAX;2122/// An immutable container semantically equivalent to `Arc<Vec<bool>>` but represented as `Arc<Vec<u8>>` where23/// each boolean is represented as a single bit.24///25/// # Examples26/// ```27/// use polars_arrow::bitmap::{Bitmap, MutableBitmap};28///29/// let bitmap = Bitmap::from([true, false, true]);30/// assert_eq!(bitmap.iter().collect::<Vec<_>>(), vec![true, false, true]);31///32/// // creation directly from bytes33/// let bitmap = Bitmap::try_new(vec![0b00001101], 5).unwrap();34/// // note: the first bit is the left-most of the first byte35/// assert_eq!(bitmap.iter().collect::<Vec<_>>(), vec![true, false, true, true, false]);36/// // we can also get the slice:37/// assert_eq!(bitmap.as_slice(), ([0b00001101u8].as_ref(), 0, 5));38/// // debug helps :)39/// assert_eq!(format!("{:?}", bitmap), "Bitmap { len: 5, offset: 0, bytes: [0b___01101] }");40///41/// // it supports copy-on-write semantics (to a `MutableBitmap`)42/// let bitmap: MutableBitmap = bitmap.into_mut().right().unwrap();43/// assert_eq!(bitmap, MutableBitmap::from([true, false, true, true, false]));44///45/// // slicing is 'O(1)' (data is shared)46/// let bitmap = Bitmap::try_new(vec![0b00001101], 5).unwrap();47/// let mut sliced = bitmap.clone();48/// sliced.slice(1, 4);49/// assert_eq!(sliced.as_slice(), ([0b00001101u8].as_ref(), 1, 4)); // 1 here is the offset:50/// assert_eq!(format!("{:?}", sliced), "Bitmap { len: 4, offset: 1, bytes: [0b___0110_] }");51/// // when sliced (or cloned), it is no longer possible to `into_mut`.52/// let same: Bitmap = sliced.into_mut().left().unwrap();53/// ```54#[derive(Default, Clone)]55pub struct Bitmap {56storage: SharedStorage<u8>,57// Both offset and length are measured in bits. They are used to bound the58// bitmap to a region of Bytes.59offset: usize,60length: usize,6162// A bit field that contains our cache for the number of unset bits.63// If it is u64::MAX, we have no known value at all.64// Other bit patterns where the top bit is set is reserved for future use.65// If the top bit is not set we have an exact count.66unset_bit_count_cache: RelaxedCell<u64>,67}6869#[inline(always)]70fn has_cached_unset_bit_count(ubcc: u64) -> bool {71ubcc >> 63 == 072}7374impl std::fmt::Debug for Bitmap {75fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {76let (bytes, offset, len) = self.as_slice();77fmt(bytes, offset, len, f)78}79}8081pub(super) fn check(bytes: &[u8], offset: usize, length: usize) -> PolarsResult<()> {82if offset + length > bytes.len().saturating_mul(8) {83polars_bail!(InvalidOperation:84"The offset + length of the bitmap ({}) must be `<=` to the number of bytes times 8 ({})",85offset + length,86bytes.len().saturating_mul(8)87);88}89Ok(())90}9192impl Bitmap {93/// Initializes an empty [`Bitmap`].94#[inline]95pub fn new() -> Self {96Self::default()97}9899/// Initializes a new [`Bitmap`] from vector of bytes and a length.100/// # Errors101/// This function errors iff `length > bytes.len() * 8`102#[inline]103pub fn try_new(bytes: Vec<u8>, length: usize) -> PolarsResult<Self> {104check(&bytes, 0, length)?;105Ok(Self {106storage: SharedStorage::from_vec(bytes),107length,108offset: 0,109unset_bit_count_cache: RelaxedCell::from(if length == 0 {1100111} else {112UNKNOWN_BIT_COUNT113}),114})115}116117/// Returns the length of the [`Bitmap`].118#[inline]119pub fn len(&self) -> usize {120self.length121}122123/// Returns whether [`Bitmap`] is empty124#[inline]125pub fn is_empty(&self) -> bool {126self.len() == 0127}128129/// Returns a new iterator of `bool` over this bitmap130pub fn iter(&self) -> BitmapIter<'_> {131BitmapIter::new(&self.storage, self.offset, self.length)132}133134/// Returns an iterator over bits in bit chunks [`BitChunk`].135///136/// This iterator is useful to operate over multiple bits via e.g. bitwise.137pub fn chunks<T: BitChunk>(&self) -> BitChunks<'_, T> {138BitChunks::new(&self.storage, self.offset, self.length)139}140141/// Returns a fast iterator that gives 32 bits at a time.142/// Has a remainder that must be handled separately.143pub fn fast_iter_u32(&self) -> FastU32BitmapIter<'_> {144FastU32BitmapIter::new(&self.storage, self.offset, self.length)145}146147/// Returns a fast iterator that gives 56 bits at a time.148/// Has a remainder that must be handled separately.149pub fn fast_iter_u56(&self) -> FastU56BitmapIter<'_> {150FastU56BitmapIter::new(&self.storage, self.offset, self.length)151}152153/// Returns a fast iterator that gives 64 bits at a time.154/// Has a remainder that must be handled separately.155pub fn fast_iter_u64(&self) -> FastU64BitmapIter<'_> {156FastU64BitmapIter::new(&self.storage, self.offset, self.length)157}158159/// Returns an iterator that only iterates over the set bits.160pub fn true_idx_iter(&self) -> TrueIdxIter<'_> {161TrueIdxIter::new(self.len(), Some(self))162}163164/// Returns the bits of this [`Bitmap`] as a [`AlignedBitmapSlice`].165pub fn aligned<T: BitChunk>(&self) -> AlignedBitmapSlice<'_, T> {166AlignedBitmapSlice::new(&self.storage, self.offset, self.length)167}168169/// Returns the byte slice of this [`Bitmap`].170///171/// The returned tuple contains:172/// * `.1`: The byte slice, truncated to the start of the first bit. So the start of the slice173/// is within the first 8 bits.174/// * `.2`: The start offset in bits on a range `0 <= offsets < 8`.175/// * `.3`: The length in number of bits.176#[inline]177pub fn as_slice(&self) -> (&[u8], usize, usize) {178let start = self.offset / 8;179let len = (self.offset % 8 + self.length).saturating_add(7) / 8;180(181&self.storage[start..start + len],182self.offset % 8,183self.length,184)185}186187/// Returns the number of set bits on this [`Bitmap`].188///189/// See `unset_bits` for details.190#[inline]191pub fn set_bits(&self) -> usize {192self.length - self.unset_bits()193}194195/// Returns the number of set bits on this [`Bitmap`] if it is known.196///197/// See `lazy_unset_bits` for details.198#[inline]199pub fn lazy_set_bits(&self) -> Option<usize> {200Some(self.length - self.lazy_unset_bits()?)201}202203/// Returns the number of unset bits on this [`Bitmap`].204///205/// Guaranteed to be `<= self.len()`.206///207/// # Implementation208///209/// This function counts the number of unset bits if it is not already210/// computed. Repeated calls use the cached bitcount.211pub fn unset_bits(&self) -> usize {212self.lazy_unset_bits().unwrap_or_else(|| {213let zeros = count_zeros(&self.storage, self.offset, self.length);214self.unset_bit_count_cache.store(zeros as u64);215zeros216})217}218219/// Returns the number of unset bits on this [`Bitmap`] if it is known.220///221/// Guaranteed to be `<= self.len()`.222pub fn lazy_unset_bits(&self) -> Option<usize> {223let cache = self.unset_bit_count_cache.load();224has_cached_unset_bit_count(cache).then_some(cache as usize)225}226227/// Updates the count of the number of set bits on this [`Bitmap`].228///229/// # Safety230///231/// The number of set bits must be correct.232pub unsafe fn update_bit_count(&mut self, bits_set: usize) {233assert!(bits_set <= self.length);234let zeros = self.length - bits_set;235self.unset_bit_count_cache.store(zeros as u64);236}237238/// Slices `self`, offsetting by `offset` and truncating up to `length` bits.239/// # Panic240/// Panics iff `offset + length > self.length`, i.e. if the offset and `length`241/// exceeds the allocated capacity of `self`.242#[inline]243pub fn slice(&mut self, offset: usize, length: usize) {244assert!(offset + length <= self.length);245unsafe { self.slice_unchecked(offset, length) }246}247248/// Slices `self`, offsetting by `offset` and truncating up to `length` bits.249///250/// # Safety251/// The caller must ensure that `self.offset + offset + length <= self.len()`252#[inline]253pub unsafe fn slice_unchecked(&mut self, offset: usize, length: usize) {254// Fast path: no-op slice.255if offset == 0 && length == self.length {256return;257}258259// Fast path: we have no nulls or are full-null.260let unset_bit_count_cache = self.unset_bit_count_cache.get_mut();261if *unset_bit_count_cache == 0 || *unset_bit_count_cache == self.length as u64 {262let new_count = if *unset_bit_count_cache > 0 {263length as u64264} else {2650266};267*unset_bit_count_cache = new_count;268self.offset += offset;269self.length = length;270return;271}272273if has_cached_unset_bit_count(*unset_bit_count_cache) {274// If we keep all but a small portion of the array it is worth275// doing an eager re-count since we can reuse the old count via the276// inclusion-exclusion principle.277let small_portion = (self.length / 5).max(32);278if length + small_portion >= self.length {279// Subtract the null count of the chunks we slice off.280let slice_end = self.offset + offset + length;281let head_count = count_zeros(&self.storage, self.offset, offset);282let tail_count =283count_zeros(&self.storage, slice_end, self.length - length - offset);284let new_count = *unset_bit_count_cache - head_count as u64 - tail_count as u64;285*unset_bit_count_cache = new_count;286} else {287*unset_bit_count_cache = UNKNOWN_BIT_COUNT;288}289}290291self.offset += offset;292self.length = length;293}294295/// Slices `self`, offsetting by `offset` and truncating up to `length` bits.296/// # Panic297/// Panics iff `offset + length > self.length`, i.e. if the offset and `length`298/// exceeds the allocated capacity of `self`.299#[inline]300#[must_use]301pub fn sliced(self, offset: usize, length: usize) -> Self {302assert!(offset + length <= self.length);303unsafe { self.sliced_unchecked(offset, length) }304}305306/// Slices `self`, offsetting by `offset` and truncating up to `length` bits.307///308/// # Safety309/// The caller must ensure that `self.offset + offset + length <= self.len()`310#[inline]311#[must_use]312pub unsafe fn sliced_unchecked(mut self, offset: usize, length: usize) -> Self {313self.slice_unchecked(offset, length);314self315}316317/// Returns whether the bit at position `i` is set.318/// # Panics319/// Panics iff `i >= self.len()`.320#[inline]321pub fn get_bit(&self, i: usize) -> bool {322assert!(i < self.len());323unsafe { self.get_bit_unchecked(i) }324}325326/// Unsafely returns whether the bit at position `i` is set.327///328/// # Safety329/// Unsound iff `i >= self.len()`.330#[inline]331pub unsafe fn get_bit_unchecked(&self, i: usize) -> bool {332debug_assert!(i < self.len());333get_bit_unchecked(&self.storage, self.offset + i)334}335336/// Returns a pointer to the start of this [`Bitmap`] (ignores `offsets`)337/// This pointer is allocated iff `self.len() > 0`.338pub(crate) fn as_ptr(&self) -> *const u8 {339self.storage.deref().as_ptr()340}341342/// Returns a pointer to the start of this [`Bitmap`] (ignores `offsets`)343/// This pointer is allocated iff `self.len() > 0`.344pub(crate) fn offset(&self) -> usize {345self.offset346}347348/// Converts this [`Bitmap`] to [`MutableBitmap`], returning itself if the conversion349/// is not possible350///351/// This operation returns a [`MutableBitmap`] iff:352/// * this [`Bitmap`] is not an offsetted slice of another [`Bitmap`]353/// * this [`Bitmap`] has not been cloned (i.e. [`Arc`]`::get_mut` yields [`Some`])354/// * this [`Bitmap`] was not imported from the c data interface (FFI)355pub fn into_mut(mut self) -> Either<Self, MutableBitmap> {356match self.storage.try_into_vec() {357Ok(v) => Either::Right(MutableBitmap::from_vec(v, self.length)),358Err(storage) => {359self.storage = storage;360Either::Left(self)361},362}363}364365/// Converts this [`Bitmap`] into a [`MutableBitmap`], cloning its internal366/// buffer if required (clone-on-write).367pub fn make_mut(self) -> MutableBitmap {368match self.into_mut() {369Either::Left(data) => {370if data.offset > 0 {371// re-align the bits (remove the offset)372let chunks = data.chunks::<u64>();373let remainder = chunks.remainder();374let vec = chunk_iter_to_vec(chunks.chain(std::iter::once(remainder)));375MutableBitmap::from_vec(vec, data.length)376} else {377let len = bytes_for(data.length);378MutableBitmap::from_vec(data.storage[0..len].to_vec(), data.length)379}380},381Either::Right(data) => data,382}383}384385/// Initializes an new [`Bitmap`] filled with unset values.386#[inline]387pub fn new_zeroed(length: usize) -> Self {388// We intentionally leak 1MiB of zeroed memory once so we don't have to389// refcount it.390const GLOBAL_ZERO_SIZE: usize = 1024 * 1024;391static GLOBAL_ZEROES: LazyLock<SharedStorage<u8>> = LazyLock::new(|| {392let mut ss = SharedStorage::from_vec(vec![0; GLOBAL_ZERO_SIZE]);393ss.leak();394ss395});396397let bytes_needed = length.div_ceil(8);398let storage = if bytes_needed <= GLOBAL_ZERO_SIZE {399GLOBAL_ZEROES.clone()400} else {401SharedStorage::from_vec(vec![0; bytes_needed])402};403Self {404storage,405offset: 0,406length,407unset_bit_count_cache: RelaxedCell::from(length as u64),408}409}410411/// Initializes an new [`Bitmap`] filled with the given value.412#[inline]413pub fn new_with_value(value: bool, length: usize) -> Self {414if !value {415return Self::new_zeroed(length);416}417418unsafe {419Bitmap::from_inner_unchecked(420SharedStorage::from_vec(vec![u8::MAX; length.saturating_add(7) / 8]),4210,422length,423Some(0),424)425}426}427428/// Counts the nulls (unset bits) starting from `offset` bits and for `length` bits.429#[inline]430pub fn null_count_range(&self, offset: usize, length: usize) -> usize {431count_zeros(&self.storage, self.offset + offset, length)432}433434/// Creates a new [`Bitmap`] from a slice and length.435/// # Panic436/// Panics iff `length > bytes.len() * 8`437#[inline]438pub fn from_u8_slice<T: AsRef<[u8]>>(slice: T, length: usize) -> Self {439Bitmap::try_new(slice.as_ref().to_vec(), length).unwrap()440}441442/// Alias for `Bitmap::try_new().unwrap()`443/// This function is `O(1)`444/// # Panic445/// This function panics iff `length > bytes.len() * 8`446#[inline]447pub fn from_u8_vec(vec: Vec<u8>, length: usize) -> Self {448Bitmap::try_new(vec, length).unwrap()449}450451/// Returns whether the bit at position `i` is set.452#[inline]453pub fn get(&self, i: usize) -> Option<bool> {454if i < self.len() {455Some(unsafe { self.get_bit_unchecked(i) })456} else {457None458}459}460461/// Creates a [`Bitmap`] from its internal representation.462/// This is the inverted from [`Bitmap::into_inner`]463///464/// # Safety465/// Callers must ensure all invariants of this struct are upheld.466pub unsafe fn from_inner_unchecked(467storage: SharedStorage<u8>,468offset: usize,469length: usize,470unset_bits: Option<usize>,471) -> Self {472debug_assert!(check(&storage[..], offset, length).is_ok());473474let unset_bit_count_cache = if let Some(n) = unset_bits {475RelaxedCell::from(n as u64)476} else {477RelaxedCell::from(UNKNOWN_BIT_COUNT)478};479Self {480storage,481offset,482length,483unset_bit_count_cache,484}485}486487/// Checks whether two [`Bitmap`]s have shared set bits.488///489/// This is an optimized version of `(self & other) != 0000..`.490pub fn intersects_with(&self, other: &Self) -> bool {491self.num_intersections_with(other) != 0492}493494/// Calculates the number of shared set bits between two [`Bitmap`]s.495pub fn num_intersections_with(&self, other: &Self) -> usize {496num_intersections_with(self, other)497}498499/// Select between `truthy` and `falsy` based on `self`.500///501/// This essentially performs:502///503/// `out[i] = if self[i] { truthy[i] } else { falsy[i] }`504pub fn select(&self, truthy: &Self, falsy: &Self) -> Self {505super::bitmap_ops::select(self, truthy, falsy)506}507508/// Select between `truthy` and constant `falsy` based on `self`.509///510/// This essentially performs:511///512/// `out[i] = if self[i] { truthy[i] } else { falsy }`513pub fn select_constant(&self, truthy: &Self, falsy: bool) -> Self {514super::bitmap_ops::select_constant(self, truthy, falsy)515}516517/// Calculates the number of edges from `0 -> 1` and `1 -> 0`.518pub fn num_edges(&self) -> usize {519super::bitmap_ops::num_edges(self)520}521522/// Returns the number of zero bits from the start before a one bit is seen523pub fn leading_zeros(&self) -> usize {524utils::leading_zeros(&self.storage, self.offset, self.length)525}526/// Returns the number of one bits from the start before a zero bit is seen527pub fn leading_ones(&self) -> usize {528utils::leading_ones(&self.storage, self.offset, self.length)529}530/// Returns the number of zero bits from the back before a one bit is seen531pub fn trailing_zeros(&self) -> usize {532utils::trailing_zeros(&self.storage, self.offset, self.length)533}534/// Returns the number of one bits from the back before a zero bit is seen535pub fn trailing_ones(&self) -> usize {536utils::trailing_ones(&self.storage, self.offset, self.length)537}538539/// Take all `0` bits at the start of the [`Bitmap`] before a `1` is seen, returning how many540/// bits were taken541pub fn take_leading_zeros(&mut self) -> usize {542if self543.lazy_unset_bits()544.is_some_and(|unset_bits| unset_bits == self.length)545{546let leading_zeros = self.length;547self.offset += self.length;548self.length = 0;549*self.unset_bit_count_cache.get_mut() = 0;550return leading_zeros;551}552553let leading_zeros = self.leading_zeros();554self.offset += leading_zeros;555self.length -= leading_zeros;556if has_cached_unset_bit_count(*self.unset_bit_count_cache.get_mut()) {557*self.unset_bit_count_cache.get_mut() -= leading_zeros as u64;558}559leading_zeros560}561/// Take all `1` bits at the start of the [`Bitmap`] before a `0` is seen, returning how many562/// bits were taken563pub fn take_leading_ones(&mut self) -> usize {564if self565.lazy_unset_bits()566.is_some_and(|unset_bits| unset_bits == 0)567{568let leading_ones = self.length;569self.offset += self.length;570self.length = 0;571*self.unset_bit_count_cache.get_mut() = 0;572return leading_ones;573}574575let leading_ones = self.leading_ones();576self.offset += leading_ones;577self.length -= leading_ones;578// @NOTE: the unset_bit_count_cache remains unchanged579leading_ones580}581/// Take all `0` bits at the back of the [`Bitmap`] before a `1` is seen, returning how many582/// bits were taken583pub fn take_trailing_zeros(&mut self) -> usize {584if self585.lazy_unset_bits()586.is_some_and(|unset_bits| unset_bits == self.length)587{588let trailing_zeros = self.length;589self.length = 0;590*self.unset_bit_count_cache.get_mut() = 0;591return trailing_zeros;592}593594let trailing_zeros = self.trailing_zeros();595self.length -= trailing_zeros;596if has_cached_unset_bit_count(*self.unset_bit_count_cache.get_mut()) {597*self.unset_bit_count_cache.get_mut() -= trailing_zeros as u64;598}599trailing_zeros600}601/// Take all `1` bits at the back of the [`Bitmap`] before a `0` is seen, returning how many602/// bits were taken603pub fn take_trailing_ones(&mut self) -> usize {604if self605.lazy_unset_bits()606.is_some_and(|unset_bits| unset_bits == 0)607{608let trailing_ones = self.length;609self.length = 0;610*self.unset_bit_count_cache.get_mut() = 0;611return trailing_ones;612}613614let trailing_ones = self.trailing_ones();615self.length -= trailing_ones;616// @NOTE: the unset_bit_count_cache remains unchanged617trailing_ones618}619}620621impl<P: AsRef<[bool]>> From<P> for Bitmap {622fn from(slice: P) -> Self {623Self::from_trusted_len_iter(slice.as_ref().iter().copied())624}625}626627impl FromIterator<bool> for Bitmap {628fn from_iter<I>(iter: I) -> Self629where630I: IntoIterator<Item = bool>,631{632MutableBitmap::from_iter(iter).into()633}634}635636impl FromTrustedLenIterator<bool> for Bitmap {637fn from_iter_trusted_length<T: IntoIterator<Item = bool>>(iter: T) -> Self638where639T::IntoIter: TrustedLen,640{641MutableBitmap::from_trusted_len_iter(iter.into_iter()).into()642}643}644645impl Bitmap {646/// Creates a new [`Bitmap`] from an iterator of booleans.647///648/// # Safety649/// The iterator must report an accurate length.650#[inline]651pub unsafe fn from_trusted_len_iter_unchecked<I: Iterator<Item = bool>>(iterator: I) -> Self {652MutableBitmap::from_trusted_len_iter_unchecked(iterator).into()653}654655/// Creates a new [`Bitmap`] from an iterator of booleans.656#[inline]657pub fn from_trusted_len_iter<I: TrustedLen<Item = bool>>(iterator: I) -> Self {658MutableBitmap::from_trusted_len_iter(iterator).into()659}660661/// Creates a new [`Bitmap`] from a fallible iterator of booleans.662#[inline]663pub fn try_from_trusted_len_iter<E, I: TrustedLen<Item = std::result::Result<bool, E>>>(664iterator: I,665) -> std::result::Result<Self, E> {666Ok(MutableBitmap::try_from_trusted_len_iter(iterator)?.into())667}668669/// Creates a new [`Bitmap`] from a fallible iterator of booleans.670///671/// # Safety672/// The iterator must report an accurate length.673#[inline]674pub unsafe fn try_from_trusted_len_iter_unchecked<675E,676I: Iterator<Item = std::result::Result<bool, E>>,677>(678iterator: I,679) -> std::result::Result<Self, E> {680Ok(MutableBitmap::try_from_trusted_len_iter_unchecked(iterator)?.into())681}682}683684impl<'a> IntoIterator for &'a Bitmap {685type Item = bool;686type IntoIter = BitmapIter<'a>;687688fn into_iter(self) -> Self::IntoIter {689BitmapIter::<'a>::new(&self.storage, self.offset, self.length)690}691}692693impl IntoIterator for Bitmap {694type Item = bool;695type IntoIter = IntoIter;696697fn into_iter(self) -> Self::IntoIter {698IntoIter::new(self)699}700}701702impl Splitable for Bitmap {703#[inline(always)]704fn check_bound(&self, offset: usize) -> bool {705offset <= self.len()706}707708unsafe fn _split_at_unchecked(&self, offset: usize) -> (Self, Self) {709if offset == 0 {710return (Self::new(), self.clone());711}712if offset == self.len() {713return (self.clone(), Self::new());714}715716let ubcc = self.unset_bit_count_cache.load();717718let lhs_length = offset;719let rhs_length = self.length - offset;720721let mut lhs_ubcc = UNKNOWN_BIT_COUNT;722let mut rhs_ubcc = UNKNOWN_BIT_COUNT;723724if has_cached_unset_bit_count(ubcc) {725if ubcc == 0 {726lhs_ubcc = 0;727rhs_ubcc = 0;728} else if ubcc == self.length as u64 {729lhs_ubcc = offset as u64;730rhs_ubcc = (self.length - offset) as u64;731} else {732// If we keep all but a small portion of the array it is worth733// doing an eager re-count since we can reuse the old count via the734// inclusion-exclusion principle.735let small_portion = (self.length / 4).max(32);736737if lhs_length <= rhs_length {738if rhs_length + small_portion >= self.length {739let count = count_zeros(&self.storage, self.offset, lhs_length) as u64;740lhs_ubcc = count;741rhs_ubcc = ubcc - count;742}743} else if lhs_length + small_portion >= self.length {744let count = count_zeros(&self.storage, self.offset + offset, rhs_length) as u64;745lhs_ubcc = ubcc - count;746rhs_ubcc = count;747}748}749}750751debug_assert!(lhs_ubcc == UNKNOWN_BIT_COUNT || lhs_ubcc <= ubcc);752debug_assert!(rhs_ubcc == UNKNOWN_BIT_COUNT || rhs_ubcc <= ubcc);753754(755Self {756storage: self.storage.clone(),757offset: self.offset,758length: lhs_length,759unset_bit_count_cache: RelaxedCell::from(lhs_ubcc),760},761Self {762storage: self.storage.clone(),763offset: self.offset + offset,764length: rhs_length,765unset_bit_count_cache: RelaxedCell::from(rhs_ubcc),766},767)768}769}770771772