1+ //! A library for dynamically sized bitsets with small storage optimization.
2+ //!
3+ //! All values up to `usize::MAX >> 1` are stored without incurring any heap allocations.\
4+ //! Any larger values dynamically allocate an appropriately sized `u32` slice on the heap.\
5+ //! [`SmolBitSet`] also has a niche optimization so [`Option<SmolBitSet>`] and [`SmolBitSet`] have the same size of 1 [`usize`].
6+
7+ #![ doc( html_root_url = "https://docs.rs/smolbitset/*" ) ]
18#![ allow( dead_code) ]
29#![ cfg_attr( not( feature = "std" ) , no_std) ]
310
@@ -23,7 +30,7 @@ use core::ptr::{self, NonNull};
2330
2431/// Returns the index of the most significant bit set to 1 in the given data.
2532///
26- /// Note: the least significant bit is at index 1!
33+ /// The least significant bit is at index 1!
2734macro_rules! highest_set_bit {
2835 ( $t: ty, $val: expr) => {
2936 ( <$t>:: BITS - $val. leading_zeros( ) ) as usize
@@ -49,13 +56,95 @@ pub struct SmolBitSet {
4956}
5057
5158impl SmolBitSet {
59+ /// Creates a new empty [`SmolBitSet`].
60+ ///
61+ /// # Examples
62+ ///
63+ /// ```
64+ /// use smolbitset::SmolBitSet;
65+ ///
66+ /// # #[allow(unused_mut)]
67+ /// let mut sbs = SmolBitSet::new();
68+ /// ```
5269 #[ must_use]
5370 #[ inline]
5471 pub const fn new ( ) -> Self {
5572 let ptr = unsafe { NonNull :: new_unchecked ( ptr:: without_provenance_mut ( 0b1 ) ) } ;
73+
74+ // #![feature(nonnull_provenance)] -> https://github.com/rust-lang/rust/issues/135243
75+ // let ptr = NonNull::without_provenance(core::num::NonZero::<usize>::MIN);
76+
5677 Self { ptr }
5778 }
5879
80+ /// Creates a new [`SmolBitSet`] from the provided `val` without any heap allocation.
81+ ///
82+ /// # Panics
83+ ///
84+ /// Panics if the most significant bit in `val` is set to 1.
85+ ///
86+ /// # Examples
87+ ///
88+ /// ```
89+ /// use smolbitset::SmolBitSet;
90+ ///
91+ /// const sbs: SmolBitSet = SmolBitSet::new_small(1234);
92+ /// assert_eq!(sbs, SmolBitSet::from(1234u16));
93+ /// ```
94+ #[ must_use]
95+ pub const fn new_small ( val : usize ) -> Self {
96+ assert ! ( val. leading_zeros( ) >= 1 , "the highest bit in val must be 0" ) ;
97+
98+ let mut res = Self :: new ( ) ;
99+ unsafe {
100+ res. write_inline_data_unchecked ( val) ;
101+ }
102+
103+ res
104+ }
105+
106+ /// Creates a new [`SmolBitSet`] from the provided slice of `bits`.
107+ ///
108+ /// # Examples
109+ ///
110+ /// ```
111+ /// use smolbitset::SmolBitSet;
112+ ///
113+ /// let sbs = SmolBitSet::from_bits(&[0, 4, 3, 6]);
114+ /// assert_eq!(sbs, SmolBitSet::from(0b0101_1001u8));
115+ /// # let sbs = SmolBitSet::from_bits(&[63]);
116+ /// # assert_eq!(sbs, SmolBitSet::from(1u64 << 63));
117+ /// ```
118+ #[ must_use]
119+ pub fn from_bits ( bits : & [ usize ] ) -> Self {
120+ let Some ( hb) = bits. iter ( ) . copied ( ) . max ( ) else {
121+ return Self :: new ( ) ;
122+ } ;
123+
124+ let mut res = Self :: new ( ) ;
125+ res. ensure_capacity ( hb + 1 ) ;
126+
127+ if res. is_inline ( ) {
128+ let mut data = 0 ;
129+
130+ for & bit in bits {
131+ data |= 1 << bit;
132+ }
133+
134+ unsafe { res. write_inline_data_unchecked ( data) }
135+ } else {
136+ let data = unsafe { res. as_slice_mut_unchecked ( ) } ;
137+
138+ for & bit in bits {
139+ let s = bit % BST_BITS ;
140+ let b = bit / BST_BITS ;
141+ data[ b] |= 1 << s;
142+ }
143+ }
144+
145+ res
146+ }
147+
59148 #[ inline]
60149 fn is_inline ( & self ) -> bool {
61150 self . ptr . addr ( ) . get ( ) & 0b1 == 1
@@ -119,6 +208,8 @@ impl SmolBitSet {
119208 unsafe { slice:: from_raw_parts_mut ( self . data_ptr_unchecked ( ) , self . len_unchecked ( ) ) }
120209 }
121210
211+ /// # Warning
212+ /// `highest_bit` is 1 indexed, so the least significant bit is 1, not 0!
122213 #[ inline]
123214 fn spill ( & mut self , highest_bit : usize ) {
124215 if !self . is_inline ( ) {
@@ -130,6 +221,8 @@ impl SmolBitSet {
130221 }
131222 }
132223
224+ /// # Warning
225+ /// `highest_bit` is 1 indexed, so the least significant bit is 1, not 0!
133226 unsafe fn do_spill ( & mut self , highest_bit : usize ) {
134227 let len = highest_bit. div_ceil ( BST_BITS ) ;
135228 let len = core:: cmp:: max ( len, INLINE_SLICE_PARTS ) ;
@@ -160,6 +253,8 @@ impl SmolBitSet {
160253 self . ptr = unsafe { NonNull :: new_unchecked ( ptr. cast ( ) ) } ;
161254 }
162255
256+ /// # Warning
257+ /// `highest_bit` is 1 indexed, so the least significant bit is 1, not 0!
163258 #[ inline]
164259 fn ensure_capacity ( & mut self , highest_bit : usize ) {
165260 if self . is_inline ( ) {
@@ -180,6 +275,8 @@ impl SmolBitSet {
180275 }
181276 }
182277
278+ /// # Warning
279+ /// `highest_bit` is 1 indexed, so the least significant bit is 1, not 0!
183280 unsafe fn do_grow ( & mut self , len : usize , highest_bit : usize ) {
184281 // we need to grow our slice allocation
185282 let new_len = highest_bit. div_ceil ( BST_BITS ) ;
@@ -206,6 +303,9 @@ impl SmolBitSet {
206303 self . ptr = unsafe { NonNull :: new_unchecked ( new_ptr) } ;
207304 }
208305
306+ /// Returns the index of the most significant bit set to 1.
307+ ///
308+ /// The least significant bit is at index 1!
209309 #[ inline]
210310 fn highest_set_bit ( & self ) -> usize {
211311 if self . is_inline ( ) {
0 commit comments