Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
tower120 authored Jan 24, 2024
1 parent 2b81bb4 commit 77c1be3
Show file tree
Hide file tree
Showing 15 changed files with 171 additions and 191 deletions.
10 changes: 7 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,18 @@
- `BitSetInterface` now have default implementation.
- `BitSet` no longer implements `BitSetInterface`.
But `&BitSet` still does. This prevents accidental sending container by value.
- `config::with_cache::*` moved to `config::*` with additional default generic argument.
- `config::with_cache::*` moved to `config::*` with additional default generic argument.
- `crate::bit_queue` moved to `internals::bit_queue`.
- `crate::Primitive` moved to `internals::Primitive`.

### Added
- `BitBlock::first_u64()`.
- `BitBlock::first_u64_mut()`.
- `BitBlock::as_array()`.
- `BitBlock::as_array_mut()`.
- Some `BitBlock` methods now have default implementations.
- `BitSetOp::HIERARCHY_OPERANDS_CONTAIN_RESULT` marker, for intersection-like
optimization in user-defined operations.
- Machinery, which allows to implement custom bitsets. Enabled with `impl` flag.
- `internals` module, with implementation details that end user can use for deep customization.

### Removed
- `num_traits` dependency removed.
Expand Down
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ exclude = ["/doc", "/.github"]

[features]
default = ["simd"]
# Makes implement module and impl_bitset* macros visible.
# Allows to implement custom bitsets.
# Makes LevelMasks, LevelMasksIterExt and impl_bitset! visible.
# Having them hidden by default prevents your code completion tool
# from showing you irrelevant implementation methods
# (even if you did not import them).
impl = []
# You don't need this. Original legacy iterator.
simple_iter = []
Expand Down
2 changes: 1 addition & 1 deletion examples/custom_bitset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::marker::PhantomData;
use std::mem::{ManuallyDrop, MaybeUninit};
use hi_sparse_bitset::config::Config;
use hi_sparse_bitset::{BitBlock, BitSetBase, impl_bitset};
use hi_sparse_bitset::implement::{LevelMasks, LevelMasksIterExt};
use hi_sparse_bitset::internals::{LevelMasks, LevelMasksIterExt};

#[derive(Default)]
struct Empty<Conf: Config>(PhantomData<Conf>);
Expand Down
2 changes: 1 addition & 1 deletion examples/custom_bitset_simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::marker::PhantomData;
use std::mem::{ManuallyDrop, MaybeUninit};
use hi_sparse_bitset::config::Config;
use hi_sparse_bitset::{BitBlock, BitSetBase, impl_bitset_simple};
use hi_sparse_bitset::implement::LevelMasks;
use hi_sparse_bitset::internals::LevelMasks;

#[derive(Default)]
struct Empty<Conf: Config>(PhantomData<Conf>);
Expand Down
2 changes: 1 addition & 1 deletion src/apply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::mem::{ManuallyDrop, MaybeUninit};
use std::ptr::addr_of_mut;
use crate::ops::*;
use crate::BitSetInterface;
use crate::implement::impl_bitset;
use crate::internals::impl_bitset;
use crate::bitset_interface::{BitSetBase, LevelMasks, LevelMasksIterExt};
use crate::config::Config;

Expand Down
184 changes: 60 additions & 124 deletions src/bit_block.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::mem;
use std::ops::{BitAnd, BitOr, BitXor, ControlFlow};
use crate::bit_utils;
use crate::bit_queue::{ArrayBitQueue, BitQueue, PrimitiveBitQueue};
Expand All @@ -14,45 +15,68 @@ pub trait BitBlock
+ Eq + PartialEq
+ Sized + Copy + Clone
{
/// 2^N bits
const SIZE_POT_EXPONENT: usize;

/// Size in bits
#[inline]
/*const*/ fn size() -> usize {
1 << Self::SIZE_POT_EXPONENT
}

fn zero() -> Self;
fn is_zero(&self) -> bool;

// We could actually use more universal as_array and as_array_mut instead.
// But this seems to be unnecessary now.
fn first_u64(&self) -> &u64;
fn first_u64_mut(&mut self) -> &mut u64;

#[inline]
fn is_zero(&self) -> bool {
self == &Self::zero()
}

/// Returns previous bit
///
/// `bit_index` is guaranteed to be valid
fn set_bit<const BIT: bool>(&mut self, bit_index: usize) -> bool;
#[inline]
fn set_bit<const BIT: bool>(&mut self, bit_index: usize) -> bool {
unsafe{
bit_utils::set_array_bit_unchecked::<BIT, _>(self.as_array_mut(), bit_index)
}
}

/// `bit_index` is guaranteed to be valid
fn get_bit(&self, bit_index: usize) -> bool;
#[inline]
fn get_bit(&self, bit_index: usize) -> bool{
unsafe{
bit_utils::get_array_bit_unchecked(self.as_array(), bit_index)
}
}

// TODO: This can be removed, since there is BitQueue::traverse
// which do the same and perform the same in optimized build.
/// Returns [Break] if traverse was interrupted (`f` returns [Break]).
///
/// [Break]: ControlFlow::Break
#[inline]
fn traverse_bits<F>(&self, f: F) -> ControlFlow<()>
where
F: FnMut(usize) -> ControlFlow<()>;
F: FnMut(usize) -> ControlFlow<()>
{
bit_utils::traverse_array_one_bits(self.as_array(), f)
}

type BitsIter: BitQueue;
fn bits_iter(self) -> Self::BitsIter;

/*type AsArray: AsRef<[u64]>;
fn as_array_u64(&self) -> &Self::AsArray;*/
fn into_bits_iter(self) -> Self::BitsIter;

fn count_ones(&self) -> usize;
fn as_array(&self) -> &[u64];
fn as_array_mut(&mut self) -> &mut [u64];

#[inline]
fn count_ones(&self) -> usize {
let mut sum = 0;
// will be unrolled at compile time
for &i in self.as_array(){
sum += u64::count_ones(i);
}
sum as usize
}
}

impl BitBlock for u64{
Expand All @@ -63,21 +87,6 @@ impl BitBlock for u64{
0
}

#[inline]
fn is_zero(&self) -> bool {
*self == 0
}

#[inline]
fn first_u64(&self) -> &u64 {
self
}

#[inline]
fn first_u64_mut(&mut self) -> &mut u64 {
self
}

#[inline]
fn set_bit<const BIT: bool>(&mut self, bit_index: usize) -> bool{
unsafe{
Expand All @@ -102,26 +111,27 @@ impl BitBlock for u64{

type BitsIter = PrimitiveBitQueue<u64>;
#[inline]
fn bits_iter(self) -> Self::BitsIter {
fn into_bits_iter(self) -> Self::BitsIter {
PrimitiveBitQueue::new(self)
}

/*type AsArray = [u64; 1];
#[inline]
fn as_array_u64(&self) -> &Self::AsArray {
fn as_array(&self) -> &[u64] {
unsafe {
mem::transmute::<&u64, &[u64; 1]>(self)
}
}*/
}
}

#[inline]
fn count_ones(&self) -> usize {
u64::count_ones(*self) as usize
fn as_array_mut(&mut self) -> &mut [u64] {
unsafe {
mem::transmute::<&mut u64, &mut [u64; 1]>(self)
}
}
}

#[cfg(feature = "simd")]
#[cfg_attr(docsrs, doc(cfg(feature = "simd")))]
impl BitBlock for wide::u64x2{
const SIZE_POT_EXPONENT: usize = 7;

Expand All @@ -138,62 +148,25 @@ impl BitBlock for wide::u64x2{
(array[0] | array[1]) == 0
}

#[inline]
fn first_u64(&self) -> &u64 {
unsafe{ self.as_array_ref().get_unchecked(0) }
}

#[inline]
fn first_u64_mut(&mut self) -> &mut u64 {
unsafe{ self.as_array_mut().get_unchecked_mut(0) }
}

#[inline]
fn set_bit<const BIT: bool>(&mut self, bit_index: usize) -> bool {
unsafe{
bit_utils::set_array_bit_unchecked::<BIT, _>(self.as_array_mut(), bit_index)
}
}

#[inline]
fn get_bit(&self, bit_index: usize) -> bool {
unsafe{
bit_utils::get_array_bit_unchecked(self.as_array_ref(), bit_index)
}
}

#[inline]
fn traverse_bits<F>(&self, f: F) -> ControlFlow<()>
where
F: FnMut(usize) -> ControlFlow<()>
{
let array = self.as_array_ref();
bit_utils::traverse_array_one_bits(array, f)
}

type BitsIter = ArrayBitQueue<u64, 2>;
#[inline]
fn bits_iter(self) -> Self::BitsIter {
fn into_bits_iter(self) -> Self::BitsIter {
Self::BitsIter::new(self.to_array())
}

/*type AsArray = [u64; 2];
#[inline]
fn as_array_u64(&self) -> &[u64; 2] {
fn as_array(&self) -> &[u64] {
self.as_array_ref()
}*/
}

#[inline]
fn count_ones(&self) -> usize {
// TODO: there is faster solutions for this http://0x80.pl/articles/sse-popcount.html
let primitives = self.as_array_ref();
let len = primitives[0].count_ones() + primitives[1].count_ones();
len as usize
fn as_array_mut(&mut self) -> &mut [u64] {
self.as_array_mut()
}
}

#[cfg(feature = "simd")]
#[cfg_attr(docsrs, doc(cfg(feature = "simd")))]
impl BitBlock for wide::u64x4{
const SIZE_POT_EXPONENT: usize = 8;

Expand All @@ -202,56 +175,19 @@ impl BitBlock for wide::u64x4{
wide::u64x4::ZERO
}

type BitsIter = ArrayBitQueue<u64, 4>;
#[inline]
fn is_zero(&self) -> bool {
*self == Self::zero()
}

#[inline]
fn first_u64(&self) -> &u64 {
unsafe{ self.as_array_ref().get_unchecked(0) }
}

#[inline]
fn first_u64_mut(&mut self) -> &mut u64 {
unsafe{ self.as_array_mut().get_unchecked_mut(0) }
}

#[inline]
fn set_bit<const BIT: bool>(&mut self, bit_index: usize) -> bool {
unsafe{
bit_utils::set_array_bit_unchecked::<BIT, _>(self.as_array_mut(), bit_index)
}
}

#[inline]
fn get_bit(&self, bit_index: usize) -> bool {
unsafe{
bit_utils::get_array_bit_unchecked(self.as_array_ref(), bit_index)
}
}

#[inline]
fn traverse_bits<F>(&self, f: F) -> ControlFlow<()>
where
F: FnMut(usize) -> ControlFlow<()>
{
let array = self.as_array_ref();
bit_utils::traverse_array_one_bits(array, f)
fn into_bits_iter(self) -> Self::BitsIter {
Self::BitsIter::new(self.to_array())
}

type BitsIter = ArrayBitQueue<u64, 4>;
#[inline]
fn bits_iter(self) -> Self::BitsIter {
Self::BitsIter::new(self.to_array())
fn as_array(&self) -> &[u64] {
self.as_array_ref()
}

#[inline]
fn count_ones(&self) -> usize {
// TODO: there is faster solutions for this http://0x80.pl/articles/sse-popcount.html
let primitives = self.as_array_ref();
let len = primitives[0].count_ones() + primitives[1].count_ones()
+ primitives[2].count_ones() + primitives[3].count_ones();
len as usize
fn as_array_mut(&mut self) -> &mut [u64] {
self.as_array_mut()
}
}
4 changes: 0 additions & 4 deletions src/bit_queue.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
//! Bit iterator for [BitBlock].
//!
//! [BitBlock]: crate::BitBlock

use std::mem;
use std::mem::{ManuallyDrop, size_of};
use std::ops::ControlFlow;
Expand Down
2 changes: 1 addition & 1 deletion src/bitset_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ impl<'a, T: LevelMasksIterExt> LevelMasksIterExt for &'a T {
///
/// [CachingBlockIter]: crate::iter::CachingBlockIter
/// [CachingIndexIter]: crate::iter::CachingIndexIter
/// [LevelMasksIterExt]: crate::implement::LevelMasksIterExt
/// [LevelMasksIterExt]: crate::internals::LevelMasksIterExt
/// [impl_bitset!]: crate::impl_bitset!
/// [apply]: crate::apply()
/// [reduce]: crate::reduce()
Expand Down
2 changes: 2 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ impl<DefaultCache: ReduceCache> Config for _64bit<DefaultCache> {
#[derive(Default)]
pub struct _128bit<DefaultCache: ReduceCache = self::DefaultCache>(PhantomData<DefaultCache>);
#[cfg(feature = "simd")]
#[cfg_attr(docsrs, doc(cfg(feature = "simd")))]
impl<DefaultCache: ReduceCache> Config for _128bit<DefaultCache> {
type Level0BitBlock = wide::u64x2;
type Level0BlockIndices = [u8; 128];
Expand All @@ -140,6 +141,7 @@ impl<DefaultCache: ReduceCache> Config for _128bit<DefaultCache> {
#[derive(Default)]
pub struct _256bit<DefaultCache: ReduceCache = self::DefaultCache>(PhantomData<DefaultCache>);
#[cfg(feature = "simd")]
#[cfg_attr(docsrs, doc(cfg(feature = "simd")))]
impl<DefaultCache: ReduceCache> Config for _256bit<DefaultCache> {
type Level0BitBlock = wide::u64x4;
type Level0BlockIndices = [u8; 256];
Expand Down
Loading

0 comments on commit 77c1be3

Please sign in to comment.