diff --git a/Cargo.lock b/Cargo.lock index 62ea244..7a8a148 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,6 +124,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.15" @@ -213,6 +223,8 @@ name = "json-canon" version = "0.1.3" dependencies = [ "criterion", + "crossbeam-queue", + "lazy_static", "ryu-js", "serde", "serde_derive", diff --git a/rust/json-canon/Cargo.toml b/rust/json-canon/Cargo.toml index 7131317..0810ffe 100644 --- a/rust/json-canon/Cargo.toml +++ b/rust/json-canon/Cargo.toml @@ -14,6 +14,8 @@ readme = "README.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +crossbeam-queue = "0.3.8" +lazy_static = "1.4.0" ryu-js = { version = "0.2.2", default-features = false } serde = { version = "1.0.162", default-features = false } serde_json = { version = "1.0.96", default-features = false, features = ["std", "float_roundtrip"] } diff --git a/rust/json-canon/src/lib.rs b/rust/json-canon/src/lib.rs index 45b4199..7986e5a 100644 --- a/rust/json-canon/src/lib.rs +++ b/rust/json-canon/src/lib.rs @@ -35,6 +35,7 @@ //! mod object; +mod pool; mod ser; pub use self::ser::{to_string, to_vec, to_writer}; diff --git a/rust/json-canon/src/object.rs b/rust/json-canon/src/object.rs index cd7f974..a31cabe 100644 --- a/rust/json-canon/src/object.rs +++ b/rust/json-canon/src/object.rs @@ -1,10 +1,18 @@ use std::{ io::{self, sink, Error, ErrorKind, Write}, str::from_utf8_unchecked, + sync::Arc, }; +use lazy_static::lazy_static; use serde_json::ser::{CompactFormatter, Formatter}; +use crate::pool::{Clear, Pool, PoolObjectContainer}; + +lazy_static! { + static ref POOL: Arc> = Arc::new(Pool::with_capacity(256)); +} + #[derive(Clone, Debug)] pub(crate) struct ObjectEntry { key: Vec, @@ -13,15 +21,30 @@ pub(crate) struct ObjectEntry { is_key_done: bool, } -impl ObjectEntry { - pub(crate) fn new() -> Self { +impl Clear for ObjectEntry { + fn clear(&mut self) { + self.key.clear(); + self.key_bytes.clear(); + self.value.clear(); + self.is_key_done = false; + } +} + +impl Default for ObjectEntry { + fn default() -> Self { Self { - key: Vec::new(), - key_bytes: Vec::new(), - value: Vec::new(), + key: Vec::with_capacity(8), + key_bytes: Vec::with_capacity(8), + value: Vec::with_capacity(16), is_key_done: false, } } +} + +impl ObjectEntry { + pub(crate) fn new() -> PoolObjectContainer { + Pool::create(POOL.clone()) + } #[inline] pub(crate) fn end_key(&mut self) { @@ -84,9 +107,9 @@ impl ObjectEntry { } } -#[derive(Clone, Debug)] +#[derive(Debug)] pub(crate) struct Object { - entries: Vec, + entries: Vec>, } impl Object { @@ -96,7 +119,7 @@ impl Object { } } - pub(crate) fn current_entry(&mut self) -> io::Result<&mut ObjectEntry> { + pub(crate) fn current_entry(&mut self) -> io::Result<&mut PoolObjectContainer> { self.entries.last_mut().ok_or_else(|| { Error::new( ErrorKind::InvalidData, @@ -164,7 +187,7 @@ impl Object { } } -#[derive(Clone, Debug)] +#[derive(Debug)] pub(crate) struct ObjectStack { objects: Vec, } diff --git a/rust/json-canon/src/pool.rs b/rust/json-canon/src/pool.rs new file mode 100644 index 0000000..fbf50de --- /dev/null +++ b/rust/json-canon/src/pool.rs @@ -0,0 +1,105 @@ +use std::{ + fmt::Debug, + mem::ManuallyDrop, + ops::{Deref, DerefMut}, + ptr, + sync::Arc, +}; + +use crossbeam_queue::ArrayQueue; + +pub trait Clear { + fn clear(&mut self); +} + +#[derive(Debug)] +pub struct Pool { + pub(crate) values: ArrayQueue, + pub(crate) max_size: usize, +} + +impl Pool +where + T: Clear, +{ + #[inline] + pub fn with_capacity(capacity: usize) -> Self { + Self { + values: ArrayQueue::new(capacity), + max_size: capacity, + } + } + + #[inline] + pub fn create(self: Arc) -> PoolObjectContainer + where + T: Clear + Default, + { + let val = self.values.pop().unwrap_or_else(|| Default::default()); + PoolObjectContainer::new(val, self) + } + + #[inline] + pub fn max_size(&self) -> usize { + self.max_size + } + + #[inline] + pub fn len(&self) -> usize { + self.values.len() + } + + #[inline] + pub fn push(&self, value: T) -> Result<(), T> { + self.values.push(value) + } +} + +#[derive(Debug)] +pub struct PoolObjectContainer { + pool: Arc>, + inner: ManuallyDrop, +} + +impl PoolObjectContainer { + #[inline] + fn new(val: T, pool: Arc>) -> Self { + Self { + pool, + inner: ManuallyDrop::new(val), + } + } +} + +impl Deref for PoolObjectContainer { + type Target = T; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for PoolObjectContainer { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +impl Drop for PoolObjectContainer { + fn drop(&mut self) { + let val = unsafe { ptr::read(&self.inner) }; + let mut val = ManuallyDrop::into_inner(val); + + let pool = &self.pool; + if pool.len() >= pool.max_size() { + drop(val); + } else { + val.clear(); + if let Err(val) = pool.push(val) { + drop(val); + } + } + } +} diff --git a/rust/json-canon/src/ser.rs b/rust/json-canon/src/ser.rs index f29c835..cf2c415 100644 --- a/rust/json-canon/src/ser.rs +++ b/rust/json-canon/src/ser.rs @@ -71,7 +71,7 @@ static MAX_SAFE_INTEGER_I64: i64 = 9_007_199_254_740_991; static MAX_SAFE_INTEGER_U128: u128 = 9_007_199_254_740_991; static MAX_SAFE_INTEGER_I128: i128 = 9_007_199_254_740_991; -#[derive(Clone, Debug)] +#[derive(Debug)] #[repr(transparent)] pub struct CanonicalFormatter { stack: ObjectStack,