From 934b3cfaa3818602b74ac81b7b10bfa3be6a0a21 Mon Sep 17 00:00:00 2001 From: Jordan Doyle Date: Sun, 03 Nov 2024 22:40:19 +0000 Subject: [PATCH] Implement deserialization --- Cargo.toml | 15 ++++++++++----- README.md | 38 +++++++++----------------------------- benches/borrowed.rs | 58 ---------------------------------------------------------- benches/deserialize.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ benches/owned.rs | 60 ------------------------------------------------------------ benches/serialize.rs | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/byte.rs | 4 ++-- src/de.rs | 398 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 14 +++++++++----- src/ser.rs | 44 ++++++++++++++++++-------------------------- test/test.bin | 0 src/snapshots/serde_bson__de__test__deserialize.snap | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 764 insertions(+), 185 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0854375..f40f601 100644 --- a/Cargo.toml +++ a/Cargo.toml @@ -12,19 +12,24 @@ [dependencies] serde = "1" bytes = "1" -itoa = "0.4" +itoa = "1.0" +simdutf8 = "0.1" +memchr = "2.7" +thiserror = "1" +bumpalo = { version = "3.16", features = ["collections"] } [dev-dependencies] serde = { version = "1", features = ["derive"] } serde_bytes = "0.11" -bson = "1.2" -criterion = "0.3" +bson = "2.13" +criterion = "0.5" rand = "0.8" +insta = "1.4" [[bench]] -name = "borrowed" +name = "serialize" harness = false [[bench]] -name = "owned" +name = "deserialize" harness = false diff --git a/README.md b/README.md index b7899bd..5875fd2 100644 --- a/README.md +++ a/README.md @@ -1,38 +1,18 @@ ## serde_bson Originally implemented as a workaround to the `bson` crate cloning every value it -comes across and it's looking like it shows significant improvement across the board -for serialisation (~80% improvement). +comes across. The `bson` crate has since improved in this aspect, however this +clean room implementation of the spec still shows significant speedup in both +serialisation and deserialisation. ``` -borrowed: mongodb's bson time: [1.1160 us 1.1171 us 1.1183 us] -Found 2 outliers among 100 measurements (2.00%) - 2 (2.00%) high mild +deserialize: mongodb's bson + time: [867.32 ns 867.62 ns 867.97 ns] -borrowed: serde_bson time: [201.99 ns 202.17 ns 202.38 ns] -Found 10 outliers among 100 measurements (10.00%) - 4 (4.00%) low mild - 4 (4.00%) high mild - 2 (2.00%) high severe -``` - -Even on owned data it shows a significant improvement: +deserialize: serde_bson time: [468.41 ns 470.12 ns 472.06 ns] -``` -owned: mongodb's bson time: [1.0740 us 1.0762 us 1.0794 us] -Found 6 outliers among 100 measurements (6.00%) - 4 (4.00%) low mild - 1 (1.00%) high mild - 1 (1.00%) high severe +serialize: mongodb's bson + time: [684.01 ns 686.48 ns 689.57 ns] -owned: serde_bson time: [209.67 ns 210.18 ns 211.06 ns] -Found 9 outliers among 100 measurements (9.00%) - 5 (5.00%) high mild - 4 (4.00%) high severe +serialize: serde_bson time: [136.42 ns 136.86 ns 137.36 ns] ``` - -There's a few pieces missing such as arrays and nested documents but they're not -too difficult to add, it's just that it's 2:38am and I've smashed this out in an -hour. - -Pull requests welcome as always. diff --git a/benches/borrowed.rs b/benches/borrowed.rs deleted file mode 100644 index 44f382a..0000000 100644 --- a/benches/borrowed.rs +++ /dev/null @@ -1,58 +1,0 @@ -use bytes::BufMut; -use criterion::{black_box, criterion_group, criterion_main, Criterion}; - -#[derive(serde::Serialize)] -pub struct A<'a> { - a: &'a str, - b: &'a str, - c: &'a str, - d: i64, - e: f64, - #[serde(with = "serde_bytes")] - f: &'a [u8], -} - -fn benchmark(c: &mut Criterion) { - let val = A { - a: "Now this is a story all about how - My life got flipped turned upside down - And I'd like to take a minute, just sit right there - I'll tell you how I became the prince of a town called Bel-Air", - b: "In West Philadelphia born and raised - On the playground is where I spent most of my days - Chillin' out, maxin', relaxin' all cool - And all shootin' some b-ball outside of the school - When a couple of guys who were up to no good - Started makin' trouble in my neighborhood", - c: "I got in one little fight and my mom got scared - And said 'You're movin' with your auntie and uncle in Bel-Air'", - d: 420, - e: 420.69696969696969, - f: "Above are some popular 'pop culture' references for your perusal and enjoyment" - .as_bytes(), - }; - - c.bench_function("borrowed: mongodb's bson", |b| { - let mut theirs = Vec::new(); - - b.iter(|| { - bson::ser::to_document(black_box(&val)) - .unwrap() - .to_writer(&mut theirs) - .unwrap(); - theirs.clear(); - }) - }); - - c.bench_function("borrowed: serde_bson", |b| { - let mut out = bytes::BytesMut::new(); - - b.iter(|| { - serde_bson::to_string(black_box(&val), &mut out).unwrap(); - drop(out.split()); - }); - }); -} - -criterion_group!(benches, benchmark); -criterion_main!(benches); diff --git a/benches/deserialize.rs b/benches/deserialize.rs new file mode 100644 index 0000000..194fc38 100644 --- /dev/null +++ a/benches/deserialize.rs @@ -1,0 +1,51 @@ +use bytes::BufMut; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] +pub struct A<'a> { + cool: i32, + #[serde(with = "serde_bytes")] + beans: &'a [u8], + bro: &'a str, + b: B<'a>, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] +pub enum Test { + Abc, + Def(i32), + Ghi(i32, i32, i32), + Jkl { a: i32, b: i32 }, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] +pub struct Tup(i32, i32); + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] +pub struct B<'a> { + s: &'a str, + a: Vec<&'a str>, + e: Test, + e2: Test, + e3: Test, + e4: Test, + t: (i32, i32, i32), + ts: Tup, + y: bool, +} + +fn benchmark(c: &mut Criterion) { + let data = include_bytes!("../test/test.bin"); + + c.bench_function("deserialize: mongodb's bson", |b| { + b.iter(|| bson::de::from_slice::(black_box(data))) + }); + + c.bench_function("deserialize: serde_bson", |b| { + b.iter(|| serde_bson::de::from_bytes::(black_box(data))); + }); +} + +criterion_group!(benches, benchmark); +criterion_main!(benches); diff --git a/benches/owned.rs b/benches/owned.rs deleted file mode 100644 index 773567e..0000000 100644 --- a/benches/owned.rs +++ /dev/null @@ -1,60 +1,0 @@ -use bytes::BufMut; -use criterion::{black_box, criterion_group, criterion_main, Criterion}; - -#[derive(serde::Serialize)] -pub struct A { - a: String, - b: String, - c: String, - d: i64, - e: f64, - #[serde(with = "serde_bytes")] - f: Vec, -} - -fn benchmark(c: &mut Criterion) { - let val = A { - a: "Now this is a story all about how - My life got flipped turned upside down - And I'd like to take a minute, just sit right there - I'll tell you how I became the prince of a town called Bel-Air" - .to_string(), - b: "In West Philadelphia born and raised - On the playground is where I spent most of my days - Chillin' out, maxin', relaxin' all cool - And all shootin' some b-ball outside of the school - When a couple of guys who were up to no good - Started makin' trouble in my neighborhood" - .to_string(), - c: "I got in one little fight and my mom got scared - And said 'You're movin' with your auntie and uncle in Bel-Air'" - .to_string(), - d: 420, - e: 420.69696969696969, - f: "Above are some popular 'pop culture' references for your perusal and enjoyment".into(), - }; - - c.bench_function("owned: mongodb's bson", |b| { - let mut theirs = Vec::new(); - - b.iter(|| { - bson::ser::to_document(black_box(&val)) - .unwrap() - .to_writer(&mut theirs) - .unwrap(); - theirs.clear(); - }) - }); - - c.bench_function("owned: serde_bson", |b| { - let mut out = bytes::BytesMut::new(); - - b.iter(|| { - serde_bson::to_string(black_box(&val), &mut out).unwrap(); - drop(out.split()); - }); - }); -} - -criterion_group!(benches, benchmark); -criterion_main!(benches); diff --git a/benches/serialize.rs b/benches/serialize.rs new file mode 100644 index 0000000..ba8fb02 100644 --- /dev/null +++ a/benches/serialize.rs @@ -1,0 +1,60 @@ +use bytes::BufMut; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; + +#[derive(serde::Serialize)] +pub struct A { + a: String, + b: String, + c: String, + d: i64, + e: f64, + #[serde(with = "serde_bytes")] + f: Vec, +} + +fn benchmark(c: &mut Criterion) { + let val = A { + a: "Now this is a story all about how + My life got flipped turned upside down + And I'd like to take a minute, just sit right there + I'll tell you how I became the prince of a town called Bel-Air" + .to_string(), + b: "In West Philadelphia born and raised + On the playground is where I spent most of my days + Chillin' out, maxin', relaxin' all cool + And all shootin' some b-ball outside of the school + When a couple of guys who were up to no good + Started makin' trouble in my neighborhood" + .to_string(), + c: "I got in one little fight and my mom got scared + And said 'You're movin' with your auntie and uncle in Bel-Air'" + .to_string(), + d: 420, + e: 420.69696969696969, + f: "Above are some popular 'pop culture' references for your perusal and enjoyment".into(), + }; + + c.bench_function("serialize: mongodb's bson", |b| { + let mut theirs = Vec::new(); + + b.iter(|| { + bson::ser::to_document(black_box(&val)) + .unwrap() + .to_writer(&mut theirs) + .unwrap(); + theirs.clear(); + }) + }); + + c.bench_function("serialize: serde_bson", |b| { + let mut out = bytes::BytesMut::new(); + + b.iter(|| { + serde_bson::to_string(black_box(&val), &mut out).unwrap(); + drop(out.split()); + }); + }); +} + +criterion_group!(benches, benchmark); +criterion_main!(benches); diff --git a/src/byte.rs b/src/byte.rs index 595d30f..82ae942 100644 --- a/src/byte.rs +++ a/src/byte.rs @@ -49,7 +49,7 @@ } ); -impl<'a, B: BytesLikeBuf> BytesLikeBuf for &mut B { +impl BytesLikeBuf for &mut B { type Out = ::Out; fn put_u8(&mut self, v: u8) { @@ -115,7 +115,7 @@ } fn put_slice(&mut self, s: &[u8]) { - self.bytes += std::mem::size_of::() * s.len(); + self.bytes += std::mem::size_of_val(s); } fn split_off(&mut self, _at: usize) -> Self { diff --git a/src/de.rs b/src/de.rs new file mode 100644 index 0000000..61a3f1a 100644 --- /dev/null +++ a/src/de.rs @@ -1,0 +1,398 @@ +use memchr::memchr; +use std::{cell::RefCell, convert::TryInto, fmt::Display}; + +use serde::{ + de::{ + value::BorrowedStrDeserializer, EnumAccess, IntoDeserializer, MapAccess, SeqAccess, + VariantAccess, Visitor, + }, + forward_to_deserialize_any, Deserializer, +}; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("unexpected map end")] + UnexpectedMapEnd, + #[error("unexpected key")] + UnexpectedKey, + #[error("end of file")] + EndOfFile, + #[error("custom: {0}")] + Custom(String), + #[error("malformed map, missing key or document end")] + MalformedMapMissingKey, + #[error("unexpected enum")] + UnexpectedEnum, +} + +impl serde::de::Error for Error { + fn custom(msg: T) -> Self + where + T: Display, + { + Self::Custom(msg.to_string()) + } +} + +thread_local! { + static ALLOCATOR: RefCell = RefCell::new(bumpalo::Bump::new()); +} + +pub fn from_bytes<'de, D: serde::de::Deserialize<'de>>(data: &'de [u8]) -> Result { + ALLOCATOR.with_borrow_mut(|allocator| { + allocator.reset(); + + let mut tape = bumpalo::collections::Vec::new_in(allocator); + to_tape(data, &mut tape); + D::deserialize(&mut BsonDeserializer { tape: &tape }) + }) +} + +struct BsonDeserializer<'a, 'de> { + tape: &'a [Tape<'de>], +} + +impl<'a, 'de> BsonDeserializer<'a, 'de> { + fn next_item(&mut self) -> Option<&'a Tape<'de>> { + let (next, rest) = self.tape.split_first()?; + self.tape = rest; + Some(next) + } +} + +impl<'de> Deserializer<'de> for &mut BsonDeserializer<'_, 'de> { + type Error = Error; + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.next_item() { + Some(Tape::DocumentStart) => visitor.visit_map(self), + Some(Tape::DocumentEnd) => Err(Error::UnexpectedMapEnd), + Some(Tape::Key(_)) => Err(Error::UnexpectedKey), + Some(Tape::Double(value)) => visitor.visit_f64(*value), + Some(Tape::String(value)) => visitor.visit_borrowed_str(value), + Some(Tape::ArrayStart) => self.deserialize_seq(visitor), + Some(Tape::Binary(value, _)) => visitor.visit_borrowed_bytes(value), + Some(Tape::Boolean(value)) => visitor.visit_bool(*value), + Some(Tape::UtcDateTime(value)) => visitor.visit_i64(*value), + Some(Tape::Null) => visitor.visit_none(), + Some(Tape::I32(value)) => visitor.visit_i32(*value), + Some(Tape::Timestamp(value)) => visitor.visit_u64(*value), + Some(Tape::I64(value)) => visitor.visit_i64(*value), + None => Err(Error::EndOfFile), + } + } + + fn deserialize_seq(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + if let Some(Tape::ArrayStart) = self.tape.first() { + self.tape = &self.tape[1..]; + } + + let res = visitor.visit_seq(&mut *self)?; + + let Some(Tape::DocumentEnd) = self.next_item() else { + return Err(Error::UnexpectedMapEnd); + }; + + Ok(res) + } + + fn deserialize_enum( + self, + _name: &'static str, + _variants: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + match self.next_item() { + Some(Tape::String(s)) => visitor.visit_enum(s.into_deserializer()), + Some(Tape::DocumentStart) => { + let data = visitor.visit_enum(&mut EnumDeserializer { deser: &mut *self })?; + + let Some(Tape::DocumentEnd) = self.next_item() else { + return Err(Error::UnexpectedMapEnd); + }; + + Ok(data) + } + Some(Tape::ArrayStart) => { + let data = visitor.visit_enum(&mut EnumDeserializer { deser: &mut *self })?; + + let Some(Tape::DocumentEnd) = self.next_item() else { + return Err(Error::UnexpectedMapEnd); + }; + + Ok(data) + } + _ => Err(Error::UnexpectedEnum), + } + } + + forward_to_deserialize_any! { + bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes + byte_buf option unit unit_struct newtype_struct tuple tuple_struct + map struct identifier ignored_any + } +} + +struct EnumDeserializer<'a, 'b, 'de> { + deser: &'b mut BsonDeserializer<'a, 'de>, +} + +impl<'de> Deserializer<'de> for &mut EnumDeserializer<'_, '_, 'de> { + type Error = Error; + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + if let Some(Tape::Key(key)) = self.deser.tape.first() { + self.deser.tape = &self.deser.tape[1..]; + visitor.visit_borrowed_str(key) + } else { + self.deser.deserialize_any(visitor) + } + } + + forward_to_deserialize_any! { + bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes + byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct + map struct enum identifier ignored_any + } +} + +impl<'de> VariantAccess<'de> for &mut EnumDeserializer<'_, '_, 'de> { + type Error = Error; + + fn unit_variant(self) -> Result<(), Self::Error> { + unreachable!() + } + + fn newtype_variant_seed(self, seed: T) -> Result + where + T: serde::de::DeserializeSeed<'de>, + { + seed.deserialize(self) + } + + fn tuple_variant(self, _len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + + fn struct_variant( + self, + _fields: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + self.deserialize_map(visitor) + } +} + +impl<'de> EnumAccess<'de> for &mut EnumDeserializer<'_, '_, 'de> { + type Error = Error; + type Variant = Self; + + fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error> + where + V: serde::de::DeserializeSeed<'de>, + { + let value = seed.deserialize(&mut *self)?; + + Ok((value, self)) + } +} + +impl<'de> MapAccess<'de> for BsonDeserializer<'_, 'de> { + type Error = Error; + + fn next_key_seed(&mut self, seed: K) -> Result, Self::Error> + where + K: serde::de::DeserializeSeed<'de>, + { + let data = match self.next_item() { + Some(Tape::DocumentEnd) => return Ok(None), + Some(Tape::Key(key)) => key, + _ => return Err(Error::MalformedMapMissingKey), + }; + + seed.deserialize(BorrowedStrDeserializer::new(data)) + .map(Some) + } + + fn next_value_seed(&mut self, seed: V) -> Result + where + V: serde::de::DeserializeSeed<'de>, + { + seed.deserialize(self) + } +} + +impl<'de> SeqAccess<'de> for BsonDeserializer<'_, 'de> { + type Error = Error; + + fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> + where + T: serde::de::DeserializeSeed<'de>, + { + if let Some(Tape::DocumentEnd) = self.tape.first() { + return Ok(None); + } + + let Some(Tape::Key(_)) = self.next_item() else { + return Err(Error::MalformedMapMissingKey); + }; + + seed.deserialize(self).map(Some) + } +} + +#[derive(Debug)] +pub enum Tape<'a> { + DocumentStart, // start of input or 0x03 + DocumentEnd, // 0x00 + Key(&'a str), // + Double(f64), // 0x01 + String(&'a str), // 0x02 + ArrayStart, // 0x04 + Binary(&'a [u8], u8), // 0x05 + Boolean(bool), // 0x08 + UtcDateTime(i64), // 0x09 + Null, // 0x0a + I32(i32), // 0x10 + Timestamp(u64), // 0x11 + I64(i64), // 0x12 +} + +fn to_tape<'a>(input: &'a [u8], tape: &mut bumpalo::collections::Vec<'_, Tape<'a>>) { + let length = u32::from_le_bytes([input[0], input[1], input[2], input[3]]) as usize; + + let input = &input[4..length]; + + let mut position = 0; + tape.push(Tape::DocumentStart); + + let take_cstring = |position: &mut usize| { + let idx = memchr(b'\0', &input[*position..]).expect("unterminated c-string"); + let s = simdutf8::basic::from_utf8(&input[*position..*position + idx]).unwrap(); + *position += idx + 1; + s + }; + + let take_bytes = |position: &mut usize, n| { + let res = &input[*position..*position + n]; + *position += n; + res + }; + + while position < length - 4 { + position += 1; + match input[position - 1] { + 0x00 => { + tape.push(Tape::DocumentEnd); + } + 0x01 => { + let key = take_cstring(&mut position); + let value = f64::from_le_bytes(take_bytes(&mut position, 8).try_into().unwrap()); + tape.push(Tape::Key(key)); + tape.push(Tape::Double(value)); + } + 0x02 => { + let key = take_cstring(&mut position); + let length = + u32::from_le_bytes(take_bytes(&mut position, 4).try_into().unwrap()) as usize; + let value = + simdutf8::basic::from_utf8(&input[position..position + length - 1]).unwrap(); + position += length; + tape.push(Tape::Key(key)); + tape.push(Tape::String(value)); + } + 0x03 => { + let key = take_cstring(&mut position); + let _length = take_bytes(&mut position, 4); + tape.push(Tape::Key(key)); + tape.push(Tape::DocumentStart); + } + 0x04 => { + let key = take_cstring(&mut position); + let _length = take_bytes(&mut position, 4); + tape.push(Tape::Key(key)); + tape.push(Tape::ArrayStart); + } + 0x05 => { + let key = take_cstring(&mut position); + let length = + u32::from_le_bytes(take_bytes(&mut position, 4).try_into().unwrap()) as usize; + let subtype = input[position]; + position += 1; + let value = &input[position..position + length]; + position += length; + tape.push(Tape::Key(key)); + tape.push(Tape::Binary(value, subtype)); + } + 0x08 => { + let key = take_cstring(&mut position); + let value = input[position] == 1; + position += 1; + tape.push(Tape::Key(key)); + tape.push(Tape::Boolean(value)); + } + 0x09 => { + let key = take_cstring(&mut position); + let value = i64::from_le_bytes(take_bytes(&mut position, 8).try_into().unwrap()); + tape.push(Tape::Key(key)); + tape.push(Tape::UtcDateTime(value)); + } + 0x0a => { + let key = take_cstring(&mut position); + tape.push(Tape::Key(key)); + tape.push(Tape::Null); + } + 0x10 => { + let key = take_cstring(&mut position); + let value = i32::from_le_bytes(take_bytes(&mut position, 4).try_into().unwrap()); + tape.push(Tape::Key(key)); + tape.push(Tape::I32(value)); + } + 0x11 => { + let key = take_cstring(&mut position); + let value = u64::from_le_bytes(take_bytes(&mut position, 8).try_into().unwrap()); + tape.push(Tape::Key(key)); + tape.push(Tape::Timestamp(value)); + } + 0x12 => { + let key = take_cstring(&mut position); + let value = i64::from_le_bytes(take_bytes(&mut position, 8).try_into().unwrap()); + tape.push(Tape::Key(key)); + tape.push(Tape::I64(value)); + } + _ => {} + }; + } +} + +#[cfg(test)] +mod test { + #[test] + fn deserialize() { + let f = std::fs::read("test/test.bin").unwrap(); + + let bump = bumpalo::Bump::new(); + let mut tape = bumpalo::collections::Vec::new_in(&bump); + + super::to_tape(&f, &mut tape); + insta::assert_debug_snapshot!(tape); + } +} diff --git a/src/lib.rs b/src/lib.rs index aeefa00..74c59da 100644 --- a/src/lib.rs +++ a/src/lib.rs @@ -1,4 +1,5 @@ mod byte; +pub mod de; mod error; pub mod ser; @@ -29,11 +30,11 @@ mod test { use super::{serialised_size_of, to_string}; use bytes::{BufMut, BytesMut}; - use serde::Serialize; + use serde::{Deserialize, Serialize}; #[test] pub fn test_basic() { - #[derive(Serialize)] + #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] pub struct A<'a> { cool: i32, #[serde(with = "serde_bytes")] @@ -42,7 +43,7 @@ b: B<'a>, } - #[derive(Serialize)] + #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] pub enum Test { Abc, Def(i32), @@ -50,10 +51,10 @@ Jkl { a: i32, b: i32 }, } - #[derive(Serialize)] + #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] pub struct Tup(i32, i32); - #[derive(Serialize)] + #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] pub struct B<'a> { s: &'a str, a: Vec<&'a str>, @@ -98,5 +99,8 @@ let calculated_size = serialised_size_of(&test).unwrap(); assert_eq!(calculated_size, ours.len()); assert_eq!(calculated_size, theirs.len()); + + let deserialized: A = crate::de::from_bytes(&ours).unwrap(); + assert_eq!(&deserialized, test); } } diff --git a/src/ser.rs b/src/ser.rs index adb7afc..ac7bc70 100644 --- a/src/ser.rs +++ a/src/ser.rs @@ -106,9 +106,9 @@ Ok(()) } - fn serialize_some(self, value: &T) -> Result + fn serialize_some(self, value: &T) -> Result where - T: Serialize, + T: ?Sized + Serialize, { value.serialize(self) } @@ -130,18 +130,18 @@ self.serialize_str(variant) } - fn serialize_newtype_struct( + fn serialize_newtype_struct( self, _name: &'static str, value: &T, ) -> Result where - T: Serialize, + T: ?Sized + Serialize, { value.serialize(self) } - fn serialize_newtype_variant( + fn serialize_newtype_variant( self, _name: &'static str, _variant_index: u32, @@ -149,7 +149,7 @@ value: &T, ) -> Result where - T: Serialize, + T: ?Sized + Serialize, { let mut struct_serializer = self.serialize_struct("", 0)?; struct_serializer.serialize_field(variant, value)?; @@ -294,9 +294,9 @@ type Ok = (); type Error = as serde::Serializer>::Error; - fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> where - T: Serialize, + T: ?Sized + Serialize, { self.inner.serialize_element(value) } @@ -317,9 +317,9 @@ type Ok = (); type Error = as serde::Serializer>::Error; - fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> + fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> where - T: Serialize, + T: ?Sized + Serialize, { // we're basically inside a SeqSerializer here, but we can't instantiate one // so we'll duplicate the functionality instead @@ -350,13 +350,9 @@ type Ok = (); type Error = as serde::Serializer>::Error; - fn serialize_field( - &mut self, - key: &'static str, - value: &T, - ) -> Result<(), Self::Error> + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> where - T: Serialize, + T: ?Sized + Serialize, { // we're basically inside a nested StructSerializer here, but we can't // instantiate one so we'll duplicate the functionality instead. this @@ -386,9 +382,9 @@ type Ok = (); type Error = as serde::Serializer>::Error; - fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> + fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> where - T: Serialize, + T: ?Sized + Serialize, { self.inner.serialize_element(value) } @@ -408,9 +404,9 @@ type Ok = (); type Error = as serde::Serializer>::Error; - fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> where - T: Serialize, + T: ?Sized + Serialize, { value.serialize(Serializer { key: Some(DocumentKey::Int(self.key)), @@ -435,13 +431,9 @@ type Ok = (); type Error = as serde::Serializer>::Error; - fn serialize_field( - &mut self, - key: &'static str, - value: &T, - ) -> Result<(), Self::Error> + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> where - T: Serialize, + T: ?Sized + Serialize, { value.serialize(Serializer { key: Some(DocumentKey::Str(key)), diff --git a/test/test.bin b/test/test.bin new file mode 100644 index 0000000000000000000000000000000000000000..a4e8a4a1fe97844f1cf0aeffbf8008510a8c2761 100644 Binary files /dev/null and a/test/test.bin differ diff --git a/src/snapshots/serde_bson__de__test__deserialize.snap b/src/snapshots/serde_bson__de__test__deserialize.snap new file mode 100644 index 0000000..7ba31a9 100644 --- /dev/null +++ a/src/snapshots/serde_bson__de__test__deserialize.snap @@ -1,0 +1,207 @@ +--- +source: src/de.rs +expression: x +--- +[ + DocumentStart, + Key( + "cool", + ), + I32( + 999, + ), + Key( + "beans", + ), + Binary( + [ + 115, + 111, + 32, + 116, + 104, + 101, + 114, + 101, + 32, + 119, + 97, + 115, + 32, + 116, + 104, + 105, + 115, + 32, + 111, + 110, + 101, + 32, + 116, + 105, + 109, + 101, + 32, + 97, + 116, + 32, + 98, + 97, + 110, + 100, + 99, + 97, + 109, + 112, + ], + 0, + ), + Key( + "bro", + ), + String( + "the craziest thing happened", + ), + Key( + "b", + ), + DocumentStart, + Key( + "s", + ), + String( + "dddd", + ), + Key( + "a", + ), + ArrayStart, + Key( + "0", + ), + String( + "yooo", + ), + Key( + "1", + ), + String( + "mayn", + ), + DocumentEnd, + Key( + "e", + ), + String( + "Abc", + ), + Key( + "e2", + ), + DocumentStart, + Key( + "Def", + ), + I32( + 1999, + ), + DocumentEnd, + Key( + "e3", + ), + DocumentStart, + Key( + "Ghi", + ), + ArrayStart, + Key( + "0", + ), + I32( + 16, + ), + Key( + "1", + ), + I32( + 7, + ), + Key( + "2", + ), + I32( + 1999, + ), + DocumentEnd, + DocumentEnd, + Key( + "e4", + ), + DocumentStart, + Key( + "Jkl", + ), + DocumentStart, + Key( + "a", + ), + I32( + 16, + ), + Key( + "b", + ), + I32( + 7, + ), + DocumentEnd, + DocumentEnd, + Key( + "t", + ), + ArrayStart, + Key( + "0", + ), + I32( + 16, + ), + Key( + "1", + ), + I32( + 7, + ), + Key( + "2", + ), + I32( + 1999, + ), + DocumentEnd, + Key( + "ts", + ), + ArrayStart, + Key( + "0", + ), + I32( + 99, + ), + Key( + "1", + ), + I32( + 100, + ), + DocumentEnd, + Key( + "y", + ), + Boolean( + false, + ), + DocumentEnd, + DocumentEnd, +] -- rgit 0.1.5