Path: blob/main/crates/polars-arrow/src/bitmap/immutable.rs
8406 views
#![allow(unsafe_op_in_unsafe_fn)]1use std::ops::Deref;23use either::Either;4use polars_buffer::{Buffer, SharedStorage};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::trusted_len::TrustedLen;1819const UNKNOWN_BIT_COUNT: u64 = u64::MAX;2021/// An immutable container semantically equivalent to `Arc<Vec<bool>>` but represented as `Arc<Vec<u8>>` where22/// each boolean is represented as a single bit.23///24/// # Examples25/// ```26/// use polars_arrow::bitmap::{Bitmap, MutableBitmap};27///28/// let bitmap = Bitmap::from([true, false, true]);29/// assert_eq!(bitmap.iter().collect::<Vec<_>>(), vec![true, false, true]);30///31/// // creation directly from bytes32/// let bitmap = Bitmap::try_new(vec![0b00001101], 5).unwrap();33/// // note: the first bit is the left-most of the first byte34/// assert_eq!(bitmap.iter().collect::<Vec<_>>(), vec![true, false, true, true, false]);35/// // we can also get the slice:36/// assert_eq!(bitmap.as_slice(), ([0b00001101u8].as_ref(), 0, 5));37/// // debug helps :)38/// assert_eq!(format!("{:?}", bitmap), "Bitmap { len: 5, offset: 0, bytes: [0b___01101] }");39///40/// // it supports copy-on-write semantics (to a `MutableBitmap`)41/// let bitmap: MutableBitmap = bitmap.into_mut().right().unwrap();42/// assert_eq!(bitmap, MutableBitmap::from([true, false, true, true, false]));43///44/// // slicing is 'O(1)' (data is shared)45/// let bitmap = Bitmap::try_new(vec![0b00001101], 5).unwrap();46/// let mut sliced = bitmap.clone();47/// sliced.slice(1, 4);48/// assert_eq!(sliced.as_slice(), ([0b00001101u8].as_ref(), 1, 4)); // 1 here is the offset:49/// assert_eq!(format!("{:?}", sliced), "Bitmap { len: 4, offset: 1, bytes: [0b___0110_] }");50/// // when sliced (or cloned), it is no longer possible to `into_mut`.51/// let same: Bitmap = sliced.into_mut().left().unwrap();52/// ```53#[derive(Default, Clone)]54pub struct Bitmap {55storage: SharedStorage<u8>,56// Both offset and length are measured in bits. They are used to bound the57// bitmap to a region of Bytes.58offset: usize,59length: usize,6061// A bit field that contains our cache for the number of unset bits.62// If it is u64::MAX, we have no known value at all.63// Other bit patterns where the top bit is set is reserved for future use.64// If the top bit is not set we have an exact count.65unset_bit_count_cache: RelaxedCell<u64>,66}6768#[inline(always)]69fn has_cached_unset_bit_count(ubcc: u64) -> bool {70ubcc >> 63 == 071}7273impl std::fmt::Debug for Bitmap {74fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {75let (bytes, offset, len) = self.as_slice();76fmt(bytes, offset, len, f)77}78}7980pub(super) fn check(bytes: &[u8], offset: usize, length: usize) -> PolarsResult<()> {81if offset + length > bytes.len().saturating_mul(8) {82polars_bail!(InvalidOperation:83"The offset + length of the bitmap ({}) must be `<=` to the number of bytes times 8 ({})",84offset + length,85bytes.len().saturating_mul(8)86);87}88Ok(())89}9091impl Bitmap {92/// Initializes an empty [`Bitmap`].93#[inline]94pub fn new() -> Self {95Self::default()96}9798/// Initializes a new [`Bitmap`] from vector of bytes and a length.99/// # Errors100/// This function errors iff `length > bytes.len() * 8`101#[inline]102pub fn try_new(bytes: Vec<u8>, length: usize) -> PolarsResult<Self> {103check(&bytes, 0, length)?;104Ok(Self {105storage: SharedStorage::from_vec(bytes),106length,107offset: 0,108unset_bit_count_cache: RelaxedCell::from(if length == 0 {1090110} else {111UNKNOWN_BIT_COUNT112}),113})114}115116/// Returns the length of the [`Bitmap`].117#[inline]118pub fn len(&self) -> usize {119self.length120}121122/// Returns whether [`Bitmap`] is empty123#[inline]124pub fn is_empty(&self) -> bool {125self.len() == 0126}127128/// Returns a new iterator of `bool` over this bitmap129pub fn iter(&self) -> BitmapIter<'_> {130BitmapIter::new(&self.storage, self.offset, self.length)131}132133/// Returns an iterator over bits in bit chunks [`BitChunk`].134///135/// This iterator is useful to operate over multiple bits via e.g. bitwise.136pub fn chunks<T: BitChunk>(&self) -> BitChunks<'_, T> {137BitChunks::new(&self.storage, self.offset, self.length)138}139140/// Returns a fast iterator that gives 32 bits at a time.141/// Has a remainder that must be handled separately.142pub fn fast_iter_u32(&self) -> FastU32BitmapIter<'_> {143FastU32BitmapIter::new(&self.storage, self.offset, self.length)144}145146/// Returns a fast iterator that gives 56 bits at a time.147/// Has a remainder that must be handled separately.148pub fn fast_iter_u56(&self) -> FastU56BitmapIter<'_> {149FastU56BitmapIter::new(&self.storage, self.offset, self.length)150}151152/// Returns a fast iterator that gives 64 bits at a time.153/// Has a remainder that must be handled separately.154pub fn fast_iter_u64(&self) -> FastU64BitmapIter<'_> {155FastU64BitmapIter::new(&self.storage, self.offset, self.length)156}157158/// Returns an iterator that only iterates over the set bits.159pub fn true_idx_iter(&self) -> TrueIdxIter<'_> {160TrueIdxIter::new(self.len(), Some(self))161}162163/// Returns the bits of this [`Bitmap`] as a [`AlignedBitmapSlice`].164pub fn aligned<T: BitChunk>(&self) -> AlignedBitmapSlice<'_, T> {165AlignedBitmapSlice::new(&self.storage, self.offset, self.length)166}167168/// Returns the byte slice of this [`Bitmap`].169///170/// The returned tuple contains:171/// * `.1`: The byte slice, truncated to the start of the first bit. So the start of the slice172/// is within the first 8 bits.173/// * `.2`: The start offset in bits on a range `0 <= offsets < 8`.174/// * `.3`: The length in number of bits.175#[inline]176pub fn as_slice(&self) -> (&[u8], usize, usize) {177let start = self.offset / 8;178let len = (self.offset % 8 + self.length).saturating_add(7) / 8;179(180&self.storage[start..start + len],181self.offset % 8,182self.length,183)184}185186/// Returns the number of set bits on this [`Bitmap`].187///188/// See `unset_bits` for details.189#[inline]190pub fn set_bits(&self) -> usize {191self.length - self.unset_bits()192}193194/// Returns the number of set bits on this [`Bitmap`] if it is known.195///196/// See `lazy_unset_bits` for details.197#[inline]198pub fn lazy_set_bits(&self) -> Option<usize> {199Some(self.length - self.lazy_unset_bits()?)200}201202/// Returns the number of unset bits on this [`Bitmap`].203///204/// Guaranteed to be `<= self.len()`.205///206/// # Implementation207///208/// This function counts the number of unset bits if it is not already209/// computed. Repeated calls use the cached bitcount.210pub fn unset_bits(&self) -> usize {211self.lazy_unset_bits().unwrap_or_else(|| {212let zeros = count_zeros(&self.storage, self.offset, self.length);213self.unset_bit_count_cache.store(zeros as u64);214zeros215})216}217218/// Returns the number of unset bits on this [`Bitmap`] if it is known.219///220/// Guaranteed to be `<= self.len()`.221pub fn lazy_unset_bits(&self) -> Option<usize> {222let cache = self.unset_bit_count_cache.load();223has_cached_unset_bit_count(cache).then_some(cache as usize)224}225226/// Updates the count of the number of set bits on this [`Bitmap`].227///228/// # Safety229///230/// The number of set bits must be correct.231pub unsafe fn update_bit_count(&mut self, bits_set: usize) {232assert!(bits_set <= self.length);233let zeros = self.length - bits_set;234self.unset_bit_count_cache.store(zeros as u64);235}236237/// Slices `self`, offsetting by `offset` and truncating up to `length` bits.238/// # Panic239/// Panics iff `offset + length > self.length`, i.e. if the offset and `length`240/// exceeds the allocated capacity of `self`.241#[inline]242pub fn slice(&mut self, offset: usize, length: usize) {243assert!(offset + length <= self.length);244unsafe { self.slice_unchecked(offset, length) }245}246247/// Slices `self`, offsetting by `offset` and truncating up to `length` bits.248///249/// # Safety250/// The caller must ensure that `self.offset + offset + length <= self.len()`251#[inline]252pub unsafe fn slice_unchecked(&mut self, offset: usize, length: usize) {253// Fast path: no-op slice.254if offset == 0 && length == self.length {255return;256}257258// Fast path: we have no nulls or are full-null.259let unset_bit_count_cache = self.unset_bit_count_cache.get_mut();260if *unset_bit_count_cache == 0 || *unset_bit_count_cache == self.length as u64 {261let new_count = if *unset_bit_count_cache > 0 {262length as u64263} else {2640265};266*unset_bit_count_cache = new_count;267self.offset += offset;268self.length = length;269return;270}271272if has_cached_unset_bit_count(*unset_bit_count_cache) {273// If we keep all but a small portion of the array it is worth274// doing an eager re-count since we can reuse the old count via the275// inclusion-exclusion principle.276let small_portion = (self.length / 5).max(32);277if length + small_portion >= self.length {278// Subtract the null count of the chunks we slice off.279let slice_end = self.offset + offset + length;280let head_count = count_zeros(&self.storage, self.offset, offset);281let tail_count =282count_zeros(&self.storage, slice_end, self.length - length - offset);283let new_count = *unset_bit_count_cache - head_count as u64 - tail_count as u64;284*unset_bit_count_cache = new_count;285} else {286*unset_bit_count_cache = UNKNOWN_BIT_COUNT;287}288}289290self.offset += offset;291self.length = length;292}293294/// Slices `self`, offsetting by `offset` and truncating up to `length` bits.295/// # Panic296/// Panics iff `offset + length > self.length`, i.e. if the offset and `length`297/// exceeds the allocated capacity of `self`.298#[inline]299#[must_use]300pub fn sliced(self, offset: usize, length: usize) -> Self {301assert!(offset + length <= self.length);302unsafe { self.sliced_unchecked(offset, length) }303}304305/// Slices `self`, offsetting by `offset` and truncating up to `length` bits.306///307/// # Safety308/// The caller must ensure that `self.offset + offset + length <= self.len()`309#[inline]310#[must_use]311pub unsafe fn sliced_unchecked(mut self, offset: usize, length: usize) -> Self {312self.slice_unchecked(offset, length);313self314}315316/// Returns whether the bit at position `i` is set.317/// # Panics318/// Panics iff `i >= self.len()`.319#[inline]320pub fn get_bit(&self, i: usize) -> bool {321assert!(i < self.len());322unsafe { self.get_bit_unchecked(i) }323}324325/// Unsafely returns whether the bit at position `i` is set.326///327/// # Safety328/// Unsound iff `i >= self.len()`.329#[inline]330pub unsafe fn get_bit_unchecked(&self, i: usize) -> bool {331debug_assert!(i < self.len());332get_bit_unchecked(&self.storage, self.offset + i)333}334335/// Returns a pointer to the start of this [`Bitmap`] (ignores `offsets`)336/// This pointer is allocated iff `self.len() > 0`.337pub(crate) fn as_ptr(&self) -> *const u8 {338self.storage.deref().as_ptr()339}340341/// Returns a pointer to the start of this [`Bitmap`] (ignores `offsets`)342/// This pointer is allocated iff `self.len() > 0`.343pub(crate) fn offset(&self) -> usize {344self.offset345}346347/// Converts this [`Bitmap`] to [`MutableBitmap`], returning itself if the conversion348/// is not possible349///350/// This operation returns a [`MutableBitmap`] iff:351/// * this [`Bitmap`] is not an offsetted slice of another [`Bitmap`]352/// * this [`Bitmap`] has not been cloned (i.e. [`Arc`]`::get_mut` yields [`Some`])353/// * this [`Bitmap`] was not imported from the c data interface (FFI)354pub fn into_mut(mut self) -> Either<Self, MutableBitmap> {355match self.storage.try_into_vec() {356Ok(v) => Either::Right(MutableBitmap::from_vec(v, self.length)),357Err(storage) => {358self.storage = storage;359Either::Left(self)360},361}362}363364/// Converts this [`Bitmap`] into a [`MutableBitmap`], cloning its internal365/// buffer if required (clone-on-write).366pub fn make_mut(self) -> MutableBitmap {367match self.into_mut() {368Either::Left(data) => {369if data.offset > 0 {370// re-align the bits (remove the offset)371let chunks = data.chunks::<u64>();372let remainder = chunks.remainder();373let vec = chunk_iter_to_vec(chunks.chain(std::iter::once(remainder)));374MutableBitmap::from_vec(vec, data.length)375} else {376let len = bytes_for(data.length);377MutableBitmap::from_vec(data.storage[0..len].to_vec(), data.length)378}379},380Either::Right(data) => data,381}382}383384/// Initializes an new [`Bitmap`] filled with unset values.385#[inline]386pub fn new_zeroed(length: usize) -> Self {387let bytes_needed = length.div_ceil(8);388let storage = Buffer::zeroed(bytes_needed).into_storage();389Self {390storage,391offset: 0,392length,393unset_bit_count_cache: RelaxedCell::from(length as u64),394}395}396397/// Initializes an new [`Bitmap`] filled with the given value.398#[inline]399pub fn new_with_value(value: bool, length: usize) -> Self {400if !value {401return Self::new_zeroed(length);402}403404unsafe {405Bitmap::from_inner_unchecked(406SharedStorage::from_vec(vec![u8::MAX; length.saturating_add(7) / 8]),4070,408length,409Some(0),410)411}412}413414/// Counts the nulls (unset bits) starting from `offset` bits and for `length` bits.415#[inline]416pub fn null_count_range(&self, offset: usize, length: usize) -> usize {417count_zeros(&self.storage, self.offset + offset, length)418}419420/// Creates a new [`Bitmap`] from a slice and length.421/// # Panic422/// Panics iff `length > bytes.len() * 8`423#[inline]424pub fn from_u8_slice<T: AsRef<[u8]>>(slice: T, length: usize) -> Self {425Bitmap::try_new(slice.as_ref().to_vec(), length).unwrap()426}427428/// Alias for `Bitmap::try_new().unwrap()`429/// This function is `O(1)`430/// # Panic431/// This function panics iff `length > bytes.len() * 8`432#[inline]433pub fn from_u8_vec(vec: Vec<u8>, length: usize) -> Self {434Bitmap::try_new(vec, length).unwrap()435}436437/// Returns whether the bit at position `i` is set.438#[inline]439pub fn get(&self, i: usize) -> Option<bool> {440if i < self.len() {441Some(unsafe { self.get_bit_unchecked(i) })442} else {443None444}445}446447/// Creates a [`Bitmap`] from its internal representation.448/// This is the inverted from [`Bitmap::into_inner`]449///450/// # Safety451/// Callers must ensure all invariants of this struct are upheld.452pub unsafe fn from_inner_unchecked(453storage: SharedStorage<u8>,454offset: usize,455length: usize,456unset_bits: Option<usize>,457) -> Self {458debug_assert!(check(&storage[..], offset, length).is_ok());459460let unset_bit_count_cache = if let Some(n) = unset_bits {461RelaxedCell::from(n as u64)462} else {463RelaxedCell::from(UNKNOWN_BIT_COUNT)464};465Self {466storage,467offset,468length,469unset_bit_count_cache,470}471}472473/// Checks whether two [`Bitmap`]s have shared set bits.474///475/// This is an optimized version of `(self & other) != 0000..`.476pub fn intersects_with(&self, other: &Self) -> bool {477self.num_intersections_with(other) != 0478}479480/// Calculates the number of shared set bits between two [`Bitmap`]s.481pub fn num_intersections_with(&self, other: &Self) -> usize {482num_intersections_with(483super::bitmask::BitMask::from_bitmap(self),484super::bitmask::BitMask::from_bitmap(other),485)486}487488/// Select between `truthy` and `falsy` based on `self`.489///490/// This essentially performs:491///492/// `out[i] = if self[i] { truthy[i] } else { falsy[i] }`493pub fn select(&self, truthy: &Self, falsy: &Self) -> Self {494super::bitmap_ops::select(self, truthy, falsy)495}496497/// Select between `truthy` and constant `falsy` based on `self`.498///499/// This essentially performs:500///501/// `out[i] = if self[i] { truthy[i] } else { falsy }`502pub fn select_constant(&self, truthy: &Self, falsy: bool) -> Self {503super::bitmap_ops::select_constant(self, truthy, falsy)504}505506/// Calculates the number of edges from `0 -> 1` and `1 -> 0`.507pub fn num_edges(&self) -> usize {508super::bitmap_ops::num_edges(self)509}510511/// Returns the number of zero bits from the start before a one bit is seen512pub fn leading_zeros(&self) -> usize {513utils::leading_zeros(&self.storage, self.offset, self.length)514}515/// Returns the number of one bits from the start before a zero bit is seen516pub fn leading_ones(&self) -> usize {517utils::leading_ones(&self.storage, self.offset, self.length)518}519/// Returns the number of zero bits from the back before a one bit is seen520pub fn trailing_zeros(&self) -> usize {521utils::trailing_zeros(&self.storage, self.offset, self.length)522}523/// Returns the number of one bits from the back before a zero bit is seen524pub fn trailing_ones(&self) -> usize {525utils::trailing_ones(&self.storage, self.offset, self.length)526}527528/// Take all `0` bits at the start of the [`Bitmap`] before a `1` is seen, returning how many529/// bits were taken530pub fn take_leading_zeros(&mut self) -> usize {531if self532.lazy_unset_bits()533.is_some_and(|unset_bits| unset_bits == self.length)534{535let leading_zeros = self.length;536self.offset += self.length;537self.length = 0;538*self.unset_bit_count_cache.get_mut() = 0;539return leading_zeros;540}541542let leading_zeros = self.leading_zeros();543self.offset += leading_zeros;544self.length -= leading_zeros;545if has_cached_unset_bit_count(*self.unset_bit_count_cache.get_mut()) {546*self.unset_bit_count_cache.get_mut() -= leading_zeros as u64;547}548leading_zeros549}550/// Take all `1` bits at the start of the [`Bitmap`] before a `0` is seen, returning how many551/// bits were taken552pub fn take_leading_ones(&mut self) -> usize {553if self554.lazy_unset_bits()555.is_some_and(|unset_bits| unset_bits == 0)556{557let leading_ones = self.length;558self.offset += self.length;559self.length = 0;560*self.unset_bit_count_cache.get_mut() = 0;561return leading_ones;562}563564let leading_ones = self.leading_ones();565self.offset += leading_ones;566self.length -= leading_ones;567// @NOTE: the unset_bit_count_cache remains unchanged568leading_ones569}570/// Take all `0` bits at the back of the [`Bitmap`] before a `1` is seen, returning how many571/// bits were taken572pub fn take_trailing_zeros(&mut self) -> usize {573if self574.lazy_unset_bits()575.is_some_and(|unset_bits| unset_bits == self.length)576{577let trailing_zeros = self.length;578self.length = 0;579*self.unset_bit_count_cache.get_mut() = 0;580return trailing_zeros;581}582583let trailing_zeros = self.trailing_zeros();584self.length -= trailing_zeros;585if has_cached_unset_bit_count(*self.unset_bit_count_cache.get_mut()) {586*self.unset_bit_count_cache.get_mut() -= trailing_zeros as u64;587}588trailing_zeros589}590/// Take all `1` bits at the back of the [`Bitmap`] before a `0` is seen, returning how many591/// bits were taken592pub fn take_trailing_ones(&mut self) -> usize {593if self594.lazy_unset_bits()595.is_some_and(|unset_bits| unset_bits == 0)596{597let trailing_ones = self.length;598self.length = 0;599*self.unset_bit_count_cache.get_mut() = 0;600return trailing_ones;601}602603let trailing_ones = self.trailing_ones();604self.length -= trailing_ones;605// @NOTE: the unset_bit_count_cache remains unchanged606trailing_ones607}608}609610impl<P: AsRef<[bool]>> From<P> for Bitmap {611fn from(slice: P) -> Self {612Self::from_trusted_len_iter(slice.as_ref().iter().copied())613}614}615616impl FromIterator<bool> for Bitmap {617fn from_iter<I>(iter: I) -> Self618where619I: IntoIterator<Item = bool>,620{621MutableBitmap::from_iter(iter).into()622}623}624625impl FromTrustedLenIterator<bool> for Bitmap {626fn from_iter_trusted_length<T: IntoIterator<Item = bool>>(iter: T) -> Self627where628T::IntoIter: TrustedLen,629{630MutableBitmap::from_trusted_len_iter(iter.into_iter()).into()631}632}633634impl Bitmap {635/// Creates a new [`Bitmap`] from an iterator of booleans.636///637/// # Safety638/// The iterator must report an accurate length.639#[inline]640pub unsafe fn from_trusted_len_iter_unchecked<I: Iterator<Item = bool>>(iterator: I) -> Self {641MutableBitmap::from_trusted_len_iter_unchecked(iterator).into()642}643644/// Creates a new [`Bitmap`] from an iterator of booleans.645#[inline]646pub fn from_trusted_len_iter<I: TrustedLen<Item = bool>>(iterator: I) -> Self {647MutableBitmap::from_trusted_len_iter(iterator).into()648}649650/// Creates a new [`Bitmap`] from a fallible iterator of booleans.651#[inline]652pub fn try_from_trusted_len_iter<E, I: TrustedLen<Item = std::result::Result<bool, E>>>(653iterator: I,654) -> std::result::Result<Self, E> {655Ok(MutableBitmap::try_from_trusted_len_iter(iterator)?.into())656}657658/// Creates a new [`Bitmap`] from a fallible iterator of booleans.659///660/// # Safety661/// The iterator must report an accurate length.662#[inline]663pub unsafe fn try_from_trusted_len_iter_unchecked<664E,665I: Iterator<Item = std::result::Result<bool, E>>,666>(667iterator: I,668) -> std::result::Result<Self, E> {669Ok(MutableBitmap::try_from_trusted_len_iter_unchecked(iterator)?.into())670}671}672673impl<'a> IntoIterator for &'a Bitmap {674type Item = bool;675type IntoIter = BitmapIter<'a>;676677fn into_iter(self) -> Self::IntoIter {678BitmapIter::<'a>::new(&self.storage, self.offset, self.length)679}680}681682impl IntoIterator for Bitmap {683type Item = bool;684type IntoIter = IntoIter;685686fn into_iter(self) -> Self::IntoIter {687IntoIter::new(self)688}689}690691impl Splitable for Bitmap {692#[inline(always)]693fn check_bound(&self, offset: usize) -> bool {694offset <= self.len()695}696697unsafe fn _split_at_unchecked(&self, offset: usize) -> (Self, Self) {698if offset == 0 {699return (Self::new(), self.clone());700}701if offset == self.len() {702return (self.clone(), Self::new());703}704705let ubcc = self.unset_bit_count_cache.load();706707let lhs_length = offset;708let rhs_length = self.length - offset;709710let mut lhs_ubcc = UNKNOWN_BIT_COUNT;711let mut rhs_ubcc = UNKNOWN_BIT_COUNT;712713if has_cached_unset_bit_count(ubcc) {714if ubcc == 0 {715lhs_ubcc = 0;716rhs_ubcc = 0;717} else if ubcc == self.length as u64 {718lhs_ubcc = offset as u64;719rhs_ubcc = (self.length - offset) as u64;720} else {721// If we keep all but a small portion of the array it is worth722// doing an eager re-count since we can reuse the old count via the723// inclusion-exclusion principle.724let small_portion = (self.length / 4).max(32);725726if lhs_length <= rhs_length {727if rhs_length + small_portion >= self.length {728let count = count_zeros(&self.storage, self.offset, lhs_length) as u64;729lhs_ubcc = count;730rhs_ubcc = ubcc - count;731}732} else if lhs_length + small_portion >= self.length {733let count = count_zeros(&self.storage, self.offset + offset, rhs_length) as u64;734lhs_ubcc = ubcc - count;735rhs_ubcc = count;736}737}738}739740debug_assert!(lhs_ubcc == UNKNOWN_BIT_COUNT || lhs_ubcc <= ubcc);741debug_assert!(rhs_ubcc == UNKNOWN_BIT_COUNT || rhs_ubcc <= ubcc);742743(744Self {745storage: self.storage.clone(),746offset: self.offset,747length: lhs_length,748unset_bit_count_cache: RelaxedCell::from(lhs_ubcc),749},750Self {751storage: self.storage.clone(),752offset: self.offset + offset,753length: rhs_length,754unset_bit_count_cache: RelaxedCell::from(rhs_ubcc),755},756)757}758}759760761