From eefc4612e9aa77e3984559ce3a3beede6ae2ddef Mon Sep 17 00:00:00 2001 From: Pure White Date: Sat, 23 Dec 2023 18:46:15 +0800 Subject: [PATCH 1/6] feat: manually add padding to optimize clone cost --- Cargo.lock | 53 ++++++++++++-------------- Cargo.toml | 2 +- README.md | 94 ++++++++++++++-------------------------------- benches/faststr.rs | 4 ++ src/lib.rs | 56 +++++++++++++++++---------- 5 files changed, 93 insertions(+), 116 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4787455..51bdbee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -159,9 +159,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -170,22 +170,21 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.15" +version = "0.9.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", "memoffset", - "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" dependencies = [ "cfg-if", ] @@ -208,7 +207,7 @@ dependencies = [ [[package]] name = "faststr" -version = "0.2.12" +version = "0.2.13" dependencies = [ "bytes", "criterion", @@ -271,9 +270,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" @@ -286,9 +285,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.150" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "linux-raw-sys" @@ -328,9 +327,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" @@ -374,9 +373,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" dependencies = [ "unicode-ident", ] @@ -454,9 +453,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "rustix" -version = "0.38.26" +version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ "bitflags", "errno", @@ -467,9 +466,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "same-file" @@ -480,12 +479,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "serde" version = "1.0.193" @@ -525,9 +518,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" -version = "2.0.39" +version = "2.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8" dependencies = [ "proc-macro2", "quote", @@ -561,9 +554,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" diff --git a/Cargo.toml b/Cargo.toml index b632f5f..e1e3284 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "faststr" -version = "0.2.12" +version = "0.2.13" authors = ["Volo Team "] edition = "2021" description = "Faststr is a string library that reduces the cost of clone." diff --git a/README.md b/README.md index 29260de..a685fb2 100644 --- a/README.md +++ b/README.md @@ -61,118 +61,82 @@ $ cargo bench ### AARCH64 -#### M1Max - -``` -empty faststr time: [19.315 ns 19.345 ns 19.377 ns] - -empty string time: [2.2097 ns 2.2145 ns 2.2194 ns] - -static faststr time: [19.483 ns 19.598 ns 19.739 ns] - -inline faststr time: [20.447 ns 20.476 ns 20.507 ns] - -string hello world time: [17.215 ns 17.239 ns 17.263 ns] - -512B faststr time: [23.883 ns 23.922 ns 23.965 ns] - -512B string time: [50.733 ns 51.360 ns 52.041 ns] - -4096B faststr time: [23.893 ns 23.959 ns 24.033 ns] - -4096B string time: [78.323 ns 79.565 ns 80.830 ns] - -16384B faststr time: [23.829 ns 23.885 ns 23.952 ns] - -16384B string time: [395.83 ns 402.46 ns 408.51 ns] - -65536B faststr time: [23.934 ns 24.002 ns 24.071 ns] - -65536B string time: [1.3142 µs 1.3377 µs 1.3606 µs] - -524288B faststr time: [23.881 ns 23.926 ns 23.976 ns] - -524288B string time: [8.8109 µs 8.8577 µs 8.9024 µs] - -1048576B faststr time: [23.968 ns 24.032 ns 24.094 ns] - -1048576B string time: [18.424 µs 18.534 µs 18.646 µs] -``` - #### M3Max ``` -empty faststr time: [16.178 ns 16.189 ns 16.201 ns] +empty faststr time: [2.0188 ns 2.0271 ns 2.0356 ns] empty string time: [2.1306 ns 2.1333 ns 2.1365 ns] -static faststr time: [16.310 ns 16.325 ns 16.341 ns] +static faststr time: [2.0458 ns 2.0589 ns 2.0709 ns] -inline faststr time: [17.024 ns 17.040 ns 17.056 ns] +inline faststr time: [2.2270 ns 2.2332 ns 2.2399 ns] string hello world time: [12.553 ns 12.575 ns 12.597 ns] -512B faststr time: [16.771 ns 16.790 ns 16.810 ns] +512B faststr time: [3.8373 ns 3.8454 ns 3.8540 ns] 512B string time: [36.895 ns 37.007 ns 37.121 ns] -4096B faststr time: [16.785 ns 16.811 ns 16.840 ns] +4096B faststr time: [3.8205 ns 3.8260 ns 3.8317 ns] 4096B string time: [55.275 ns 55.355 ns 55.446 ns] -16384B faststr time: [16.796 ns 16.819 ns 16.843 ns] +16384B faststr time: [3.8191 ns 3.8246 ns 3.8306 ns] 16384B string time: [338.18 ns 352.36 ns 365.02 ns] -65536B faststr time: [16.968 ns 17.011 ns 17.062 ns] +65536B faststr time: [3.8169 ns 3.8221 ns 3.8277 ns] 65536B string time: [662.52 ns 663.75 ns 664.96 ns] -524288B faststr time: [16.806 ns 16.834 ns 16.868 ns] +524288B faststr time: [3.8140 ns 3.8178 ns 3.8219 ns] 524288B string time: [6.2681 µs 6.2755 µs 6.2827 µs] -1048576B faststr time: [16.823 ns 16.849 ns 16.880 ns] +1048576B faststr time: [3.8235 ns 3.8290 ns 3.8348 ns] 1048576B string time: [12.422 µs 12.438 µs 12.453 µs] ``` -### AMD EPYC 7Y83 +### amd64 + +#### AMD EPYC 7Y83 ``` -empty faststr time: [42.724 ns 42.728 ns 42.732 ns] +empty faststr time: [4.3325 ns 4.3330 ns 4.3335 ns] -empty string time: [4.6490 ns 4.6494 ns 4.6499 ns] +empty string time: [4.6413 ns 4.6422 ns 4.6434 ns] -static faststr time: [42.519 ns 42.525 ns 42.532 ns] +static faststr time: [4.3328 ns 4.3333 ns 4.3339 ns] -inline faststr time: [43.446 ns 43.450 ns 43.454 ns] +inline faststr time: [4.6567 ns 4.6580 ns 4.6593 ns] -string hello world time: [12.385 ns 12.385 ns 12.387 ns] +string hello world time: [12.897 ns 12.929 ns 12.954 ns] -512B faststr time: [42.232 ns 42.238 ns 42.244 ns] +512B faststr time: [4.4218 ns 4.4253 ns 4.4291 ns] -512B string time: [15.822 ns 15.846 ns 15.894 ns] +512B string time: [16.087 ns 16.094 ns 16.105 ns] -4096B faststr time: [41.741 ns 41.918 ns 42.069 ns] +4096B faststr time: [4.4066 ns 4.4099 ns 4.4141 ns] -4096B string time: [84.492 ns 84.668 ns 84.839 ns] +4096B string time: [96.905 ns 97.401 ns 97.879 ns] -16384B faststr time: [42.245 ns 42.250 ns 42.255 ns] +16384B faststr time: [4.4150 ns 4.4277 ns 4.4414 ns] -16384B string time: [225.36 ns 225.42 ns 225.47 ns] +16384B string time: [229.25 ns 229.30 ns 229.34 ns] -65536B faststr time: [41.987 ns 42.087 ns 42.166 ns] +65536B faststr time: [4.4562 ns 4.4623 ns 4.4690 ns] -65536B string time: [1.3212 µs 1.3215 µs 1.3219 µs] +65536B string time: [1.3325 µs 1.3328 µs 1.3332 µs] -524288B faststr time: [42.272 ns 42.277 ns 42.283 ns] +524288B faststr time: [4.4167 ns 4.4240 ns 4.4326 ns] -524288B string time: [14.373 µs 14.380 µs 14.388 µs] +524288B string time: [18.268 µs 18.277 µs 18.287 µs] -1048576B faststr time: [42.279 ns 42.287 ns 42.295 ns] +1048576B faststr time: [4.4275 ns 4.4385 ns 4.4494 ns] -1048576B string time: [27.995 µs 28.015 µs 28.038 µs] +1048576B string time: [32.839 µs 33.777 µs 34.554 µs] ``` ## Related Projects diff --git a/benches/faststr.rs b/benches/faststr.rs index 4c9f18c..6439c06 100644 --- a/benches/faststr.rs +++ b/benches/faststr.rs @@ -16,9 +16,13 @@ fn criterion_benchmark(c: &mut Criterion) { for size in [512, 4 * 1024, 16 * 1024, 64 * 1024, 512 * 1024, 1024 * 1024] { let s = FastStr::from("a".repeat(size)); + let _s1 = black_box(s.clone()); + let _s2 = black_box(s.clone()); c.bench_function(format!("{}B faststr", size).as_str(), |b| { b.iter(|| black_box(s.clone())) }); + drop(_s1); + drop(_s2); let s = String::from("a".repeat(size)); c.bench_function(format!("{}B string", size).as_str(), |b| { b.iter(|| black_box(s.clone())) diff --git a/src/lib.rs b/src/lib.rs index 1a7443e..caad0ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,10 +34,14 @@ impl FastStr { Self(Repr::new(text)) } - /// Create a new inline `FastStr` (up to 38 bytes long) from a string slice `s`. + /// Create a new inline `FastStr` (up to 2 bytes long) from a string slice `s`. /// - /// This constructor panics if the length of `s` is greater than 38. + /// This constructor panics if the length of `s` is greater than 22. + /// + /// Note: the inline length is not guaranteed. #[inline] + #[doc(hidden)] + #[deprecated(since = "0.2.13", note = "Please use `FastStr::new()` instead.")] pub const fn new_inline(s: &str) -> Self { if s.len() > INLINE_CAP { panic!("[FastStr] string is too long to inline"); @@ -48,10 +52,11 @@ impl FastStr { buf[i] = s.as_bytes()[i]; i += 1 } - Self(Repr::Inline { + Self(Repr::Inline(Inline { + _pad: 0, len: s.len() as u8, buf, - }) + })) } /// Create a new `FastStr` from an `Arc`. @@ -225,10 +230,11 @@ impl FastStr { ch.encode_utf8(&mut buf[len..]); len += size; } - Self(Repr::Inline { + Self(Repr::Inline(Inline { + _pad: 0, len: len as u8, buf, - }) + })) } } @@ -404,10 +410,11 @@ where buf[len..][..size].copy_from_slice(slice.as_bytes()); len += size; } - FastStr(Repr::Inline { + FastStr(Repr::Inline(Inline { + _pad: 0, len: len as u8, buf, - }) + })) } impl iter::FromIterator for FastStr { @@ -496,7 +503,7 @@ impl From> for FastStr { } } -const INLINE_CAP: usize = 38; +const INLINE_CAP: usize = 22; #[derive(Clone)] enum Repr { @@ -505,7 +512,14 @@ enum Repr { ArcStr(Arc), ArcString(Arc), StaticStr(&'static str), - Inline { len: u8, buf: [u8; INLINE_CAP] }, + Inline(Inline), +} + +#[derive(Clone, Copy)] +struct Inline { + _pad: u64, + len: u8, + buf: [u8; INLINE_CAP], } impl Repr { @@ -524,10 +538,11 @@ impl Repr { if len <= INLINE_CAP { let mut buf = [0; INLINE_CAP]; buf[..len].copy_from_slice(text.as_bytes()); - return Self::Inline { + return Self::Inline(Inline { + _pad: 0, len: len as u8, buf, - }; + }); } } @@ -563,7 +578,7 @@ impl Repr { Self::ArcStr(arc_str) => arc_str.len(), Self::ArcString(arc_string) => arc_string.len(), Self::StaticStr(s) => s.len(), - Self::Inline { len, .. } => *len as usize, + Self::Inline(Inline { len, .. }) => *len as usize, } } @@ -575,7 +590,7 @@ impl Repr { Self::ArcStr(arc_str) => arc_str.is_empty(), Self::ArcString(arc_string) => arc_string.is_empty(), Self::StaticStr(s) => s.is_empty(), - Self::Inline { len, .. } => *len == 0, + Self::Inline(Inline { len, .. }) => *len == 0, } } @@ -588,7 +603,7 @@ impl Repr { Self::ArcStr(arc_str) => arc_str, Self::ArcString(arc_string) => arc_string, Self::StaticStr(s) => s, - Self::Inline { len, buf } => unsafe { + Self::Inline(Inline { len, buf, .. }) => unsafe { std::str::from_utf8_unchecked(&buf[..*len as usize]) }, } @@ -604,7 +619,7 @@ impl Repr { Arc::try_unwrap(arc_string).unwrap_or_else(|arc| (*arc).clone()) } Self::StaticStr(s) => s.to_string(), - Self::Inline { len, buf } => unsafe { + Self::Inline(Inline { len, buf, .. }) => unsafe { String::from_utf8_unchecked(buf[..len as usize].to_vec()) }, } @@ -620,7 +635,7 @@ impl Repr { Bytes::from(Arc::try_unwrap(arc_string).unwrap_or_else(|arc| (*arc).clone())) } Self::StaticStr(s) => Bytes::from_static(s.as_bytes()), - Self::Inline { len, buf } => Bytes::from(buf[..len as usize].to_vec()), + Self::Inline(Inline { len, buf, .. }) => Bytes::from(buf[..len as usize].to_vec()), } } @@ -663,14 +678,15 @@ impl Repr { Repr::StaticStr(s) => Self::StaticStr(unsafe { std::str::from_utf8_unchecked(&s.as_bytes()[sub_offset..sub_offset + sub_len]) }), - Repr::Inline { len: _, buf } => Self::Inline { + Repr::Inline(Inline { len: _, buf, .. }) => Self::Inline(Inline { + _pad: 0, len: sub_len as u8, buf: { let mut new_buf = [0; INLINE_CAP]; new_buf[..sub_len].copy_from_slice(&buf[sub_offset..sub_offset + sub_len]); new_buf }, - }, + }), } } } @@ -684,7 +700,7 @@ impl AsRef<[u8]> for Repr { Self::ArcStr(arc_str) => arc_str.as_bytes(), Self::ArcString(arc_string) => arc_string.as_bytes(), Self::StaticStr(s) => s.as_bytes(), - Self::Inline { len, buf } => &buf[..*len as usize], + Self::Inline(Inline { len, buf, .. }) => &buf[..*len as usize], } } } From 2a9a24516c5745f1ece5ce990eb392e8acab9160 Mon Sep 17 00:00:00 2001 From: Pure White Date: Sat, 23 Dec 2023 20:20:29 +0800 Subject: [PATCH 2/6] use usize for len instead of add padding --- src/lib.rs | 36 +++++++++--------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index caad0ac..e400ca0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,9 +34,9 @@ impl FastStr { Self(Repr::new(text)) } - /// Create a new inline `FastStr` (up to 2 bytes long) from a string slice `s`. + /// Create a new inline `FastStr` (up to 24 bytes long) from a string slice `s`. /// - /// This constructor panics if the length of `s` is greater than 22. + /// This constructor panics if the length of `s` is greater than 24. /// /// Note: the inline length is not guaranteed. #[inline] @@ -52,11 +52,7 @@ impl FastStr { buf[i] = s.as_bytes()[i]; i += 1 } - Self(Repr::Inline(Inline { - _pad: 0, - len: s.len() as u8, - buf, - })) + Self(Repr::Inline(Inline { len: s.len(), buf })) } /// Create a new `FastStr` from an `Arc`. @@ -230,11 +226,7 @@ impl FastStr { ch.encode_utf8(&mut buf[len..]); len += size; } - Self(Repr::Inline(Inline { - _pad: 0, - len: len as u8, - buf, - })) + Self(Repr::Inline(Inline { len, buf })) } } @@ -410,11 +402,7 @@ where buf[len..][..size].copy_from_slice(slice.as_bytes()); len += size; } - FastStr(Repr::Inline(Inline { - _pad: 0, - len: len as u8, - buf, - })) + FastStr(Repr::Inline(Inline { len, buf })) } impl iter::FromIterator for FastStr { @@ -503,7 +491,7 @@ impl From> for FastStr { } } -const INLINE_CAP: usize = 22; +const INLINE_CAP: usize = 24; #[derive(Clone)] enum Repr { @@ -517,8 +505,7 @@ enum Repr { #[derive(Clone, Copy)] struct Inline { - _pad: u64, - len: u8, + len: usize, buf: [u8; INLINE_CAP], } @@ -538,11 +525,7 @@ impl Repr { if len <= INLINE_CAP { let mut buf = [0; INLINE_CAP]; buf[..len].copy_from_slice(text.as_bytes()); - return Self::Inline(Inline { - _pad: 0, - len: len as u8, - buf, - }); + return Self::Inline(Inline { len, buf }); } } @@ -679,8 +662,7 @@ impl Repr { std::str::from_utf8_unchecked(&s.as_bytes()[sub_offset..sub_offset + sub_len]) }), Repr::Inline(Inline { len: _, buf, .. }) => Self::Inline(Inline { - _pad: 0, - len: sub_len as u8, + len: sub_len, buf: { let mut new_buf = [0; INLINE_CAP]; new_buf[..sub_len].copy_from_slice(&buf[sub_offset..sub_offset + sub_len]); From 4b6610870f62bb2d27589dc8b21afb4977a29d56 Mon Sep 17 00:00:00 2001 From: Pure White Date: Sat, 23 Dec 2023 20:26:18 +0800 Subject: [PATCH 3/6] optimize code --- benches/faststr.rs | 1 + src/lib.rs | 32 +++++++++++++------------------- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/benches/faststr.rs b/benches/faststr.rs index 6439c06..709d367 100644 --- a/benches/faststr.rs +++ b/benches/faststr.rs @@ -9,6 +9,7 @@ fn criterion_benchmark(c: &mut Criterion) { let s = FastStr::from("Hello, world!"); c.bench_function("static faststr", |b| b.iter(|| black_box(s.clone()))); + #[allow(deprecated)] let s = FastStr::new_inline("Hello, world!"); c.bench_function("inline faststr", |b| b.iter(|| black_box(s.clone()))); let s = String::from("Hello, world!"); diff --git a/src/lib.rs b/src/lib.rs index e400ca0..db199ed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,7 +52,7 @@ impl FastStr { buf[i] = s.as_bytes()[i]; i += 1 } - Self(Repr::Inline(Inline { len: s.len(), buf })) + Self(Repr::Inline { len: s.len(), buf }) } /// Create a new `FastStr` from an `Arc`. @@ -226,7 +226,7 @@ impl FastStr { ch.encode_utf8(&mut buf[len..]); len += size; } - Self(Repr::Inline(Inline { len, buf })) + Self(Repr::Inline { len, buf }) } } @@ -402,7 +402,7 @@ where buf[len..][..size].copy_from_slice(slice.as_bytes()); len += size; } - FastStr(Repr::Inline(Inline { len, buf })) + FastStr(Repr::Inline { len, buf }) } impl iter::FromIterator for FastStr { @@ -500,13 +500,7 @@ enum Repr { ArcStr(Arc), ArcString(Arc), StaticStr(&'static str), - Inline(Inline), -} - -#[derive(Clone, Copy)] -struct Inline { - len: usize, - buf: [u8; INLINE_CAP], + Inline { len: usize, buf: [u8; INLINE_CAP] }, } impl Repr { @@ -525,7 +519,7 @@ impl Repr { if len <= INLINE_CAP { let mut buf = [0; INLINE_CAP]; buf[..len].copy_from_slice(text.as_bytes()); - return Self::Inline(Inline { len, buf }); + return Self::Inline { len, buf }; } } @@ -561,7 +555,7 @@ impl Repr { Self::ArcStr(arc_str) => arc_str.len(), Self::ArcString(arc_string) => arc_string.len(), Self::StaticStr(s) => s.len(), - Self::Inline(Inline { len, .. }) => *len as usize, + Self::Inline { len, .. } => *len as usize, } } @@ -573,7 +567,7 @@ impl Repr { Self::ArcStr(arc_str) => arc_str.is_empty(), Self::ArcString(arc_string) => arc_string.is_empty(), Self::StaticStr(s) => s.is_empty(), - Self::Inline(Inline { len, .. }) => *len == 0, + Self::Inline { len, .. } => *len == 0, } } @@ -586,7 +580,7 @@ impl Repr { Self::ArcStr(arc_str) => arc_str, Self::ArcString(arc_string) => arc_string, Self::StaticStr(s) => s, - Self::Inline(Inline { len, buf, .. }) => unsafe { + Self::Inline { len, buf } => unsafe { std::str::from_utf8_unchecked(&buf[..*len as usize]) }, } @@ -602,7 +596,7 @@ impl Repr { Arc::try_unwrap(arc_string).unwrap_or_else(|arc| (*arc).clone()) } Self::StaticStr(s) => s.to_string(), - Self::Inline(Inline { len, buf, .. }) => unsafe { + Self::Inline { len, buf } => unsafe { String::from_utf8_unchecked(buf[..len as usize].to_vec()) }, } @@ -618,7 +612,7 @@ impl Repr { Bytes::from(Arc::try_unwrap(arc_string).unwrap_or_else(|arc| (*arc).clone())) } Self::StaticStr(s) => Bytes::from_static(s.as_bytes()), - Self::Inline(Inline { len, buf, .. }) => Bytes::from(buf[..len as usize].to_vec()), + Self::Inline { len, buf } => Bytes::from(buf[..len as usize].to_vec()), } } @@ -661,14 +655,14 @@ impl Repr { Repr::StaticStr(s) => Self::StaticStr(unsafe { std::str::from_utf8_unchecked(&s.as_bytes()[sub_offset..sub_offset + sub_len]) }), - Repr::Inline(Inline { len: _, buf, .. }) => Self::Inline(Inline { + Repr::Inline { len: _, buf } => Self::Inline { len: sub_len, buf: { let mut new_buf = [0; INLINE_CAP]; new_buf[..sub_len].copy_from_slice(&buf[sub_offset..sub_offset + sub_len]); new_buf }, - }), + }, } } } @@ -682,7 +676,7 @@ impl AsRef<[u8]> for Repr { Self::ArcStr(arc_str) => arc_str.as_bytes(), Self::ArcString(arc_string) => arc_string.as_bytes(), Self::StaticStr(s) => s.as_bytes(), - Self::Inline(Inline { len, buf, .. }) => &buf[..*len as usize], + Self::Inline { len, buf } => &buf[..*len as usize], } } } From de5d4a95a7a091b3ea3de6327b54adf8ec9aa028 Mon Sep 17 00:00:00 2001 From: Pure White Date: Sat, 23 Dec 2023 20:31:18 +0800 Subject: [PATCH 4/6] fix clippy --- src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index db199ed..73708de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -555,7 +555,7 @@ impl Repr { Self::ArcStr(arc_str) => arc_str.len(), Self::ArcString(arc_string) => arc_string.len(), Self::StaticStr(s) => s.len(), - Self::Inline { len, .. } => *len as usize, + Self::Inline { len, .. } => *len, } } @@ -581,7 +581,7 @@ impl Repr { Self::ArcString(arc_string) => arc_string, Self::StaticStr(s) => s, Self::Inline { len, buf } => unsafe { - std::str::from_utf8_unchecked(&buf[..*len as usize]) + std::str::from_utf8_unchecked(&buf[..*len]) }, } } @@ -597,7 +597,7 @@ impl Repr { } Self::StaticStr(s) => s.to_string(), Self::Inline { len, buf } => unsafe { - String::from_utf8_unchecked(buf[..len as usize].to_vec()) + String::from_utf8_unchecked(buf[..len].to_vec()) }, } } @@ -612,7 +612,7 @@ impl Repr { Bytes::from(Arc::try_unwrap(arc_string).unwrap_or_else(|arc| (*arc).clone())) } Self::StaticStr(s) => Bytes::from_static(s.as_bytes()), - Self::Inline { len, buf } => Bytes::from(buf[..len as usize].to_vec()), + Self::Inline { len, buf } => Bytes::from(buf[..len].to_vec()), } } @@ -676,7 +676,7 @@ impl AsRef<[u8]> for Repr { Self::ArcStr(arc_str) => arc_str.as_bytes(), Self::ArcString(arc_string) => arc_string.as_bytes(), Self::StaticStr(s) => s.as_bytes(), - Self::Inline { len, buf } => &buf[..*len as usize], + Self::Inline { len, buf } => &buf[..*len], } } } From 9b5fad578857b949f3e96b1366da6de94c7e7d26 Mon Sep 17 00:00:00 2001 From: Pure White Date: Sat, 23 Dec 2023 20:37:11 +0800 Subject: [PATCH 5/6] fix lint --- src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 73708de..7566a09 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -580,9 +580,7 @@ impl Repr { Self::ArcStr(arc_str) => arc_str, Self::ArcString(arc_string) => arc_string, Self::StaticStr(s) => s, - Self::Inline { len, buf } => unsafe { - std::str::from_utf8_unchecked(&buf[..*len]) - }, + Self::Inline { len, buf } => unsafe { std::str::from_utf8_unchecked(&buf[..*len]) }, } } From 6f7687956c1ce5bf5739830c237e1363f4ea6b86 Mon Sep 17 00:00:00 2001 From: Pure White Date: Sun, 24 Dec 2023 00:26:06 +0800 Subject: [PATCH 6/6] use repr(u64) --- src/lib.rs | 48 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7566a09..502818e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,14 +34,17 @@ impl FastStr { Self(Repr::new(text)) } - /// Create a new inline `FastStr` (up to 24 bytes long) from a string slice `s`. + /// Create a new inline `FastStr` (up to 30 bytes long) from a string slice `s`. /// - /// This constructor panics if the length of `s` is greater than 24. + /// This constructor panics if the length of `s` is greater than 30. /// /// Note: the inline length is not guaranteed. #[inline] #[doc(hidden)] - #[deprecated(since = "0.2.13", note = "Please use `FastStr::new()` instead.")] + #[deprecated( + since = "0.2.13", + note = "The inline threshold is not stable. Please use `FastStr::new()` instead." + )] pub const fn new_inline(s: &str) -> Self { if s.len() > INLINE_CAP { panic!("[FastStr] string is too long to inline"); @@ -52,7 +55,10 @@ impl FastStr { buf[i] = s.as_bytes()[i]; i += 1 } - Self(Repr::Inline { len: s.len(), buf }) + Self(Repr::Inline { + len: s.len() as u8, + buf, + }) } /// Create a new `FastStr` from an `Arc`. @@ -226,7 +232,10 @@ impl FastStr { ch.encode_utf8(&mut buf[len..]); len += size; } - Self(Repr::Inline { len, buf }) + Self(Repr::Inline { + len: len as u8, + buf, + }) } } @@ -402,7 +411,10 @@ where buf[len..][..size].copy_from_slice(slice.as_bytes()); len += size; } - FastStr(Repr::Inline { len, buf }) + FastStr(Repr::Inline { + len: len as u8, + buf, + }) } impl iter::FromIterator for FastStr { @@ -491,16 +503,17 @@ impl From> for FastStr { } } -const INLINE_CAP: usize = 24; +const INLINE_CAP: usize = 30; #[derive(Clone)] +#[repr(u64)] enum Repr { Empty, Bytes(Bytes), ArcStr(Arc), ArcString(Arc), StaticStr(&'static str), - Inline { len: usize, buf: [u8; INLINE_CAP] }, + Inline { len: u8, buf: [u8; INLINE_CAP] }, } impl Repr { @@ -519,7 +532,10 @@ impl Repr { if len <= INLINE_CAP { let mut buf = [0; INLINE_CAP]; buf[..len].copy_from_slice(text.as_bytes()); - return Self::Inline { len, buf }; + return Self::Inline { + len: len as u8, + buf, + }; } } @@ -555,7 +571,7 @@ impl Repr { Self::ArcStr(arc_str) => arc_str.len(), Self::ArcString(arc_string) => arc_string.len(), Self::StaticStr(s) => s.len(), - Self::Inline { len, .. } => *len, + Self::Inline { len, .. } => *len as usize, } } @@ -580,7 +596,9 @@ impl Repr { Self::ArcStr(arc_str) => arc_str, Self::ArcString(arc_string) => arc_string, Self::StaticStr(s) => s, - Self::Inline { len, buf } => unsafe { std::str::from_utf8_unchecked(&buf[..*len]) }, + Self::Inline { len, buf } => unsafe { + std::str::from_utf8_unchecked(&buf[..*len as usize]) + }, } } @@ -595,7 +613,7 @@ impl Repr { } Self::StaticStr(s) => s.to_string(), Self::Inline { len, buf } => unsafe { - String::from_utf8_unchecked(buf[..len].to_vec()) + String::from_utf8_unchecked(buf[..len as usize].to_vec()) }, } } @@ -610,7 +628,7 @@ impl Repr { Bytes::from(Arc::try_unwrap(arc_string).unwrap_or_else(|arc| (*arc).clone())) } Self::StaticStr(s) => Bytes::from_static(s.as_bytes()), - Self::Inline { len, buf } => Bytes::from(buf[..len].to_vec()), + Self::Inline { len, buf } => Bytes::from(buf[..len as usize].to_vec()), } } @@ -654,7 +672,7 @@ impl Repr { std::str::from_utf8_unchecked(&s.as_bytes()[sub_offset..sub_offset + sub_len]) }), Repr::Inline { len: _, buf } => Self::Inline { - len: sub_len, + len: sub_len as u8, buf: { let mut new_buf = [0; INLINE_CAP]; new_buf[..sub_len].copy_from_slice(&buf[sub_offset..sub_offset + sub_len]); @@ -674,7 +692,7 @@ impl AsRef<[u8]> for Repr { Self::ArcStr(arc_str) => arc_str.as_bytes(), Self::ArcString(arc_string) => arc_string.as_bytes(), Self::StaticStr(s) => s.as_bytes(), - Self::Inline { len, buf } => &buf[..*len], + Self::Inline { len, buf } => &buf[..*len as usize], } } }