Skip to content

Commit

Permalink
Use traits and generic parameters for serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
andreas committed Jun 13, 2023
1 parent eaadd4a commit 61e8433
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 115 deletions.
16 changes: 8 additions & 8 deletions croaring/benches/benches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

extern crate test;

use croaring::Bitmap;
use croaring::{Bitmap, Portable};
use test::Bencher;

#[bench]
Expand Down Expand Up @@ -314,7 +314,7 @@ fn bench_get_serialized_size_in_bytes(b: &mut Bencher) {
bitmap.add(3);

b.iter(|| {
bitmap.get_serialized_size_in_bytes();
bitmap.get_serialized_size_in_bytes::<Portable>();
});
}

Expand Down Expand Up @@ -348,7 +348,7 @@ fn bench_serialize_100000(b: &mut Bencher) {
let bitmap: Bitmap = (1..100000).collect();

b.iter(|| {
bitmap.serialize();
bitmap.serialize::<Portable>();
});
}

Expand All @@ -357,26 +357,26 @@ fn bench_serialize_1000000(b: &mut Bencher) {
let bitmap: Bitmap = (1..1000000).collect();

b.iter(|| {
bitmap.serialize();
bitmap.serialize::<Portable>();
});
}

#[bench]
fn bench_deserialize_100000(b: &mut Bencher) {
let bitmap: Bitmap = (1..100000).collect();
let serialized_buffer = bitmap.serialize();
let serialized_buffer = bitmap.serialize::<Portable>();

b.iter(|| {
Bitmap::deserialize(&serialized_buffer);
Bitmap::deserialize::<Portable>(&serialized_buffer);
});
}

#[bench]
fn bench_deserialize_1000000(b: &mut Bencher) {
let bitmap: Bitmap = (1..1000000).collect();
let serialized_buffer = bitmap.serialize();
let serialized_buffer = bitmap.serialize::<Portable>();

b.iter(|| {
Bitmap::deserialize(&serialized_buffer);
Bitmap::deserialize::<Portable>(&serialized_buffer);
});
}
37 changes: 11 additions & 26 deletions croaring/src/bitmap/imp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::convert::TryInto;
use std::mem;
use std::ops::{Bound, RangeBounds};

use super::serialization::{Deserializer, Serializer};
use super::{Bitmap, Statistics};

impl Bitmap {
Expand Down Expand Up @@ -735,15 +736,8 @@ impl Bitmap {
/// Computes the serialized size in bytes of the Bitmap.
#[inline]
#[doc(alias = "roaring_bitmap_portable_size_in_bytes")]
pub fn get_serialized_size_in_bytes(&self) -> usize {
super::PortableSerializer::get_serialized_size_in_bytes(&self)
}

/// Computes the serialized size in bytes of the Bitmap for the frozen format.
#[inline]
#[doc(alias = "roaring_bitmap_frozen_size_in_bytes")]
pub fn get_frozen_serialized_size_in_bytes(&self) -> usize {
super::FrozenSerializer::get_serialized_size_in_bytes(&self)
pub fn get_serialized_size_in_bytes<S: Serializer>(&self) -> usize {
S::get_serialized_size_in_bytes(&self)
}

/// Serializes a bitmap to a slice of bytes.
Expand All @@ -763,9 +757,9 @@ impl Bitmap {
/// ```
#[inline]
#[doc(alias = "roaring_bitmap_portable_serialize")]
pub fn serialize(&self) -> Vec<u8> {
pub fn serialize<S: Serializer>(&self) -> Vec<u8> {
let mut dst = Vec::new();
self.serialize_into(&mut dst);
self.serialize_into::<S>(&mut dst);
dst
}

Expand All @@ -791,17 +785,8 @@ impl Bitmap {
/// ```
#[inline]
#[doc(alias = "roaring_bitmap_portable_serialize")]
pub fn serialize_into<'a>(&self, dst: &'a mut Vec<u8>) -> &'a [u8] {
super::PortableSerializer::serialize_into(self, dst)
}

/// Serialize into the "frozen" format
///
/// This has an odd API because it always returns a slice which is aligned to 32 bytes:
/// This means the returned slice may not start exactly at the beginning of the passed Vec
#[doc(alias = "roaring_bitmap_frozen_serialize")]
pub fn serialize_frozen_into<'a>(&self, dst: &'a mut Vec<u8>) -> &'a [u8] {
super::FrozenSerializer::serialize_into(self, dst)
pub fn serialize_into<'a, S: Serializer>(&self, dst: &'a mut Vec<u8>) -> &'a [u8] {
S::serialize_into(self, dst)
}

/// Given a serialized bitmap as slice of bytes returns a bitmap instance.
Expand All @@ -826,17 +811,17 @@ impl Bitmap {
/// ```
#[inline]
#[doc(alias = "roaring_bitmap_portable_deserialize_safe")]
pub fn try_deserialize(buffer: &[u8]) -> Option<Self> {
super::PortableSerializer::try_deserialize(buffer)
pub fn try_deserialize<D: Deserializer>(buffer: &[u8]) -> Option<Self> {
D::try_deserialize(buffer)
}

/// Given a serialized bitmap as slice of bytes returns a bitmap instance.
/// See example of [`Self::serialize`] function.
///
/// On invalid input returns empty bitmap.
#[inline]
pub fn deserialize(buffer: &[u8]) -> Self {
Self::try_deserialize(buffer).unwrap_or_else(Bitmap::create)
pub fn deserialize<D: Deserializer>(buffer: &[u8]) -> Self {
Self::try_deserialize::<D>(buffer).unwrap_or_else(Bitmap::create)
}

/// Creates a new bitmap from a slice of u32 integers
Expand Down
2 changes: 1 addition & 1 deletion croaring/src/bitmap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,4 @@ mod view;

pub use self::iter::BitmapIterator;
pub use self::lazy::LazyBitmap;
pub use self::serialization::{FrozenSerializer, NativeSerializer, PortableSerializer};
pub use self::serialization::{Frozen, Native, Portable};
86 changes: 71 additions & 15 deletions croaring/src/bitmap/serialization.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
use super::Bitmap;
use super::{Bitmap, BitmapView};

use std::ffi::{c_char, c_void};

pub struct PortableSerializer {}
pub trait Serializer {
fn serialize_into<'a>(bitmap: &Bitmap, dst: &'a mut Vec<u8>) -> &'a [u8];
fn get_serialized_size_in_bytes(bitmap: &Bitmap) -> usize;
}

pub trait Deserializer {
fn try_deserialize(buffer: &[u8]) -> Option<Bitmap>;
}

pub trait ViewDeserializer {
unsafe fn deserialize_view(data: &[u8]) -> BitmapView<'_>;
}

pub enum Portable {}

impl PortableSerializer {
pub fn serialize_into<'a>(bitmap: &Bitmap, dst: &'a mut Vec<u8>) -> &'a [u8] {
impl Serializer for Portable {
fn serialize_into<'a>(bitmap: &Bitmap, dst: &'a mut Vec<u8>) -> &'a [u8] {
let len = Self::get_serialized_size_in_bytes(bitmap);

dst.reserve(len);
Expand All @@ -22,11 +35,13 @@ impl PortableSerializer {
dst
}

pub fn get_serialized_size_in_bytes(bitmap: &Bitmap) -> usize {
fn get_serialized_size_in_bytes(bitmap: &Bitmap) -> usize {
unsafe { ffi::roaring_bitmap_portable_size_in_bytes(&bitmap.bitmap) }
}
}

pub fn try_deserialize(buffer: &[u8]) -> Option<Bitmap> {
impl Deserializer for Portable {
fn try_deserialize(buffer: &[u8]) -> Option<Bitmap> {
unsafe {
let bitmap = ffi::roaring_bitmap_portable_deserialize_safe(
buffer.as_ptr() as *const c_char,
Expand All @@ -42,10 +57,32 @@ impl PortableSerializer {
}
}

pub struct NativeSerializer {}
impl ViewDeserializer for Portable {
/// Read bitmap from a serialized buffer
///
/// This is meant to be compatible with the Java and Go versions
///
/// # Safety
/// * `data` must be the result of serializing a roaring bitmap in portable mode
/// (following `https://github.com/RoaringBitmap/RoaringFormatSpec`), for example, with
/// [`Bitmap::serialize`]
/// * Using this function (or the returned bitmap in any way) may execute unaligned memory accesses
///
unsafe fn deserialize_view<'a>(data: &'a [u8]) -> BitmapView {
// portable_deserialize_size does some amount of checks, and returns zero if data cannot be valid
debug_assert_ne!(
ffi::roaring_bitmap_portable_deserialize_size(data.as_ptr().cast(), data.len()),
0,
);
let roaring = ffi::roaring_bitmap_portable_deserialize_frozen(data.as_ptr().cast());
BitmapView::take_heap(roaring)
}
}

pub enum Native {}

impl NativeSerializer {
pub fn serialize_into<'a>(bitmap: &Bitmap, dst: &'a mut Vec<u8>) -> &'a [u8] {
impl Serializer for Native {
fn serialize_into<'a>(bitmap: &Bitmap, dst: &'a mut Vec<u8>) -> &'a [u8] {
let len = Self::get_serialized_size_in_bytes(bitmap);

dst.reserve(len);
Expand All @@ -62,11 +99,13 @@ impl NativeSerializer {
dst
}

pub fn get_serialized_size_in_bytes(bitmap: &Bitmap) -> usize {
fn get_serialized_size_in_bytes(bitmap: &Bitmap) -> usize {
unsafe { ffi::roaring_bitmap_size_in_bytes(&bitmap.bitmap) }
}
}

pub fn try_deserialize(buffer: &[u8]) -> Option<Bitmap> {
impl Deserializer for Native {
fn try_deserialize(buffer: &[u8]) -> Option<Bitmap> {
unsafe {
let bitmap = ffi::roaring_bitmap_deserialize_safe(
buffer.as_ptr() as *const c_void,
Expand All @@ -82,10 +121,10 @@ impl NativeSerializer {
}
}

pub struct FrozenSerializer {}
pub enum Frozen {}

impl FrozenSerializer {
pub fn serialize_into<'a>(bitmap: &Bitmap, dst: &'a mut Vec<u8>) -> &'a [u8] {
impl Serializer for Frozen {
fn serialize_into<'a>(bitmap: &Bitmap, dst: &'a mut Vec<u8>) -> &'a [u8] {
const REQUIRED_ALIGNMENT: usize = 32;
let len = Self::get_serialized_size_in_bytes(bitmap);

Expand Down Expand Up @@ -115,7 +154,24 @@ impl FrozenSerializer {
&dst[offset..total_len]
}

pub fn get_serialized_size_in_bytes(bitmap: &Bitmap) -> usize {
fn get_serialized_size_in_bytes(bitmap: &Bitmap) -> usize {
unsafe { ffi::roaring_bitmap_frozen_size_in_bytes(&bitmap.bitmap) }
}
}

impl ViewDeserializer for Frozen {
/// Create a frozen bitmap view using the passed data
///
/// # Safety
/// * `data` must be the result of serializing a roaring bitmap in frozen mode
/// (in c with `roaring_bitmap_frozen_serialize`, or via [`Bitmap::serialize_frozen_into`]).
/// * Its beginning must be aligned by 32 bytes.
/// * data.len() must be equal exactly to the size of the frozen bitmap.
unsafe fn deserialize_view<'a>(data: &'a [u8]) -> BitmapView {
const REQUIRED_ALIGNMENT: usize = 32;
assert_eq!(data.as_ptr() as usize % REQUIRED_ALIGNMENT, 0);

let roaring = ffi::roaring_bitmap_frozen_view(data.as_ptr().cast(), data.len());
BitmapView::take_heap(roaring)
}
}
51 changes: 4 additions & 47 deletions croaring/src/bitmap/view.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use super::serialization::ViewDeserializer;
use super::{Bitmap, BitmapView};
use ffi::roaring_bitmap_t;
use std::marker::PhantomData;
Expand All @@ -19,7 +20,7 @@ const fn original_bitmap_ptr(bitmap: &roaring_bitmap_t) -> *const roaring_bitmap
impl<'a> BitmapView<'a> {
#[inline]
#[allow(clippy::assertions_on_constants)]
unsafe fn take_heap(p: *const roaring_bitmap_t) -> Self {
pub(crate) unsafe fn take_heap(p: *const roaring_bitmap_t) -> Self {
// This depends somewhat heavily on the implementation of croaring,
// In particular, that `roaring_bitmap_t` doesn't store any pointers into itself
// (it can be moved safely), and a "frozen" bitmap is stored in an arena, and the
Expand All @@ -44,44 +45,6 @@ impl<'a> BitmapView<'a> {
}
}

/// Create a frozen bitmap view using the passed data
///
/// # Safety
/// * `data` must be the result of serializing a roaring bitmap in frozen mode
/// (in c with `roaring_bitmap_frozen_serialize`, or via [`Bitmap::serialize_frozen_into`]).
/// * Its beginning must be aligned by 32 bytes.
/// * data.len() must be equal exactly to the size of the frozen bitmap.
///
/// # Examples
///
/// ```
/// use croaring::{Bitmap, BitmapView};
/// let orig_bitmap = Bitmap::of(&[1, 2, 3, 4]);
/// let mut buf = Vec::new();
/// let data: &[u8] = orig_bitmap.serialize_frozen_into(&mut buf);
/// let view = unsafe { BitmapView::deserialize_frozen(&data) };
/// assert!(view.contains_range(1..=4));
/// assert_eq!(orig_bitmap, view);
/// ```
#[doc(alias = "roaring_bitmap_frozen_view")]
pub unsafe fn deserialize_frozen(data: &'a [u8]) -> Self {
const REQUIRED_ALIGNMENT: usize = 32;
assert_eq!(data.as_ptr() as usize % REQUIRED_ALIGNMENT, 0);

let roaring = ffi::roaring_bitmap_frozen_view(data.as_ptr().cast(), data.len());
Self::take_heap(roaring)
}

/// Read bitmap from a serialized buffer
///
/// This is meant to be compatible with the Java and Go versions
///
/// # Safety
/// * `data` must be the result of serializing a roaring bitmap in portable mode
/// (following `https://github.com/RoaringBitmap/RoaringFormatSpec`), for example, with
/// [`Bitmap::serialize`]
/// * Using this function (or the returned bitmap in any way) may execute unaligned memory accesses
///
/// # Examples
///
/// ```
Expand All @@ -93,14 +56,8 @@ impl<'a> BitmapView<'a> {
/// assert_eq!(orig_bitmap, view);
/// ```
#[doc(alias = "roaring_bitmap_portable_deserialize_frozen")]
pub unsafe fn deserialize(data: &'a [u8]) -> Self {
// portable_deserialize_size does some amount of checks, and returns zero if data cannot be valid
debug_assert_ne!(
ffi::roaring_bitmap_portable_deserialize_size(data.as_ptr().cast(), data.len()),
0,
);
let roaring = ffi::roaring_bitmap_portable_deserialize_frozen(data.as_ptr().cast());
Self::take_heap(roaring)
pub unsafe fn deserialize<S: ViewDeserializer>(data: &'a [u8]) -> Self {
S::deserialize_view(data)
}

/// Create an owned, mutable bitmap from this view
Expand Down
1 change: 1 addition & 0 deletions croaring/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ pub use bitmap::BitmapIterator;
pub use treemap::Treemap;

pub use bitmap::BitmapView;
pub use bitmap::{Frozen, Native, Portable};

0 comments on commit 61e8433

Please sign in to comment.