From cc46a3b389c74a3fe822a8a13a6f54247803ef5f Mon Sep 17 00:00:00 2001 From: Dmitry Mottl Date: Wed, 20 Sep 2023 10:41:03 +0700 Subject: [PATCH 1/2] Adds support for Bincode --- CHANGELOG.md | 7 +++++ Cargo.toml | 8 +++-- src/bincode.rs | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 8 ++++- 4 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 src/bincode.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cc3ba9..148c41a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.1.0] - 2023-09-20 + +### ADDED + +- Added support for [Bincode](https://docs.rs/bincode/2.0.0-rc.3/bincode/). Use `bincode` + feature to enable it. + ## [1.0.1] - 2022-03-24 ### FIXED diff --git a/Cargo.toml b/Cargo.toml index 69b3aac..7ac2f90 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smartstring" -version = "1.0.1" +version = "1.1.0" authors = ["Bodil Stokke "] edition = "2021" license = "MPL-2.0+" @@ -15,7 +15,7 @@ rust-version = "1.57" build = "./build.rs" [package.metadata.docs.rs] -features = ["arbitrary", "proptest", "serde"] +features = ["arbitrary", "proptest", "serde", "bincode"] [badges] travis-ci = { repository = "bodil/smartstring", branch = "master" } @@ -25,15 +25,17 @@ name = "smartstring" harness = false [features] -default = ["std"] +default = ["std", "bincode"] std = [] test = ["std", "arbitrary", "arbitrary/derive"] +bincode = ["dep:bincode"] [dependencies] static_assertions = "1" serde = { version = "1", optional = true } arbitrary = { version = "1", optional = true } proptest = { version = "1", optional = true } +bincode = { version = "2.0.0-rc.3", features=["alloc"], optional = true } [dev-dependencies] proptest = "1" diff --git a/src/bincode.rs b/src/bincode.rs new file mode 100644 index 0000000..9dcc855 --- /dev/null +++ b/src/bincode.rs @@ -0,0 +1,83 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +//! Support for Bincode integration. Enable this with the `bincode` feature. + +use crate::{Compact, LazyCompact, SmartString, SmartStringMode, MAX_INLINE}; +use std::ops::Deref; + +use bincode::{ + de::Decoder, + enc::Encoder, + error::{DecodeError, EncodeError}, + impl_borrow_decode, Decode, Encode, +}; + +impl Encode for SmartString { + fn encode(&self, encoder: &mut E) -> Result<(), EncodeError> { + self.as_bytes().encode(encoder) + } +} + +impl Decode for SmartString { + fn decode(decoder: &mut D) -> Result { + let bytes = as Decode>::decode(decoder)?; + let string = String::from_utf8(bytes).map_err(|e| DecodeError::Utf8 { + inner: e.utf8_error(), + })?; + Ok(if string.len() > MAX_INLINE { + Self::from_boxed(string.into()) + } else { + Self::from_inline(string.deref().into()) + }) + } +} + +impl_borrow_decode!(SmartString); +impl_borrow_decode!(SmartString); + +#[cfg(test)] +mod test { + use crate::{Compact, LazyCompact, SmartString}; + + #[test] + fn test_bincode_compact() { + let mut buf: [u8; 64] = [0; 64]; + let short_str = "Hello world"; + let long_str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"; + + let config = bincode::config::standard(); + let smartstring = SmartString::::from(short_str); + let len = bincode::encode_into_slice(smartstring, &mut buf, config).unwrap(); + let smartstring: SmartString = + bincode::decode_from_slice(&buf[..len], config).unwrap().0; + assert_eq!(smartstring, short_str); + + let smartstring = SmartString::::from(long_str); + let len = bincode::encode_into_slice(smartstring, &mut buf, config).unwrap(); + let smartstring: SmartString = + bincode::decode_from_slice(&buf[..len], config).unwrap().0; + assert_eq!(smartstring, long_str); + } + + #[test] + fn test_bincode_lazy_compact() { + let mut buf: [u8; 64] = [0; 64]; + let short_str = "Hello world"; + let long_str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"; + + let config = bincode::config::standard(); + let smartstring = SmartString::::from(short_str); + let len = bincode::encode_into_slice(smartstring, &mut buf, config).unwrap(); + let smartstring: SmartString = + bincode::decode_from_slice(&buf[..len], config).unwrap().0; + assert_eq!(smartstring, short_str); + + let smartstring = SmartString::::from(long_str); + let len = bincode::encode_into_slice(smartstring, &mut buf, config).unwrap(); + let smartstring: SmartString = + bincode::decode_from_slice(&buf[..len], config).unwrap().0; + assert_eq!(smartstring, long_str); + } +} diff --git a/src/lib.rs b/src/lib.rs index 33e0023..80fcb4b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,7 +82,7 @@ //! //! ```no_compile //! [dependencies] -//! smartstring = { version = "*", features = ["proptest", "serde"] } +//! smartstring = { version = "*", features = ["proptest", "serde", "bincode"] } //! ``` //! //! | Feature | Description | @@ -90,9 +90,12 @@ //! | [`arbitrary`](https://crates.io/crates/arbitrary) | [`Arbitrary`][Arbitrary] implementation for [`SmartString`]. | //! | [`proptest`](https://crates.io/crates/proptest) | A strategy for generating [`SmartString`]s from a regular expression. | //! | [`serde`](https://crates.io/crates/serde) | [`Serialize`][Serialize] and [`Deserialize`][Deserialize] implementations for [`SmartString`]. | +//! | [`bincode`](https://crates.io/crates/bincode) | [`Encode`][Encode] and [`Decode`][Decode] implementations for [`SmartString`]. | //! //! [Serialize]: https://docs.rs/serde/latest/serde/trait.Serialize.html //! [Deserialize]: https://docs.rs/serde/latest/serde/trait.Deserialize.html +//! [Encode]: https://docs.rs/bincode/2.0.0-rc.3/bincode/enc/trait.Encode.html +//! [Decode]: https://docs.rs/bincode/2.0.0-rc.3/bincode/de/trait.Decode.html //! [Arbitrary]: https://docs.rs/arbitrary/latest/arbitrary/trait.Arbitrary.html // Ensure all unsafe blocks get flagged for manual validation. @@ -159,6 +162,9 @@ mod arbitrary; #[cfg(feature = "proptest")] pub mod proptest; +#[cfg(feature = "bincode")] +mod bincode; + /// Convenient type aliases. pub mod alias { use super::*; From e9d97901129ffaab51b8de0ecd129eac6d67ab73 Mon Sep 17 00:00:00 2001 From: Dmitry Mottl Date: Mon, 16 Mar 2026 12:01:22 +0300 Subject: [PATCH 2/2] Updates bincode to 2.0.1, fixes some issues --- Cargo.toml | 5 ++--- build.rs | 3 +++ src/bincode.rs | 22 ++++++++-------------- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7ac2f90..d8181d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,6 @@ readme = "./README.md" categories = ["data-structures"] keywords = ["cache-local", "cpu-cache", "small-string", "sso", "inline-string"] exclude = ["release.toml", "proptest-regressions/**"] -rust-version = "1.57" build = "./build.rs" [package.metadata.docs.rs] @@ -35,11 +34,11 @@ static_assertions = "1" serde = { version = "1", optional = true } arbitrary = { version = "1", optional = true } proptest = { version = "1", optional = true } -bincode = { version = "2.0.0-rc.3", features=["alloc"], optional = true } +bincode = { version = "2.0.1", features=["alloc"], optional = true } [dev-dependencies] proptest = "1" -proptest-derive = "0.3" +proptest-derive = "0.5" criterion = "0.3" rand = "0.8" serde_test = "1" diff --git a/build.rs b/build.rs index fb8ca0c..c84b9f3 100644 --- a/build.rs +++ b/build.rs @@ -5,6 +5,9 @@ use version_check as rustc; fn main() { + println!("cargo:rustc-check-cfg=cfg(has_allocator)"); + println!("cargo:rustc-check-cfg=cfg(needs_allocator_feature)"); + let ac = autocfg::new(); let has_feature = Some(true) == rustc::supports_feature("allocator_api"); let has_api = ac.probe_trait("alloc::alloc::Allocator"); diff --git a/src/bincode.rs b/src/bincode.rs index 9dcc855..46b38cd 100644 --- a/src/bincode.rs +++ b/src/bincode.rs @@ -4,8 +4,8 @@ //! Support for Bincode integration. Enable this with the `bincode` feature. -use crate::{Compact, LazyCompact, SmartString, SmartStringMode, MAX_INLINE}; -use std::ops::Deref; +use crate::{Compact, LazyCompact, SmartString, SmartStringMode}; +use std::borrow::Cow; use bincode::{ de::Decoder, @@ -16,21 +16,15 @@ use bincode::{ impl Encode for SmartString { fn encode(&self, encoder: &mut E) -> Result<(), EncodeError> { - self.as_bytes().encode(encoder) + self.as_str().encode(encoder) } } -impl Decode for SmartString { - fn decode(decoder: &mut D) -> Result { - let bytes = as Decode>::decode(decoder)?; - let string = String::from_utf8(bytes).map_err(|e| DecodeError::Utf8 { - inner: e.utf8_error(), - })?; - Ok(if string.len() > MAX_INLINE { - Self::from_boxed(string.into()) - } else { - Self::from_inline(string.deref().into()) - }) +impl Decode for SmartString { + fn decode>(decoder: &mut D) -> Result { + // Decode as a Cow to borrow from the decoder's buffer + let s = as Decode>::decode(decoder)?; + Ok(Self::from(s.as_ref())) } }