Coverage Report

Created: 2026-07-14 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/kstring-2.0.2/src/string.rs
Line
Count
Source
1
use std::{borrow::Cow, fmt};
2
3
use crate::stack::StackString;
4
use crate::KStringCowBase;
5
use crate::KStringRef;
6
7
pub(crate) type StdString = std::string::String;
8
9
/// A UTF-8 encoded, immutable string.
10
pub type KString = KStringBase<crate::backend::DefaultStr>;
11
12
/// A UTF-8 encoded, immutable string.
13
#[derive(Clone)]
14
#[repr(transparent)]
15
pub struct KStringBase<B> {
16
    inner: KStringInner<B>,
17
}
18
19
impl<B> KStringBase<B> {
20
    pub const EMPTY: Self = KStringBase::from_static("");
21
22
    /// Create a new empty `KStringBase`.
23
    #[inline]
24
    #[must_use]
25
    pub fn new() -> Self {
26
        Self::EMPTY
27
    }
28
29
    /// Create a reference to a `'static` data.
30
    #[inline]
31
    #[must_use]
32
0
    pub const fn from_static(other: &'static str) -> Self {
33
0
        Self {
34
0
            inner: KStringInner::from_static(other),
35
0
        }
36
0
    }
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>>>::from_static
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>>>::from_static
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>>>::from_static
37
38
    /// Create an inline string, if possible
39
    #[inline]
40
    #[must_use]
41
    pub fn try_inline(other: &str) -> Option<Self> {
42
        KStringInner::try_inline(other).map(|inner| Self { inner })
43
    }
44
}
45
46
impl<B: crate::backend::HeapStr> KStringBase<B> {
47
    /// Create an owned `KStringBase`.
48
    #[inline]
49
    #[must_use]
50
    pub fn from_boxed(other: crate::backend::BoxedStr) -> Self {
51
        Self {
52
            inner: KStringInner::from_boxed(other),
53
        }
54
    }
55
56
    /// Create an owned `KStringBase`.
57
    #[inline]
58
    #[must_use]
59
    pub fn from_string(other: StdString) -> Self {
60
        Self {
61
            inner: KStringInner::from_string(other),
62
        }
63
    }
64
65
    /// Create an owned `KStringBase` optimally from a reference.
66
    #[inline]
67
    #[must_use]
68
4.46M
    pub fn from_ref(other: &str) -> Self {
69
4.46M
        Self {
70
4.46M
            inner: KStringInner::from_ref(other),
71
4.46M
        }
72
4.46M
    }
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>>>::from_ref
<kstring::string::KStringBase<alloc::boxed::Box<str>>>::from_ref
Line
Count
Source
68
33.8k
    pub fn from_ref(other: &str) -> Self {
69
33.8k
        Self {
70
33.8k
            inner: KStringInner::from_ref(other),
71
33.8k
        }
72
33.8k
    }
<kstring::string::KStringBase<alloc::boxed::Box<str>>>::from_ref
Line
Count
Source
68
4.43M
    pub fn from_ref(other: &str) -> Self {
69
4.43M
        Self {
70
4.43M
            inner: KStringInner::from_ref(other),
71
4.43M
        }
72
4.43M
    }
73
74
    /// Get a reference to the `KStringBase`.
75
    #[inline]
76
    #[must_use]
77
3.19M
    pub fn as_ref(&self) -> KStringRef<'_> {
78
3.19M
        self.inner.as_ref()
79
3.19M
    }
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>>>::as_ref
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>>>::as_ref
<kstring::string::KStringBase<alloc::boxed::Box<str>>>::as_ref
Line
Count
Source
77
3.19M
    pub fn as_ref(&self) -> KStringRef<'_> {
78
3.19M
        self.inner.as_ref()
79
3.19M
    }
80
81
    /// Extracts a string slice containing the entire `KStringBase`.
82
    #[inline]
83
    #[must_use]
84
73.5k
    pub fn as_str(&self) -> &str {
85
73.5k
        self.inner.as_str()
86
73.5k
    }
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>>>::as_str
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>>>::as_str
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>>>::as_str
<kstring::string::KStringBase<alloc::boxed::Box<str>>>::as_str
Line
Count
Source
84
73.5k
    pub fn as_str(&self) -> &str {
85
73.5k
        self.inner.as_str()
86
73.5k
    }
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>>>::as_str
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>>>::as_str
87
88
    /// Convert to a mutable string type, cloning the data if necessary.
89
    #[inline]
90
    #[must_use]
91
    pub fn into_string(self) -> StdString {
92
        String::from(self.into_boxed_str())
93
    }
94
95
    /// Convert to a mutable string type, cloning the data if necessary.
96
    #[inline]
97
    #[must_use]
98
    pub fn into_boxed_str(self) -> crate::backend::BoxedStr {
99
        self.inner.into_boxed_str()
100
    }
101
102
    /// Convert to a Cow str
103
    #[inline]
104
    #[must_use]
105
    pub fn into_cow_str(self) -> Cow<'static, str> {
106
        self.inner.into_cow_str()
107
    }
108
}
109
110
impl<B: crate::backend::HeapStr> std::ops::Deref for KStringBase<B> {
111
    type Target = str;
112
113
    #[inline]
114
19.1k
    fn deref(&self) -> &str {
115
19.1k
        self.as_str()
116
19.1k
    }
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>> as core::ops::deref::Deref>::deref
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>> as core::ops::deref::Deref>::deref
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>> as core::ops::deref::Deref>::deref
<kstring::string::KStringBase<alloc::boxed::Box<str>> as core::ops::deref::Deref>::deref
Line
Count
Source
114
19.1k
    fn deref(&self) -> &str {
115
19.1k
        self.as_str()
116
19.1k
    }
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>> as core::ops::deref::Deref>::deref
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>> as core::ops::deref::Deref>::deref
117
}
118
119
impl<B: crate::backend::HeapStr> Eq for KStringBase<B> {}
120
121
impl<B: crate::backend::HeapStr> PartialEq<KStringBase<B>> for KStringBase<B> {
122
    #[inline]
123
443
    fn eq(&self, other: &Self) -> bool {
124
443
        PartialEq::eq(self.as_str(), other.as_str())
125
443
    }
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>> as core::cmp::PartialEq>::eq
<kstring::string::KStringBase<alloc::boxed::Box<str>> as core::cmp::PartialEq>::eq
Line
Count
Source
123
443
    fn eq(&self, other: &Self) -> bool {
124
443
        PartialEq::eq(self.as_str(), other.as_str())
125
443
    }
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>> as core::cmp::PartialEq>::eq
126
}
127
128
impl<B: crate::backend::HeapStr> PartialEq<str> for KStringBase<B> {
129
    #[inline]
130
    fn eq(&self, other: &str) -> bool {
131
        PartialEq::eq(self.as_str(), other)
132
    }
133
}
134
135
impl<'s, B: crate::backend::HeapStr> PartialEq<&'s str> for KStringBase<B> {
136
    #[inline]
137
    fn eq(&self, other: &&str) -> bool {
138
        PartialEq::eq(self.as_str(), *other)
139
    }
140
}
141
142
impl<B: crate::backend::HeapStr> PartialEq<String> for KStringBase<B> {
143
    #[inline]
144
    fn eq(&self, other: &StdString) -> bool {
145
        PartialEq::eq(self.as_str(), other.as_str())
146
    }
147
}
148
149
impl<B: crate::backend::HeapStr> Ord for KStringBase<B> {
150
    #[inline]
151
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
152
        self.as_str().cmp(other.as_str())
153
    }
154
}
155
156
impl<B: crate::backend::HeapStr> PartialOrd for KStringBase<B> {
157
    #[inline]
158
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
159
        Some(self.cmp(other))
160
    }
161
}
162
163
impl<B: crate::backend::HeapStr> std::hash::Hash for KStringBase<B> {
164
    #[inline]
165
30.2k
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
166
30.2k
        self.as_str().hash(state);
167
30.2k
    }
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>> as core::hash::Hash>::hash::<std::hash::random::DefaultHasher>
<kstring::string::KStringBase<alloc::boxed::Box<str>> as core::hash::Hash>::hash::<std::hash::random::DefaultHasher>
Line
Count
Source
165
30.2k
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
166
30.2k
        self.as_str().hash(state);
167
30.2k
    }
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>> as core::hash::Hash>::hash::<std::hash::random::DefaultHasher>
168
}
169
170
impl<B: crate::backend::HeapStr> fmt::Debug for KStringBase<B> {
171
    #[inline]
172
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173
        self.as_str().fmt(f)
174
    }
175
}
176
177
impl<B: crate::backend::HeapStr> fmt::Display for KStringBase<B> {
178
    #[inline]
179
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180
        fmt::Display::fmt(self.as_str(), f)
181
    }
182
}
183
184
impl<B: crate::backend::HeapStr> AsRef<str> for KStringBase<B> {
185
    #[inline]
186
    fn as_ref(&self) -> &str {
187
        self.as_str()
188
    }
189
}
190
191
impl<B: crate::backend::HeapStr> AsRef<[u8]> for KStringBase<B> {
192
    #[inline]
193
    fn as_ref(&self) -> &[u8] {
194
        self.as_bytes()
195
    }
196
}
197
198
impl<B: crate::backend::HeapStr> AsRef<std::ffi::OsStr> for KStringBase<B> {
199
    #[inline]
200
    fn as_ref(&self) -> &std::ffi::OsStr {
201
        (**self).as_ref()
202
    }
203
}
204
205
impl<B: crate::backend::HeapStr> AsRef<std::path::Path> for KStringBase<B> {
206
    #[inline]
207
    fn as_ref(&self) -> &std::path::Path {
208
        std::path::Path::new(self)
209
    }
210
}
211
212
impl<B: crate::backend::HeapStr> std::borrow::Borrow<str> for KStringBase<B> {
213
    #[inline]
214
9.50k
    fn borrow(&self) -> &str {
215
9.50k
        self.as_str()
216
9.50k
    }
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>> as core::borrow::Borrow<str>>::borrow
<kstring::string::KStringBase<alloc::boxed::Box<str>> as core::borrow::Borrow<str>>::borrow
Line
Count
Source
214
9.50k
    fn borrow(&self) -> &str {
215
9.50k
        self.as_str()
216
9.50k
    }
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>> as core::borrow::Borrow<str>>::borrow
217
}
218
219
impl<B: crate::backend::HeapStr> Default for KStringBase<B> {
220
    #[inline]
221
    fn default() -> Self {
222
        Self::new()
223
    }
224
}
225
226
impl<'s, B: crate::backend::HeapStr> From<KStringRef<'s>> for KStringBase<B> {
227
    #[inline]
228
4.45M
    fn from(other: KStringRef<'s>) -> Self {
229
4.45M
        other.to_owned()
230
4.45M
    }
Unexecuted instantiation: <kstring::string::KStringBase<alloc::boxed::Box<str>> as core::convert::From<kstring::string_ref::KStringRef>>::from
<kstring::string::KStringBase<alloc::boxed::Box<str>> as core::convert::From<kstring::string_ref::KStringRef>>::from
Line
Count
Source
228
21.3k
    fn from(other: KStringRef<'s>) -> Self {
229
21.3k
        other.to_owned()
230
21.3k
    }
<kstring::string::KStringBase<alloc::boxed::Box<str>> as core::convert::From<kstring::string_ref::KStringRef>>::from
Line
Count
Source
228
4.43M
    fn from(other: KStringRef<'s>) -> Self {
229
4.43M
        other.to_owned()
230
4.43M
    }
231
}
232
233
impl<'s, B: crate::backend::HeapStr> From<&'s KStringRef<'s>> for KStringBase<B> {
234
    #[inline]
235
    fn from(other: &'s KStringRef<'s>) -> Self {
236
        other.to_owned()
237
    }
238
}
239
240
impl<'s, B: crate::backend::HeapStr> From<KStringCowBase<'s, B>> for KStringBase<B> {
241
    #[inline]
242
    fn from(other: KStringCowBase<'s, B>) -> Self {
243
        other.into_owned()
244
    }
245
}
246
247
impl<'s, B: crate::backend::HeapStr> From<&'s KStringCowBase<'s, B>> for KStringBase<B> {
248
    #[inline]
249
    fn from(other: &'s KStringCowBase<'s, B>) -> Self {
250
        other.clone().into_owned()
251
    }
252
}
253
254
impl<B: crate::backend::HeapStr> From<StdString> for KStringBase<B> {
255
    #[inline]
256
    fn from(other: StdString) -> Self {
257
        Self::from_string(other)
258
    }
259
}
260
261
impl<'s, B: crate::backend::HeapStr> From<&'s StdString> for KStringBase<B> {
262
    #[inline]
263
    fn from(other: &'s StdString) -> Self {
264
        Self::from_ref(other)
265
    }
266
}
267
268
impl<B: crate::backend::HeapStr> From<crate::backend::BoxedStr> for KStringBase<B> {
269
    #[inline]
270
    fn from(other: crate::backend::BoxedStr) -> Self {
271
        Self::from_boxed(other)
272
    }
273
}
274
275
impl<'s, B: crate::backend::HeapStr> From<&'s crate::backend::BoxedStr> for KStringBase<B> {
276
    #[inline]
277
    fn from(other: &'s crate::backend::BoxedStr) -> Self {
278
        Self::from_ref(other)
279
    }
280
}
281
282
impl<B: crate::backend::HeapStr> From<&'static str> for KStringBase<B> {
283
    #[inline]
284
    fn from(other: &'static str) -> Self {
285
        Self::from_static(other)
286
    }
287
}
288
289
impl<B: crate::backend::HeapStr> std::str::FromStr for KStringBase<B> {
290
    type Err = std::convert::Infallible;
291
    #[inline]
292
    fn from_str(s: &str) -> Result<Self, Self::Err> {
293
        Ok(Self::from_ref(s))
294
    }
295
}
296
297
#[cfg(feature = "serde")]
298
impl<B: crate::backend::HeapStr> serde::Serialize for KStringBase<B> {
299
    #[inline]
300
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
301
    where
302
        S: serde::Serializer,
303
    {
304
        serializer.serialize_str(self.as_str())
305
    }
306
}
307
308
#[cfg(feature = "serde")]
309
impl<'de, B: crate::backend::HeapStr> serde::Deserialize<'de> for KStringBase<B> {
310
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
311
    where
312
        D: serde::Deserializer<'de>,
313
    {
314
        deserializer.deserialize_string(StringVisitor(std::marker::PhantomData))
315
    }
316
}
317
318
#[cfg(feature = "serde")]
319
struct StringVisitor<B>(std::marker::PhantomData<B>);
320
321
#[cfg(feature = "serde")]
322
impl<'de, B: crate::backend::HeapStr> serde::de::Visitor<'de> for StringVisitor<B> {
323
    type Value = KStringBase<B>;
324
325
    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
326
        formatter.write_str("a string")
327
    }
328
329
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
330
    where
331
        E: serde::de::Error,
332
    {
333
        Ok(Self::Value::from_ref(v))
334
    }
335
336
    fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
337
    where
338
        E: serde::de::Error,
339
    {
340
        Ok(Self::Value::from_string(v))
341
    }
342
343
    fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
344
    where
345
        E: serde::de::Error,
346
    {
347
        match std::str::from_utf8(v) {
348
            Ok(s) => Ok(Self::Value::from_ref(s)),
349
            Err(_) => Err(serde::de::Error::invalid_value(
350
                serde::de::Unexpected::Bytes(v),
351
                &self,
352
            )),
353
        }
354
    }
355
356
    fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
357
    where
358
        E: serde::de::Error,
359
    {
360
        match String::from_utf8(v) {
361
            Ok(s) => Ok(Self::Value::from_string(s)),
362
            Err(e) => Err(serde::de::Error::invalid_value(
363
                serde::de::Unexpected::Bytes(&e.into_bytes()),
364
                &self,
365
            )),
366
        }
367
    }
368
}
369
370
use inner::KStringInner;
371
372
#[cfg(not(feature = "unsafe"))]
373
mod inner {
374
    use super::*;
375
376
    pub(super) enum KStringInner<B> {
377
        Singleton(&'static str),
378
        Inline(StackString<CAPACITY>),
379
        Owned(B),
380
    }
381
382
    impl<B> KStringInner<B> {
383
        /// Create a reference to a `'static` data.
384
        #[inline]
385
        pub const fn from_static(other: &'static str) -> Self {
386
            Self::Singleton(other)
387
        }
388
389
        #[inline]
390
        pub fn try_inline(other: &str) -> Option<Self> {
391
            StackString::try_new(other).map(Self::Inline)
392
        }
393
    }
394
395
    impl<B: crate::backend::HeapStr> KStringInner<B> {
396
        #[inline]
397
        pub(super) fn from_boxed(other: crate::backend::BoxedStr) -> Self {
398
            #[allow(clippy::useless_conversion)]
399
            Self::Owned(B::from_boxed_str(other))
400
        }
401
402
        #[inline]
403
        pub(super) fn from_string(other: StdString) -> Self {
404
            if (0..=CAPACITY).contains(&other.len()) {
405
                let inline = { StackString::new(other.as_str()) };
406
                Self::Inline(inline)
407
            } else {
408
                Self::from_boxed(other.into_boxed_str())
409
            }
410
        }
411
412
        #[inline]
413
        pub(super) fn from_ref(other: &str) -> Self {
414
            if (0..=CAPACITY).contains(&other.len()) {
415
                let inline = { StackString::new(other) };
416
                Self::Inline(inline)
417
            } else {
418
                Self::Owned(B::from_str(other))
419
            }
420
        }
421
422
        #[inline]
423
        pub(super) fn as_ref(&self) -> KStringRef<'_> {
424
            match self {
425
                Self::Singleton(s) => KStringRef::from_static(s),
426
                Self::Inline(s) => KStringRef::from_ref(s.as_str()),
427
                Self::Owned(s) => KStringRef::from_ref(s.as_str()),
428
            }
429
        }
430
431
        #[inline]
432
        pub(super) fn as_str(&self) -> &str {
433
            match self {
434
                Self::Singleton(s) => s,
435
                Self::Inline(s) => s.as_str(),
436
                Self::Owned(s) => s.as_str(),
437
            }
438
        }
439
440
        #[inline]
441
        pub(super) fn into_boxed_str(self) -> crate::backend::BoxedStr {
442
            match self {
443
                Self::Singleton(s) => crate::backend::BoxedStr::from(s),
444
                Self::Inline(s) => crate::backend::BoxedStr::from(s.as_str()),
445
                Self::Owned(s) => crate::backend::BoxedStr::from(s.as_str()),
446
            }
447
        }
448
449
        /// Convert to a Cow str
450
        #[inline]
451
        pub(super) fn into_cow_str(self) -> Cow<'static, str> {
452
            match self {
453
                Self::Singleton(s) => Cow::Borrowed(s),
454
                Self::Inline(s) => Cow::Owned(s.as_str().into()),
455
                Self::Owned(s) => Cow::Owned(s.as_str().into()),
456
            }
457
        }
458
    }
459
460
    // Explicit to avoid inlining which cuts clone times in half.
461
    //
462
    // An automatically derived `clone()` has 10ns overhead while the explicit `Deref`/`as_str` has
463
    // none of that.  Being explicit and removing the `#[inline]` attribute dropped the overhead to
464
    // 5ns.
465
    //
466
    // My only guess is that the `clone()` calls we delegate to are just that much bigger than
467
    // `as_str()` that, when combined with a jump table, is blowing the icache, slowing things down.
468
    impl<B: Clone> Clone for KStringInner<B> {
469
        fn clone(&self) -> Self {
470
            match self {
471
                Self::Singleton(s) => Self::Singleton(s),
472
                Self::Inline(s) => Self::Inline(*s),
473
                Self::Owned(s) => Self::Owned(s.clone()),
474
            }
475
        }
476
    }
477
478
    #[allow(unused)]
479
    const LEN_SIZE: usize = std::mem::size_of::<crate::stack::Len>();
480
481
    #[allow(unused)]
482
    const TAG_SIZE: usize = std::mem::size_of::<u8>();
483
484
    #[allow(unused)]
485
    const MAX_CAPACITY: usize =
486
        std::mem::size_of::<crate::string::StdString>() - TAG_SIZE - LEN_SIZE;
487
488
    // Performance seems to slow down when trying to occupy all of the padding left by `String`'s
489
    // discriminant.  The question is whether faster len=1-16 "allocations" outweighs going to the heap
490
    // for len=17-22.
491
    #[allow(unused)]
492
    const ALIGNED_CAPACITY: usize = std::mem::size_of::<crate::backend::DefaultStr>() - LEN_SIZE;
493
494
    #[cfg(feature = "max_inline")]
495
    const CAPACITY: usize = MAX_CAPACITY;
496
    #[cfg(not(feature = "max_inline"))]
497
    const CAPACITY: usize = ALIGNED_CAPACITY;
498
}
499
500
#[cfg(feature = "unsafe")]
501
mod inner {
502
    use super::*;
503
504
    #[repr(C)]
505
    pub(super) union KStringInner<B> {
506
        tag: TagVariant,
507
        singleton: SingletonVariant,
508
        owned: std::mem::ManuallyDrop<OwnedVariant<B>>,
509
        inline: InlineVariant,
510
    }
511
512
    impl<B> KStringInner<B> {
513
        /// Create a reference to a `'static` data.
514
        #[inline]
515
0
        pub const fn from_static(other: &'static str) -> Self {
516
0
            Self {
517
0
                singleton: SingletonVariant::new(other),
518
0
            }
519
0
        }
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::from_static
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::from_static
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::from_static
520
521
        #[inline]
522
        pub fn try_inline(other: &str) -> Option<Self> {
523
            StackString::try_new(other).map(|inline| Self {
524
                inline: InlineVariant::new(inline),
525
            })
526
        }
527
528
        #[inline]
529
7.76M
        const fn tag(&self) -> Tag {
530
            unsafe {
531
                // SAFETY: `tag` is in the same spot in each variant
532
7.76M
                self.tag.tag
533
            }
534
7.76M
        }
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::tag
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::tag
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::tag
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::tag
<kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::tag
Line
Count
Source
529
46.2k
        const fn tag(&self) -> Tag {
530
            unsafe {
531
                // SAFETY: `tag` is in the same spot in each variant
532
46.2k
                self.tag.tag
533
            }
534
46.2k
        }
<kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::tag
Line
Count
Source
529
96.3k
        const fn tag(&self) -> Tag {
530
            unsafe {
531
                // SAFETY: `tag` is in the same spot in each variant
532
96.3k
                self.tag.tag
533
            }
534
96.3k
        }
<kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::tag
Line
Count
Source
529
3.19M
        const fn tag(&self) -> Tag {
530
            unsafe {
531
                // SAFETY: `tag` is in the same spot in each variant
532
3.19M
                self.tag.tag
533
            }
534
3.19M
        }
<kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::tag
Line
Count
Source
529
1.24M
        const fn tag(&self) -> Tag {
530
            unsafe {
531
                // SAFETY: `tag` is in the same spot in each variant
532
1.24M
                self.tag.tag
533
            }
534
1.24M
        }
<kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::tag
Line
Count
Source
529
3.19M
        const fn tag(&self) -> Tag {
530
            unsafe {
531
                // SAFETY: `tag` is in the same spot in each variant
532
3.19M
                self.tag.tag
533
            }
534
3.19M
        }
535
    }
536
537
    impl<B: crate::backend::HeapStr> KStringInner<B> {
538
        #[inline]
539
        pub(super) fn from_boxed(other: crate::backend::BoxedStr) -> Self {
540
            #[allow(clippy::useless_conversion)]
541
            let payload = B::from_boxed_str(other);
542
            Self {
543
                owned: std::mem::ManuallyDrop::new(OwnedVariant::new(payload)),
544
            }
545
        }
546
547
        #[inline]
548
        pub(super) fn from_string(other: StdString) -> Self {
549
            if (0..=CAPACITY).contains(&other.len()) {
550
                let payload = unsafe {
551
                    // SAFETY: range check ensured this is always safe
552
                    StackString::new_unchecked(other.as_str())
553
                };
554
                Self {
555
                    inline: InlineVariant::new(payload),
556
                }
557
            } else {
558
                Self::from_boxed(other.into_boxed_str())
559
            }
560
        }
561
562
        #[inline]
563
4.46M
        pub(super) fn from_ref(other: &str) -> Self {
564
4.46M
            if (0..=CAPACITY).contains(&other.len()) {
565
4.46M
                let payload = unsafe {
566
                    // SAFETY: range check ensured this is always safe
567
4.46M
                    StackString::new_unchecked(other)
568
                };
569
4.46M
                Self {
570
4.46M
                    inline: InlineVariant::new(payload),
571
4.46M
                }
572
            } else {
573
                #[allow(clippy::useless_conversion)]
574
6.83k
                let payload = B::from_str(other);
575
6.83k
                Self {
576
6.83k
                    owned: std::mem::ManuallyDrop::new(OwnedVariant::new(payload)),
577
6.83k
                }
578
            }
579
4.46M
        }
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::from_ref
<kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::from_ref
Line
Count
Source
563
33.8k
        pub(super) fn from_ref(other: &str) -> Self {
564
33.8k
            if (0..=CAPACITY).contains(&other.len()) {
565
33.0k
                let payload = unsafe {
566
                    // SAFETY: range check ensured this is always safe
567
33.0k
                    StackString::new_unchecked(other)
568
                };
569
33.0k
                Self {
570
33.0k
                    inline: InlineVariant::new(payload),
571
33.0k
                }
572
            } else {
573
                #[allow(clippy::useless_conversion)]
574
852
                let payload = B::from_str(other);
575
852
                Self {
576
852
                    owned: std::mem::ManuallyDrop::new(OwnedVariant::new(payload)),
577
852
                }
578
            }
579
33.8k
        }
<kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::from_ref
Line
Count
Source
563
4.43M
        pub(super) fn from_ref(other: &str) -> Self {
564
4.43M
            if (0..=CAPACITY).contains(&other.len()) {
565
4.42M
                let payload = unsafe {
566
                    // SAFETY: range check ensured this is always safe
567
4.42M
                    StackString::new_unchecked(other)
568
                };
569
4.42M
                Self {
570
4.42M
                    inline: InlineVariant::new(payload),
571
4.42M
                }
572
            } else {
573
                #[allow(clippy::useless_conversion)]
574
5.97k
                let payload = B::from_str(other);
575
5.97k
                Self {
576
5.97k
                    owned: std::mem::ManuallyDrop::new(OwnedVariant::new(payload)),
577
5.97k
                }
578
            }
579
4.43M
        }
580
581
        #[inline]
582
3.19M
        pub(super) fn as_ref(&self) -> KStringRef<'_> {
583
3.19M
            let tag = self.tag();
584
            unsafe {
585
                // SAFETY: `tag` ensures access to correct variant
586
3.19M
                if tag.is_singleton() {
587
0
                    KStringRef::from_static(self.singleton.payload)
588
3.19M
                } else if tag.is_owned() {
589
5.45k
                    KStringRef::from_ref(self.owned.payload.as_str())
590
                } else {
591
3.18M
                    debug_assert!(tag.is_inline());
592
3.18M
                    KStringRef::from_ref(self.inline.payload.as_str())
593
                }
594
            }
595
3.19M
        }
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::as_ref
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::as_ref
<kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::as_ref
Line
Count
Source
582
3.19M
        pub(super) fn as_ref(&self) -> KStringRef<'_> {
583
3.19M
            let tag = self.tag();
584
            unsafe {
585
                // SAFETY: `tag` ensures access to correct variant
586
3.19M
                if tag.is_singleton() {
587
0
                    KStringRef::from_static(self.singleton.payload)
588
3.19M
                } else if tag.is_owned() {
589
5.45k
                    KStringRef::from_ref(self.owned.payload.as_str())
590
                } else {
591
3.18M
                    debug_assert!(tag.is_inline());
592
3.18M
                    KStringRef::from_ref(self.inline.payload.as_str())
593
                }
594
            }
595
3.19M
        }
596
597
        #[inline]
598
73.5k
        pub(super) fn as_str(&self) -> &str {
599
73.5k
            let tag = self.tag();
600
            unsafe {
601
                // SAFETY: `tag` ensures access to correct variant
602
73.5k
                if tag.is_singleton() {
603
0
                    self.singleton.payload
604
73.5k
                } else if tag.is_owned() {
605
1.77k
                    self.owned.payload.as_str()
606
                } else {
607
71.7k
                    debug_assert!(tag.is_inline());
608
71.7k
                    self.inline.payload.as_str()
609
                }
610
            }
611
73.5k
        }
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::as_str
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::as_str
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::as_str
<kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::as_str
Line
Count
Source
598
73.5k
        pub(super) fn as_str(&self) -> &str {
599
73.5k
            let tag = self.tag();
600
            unsafe {
601
                // SAFETY: `tag` ensures access to correct variant
602
73.5k
                if tag.is_singleton() {
603
0
                    self.singleton.payload
604
73.5k
                } else if tag.is_owned() {
605
1.77k
                    self.owned.payload.as_str()
606
                } else {
607
71.7k
                    debug_assert!(tag.is_inline());
608
71.7k
                    self.inline.payload.as_str()
609
                }
610
            }
611
73.5k
        }
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::as_str
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>>>::as_str
612
613
        #[inline]
614
        pub(super) fn into_boxed_str(self) -> crate::backend::BoxedStr {
615
            let tag = self.tag();
616
            unsafe {
617
                // SAFETY: `tag` ensures access to correct variant
618
                if tag.is_singleton() {
619
                    crate::backend::BoxedStr::from(self.singleton.payload)
620
                } else if tag.is_owned() {
621
                    crate::backend::BoxedStr::from(self.owned.payload.as_str())
622
                } else {
623
                    debug_assert!(tag.is_inline());
624
                    crate::backend::BoxedStr::from(self.inline.payload.as_ref())
625
                }
626
            }
627
        }
628
629
        /// Convert to a Cow str
630
        #[inline]
631
        pub(super) fn into_cow_str(self) -> Cow<'static, str> {
632
            let tag = self.tag();
633
            unsafe {
634
                // SAFETY: `tag` ensures access to correct variant
635
                if tag.is_singleton() {
636
                    Cow::Borrowed(self.singleton.payload)
637
                } else if tag.is_owned() {
638
                    Cow::Owned(self.owned.payload.as_str().into())
639
                } else {
640
                    debug_assert!(tag.is_inline());
641
                    Cow::Owned(self.inline.payload.as_str().into())
642
                }
643
            }
644
        }
645
    }
646
647
    // Explicit to avoid inlining which cuts clone times in half.
648
    //
649
    // An automatically derived `clone()` has 10ns overhead while the explicit `Deref`/`as_str` has
650
    // none of that.  Being explicit and removing the `#[inline]` attribute dropped the overhead to
651
    // 5ns.
652
    //
653
    // My only guess is that the `clone()` calls we delegate to are just that much bigger than
654
    // `as_str()` that, when combined with a jump table, is blowing the icache, slowing things down.
655
    impl<B: Clone> Clone for KStringInner<B> {
656
17.6k
        fn clone(&self) -> Self {
657
17.6k
            let tag = self.tag();
658
17.6k
            if tag.is_owned() {
659
                unsafe {
660
                    // SAFETY: `tag` ensures access to correct variant
661
326
                    Self {
662
326
                        owned: std::mem::ManuallyDrop::new(OwnedVariant::new(
663
326
                            self.owned.payload.clone(),
664
326
                        )),
665
326
                    }
666
                }
667
            } else {
668
                unsafe {
669
                    // SAFETY: `tag` ensures access to correct variant
670
                    // SAFETY: non-owned types are copyable
671
17.2k
                    std::mem::transmute_copy(self)
672
                }
673
            }
674
17.6k
        }
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>> as core::clone::Clone>::clone
<kstring::string::inner::KStringInner<alloc::boxed::Box<str>> as core::clone::Clone>::clone
Line
Count
Source
656
17.6k
        fn clone(&self) -> Self {
657
17.6k
            let tag = self.tag();
658
17.6k
            if tag.is_owned() {
659
                unsafe {
660
                    // SAFETY: `tag` ensures access to correct variant
661
326
                    Self {
662
326
                        owned: std::mem::ManuallyDrop::new(OwnedVariant::new(
663
326
                            self.owned.payload.clone(),
664
326
                        )),
665
326
                    }
666
                }
667
            } else {
668
                unsafe {
669
                    // SAFETY: `tag` ensures access to correct variant
670
                    // SAFETY: non-owned types are copyable
671
17.2k
                    std::mem::transmute_copy(self)
672
                }
673
            }
674
17.6k
        }
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>> as core::clone::Clone>::clone
675
    }
676
677
    impl<B> Drop for KStringInner<B> {
678
4.48M
        fn drop(&mut self) {
679
4.48M
            let tag = self.tag();
680
4.48M
            if tag.is_owned() {
681
                unsafe {
682
                    // SAFETY: `tag` ensures we are using the right variant
683
7.15k
                    std::mem::ManuallyDrop::drop(&mut self.owned)
684
                }
685
4.47M
            }
686
4.48M
        }
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>> as core::ops::drop::Drop>::drop
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>> as core::ops::drop::Drop>::drop
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>> as core::ops::drop::Drop>::drop
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>> as core::ops::drop::Drop>::drop
<kstring::string::inner::KStringInner<alloc::boxed::Box<str>> as core::ops::drop::Drop>::drop
Line
Count
Source
678
46.2k
        fn drop(&mut self) {
679
46.2k
            let tag = self.tag();
680
46.2k
            if tag.is_owned() {
681
                unsafe {
682
                    // SAFETY: `tag` ensures we are using the right variant
683
906
                    std::mem::ManuallyDrop::drop(&mut self.owned)
684
                }
685
45.3k
            }
686
46.2k
        }
<kstring::string::inner::KStringInner<alloc::boxed::Box<str>> as core::ops::drop::Drop>::drop
Line
Count
Source
678
5.24k
        fn drop(&mut self) {
679
5.24k
            let tag = self.tag();
680
5.24k
            if tag.is_owned() {
681
                unsafe {
682
                    // SAFETY: `tag` ensures we are using the right variant
683
272
                    std::mem::ManuallyDrop::drop(&mut self.owned)
684
                }
685
4.97k
            }
686
5.24k
        }
<kstring::string::inner::KStringInner<alloc::boxed::Box<str>> as core::ops::drop::Drop>::drop
Line
Count
Source
678
3.19M
        fn drop(&mut self) {
679
3.19M
            let tag = self.tag();
680
3.19M
            if tag.is_owned() {
681
                unsafe {
682
                    // SAFETY: `tag` ensures we are using the right variant
683
5.45k
                    std::mem::ManuallyDrop::drop(&mut self.owned)
684
                }
685
3.18M
            }
686
3.19M
        }
<kstring::string::inner::KStringInner<alloc::boxed::Box<str>> as core::ops::drop::Drop>::drop
Line
Count
Source
678
1.24M
        fn drop(&mut self) {
679
1.24M
            let tag = self.tag();
680
1.24M
            if tag.is_owned() {
681
                unsafe {
682
                    // SAFETY: `tag` ensures we are using the right variant
683
526
                    std::mem::ManuallyDrop::drop(&mut self.owned)
684
                }
685
1.24M
            }
686
1.24M
        }
Unexecuted instantiation: <kstring::string::inner::KStringInner<alloc::boxed::Box<str>> as core::ops::drop::Drop>::drop
687
    }
688
689
    #[allow(unused)]
690
    const LEN_SIZE: usize = std::mem::size_of::<crate::stack::Len>();
691
692
    #[allow(unused)]
693
    const TAG_SIZE: usize = std::mem::size_of::<Tag>();
694
695
    #[allow(unused)]
696
    const PAYLOAD_SIZE: usize = std::mem::size_of::<crate::backend::DefaultStr>();
697
    type Payload = Padding<PAYLOAD_SIZE>;
698
699
    #[allow(unused)]
700
    const TARGET_SIZE: usize = std::mem::size_of::<Target>();
701
    type Target = crate::string::StdString;
702
703
    #[allow(unused)]
704
    const MAX_CAPACITY: usize = TARGET_SIZE - LEN_SIZE - TAG_SIZE;
705
706
    // Performance seems to slow down when trying to occupy all of the padding left by `String`'s
707
    // discriminant.  The question is whether faster len=1-16 "allocations" outweighs going to the heap
708
    // for len=17-22.
709
    #[allow(unused)]
710
    const ALIGNED_CAPACITY: usize = PAYLOAD_SIZE - LEN_SIZE;
711
712
    #[cfg(feature = "max_inline")]
713
    const CAPACITY: usize = MAX_CAPACITY;
714
    #[cfg(not(feature = "max_inline"))]
715
    const CAPACITY: usize = ALIGNED_CAPACITY;
716
717
    const PAYLOAD_PAD_SIZE: usize = TARGET_SIZE - PAYLOAD_SIZE - TAG_SIZE;
718
    const INLINE_PAD_SIZE: usize = TARGET_SIZE - CAPACITY - LEN_SIZE - TAG_SIZE;
719
720
    #[derive(Copy, Clone)]
721
    #[repr(C)]
722
    struct TagVariant {
723
        payload: Payload,
724
        pad: Padding<PAYLOAD_PAD_SIZE>,
725
        tag: Tag,
726
    }
727
    static_assertions::assert_eq_size!(Target, TagVariant);
728
729
    #[derive(Copy, Clone)]
730
    #[repr(C)]
731
    struct SingletonVariant {
732
        payload: &'static str,
733
        pad: Padding<PAYLOAD_PAD_SIZE>,
734
        tag: Tag,
735
    }
736
    static_assertions::assert_eq_size!(Payload, &'static str);
737
    static_assertions::assert_eq_size!(Target, SingletonVariant);
738
739
    impl SingletonVariant {
740
        #[inline]
741
0
        const fn new(payload: &'static str) -> Self {
742
0
            Self {
743
0
                payload,
744
0
                pad: Padding::new(),
745
0
                tag: Tag::SINGLETON,
746
0
            }
747
0
        }
748
    }
749
750
    impl std::fmt::Debug for SingletonVariant {
751
        #[inline]
752
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
753
            self.payload.fmt(f)
754
        }
755
    }
756
757
    #[derive(Clone)]
758
    #[repr(C)]
759
    struct OwnedVariant<B> {
760
        payload: B,
761
        pad: Padding<PAYLOAD_PAD_SIZE>,
762
        tag: Tag,
763
    }
764
    static_assertions::assert_eq_size!(Payload, crate::backend::DefaultStr);
765
    static_assertions::assert_eq_size!(Target, OwnedVariant<crate::backend::DefaultStr>);
766
767
    impl<B> OwnedVariant<B> {
768
        #[inline]
769
7.15k
        const fn new(payload: B) -> Self {
770
7.15k
            Self {
771
7.15k
                payload,
772
7.15k
                pad: Padding::new(),
773
7.15k
                tag: Tag::OWNED,
774
7.15k
            }
775
7.15k
        }
Unexecuted instantiation: <kstring::string::inner::OwnedVariant<alloc::boxed::Box<str>>>::new
<kstring::string::inner::OwnedVariant<alloc::boxed::Box<str>>>::new
Line
Count
Source
769
1.17k
        const fn new(payload: B) -> Self {
770
1.17k
            Self {
771
1.17k
                payload,
772
1.17k
                pad: Padding::new(),
773
1.17k
                tag: Tag::OWNED,
774
1.17k
            }
775
1.17k
        }
<kstring::string::inner::OwnedVariant<alloc::boxed::Box<str>>>::new
Line
Count
Source
769
5.97k
        const fn new(payload: B) -> Self {
770
5.97k
            Self {
771
5.97k
                payload,
772
5.97k
                pad: Padding::new(),
773
5.97k
                tag: Tag::OWNED,
774
5.97k
            }
775
5.97k
        }
776
    }
777
778
    impl<B: crate::backend::HeapStr> std::fmt::Debug for OwnedVariant<B> {
779
        #[inline]
780
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
781
            self.payload.fmt(f)
782
        }
783
    }
784
785
    #[derive(Copy, Clone)]
786
    #[repr(C)]
787
    struct InlineVariant {
788
        payload: StackString<CAPACITY>,
789
        pad: Padding<INLINE_PAD_SIZE>,
790
        tag: Tag,
791
    }
792
    static_assertions::assert_eq_size!(Target, InlineVariant);
793
794
    impl InlineVariant {
795
        #[inline]
796
4.46M
        const fn new(payload: StackString<CAPACITY>) -> Self {
797
4.46M
            Self {
798
4.46M
                payload,
799
4.46M
                pad: Padding::new(),
800
4.46M
                tag: Tag::INLINE,
801
4.46M
            }
802
4.46M
        }
803
    }
804
805
    impl std::fmt::Debug for InlineVariant {
806
        #[inline]
807
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
808
            self.payload.fmt(f)
809
        }
810
    }
811
812
    #[derive(Copy, Clone, PartialEq, Eq)]
813
    #[repr(transparent)]
814
    struct Tag(u8);
815
816
    impl Tag {
817
        const SINGLETON: Tag = Tag(0);
818
        const OWNED: Tag = Tag(u8::MAX);
819
        const INLINE: Tag = Tag(1);
820
821
        #[inline]
822
6.52M
        const fn is_singleton(self) -> bool {
823
6.52M
            self.0 == Self::SINGLETON.0
824
6.52M
        }
825
826
        #[inline]
827
11.0M
        const fn is_owned(self) -> bool {
828
11.0M
            self.0 == Self::OWNED.0
829
11.0M
        }
830
831
        #[inline]
832
3.25M
        const fn is_inline(self) -> bool {
833
3.25M
            !self.is_singleton() && !self.is_owned()
834
3.25M
        }
835
    }
836
837
    #[derive(Copy, Clone)]
838
    #[repr(transparent)]
839
    struct Padding<const L: usize>([std::mem::MaybeUninit<u8>; L]);
840
841
    impl<const L: usize> Padding<L> {
842
4.46M
        const fn new() -> Self {
843
4.46M
            let padding = unsafe {
844
                // SAFETY: Padding, never actually used
845
4.46M
                std::mem::MaybeUninit::uninit().assume_init()
846
            };
847
4.46M
            Self(padding)
848
4.46M
        }
Unexecuted instantiation: <kstring::string::inner::Padding<7>>::new
<kstring::string::inner::Padding<7>>::new
Line
Count
Source
842
34.1k
        const fn new() -> Self {
843
34.1k
            let padding = unsafe {
844
                // SAFETY: Padding, never actually used
845
34.1k
                std::mem::MaybeUninit::uninit().assume_init()
846
            };
847
34.1k
            Self(padding)
848
34.1k
        }
<kstring::string::inner::Padding<7>>::new
Line
Count
Source
842
4.43M
        const fn new() -> Self {
843
4.43M
            let padding = unsafe {
844
                // SAFETY: Padding, never actually used
845
4.43M
                std::mem::MaybeUninit::uninit().assume_init()
846
            };
847
4.43M
            Self(padding)
848
4.43M
        }
849
    }
850
851
    impl<const L: usize> Default for Padding<L> {
852
        fn default() -> Self {
853
            Self::new()
854
        }
855
    }
856
}
857
858
#[cfg(test)]
859
mod test {
860
    use super::*;
861
862
    #[test]
863
    fn test_size() {
864
        println!("KString: {}", std::mem::size_of::<KString>());
865
    }
866
}