From 9de771cfc793598b07af008df8c3f88f11f0953e Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 26 Feb 2024 12:12:16 -0800 Subject: [PATCH 1/4] v0.5.0 --- Cargo.toml | 2 +- README.md | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8a2a035..7ca7e02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fixedbitset" -version = "0.4.2" +version = "0.5.0" authors = ["bluss"] license = "MIT OR Apache-2.0" readme = "README.md" diff --git a/README.md b/README.md index 65ec173..989d518 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,17 @@ Please read the [API documentation here](https://docs.rs/fixedbitset/) # Recent Changes +- 0.5.0 + - [#74](https://github.com/petgraph/fixedbitset/pull/74): Accelerated set operations (union, intersection, difference, + symmetric difference) by using larger blocks internally, by @james7132. + - [#88](https://github.com/petgraph/fixedbitset/pull/88): Added `FixedBitSet::remove` by @james7132. + - [#89](https://github.com/petgraph/fixedbitset/pull/89): Added `FixedBitSet::zeros` and the `Zeros` iterator by @james7132. + - [#92](https://github.com/petgraph/fixedbitset/pull/92): Added `FixedBitSet::grow_and_insert` function, a + non-panicking version of `insert` that grows the underlying storage as need, by @shuoli84. + - [#98](https://github.com/petgraph/fixedbitset/pull/98): `Ones` now implements `DoubleEndedIterator`, by @tikhu. + - [#99](https://github.com/petgraph/fixedbitset/pull/99): **Breaking change**: serde now serializes and deserializes from a little-endian encoded + raw byte buffer. Existing stored instances of the serialized bitsets will need to be + re-encoded. - 0.4.2 - [#79](https://github.com/petgraph/fixedbitset/pull/79): Add `is_clear`, clarify `is_empty` and `len` documentation by \@nicopap. From 0746e7560c97cf8fc9871ae912cf70962330bf6a Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 16 Mar 2024 01:31:51 -0700 Subject: [PATCH 2/4] Add unsafe *_unchecked versions of point queries --- src/lib.rs | 112 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 98 insertions(+), 14 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index af8084f..54290da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -209,6 +209,19 @@ impl FixedBitSet { } } + /// Return **true** if the bit is enabled in the **FixedBitSet**, + /// **false** otherwise. + /// + /// Note: unlike `contains`, this function will not return false if called + /// with a value that is out of bounds. + /// + /// # Safety + /// `bit` must be less than `self.len()` + pub unsafe fn contains_unchecked(&self, bit: usize) -> bool { + let (block, i) = div_rem(bit); + (self.data.get_unchecked(block) & (1 << i)) != 0 + } + /// Clear all bits. #[inline] pub fn clear(&mut self) { @@ -224,10 +237,22 @@ impl FixedBitSet { pub fn insert(&mut self, bit: usize) { assert!( bit < self.length, - "insert at index {} exceeds fixbitset size {}", + "insert at index {} exceeds fixedbitset size {}", bit, self.length ); + // SAFETY: The above assertion ensures that the block is inside the Vec's allocation. + unsafe { + self.insert_unchecked(bit); + } + } + + /// Enable `bit` without any length checks. + /// + /// # Safety + /// `bit` must be less than `self.len()` + #[inline] + pub unsafe fn insert_unchecked(&mut self, bit: usize) { let (block, i) = div_rem(bit); // SAFETY: The above assertion ensures that the block is inside the Vec's allocation. unsafe { @@ -242,10 +267,22 @@ impl FixedBitSet { pub fn remove(&mut self, bit: usize) { assert!( bit < self.length, - "remove at index {} exceeds fixbitset size {}", + "remove at index {} exceeds fixedbitset size {}", bit, self.length ); + // SAFETY: The above assertion ensures that the block is inside the Vec's allocation. + unsafe { + self.remove_unchecked(bit); + } + } + + /// Disable `bit` without any bounds checking. + /// + /// # Safety + /// `bit` must be less than `self.len()` + #[inline] + pub unsafe fn remove_unchecked(&mut self, bit: usize) { let (block, i) = div_rem(bit); // SAFETY: The above assertion ensures that the block is inside the Vec's allocation. unsafe { @@ -260,10 +297,20 @@ impl FixedBitSet { pub fn put(&mut self, bit: usize) -> bool { assert!( bit < self.length, - "put at index {} exceeds fixbitset size {}", + "put at index {} exceeds fixedbitset size {}", bit, self.length ); + // SAFETY: The above assertion ensures that the block is inside the Vec's allocation. + unsafe { self.put_unchecked(bit) } + } + + /// Enable `bit`, and return its previous value without doing any bounds checking. + /// + /// # Safety + /// `bit` must be less than `self.len()` + #[inline] + pub unsafe fn put_unchecked(&mut self, bit: usize) -> bool { let (block, i) = div_rem(bit); // SAFETY: The above assertion ensures that the block is inside the Vec's allocation. unsafe { @@ -281,10 +328,22 @@ impl FixedBitSet { pub fn toggle(&mut self, bit: usize) { assert!( bit < self.length, - "toggle at index {} exceeds fixbitset size {}", + "toggle at index {} exceeds fixedbitset size {}", bit, self.length ); + // SAFETY: The above assertion ensures that the block is inside the Vec's allocation. + unsafe { + self.toggle_unchecked(bit); + } + } + + /// Toggle `bit` (inverting its state) without any bounds checking. + /// + /// # Safety + /// `bit` must be less than `self.len()` + #[inline] + pub unsafe fn toggle_unchecked(&mut self, bit: usize) { let (block, i) = div_rem(bit); // SAFETY: The above assertion ensures that the block is inside the Vec's allocation. unsafe { @@ -292,15 +351,29 @@ impl FixedBitSet { } } + /// Sets a bit to the provided `enabled` value. + /// /// **Panics** if **bit** is out of bounds. #[inline] pub fn set(&mut self, bit: usize, enabled: bool) { assert!( bit < self.length, - "set at index {} exceeds fixbitset size {}", + "set at index {} exceeds fixedbitset size {}", bit, self.length ); + // SAFETY: The above assertion ensures that the block is inside the Vec's allocation. + unsafe { + self.set_unchecked(bit, enabled); + } + } + + /// Sets a bit to the provided `enabled` value without doing any bounds checking. + /// + /// # Safety + /// `bit` must be less than `self.len()` + #[inline] + pub unsafe fn set_unchecked(&mut self, bit: usize, enabled: bool) { let (block, i) = div_rem(bit); // SAFETY: The above assertion ensures that the block is inside the Vec's allocation. let elt = unsafe { self.data.get_unchecked_mut(block) }; @@ -316,21 +389,32 @@ impl FixedBitSet { /// **Panics** if **to** is out of bounds. #[inline] pub fn copy_bit(&mut self, from: usize, to: usize) { + assert!( + from < self.length, + "copy from index {} exceeds fixedbitset size {}", + from, + self.length + ); assert!( to < self.length, - "copy at index {} exceeds fixbitset size {}", + "copy to index {} exceeds fixedbitset size {}", to, self.length ); - let (to_block, t) = div_rem(to); - let enabled = self.contains(from); // SAFETY: The above assertion ensures that the block is inside the Vec's allocation. - let to_elt = unsafe { self.data.get_unchecked_mut(to_block) }; - if enabled { - *to_elt |= 1 << t; - } else { - *to_elt &= !(1 << t); - } + unsafe { self.copy_bit_unchecked(from, to) }; + } + + /// Copies boolean value from specified bit to the specified bit. + /// + /// # Safety + /// `from` and `to` must both be less than `self.len()` + #[inline] + pub unsafe fn copy_bit_unchecked(&mut self, from: usize, to: usize) { + // SAFETY: Caller must ensure that `from` is within bounds. + let enabled = self.contains(from); + // SAFETY: Caller must ensure that `to` is within bounds. + self.set_unchecked(to, enabled); } /// Count the number of set bits in the given bit range. From 7afab87f36c58d33adbe05c59083ca6b4cb84511 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 16 Mar 2024 01:40:46 -0700 Subject: [PATCH 3/4] Cleanup --- src/lib.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0a5bb7e..c1e3fe2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -214,8 +214,8 @@ impl FixedBitSet { /// Return **true** if the bit is enabled in the **FixedBitSet**, /// **false** otherwise. /// - /// Note: unlike `contains`, this function will not return false if called - /// with a value that is out of bounds. + /// Note: unlike `contains`, calling this with an invalid `bit` + /// is undefined behavior. /// /// # Safety /// `bit` must be less than `self.len()` @@ -388,33 +388,33 @@ impl FixedBitSet { /// Copies boolean value from specified bit to the specified bit. /// + /// If `from` is out-of-bounds, `to` will be unset. + /// /// **Panics** if **to** is out of bounds. #[inline] pub fn copy_bit(&mut self, from: usize, to: usize) { - assert!( - from < self.length, - "copy from index {} exceeds fixedbitset size {}", - from, - self.length - ); assert!( to < self.length, "copy to index {} exceeds fixedbitset size {}", to, self.length ); + let enabled = self.contains(from); // SAFETY: The above assertion ensures that the block is inside the Vec's allocation. - unsafe { self.copy_bit_unchecked(from, to) }; + unsafe { self.set_unchecked(to, enabled) }; } /// Copies boolean value from specified bit to the specified bit. /// + /// Note: unlike `copy_bit`, calling this with an invalid `from` + /// is undefined behavior. + /// /// # Safety - /// `from` and `to` must both be less than `self.len()` + /// `to` must both be less than `self.len()` #[inline] pub unsafe fn copy_bit_unchecked(&mut self, from: usize, to: usize) { // SAFETY: Caller must ensure that `from` is within bounds. - let enabled = self.contains(from); + let enabled = self.contains_unchecked(from); // SAFETY: Caller must ensure that `to` is within bounds. self.set_unchecked(to, enabled); } From 25cc4e4d388ef33c4ce60ea66ec0b87452060f3d Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 16 Mar 2024 01:42:38 -0700 Subject: [PATCH 4/4] inline --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index c1e3fe2..c12450e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -219,6 +219,7 @@ impl FixedBitSet { /// /// # Safety /// `bit` must be less than `self.len()` + #[inline] pub unsafe fn contains_unchecked(&self, bit: usize) -> bool { let (block, i) = div_rem(bit); (self.data.get_unchecked(block) & (1 << i)) != 0