From 3b94205e772dd42bd16aadc8215cb3ea911af76b Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Sun, 30 Jul 2023 09:48:52 -0700 Subject: [PATCH] Removed more allocations + other optimizations The most noisy change is that all public APIs are now marked as inline, allowing the optimizer to see through many APIs. This produces nearly identical results to enabling LTO. The next optimization was to change read_buffered_bytes to reuse a scratch buffer. This allows buffered reads to share a single allocation, preventing many tiny allocations when reading things like bytes/strings from a reader. The final big optimization was to make SymbolMap store its list of 'ephemeral' strings in a single String, only storing the offsets. This further reduced the number of allocations deserializing data required. Some minor optimizations to how headers are encoded to optimize for the most common paths also have been made. All said, these changes account for ~10% improvement in the logs benchmark on average, depending on the data set. --- Cargo.toml | 3 +- benchmarks/Cargo.toml | 24 +++ {pot => benchmarks}/benches/benchmarks.rs | 34 ++- {pot => benchmarks}/examples/logs.rs | 0 pot/Cargo.toml | 15 -- pot/src/de.rs | 223 ++++++++++++++----- pot/src/format.rs | 250 ++++++++++++++++------ pot/src/lib.rs | 47 ++-- pot/src/reader.rs | 69 +++++- pot/src/ser.rs | 60 +++++- pot/src/value.rs | 54 +++++ 11 files changed, 600 insertions(+), 179 deletions(-) create mode 100644 benchmarks/Cargo.toml rename {pot => benchmarks}/benches/benchmarks.rs (83%) rename {pot => benchmarks}/examples/logs.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index aad1f607..e7f2cf87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] -members = ["xtask", "pot"] +members = ["xtask", "pot", "benchmarks"] [profile.bench] # debug = true +lto = true diff --git a/benchmarks/Cargo.toml b/benchmarks/Cargo.toml new file mode 100644 index 00000000..663f6cc9 --- /dev/null +++ b/benchmarks/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "benchmarks" +version = "0.0.0" +edition = "2021" +publish = false + +[dependencies] +serde = { version = "1.0.136", features = ["derive"] } +pot = { path = "../pot" } +rand = "0.8.4" +fake = "2.4.3" +chrono = { version = "0.4.19", features = ["serde"] } +anyhow = "1.0.53" +cli-table = "0.4.6" +thousands = "0.2.0" +ciborium = "0.2.0" +bincode = "1.3.3" +rmp-serde = "1.1.0" +criterion = { version = "0.5", features = ["html_reports"] } + + +[[bench]] +name = "benchmarks" +harness = false diff --git a/pot/benches/benchmarks.rs b/benchmarks/benches/benchmarks.rs similarity index 83% rename from pot/benches/benchmarks.rs rename to benchmarks/benches/benchmarks.rs index bf073a85..e9c203fd 100644 --- a/pot/benches/benchmarks.rs +++ b/benchmarks/benches/benchmarks.rs @@ -2,6 +2,7 @@ //! //! Proper benchmarks will be coming. +use std::env; use std::fmt::Display; use chrono::{DateTime, Utc}; @@ -10,7 +11,8 @@ use fake::faker::filesystem::en::FilePath; use fake::faker::internet::en::Username; use fake::faker::lorem::en::Sentence; use fake::Fake; -use rand::{thread_rng, Rng}; +use rand::rngs::StdRng; +use rand::{thread_rng, Rng, SeedableRng}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, PartialEq)] @@ -107,8 +109,36 @@ fn bench_logs(c: &mut Criterion) { let mut logs = LogArchive { entries: Vec::with_capacity(LOG_ENTRIES), }; + let random_seed = env::args().find(|arg| arg.starts_with("-s")).map_or_else( + || thread_rng().gen(), + |seed| { + let (_, seed) = seed.split_at(2); + let (upper, lower) = if seed.len() > 32 { + let (upper, lower) = seed.split_at(seed.len() - 32); + ( + u128::from_str_radix(upper, 16).expect("invalid hexadecimal seed"), + u128::from_str_radix(lower, 16).expect("invalid hexadecimal seed"), + ) + } else { + ( + 0, + u128::from_str_radix(seed, 16).expect("invalid hexadecimal seed"), + ) + }; + let mut seed = [0; 32]; + seed[..16].copy_from_slice(&upper.to_be_bytes()); + seed[16..].copy_from_slice(&lower.to_be_bytes()); + seed + }, + ); + print!("Using random seed -s"); + for b in random_seed { + print!("{b:02x}"); + } + println!(); + let mut rng = StdRng::from_seed(random_seed); for _ in 0..LOG_ENTRIES { - logs.entries.push(Log::generate(&mut thread_rng())); + logs.entries.push(Log::generate(&mut rng)); } let mut serialize_group = c.benchmark_group("logs/serialize"); diff --git a/pot/examples/logs.rs b/benchmarks/examples/logs.rs similarity index 100% rename from pot/examples/logs.rs rename to benchmarks/examples/logs.rs diff --git a/pot/Cargo.toml b/pot/Cargo.toml index 3a7604fa..156314c4 100644 --- a/pot/Cargo.toml +++ b/pot/Cargo.toml @@ -21,20 +21,5 @@ half = "2.2.1" [dev-dependencies] tracing-subscriber = "0.3.8" tracing = "0.1.30" -rand = "0.8.4" -fake = "2.4.3" -chrono = { version = "0.4.19", features = ["serde"] } -anyhow = "1.0.53" -cli-table = "0.4.6" -thousands = "0.2.0" -ciborium = "0.2.0" -bincode = "1.3.3" -rmp-serde = "1.1.0" -criterion = { version = "0.5", features = ["html_reports"] } serde_bytes = "0.11.5" -serde_json = "1.0.78" approx = "0.5.1" - -[[bench]] -name = "benchmarks" -harness = false diff --git a/pot/src/de.rs b/pot/src/de.rs index 8cc593fc..20c006ac 100644 --- a/pot/src/de.rs +++ b/pot/src/de.rs @@ -1,6 +1,6 @@ -use std::borrow::Cow; use std::collections::VecDeque; use std::fmt::Debug; +use std::ops::{Deref, Range}; use byteorder::ReadBytesExt; use format::Kind; @@ -13,7 +13,7 @@ use tracing::instrument; use crate::format::{ self, Atom, Float, InnerFloat, InnerInteger, Integer, Nucleus, CURRENT_VERSION, }; -use crate::reader::{IoReader, Reader, SliceReader}; +use crate::reader::{BufferedBytes, IoReader, Reader, SliceReader}; use crate::{Error, Result}; /// Deserializer for the Pot format. @@ -22,6 +22,7 @@ pub struct Deserializer<'s, 'de, R: Reader<'de>> { symbols: SymbolMap<'s, 'de>, peeked_atom: VecDeque>, remaining_budget: usize, + scratch: Vec, } impl<'s, 'de, R: Reader<'de>> Debug for Deserializer<'s, 'de, R> { @@ -36,6 +37,7 @@ impl<'s, 'de, R: Reader<'de>> Debug for Deserializer<'s, 'de, R> { impl<'s, 'de> Deserializer<'s, 'de, SliceReader<'de>> { /// Returns a new deserializer for `input`. + #[inline] pub(crate) fn from_slice(input: &'de [u8], maximum_bytes_allocatable: usize) -> Result { Self::from_slice_with_symbols(input, SymbolMap::new(), maximum_bytes_allocatable) } @@ -50,6 +52,7 @@ impl<'s, 'de> Deserializer<'s, 'de, SliceReader<'de>> { /// Returns `true` if the input has been consumed completely. #[must_use] + #[inline] pub fn end_of_input(&self) -> bool { self.input.data.is_empty() && self.peeked_atom.is_empty() } @@ -57,32 +60,29 @@ impl<'s, 'de> Deserializer<'s, 'de, SliceReader<'de>> { impl<'s, 'de, R: ReadBytesExt> Deserializer<'s, 'de, IoReader> { /// Returns a new deserializer for `input`. + #[inline] pub(crate) fn from_read(input: R, maximum_bytes_allocatable: usize) -> Result { - Self::from_read_with_symbols(input, SymbolMap::new(), maximum_bytes_allocatable) - } - - fn from_read_with_symbols( - input: R, - symbols: SymbolMap<'s, 'de>, - maximum_bytes_allocatable: usize, - ) -> Result { - Self::new(IoReader::new(input), symbols, maximum_bytes_allocatable) + Self::new( + IoReader::new(input), + SymbolMap::new(), + maximum_bytes_allocatable, + ) } } impl<'s, 'de, R: Reader<'de>> Deserializer<'s, 'de, R> { + #[inline] pub(crate) fn new( input: R, - mut symbols: SymbolMap<'s, 'de>, + symbols: SymbolMap<'s, 'de>, maximum_bytes_allocatable: usize, ) -> Result { - // TODO make this configurable - symbols.reserve(1024); let mut deserializer = Deserializer { input, symbols, peeked_atom: VecDeque::new(), remaining_budget: maximum_bytes_allocatable, + scratch: Vec::new(), }; deserializer.read_header()?; Ok(deserializer) @@ -101,7 +101,11 @@ impl<'s, 'de, R: Reader<'de>> Deserializer<'s, 'de, R> { if let Some(peeked) = self.peeked_atom.pop_front() { Ok(peeked) } else { - format::read_atom(&mut self.input, &mut self.remaining_budget) + format::read_atom( + &mut self.input, + &mut self.remaining_budget, + &mut self.scratch, + ) } } @@ -132,17 +136,19 @@ impl<'s, 'de, R: Reader<'de>> Deserializer<'s, 'de, R> { self.symbols.visit_symbol_id(arg, visitor) } else { // New symbol - let name = self.input.buffered_read_bytes(arg as usize)?; + let name = self + .input + .buffered_read_bytes(arg as usize, &mut self.scratch)?; match name { - Cow::Borrowed(name) => { + BufferedBytes::Data(name) => { let name = std::str::from_utf8(name)?; - self.symbols.push(Cow::Borrowed(name)); + self.symbols.push_borrowed(name); visitor.visit_borrowed_str(name) } - Cow::Owned(name) => { - let name = String::from_utf8(name)?; - let result = visitor.visit_str(&name); - self.symbols.push(Cow::Owned(name)); + BufferedBytes::Scratch => { + let name = std::str::from_utf8(&self.scratch)?; + let result = visitor.visit_str(name); + self.symbols.push(name); result } } @@ -153,6 +159,7 @@ impl<'s, 'de, R: Reader<'de>> Deserializer<'s, 'de, R> { impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer<'s, 'de, R> { type Error = Error; + #[inline] fn is_human_readable(&self) -> bool { false } @@ -162,6 +169,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer // Formats that support `deserialize_any` are known as self-describing. #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] #[allow(clippy::cast_possible_truncation)] + #[inline] fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, @@ -222,18 +230,18 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer Kind::Symbol => self.visit_symbol(&atom, visitor), Kind::Bytes => match &atom.nucleus { Some(Nucleus::Bytes(bytes)) => match bytes { - Cow::Borrowed(bytes) => { + BufferedBytes::Data(bytes) => { if let Ok(as_str) = std::str::from_utf8(bytes) { visitor.visit_borrowed_str(as_str) } else { visitor.visit_borrowed_bytes(bytes) } } - Cow::Owned(bytes) => { - if let Ok(as_str) = std::str::from_utf8(bytes) { + BufferedBytes::Scratch => { + if let Ok(as_str) = std::str::from_utf8(&self.scratch) { visitor.visit_str(as_str) } else { - visitor.visit_bytes(bytes) + visitor.visit_bytes(&self.scratch) } } }, @@ -245,6 +253,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_bool(self, visitor: V) -> Result where V: Visitor<'de>, @@ -264,6 +273,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_i8(self, visitor: V) -> Result where V: Visitor<'de>, @@ -285,6 +295,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_i16(self, visitor: V) -> Result where V: Visitor<'de>, @@ -306,6 +317,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_i32(self, visitor: V) -> Result where V: Visitor<'de>, @@ -327,6 +339,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_i64(self, visitor: V) -> Result where V: Visitor<'de>, @@ -348,6 +361,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_i128(self, visitor: V) -> Result where V: Visitor<'de>, @@ -369,6 +383,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_u8(self, visitor: V) -> Result where V: Visitor<'de>, @@ -390,6 +405,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_u16(self, visitor: V) -> Result where V: Visitor<'de>, @@ -411,6 +427,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_u32(self, visitor: V) -> Result where V: Visitor<'de>, @@ -432,6 +449,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_u64(self, visitor: V) -> Result where V: Visitor<'de>, @@ -453,6 +471,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_u128(self, visitor: V) -> Result where V: Visitor<'de>, @@ -474,6 +493,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_f32(self, visitor: V) -> Result where V: Visitor<'de>, @@ -503,6 +523,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_f64(self, visitor: V) -> Result where V: Visitor<'de>, @@ -532,6 +553,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_char(self, visitor: V) -> Result where V: Visitor<'de>, @@ -556,6 +578,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_str(self, visitor: V) -> Result where V: Visitor<'de>, @@ -564,8 +587,12 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer match atom.kind { Kind::Bytes => match atom.nucleus { Some(Nucleus::Bytes(bytes)) => match bytes { - Cow::Borrowed(bytes) => visitor.visit_borrowed_str(std::str::from_utf8(bytes)?), - Cow::Owned(bytes) => visitor.visit_str(std::str::from_utf8(&bytes)?), + BufferedBytes::Data(bytes) => { + visitor.visit_borrowed_str(std::str::from_utf8(bytes)?) + } + BufferedBytes::Scratch => { + visitor.visit_str(std::str::from_utf8(&self.scratch)?) + } }, _ => unreachable!("read_atom should never return anything else"), }, @@ -585,6 +612,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_string(self, visitor: V) -> Result where V: Visitor<'de>, @@ -594,6 +622,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] #[allow(clippy::cast_possible_truncation)] + #[inline] fn deserialize_bytes(self, visitor: V) -> Result where V: Visitor<'de>, @@ -602,8 +631,8 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer match atom.kind { Kind::Bytes => match atom.nucleus { Some(Nucleus::Bytes(bytes)) => match bytes { - Cow::Borrowed(bytes) => visitor.visit_borrowed_bytes(bytes), - Cow::Owned(bytes) => visitor.visit_bytes(&bytes), + BufferedBytes::Data(bytes) => visitor.visit_borrowed_bytes(bytes), + BufferedBytes::Scratch => visitor.visit_bytes(&self.scratch), }, _ => unreachable!("read_atom should never return anything else"), }, @@ -630,6 +659,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_byte_buf(self, visitor: V) -> Result where V: Visitor<'de>, @@ -638,6 +668,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_option(self, visitor: V) -> Result where V: Visitor<'de>, @@ -645,7 +676,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer let atom = self.peek_atom()?; if matches!(atom.kind, Kind::Special) && atom.nucleus.is_none() { // Consume the atom. - drop(self.read_atom()?); + self.read_atom()?; return visitor.visit_none(); } @@ -654,6 +685,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer // In Serde, unit means an anonymous value containing no data. #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_unit(self, visitor: V) -> Result where V: Visitor<'de>, @@ -668,6 +700,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer // Unit struct means a named value containing no data. #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_unit_struct(self, _name: &'static str, visitor: V) -> Result where V: Visitor<'de>, @@ -679,6 +712,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer // insignificant wrappers around the data they contain. That means not // parsing anything other than the contained value. #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result where V: Visitor<'de>, @@ -688,6 +722,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] #[allow(clippy::cast_possible_truncation)] + #[inline] fn deserialize_seq(self, visitor: V) -> Result where V: Visitor<'de>, @@ -706,6 +741,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_tuple(self, _len: usize, visitor: V) -> Result where V: Visitor<'de>, @@ -713,6 +749,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer self.deserialize_seq(visitor) } + #[inline] fn deserialize_tuple_struct( self, _name: &'static str, @@ -727,6 +764,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] #[allow(clippy::cast_possible_truncation)] + #[inline] fn deserialize_map(self, visitor: V) -> Result where V: Visitor<'de>, @@ -743,6 +781,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_struct( self, _name: &'static str, @@ -756,6 +795,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_enum( self, _name: &'static str, @@ -770,6 +810,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] #[allow(clippy::cast_possible_truncation)] + #[inline] fn deserialize_identifier(self, visitor: V) -> Result where V: Visitor<'de>, @@ -779,7 +820,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer Kind::Symbol => self.visit_symbol(&atom, visitor), Kind::Bytes => { if let Some(Nucleus::Bytes(bytes)) = atom.nucleus { - let as_str = std::str::from_utf8(&bytes) + let as_str = std::str::from_utf8(bytes.as_slice(&self.scratch)) .map_err(|err| Error::InvalidUtf8(err.to_string()))?; visitor.visit_str(as_str) } else { @@ -791,6 +832,7 @@ impl<'a, 'de, 's, R: Reader<'de>> de::Deserializer<'de> for &'a mut Deserializer } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn deserialize_ignored_any(self, visitor: V) -> Result where V: Visitor<'de>, @@ -804,6 +846,7 @@ struct EmptyList; impl<'de> MapAccess<'de> for EmptyList { type Error = Error; + #[inline] fn next_key_seed(&mut self, _seed: K) -> Result> where K: DeserializeSeed<'de>, @@ -811,6 +854,7 @@ impl<'de> MapAccess<'de> for EmptyList { Ok(None) } + #[inline] fn next_value_seed(&mut self, _seed: V) -> Result where V: DeserializeSeed<'de>, @@ -822,6 +866,7 @@ impl<'de> MapAccess<'de> for EmptyList { impl<'de> SeqAccess<'de> for EmptyList { type Error = Error; + #[inline] fn next_element_seed(&mut self, _seed: T) -> Result> where T: DeserializeSeed<'de>, @@ -861,7 +906,7 @@ impl<'a, 's, 'de, R: Reader<'de>> AtomList<'a, 's, 'de, R> { && matches!(atom.nucleus, Some(Nucleus::DynamicEnd)) { self.eof = true; - drop(self.de.read_atom()?); + self.de.read_atom()?; return Ok(true); } } @@ -885,6 +930,7 @@ impl<'a, 's, 'de, R: Reader<'de>> SeqAccess<'de> for AtomList<'a, 's, 'de, R> { type Error = Error; #[cfg_attr(feature = "tracing", instrument(skip(seed)))] + #[inline] fn next_element_seed(&mut self, seed: T) -> Result> where T: DeserializeSeed<'de>, @@ -897,6 +943,7 @@ impl<'a, 's, 'de, R: Reader<'de>> SeqAccess<'de> for AtomList<'a, 's, 'de, R> { } } + #[inline] fn size_hint(&self) -> Option { self.count } @@ -906,6 +953,7 @@ impl<'a, 's, 'de, R: Reader<'de>> MapAccess<'de> for AtomList<'a, 's, 'de, R> { type Error = Error; #[cfg_attr(feature = "tracing", instrument(skip(seed)))] + #[inline] fn next_key_seed(&mut self, seed: T) -> Result> where T: DeserializeSeed<'de>, @@ -919,6 +967,7 @@ impl<'a, 's, 'de, R: Reader<'de>> MapAccess<'de> for AtomList<'a, 's, 'de, R> { } #[cfg_attr(feature = "tracing", instrument(skip(seed)))] + #[inline] fn next_value_seed(&mut self, seed: V) -> Result where V: DeserializeSeed<'de>, @@ -927,6 +976,7 @@ impl<'a, 's, 'de, R: Reader<'de>> MapAccess<'de> for AtomList<'a, 's, 'de, R> { seed.deserialize(&mut *self.de) } + #[inline] fn size_hint(&self) -> Option { self.count } @@ -937,6 +987,7 @@ impl<'a, 's, 'de, R: Reader<'de>> EnumAccess<'de> for &'a mut Deserializer<'s, ' type Variant = Self; #[cfg_attr(feature = "tracing", instrument(skip(seed)))] + #[inline] fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant)> where V: DeserializeSeed<'de>, @@ -959,11 +1010,13 @@ impl<'a, 's, 'de, R: Reader<'de>> VariantAccess<'de> for &'a mut Deserializer<'s type Error = Error; #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn unit_variant(self) -> Result<()> { Ok(()) } #[cfg_attr(feature = "tracing", instrument(skip(seed)))] + #[inline] fn newtype_variant_seed(self, seed: T) -> Result where T: DeserializeSeed<'de>, @@ -972,6 +1025,7 @@ impl<'a, 's, 'de, R: Reader<'de>> VariantAccess<'de> for &'a mut Deserializer<'s } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn tuple_variant(self, _len: usize, visitor: V) -> Result where V: Visitor<'de>, @@ -980,6 +1034,7 @@ impl<'a, 's, 'de, R: Reader<'de>> VariantAccess<'de> for &'a mut Deserializer<'s } #[cfg_attr(feature = "tracing", instrument(skip(visitor)))] + #[inline] fn struct_variant(self, _fields: &'static [&'static str], visitor: V) -> Result where V: Visitor<'de>, @@ -992,21 +1047,23 @@ impl<'a, 's, 'de, R: Reader<'de>> VariantAccess<'de> for &'a mut Deserializer<'s #[derive(Debug)] pub enum SymbolMap<'a, 'de> { /// An owned list of symbols. - Owned(Vec), + Owned(SymbolList<'static>), /// A mutable reference to an owned list of symbols. - Persistent(&'a mut Vec), + Persistent(&'a mut SymbolList<'static>), /// A list of borrowed symbols. - Borrowed(Vec>), + Borrowed(SymbolList<'de>), } impl<'de> SymbolMap<'static, 'de> { /// Returns a new symbol map that will persist symbols between payloads. #[must_use] + #[inline] pub const fn new() -> Self { - Self::Owned(Vec::new()) + Self::Owned(SymbolList::new()) } /// Returns a deserializer for `slice`. + #[inline] pub fn deserializer_for_slice<'a>( &'a mut self, slice: &'de [u8], @@ -1035,39 +1092,107 @@ impl<'a, 'de> SymbolMap<'a, 'de> { let symbol = vec .get(symbol_id as usize) .ok_or(Error::UnknownSymbol(symbol_id))?; - visitor.visit_str(symbol) + visitor.visit_str(&symbol) } Self::Persistent(vec) => { let symbol = vec .get(symbol_id as usize) .ok_or(Error::UnknownSymbol(symbol_id))?; - visitor.visit_str(symbol) + visitor.visit_str(&symbol) } Self::Borrowed(vec) => { let symbol = vec .get(symbol_id as usize) .ok_or(Error::UnknownSymbol(symbol_id))?; match symbol { - Cow::Borrowed(symbol) => visitor.visit_borrowed_str(symbol), - Cow::Owned(symbol) => visitor.visit_str(symbol), + SymbolStr::Data(symbol) => visitor.visit_borrowed_str(symbol), + SymbolStr::InList(symbol) => visitor.visit_str(symbol), } } } } - fn reserve(&mut self, amount: usize) { + fn push(&mut self, symbol: &str) { + #[allow(clippy::match_same_arms)] // false positive due to lifetimes match self { - Self::Owned(vec) => vec.reserve(amount), - Self::Persistent(vec) => vec.reserve(amount), - Self::Borrowed(vec) => vec.reserve(amount), + Self::Owned(vec) => vec.push(symbol), + Self::Persistent(vec) => vec.push(symbol), + Self::Borrowed(vec) => vec.push(symbol), } } - fn push(&mut self, symbol: Cow<'de, str>) { + fn push_borrowed(&mut self, symbol: &'de str) { match self { - Self::Owned(vec) => vec.push(symbol.to_string()), - Self::Persistent(vec) => vec.push(symbol.to_string()), - Self::Borrowed(vec) => vec.push(symbol), + Self::Owned(vec) => vec.push(symbol), + Self::Persistent(vec) => vec.push(symbol), + Self::Borrowed(vec) => vec.push_borrowed(symbol), + } + } +} + +/// A collection of symbols accumulated during deserialization. +#[derive(Debug)] +pub struct SymbolList<'de> { + buffer: String, + entries: Vec>, +} + +impl<'de> SymbolList<'de> { + const fn new() -> Self { + Self { + buffer: String::new(), + entries: Vec::new(), + } + } + + /// Push a symbol that has been borrowed from the deserialization source. + #[inline] + pub fn push_borrowed(&mut self, borrowed: &'de str) { + self.entries.push(SymbolListEntry::Borrowed(borrowed)); + } + + /// Push a symbol that cannot be borrowed from the deserialization source. + #[inline] + pub fn push(&mut self, ephemeral: &str) { + let start = self.buffer.len(); + self.buffer.push_str(ephemeral); + self.entries + .push(SymbolListEntry::Buffer(start..self.buffer.len())); + } + + /// Return the symbol stored at `index`, or `None` if `index` is out of + /// bounds. + #[inline] + #[must_use] + pub fn get(&self, index: usize) -> Option> { + match self.entries.get(index)? { + SymbolListEntry::Buffer(range) => Some(SymbolStr::InList(&self.buffer[range.clone()])), + SymbolListEntry::Borrowed(str) => Some(SymbolStr::Data(str)), } } } + +/// A symbol stored in a [`SymbolList`]. +pub enum SymbolStr<'de, 'ephemeral> { + /// A symbol that has been borrowed from the data being deserialized. + Data(&'de str), + /// A symbol that is stored inside of the [`SymbolList`]. + InList(&'ephemeral str), +} + +impl Deref for SymbolStr<'_, '_> { + type Target = str; + + #[inline] + fn deref(&self) -> &Self::Target { + match self { + SymbolStr::Data(str) | SymbolStr::InList(str) => str, + } + } +} + +#[derive(Debug, Clone)] +enum SymbolListEntry<'de> { + Buffer(Range), + Borrowed(&'de str), +} diff --git a/pot/src/format.rs b/pot/src/format.rs index 9956b67c..8a83c38b 100644 --- a/pot/src/format.rs +++ b/pot/src/format.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::fmt::Display; use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; @@ -8,49 +7,122 @@ use serde::{Deserialize, Serialize}; pub(crate) const CURRENT_VERSION: u8 = 0; -use crate::reader::Reader; +use crate::reader::{BufferedBytes, Reader}; use crate::Error; - /// Writes an atom header into `writer`. #[allow(clippy::cast_possible_truncation)] -pub fn write_atom_header( +#[inline] +fn write_tiny_atom_header( mut writer: W, kind: Kind, - arg: Option, + arg: u8, ) -> std::io::Result { - // 8 + 64 total bits, with 4 in the first u8, and 7 in the remaining. Total - // of 10 bytes required. to store a u64::MAX. - let mut bytes = [0_u8; 10]; // Kind is the 3 bits. let mut first_byte = (kind as u8) << 5; - let mut arg = arg.unwrap_or_default(); if arg > 0 { - // The last 4 bits are the first 4 bits of the arg + debug_assert!(arg < 0x10); + first_byte |= arg & 0b1111; + } + + writer.write_all(&[first_byte])?; + Ok(1) +} + +/// Writes an atom header into `writer`. +#[allow(clippy::cast_possible_truncation)] +#[inline] +pub fn write_atom_header( + mut writer: W, + kind: Kind, + mut arg: u64, +) -> std::io::Result { + if arg < 0x10 { + write_tiny_atom_header(writer, kind, arg as u8) + } else { + // Kind is the 3 bits. + let mut first_byte = (kind as u8) << 5; + // The last 4 bits are the first 4 bits of the arg. We also know + // that we're longer than one byte, due to the original match. first_byte |= arg as u8 & 0b1111; arg >>= 4; - // If the arg requires more than 4 bits, set the 5th bit. - if arg > 0 { - first_byte |= 0b10000; + first_byte |= 0b10000; + + let mut second = arg as u8 & 0x7F; + arg >>= 7; + if arg == 0 { + writer.write_all(&[first_byte, second])?; + return Ok(2); } - } - bytes[0] = first_byte; - let mut length = 1; - while arg > 0 { - let mut byte = arg as u8 & 0x7F; + + second |= 0b1000_0000; + let mut third = arg as u8 & 0x7F; arg >>= 7; - if arg > 0 { - byte |= 0b1000_0000; + if arg == 0 { + writer.write_all(&[first_byte, second, third])?; + return Ok(3); } - bytes[length] = byte; - length += 1; - } - writer.write_all(&bytes[..length])?; + third |= 0b1000_0000; + let mut fourth = arg as u8 & 0x7F; + arg >>= 7; + if arg == 0 { + writer.write_all(&[first_byte, second, third, fourth])?; + return Ok(4); + } - Ok(length) + fourth |= 0b1000_0000; + let mut fifth = arg as u8 & 0x7F; + arg >>= 7; + if arg == 0 { + writer.write_all(&[first_byte, second, third, fourth, fifth])?; + return Ok(5); + } + + fifth |= 0b1000_0000; + let mut sixth = arg as u8 & 0x7F; + arg >>= 7; + if arg == 0 { + writer.write_all(&[first_byte, second, third, fourth, fifth, sixth])?; + return Ok(6); + } + sixth |= 0b1000_0000; + let mut seventh = arg as u8 & 0x7F; + arg >>= 7; + if arg == 0 { + writer.write_all(&[first_byte, second, third, fourth, fifth, sixth, seventh])?; + return Ok(7); + } + seventh |= 0b1000_0000; + let mut eighth = arg as u8 & 0x7F; + arg >>= 7; + if arg == 0 { + writer.write_all(&[ + first_byte, second, third, fourth, fifth, sixth, seventh, eighth, + ])?; + return Ok(8); + } + + eighth |= 0b1000_0000; + let mut ninth = arg as u8 & 0x7F; + arg >>= 7; + if arg == 0 { + writer.write_all(&[ + first_byte, second, third, fourth, fifth, sixth, seventh, eighth, ninth, + ])?; + return Ok(9); + } + + ninth |= 0b1000_0000; + debug_assert!(arg <= 255); + writer.write_all(&[ + first_byte, second, third, fourth, fifth, sixth, seventh, eighth, ninth, arg as u8, + ])?; + Ok(10) + } } /// Reads an atom header (kind and argument). +#[inline] pub fn read_atom_header(reader: &mut R) -> Result<(Kind, u64), Error> { let first_byte = reader.read_u8()?; let kind = Kind::from_u8(first_byte >> 5)?; @@ -63,7 +135,7 @@ pub fn read_atom_header(reader: &mut R) -> Result<(Kind, u64), bytes_read += 1; arg |= u64::from(byte & 0x7f) << offset; offset += 7; - if byte & 0b1000_0000 == 0 || bytes_read == 10 { + if byte < 128 || bytes_read == 10 { break; } } @@ -147,6 +219,7 @@ pub(crate) const SPECIAL_COUNT: u64 = Special::Named as u64 + 1; impl TryFrom for Special { type Error = Error; + #[inline] fn try_from(value: u64) -> Result { match value { 0 => Ok(Self::None), @@ -164,6 +237,7 @@ impl TryFrom for Special { /// Writes the Pot header. A u32 written in big endian. The first three bytes /// are 'Pot' (`0x506F74`), and the fourth byte is the version. The first /// version of Pot is 0. +#[inline] pub fn write_header(mut writer: W, version: u8) -> std::io::Result { writer.write_u32::(0x506F_7400 | u32::from(version))?; Ok(4) @@ -171,6 +245,7 @@ pub fn write_header(mut writer: W, version: u8) -> std::io::Re /// Reads a Pot header. See [`write_header`] for more information. Returns the version number contained within. #[allow(clippy::similar_names, clippy::cast_possible_truncation)] +#[inline] pub fn read_header(reader: &mut R) -> Result { let header = reader.read_u32::()?; if header & 0x506F_7400 == 0x506F_7400 { @@ -181,26 +256,31 @@ pub fn read_header(reader: &mut R) -> Result { } } /// Writes a [`Kind::Special`] atom. +#[inline] pub fn write_special(writer: W, special: Special) -> std::io::Result { - write_atom_header(writer, Kind::Special, Some(special as u64)) + write_atom_header(writer, Kind::Special, special as u64) } /// Writes a [`Kind::Special`] atom with [`Special::None`]. +#[inline] pub fn write_none(writer: W) -> std::io::Result { write_special(writer, Special::None) } /// Writes a [`Kind::Special`] atom with [`Special::Unit`]. +#[inline] pub fn write_unit(writer: W) -> std::io::Result { write_special(writer, Special::Unit) } /// Writes a [`Kind::Special`] atom with [`Special::Named`]. +#[inline] pub fn write_named(writer: W) -> std::io::Result { write_special(writer, Special::Named) } /// Writes a [`Kind::Special`] atom with either [`Special::True`] or [`Special::False`]. +#[inline] pub fn write_bool(writer: W, boolean: bool) -> std::io::Result { write_special( writer, @@ -213,23 +293,22 @@ pub fn write_bool(writer: W, boolean: bool) -> std::io::Result } /// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible. +#[inline] pub fn write_i8(mut writer: W, value: i8) -> std::io::Result { - let header_len = write_atom_header( - &mut writer, - Kind::Int, - Some(std::mem::size_of::() as u64 - 1), - )?; + let header_len = + write_atom_header(&mut writer, Kind::Int, std::mem::size_of::() as u64 - 1)?; writer .write_i8(value) .map(|_| std::mem::size_of::() + header_len) } /// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible. +#[inline] pub fn write_i16(mut writer: W, value: i16) -> std::io::Result { if let Ok(value) = i8::try_from(value) { write_i8(writer, value) } else { - let header_len = write_atom_header(&mut writer, Kind::Int, Some(2 - 1))?; + let header_len = write_tiny_atom_header(&mut writer, Kind::Int, 2 - 1)?; writer .write_i16::(value) .map(|_| 2 + header_len) @@ -237,11 +316,12 @@ pub fn write_i16(mut writer: W, value: i16) -> std::io::Result } /// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible. +#[inline] pub fn write_i24(mut writer: W, value: i32) -> std::io::Result { if let Ok(value) = i16::try_from(value) { write_i16(writer, value) } else { - let header_len = write_atom_header(&mut writer, Kind::Int, Some(3 - 1))?; + let header_len = write_tiny_atom_header(&mut writer, Kind::Int, 3 - 1)?; writer .write_i24::(value) .map(|_| 3 + header_len) @@ -249,11 +329,12 @@ pub fn write_i24(mut writer: W, value: i32) -> std::io::Result } /// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible. +#[inline] pub fn write_i32(mut writer: W, value: i32) -> std::io::Result { if value >= -(2_i32.pow(23)) && value < 2_i32.pow(23) { write_i24(writer, value) } else { - let header_len = write_atom_header(&mut writer, Kind::Int, Some(4 - 1))?; + let header_len = write_tiny_atom_header(&mut writer, Kind::Int, 4 - 1)?; writer .write_i32::(value) .map(|_| 4 + header_len) @@ -261,11 +342,12 @@ pub fn write_i32(mut writer: W, value: i32) -> std::io::Result } /// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible. +#[inline] pub fn write_i48(mut writer: W, value: i64) -> std::io::Result { if let Ok(value) = i32::try_from(value) { write_i32(writer, value) } else { - let header_len = write_atom_header(&mut writer, Kind::Int, Some(6 - 1))?; + let header_len = write_tiny_atom_header(&mut writer, Kind::Int, 6 - 1)?; writer .write_i48::(value) .map(|_| 6 + header_len) @@ -273,11 +355,12 @@ pub fn write_i48(mut writer: W, value: i64) -> std::io::Result } /// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible. +#[inline] pub fn write_i64(mut writer: W, value: i64) -> std::io::Result { if value >= -(2_i64.pow(47)) && value < 2_i64.pow(47) { write_i48(writer, value) } else { - let header_len = write_atom_header(&mut writer, Kind::Int, Some(8 - 1))?; + let header_len = write_tiny_atom_header(&mut writer, Kind::Int, 8 - 1)?; writer .write_i64::(value) .map(|_| 8 + header_len) @@ -285,11 +368,12 @@ pub fn write_i64(mut writer: W, value: i64) -> std::io::Result } /// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible. +#[inline] pub fn write_i128(mut writer: W, value: i128) -> std::io::Result { if let Ok(value) = i64::try_from(value) { write_i64(writer, value) } else { - let header_len = write_atom_header(&mut writer, Kind::Int, Some(16 - 1))?; + let header_len = write_tiny_atom_header(&mut writer, Kind::Int, 16 - 1)?; writer .write_i128::(value) .map(|_| 16 + header_len) @@ -297,27 +381,21 @@ pub fn write_i128(mut writer: W, value: i128) -> std::io::Resu } /// Writes an [`Kind::UInt`] atom with the given value. +#[inline] pub fn write_u8(mut writer: W, value: u8) -> std::io::Result { - let header_len = write_atom_header( - &mut writer, - Kind::UInt, - Some(std::mem::size_of::() as u64 - 1), - )?; + let header_len = write_tiny_atom_header(&mut writer, Kind::UInt, 0)?; writer .write_u8(value) .map(|_| std::mem::size_of::() + header_len) } /// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible. +#[inline] pub fn write_u16(mut writer: W, value: u16) -> std::io::Result { if let Ok(value) = u8::try_from(value) { write_u8(writer, value) } else { - let header_len = write_atom_header( - &mut writer, - Kind::UInt, - Some(std::mem::size_of::() as u64 - 1), - )?; + let header_len = write_tiny_atom_header(&mut writer, Kind::UInt, 1)?; writer .write_u16::(value) .map(|_| std::mem::size_of::() + header_len) @@ -325,11 +403,12 @@ pub fn write_u16(mut writer: W, value: u16) -> std::io::Result } /// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible. +#[inline] pub fn write_u24(mut writer: W, value: u32) -> std::io::Result { if let Ok(value) = u16::try_from(value) { write_u16(writer, value) } else { - let header_len = write_atom_header(&mut writer, Kind::UInt, Some(3 - 1))?; + let header_len = write_tiny_atom_header(&mut writer, Kind::UInt, 2)?; writer .write_u24::(value) .map(|_| 3 + header_len) @@ -337,11 +416,12 @@ pub fn write_u24(mut writer: W, value: u32) -> std::io::Result } /// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible. +#[inline] pub fn write_u32(mut writer: W, value: u32) -> std::io::Result { if value < 2_u32.pow(24) { write_u24(writer, value) } else { - let header_len = write_atom_header(&mut writer, Kind::UInt, Some(4 - 1))?; + let header_len = write_tiny_atom_header(&mut writer, Kind::UInt, 3)?; writer .write_u32::(value) .map(|_| std::mem::size_of::() + header_len) @@ -349,11 +429,12 @@ pub fn write_u32(mut writer: W, value: u32) -> std::io::Result } /// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible. +#[inline] pub fn write_u48(mut writer: W, value: u64) -> std::io::Result { if let Ok(value) = u32::try_from(value) { write_u32(writer, value) } else { - let header_len = write_atom_header(&mut writer, Kind::UInt, Some(6 - 1))?; + let header_len = write_tiny_atom_header(&mut writer, Kind::UInt, 5)?; writer .write_u48::(value) .map(|_| 6 + header_len) @@ -361,11 +442,12 @@ pub fn write_u48(mut writer: W, value: u64) -> std::io::Result } /// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible. +#[inline] pub fn write_u64(mut writer: W, value: u64) -> std::io::Result { if value < 2_u64.pow(48) { write_u48(writer, value) } else { - let header_len = write_atom_header(&mut writer, Kind::UInt, Some(8 - 1))?; + let header_len = write_tiny_atom_header(&mut writer, Kind::UInt, 7)?; writer .write_u64::(value) .map(|_| std::mem::size_of::() + header_len) @@ -373,11 +455,12 @@ pub fn write_u64(mut writer: W, value: u64) -> std::io::Result } /// Writes an [`Kind::Int`] atom with the given value. Will encode in a smaller format if possible. +#[inline] pub fn write_u128(mut writer: W, value: u128) -> std::io::Result { if let Ok(value) = u64::try_from(value) { write_u64(writer, value) } else { - let header_len = write_atom_header(&mut writer, Kind::UInt, Some(16 - 1))?; + let header_len = write_tiny_atom_header(&mut writer, Kind::UInt, 15)?; writer .write_u128::(value) .map(|_| std::mem::size_of::() + header_len) @@ -385,23 +468,24 @@ pub fn write_u128(mut writer: W, value: u128) -> std::io::Resu } /// Writes an [`Kind::Float`] atom with the given value. +#[inline] #[allow(clippy::cast_possible_truncation, clippy::float_cmp)] pub fn write_f32(mut writer: W, value: f32) -> std::io::Result { let as_f16 = f16::from_f32(value); if as_f16.to_f32() == value { - let header_len = write_atom_header( + let header_len = write_tiny_atom_header( &mut writer, Kind::Float, - Some(std::mem::size_of::() as u64 - 1), + std::mem::size_of::() as u8 - 1, )?; writer .write_u16::(as_f16.to_bits()) .map(|_| std::mem::size_of::() + header_len) } else { - let header_len = write_atom_header( + let header_len = write_tiny_atom_header( &mut writer, Kind::Float, - Some(std::mem::size_of::() as u64 - 1), + std::mem::size_of::() as u8 - 1, )?; writer .write_f32::(value) @@ -416,16 +500,13 @@ fn read_f16(reader: &mut R) -> std::io::Result { /// Writes an [`Kind::Float`] atom with the given value. #[allow(clippy::cast_possible_truncation, clippy::float_cmp)] +#[inline] pub fn write_f64(mut writer: W, value: f64) -> std::io::Result { let as_f32 = value as f32; if f64::from(as_f32) == value { write_f32(writer, as_f32) } else { - let header_len = write_atom_header( - &mut writer, - Kind::Float, - Some(std::mem::size_of::() as u64 - 1), - )?; + let header_len = write_tiny_atom_header(&mut writer, Kind::Float, 7)?; writer .write_f64::(value) .map(|_| std::mem::size_of::() + header_len) @@ -433,13 +514,15 @@ pub fn write_f64(mut writer: W, value: f64) -> std::io::Result } /// Writes an [`Kind::Bytes`] atom with the bytes of the string. +#[inline] pub fn write_str(writer: W, value: &str) -> std::io::Result { write_bytes(writer, value.as_bytes()) } /// Writes an [`Kind::Bytes`] atom with the given value. +#[inline] pub fn write_bytes(mut writer: W, value: &[u8]) -> std::io::Result { - let header_len = write_atom_header(&mut writer, Kind::Bytes, Some(value.len() as u64))?; + let header_len = write_atom_header(&mut writer, Kind::Bytes, value.len() as u64)?; writer.write_all(value)?; Ok(value.len() + header_len) } @@ -493,6 +576,7 @@ impl Display for Integer { impl Integer { /// Returns true if the value contained is zero. #[must_use] + #[inline] pub const fn is_zero(&self) -> bool { match &self.0 { InnerInteger::I8(value) => *value == 0, @@ -512,6 +596,7 @@ impl Integer { // clippy::checked_conversions: try_from isn't const, and it would demote this from a const fn. #[allow(clippy::cast_possible_wrap)] #[allow(clippy::checked_conversions)] + #[inline] pub const fn as_i8(&self) -> Result { match &self.0 { InnerInteger::I8(value) => Ok(*value), @@ -528,6 +613,7 @@ impl Integer { /// Returns the contained value as an u8, or an error if the value is unable to fit. #[allow(clippy::cast_sign_loss)] + #[inline] pub const fn as_u8(&self) -> Result { match &self.0 { InnerInteger::U8(value) => Ok(*value), @@ -545,6 +631,7 @@ impl Integer { /// Returns the contained value as an i16, or an error if the value is unable to fit. #[allow(clippy::cast_possible_wrap)] #[allow(clippy::checked_conversions)] + #[inline] pub const fn as_i16(&self) -> Result { match &self.0 { InnerInteger::I8(value) => Ok(*value as i16), @@ -568,6 +655,7 @@ impl Integer { /// Returns the contained value as an u16, or an error if the value is unable to fit. #[allow(clippy::cast_sign_loss)] + #[inline] pub const fn as_u16(&self) -> Result { match &self.0 { InnerInteger::I8(value) => { @@ -598,6 +686,7 @@ impl Integer { /// Returns the contained value as an i32, or an error if the value is unable to fit. #[allow(clippy::cast_possible_wrap)] #[allow(clippy::checked_conversions)] + #[inline] pub const fn as_i32(&self) -> Result { match &self.0 { InnerInteger::I8(value) => Ok(*value as i32), @@ -621,6 +710,7 @@ impl Integer { /// Returns the contained value as an u32, or an error if the value is unable to fit. #[allow(clippy::cast_sign_loss)] + #[inline] pub const fn as_u32(&self) -> Result { match &self.0 { InnerInteger::I8(value) => { @@ -657,6 +747,7 @@ impl Integer { /// Returns the contained value as an i64, or an error if the value is unable to fit. #[allow(clippy::cast_possible_wrap)] #[allow(clippy::checked_conversions)] + #[inline] pub const fn as_i64(&self) -> Result { match &self.0 { InnerInteger::I8(value) => Ok(*value as i64), @@ -679,6 +770,7 @@ impl Integer { /// Returns the contained value as an i64, or an error if the value is unable to fit. #[allow(clippy::cast_possible_wrap)] + #[inline] pub const fn as_i128(&self) -> Result { match &self.0 { InnerInteger::I8(value) => Ok(*value as i128), @@ -702,6 +794,7 @@ impl Integer { /// Returns the contained value as an u64, or an error if the value is unable to fit. #[allow(clippy::cast_sign_loss)] + #[inline] pub const fn as_u64(&self) -> Result { match &self.0 { InnerInteger::I8(value) => { @@ -742,6 +835,7 @@ impl Integer { /// Returns the contained value as an u64, or an error if the value is unable to fit. #[allow(clippy::cast_sign_loss)] + #[inline] pub const fn as_u128(&self) -> Result { match &self.0 { InnerInteger::I8(value) => { @@ -788,6 +882,7 @@ impl Integer { } /// Writes this value using the smallest form possible. + #[inline] pub fn write_to(&self, writer: W) -> std::io::Result { match self.0 { InnerInteger::I8(value) => write_i8(writer, value), @@ -805,6 +900,7 @@ impl Integer { /// Reads an integer based on the atom header (`kind` and `byte_len`). /// `byte_len` should be the argument from the atom header directly. + #[inline] pub fn read_from( kind: Kind, byte_len: usize, @@ -838,6 +934,7 @@ impl Integer { /// Converts this integer to an f32, but only if it can be done without losing precision. #[allow(clippy::cast_precision_loss)] + #[inline] pub fn as_f32(&self) -> Result { let int = self.as_i32()?; if int < -(2_i32.pow(f32::MANTISSA_DIGITS)) || int >= 2_i32.pow(f32::MANTISSA_DIGITS) { @@ -849,6 +946,7 @@ impl Integer { /// Converts this integer to an f64, but only if it can be done without losing precision. #[allow(clippy::cast_precision_loss)] + #[inline] pub fn as_f64(&self) -> Result { let int = self.as_i64()?; if int < -(2_i64.pow(f64::MANTISSA_DIGITS)) || int >= 2_i64.pow(f64::MANTISSA_DIGITS) { @@ -860,6 +958,7 @@ impl Integer { /// Converts this integer to an f64, but only if it can be done without losing precision. #[allow(clippy::cast_precision_loss)] + #[inline] pub fn as_float(&self) -> Result { self.as_f32() .map(Float::from) @@ -868,6 +967,7 @@ impl Integer { } impl From for Integer { + #[inline] fn from(value: u8) -> Self { Self(InnerInteger::U8(value)) } @@ -876,6 +976,7 @@ impl From for Integer { macro_rules! impl_from_unsigned_integer { ($primitive:ty, $smaller_primitive:ty, $variant:ident) => { impl From<$primitive> for Integer { + #[inline] fn from(value: $primitive) -> Self { if let Ok(value) = <$smaller_primitive>::try_from(value) { Self::from(value as $smaller_primitive) @@ -893,6 +994,7 @@ impl_from_unsigned_integer!(u64, u32, U64); impl_from_unsigned_integer!(u128, u64, U128); impl From for Integer { + #[inline] fn from(value: i8) -> Self { Self(InnerInteger::I8(value)) } @@ -901,6 +1003,7 @@ impl From for Integer { macro_rules! impl_from_unsigned_integer { ($primitive:ty, $smaller_primitive:ty, $smaller_unsigned_primitive:ty, $variant:ident) => { impl From<$primitive> for Integer { + #[inline] fn from(value: $primitive) -> Self { if let Ok(value) = <$smaller_primitive>::try_from(value) { Self::from(value as $smaller_primitive) @@ -921,9 +1024,11 @@ impl_from_unsigned_integer!(i128, i64, u64, I128); /// Reads an atom. #[allow(clippy::cast_possible_truncation)] +#[inline] pub fn read_atom<'de, R: Reader<'de>>( reader: &mut R, remaining_budget: &mut usize, + scratch: &mut Vec, ) -> Result, Error> { let (kind, arg) = read_atom_header(reader)?; Ok(match kind { @@ -966,7 +1071,7 @@ pub fn read_atom<'de, R: Reader<'de>>( Kind::Bytes => { let bytes = arg as usize; update_budget(remaining_budget, bytes)?; - let bytes = reader.buffered_read_bytes(bytes)?; + let bytes = reader.buffered_read_bytes(bytes, scratch)?; Atom { kind, arg, @@ -976,6 +1081,7 @@ pub fn read_atom<'de, R: Reader<'de>>( }) } +#[inline] pub(crate) const fn in_memory_int_size(encoded_length: usize) -> usize { // Some integers are stored more compact than we can represent them in memory match encoded_length { @@ -985,6 +1091,7 @@ pub(crate) const fn in_memory_int_size(encoded_length: usize) -> usize { } } +#[inline] pub(crate) fn update_budget(budget: &mut usize, read_amount: usize) -> Result<(), Error> { if let Some(remaining) = budget.checked_sub(read_amount) { *budget = remaining; @@ -1019,18 +1126,21 @@ pub(crate) enum InnerFloat { } impl From for Float { + #[inline] fn from(value: f32) -> Self { Self(InnerFloat::F32(value)) } } impl From for Float { + #[inline] fn from(value: f64) -> Self { Self(InnerFloat::F64(value)) } } impl PartialEq for InnerFloat { + #[inline] fn eq(&self, other: &Self) -> bool { match (self, other) { (InnerFloat::F64(left), InnerFloat::F64(right)) => left == right, @@ -1053,6 +1163,7 @@ impl Display for Float { impl Float { /// Returns true if the value contained is zero. #[must_use] + #[inline] pub fn is_zero(&self) -> bool { match self.0 { InnerFloat::F32(value) => value.abs() <= f32::EPSILON, @@ -1062,6 +1173,7 @@ impl Float { /// Returns this number as an f32, if it can be done without losing precision. #[allow(clippy::float_cmp, clippy::cast_possible_truncation)] + #[inline] pub fn as_f32(&self) -> Result { match self.0 { InnerFloat::F32(value) => Ok(value), @@ -1078,6 +1190,7 @@ impl Float { /// Returns this number as an f64. #[must_use] + #[inline] pub const fn as_f64(&self) -> f64 { match self.0 { InnerFloat::F64(value) => value, @@ -1087,6 +1200,7 @@ impl Float { /// Returns this number as an [`Integer`], if the stored value has no fractional part. #[allow(clippy::cast_possible_truncation)] + #[inline] pub fn as_integer(&self) -> Result { match self.0 { InnerFloat::F64(value) => { @@ -1108,6 +1222,7 @@ impl Float { } /// Writes this value using the smallest form possible. + #[inline] pub fn write_to(&self, writer: W) -> std::io::Result { match self.0 { InnerFloat::F64(float) => write_f64(writer, float), @@ -1117,6 +1232,7 @@ impl Float { /// Reads a floating point number given the atom `kind` and `byte_len`. /// `byte_len` should be the exact argument from the atom header. + #[inline] pub fn read_from( kind: Kind, byte_len: usize, @@ -1145,7 +1261,7 @@ pub enum Nucleus<'de> { /// A floating point value. Float(Float), /// A buffer of bytes. - Bytes(Cow<'de, [u8]>), + Bytes(BufferedBytes<'de>), /// A unit. Unit, /// A named value. @@ -1203,14 +1319,14 @@ mod tests { fn atom_headers() { let mut out = Vec::new(); { - write_atom_header(&mut out, Kind::Map, Some(32)).unwrap(); + write_atom_header(&mut out, Kind::Map, 32).unwrap(); let (kind, arg) = read_atom_header(&mut out.as_slice()).unwrap(); assert_eq!(kind, Kind::Map); assert_eq!(arg, 32); } out.clear(); - write_atom_header(&mut out, Kind::Map, Some(u64::MAX)).unwrap(); + write_atom_header(&mut out, Kind::Map, u64::MAX).unwrap(); println!("header: {out:?}"); let (kind, arg) = read_atom_header(&mut out.as_slice()).unwrap(); assert_eq!(kind, Kind::Map); diff --git a/pot/src/lib.rs b/pot/src/lib.rs index 0776a340..b0dd2dd6 100644 --- a/pot/src/lib.rs +++ b/pot/src/lib.rs @@ -45,6 +45,7 @@ use crate::reader::IoReader; /// let deserialized = pot::from_slice::(&serialized).unwrap(); /// assert_eq!(deserialized, "hello world"); /// ``` +#[inline] pub fn to_vec(value: &T) -> Result> where T: Serialize, @@ -60,6 +61,7 @@ where /// let deserialized = pot::from_reader::(&serialized[..]).unwrap(); /// assert_eq!(deserialized, "hello world"); /// ``` +#[inline] pub fn to_writer(value: &T, writer: W) -> Result<()> where T: Serialize, @@ -75,6 +77,7 @@ where /// let deserialized = pot::from_slice::(&serialized).unwrap(); /// assert_eq!(deserialized, "hello world"); /// ``` +#[inline] pub fn from_slice<'a, T>(serialized: &'a [u8]) -> Result where T: Deserialize<'a>, @@ -90,6 +93,7 @@ where /// let deserialized = pot::from_reader::(&serialized[..]).unwrap(); /// assert_eq!(deserialized, "hello world"); /// ``` +#[inline] pub fn from_reader(reader: R) -> Result where T: DeserializeOwned, @@ -106,6 +110,7 @@ pub struct Config { } impl Default for Config { + #[inline] fn default() -> Self { Self { allocation_budget: usize::MAX, @@ -121,12 +126,14 @@ impl Config { /// can be aware of. /// /// The default allocation budget is [`usize::MAX`]. + #[inline] pub const fn allocation_budget(mut self, budget: usize) -> Self { self.allocation_budget = budget; self } /// Deserializes a value from a slice using the configured options. + #[inline] pub fn deserialize<'de, T>(&self, serialized: &'de [u8]) -> Result where T: Deserialize<'de>, @@ -142,6 +149,7 @@ impl Config { /// Deserializes a value from a [`Read`] implementer using the configured /// options. + #[inline] pub fn deserialize_from(&self, reader: R) -> Result where T: DeserializeOwned, @@ -162,6 +170,7 @@ impl Config { /// Serializes a value to a writer using the configured options. #[allow(clippy::unused_self)] + #[inline] pub fn serialize_into(&self, value: &T, writer: W) -> Result<()> where T: Serialize, @@ -178,8 +187,6 @@ mod tests { use std::marker::PhantomData; use serde::{Deserializer, Serializer}; - use serde_json::value::Value as JsonValue; - use serde_json::Number; use super::*; use crate::format::{Float, Integer, CURRENT_VERSION}; @@ -450,25 +457,6 @@ mod tests { test_serialization(&TupleStruct(1, 2), None); } - #[test] - fn json_value() { - test_serialization(&JsonValue::Null, None); - test_serialization(&JsonValue::Bool(false), None); - test_serialization(&JsonValue::Bool(true), None); - test_serialization( - &JsonValue::Array(vec![serde_json::value::Value::Null]), - None, - ); - test_serialization(&JsonValue::Number(Number::from_f64(1.).unwrap()), None); - test_serialization(&JsonValue::String(String::from("Hello world")), None); - test_serialization( - &JsonValue::Object( - std::iter::once((String::from("key"), JsonValue::Bool(true))).collect(), - ), - None, - ); - } - #[test] fn value() { macro_rules! roundtrip { @@ -561,7 +549,7 @@ mod tests { fn invalid_symbol() { let mut valid_bytes = Vec::new(); format::write_header(&mut valid_bytes, CURRENT_VERSION).unwrap(); - format::write_atom_header(&mut valid_bytes, format::Kind::Symbol, Some(4)).unwrap(); + format::write_atom_header(&mut valid_bytes, format::Kind::Symbol, 4).unwrap(); format::write_bytes(&mut valid_bytes, &0xFFFF_FFFF_u32.to_be_bytes()).unwrap(); assert!(matches!( @@ -577,7 +565,7 @@ mod tests { format::write_atom_header( &mut invalid_bytes, format::Kind::Special, - Some(format::SPECIAL_COUNT), + format::SPECIAL_COUNT, ) .unwrap(); @@ -642,8 +630,7 @@ mod tests { fn invalid_numbers() { let mut invalid_float_byte_len = Vec::new(); format::write_header(&mut invalid_float_byte_len, CURRENT_VERSION).unwrap(); - format::write_atom_header(&mut invalid_float_byte_len, format::Kind::Float, Some(0)) - .unwrap(); + format::write_atom_header(&mut invalid_float_byte_len, format::Kind::Float, 0).unwrap(); assert!(matches!(from_slice::(&invalid_float_byte_len), Err(_))); @@ -654,8 +641,7 @@ mod tests { let mut invalid_signed_byte_len = Vec::new(); format::write_header(&mut invalid_signed_byte_len, CURRENT_VERSION).unwrap(); - format::write_atom_header(&mut invalid_signed_byte_len, format::Kind::Int, Some(10)) - .unwrap(); + format::write_atom_header(&mut invalid_signed_byte_len, format::Kind::Int, 10).unwrap(); assert!(matches!( from_slice::(&invalid_signed_byte_len), @@ -669,8 +655,7 @@ mod tests { let mut invalid_unsigned_byte_len = Vec::new(); format::write_header(&mut invalid_unsigned_byte_len, CURRENT_VERSION).unwrap(); - format::write_atom_header(&mut invalid_unsigned_byte_len, format::Kind::UInt, Some(10)) - .unwrap(); + format::write_atom_header(&mut invalid_unsigned_byte_len, format::Kind::UInt, 10).unwrap(); assert!(matches!( from_slice::(&invalid_unsigned_byte_len), @@ -695,7 +680,7 @@ mod tests { fn unexpected_eof() { let mut invalid_bytes = Vec::new(); format::write_header(&mut invalid_bytes, CURRENT_VERSION).unwrap(); - format::write_atom_header(&mut invalid_bytes, format::Kind::Bytes, Some(10)).unwrap(); + format::write_atom_header(&mut invalid_bytes, format::Kind::Bytes, 10).unwrap(); assert!(matches!( from_slice::>(&invalid_bytes), Err(Error::Eof) @@ -706,7 +691,7 @@ mod tests { fn too_big_read() { let mut invalid_bytes = Vec::new(); format::write_header(&mut invalid_bytes, CURRENT_VERSION).unwrap(); - format::write_atom_header(&mut invalid_bytes, format::Kind::Bytes, Some(10)).unwrap(); + format::write_atom_header(&mut invalid_bytes, format::Kind::Bytes, 10).unwrap(); assert!(matches!( Config::default() .allocation_budget(9) diff --git a/pot/src/reader.rs b/pot/src/reader.rs index c50ead22..1b75f8c6 100644 --- a/pot/src/reader.rs +++ b/pot/src/reader.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::fmt::Debug; use std::io::Read; @@ -8,8 +7,39 @@ use crate::Error; /// A reader that can temporarily buffer bytes read. pub trait Reader<'de>: ReadBytesExt { - /// Reads exactly `length` bytes and returns a reference to the buffer. - fn buffered_read_bytes(&mut self, length: usize) -> Result, Error>; + /// Reads exactly `length` bytes. + /// + /// If the reader supports borrowing bytes, [`BufferedBytes::Data`] should + /// be returned. Otherwise, the bytes will be read into `scratch`. `scratch` + /// should only be assumed to be valid if [`BufferedBytes::Scratch`] is + /// returned. + fn buffered_read_bytes( + &mut self, + length: usize, + scratch: &mut Vec, + ) -> Result, Error>; +} + +/// Bytes that have been read into a buffer. +#[derive(Debug)] +pub enum BufferedBytes<'de> { + /// The bytes that have been read can be borrowed from the source. + Data(&'de [u8]), + /// The bytes that have been read have been stored in the scratch buffer + /// passed to the function reading bytes. + Scratch, +} + +impl BufferedBytes<'_> { + /// Resolves the bytes to a byte slice. + #[inline] + #[must_use] + pub fn as_slice<'a>(&'a self, scratch: &'a [u8]) -> &'a [u8] { + match self { + BufferedBytes::Data(data) => data, + BufferedBytes::Scratch => scratch, + } + } } /// Reads data from a slice. @@ -21,12 +51,14 @@ pub struct SliceReader<'a> { impl<'a> SliceReader<'a> { /// Returns the remaining bytes to read. #[must_use] + #[inline] pub const fn len(&self) -> usize { self.data.len() } /// Returns `true` if there are no bytes remaining to read. #[must_use] + #[inline] pub const fn is_empty(&self) -> bool { self.data.is_empty() } @@ -44,31 +76,39 @@ impl<'a> Debug for SliceReader<'a> { } impl<'a> From<&'a [u8]> for SliceReader<'a> { + #[inline] fn from(data: &'a [u8]) -> Self { Self { data } } } impl<'a> From> for &'a [u8] { + #[inline] fn from(reader: SliceReader<'a>) -> Self { reader.data } } impl<'de> Reader<'de> for SliceReader<'de> { - fn buffered_read_bytes(&mut self, length: usize) -> Result, Error> { + #[inline] + fn buffered_read_bytes( + &mut self, + length: usize, + _scratch: &mut Vec, + ) -> Result, Error> { if length > self.data.len() { self.data = &self.data[self.data.len()..]; Err(Error::Eof) } else { let (start, remaining) = self.data.split_at(length); self.data = remaining; - Ok(Cow::Borrowed(start)) + Ok(BufferedBytes::Data(start)) } } } impl<'a> Read for SliceReader<'a> { + #[inline] fn read(&mut self, buf: &mut [u8]) -> std::io::Result { let remaining_length = self.data.len(); let (to_copy, remaining) = self.data.split_at(remaining_length.min(buf.len())); @@ -77,6 +117,7 @@ impl<'a> Read for SliceReader<'a> { Ok(to_copy.len()) } + #[inline] fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> { self.read(buf).map(|_| ()) } @@ -94,30 +135,40 @@ impl IoReader { } impl<'de, R: ReadBytesExt> Reader<'de> for IoReader { - fn buffered_read_bytes(&mut self, length: usize) -> Result, Error> { - let mut buffer = vec![0; length]; - self.reader.read_exact(&mut buffer)?; - Ok(Cow::Owned(buffer)) + #[inline] + fn buffered_read_bytes( + &mut self, + length: usize, + scratch: &mut Vec, + ) -> Result, Error> { + scratch.resize(length, 0); + self.reader.read_exact(scratch)?; + Ok(BufferedBytes::Scratch) } } impl Read for IoReader { + #[inline] fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.reader.read(buf) } + #[inline] fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result { self.reader.read_vectored(bufs) } + #[inline] fn read_to_end(&mut self, buf: &mut Vec) -> std::io::Result { self.reader.read_to_end(buf) } + #[inline] fn read_to_string(&mut self, buf: &mut String) -> std::io::Result { self.reader.read_to_string(buf) } + #[inline] fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> { self.reader.read_exact(buf) } diff --git a/pot/src/ser.rs b/pot/src/ser.rs index 980d4a82..232f316e 100644 --- a/pot/src/ser.rs +++ b/pot/src/ser.rs @@ -28,6 +28,7 @@ impl<'a, W: WriteBytesExt> Debug for Serializer<'a, W> { impl<'a, W: WriteBytesExt> Serializer<'a, W> { /// Returns a new serializer outputting written bytes into `output`. + #[inline] pub fn new(output: W) -> Result { Self::new_with_symbol_map(output, SymbolMapRef::Owned(SymbolMap::default())) } @@ -47,8 +48,7 @@ impl<'a, W: WriteBytesExt> Serializer<'a, W> { if registered_symbol.new { // The arg is the length followed by a 0 bit. let arg = (symbol.len() as u64) << 1; - self.bytes_written += - format::write_atom_header(&mut self.output, Kind::Symbol, Some(arg))?; + self.bytes_written += format::write_atom_header(&mut self.output, Kind::Symbol, arg)?; self.output.write_all(symbol.as_bytes())?; self.bytes_written += symbol.len(); } else { @@ -56,7 +56,7 @@ impl<'a, W: WriteBytesExt> Serializer<'a, W> { self.bytes_written += format::write_atom_header( &mut self.output, Kind::Symbol, - Some(u64::from((registered_symbol.id << 1) | 1)), + u64::from((registered_symbol.id << 1) | 1), )?; } Ok(()) @@ -74,113 +74,132 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::Serializer for &'de mut Serialize type SerializeTupleStruct = Self; type SerializeTupleVariant = Self; + #[inline] fn is_human_readable(&self) -> bool { false } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_bool(self, v: bool) -> Result<()> { self.bytes_written += format::write_bool(&mut self.output, v)?; Ok(()) } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_i8(self, v: i8) -> Result<()> { self.bytes_written += format::write_i8(&mut self.output, v)?; Ok(()) } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_i16(self, v: i16) -> Result<()> { self.bytes_written += format::write_i16(&mut self.output, v)?; Ok(()) } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_i32(self, v: i32) -> Result<()> { self.bytes_written += format::write_i32(&mut self.output, v)?; Ok(()) } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_i64(self, v: i64) -> Result<()> { self.bytes_written += format::write_i64(&mut self.output, v)?; Ok(()) } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_i128(self, v: i128) -> Result<()> { self.bytes_written += format::write_i128(&mut self.output, v)?; Ok(()) } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_u8(self, v: u8) -> Result<()> { self.bytes_written += format::write_u8(&mut self.output, v)?; Ok(()) } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_u16(self, v: u16) -> Result<()> { self.bytes_written += format::write_u16(&mut self.output, v)?; Ok(()) } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_u32(self, v: u32) -> Result<()> { self.bytes_written += format::write_u32(&mut self.output, v)?; Ok(()) } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_u64(self, v: u64) -> Result<()> { self.bytes_written += format::write_u64(&mut self.output, v)?; Ok(()) } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_u128(self, v: u128) -> Result<()> { self.bytes_written += format::write_u128(&mut self.output, v)?; Ok(()) } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_f32(self, v: f32) -> Result<()> { self.bytes_written += format::write_f32(&mut self.output, v)?; Ok(()) } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_f64(self, v: f64) -> Result<()> { self.bytes_written += format::write_f64(&mut self.output, v)?; Ok(()) } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_char(self, v: char) -> Result<()> { self.bytes_written += format::write_u32(&mut self.output, v as u32)?; Ok(()) } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_str(self, v: &str) -> Result<()> { self.bytes_written += format::write_str(&mut self.output, v)?; Ok(()) } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_bytes(self, v: &[u8]) -> Result<()> { self.bytes_written += format::write_bytes(&mut self.output, v)?; Ok(()) } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_none(self) -> Result<()> { self.bytes_written += format::write_none(&mut self.output)?; Ok(()) } #[cfg_attr(feature = "tracing", instrument(skip(value)))] + #[inline] fn serialize_some(self, value: &T) -> Result<()> where T: ?Sized + Serialize, @@ -189,17 +208,20 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::Serializer for &'de mut Serialize } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_unit(self) -> Result<()> { self.bytes_written += format::write_unit(&mut self.output)?; Ok(()) } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { self.serialize_unit() } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_unit_variant( self, _name: &'static str, @@ -212,6 +234,7 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::Serializer for &'de mut Serialize } #[cfg_attr(feature = "tracing", instrument(skip(value)))] + #[inline] fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, @@ -220,6 +243,7 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::Serializer for &'de mut Serialize } #[cfg_attr(feature = "tracing", instrument(skip(value)))] + #[inline] fn serialize_newtype_variant( self, _name: &'static str, @@ -237,19 +261,22 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::Serializer for &'de mut Serialize } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_seq(self, len: Option) -> Result { let len = len.ok_or(Error::SequenceSizeMustBeKnown)?; self.bytes_written += - format::write_atom_header(&mut self.output, Kind::Sequence, Some(len as u64))?; + format::write_atom_header(&mut self.output, Kind::Sequence, len as u64)?; Ok(self) } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_tuple(self, len: usize) -> Result { self.serialize_seq(Some(len)) } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_tuple_struct( self, _name: &'static str, @@ -259,6 +286,7 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::Serializer for &'de mut Serialize } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_tuple_variant( self, _name: &'static str, @@ -272,10 +300,11 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::Serializer for &'de mut Serialize } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_map(self, len: Option) -> Result { if let Some(len) = len { self.bytes_written += - format::write_atom_header(&mut self.output, Kind::Map, Some(len as u64))?; + format::write_atom_header(&mut self.output, Kind::Map, len as u64)?; Ok(MapSerializer { serializer: self, known_length: true, @@ -290,11 +319,13 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::Serializer for &'de mut Serialize } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_struct(self, _name: &'static str, len: usize) -> Result { self.serialize_map(Some(len)) } #[cfg_attr(feature = "tracing", instrument)] + #[inline] fn serialize_struct_variant( self, name: &'static str, @@ -312,6 +343,7 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::SerializeSeq for &'de mut Seriali type Error = Error; type Ok = (); + #[inline] fn serialize_element(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, @@ -319,6 +351,7 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::SerializeSeq for &'de mut Seriali value.serialize(&mut **self) } + #[inline] fn end(self) -> Result<()> { Ok(()) } @@ -328,6 +361,7 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::SerializeTuple for &'de mut Seria type Error = Error; type Ok = (); + #[inline] fn serialize_element(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, @@ -335,6 +369,7 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::SerializeTuple for &'de mut Seria value.serialize(&mut **self) } + #[inline] fn end(self) -> Result<()> { Ok(()) } @@ -344,6 +379,7 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::SerializeTupleStruct for &'de mut type Error = Error; type Ok = (); + #[inline] fn serialize_field(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, @@ -351,6 +387,7 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::SerializeTupleStruct for &'de mut value.serialize(&mut **self) } + #[inline] fn end(self) -> Result<()> { Ok(()) } @@ -362,6 +399,7 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::SerializeTupleVariant type Error = Error; type Ok = (); + #[inline] fn serialize_field(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, @@ -369,6 +407,7 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::SerializeTupleVariant value.serialize(&mut **self) } + #[inline] fn end(self) -> Result<()> { Ok(()) } @@ -384,6 +423,7 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::SerializeMap for MapSerializer<'d type Error = Error; type Ok = (); + #[inline] fn serialize_key(&mut self, key: &T) -> Result<()> where T: ?Sized + Serialize, @@ -391,6 +431,7 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::SerializeMap for MapSerializer<'d key.serialize(&mut *self.serializer) } + #[inline] fn serialize_value(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, @@ -398,6 +439,7 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::SerializeMap for MapSerializer<'d value.serialize(&mut *self.serializer) } + #[inline] fn end(self) -> Result<()> { if !self.known_length { format::write_special(&mut self.serializer.output, Special::DynamicEnd)?; @@ -410,6 +452,7 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::SerializeStruct for MapSerializer type Error = Error; type Ok = (); + #[inline] fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, @@ -418,6 +461,7 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::SerializeStruct for MapSerializer value.serialize(&mut *self.serializer) } + #[inline] fn end(self) -> Result<()> { if !self.known_length { format::write_special(&mut self.serializer.output, Special::DynamicEnd)?; @@ -432,6 +476,7 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::SerializeStructVariant type Error = Error; type Ok = (); + #[inline] fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, @@ -440,6 +485,7 @@ impl<'de, 'a: 'de, W: WriteBytesExt + 'a> ser::SerializeStructVariant value.serialize(&mut *self.serializer) } + #[inline] fn end(self) -> Result<()> { if !self.known_length { format::write_special(&mut self.serializer.output, Special::DynamicEnd)?; @@ -455,6 +501,7 @@ pub struct SymbolMap { } impl Default for SymbolMap { + #[inline] fn default() -> Self { let mut symbols = Vec::default(); // TODO make this configurable @@ -471,6 +518,7 @@ struct RegisteredSymbol { impl SymbolMap { /// Returns a serializer that writes into `output` and persists symbols /// into `self`. + #[inline] pub fn serializer_for(&mut self, output: W) -> Result> { Serializer::new_with_symbol_map(output, SymbolMapRef::Borrowed(self)) } @@ -508,6 +556,7 @@ enum SymbolMapRef<'a> { impl<'a> Deref for SymbolMapRef<'a> { type Target = SymbolMap; + #[inline] fn deref(&self) -> &Self::Target { match self { SymbolMapRef::Owned(map) => map, @@ -517,6 +566,7 @@ impl<'a> Deref for SymbolMapRef<'a> { } impl<'a> DerefMut for SymbolMapRef<'a> { + #[inline] fn deref_mut(&mut self) -> &mut Self::Target { match self { SymbolMapRef::Owned(map) => map, diff --git a/pot/src/value.rs b/pot/src/value.rs index 54e40ec4..79891214 100644 --- a/pot/src/value.rs +++ b/pot/src/value.rs @@ -108,6 +108,7 @@ impl<'a> Value<'a> { /// # Ok(()) /// # } /// ``` + #[inline] pub fn from_serialize(value: T) -> Result { value.serialize(Serializer) } @@ -132,6 +133,7 @@ impl<'a> Value<'a> { /// # Ok(()) /// # } /// ``` + #[inline] pub fn deserialize_as<'de, T: Deserialize<'de>>(&'de self) -> Result { T::deserialize(Deserializer(self)) } @@ -143,6 +145,7 @@ impl<'a> Value<'a> { /// let mappings = Value::from_sequence(Vec::::new()); /// assert!(matches!(mappings, Value::Sequence(_))); /// ``` + #[inline] pub fn from_sequence, T: Into>( sequence: IntoIter, ) -> Self { @@ -157,6 +160,7 @@ impl<'a> Value<'a> { /// let mappings = Value::from_mappings(HashMap::::new()); /// assert!(matches!(mappings, Value::Mappings(_))); /// ``` + #[inline] pub fn from_mappings, K: Into, V: Into>( mappings: IntoIter, ) -> Self { @@ -199,6 +203,7 @@ impl<'a> Value<'a> { /// ); /// ``` #[must_use] + #[inline] pub fn is_empty(&self) -> bool { match self { Value::None => true, @@ -248,6 +253,7 @@ impl<'a> Value<'a> { /// ); /// ``` #[must_use] + #[inline] pub fn as_bool(&self) -> bool { match self { Value::None => false, @@ -266,6 +272,7 @@ impl<'a> Value<'a> { /// [`Self::Float`] or [`Self::Integer`]. Also returns `None` if the value is /// a float, but cannot be losslessly converted to an integer. #[must_use] + #[inline] pub fn as_integer(&self) -> Option { match self { Value::Integer(value) => Some(*value), @@ -278,6 +285,7 @@ impl<'a> Value<'a> { /// [`Self::Float`] or [`Self::Integer`]. Also returns `None` if the value is /// an integer, but cannot be losslessly converted to a float. #[must_use] + #[inline] pub fn as_float(&self) -> Option { match self { Value::Integer(value) => value.as_float().ok(), @@ -291,6 +299,7 @@ impl<'a> Value<'a> { /// [`Self::String`] and [`Self::Bytes`]. Bytes will only be returned if the /// contained bytes can be safely interpretted as UTF-8. #[must_use] + #[inline] pub fn as_str(&self) -> Option<&str> { match self { Self::Bytes(bytes) => std::str::from_utf8(bytes).ok(), @@ -303,6 +312,7 @@ impl<'a> Value<'a> { /// representation of bytes. This will only return a value with variants /// [`Self::String`] and [`Self::Bytes`]. #[must_use] + #[inline] pub fn as_bytes(&self) -> Option<&[u8]> { match self { Self::Bytes(bytes) => Some(bytes), @@ -316,6 +326,7 @@ impl<'a> Value<'a> { /// [`Self::Mappings`]. If a [`Self::Mappings`], only the value portion of /// the mapping is returned. #[must_use] + #[inline] pub fn values(&self) -> SequenceIter<'_> { match self { Self::Sequence(sequence) => SequenceIter::Sequence(sequence.iter()), @@ -329,6 +340,7 @@ impl<'a> Value<'a> { /// this value. Returns an empty iterator if not a [`Self::Sequence`] or /// [`Self::Mappings`]. If a [`Self::Sequence`], the key will always be /// `Self::None`. + #[inline] pub fn mappings(&self) -> std::slice::Iter<'_, (Self, Self)> { match self { Self::Mappings(mappings) => mappings.iter(), @@ -384,6 +396,7 @@ impl<'a> Value<'a> { } impl<'a> Serialize for Value<'a> { + #[inline] fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, @@ -429,6 +442,7 @@ impl<'a> Serialize for Value<'a> { } impl<'de: 'a, 'a> Deserialize<'de> for Value<'a> { + #[inline] fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, @@ -451,18 +465,21 @@ pub struct OwnedValue(pub Value<'static>); impl Deref for OwnedValue { type Target = Value<'static>; + #[inline] fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for OwnedValue { + #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl Serialize for OwnedValue { + #[inline] fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, @@ -472,6 +489,7 @@ impl Serialize for OwnedValue { } impl<'de> Deserialize<'de> for OwnedValue { + #[inline] fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, @@ -492,6 +510,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { formatter.write_str("any value") } + #[inline] fn visit_none(self) -> Result where E: serde::de::Error, @@ -499,6 +518,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::None) } + #[inline] fn visit_bool(self, v: bool) -> Result where E: serde::de::Error, @@ -506,6 +526,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::Bool(v)) } + #[inline] fn visit_i8(self, v: i8) -> Result where E: serde::de::Error, @@ -513,6 +534,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::Integer(Integer::from(v))) } + #[inline] fn visit_i32(self, v: i32) -> Result where E: serde::de::Error, @@ -520,6 +542,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::Integer(Integer::from(v))) } + #[inline] fn visit_i16(self, v: i16) -> Result where E: serde::de::Error, @@ -527,6 +550,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::Integer(Integer::from(v))) } + #[inline] fn visit_i64(self, v: i64) -> Result where E: serde::de::Error, @@ -534,6 +558,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::Integer(Integer::from(v))) } + #[inline] fn visit_i128(self, v: i128) -> Result where E: serde::de::Error, @@ -541,6 +566,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::Integer(Integer::from(v))) } + #[inline] fn visit_u8(self, v: u8) -> Result where E: serde::de::Error, @@ -548,6 +574,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::Integer(Integer::from(v))) } + #[inline] fn visit_u16(self, v: u16) -> Result where E: serde::de::Error, @@ -555,6 +582,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::Integer(Integer::from(v))) } + #[inline] fn visit_u32(self, v: u32) -> Result where E: serde::de::Error, @@ -562,6 +590,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::Integer(Integer::from(v))) } + #[inline] fn visit_u64(self, v: u64) -> Result where E: serde::de::Error, @@ -569,6 +598,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::Integer(Integer::from(v))) } + #[inline] fn visit_u128(self, v: u128) -> Result where E: serde::de::Error, @@ -576,6 +606,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::Integer(Integer::from(v))) } + #[inline] fn visit_f32(self, v: f32) -> Result where E: serde::de::Error, @@ -583,6 +614,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::Float(Float::from(v))) } + #[inline] fn visit_f64(self, v: f64) -> Result where E: serde::de::Error, @@ -590,6 +622,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::Float(Float::from(v))) } + #[inline] fn visit_str(self, v: &str) -> Result where E: serde::de::Error, @@ -597,6 +630,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::String(Cow::Owned(v.to_string()))) } + #[inline] fn visit_borrowed_str(self, v: &'a str) -> Result where E: serde::de::Error, @@ -604,6 +638,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::String(Cow::Borrowed(v))) } + #[inline] fn visit_string(self, v: String) -> Result where E: serde::de::Error, @@ -611,6 +646,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::String(Cow::Owned(v))) } + #[inline] fn visit_bytes(self, v: &[u8]) -> Result where E: serde::de::Error, @@ -618,6 +654,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::Bytes(Cow::Owned(v.to_vec()))) } + #[inline] fn visit_borrowed_bytes(self, v: &'a [u8]) -> Result where E: serde::de::Error, @@ -625,6 +662,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::Bytes(Cow::Borrowed(v))) } + #[inline] fn visit_byte_buf(self, v: Vec) -> Result where E: serde::de::Error, @@ -632,6 +670,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::Bytes(Cow::Owned(v))) } + #[inline] fn visit_some(self, deserializer: D) -> Result where D: serde::Deserializer<'de>, @@ -639,6 +678,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { deserializer.deserialize_any(Self::default()) } + #[inline] fn visit_unit(self) -> Result where E: serde::de::Error, @@ -646,6 +686,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::Unit) } + #[inline] fn visit_seq(self, mut seq: A) -> Result where A: serde::de::SeqAccess<'de>, @@ -661,6 +702,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { Ok(Value::Sequence(values)) } + #[inline] fn visit_map(self, mut map: A) -> Result where A: serde::de::MapAccess<'de>, @@ -678,6 +720,7 @@ impl<'de: 'a, 'a> Visitor<'de> for ValueVisitor<'a> { } impl<'a> From>> for Value<'a> { + #[inline] fn from(value: Option>) -> Self { if let Some(value) = value { value @@ -688,12 +731,14 @@ impl<'a> From>> for Value<'a> { } impl<'a> From<()> for Value<'a> { + #[inline] fn from(_: ()) -> Self { Value::Unit } } impl<'a> From for Value<'a> { + #[inline] fn from(value: bool) -> Self { Value::Bool(value) } @@ -702,6 +747,7 @@ impl<'a> From for Value<'a> { macro_rules! define_value_from_primitive { ($container:ident, $variant:ident, $primitive:ty) => { impl<'a> From<$primitive> for Value<'a> { + #[inline] fn from(value: $primitive) -> Self { Self::$container($container::from(value)) } @@ -725,42 +771,49 @@ define_value_from_primitive!(Float, F32, f32); define_value_from_primitive!(Float, F64, f64); impl<'a> From<&'a [u8]> for Value<'a> { + #[inline] fn from(bytes: &'a [u8]) -> Self { Self::Bytes(Cow::Borrowed(bytes)) } } impl<'a> From> for Value<'a> { + #[inline] fn from(bytes: Vec) -> Self { Self::Bytes(Cow::Owned(bytes)) } } impl<'a, const N: usize> From<&'a [u8; N]> for Value<'a> { + #[inline] fn from(bytes: &'a [u8; N]) -> Self { Self::Bytes(Cow::Borrowed(bytes)) } } impl<'a> From<&'a str> for Value<'a> { + #[inline] fn from(string: &'a str) -> Self { Self::String(Cow::Borrowed(string)) } } impl<'a> From for Value<'a> { + #[inline] fn from(string: String) -> Self { Self::String(Cow::Owned(string)) } } impl<'a> From>> for Value<'a> { + #[inline] fn from(value: Vec>) -> Self { Self::Sequence(value) } } impl<'a> From, Value<'a>)>> for Value<'a> { + #[inline] fn from(value: Vec<(Value<'a>, Value<'a>)>) -> Self { Self::Mappings(value) } @@ -774,6 +827,7 @@ pub enum SequenceIter<'a> { impl<'a> Iterator for SequenceIter<'a> { type Item = &'a Value<'a>; + #[inline] fn next(&mut self) -> Option { match self { SequenceIter::Sequence(sequence) => sequence.next(),