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