Skip to main content

lean_string/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![no_std]
4
5extern crate alloc;
6
7#[cfg(feature = "std")]
8extern crate std;
9
10use core::{
11    borrow::Borrow,
12    cmp, fmt,
13    hash::{Hash, Hasher},
14    ops::{Add, AddAssign, Deref},
15    str,
16    str::FromStr,
17};
18
19use alloc::{borrow::Cow, boxed::Box, string::String};
20
21#[cfg(feature = "std")]
22use std::ffi::OsStr;
23
24mod repr;
25use repr::Repr;
26
27mod errors;
28pub use errors::*;
29
30mod traits;
31pub use traits::ToLeanString;
32
33mod features;
34
35/// Compact, clone-on-write, UTF-8 encoded, growable string type.
36#[repr(transparent)]
37pub struct LeanString(Repr);
38
39const _: () = {
40    assert!(size_of::<LeanString>() == size_of::<[usize; 2]>());
41    assert!(size_of::<Option<LeanString>>() == size_of::<[usize; 2]>());
42    assert!(align_of::<LeanString>() == align_of::<usize>());
43    assert!(align_of::<Option<LeanString>>() == align_of::<usize>());
44};
45
46impl LeanString {
47    /// Creates a new empty [`LeanString`].
48    ///
49    /// Same as [`String::new()`], this will not allocate on the heap.
50    ///
51    /// # Examples
52    ///
53    /// ```
54    /// # use lean_string::LeanString;
55    /// let s = LeanString::new();
56    /// assert!(s.is_empty());
57    /// assert!(!s.is_heap_allocated());
58    /// ```
59    #[inline]
60    pub const fn new() -> Self {
61        LeanString(Repr::new())
62    }
63
64    /// Creates a new [`LeanString`] from a `&'static str`.
65    ///
66    /// # Examples
67    ///
68    /// ```
69    /// # use lean_string::LeanString;
70    /// let s = LeanString::from_static_str("Long text but static lifetime");
71    /// assert_eq!(s.as_str(), "Long text but static lifetime");
72    /// assert_eq!(s.len(), 29);
73    /// assert!(!s.is_heap_allocated());
74    /// ```
75    #[inline]
76    pub const fn from_static_str(text: &'static str) -> Self {
77        match Repr::from_static_str(text) {
78            Ok(repr) => LeanString(repr),
79            Err(_) => panic!("text is too long"),
80        }
81    }
82
83    /// Creates a new empty [`LeanString`] with at least capacity bytes.
84    ///
85    /// A [`LeanString`] will inline strings if the length is less than or equal to
86    /// `2 * size_of::<usize>()` bytes. This means that the minimum capacity of a [`LeanString`]
87    /// is `2 * size_of::<usize>()` bytes.
88    ///
89    /// # Panics
90    ///
91    /// Panics if **any** of the following conditions is met:
92    ///
93    /// - The system is out-of-memory.
94    /// - On 64-bit architecture, the `capacity` is greater than `2^56 - 1`.
95    /// - On 32-bit architecture, the `capacity` is greater than `2^32 - 1`.
96    ///
97    /// If you want to handle such a problem manually, use [`LeanString::try_with_capacity()`].
98    ///
99    /// # Examples
100    ///
101    /// ## inline capacity
102    ///
103    /// ```
104    /// # use lean_string::LeanString;
105    /// let s = LeanString::with_capacity(4);
106    /// assert_eq!(s.capacity(), 2 * size_of::<usize>());
107    /// assert!(!s.is_heap_allocated());
108    /// ```
109    ///
110    /// ## heap capacity
111    ///
112    /// ```
113    /// # use lean_string::LeanString;
114    /// let s = LeanString::with_capacity(100);
115    /// assert_eq!(s.capacity(), 100);
116    /// assert!(s.is_heap_allocated());
117    /// ```
118    #[inline]
119    pub fn with_capacity(capacity: usize) -> Self {
120        LeanString::try_with_capacity(capacity).unwrap_with_msg()
121    }
122
123    /// Fallible version of [`LeanString::with_capacity()`].
124    ///
125    /// This method won't panic if the system is out of memory, or if the `capacity` is too large, but
126    /// returns a [`ReserveError`]. Otherwise it behaves the same as [`LeanString::with_capacity()`].
127    #[inline]
128    pub fn try_with_capacity(capacity: usize) -> Result<Self, ReserveError> {
129        Repr::with_capacity(capacity).map(LeanString)
130    }
131
132    /// Converts a slice of bytes to a [`LeanString`].
133    ///
134    /// If the slice is not valid UTF-8, an error is returned.
135    ///
136    /// # Examples
137    ///
138    /// ## valid UTF-8
139    ///
140    /// ```
141    /// # use lean_string::LeanString;
142    /// let bytes = vec![240, 159, 166, 128];
143    /// let string = LeanString::from_utf8(&bytes).expect("valid UTF-8");
144    ///
145    /// assert_eq!(string, "🦀");
146    /// ```
147    ///
148    /// ## invalid UTF-8
149    ///
150    /// ```
151    /// # use lean_string::LeanString;
152    /// let bytes = &[255, 255, 255];
153    /// let result = LeanString::from_utf8(bytes);
154    ///
155    /// assert!(result.is_err());
156    /// ```
157    #[inline]
158    pub fn from_utf8(buf: &[u8]) -> Result<Self, str::Utf8Error> {
159        let str = str::from_utf8(buf)?;
160        Ok(LeanString::from(str))
161    }
162
163    /// Converts a slice of bytes to a [`LeanString`], including invalid characters.
164    ///
165    /// During this conversion, all invalid characters are replaced with the
166    /// [`char::REPLACEMENT_CHARACTER`].
167    ///
168    /// # Examples
169    ///
170    /// ```
171    /// # use lean_string::LeanString;
172    /// let invalid_bytes = b"Hello \xF0\x90\x80World";
173    /// let string = LeanString::from_utf8_lossy(invalid_bytes);
174    ///
175    /// assert_eq!(string, "Hello �World");
176    /// ```
177    #[inline]
178    pub fn from_utf8_lossy(buf: &[u8]) -> Self {
179        let mut ret = LeanString::with_capacity(buf.len());
180        for chunk in buf.utf8_chunks() {
181            ret.push_str(chunk.valid());
182            if !chunk.invalid().is_empty() {
183                ret.push(char::REPLACEMENT_CHARACTER);
184            }
185        }
186        ret
187    }
188
189    /// Converts a slice of bytes to a [`LeanString`] without checking if the bytes are valid
190    /// UTF-8.
191    ///
192    /// # Safety
193    ///
194    /// This function is unsafe because it does not check that the bytes passed to it are valid
195    /// UTF-8. If this constraint is violated, it may cause memory unsafety issues.
196    #[inline]
197    pub unsafe fn from_utf8_unchecked(buf: &[u8]) -> Self {
198        let str = unsafe { str::from_utf8_unchecked(buf) };
199        LeanString::from(str)
200    }
201
202    /// Decodes a slice of UTF-16 encoded bytes to a [`LeanString`], returning an error if `buf`
203    /// contains any invalid code points.
204    ///
205    /// # Examples
206    ///
207    /// ## valid UTF-16
208    ///
209    /// ```
210    /// # use lean_string::LeanString;
211    /// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063];
212    /// assert_eq!(LeanString::from_utf16(v).unwrap(), "𝄞music");
213    /// ```
214    ///
215    /// ## invalid UTF-16
216    ///
217    /// ```
218    /// # use lean_string::LeanString;
219    /// // 𝄞mu<invalid>ic
220    /// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
221    /// assert!(LeanString::from_utf16(v).is_err());
222    /// ```
223    #[inline]
224    pub fn from_utf16(buf: &[u16]) -> Result<Self, FromUtf16Error> {
225        let mut ret = LeanString::with_capacity(buf.len());
226        for c in char::decode_utf16(buf.iter().copied()) {
227            match c {
228                Ok(c) => ret.push(c),
229                Err(_) => return Err(FromUtf16Error),
230            }
231        }
232        Ok(ret)
233    }
234
235    /// Decodes a slice of UTF-16 encoded bytes to a [`LeanString`], replacing invalid code points
236    /// with the [`char::REPLACEMENT_CHARACTER`].
237    ///
238    /// # Examples
239    ///
240    /// ```
241    /// # use lean_string::LeanString;
242    /// // 𝄞mus<invalid>ic<invalid>
243    /// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0xDD1E, 0x0069, 0x0063, 0xD834];
244    /// assert_eq!(LeanString::from_utf16_lossy(v), "𝄞mus\u{FFFD}ic\u{FFFD}");
245    /// ```
246    #[inline]
247    pub fn from_utf16_lossy(buf: &[u16]) -> Self {
248        char::decode_utf16(buf.iter().copied())
249            .map(|c| c.unwrap_or(char::REPLACEMENT_CHARACTER))
250            .collect()
251    }
252
253    /// Returns the length of the string in bytes, not [`char`] or graphemes.
254    ///
255    /// # Examples
256    ///
257    /// ```
258    /// # use lean_string::LeanString;
259    /// let a = LeanString::from("foo");
260    /// assert_eq!(a.len(), 3);
261    ///
262    /// let fancy_f = LeanString::from("ƒoo");
263    /// assert_eq!(fancy_f.len(), 4);
264    /// assert_eq!(fancy_f.chars().count(), 3);
265    /// ```
266    #[inline]
267    pub const fn len(&self) -> usize {
268        self.0.len()
269    }
270
271    /// Returns `true` if the [`LeanString`] has a length of 0, `false` otherwise
272    ///
273    /// # Examples
274    ///
275    /// ```
276    /// # use lean_string::LeanString;
277    /// let mut s = LeanString::new();
278    /// assert!(s.is_empty());
279    ///
280    /// s.push('a');
281    /// assert!(!s.is_empty());
282    /// ```
283    #[inline]
284    pub const fn is_empty(&self) -> bool {
285        self.0.is_empty()
286    }
287
288    /// Returns the capacity of the [`LeanString`], in bytes.
289    ///
290    /// A [`LeanString`] will inline strings if the length is less than or equal to
291    /// `2 * size_of::<usize>()` bytes. This means that the minimum capacity of a [`LeanString`]
292    /// is `2 * size_of::<usize>()` bytes.
293    ///
294    /// # Examples
295    ///
296    /// ## inline capacity
297    ///
298    /// ```
299    /// # use lean_string::LeanString;
300    /// let s = LeanString::new();
301    /// assert_eq!(s.capacity(), 2 * size_of::<usize>());
302    /// ```
303    ///
304    /// ## heap capacity
305    ///
306    /// ```
307    /// # use lean_string::LeanString;
308    /// let s = LeanString::with_capacity(100);
309    /// assert_eq!(s.capacity(), 100);
310    /// ```
311    #[inline]
312    pub fn capacity(&self) -> usize {
313        self.0.capacity()
314    }
315
316    /// Returns a string slice containing the entire [`LeanString`].
317    ///
318    /// # Examples
319    ///
320    /// ```
321    /// # use lean_string::LeanString;
322    /// let s = LeanString::from("foo");
323    /// assert_eq!(s.as_str(), "foo");
324    /// ```
325    #[inline]
326    pub const fn as_str(&self) -> &str {
327        self.0.as_str()
328    }
329
330    /// Returns a byte slice containing the entire [`LeanString`].
331    ///
332    /// # Examples
333    ///
334    /// ```
335    /// # use lean_string::LeanString;
336    /// let s = LeanString::from("hello");
337    /// assert_eq!(&[104, 101, 108, 108, 111], s.as_bytes());
338    /// ```
339    #[inline]
340    pub const fn as_bytes(&self) -> &[u8] {
341        self.0.as_bytes()
342    }
343
344    /// Reserves capacity for at least `additional` bytes more than the current length.
345    ///
346    /// # Note
347    ///
348    /// This method clones the [`LeanString`] if it is not unique.
349    ///
350    /// # Panics
351    ///
352    /// Panics if **any** of the following conditions is met:
353    ///
354    /// - The system is out-of-memory.
355    /// - On 64-bit architecture, the `capacity` is greater than `2^56 - 1`.
356    /// - On 32-bit architecture, the `capacity` is greater than `2^32 - 1`.
357    ///
358    /// If you want to handle such a problem manually, use [`LeanString::try_reserve()`].
359    ///
360    /// # Examples
361    ///
362    /// ```
363    /// # use lean_string::LeanString;
364    /// let mut s = LeanString::new();
365    ///
366    /// // We have an inline storage on the stack.
367    /// assert_eq!(s.capacity(), 2 * size_of::<usize>());
368    /// assert!(!s.is_heap_allocated());
369    ///
370    /// s.reserve(100);
371    ///
372    /// // Now we have a heap storage.
373    /// assert!(s.capacity() >= s.len() + 100);
374    /// assert!(s.is_heap_allocated());
375    /// ```
376    #[inline]
377    pub fn reserve(&mut self, additional: usize) {
378        self.try_reserve(additional).unwrap_with_msg()
379    }
380
381    /// Fallible version of [`LeanString::reserve()`].
382    ///
383    /// This method won't panic if the system is out-of-memory, or the `capacity` is too large, but
384    /// return an [`ReserveError`]. Otherwise it behaves the same as [`LeanString::reserve()`].
385    #[inline]
386    pub fn try_reserve(&mut self, additional: usize) -> Result<(), ReserveError> {
387        self.0.reserve(additional)
388    }
389
390    /// Shrinks the capacity of the [`LeanString`] to match its length.
391    ///
392    /// The resulting capacity is always greater than `2 * size_of::<usize>()` bytes because
393    /// [`LeanString`] has inline (on the stack) storage.
394    ///
395    /// # Note
396    ///
397    /// This method clones the [`LeanString`] if it is not unique and its capacity is greater than
398    /// its length.
399    ///
400    /// # Panics
401    ///
402    /// Panics if cloning the [`LeanString`] fails due to the system being out-of-memory. If you
403    /// want to handle such a problem manually, use [`LeanString::try_shrink_to_fit()`].
404    ///
405    /// # Examples
406    ///
407    /// ## short string
408    ///
409    /// ```
410    /// # use lean_string::LeanString;
411    /// let mut s = LeanString::from("foo");
412    ///
413    /// s.reserve(100);
414    /// assert_eq!(s.capacity(), 3 + 100);
415    ///
416    /// s.shrink_to_fit();
417    /// assert_eq!(s.capacity(), 2 * size_of::<usize>());
418    /// ```
419    ///
420    /// ## long string
421    ///
422    /// ```
423    /// # use lean_string::LeanString;
424    /// let mut s = LeanString::from("This is a text the length is more than 16 bytes");
425    ///
426    /// s.reserve(100);
427    /// assert!(s.capacity() > 16 + 100);
428    ///
429    /// s.shrink_to_fit();
430    /// assert_eq!(s.capacity(), s.len());
431    /// ```
432    #[inline]
433    pub fn shrink_to_fit(&mut self) {
434        self.try_shrink_to_fit().unwrap_with_msg()
435    }
436
437    /// Fallible version of [`LeanString::shrink_to_fit()`].
438    ///
439    /// This method won't panic if the system is out-of-memory, or the `capacity` is too large, but
440    /// return an [`ReserveError`]. Otherwise it behaves the same as [`LeanString::shrink_to_fit()`].
441    #[inline]
442    pub fn try_shrink_to_fit(&mut self) -> Result<(), ReserveError> {
443        self.0.shrink_to(0)
444    }
445
446    /// Shrinks the capacity of the [`LeanString`] with a lower bound.
447    ///
448    /// The resulting capacity is always greater than `2 * size_of::<usize>()` bytes because the
449    /// [`LeanString`] has inline (on the stack) storage.
450    ///
451    /// # Note
452    ///
453    /// This method clones the [`LeanString`] if it is not unique and its capacity will be changed.
454    ///
455    /// # Panics
456    ///
457    /// Panics if cloning the [`LeanString`] fails due to the system being out-of-memory. If you
458    /// want to handle such a problem manually, use [`LeanString::try_shrink_to()`].
459    ///
460    /// # Examples
461    ///
462    /// ```
463    /// # use lean_string::LeanString;
464    /// let mut s = LeanString::with_capacity(100);
465    /// assert_eq!(s.capacity(), 100);
466    ///
467    /// // if the capacity was already bigger than the argument and unique, the call is no-op.
468    /// s.shrink_to(100);
469    /// assert_eq!(s.capacity(), 100);
470    ///
471    /// s.shrink_to(50);
472    /// assert_eq!(s.capacity(), 50);
473    ///
474    /// // if the string can be inlined, it is
475    /// s.shrink_to(5);
476    /// assert_eq!(s.capacity(), 2 * size_of::<usize>());
477    /// ```
478    #[inline]
479    pub fn shrink_to(&mut self, min_capacity: usize) {
480        self.try_shrink_to(min_capacity).unwrap_with_msg()
481    }
482
483    /// Fallible version of [`LeanString::shrink_to()`].
484    ///
485    /// This method won't panic if the system is out-of-memory, or the `capacity` is too large, but
486    /// return an [`ReserveError`]. Otherwise it behaves the same as [`LeanString::shrink_to()`].
487    #[inline]
488    pub fn try_shrink_to(&mut self, min_capacity: usize) -> Result<(), ReserveError> {
489        self.0.shrink_to(min_capacity)
490    }
491
492    /// Appends the given [`char`] to the end of the [`LeanString`].
493    ///
494    /// # Panics
495    ///
496    /// Panics if the system is out-of-memory. If you want to handle such a problem manually, use
497    /// [`LeanString::try_push()`].
498    ///
499    /// # Examples
500    ///
501    /// ```
502    /// # use lean_string::LeanString;
503    /// let mut s = LeanString::new();
504    /// s.push('f');
505    /// s.push('o');
506    /// s.push('o');
507    /// assert_eq!("foo", s);
508    /// ```
509    #[inline]
510    pub fn push(&mut self, ch: char) {
511        self.try_push(ch).unwrap_with_msg()
512    }
513
514    /// Fallible version of [`LeanString::push()`].
515    ///
516    /// This method won't panic if the system is out-of-memory, or the `capacity` is too large, but
517    /// return an [`ReserveError`]. Otherwise it behaves the same as [`LeanString::push()`].
518    #[inline]
519    pub fn try_push(&mut self, ch: char) -> Result<(), ReserveError> {
520        self.0.push_str(ch.encode_utf8(&mut [0; 4]))
521    }
522
523    /// Removes the last character from the [`LeanString`] and returns it.
524    /// If the [`LeanString`] is empty, `None` is returned.
525    ///
526    /// # Panics
527    ///
528    /// This method does not clone and panics the [`LeanString`] **without all** of following conditions are
529    /// true:
530    ///
531    /// - 32-bit architecture
532    /// - The [`LeanString`] is not unique.
533    /// - The length of the [`LeanString`] is greater than `2^26 - 1`.
534    ///
535    /// If you want to handle such a problem manually, use [`LeanString::try_pop()`].
536    ///
537    /// # Examples
538    ///
539    /// ```
540    /// # use lean_string::LeanString;
541    /// let mut s = LeanString::from("abč");
542    ///
543    /// assert_eq!(s.pop(), Some('č'));
544    /// assert_eq!(s.pop(), Some('b'));
545    /// assert_eq!(s.pop(), Some('a'));
546    ///
547    /// assert_eq!(s.pop(), None);
548    /// ```
549    #[inline]
550    pub fn pop(&mut self) -> Option<char> {
551        self.try_pop().unwrap_with_msg()
552    }
553
554    /// Fallible version of [`LeanString::pop()`].
555    ///
556    /// This method won't panic if the system is out-of-memory, or the `capacity` is too large, but
557    /// return an [`ReserveError`]. Otherwise it behaves the same as [`LeanString::pop()`].
558    #[inline]
559    pub fn try_pop(&mut self) -> Result<Option<char>, ReserveError> {
560        self.0.pop()
561    }
562
563    /// Appends a given string slice onto the end of this [`LeanString`].
564    ///
565    /// # Panics
566    ///
567    /// Panics if cloning the [`LeanString`] fails due to the system being out-of-memory. If you
568    /// want to handle such a problem manually, use [`LeanString::try_push_str()`].
569    ///
570    /// # Examples
571    ///
572    /// ```
573    /// # use lean_string::LeanString;
574    /// let mut s = LeanString::from("foo");
575    ///
576    /// s.push_str("bar");
577    ///
578    /// assert_eq!("foobar", s);
579    /// ```
580    #[inline]
581    pub fn push_str(&mut self, string: &str) {
582        self.try_push_str(string).unwrap_with_msg()
583    }
584
585    /// Fallible version of [`LeanString::push_str()`].
586    ///
587    /// This method won't panic if the system is out-of-memory, or the `capacity` is too large, but
588    /// return an [`ReserveError`]. Otherwise it behaves the same as [`LeanString::push_str()`].
589    #[inline]
590    pub fn try_push_str(&mut self, string: &str) -> Result<(), ReserveError> {
591        self.0.push_str(string)
592    }
593
594    /// Removes a [`char`] from the [`LeanString`] at a byte position and returns it.
595    ///
596    /// # Panics
597    ///
598    /// Panics if **any** of the following conditions:
599    ///
600    /// 1. `idx` is larger than or equal tothe [`LeanString`]'s length, or if it does not lie on a [`char`]
601    /// 2. The system is out-of-memory when cloning the [`LeanString`].
602    ///
603    /// For 2, if you want to handle such a problem manually, use [`LeanString::try_remove()`].
604    ///
605    /// # Examples
606    ///
607    /// ```
608    /// # use lean_string::LeanString;
609    /// let mut s = LeanString::from("Hello 世界");
610    ///
611    /// assert_eq!(s.remove(6), '世');
612    /// assert_eq!(s.remove(1), 'e');
613    ///
614    /// assert_eq!(s, "Hllo 界");
615    /// ```
616    /// ## Past total length:
617    ///
618    /// ```should_panic
619    /// # use lean_string::LeanString;
620    /// let mut c = LeanString::from("hello there!");
621    /// c.remove(12);
622    /// ```
623    ///
624    /// ## Not on char boundary:
625    ///
626    /// ```should_panic
627    /// # use lean_string::LeanString;
628    /// let mut c = LeanString::from("🦄");
629    /// c.remove(1);
630    /// ```
631    #[inline]
632    pub fn remove(&mut self, idx: usize) -> char {
633        self.try_remove(idx).unwrap_with_msg()
634    }
635
636    /// Fallible version of [`LeanString::remove()`].
637    ///
638    /// This method won't panic if the system is out-of-memory, but return an [`ReserveError`].
639    /// Otherwise it behaves the same as [`LeanString::remove()`].
640    ///
641    /// # Panics
642    ///
643    /// This method still panics if the `idx` is larger than or equal to the [`LeanString`]'s
644    /// length, or if it does not lie on a [`char`] boundary.
645    #[inline]
646    pub fn try_remove(&mut self, idx: usize) -> Result<char, ReserveError> {
647        self.0.remove(idx)
648    }
649
650    /// Retains only the characters specified by the `predicate`.
651    ///
652    /// If the `predicate` returns `true`, the character is kept, otherwise it is removed.
653    ///
654    /// # Panics
655    ///
656    /// Panics if the system is out-of-memory when cloning the [`LeanString`]. If you want to
657    /// handle such a problem manually, use [`LeanString::try_retain()`].
658    ///
659    /// # Examples
660    ///
661    /// ```
662    /// # use lean_string::LeanString;
663    /// let mut s = LeanString::from("äb𝄞d€");
664    ///
665    /// let keep = [false, true, true, false, true];
666    /// let mut iter = keep.iter();
667    /// s.retain(|_| *iter.next().unwrap());
668    ///
669    /// assert_eq!(s, "b𝄞€");
670    /// ```
671    #[inline]
672    pub fn retain(&mut self, predicate: impl FnMut(char) -> bool) {
673        self.try_retain(predicate).unwrap_with_msg()
674    }
675
676    /// Fallible version of [`LeanString::retain()`].
677    ///
678    /// This method won't panic if the system is out-of-memory, but return an [`ReserveError`].
679    #[inline]
680    pub fn try_retain(&mut self, predicate: impl FnMut(char) -> bool) -> Result<(), ReserveError> {
681        self.0.retain(predicate)
682    }
683
684    /// Inserts a character into the [`LeanString`] at a byte position.
685    ///
686    /// # Panics
687    ///
688    /// Panics if **any** of the following conditions:
689    ///
690    /// 1. `idx` is larger than the [`LeanString`]'s length, or if it does not lie on a [`char`]
691    ///    boundary.
692    /// 2. The system is out-of-memory when cloning the [`LeanString`].
693    /// 3. The length of after inserting is greater than `2^56 - 1` on 64-bit architecture, or
694    ///    `2^32 - 1` on 32-bit architecture.
695    ///
696    /// For 2 and 3, if you want to handle such a problem manually, use [`LeanString::try_insert()`].
697    ///
698    /// # Examples
699    ///
700    /// ```
701    /// # use lean_string::LeanString;
702    /// let mut s = LeanString::from("Hello world");
703    ///
704    /// s.insert(11, '!');
705    /// assert_eq!("Hello world!", s);
706    ///
707    /// s.insert(5, ',');
708    /// assert_eq!("Hello, world!", s);
709    /// ```
710    #[inline]
711    pub fn insert(&mut self, idx: usize, ch: char) {
712        self.try_insert(idx, ch).unwrap_with_msg()
713    }
714
715    /// Fallible version of [`LeanString::insert()`].
716    ///
717    /// This method won't panic if the system is out-of-memory, or the `capacity` becomes too large
718    /// by inserting a character, but return an [`ReserveError`]. Otherwise it behaves the same as
719    /// [`LeanString::insert()`].
720    ///
721    /// # Panics
722    ///
723    /// This method still panics if the `idx` is larger than the [`LeanString`]'s length, or if it
724    /// does not lie on a [`char`] boundary.
725    #[inline]
726    pub fn try_insert(&mut self, idx: usize, ch: char) -> Result<(), ReserveError> {
727        self.0.insert_str(idx, ch.encode_utf8(&mut [0; 4]))
728    }
729
730    /// Inserts a string slice into the [`LeanString`] at a byte position.
731    ///
732    /// # Panics
733    ///
734    /// Panics if **any** of the following conditions:
735    ///
736    /// 1. `idx` is larger than the [`LeanString`]'s length, or if it does not lie on a [`char`] boundary.
737    /// 2. The system is out-of-memory when cloning the [`LeanString`].
738    /// 3. The length of after inserting is greater than `2^56 - 1` on 64-bit architecture, or
739    ///    `2^32 - 1` on 32-bit architecture.
740    ///
741    /// For 2 and 3, if you want to handle such a problem manually, use [`LeanString::try_insert_str()`].
742    ///
743    /// # Examples
744    /// ```
745    /// # use lean_string::LeanString;
746    /// let mut s = LeanString::from("bar");
747    /// s.insert_str(0, "foo");
748    /// assert_eq!("foobar", s);
749    /// ```
750    #[inline]
751    pub fn insert_str(&mut self, idx: usize, string: &str) {
752        self.try_insert_str(idx, string).unwrap_with_msg()
753    }
754
755    /// Fallible version of [`LeanString::insert_str()`].
756    ///
757    /// This method won't panic if the system is out-of-memory, or the `capacity` becomes too large
758    /// by inserting a string slice, but return an [`ReserveError`]. Otherwise it behaves the same
759    /// as [`LeanString::insert_str()`].
760    ///
761    /// # Panics
762    ///
763    /// This method still panics if the `idx` is larger than the [`LeanString`]'s length, or if it
764    /// does not lie on a [`char`] boundary.
765    #[inline]
766    pub fn try_insert_str(&mut self, idx: usize, string: &str) -> Result<(), ReserveError> {
767        self.0.insert_str(idx, string)
768    }
769
770    /// Creates a new [`LeanString`] by repeating `self` `n` times.
771    ///
772    /// # Panics
773    ///
774    /// Panics if **any** of the following conditions is met:
775    ///
776    /// 1. The resulting capacity would overflow (`self.len() * n` exceeds `usize::MAX`).
777    /// 2. The system is out-of-memory.
778    /// 3. On 64-bit architecture, the resulting length is greater than `2^56 - 1`.
779    ///    On 32-bit architecture, it is `2^32 - 1`.
780    ///
781    /// If you want to handle such a problem manually, use [`LeanString::try_repeat()`].
782    ///
783    /// # Examples
784    ///
785    /// ```
786    /// # use lean_string::LeanString;
787    /// let s = LeanString::from("abc");
788    /// assert_eq!(s.repeat(4), "abcabcabcabc");
789    /// assert_eq!(s.repeat(0), "");
790    /// ```
791    #[inline]
792    pub fn repeat(&self, n: usize) -> Self {
793        self.try_repeat(n).unwrap_with_msg()
794    }
795
796    /// Fallible version of [`LeanString::repeat()`].
797    ///
798    /// This method won't panic, but returns a [`ReserveError`] if the capacity would overflow,
799    /// the system is out-of-memory, or the resulting length exceeds the maximum. Otherwise it
800    /// behaves the same as [`LeanString::repeat()`].
801    #[inline]
802    pub fn try_repeat(&self, n: usize) -> Result<Self, ReserveError> {
803        if n == 0 || self.is_empty() {
804            Ok(LeanString::new())
805        } else if n == 1 {
806            Ok(self.clone())
807        } else {
808            let capacity = self.len().checked_mul(n).ok_or(ReserveError)?;
809            let mut res = LeanString::try_with_capacity(capacity)?;
810            for _ in 0..n {
811                res.try_push_str(self)?;
812            }
813            Ok(res)
814        }
815    }
816
817    /// Shortens a [`LeanString`] to the specified length.
818    ///
819    /// If `new_len` is greater than or equal to the string's current length, this has no effect.
820    ///
821    /// # Panics
822    ///
823    /// Panics if **any** of the following conditions is met:
824    ///
825    /// 1. `new_len` does not lie on a [`char`] boundary.
826    /// 2. The system is out-of-memory when cloning the [`LeanString`].
827    ///
828    /// For 2, If you want to handle such a problem manually, use [`LeanString::try_truncate()`].
829    ///
830    /// # Examples
831    ///
832    /// ```
833    /// # use lean_string::LeanString;
834    /// let mut s = LeanString::from("hello");
835    /// s.truncate(2);
836    /// assert_eq!(s, "he");
837    ///
838    /// // Truncating to a larger length does nothing:
839    /// s.truncate(10);
840    /// assert_eq!(s, "he");
841    /// ```
842    #[inline]
843    pub fn truncate(&mut self, new_len: usize) {
844        self.try_truncate(new_len).unwrap_with_msg()
845    }
846
847    /// Fallible version of [`LeanString::truncate()`].
848    ///
849    /// This method won't panic if the system is out-of-memory, but return an [`ReserveError`].
850    /// Otherwise it behaves the same as [`LeanString::truncate()`].
851    ///
852    /// # Panics
853    ///
854    /// This method still panics if `new_len` does not lie on a [`char`] boundary.
855    #[inline]
856    pub fn try_truncate(&mut self, new_len: usize) -> Result<(), ReserveError> {
857        self.0.truncate(new_len)
858    }
859
860    /// Splits the string into two at the given byte index.
861    ///
862    /// Returns a newly allocated [`LeanString`]. `self` contains bytes `[0, at)`, and
863    /// the returned [`LeanString`] contains bytes `[at, len)`. `at` must be on the
864    /// boundary of a UTF-8 code point.
865    ///
866    /// # Panics
867    ///
868    /// Panics if **any** of the following conditions is met:
869    ///
870    /// 1. `at` does not lie on a [`char`] boundary, or is beyond the end of the string.
871    /// 2. The system is out-of-memory when creating the new [`LeanString`].
872    ///
873    /// For 2, if you want to handle such a problem manually, use [`LeanString::try_split_off()`].
874    ///
875    /// # Examples
876    ///
877    /// ```
878    /// # use lean_string::LeanString;
879    /// let mut hello = LeanString::from("Hello, World!");
880    /// let world = hello.split_off(7);
881    /// assert_eq!(hello, "Hello, ");
882    /// assert_eq!(world, "World!");
883    /// ```
884    #[inline]
885    #[must_use = "use `.truncate()` if you don't need the other half"]
886    pub fn split_off(&mut self, at: usize) -> Self {
887        self.try_split_off(at).unwrap_with_msg()
888    }
889
890    /// Fallible version of [`LeanString::split_off()`].
891    ///
892    /// This method won't panic if the system is out-of-memory, but returns a [`ReserveError`].
893    /// Otherwise it behaves the same as [`LeanString::split_off()`].
894    ///
895    /// # Panics
896    ///
897    /// This method still panics if `at` does not lie on a [`char`] boundary, or is beyond the
898    /// end of the string.
899    #[inline]
900    #[must_use = "use `.try_truncate()` if you don't need the other half"]
901    pub fn try_split_off(&mut self, at: usize) -> Result<Self, ReserveError> {
902        let other = Repr::from_str(&self.as_str()[at..])?;
903        self.try_truncate(at)?;
904        Ok(LeanString(other))
905    }
906
907    /// Reduces the length of the [`LeanString`] to zero.
908    ///
909    /// If the [`LeanString`] is unique, this method will not change the capacity.
910    /// Otherwise, creates a new unique [`LeanString`] without heap allocation.
911    ///
912    /// # Examples
913    ///
914    /// ## unique
915    ///
916    /// ```
917    /// # use lean_string::LeanString;
918    /// let mut s = LeanString::from("This is a example of unique LeanString");
919    /// assert_eq!(s.capacity(), 38);
920    ///
921    /// s.clear();
922    ///
923    /// assert_eq!(s, "");
924    /// assert_eq!(s.capacity(), 38);
925    /// ```
926    ///
927    /// ## not unique
928    ///
929    /// ```
930    /// # use lean_string::LeanString;
931    /// let mut s = LeanString::from("This is a example of not unique LeanString");
932    /// assert_eq!(s.capacity(), 42);
933    ///
934    /// let s2 = s.clone();
935    /// s.clear();
936    ///
937    /// assert_eq!(s, "");
938    /// assert_eq!(s.capacity(), 2 * size_of::<usize>());
939    /// ```
940    #[inline]
941    pub fn clear(&mut self) {
942        if self.0.is_unique() {
943            // SAFETY:
944            // - `self` is unique.
945            // - 0 bytes is always valid UTF-8, and initialized.
946            unsafe { self.0.set_len(0) }
947        } else {
948            self.0.replace_inner(Repr::new());
949        }
950    }
951
952    /// Returns whether the [`LeanString`] is heap-allocated.
953    ///
954    /// # Examples
955    ///
956    /// ## inline
957    ///
958    /// ```
959    /// # use lean_string::LeanString;
960    /// let s = LeanString::from("hello");
961    /// assert!(!s.is_heap_allocated());
962    /// ```
963    ///
964    /// ## heap
965    ///
966    /// ```
967    /// # use lean_string::LeanString;
968    /// let s = LeanString::from("More than 2 * size_of::<usize>() bytes is heap-allocated");
969    /// assert!(s.is_heap_allocated());
970    /// ```
971    #[inline]
972    pub fn is_heap_allocated(&self) -> bool {
973        self.0.is_heap_buffer()
974    }
975}
976
977/// A [`Clone`] implementation for [`LeanString`].
978///
979/// The clone operation is performed using a reference counting mechanism, which means:
980/// - The cloned string shares the same underlying data with the original string
981/// - The cloning process is very efficient (O(1) time complexity)
982/// - No memory allocation occurs during cloning
983///
984/// # Examples
985///
986/// ```
987/// # use lean_string::LeanString;
988/// let s1 = LeanString::from("Hello, World!");
989/// let s2 = s1.clone();
990///
991/// assert_eq!(s1, s2);
992/// ```
993impl Clone for LeanString {
994    #[inline]
995    fn clone(&self) -> Self {
996        LeanString(self.0.make_shallow_clone())
997    }
998
999    #[inline]
1000    fn clone_from(&mut self, source: &Self) {
1001        self.0.replace_inner(source.0.make_shallow_clone());
1002    }
1003}
1004
1005/// A [`Drop`] implementation for [`LeanString`].
1006///
1007/// When the last reference to a [`LeanString`] is dropped:
1008/// - If the string is heap-allocated, the heap memory is freed
1009/// - The internal state is reset to an empty inline buffer
1010///
1011/// This ensures no memory leaks occur and all resources are properly cleaned up.
1012impl Drop for LeanString {
1013    #[inline]
1014    fn drop(&mut self) {
1015        self.0.replace_inner(Repr::new());
1016    }
1017}
1018
1019// SAFETY: `LeanString` is `repr(transparent)` over `Repr`, and `Repr` works like `Arc`.
1020unsafe impl Send for LeanString {}
1021unsafe impl Sync for LeanString {}
1022
1023impl Default for LeanString {
1024    #[inline]
1025    fn default() -> Self {
1026        Self::new()
1027    }
1028}
1029
1030impl Deref for LeanString {
1031    type Target = str;
1032
1033    #[inline]
1034    fn deref(&self) -> &str {
1035        self.as_str()
1036    }
1037}
1038
1039impl fmt::Debug for LeanString {
1040    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1041        fmt::Debug::fmt(self.as_str(), f)
1042    }
1043}
1044
1045impl fmt::Display for LeanString {
1046    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1047        fmt::Display::fmt(self.as_str(), f)
1048    }
1049}
1050
1051impl AsRef<str> for LeanString {
1052    #[inline]
1053    fn as_ref(&self) -> &str {
1054        self.as_str()
1055    }
1056}
1057
1058#[cfg(feature = "std")]
1059impl AsRef<OsStr> for LeanString {
1060    #[inline]
1061    fn as_ref(&self) -> &OsStr {
1062        OsStr::new(self.as_str())
1063    }
1064}
1065
1066impl AsRef<[u8]> for LeanString {
1067    #[inline]
1068    fn as_ref(&self) -> &[u8] {
1069        self.as_bytes()
1070    }
1071}
1072
1073impl Borrow<str> for LeanString {
1074    #[inline]
1075    fn borrow(&self) -> &str {
1076        self.as_str()
1077    }
1078}
1079
1080impl Eq for LeanString {}
1081
1082impl PartialEq for LeanString {
1083    #[inline]
1084    fn eq(&self, other: &Self) -> bool {
1085        self.as_str().eq(other.as_str())
1086    }
1087}
1088
1089impl PartialEq<str> for LeanString {
1090    #[inline]
1091    fn eq(&self, other: &str) -> bool {
1092        self.as_str().eq(other)
1093    }
1094}
1095
1096impl PartialEq<LeanString> for str {
1097    #[inline]
1098    fn eq(&self, other: &LeanString) -> bool {
1099        self.eq(other.as_str())
1100    }
1101}
1102
1103impl PartialEq<&str> for LeanString {
1104    #[inline]
1105    fn eq(&self, other: &&str) -> bool {
1106        self.as_str().eq(*other)
1107    }
1108}
1109
1110impl PartialEq<LeanString> for &str {
1111    #[inline]
1112    fn eq(&self, other: &LeanString) -> bool {
1113        (*self).eq(other.as_str())
1114    }
1115}
1116
1117impl PartialEq<String> for LeanString {
1118    #[inline]
1119    fn eq(&self, other: &String) -> bool {
1120        self.as_str().eq(other.as_str())
1121    }
1122}
1123
1124impl PartialEq<LeanString> for String {
1125    #[inline]
1126    fn eq(&self, other: &LeanString) -> bool {
1127        self.as_str().eq(other.as_str())
1128    }
1129}
1130
1131impl PartialEq<Cow<'_, str>> for LeanString {
1132    #[inline]
1133    fn eq(&self, other: &Cow<'_, str>) -> bool {
1134        self.as_str().eq(other.as_ref())
1135    }
1136}
1137
1138impl PartialEq<LeanString> for Cow<'_, str> {
1139    #[inline]
1140    fn eq(&self, other: &LeanString) -> bool {
1141        self.as_ref().eq(other.as_str())
1142    }
1143}
1144
1145impl Ord for LeanString {
1146    #[inline]
1147    fn cmp(&self, other: &Self) -> cmp::Ordering {
1148        self.as_str().cmp(other.as_str())
1149    }
1150}
1151
1152impl PartialOrd for LeanString {
1153    #[inline]
1154    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1155        Some(self.cmp(other))
1156    }
1157}
1158
1159impl Hash for LeanString {
1160    #[inline]
1161    fn hash<H: Hasher>(&self, state: &mut H) {
1162        self.as_str().hash(state)
1163    }
1164}
1165
1166impl From<char> for LeanString {
1167    #[inline]
1168    #[track_caller]
1169    fn from(value: char) -> Self {
1170        LeanString(Repr::from_char(value))
1171    }
1172}
1173
1174impl From<&str> for LeanString {
1175    #[inline]
1176    #[track_caller]
1177    fn from(value: &str) -> Self {
1178        LeanString(Repr::from_str(value).unwrap_with_msg())
1179    }
1180}
1181
1182impl From<String> for LeanString {
1183    #[inline]
1184    #[track_caller]
1185    fn from(value: String) -> Self {
1186        LeanString(Repr::from_str(&value).unwrap_with_msg())
1187    }
1188}
1189
1190impl From<&String> for LeanString {
1191    #[inline]
1192    #[track_caller]
1193    fn from(value: &String) -> Self {
1194        LeanString(Repr::from_str(value).unwrap_with_msg())
1195    }
1196}
1197
1198impl From<Cow<'_, str>> for LeanString {
1199    fn from(cow: Cow<str>) -> Self {
1200        match cow {
1201            Cow::Borrowed(s) => s.into(),
1202            Cow::Owned(s) => s.into(),
1203        }
1204    }
1205}
1206
1207impl From<Box<str>> for LeanString {
1208    #[inline]
1209    #[track_caller]
1210    fn from(value: Box<str>) -> Self {
1211        LeanString(Repr::from_str(&value).unwrap_with_msg())
1212    }
1213}
1214
1215impl From<&LeanString> for LeanString {
1216    #[inline]
1217    fn from(value: &LeanString) -> Self {
1218        value.clone()
1219    }
1220}
1221
1222impl From<LeanString> for String {
1223    #[inline]
1224    fn from(value: LeanString) -> Self {
1225        value.as_str().into()
1226    }
1227}
1228
1229impl From<&LeanString> for String {
1230    #[inline]
1231    fn from(value: &LeanString) -> Self {
1232        value.as_str().into()
1233    }
1234}
1235
1236impl FromStr for LeanString {
1237    type Err = ReserveError;
1238
1239    #[inline]
1240    fn from_str(s: &str) -> Result<Self, Self::Err> {
1241        Repr::from_str(s).map(Self)
1242    }
1243}
1244
1245impl FromIterator<char> for LeanString {
1246    fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
1247        let iter = iter.into_iter();
1248
1249        let (lower_bound, _) = iter.size_hint();
1250        let mut repr = match Repr::with_capacity(lower_bound) {
1251            Ok(buf) => buf,
1252            Err(_) => Repr::new(), // Ignore the error and hope that the lower_bound is incorrect.
1253        };
1254
1255        for ch in iter {
1256            repr.push_str(ch.encode_utf8(&mut [0; 4])).unwrap_with_msg();
1257        }
1258        LeanString(repr)
1259    }
1260}
1261
1262impl<'a> FromIterator<&'a char> for LeanString {
1263    fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
1264        iter.into_iter().copied().collect()
1265    }
1266}
1267
1268impl<'a> FromIterator<&'a str> for LeanString {
1269    fn from_iter<I: IntoIterator<Item = &'a str>>(iter: I) -> Self {
1270        let mut buf = LeanString::new();
1271        buf.extend(iter);
1272        buf
1273    }
1274}
1275
1276impl FromIterator<Box<str>> for LeanString {
1277    fn from_iter<I: IntoIterator<Item = Box<str>>>(iter: I) -> Self {
1278        let mut buf = LeanString::new();
1279        buf.extend(iter);
1280        buf
1281    }
1282}
1283
1284impl<'a> FromIterator<Cow<'a, str>> for LeanString {
1285    fn from_iter<I: IntoIterator<Item = Cow<'a, str>>>(iter: I) -> Self {
1286        let mut buf = LeanString::new();
1287        buf.extend(iter);
1288        buf
1289    }
1290}
1291
1292impl FromIterator<String> for LeanString {
1293    fn from_iter<I: IntoIterator<Item = String>>(iter: I) -> Self {
1294        let mut buf = LeanString::new();
1295        buf.extend(iter);
1296        buf
1297    }
1298}
1299
1300impl FromIterator<LeanString> for LeanString {
1301    fn from_iter<T: IntoIterator<Item = LeanString>>(iter: T) -> Self {
1302        let mut buf = LeanString::new();
1303        buf.extend(iter);
1304        buf
1305    }
1306}
1307
1308impl Extend<char> for LeanString {
1309    fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
1310        let iter = iter.into_iter();
1311
1312        let (lower_bound, _) = iter.size_hint();
1313        // Ignore the error and hope that the lower_bound is incorrect.
1314        let _ = self.try_reserve(lower_bound);
1315
1316        for ch in iter {
1317            self.push(ch);
1318        }
1319    }
1320}
1321
1322impl<'a> Extend<&'a char> for LeanString {
1323    fn extend<T: IntoIterator<Item = &'a char>>(&mut self, iter: T) {
1324        self.extend(iter.into_iter().copied());
1325    }
1326}
1327
1328impl<'a> Extend<&'a str> for LeanString {
1329    fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) {
1330        iter.into_iter().for_each(|s| self.push_str(s));
1331    }
1332}
1333
1334impl Extend<Box<str>> for LeanString {
1335    fn extend<T: IntoIterator<Item = Box<str>>>(&mut self, iter: T) {
1336        iter.into_iter().for_each(move |s| self.push_str(&s));
1337    }
1338}
1339
1340impl<'a> Extend<Cow<'a, str>> for LeanString {
1341    fn extend<T: IntoIterator<Item = Cow<'a, str>>>(&mut self, iter: T) {
1342        iter.into_iter().for_each(move |s| self.push_str(&s));
1343    }
1344}
1345
1346impl Extend<String> for LeanString {
1347    fn extend<T: IntoIterator<Item = String>>(&mut self, iter: T) {
1348        iter.into_iter().for_each(move |s| self.push_str(&s));
1349    }
1350}
1351
1352impl Extend<LeanString> for LeanString {
1353    fn extend<T: IntoIterator<Item = LeanString>>(&mut self, iter: T) {
1354        for s in iter {
1355            self.push_str(&s);
1356        }
1357    }
1358}
1359
1360impl Extend<LeanString> for String {
1361    fn extend<T: IntoIterator<Item = LeanString>>(&mut self, iter: T) {
1362        for s in iter {
1363            self.push_str(&s);
1364        }
1365    }
1366}
1367
1368impl fmt::Write for LeanString {
1369    #[inline]
1370    fn write_str(&mut self, s: &str) -> fmt::Result {
1371        self.push_str(s);
1372        Ok(())
1373    }
1374
1375    #[inline]
1376    fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
1377        match args.as_str() {
1378            Some(s) => {
1379                if self.is_empty() && !self.is_heap_allocated() {
1380                    // Since self is empty inline buffer or empty static buffer, constructing a new
1381                    // one with `from_static_str` is more efficient since it is O(1).
1382                    *self = LeanString::from_static_str(s);
1383                } else {
1384                    self.push_str(s);
1385                }
1386                Ok(())
1387            }
1388            None => fmt::write(self, args),
1389        }
1390    }
1391}
1392
1393impl Add<&str> for LeanString {
1394    type Output = Self;
1395
1396    #[inline]
1397    fn add(mut self, rhs: &str) -> Self::Output {
1398        self.push_str(rhs);
1399        self
1400    }
1401}
1402
1403impl AddAssign<&str> for LeanString {
1404    #[inline]
1405    fn add_assign(&mut self, rhs: &str) {
1406        self.push_str(rhs);
1407    }
1408}
1409
1410trait UnwrapWithMsg {
1411    type T;
1412    fn unwrap_with_msg(self) -> Self::T;
1413}
1414
1415impl<T, E: fmt::Display> UnwrapWithMsg for Result<T, E> {
1416    type T = T;
1417    #[inline(always)]
1418    #[track_caller]
1419    fn unwrap_with_msg(self) -> T {
1420        #[inline(never)]
1421        #[cold]
1422        #[track_caller]
1423        fn do_panic_with_msg<E: fmt::Display>(error: E) -> ! {
1424            panic!("{error}")
1425        }
1426
1427        match self {
1428            Ok(value) => value,
1429            Err(err) => do_panic_with_msg(err),
1430        }
1431    }
1432}