From 3596757edd28de2221bdda7872edbdf6a06be4f2 Mon Sep 17 00:00:00 2001 From: Keith Cirkel Date: Thu, 13 Jul 2023 11:03:07 +0100 Subject: [PATCH] add serde serialization support --- Cargo.toml | 6 +++ README.md | 31 +++++++++++ src/boxed.rs | 16 ++++++ src/collections/vec.rs | 24 +++++++++ tests/all/main.rs | 3 ++ tests/all/serde.rs | 119 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 199 insertions(+) create mode 100644 tests/all/serde.rs diff --git a/Cargo.toml b/Cargo.toml index 115b58e..02536ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,10 +36,15 @@ harness = false # implement its `Allocator` trait. allocator-api2 = { version = "0.2.8", default-features = false, optional = true } +# This dependency is here to allow integration with Serde, if the `serde` feature is enabled +serde = { version = "1.0.171", optional = true } + [dev-dependencies] quickcheck = "1.0.3" criterion = "0.3.6" rand = "0.8.5" +serde = { version = "1.0.197", features = ["derive"] } +serde_json = "1.0.115" [features] default = [] @@ -47,6 +52,7 @@ collections = [] boxed = [] allocator_api = [] std = [] +serde = ["dep:serde"] # [profile.bench] # debug = true diff --git a/README.md b/README.md index d0da14c..b3756e0 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,37 @@ in its space itself. } ``` +#### Serde + +Adding the `serde` feature flag will enable transparent serialization of Vecs and +boxed values. + +```toml +[dependencies] +bumpalo = { version = "3.9", features = ["collections", "boxed", "serde"] } +``` + +```rust,ignore +use bumpalo::{Bump, boxed::Box, collections::Vec}; + +// Create a new bump arena. +let bump = Bump::new(); + +// Create a `Box` +let box = Box::new_in("hello", &bump); + +// Serialize with serde_json +assert_eq!(serde_json::to_string(&box).unwrap(), "\"hello\""); + +// Create a `Vec` +let vec = Vec::new_in( &bump); +vec.push(1); +vec.push(2); + +// Serialize with serde_json +assert_eq!(serde_json::to_string(&vec).unwrap(), "[1, 2]"); +``` + ### `#![no_std]` Support Bumpalo is a `no_std` crate by default. It depends only on the `alloc` and `core` crates. diff --git a/src/boxed.rs b/src/boxed.rs index af0737c..497bbbb 100644 --- a/src/boxed.rs +++ b/src/boxed.rs @@ -681,3 +681,19 @@ impl<'a, T, const N: usize> TryFrom> for Box<'a, [T; N]> { } } } + +#[cfg(feature = "serde")] +mod serialize { + use super::*; + + use serde::{Serialize, Serializer}; + + impl<'a, T> Serialize for Box<'a, T> + where + T: Serialize, + { + fn serialize(&self, serializer: S) -> Result { + T::serialize(self, serializer) + } + } +} diff --git a/src/collections/vec.rs b/src/collections/vec.rs index 0dab700..2488c3f 100644 --- a/src/collections/vec.rs +++ b/src/collections/vec.rs @@ -2760,3 +2760,27 @@ impl<'bump> io::Write for Vec<'bump, u8> { Ok(()) } } + +#[cfg(feature = "serde")] +mod serialize { + use super::*; + + use serde::{ser::SerializeSeq, Serialize, Serializer}; + + impl<'a, T> Serialize for Vec<'a, T> + where + T: Serialize, + { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(self.len))?; + for e in self.iter() { + seq.serialize_element(e)?; + } + seq.end() + } + } + +} diff --git a/tests/all/main.rs b/tests/all/main.rs index 2f1ffa8..18f3bc5 100755 --- a/tests/all/main.rs +++ b/tests/all/main.rs @@ -16,4 +16,7 @@ mod try_alloc_try_with; mod try_alloc_with; mod vec; +#[cfg(feature = "serde")] +mod serde; + fn main() {} diff --git a/tests/all/serde.rs b/tests/all/serde.rs new file mode 100644 index 0000000..a37962c --- /dev/null +++ b/tests/all/serde.rs @@ -0,0 +1,119 @@ +#![cfg(feature = "collections")] +#![cfg(feature = "serde")] + +use bumpalo::{boxed::Box, vec, Bump}; +use serde::{Deserialize, Serialize}; + +macro_rules! compare_std_vec { + (in $bump:ident; $($x:expr),+) => {{ + let mut vec = vec![in &$bump; $($x),+]; + let std_vec = std::vec![$($x),+]; + (vec, std_vec) + }} +} + +macro_rules! compare_std_box { + (in $bump:ident; $x:expr) => { + (Box::new_in($x, &$bump), std::boxed::Box::new($x)) + }; +} + +macro_rules! assert_eq_json { + ($a:ident, $b:ident) => { + assert_eq!( + serde_json::to_string(&$a).unwrap(), + serde_json::to_string(&$b).unwrap(), + ) + }; +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[serde(tag = "t", content = "c")] +enum Test { + First, + Second, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[serde()] +struct Mixed { + i: i32, + s: String, + o: Option, + e: Test, +} + +#[test] +fn test_vec_serializes_str() { + let bump = Bump::new(); + let (vec, std_vec) = compare_std_vec![in bump; "hello", "world"]; + assert_eq_json!(vec, std_vec); + let de: std::vec::Vec = + serde_json::from_str(&serde_json::to_string(&vec).unwrap()).unwrap(); + assert_eq!(de, std_vec); +} + +#[test] +fn test_vec_serializes_f32() { + let bump = Bump::new(); + let (vec, std_vec) = compare_std_vec![in bump; 1.5707964, 3.1415927]; + assert_eq_json!(vec, std_vec); + let de: std::vec::Vec = + serde_json::from_str(&serde_json::to_string(&vec).unwrap()).unwrap(); + assert_eq!(de, std_vec); +} + +#[cfg(feature = "serde")] +#[test] +fn test_vec_serializes_complex() { + let bump = Bump::new(); + let (vec, std_vec) = compare_std_vec![ + in bump; + Mixed { + i: 8, + s: "a".into(), + o: None, + e: Test::Second, + }, + Mixed { + i: 8, + s: "b".into(), + o: Some("some".into()), + e: Test::First, + } + ]; + assert_eq_json!(vec, std_vec); + let de: std::vec::Vec = + serde_json::from_str(&serde_json::to_string(&vec).unwrap()).unwrap(); + assert_eq!(de, std_vec); +} + +#[test] +fn test_box_serializes() { + let bump = Bump::new(); + let (box_int, std_box_int) = compare_std_box!(in bump; 1); + assert_eq_json!(box_int, std_box_int); + let (box_str, std_box_str) = compare_std_box!(in bump; 1); + assert_eq_json!(box_str, std_box_str); + let (box_vec, std_box_vec) = compare_std_box!(in bump; std::vec!["hello", "world"]); + assert_eq_json!(box_vec, std_box_vec); +} + +#[cfg(feature = "serde")] +#[test] +fn test_box_serializes_complex() { + let bump = Bump::new(); + let (vec, std_vec) = compare_std_box![ + in bump; + Mixed { + i: 8, + s: "a".into(), + o: None, + e: Test::Second, + } + ]; + assert_eq_json!(vec, std_vec); + let de: std::boxed::Box = + serde_json::from_str(&serde_json::to_string(&vec).unwrap()).unwrap(); + assert_eq!(de, std_vec); +}