Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/tests_impl.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ jobs:
with:
tool: cargo-nextest
- uses: actions/checkout@v4
- run: RUSTFLAGS="
- run:
MIRIFLAGS="-Zmiri-disable-isolation"
RUSTFLAGS="
${{ inputs.rustflags }}
--cfg ${{ inputs.cache_type_flag }}
--cfg ${{ inputs.config_type_flag }}
Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ arrayvec = { version = "0.7", optional = true }
base64 = { version = "0.22", optional = true }

[dev-dependencies]
anyhow = "1"
tempfile = "3"
serde_json = "1.0"
bincode = { version = "2.0", features = ["serde"] }
rand = "0.8"
Expand Down
6 changes: 6 additions & 0 deletions miri_test_64.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@echo off
setlocal
set MIRIFLAGS=-Zmiri-disable-isolation
set RUSTFLAGS=--cfg hisparsebitset_test_64
cargo +nightly miri nextest run -j6 --all-features
endlocal
72 changes: 37 additions & 35 deletions src/bitset/serde.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::fmt;
use std::fmt::Formatter;
use std::io::Cursor;
use std::marker::PhantomData;
use serde::{Deserialize, Serialize};
use serde::de::SeqAccess;
use crate::{BitBlock, BitSet};
use crate::bitset::level::IBlock;
use crate::config::Config;
Expand All @@ -20,7 +21,7 @@ impl<Conf: Config> Serialize for BitSet<Conf>{

// real_len <= approx_len
let approx_len =
Conf::DataBitBlock::size() // root block
Conf::DataBitBlock::size() // root block
+ (1 + self.level0.mask().count_ones()) * Conf::Level1BitBlock::size() // lvl1 blocks
+ (1 + self.data.blocks().len()) * Conf::DataBitBlock::size(); // approx data blocks

Expand Down Expand Up @@ -52,48 +53,35 @@ impl<'de, Conf: Config> Deserialize<'de> for BitSet<Conf>{
where
D: serde::Deserializer<'de>
{
use serde::de::Error;

if deserializer.is_human_readable() {
use base64::{read::DecoderReader, engine::general_purpose::STANDARD};

let s: &str = <&str>::deserialize(deserializer)?;
let mut decoder = DecoderReader::new(Cursor::new(s), &STANDARD);

BitSet::deserialize(&mut decoder).map_err(serde::de::Error::custom)
struct Visitor<Conf>(PhantomData<Conf>);
impl<'de, Conf: Config> serde::de::Visitor<'de> for Visitor<Conf> {
type Value = BitSet<Conf>;

fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str("a string")
}

fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
use base64::{read::DecoderReader, engine::general_purpose::STANDARD};
let mut decoder = DecoderReader::new(Cursor::new(v), &STANDARD);
BitSet::deserialize(&mut decoder).map_err(Error::custom)
}
}
deserializer.deserialize_str(Visitor(PhantomData))
} else {
struct Visitor<Conf>(PhantomData<Conf>);
impl<'de, Conf: Config> serde::de::Visitor<'de> for Visitor<Conf> {
type Value = BitSet<Conf>;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str("a byte slice")
}

fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
BitSet::deserialize(&mut Cursor::new(v)).map_err(serde::de::Error::custom)
}

// Not in use now - bincode does not support deserialize_any
/// This is a fallback implementation for serializers that
/// lie in is_human_readable().
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut v: Vec<u8> =
if let Some(len) = seq.size_hint() {
Vec::with_capacity(len)
} else {
Vec::new()
};

while let Some(byte) = seq.next_element::<u8>()? {
v.push(byte);
}

BitSet::deserialize(&mut Cursor::new(v)).map_err(serde::de::Error::custom)
fn visit_bytes<E: Error>(self, v: &[u8]) -> Result<Self::Value, E> {
BitSet::deserialize(&mut Cursor::new(v)).map_err(Error::custom)
}
}
deserializer.deserialize_bytes(Visitor(PhantomData))
Expand All @@ -103,6 +91,7 @@ impl<'de, Conf: Config> Deserialize<'de> for BitSet<Conf>{

#[cfg(test)]
mod tests {
use std::io::Seek;
use itertools::assert_equal;
use crate::config;
use super::*;
Expand Down Expand Up @@ -141,4 +130,17 @@ mod tests {
assert_eq!(bitset, deserialized_bitset);
assert_equal(bitset.iter(), deserialized_bitset.iter()); // check by iter too.
}

#[test]
fn serde_json_w_file() -> anyhow::Result<()> {
type BitSet = crate::BitSet<config::_64bit>;
let set = BitSet::from_iter(0..260_000usize);

let mut file = tempfile::tempfile()?;
serde_json::to_writer(&mut file, &set)?;

file.rewind()?;
let _s: BitSet = serde_json::from_reader(file)?;
Ok(())
}
}