Skip to content

Commit 5de641d

Browse files
committed
Add impl_string! macro and add SmolStr support
1 parent 36eaa3a commit 5de641d

8 files changed

Lines changed: 89 additions & 63 deletions

File tree

Cargo.lock

Lines changed: 33 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ zstd = "0.13.0"
4242

4343
[features]
4444
derive = [ "dep:bitcode_derive" ]
45-
std = [ "serde?/std", "glam?/std", "arrayvec?/std" ]
45+
std = [ "serde?/std", "glam?/std", "arrayvec?/std", "smol_str?/std" ]
4646
default = [ "derive", "std" ]
4747

4848
[package.metadata.docs.rs]

fuzz/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ bitcode = { path = "..", features = [ "arrayvec", "rust_decimal", "serde", "smol
1414
libfuzzer-sys = "0.4"
1515
rust_decimal = "1.36.0"
1616
serde = { version ="1.0", features = [ "derive" ] }
17-
smol_str = "0.3"
17+
smol_str = { version = "0.3", features = ["serde"] }
1818
time = { version = "0.3", features = ["serde"]}
1919

2020
# Prevent this from interfering with workspaces

src/derive/impls.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ macro_rules! impl_both {
3131
impl_both!(bool, BoolEncoder, BoolDecoder);
3232
impl_both!(f32, F32Encoder, F32Decoder);
3333
impl_both!(String, StrEncoder, StrDecoder);
34+
#[cfg(feature = "smol_str")]
35+
impl_both!(smol_str::SmolStr, StrEncoder, StrDecoder);
3436

3537
macro_rules! impl_int {
3638
($($t:ty),+) => {

src/derive/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ mod ip_addr;
1717
mod map;
1818
mod option;
1919
mod result;
20-
pub(crate) mod smart_ptr;
20+
mod smart_ptr;
2121
mod variant;
2222
pub(crate) mod vec;
2323

src/ext/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ mod arrayvec;
55
mod glam;
66
#[cfg(feature = "rust_decimal")]
77
mod rust_decimal;
8-
#[cfg(feature = "smol_str")]
9-
mod smol_str;
108
#[cfg(feature = "time")]
119
mod time;
1210
#[cfg(feature = "uuid")]

src/ext/smol_str.rs

Lines changed: 0 additions & 37 deletions
This file was deleted.

src/str.rs

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use crate::error::err;
55
use crate::fast::{NextUnchecked, SliceImpl};
66
use crate::length::LengthDecoder;
77
use crate::u8_char::U8Char;
8-
use alloc::borrow::ToOwned;
98
use alloc::string::String;
109
use alloc::vec::Vec;
1110
use core::num::NonZeroUsize;
@@ -57,21 +56,37 @@ impl<'b> Encoder<&'b str> for StrEncoder {
5756
}
5857
}
5958

60-
impl Encoder<String> for StrEncoder {
61-
#[inline(always)]
62-
fn encode(&mut self, t: &String) {
63-
self.encode(t.as_str());
64-
}
59+
macro_rules! impl_string {
60+
($t:ty) => {
61+
impl crate::coder::Encoder<$t> for crate::str::StrEncoder {
62+
#[inline(always)]
63+
fn encode(&mut self, t: &$t) {
64+
self.encode(t.as_str());
65+
}
66+
67+
#[inline(always)]
68+
fn encode_vectored<'a>(&mut self, i: impl Iterator<Item = &'a $t> + Clone)
69+
where
70+
$t: 'a,
71+
{
72+
self.encode_vectored(i.map(|s| s.as_str()));
73+
}
74+
}
6575

66-
#[inline(always)]
67-
fn encode_vectored<'a>(&mut self, i: impl Iterator<Item = &'a String> + Clone)
68-
where
69-
String: 'a,
70-
{
71-
self.encode_vectored(i.map(String::as_str));
72-
}
76+
impl<'a> crate::coder::Decoder<'a, $t> for crate::str::StrDecoder<'a> {
77+
#[inline(always)]
78+
fn decode(&mut self) -> $t {
79+
let v: &str = self.decode();
80+
<$t>::from(v)
81+
}
82+
}
83+
};
7384
}
7485

86+
impl_string!(String);
87+
#[cfg(feature = "smol_str")]
88+
impl_string!(smol_str::SmolStr);
89+
7590
// Doesn't use VecDecoder because can't decode &[u8].
7691
#[derive(Default)]
7792
pub struct StrDecoder<'a> {
@@ -131,14 +146,6 @@ impl<'a> Decoder<'a, &'a str> for StrDecoder<'a> {
131146
}
132147
}
133148

134-
impl<'a> Decoder<'a, String> for StrDecoder<'a> {
135-
#[inline(always)]
136-
fn decode(&mut self) -> String {
137-
let v: &str = self.decode();
138-
v.to_owned()
139-
}
140-
}
141-
142149
/// Tests 128 bytes a time instead of `<[u8]>::is_ascii` which only tests 8.
143150
/// 390% faster on 8KB, 27% faster on 1GB (RAM bottleneck).
144151
fn is_ascii_simd(v: &[u8]) -> bool {
@@ -288,3 +295,26 @@ mod tests2 {
288295
}
289296
crate::bench_encode_decode!(str_vec: Vec<String>);
290297
}
298+
299+
#[cfg(all(test, feature = "smol_str"))]
300+
mod smol_str_tests {
301+
use smol_str::SmolStr;
302+
303+
/// Short strings stay inline after decode (no heap allocation).
304+
#[test]
305+
fn decoded_short_string_is_not_heap_allocated() {
306+
let s = SmolStr::new("hello");
307+
assert!(!s.is_heap_allocated());
308+
let decoded: SmolStr = crate::decode(&crate::encode(&s)).unwrap();
309+
assert!(!decoded.is_heap_allocated());
310+
}
311+
312+
/// Long strings are heap-allocated after decode.
313+
#[test]
314+
fn decoded_long_string_is_heap_allocated() {
315+
let s = SmolStr::new("this is a longer string that exceeds inline storage");
316+
assert!(s.is_heap_allocated());
317+
let decoded: SmolStr = crate::decode(&crate::encode(&s)).unwrap();
318+
assert!(decoded.is_heap_allocated());
319+
}
320+
}

0 commit comments

Comments
 (0)