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}