From d8e5848d25f6b5471fd08994610735e4200bf352 Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 26 Feb 2024 11:37:44 -0800 Subject: [PATCH 1/7] Fix Serde implementation --- .gitignore | 1 + Cargo.toml | 3 +- src/lib.rs | 16 +++--- src/serde_impl.rs | 140 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+), 8 deletions(-) create mode 100644 src/serde_impl.rs diff --git a/.gitignore b/.gitignore index 540d72e..7f93f83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target/ .idea/ Cargo.lock +.vscode \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 1c2bbce..d6eb7ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ keywords = ["container", "data-structure", "bitvec", "bitset", "no_std"] categories = ["data-structures"] [features] +serde = ["dep:serde", "std"] std = [] default = ["std"] @@ -21,7 +22,7 @@ no-dev-version = true tag-name = "{{version}}" [dependencies] -serde = { version = "1.0", features = ["derive"], optional = true } +serde = { version = "1.0", optional = true } [dev-dependencies] serde_json = "1.0" diff --git a/src/lib.rs b/src/lib.rs index 23fea19..a62ddc6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,7 @@ mod range; #[cfg(feature = "serde")] extern crate serde; #[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; +mod serde_impl; use std::fmt::Write; use std::fmt::{Binary, Display, Error, Formatter}; @@ -38,7 +38,9 @@ use std::cmp::{Ord, Ordering}; use std::iter::{Chain, ExactSizeIterator, FromIterator, FusedIterator}; use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Index}; -const BITS: usize = std::mem::size_of::() * 8; +pub(crate) const BITS: usize = std::mem::size_of::() * 8; +pub(crate) const BYTES: usize = std::mem::size_of::(); + pub type Block = usize; #[inline] @@ -55,11 +57,10 @@ fn div_rem(x: usize) -> (usize, usize) { /// Derived traits depend on both the zeros and ones, so [0,1] is not equal to /// [0,1,0]. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct FixedBitSet { - data: Vec, + pub(crate) data: Vec, /// length in bits - length: usize, + pub(crate) length: usize, } impl FixedBitSet { @@ -1806,8 +1807,9 @@ mod tests { assert_eq!(format!("{:#}", fb), "0b00101000"); } + // TODO: Rewite this test to be platform agnostic. #[test] - #[cfg(feature = "serde")] + #[cfg(all(feature = "serde", target_pointer_width = "64"))] fn test_serialize() { let mut fb = FixedBitSet::with_capacity(10); fb.put(2); @@ -1815,7 +1817,7 @@ mod tests { fb.put(6); fb.put(8); let serialized = serde_json::to_string(&fb).unwrap(); - assert_eq!(r#"{"data":[332],"length":10}"#, serialized); + assert_eq!(r#"{"length":10,"data":[76,1,0,0,0,0,0,0]}"#, serialized); } } diff --git a/src/serde_impl.rs b/src/serde_impl.rs new file mode 100644 index 0000000..0005436 --- /dev/null +++ b/src/serde_impl.rs @@ -0,0 +1,140 @@ +use crate::std::{convert::TryFrom, fmt}; +use crate::{FixedBitSet, BYTES}; +use serde::de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor}; +use serde::ser::{Serialize, SerializeStruct, Serializer}; + +struct BitSetByteSerializer<'a>(&'a FixedBitSet); + +impl Serialize for FixedBitSet { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut struct_serializer = serializer.serialize_struct("FixedBitset", 2)?; + struct_serializer.serialize_field("length", &(self.length as u64))?; + struct_serializer.serialize_field("data", &BitSetByteSerializer(self))?; + struct_serializer.end() + } +} + +impl<'a> Serialize for BitSetByteSerializer<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let len = self.0.data.len() * BYTES; + // PERF: Figure out a way to do this without allocating. + let mut temp = Vec::with_capacity(len); + for block in &self.0.data { + temp.extend(&block.to_le_bytes()); + } + serializer.serialize_bytes(&temp) + } +} + +impl<'de> Deserialize<'de> for FixedBitSet { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + enum Field { + Length, + Data, + } + + impl<'de> Deserialize<'de> for Field { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct FieldVisitor; + + impl<'de> Visitor<'de> for FieldVisitor { + type Value = Field; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("`length` or `data`") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + match value { + "length" => Ok(Field::Length), + "data" => Ok(Field::Data), + _ => Err(de::Error::unknown_field(value, FIELDS)), + } + } + } + + deserializer.deserialize_identifier(FieldVisitor) + } + } + + struct FixedBitSetVisitor; + + impl<'de> Visitor<'de> for FixedBitSetVisitor { + type Value = FixedBitSet; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("struct Duration") + } + + fn visit_seq(self, mut seq: V) -> Result + where + V: SeqAccess<'de>, + { + let length = seq + .next_element()? + .ok_or_else(|| de::Error::invalid_length(0, &self))?; + let data = seq + .next_element()? + .ok_or_else(|| de::Error::invalid_length(1, &self))?; + Ok(FixedBitSet { length, data }) + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut length = None; + let mut temp: Option<&[u8]> = None; + while let Some(key) = map.next_key()? { + match key { + Field::Length => { + if length.is_some() { + return Err(de::Error::duplicate_field("length")); + } + length = Some(map.next_value()?); + } + Field::Data => { + if temp.is_some() { + return Err(de::Error::duplicate_field("data")); + } + temp = Some(map.next_value()?); + } + } + } + let length = length.ok_or_else(|| de::Error::missing_field("length"))?; + let temp = temp.ok_or_else(|| de::Error::missing_field("data"))?; + let block_len = length / BYTES + 1; + let mut data = Vec::with_capacity(block_len); + for chunk in temp.chunks(BYTES) { + match <&[u8; BYTES]>::try_from(chunk) { + Ok(bytes) => data.push(usize::from_le_bytes(*bytes)), + Err(_) => { + let mut bytes = [0u8; BYTES]; + bytes[0..BYTES].copy_from_slice(chunk); + data.push(usize::from_le_bytes(bytes)); + } + } + } + Ok(FixedBitSet { length, data }) + } + } + + const FIELDS: &'static [&'static str] = &["length", "data"]; + deserializer.deserialize_struct("Duration", FIELDS, FixedBitSetVisitor) + } +} From 2da058ae858c92c72a7060ca6dade42caa97f465 Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 26 Feb 2024 11:42:56 -0800 Subject: [PATCH 2/7] Remove the extra feature --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d6eb7ce..e49cd67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,6 @@ keywords = ["container", "data-structure", "bitvec", "bitset", "no_std"] categories = ["data-structures"] [features] -serde = ["dep:serde", "std"] std = [] default = ["std"] From b5b9ee3c7ea9014f59089d4f02fbc379335d5d1d Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 26 Feb 2024 11:48:47 -0800 Subject: [PATCH 3/7] Bump MSRV to 1.56 and use the 2021 edition --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index e49cd67..b06e7f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,8 @@ version = "0.4.2" authors = ["bluss"] license = "MIT/Apache-2.0" readme = "README.md" +rust-version = "1.56" +edition = "2021" description = "FixedBitSet is a simple bitset collection" documentation = "https://docs.rs/fixedbitset/" From ee3ad789a5567bc0508fb4e3111147ddc9f91210 Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 26 Feb 2024 11:49:22 -0800 Subject: [PATCH 4/7] Up MSRV in CI --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0d2c683..b259f66 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - rust: [1.39.0, stable, nightly] + rust: [1.56.0, stable, nightly] steps: - uses: actions/checkout@v2 From b0aa70405047e631a6c893ffdba6bbe13b6015c8 Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 26 Feb 2024 11:51:40 -0800 Subject: [PATCH 5/7] Fix no_std --- src/lib.rs | 1 + src/range.rs | 2 ++ src/serde_impl.rs | 5 ++++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index a62ddc6..3efb1f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,6 +39,7 @@ use std::iter::{Chain, ExactSizeIterator, FromIterator, FusedIterator}; use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Index}; pub(crate) const BITS: usize = std::mem::size_of::() * 8; +#[cfg(feature = "serde")] pub(crate) const BYTES: usize = std::mem::size_of::(); pub type Block = usize; diff --git a/src/range.rs b/src/range.rs index f70c20a..738b869 100644 --- a/src/range.rs +++ b/src/range.rs @@ -1,3 +1,5 @@ +#[cfg(not(feature = "std"))] +use core as std; use std::ops::{Range, RangeFrom, RangeFull, RangeTo}; // Taken from https://github.com/bluss/odds/blob/master/src/range.rs. diff --git a/src/serde_impl.rs b/src/serde_impl.rs index 0005436..47a0070 100644 --- a/src/serde_impl.rs +++ b/src/serde_impl.rs @@ -1,4 +1,7 @@ -use crate::std::{convert::TryFrom, fmt}; +#[cfg(not(feature = "std"))] +use core as std; + +use std::{convert::TryFrom, fmt}; use crate::{FixedBitSet, BYTES}; use serde::de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor}; use serde::ser::{Serialize, SerializeStruct, Serializer}; From 414b085a853b0aa628d8473fb01654c07b1a4c9f Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 26 Feb 2024 11:52:23 -0800 Subject: [PATCH 6/7] Formatting --- src/serde_impl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serde_impl.rs b/src/serde_impl.rs index 47a0070..53d97f4 100644 --- a/src/serde_impl.rs +++ b/src/serde_impl.rs @@ -1,10 +1,10 @@ #[cfg(not(feature = "std"))] use core as std; -use std::{convert::TryFrom, fmt}; use crate::{FixedBitSet, BYTES}; use serde::de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor}; use serde::ser::{Serialize, SerializeStruct, Serializer}; +use std::{convert::TryFrom, fmt}; struct BitSetByteSerializer<'a>(&'a FixedBitSet); From 269f50dcf3f5197ed49f549980a31c513bd6aa0e Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 26 Feb 2024 11:57:24 -0800 Subject: [PATCH 7/7] Fix benches --- benches/benches/benches.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benches/benches/benches.rs b/benches/benches/benches.rs index 0e7dbc5..68a1217 100644 --- a/benches/benches/benches.rs +++ b/benches/benches/benches.rs @@ -195,12 +195,12 @@ fn count_ones(c: &mut Criterion) { criterion_group!( benches, - bitchange, iter_ones_using_contains_all_zeros, iter_ones_using_contains_all_ones, iter_ones_all_zeros, iter_ones_sparse, iter_ones_all_ones, + iter_ones_all_ones_rev, insert_range, insert, intersect_with,