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 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 8a2a035..d6ee6e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,8 @@ version = "0.4.2" authors = ["bluss"] license = "MIT OR Apache-2.0" readme = "README.md" +rust-version = "1.56" +edition = "2021" description = "FixedBitSet is a simple bitset collection" documentation = "https://docs.rs/fixedbitset/" @@ -21,7 +23,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/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, diff --git a/src/lib.rs b/src/lib.rs index af8084f..0336512 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,10 @@ 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; +#[cfg(feature = "serde")] +pub(crate) const BYTES: usize = std::mem::size_of::(); + pub type Block = usize; #[inline] @@ -55,11 +58,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 { @@ -2022,8 +2024,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); @@ -2031,7 +2034,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/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 new file mode 100644 index 0000000..53d97f4 --- /dev/null +++ b/src/serde_impl.rs @@ -0,0 +1,143 @@ +#[cfg(not(feature = "std"))] +use core as std; + +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); + +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) + } +}