Coverage Report

Created: 2026-03-31 07:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/kstring-2.0.2/src/stack.rs
Line
Count
Source
1
use std::fmt;
2
3
pub(crate) type Len = u8;
4
5
/// Fixed-size stack-allocated string
6
#[derive(Copy, Clone)]
7
pub struct StackString<const CAPACITY: usize> {
8
    len: Len,
9
    buffer: StrBuffer<CAPACITY>,
10
}
11
12
impl<const CAPACITY: usize> StackString<CAPACITY> {
13
    pub const CAPACITY: usize = CAPACITY;
14
    pub const EMPTY: Self = Self::empty();
15
16
    const fn empty() -> Self {
17
        Self {
18
            len: 0,
19
            buffer: StrBuffer::empty(),
20
        }
21
    }
22
23
    /// Create a `StackString` from a `&str`, if it'll fit within `Self::CAPACITY`
24
    ///
25
    /// # Examples
26
    ///
27
    /// Basic usage:
28
    ///
29
    /// ```
30
    /// let s = kstring::StackString::<3>::try_new("foo");
31
    /// assert_eq!(s.as_deref(), Some("foo"));
32
    /// let s = kstring::StackString::<3>::try_new("foobar");
33
    /// assert_eq!(s, None);
34
    /// ```
35
    #[inline]
36
    #[must_use]
37
    pub fn try_new(s: &str) -> Option<Self> {
38
        let len = s.as_bytes().len();
39
        if len <= Self::CAPACITY {
40
            #[cfg(feature = "unsafe")]
41
            let stack = {
42
                unsafe {
43
                    // SAFETY: We've confirmed `len` is within size
44
                    Self::new_unchecked(s)
45
                }
46
            };
47
            #[cfg(not(feature = "unsafe"))]
48
            let stack = { Self::new(s) };
49
            Some(stack)
50
        } else {
51
            None
52
        }
53
    }
54
55
    /// Create a `StackString` from a `&str`
56
    ///
57
    /// # Panic
58
    ///
59
    /// Calling this function with a string larger than `Self::CAPACITY` will panic
60
    ///
61
    /// # Examples
62
    ///
63
    /// Basic usage:
64
    ///
65
    /// ```
66
    /// let s = kstring::StackString::<3>::new("foo");
67
    /// assert_eq!(s, "foo");
68
    /// ```
69
    #[inline]
70
    #[must_use]
71
    pub fn new(s: &str) -> Self {
72
        let len = s.as_bytes().len() as u8;
73
        debug_assert!(Self::CAPACITY <= Len::MAX.into());
74
        let buffer = StrBuffer::new(s);
75
        Self { len, buffer }
76
    }
77
78
    /// Create a `StackString` from a `&str`
79
    ///
80
    /// # Safety
81
    ///
82
    /// Calling this function with a string larger than `Self::CAPACITY` is undefined behavior.
83
    ///
84
    /// # Examples
85
    ///
86
    /// Basic usage:
87
    ///
88
    /// ```
89
    /// let s = unsafe {
90
    ///     // SAFETY: Literal is short-enough
91
    ///     kstring::StackString::<3>::new_unchecked("foo")
92
    /// };
93
    /// assert_eq!(s, "foo");
94
    /// ```
95
    #[inline]
96
    #[must_use]
97
    #[cfg(feature = "unsafe")]
98
12.4M
    pub unsafe fn new_unchecked(s: &str) -> Self {
99
12.4M
        let len = s.as_bytes().len() as u8;
100
12.4M
        debug_assert!(Self::CAPACITY <= Len::MAX.into());
101
12.4M
        let buffer = StrBuffer::new_unchecked(s);
102
12.4M
        Self { len, buffer }
103
12.4M
    }
Unexecuted instantiation: <kstring::stack::StackString<15>>::new_unchecked
<kstring::stack::StackString<15>>::new_unchecked
Line
Count
Source
98
5.81M
    pub unsafe fn new_unchecked(s: &str) -> Self {
99
5.81M
        let len = s.as_bytes().len() as u8;
100
5.81M
        debug_assert!(Self::CAPACITY <= Len::MAX.into());
101
5.81M
        let buffer = StrBuffer::new_unchecked(s);
102
5.81M
        Self { len, buffer }
103
5.81M
    }
<kstring::stack::StackString<15>>::new_unchecked
Line
Count
Source
98
6.58M
    pub unsafe fn new_unchecked(s: &str) -> Self {
99
6.58M
        let len = s.as_bytes().len() as u8;
100
6.58M
        debug_assert!(Self::CAPACITY <= Len::MAX.into());
101
6.58M
        let buffer = StrBuffer::new_unchecked(s);
102
6.58M
        Self { len, buffer }
103
6.58M
    }
104
105
    /// Extracts a string slice containing the entire `StackString`.
106
    ///
107
    /// # Examples
108
    ///
109
    /// Basic usage:
110
    ///
111
    /// ```
112
    /// let s = kstring::StackString::<3>::try_new("foo").unwrap();
113
    ///
114
    /// assert_eq!("foo", s.as_str());
115
    /// ```
116
    #[inline]
117
    #[must_use]
118
15.5M
    pub fn as_str(&self) -> &str {
119
15.5M
        let len = self.len as usize;
120
        #[cfg(feature = "unsafe")]
121
        unsafe {
122
            // SAFETY: Constructors guarantee that `buffer[..len]` is a `str`,
123
            // and we don't mutate the data afterwards.
124
15.5M
            self.buffer.as_str_unchecked(len)
125
        }
126
        #[cfg(not(feature = "unsafe"))]
127
        self.buffer.as_str(len)
128
15.5M
    }
Unexecuted instantiation: <kstring::stack::StackString<15>>::as_str
Unexecuted instantiation: <kstring::stack::StackString<15>>::as_str
Unexecuted instantiation: <kstring::stack::StackString<15>>::as_str
<kstring::stack::StackString<15>>::as_str
Line
Count
Source
118
11.4M
    pub fn as_str(&self) -> &str {
119
11.4M
        let len = self.len as usize;
120
        #[cfg(feature = "unsafe")]
121
        unsafe {
122
            // SAFETY: Constructors guarantee that `buffer[..len]` is a `str`,
123
            // and we don't mutate the data afterwards.
124
11.4M
            self.buffer.as_str_unchecked(len)
125
        }
126
        #[cfg(not(feature = "unsafe"))]
127
        self.buffer.as_str(len)
128
11.4M
    }
Unexecuted instantiation: <kstring::stack::StackString<15>>::as_str
<kstring::stack::StackString<15>>::as_str
Line
Count
Source
118
4.10M
    pub fn as_str(&self) -> &str {
119
4.10M
        let len = self.len as usize;
120
        #[cfg(feature = "unsafe")]
121
        unsafe {
122
            // SAFETY: Constructors guarantee that `buffer[..len]` is a `str`,
123
            // and we don't mutate the data afterwards.
124
4.10M
            self.buffer.as_str_unchecked(len)
125
        }
126
        #[cfg(not(feature = "unsafe"))]
127
        self.buffer.as_str(len)
128
4.10M
    }
129
130
    /// Converts a `StackString` into a mutable string slice.
131
    ///
132
    /// # Examples
133
    ///
134
    /// Basic usage:
135
    ///
136
    /// ```
137
    /// let mut s = kstring::StackString::<6>::try_new("foobar").unwrap();
138
    /// let s_mut_str = s.as_mut_str();
139
    ///
140
    /// s_mut_str.make_ascii_uppercase();
141
    ///
142
    /// assert_eq!("FOOBAR", s_mut_str);
143
    /// ```
144
    #[inline]
145
    #[must_use]
146
    pub fn as_mut_str(&mut self) -> &mut str {
147
        let len = self.len as usize;
148
        #[cfg(feature = "unsafe")]
149
        unsafe {
150
            // SAFETY: Constructors guarantee that `buffer[..len]` is a `str`,
151
            // and we don't mutate the data afterwards.
152
            self.buffer.as_mut_str_unchecked(len)
153
        }
154
        #[cfg(not(feature = "unsafe"))]
155
        self.buffer.as_mut_str(len)
156
    }
157
158
    /// Returns the length of this `StasckString`, in bytes, not [`char`]s or
159
    /// graphemes. In other words, it might not be what a human considers the
160
    /// length of the string.
161
    ///
162
    /// # Examples
163
    ///
164
    /// Basic usage:
165
    ///
166
    /// ```
167
    /// let a = kstring::StackString::<3>::try_new("foo").unwrap();
168
    /// assert_eq!(a.len(), 3);
169
    ///
170
    /// let fancy_f = kstring::StackString::<4>::try_new("ƒoo").unwrap();
171
    /// assert_eq!(fancy_f.len(), 4);
172
    /// assert_eq!(fancy_f.chars().count(), 3);
173
    /// ```
174
    #[inline]
175
    #[must_use]
176
    pub fn len(&self) -> usize {
177
        self.len as usize
178
    }
179
180
    /// Returns `true` if this `StackString` has a length of zero, and `false` otherwise.
181
    ///
182
    /// # Examples
183
    ///
184
    /// Basic usage:
185
    ///
186
    /// ```
187
    /// let mut v = kstring::StackString::<20>::EMPTY;
188
    /// assert!(v.is_empty());
189
    ///
190
    /// let a = kstring::StackString::<3>::try_new("foo").unwrap();
191
    /// assert!(!a.is_empty());
192
    /// ```
193
    #[inline]
194
    #[must_use]
195
    pub fn is_empty(&self) -> bool {
196
        self.len() == 0
197
    }
198
199
    /// Truncates this `StackString`, removing all contents.
200
    ///
201
    /// # Examples
202
    ///
203
    /// Basic usage:
204
    ///
205
    /// ```
206
    /// let mut s = kstring::StackString::<3>::try_new("foo").unwrap();
207
    ///
208
    /// s.clear();
209
    ///
210
    /// assert!(s.is_empty());
211
    /// assert_eq!(0, s.len());
212
    /// ```
213
    #[inline]
214
    pub fn clear(&mut self) {
215
        self.len = 0;
216
    }
217
218
    /// Shortens this `StackString` to the specified length.
219
    ///
220
    /// If `new_len` is greater than the string's current length, this has no
221
    /// effect.
222
    ///
223
    /// Note that this method has no effect on the allocated capacity
224
    /// of the string
225
    ///
226
    /// # Panics
227
    ///
228
    /// Panics if `new_len` does not lie on a [`char`] boundary.
229
    ///
230
    /// # Examples
231
    ///
232
    /// Basic usage:
233
    ///
234
    /// ```
235
    /// let mut s = kstring::StackString::<5>::try_new("hello").unwrap();
236
    ///
237
    /// s.truncate(2);
238
    ///
239
    /// assert_eq!(s, "he");
240
    /// ```
241
    #[inline]
242
    pub fn truncate(&mut self, new_len: usize) {
243
        if new_len <= self.len() {
244
            assert!(self.is_char_boundary(new_len));
245
            self.len = new_len as u8;
246
        }
247
    }
248
}
249
250
impl<const CAPACITY: usize> Default for StackString<CAPACITY> {
251
    fn default() -> Self {
252
        Self::empty()
253
    }
254
}
255
256
impl<const CAPACITY: usize> std::ops::Deref for StackString<CAPACITY> {
257
    type Target = str;
258
259
    #[inline]
260
    fn deref(&self) -> &str {
261
        self.as_str()
262
    }
263
}
264
265
impl<const CAPACITY: usize> Eq for StackString<CAPACITY> {}
266
267
impl<const C1: usize, const C2: usize> PartialEq<StackString<C1>> for StackString<C2> {
268
    #[inline]
269
    fn eq(&self, other: &StackString<C1>) -> bool {
270
        PartialEq::eq(self.as_str(), other.as_str())
271
    }
272
}
273
274
impl<const CAPACITY: usize> PartialEq<str> for StackString<CAPACITY> {
275
    #[inline]
276
    fn eq(&self, other: &str) -> bool {
277
        PartialEq::eq(self.as_str(), other)
278
    }
279
}
280
281
impl<'s, const CAPACITY: usize> PartialEq<&'s str> for StackString<CAPACITY> {
282
    #[inline]
283
    fn eq(&self, other: &&str) -> bool {
284
        PartialEq::eq(self.as_str(), *other)
285
    }
286
}
287
288
impl<const CAPACITY: usize> PartialEq<String> for StackString<CAPACITY> {
289
    #[inline]
290
    fn eq(&self, other: &String) -> bool {
291
        PartialEq::eq(self.as_str(), other.as_str())
292
    }
293
}
294
295
impl<const CAPACITY: usize> Ord for StackString<CAPACITY> {
296
    #[inline]
297
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
298
        self.as_str().cmp(other.as_str())
299
    }
300
}
301
302
impl<const C1: usize, const C2: usize> PartialOrd<StackString<C1>> for StackString<C2> {
303
    #[inline]
304
    fn partial_cmp(&self, other: &StackString<C1>) -> Option<std::cmp::Ordering> {
305
        self.as_str().partial_cmp(other.as_str())
306
    }
307
}
308
309
impl<const CAPACITY: usize> PartialOrd<str> for StackString<CAPACITY> {
310
    #[inline]
311
    fn partial_cmp(&self, other: &str) -> Option<std::cmp::Ordering> {
312
        self.as_str().partial_cmp(other)
313
    }
314
}
315
316
impl<'s, const CAPACITY: usize> PartialOrd<&'s str> for StackString<CAPACITY> {
317
    #[inline]
318
    fn partial_cmp(&self, other: &&str) -> Option<std::cmp::Ordering> {
319
        self.as_str().partial_cmp(other)
320
    }
321
}
322
323
impl<const CAPACITY: usize> PartialOrd<String> for StackString<CAPACITY> {
324
    #[inline]
325
    fn partial_cmp(&self, other: &String) -> Option<std::cmp::Ordering> {
326
        self.as_str().partial_cmp(other.as_str())
327
    }
328
}
329
330
impl<const CAPACITY: usize> std::hash::Hash for StackString<CAPACITY> {
331
    #[inline]
332
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
333
        self.as_str().hash(state);
334
    }
335
}
336
337
impl<const CAPACITY: usize> fmt::Debug for StackString<CAPACITY> {
338
    #[inline]
339
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340
        fmt::Debug::fmt(self.as_str(), f)
341
    }
342
}
343
344
impl<const CAPACITY: usize> fmt::Display for StackString<CAPACITY> {
345
    #[inline]
346
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347
        fmt::Display::fmt(self.as_str(), f)
348
    }
349
}
350
351
impl<const CAPACITY: usize> AsRef<str> for StackString<CAPACITY> {
352
    #[inline]
353
    fn as_ref(&self) -> &str {
354
        self.as_str()
355
    }
356
}
357
358
impl<const CAPACITY: usize> AsRef<[u8]> for StackString<CAPACITY> {
359
    #[inline]
360
    fn as_ref(&self) -> &[u8] {
361
        self.as_bytes()
362
    }
363
}
364
365
impl<const CAPACITY: usize> AsRef<std::ffi::OsStr> for StackString<CAPACITY> {
366
    #[inline]
367
    fn as_ref(&self) -> &std::ffi::OsStr {
368
        (**self).as_ref()
369
    }
370
}
371
372
impl<const CAPACITY: usize> AsRef<std::path::Path> for StackString<CAPACITY> {
373
    #[inline]
374
    fn as_ref(&self) -> &std::path::Path {
375
        std::path::Path::new(self)
376
    }
377
}
378
379
impl<const CAPACITY: usize> std::borrow::Borrow<str> for StackString<CAPACITY> {
380
    #[inline]
381
    fn borrow(&self) -> &str {
382
        self.as_str()
383
    }
384
}
385
386
#[derive(Copy, Clone)]
387
#[repr(transparent)]
388
pub(crate) struct StrBuffer<const CAPACITY: usize>([u8; CAPACITY]);
389
390
impl<const CAPACITY: usize> StrBuffer<CAPACITY> {
391
12.4M
    pub(crate) const fn empty() -> Self {
392
12.4M
        let array = [0; CAPACITY];
393
12.4M
        StrBuffer(array)
394
12.4M
    }
Unexecuted instantiation: <kstring::stack::StrBuffer<15>>::empty
<kstring::stack::StrBuffer<15>>::empty
Line
Count
Source
391
5.81M
    pub(crate) const fn empty() -> Self {
392
5.81M
        let array = [0; CAPACITY];
393
5.81M
        StrBuffer(array)
394
5.81M
    }
<kstring::stack::StrBuffer<15>>::empty
Line
Count
Source
391
6.58M
    pub(crate) const fn empty() -> Self {
392
6.58M
        let array = [0; CAPACITY];
393
6.58M
        StrBuffer(array)
394
6.58M
    }
395
396
    #[inline]
397
    pub(crate) fn new(s: &str) -> Self {
398
        let len = s.as_bytes().len();
399
        debug_assert!(len <= CAPACITY);
400
        let mut buffer = Self::default();
401
        if let Some(buffer) = buffer.0.get_mut(..len) {
402
            buffer.copy_from_slice(s.as_bytes());
403
        } else {
404
            panic!("`{}` is larger than capacity {}", s, CAPACITY);
405
        }
406
        buffer
407
    }
408
409
    #[inline]
410
    #[cfg(not(feature = "unsafe"))]
411
    pub(crate) fn as_str(&self, len: usize) -> &str {
412
        let slice = self.0.get(..len).unwrap();
413
        std::str::from_utf8(slice).unwrap()
414
    }
415
416
    #[inline]
417
    #[cfg(not(feature = "unsafe"))]
418
    pub(crate) fn as_mut_str(&mut self, len: usize) -> &mut str {
419
        let slice = self.0.get_mut(..len).unwrap();
420
        std::str::from_utf8_mut(slice).unwrap()
421
    }
422
}
423
424
impl<const CAPACITY: usize> StrBuffer<CAPACITY> {
425
    #[inline]
426
    #[cfg(feature = "unsafe")]
427
12.4M
    pub(crate) unsafe fn new_unchecked(s: &str) -> Self {
428
12.4M
        let len = s.as_bytes().len();
429
12.4M
        debug_assert!(len <= CAPACITY);
430
12.4M
        let mut buffer = Self::default();
431
12.4M
        buffer
432
12.4M
            .0
433
12.4M
            .get_unchecked_mut(..len)
434
12.4M
            .copy_from_slice(s.as_bytes());
435
12.4M
        buffer
436
12.4M
    }
Unexecuted instantiation: <kstring::stack::StrBuffer<15>>::new_unchecked
<kstring::stack::StrBuffer<15>>::new_unchecked
Line
Count
Source
427
5.81M
    pub(crate) unsafe fn new_unchecked(s: &str) -> Self {
428
5.81M
        let len = s.as_bytes().len();
429
5.81M
        debug_assert!(len <= CAPACITY);
430
5.81M
        let mut buffer = Self::default();
431
5.81M
        buffer
432
5.81M
            .0
433
5.81M
            .get_unchecked_mut(..len)
434
5.81M
            .copy_from_slice(s.as_bytes());
435
5.81M
        buffer
436
5.81M
    }
<kstring::stack::StrBuffer<15>>::new_unchecked
Line
Count
Source
427
6.58M
    pub(crate) unsafe fn new_unchecked(s: &str) -> Self {
428
6.58M
        let len = s.as_bytes().len();
429
6.58M
        debug_assert!(len <= CAPACITY);
430
6.58M
        let mut buffer = Self::default();
431
6.58M
        buffer
432
6.58M
            .0
433
6.58M
            .get_unchecked_mut(..len)
434
6.58M
            .copy_from_slice(s.as_bytes());
435
6.58M
        buffer
436
6.58M
    }
437
438
    #[inline]
439
    #[cfg(feature = "unsafe")]
440
15.5M
    pub(crate) unsafe fn as_str_unchecked(&self, len: usize) -> &str {
441
15.5M
        let slice = self.0.get_unchecked(..len);
442
15.5M
        std::str::from_utf8_unchecked(slice)
443
15.5M
    }
Unexecuted instantiation: <kstring::stack::StrBuffer<15>>::as_str_unchecked
Unexecuted instantiation: <kstring::stack::StrBuffer<15>>::as_str_unchecked
Unexecuted instantiation: <kstring::stack::StrBuffer<15>>::as_str_unchecked
<kstring::stack::StrBuffer<15>>::as_str_unchecked
Line
Count
Source
440
11.4M
    pub(crate) unsafe fn as_str_unchecked(&self, len: usize) -> &str {
441
11.4M
        let slice = self.0.get_unchecked(..len);
442
11.4M
        std::str::from_utf8_unchecked(slice)
443
11.4M
    }
Unexecuted instantiation: <kstring::stack::StrBuffer<15>>::as_str_unchecked
<kstring::stack::StrBuffer<15>>::as_str_unchecked
Line
Count
Source
440
4.10M
    pub(crate) unsafe fn as_str_unchecked(&self, len: usize) -> &str {
441
4.10M
        let slice = self.0.get_unchecked(..len);
442
4.10M
        std::str::from_utf8_unchecked(slice)
443
4.10M
    }
444
445
    #[inline]
446
    #[cfg(feature = "unsafe")]
447
    pub(crate) unsafe fn as_mut_str_unchecked(&mut self, len: usize) -> &mut str {
448
        let slice = self.0.get_unchecked_mut(..len);
449
        std::str::from_utf8_unchecked_mut(slice)
450
    }
451
}
452
453
impl<const CAPACITY: usize> Default for StrBuffer<CAPACITY> {
454
12.4M
    fn default() -> Self {
455
12.4M
        Self::empty()
456
12.4M
    }
Unexecuted instantiation: <kstring::stack::StrBuffer<15> as core::default::Default>::default
<kstring::stack::StrBuffer<15> as core::default::Default>::default
Line
Count
Source
454
5.81M
    fn default() -> Self {
455
5.81M
        Self::empty()
456
5.81M
    }
<kstring::stack::StrBuffer<15> as core::default::Default>::default
Line
Count
Source
454
6.58M
    fn default() -> Self {
455
6.58M
        Self::empty()
456
6.58M
    }
457
}