1use std::borrow::Borrow;
2use std::borrow::BorrowMut;
3use std::borrow::Cow;
4use std::cmp;
5use std::convert::Infallible;
6use std::fmt;
7use std::hash::Hash;
8use std::hash::Hasher;
9use std::ops;
10use std::rc::Rc;
11use std::string::FromUtf16Error;
12use std::string::FromUtf8Error;
13use std::sync::Arc;
14
15use crate::pascal_string;
16use crate::DisplayExt;
17use crate::PascalString;
18
19#[cfg(feature = "serde")]
20mod with_serde;
21
22mod error;
23pub use error::Utf16DecodeError;
24
25mod into_chars;
26pub use into_chars::IntoChars;
27
28pub const DEFAULT_CAPACITY: usize = 30;
29
30#[derive(Clone)]
41pub enum SmartString<const N: usize = DEFAULT_CAPACITY> {
42 Heap(String),
43 Stack(PascalString<N>),
44}
45
46impl<const N: usize> SmartString<N> {
47 #[inline]
48 fn ensure_heap_mut(&mut self) -> &mut String {
49 if let Self::Stack(s) = self {
50 *self = Self::Heap(s.to_string());
51 }
52 match self {
53 Self::Heap(s) => s,
54 Self::Stack(_) => unreachable!("just promoted to heap"),
55 }
56 }
57
58 #[inline]
59 #[must_use]
60 pub const fn new() -> Self {
61 Self::Stack(PascalString::new())
62 }
63
64 #[inline]
65 #[must_use]
66 pub fn with_capacity(capacity: usize) -> Self {
67 if capacity <= N {
68 Self::new()
69 } else {
70 Self::Heap(String::with_capacity(capacity))
71 }
72 }
73
74 #[inline]
88 pub fn try_with_capacity(capacity: usize) -> Result<Self, std::collections::TryReserveError> {
89 if capacity <= N {
90 return Ok(Self::new());
91 }
92
93 let mut s = String::new();
94 s.try_reserve_exact(capacity)?;
96 Ok(Self::Heap(s))
97 }
98
99 #[inline]
100 pub fn from_utf8(vec: Vec<u8>) -> Result<Self, FromUtf8Error> {
101 String::from_utf8(vec).map(Self::Heap)
102 }
103
104 pub fn from_utf16(v: &[u16]) -> Result<Self, FromUtf16Error> {
105 String::from_utf16(v).map(|s| Self::from(s.as_str()))
106 }
107
108 #[must_use]
109 #[inline]
110 pub fn from_utf16_lossy(v: &[u16]) -> Self {
111 let s = String::from_utf16_lossy(v);
112 Self::from(s.as_str())
113 }
114
115 #[inline]
116 #[must_use]
117 pub fn as_str(&self) -> &str {
118 self
119 }
120
121 #[inline]
126 #[must_use]
127 pub fn as_bytes(&self) -> &[u8] {
128 self.as_str().as_bytes()
129 }
130
131 #[inline]
132 #[must_use]
133 pub fn len(&self) -> usize {
134 self.as_str().len()
135 }
136
137 #[inline]
138 #[must_use]
139 pub fn is_empty(&self) -> bool {
140 self.as_str().is_empty()
141 }
142
143 #[inline]
144 #[must_use]
145 pub fn as_mut_str(&mut self) -> &mut str {
146 self
147 }
148
149 #[inline]
150 pub fn is_heap(&self) -> bool {
151 matches!(self, Self::Heap(_))
152 }
153
154 #[inline]
155 pub fn is_stack(&self) -> bool {
156 matches!(self, Self::Stack(_))
157 }
158
159 #[inline]
160 #[must_use]
161 pub fn into_heap(self) -> Self {
162 Self::Heap(match self {
163 Self::Stack(s) => s.to_string(),
164 Self::Heap(s) => s,
165 })
166 }
167
168 #[inline]
169 #[must_use]
170 pub fn try_into_stack(self) -> Self {
171 match self {
172 Self::Stack(s) => Self::Stack(s),
173 Self::Heap(s) => match PascalString::try_from(s.as_str()) {
174 Ok(s) => Self::Stack(s),
175 Err(pascal_string::TryFromStrError::TooLong) => Self::Heap(s),
176 },
177 }
178 }
179
180 #[inline]
181 pub fn push_str(&mut self, string: &str) {
182 match self {
183 Self::Heap(s) => s.push_str(string),
184 Self::Stack(s) => match s.try_push_str(string) {
185 Ok(()) => (),
186 Err(pascal_string::TryFromStrError::TooLong) => {
187 let mut new = String::with_capacity(s.len() + string.len());
188 new.push_str(s.as_str());
189 new.push_str(string);
190 *self = Self::Heap(new);
191 }
192 },
193 }
194 }
195
196 #[inline]
197 pub fn capacity(&self) -> usize {
198 match self {
199 Self::Heap(s) => s.capacity(),
200 Self::Stack(s) => s.capacity(),
201 }
202 }
203
204 #[inline]
205 pub fn reserve(&mut self, additional: usize) {
206 match self {
207 Self::Heap(s) => s.reserve(additional),
208 Self::Stack(s) => {
209 if s.capacity() - s.len() < additional {
210 let mut new = String::with_capacity(s.len() + additional);
211 new.push_str(s.as_str());
212 *self = Self::Heap(new);
213 }
214 }
215 }
216 }
217
218 pub fn reserve_exact(&mut self, additional: usize) {
219 match self {
220 Self::Heap(s) => s.reserve_exact(additional),
221 Self::Stack(s) => {
222 if s.capacity() - s.len() < additional {
223 let mut new = String::new();
224 new.reserve_exact(s.len() + additional);
225 new.push_str(s.as_str());
226 *self = Self::Heap(new);
227 }
228 }
229 }
230 }
231
232 pub fn try_reserve(
233 &mut self,
234 additional: usize,
235 ) -> Result<(), std::collections::TryReserveError> {
236 match self {
237 Self::Heap(s) => s.try_reserve(additional),
238 Self::Stack(s) => {
239 if s.capacity() - s.len() < additional {
240 let mut new = String::new();
241 new.try_reserve(s.len() + additional)?;
242 new.push_str(s.as_str());
243 *self = Self::Heap(new);
244 }
245 Ok(())
246 }
247 }
248 }
249
250 pub fn try_reserve_exact(
251 &mut self,
252 additional: usize,
253 ) -> Result<(), std::collections::TryReserveError> {
254 match self {
255 Self::Heap(s) => s.try_reserve_exact(additional),
256 Self::Stack(s) => {
257 if s.capacity() - s.len() < additional {
258 let mut new = String::new();
259 new.try_reserve_exact(s.len() + additional)?;
260 new.push_str(s.as_str());
261 *self = Self::Heap(new);
262 }
263 Ok(())
264 }
265 }
266 }
267
268 #[inline]
269 pub fn shrink_to_fit(&mut self) {
270 match self {
271 Self::Heap(s) => s.shrink_to_fit(),
272 Self::Stack(_) => (),
273 }
274 }
275
276 #[inline]
277 pub fn shrink_to(&mut self, min_capacity: usize) {
278 match self {
279 Self::Heap(s) => s.shrink_to(min_capacity),
280 Self::Stack(_) => (),
281 }
282 }
283
284 pub fn push(&mut self, ch: char) {
285 match self {
286 Self::Heap(s) => s.push(ch),
287 Self::Stack(s) => match s.try_push(ch) {
288 Ok(()) => (),
289 Err(pascal_string::TryFromStrError::TooLong) => {
290 let mut new = String::with_capacity(s.len() + ch.len_utf8());
291 new.push_str(s.as_str());
292 new.push(ch);
293 *self = Self::Heap(new);
294 }
295 },
296 }
297 }
298
299 #[inline]
300 pub fn truncate(&mut self, new_len: usize) {
301 match self {
302 Self::Heap(s) => s.truncate(new_len),
303 Self::Stack(s) => s.truncate(new_len),
304 }
305 }
306
307 #[inline]
308 pub fn pop(&mut self) -> Option<char> {
309 match self {
310 Self::Heap(s) => s.pop(),
311 Self::Stack(s) => s.pop(),
312 }
313 }
314
315 #[inline]
316 pub fn clear(&mut self) {
317 match self {
318 Self::Heap(s) => s.clear(),
319 Self::Stack(s) => s.clear(),
320 }
321 }
322
323 #[inline]
330 #[must_use]
331 pub fn into_chars(self) -> IntoChars<N> {
332 match self {
333 Self::Stack(s) => IntoChars::new_stack(s),
334 Self::Heap(s) => IntoChars::new_heap(s),
335 }
336 }
337
338 #[inline]
339 #[must_use]
340 pub fn into_string(self) -> String {
341 self.into()
342 }
343
344 #[inline]
345 #[must_use]
346 pub fn into_bytes(self) -> Vec<u8> {
347 self.into_string().into_bytes()
348 }
349
350 #[inline]
351 #[must_use]
352 pub fn into_boxed_str(self) -> Box<str> {
353 self.into_string().into_boxed_str()
354 }
355
356 #[rustversion::since(1.72)]
361 #[inline]
362 #[must_use]
363 pub fn leak<'a>(self) -> &'a mut str {
364 self.into_string().leak()
365 }
366
367 #[inline]
368 #[must_use]
369 pub fn from_utf8_lossy(v: &[u8]) -> Cow<'_, str> {
370 String::from_utf8_lossy(v)
371 }
372
373 #[inline]
378 #[must_use]
379 pub fn from_utf8_lossy_owned(v: Vec<u8>) -> Self {
380 let owned = match String::from_utf8(v) {
381 Ok(s) => s,
382 Err(e) => String::from_utf8_lossy(&e.into_bytes()).into_owned(),
383 };
384
385 if owned.len() <= N {
386 if let Ok(ps) = PascalString::<N>::try_from(owned.as_str()) {
388 return Self::Stack(ps);
389 }
390 }
391 Self::Heap(owned)
392 }
393
394 #[inline]
395 pub fn insert(&mut self, idx: usize, ch: char) {
396 match self {
397 Self::Heap(s) => s.insert(idx, ch),
398 Self::Stack(s) => match s.try_insert(idx, ch) {
399 Ok(()) => (),
400 Err(pascal_string::InsertError::TooLong) => self.ensure_heap_mut().insert(idx, ch),
401 Err(_) => panic!("invalid index or char boundary"),
402 },
403 }
404 }
405
406 #[inline]
407 pub fn insert_str(&mut self, idx: usize, string: &str) {
408 match self {
409 Self::Heap(s) => s.insert_str(idx, string),
410 Self::Stack(s) => match s.try_insert_str(idx, string) {
411 Ok(()) => (),
412 Err(pascal_string::InsertError::TooLong) => {
413 self.ensure_heap_mut().insert_str(idx, string)
414 }
415 Err(_) => panic!("invalid index or char boundary"),
416 },
417 }
418 }
419
420 #[inline]
427 pub fn insert_str_truncated<'s>(&mut self, idx: usize, string: &'s str) -> &'s str {
428 self.try_insert_str_truncated(idx, string)
429 .expect("invalid index or char boundary")
430 }
431
432 #[inline]
436 pub fn try_insert_str_truncated<'s>(
437 &mut self,
438 idx: usize,
439 string: &'s str,
440 ) -> Result<&'s str, pascal_string::InsertError> {
441 match self {
442 Self::Heap(s) => {
443 let len = s.len();
444 if idx > len {
445 return Err(pascal_string::InsertError::OutOfBounds { idx, len });
446 }
447 if !s.is_char_boundary(idx) {
448 return Err(pascal_string::InsertError::NotCharBoundary { idx });
449 }
450 s.insert_str(idx, string);
451 Ok("")
452 }
453 Self::Stack(s) => s.try_insert_str_truncated(idx, string),
454 }
455 }
456
457 #[inline]
458 pub fn remove(&mut self, idx: usize) -> char {
459 match self {
460 Self::Heap(s) => s.remove(idx),
461 Self::Stack(s) => s.remove(idx),
462 }
463 }
464
465 #[inline]
466 pub fn retain<F>(&mut self, mut f: F)
467 where
468 F: FnMut(char) -> bool,
469 {
470 match self {
471 Self::Heap(s) => s.retain(f),
472 Self::Stack(s) => {
473 let mut out = PascalString::<N>::new();
475 for ch in s.as_str().chars() {
476 if f(ch) {
477 out.try_push(ch).expect("retain cannot overflow");
478 }
479 }
480 *s = out;
481 }
482 }
483 }
484
485 #[inline]
486 pub fn drain<R>(&mut self, range: R) -> std::string::Drain<'_>
487 where
488 R: std::ops::RangeBounds<usize>,
489 {
490 self.ensure_heap_mut().drain(range)
491 }
492
493 #[inline]
494 pub fn split_off(&mut self, at: usize) -> Self {
495 match self {
496 Self::Heap(s) => {
497 let len = s.len();
498 assert!(at <= len, "index out of bounds");
499 assert!(s.is_char_boundary(at), "index is not a char boundary");
500
501 let tail = &s[at..];
504 if tail.len() <= N {
505 let other = PascalString::try_from(tail)
506 .expect("tail length checked against stack capacity");
507 s.truncate(at);
508 return SmartString::Stack(other);
509 }
510
511 SmartString::Heap(s.split_off(at))
512 }
513 Self::Stack(s) => {
514 let len = s.len();
515 assert!(at <= len, "index out of bounds");
516 assert!(s.is_char_boundary(at), "index is not a char boundary");
517 let tail = &s.as_str()[at..];
518 let other = PascalString::try_from(tail)
519 .expect("tail of a PascalString must fit within the same capacity");
520 s.truncate(at);
521 SmartString::Stack(other)
522 }
523 }
524 }
525
526 #[inline]
527 pub fn replace_range<R>(&mut self, range: R, replace_with: &str)
528 where
529 R: std::ops::RangeBounds<usize>,
530 {
531 let s = self.as_str();
532 let len = s.len();
533
534 let start = match range.start_bound() {
535 std::ops::Bound::Included(&n) => n,
536 std::ops::Bound::Excluded(&n) => n + 1,
537 std::ops::Bound::Unbounded => 0,
538 };
539 let end = match range.end_bound() {
540 std::ops::Bound::Included(&n) => n + 1,
541 std::ops::Bound::Excluded(&n) => n,
542 std::ops::Bound::Unbounded => len,
543 };
544
545 match self {
546 Self::Heap(s) => s.replace_range(start..end, replace_with),
547 Self::Stack(ps) => match ps.try_replace_range_bounds(start, end, replace_with) {
548 Ok(()) => (),
549 Err(pascal_string::ReplaceRangeError::TooLong) => {
550 self.ensure_heap_mut()
551 .replace_range(start..end, replace_with);
552 }
553 Err(_) => panic!("invalid range or char boundary"),
554 },
555 }
556 }
557
558 pub fn extend_from_within<R: std::ops::RangeBounds<usize>>(&mut self, src: R) {
562 use std::ops::Bound;
563 let len = self.len();
564 let start = match src.start_bound() {
565 Bound::Included(&n) => n,
566 Bound::Excluded(&n) => n
567 .checked_add(1)
568 .expect("extend_from_within: start overflow"),
569 Bound::Unbounded => 0,
570 };
571 let end = match src.end_bound() {
572 Bound::Included(&n) => n.checked_add(1).expect("extend_from_within: end overflow"),
573 Bound::Excluded(&n) => n,
574 Bound::Unbounded => len,
575 };
576 assert!(
577 start <= end,
578 "extend_from_within: start ({start}) > end ({end})"
579 );
580 assert!(end <= len, "extend_from_within: end ({end}) > len ({len})");
581 let s = self.as_str();
582 assert!(
583 s.is_char_boundary(start),
584 "extend_from_within: start not on char boundary"
585 );
586 assert!(
587 s.is_char_boundary(end),
588 "extend_from_within: end not on char boundary"
589 );
590 let to_append = self.as_str()[start..end].to_string();
592 self.push_str(&to_append);
593 }
594
595 pub fn remove_matches(&mut self, pat: &str) {
599 if pat.is_empty() {
600 return;
601 }
602 match self {
603 Self::Stack(s) => s.remove_matches(pat),
604 Self::Heap(s) => {
605 let mut result = String::with_capacity(s.len());
607 let mut src = 0;
608 let pat_len = pat.len();
609 while src < s.len() {
610 if s[src..].starts_with(pat) {
611 src += pat_len;
612 } else {
613 let ch = s[src..].chars().next().unwrap();
614 result.push(ch);
615 src += ch.len_utf8();
616 }
617 }
618 *s = result;
619 }
620 }
621 }
622
623 #[inline]
627 pub fn remove_matches_char(&mut self, pat: char) {
628 self.retain(|c| c != pat);
629 }
630
631 #[inline]
635 pub fn replace_first(&mut self, pat: &str, replacement: &str) {
636 if let Some(start) = self.as_str().find(pat) {
637 let end = start + pat.len();
638 self.replace_range(start..end, replacement);
639 }
640 }
641
642 #[inline]
646 pub fn replace_first_char(&mut self, pat: char, replacement: &str) {
647 if let Some(start) = self.as_str().find(pat) {
648 let end = start + pat.len_utf8();
649 self.replace_range(start..end, replacement);
650 }
651 }
652
653 #[inline]
657 pub fn replace_last(&mut self, pat: &str, replacement: &str) {
658 if let Some(start) = self.as_str().rfind(pat) {
659 let end = start + pat.len();
660 self.replace_range(start..end, replacement);
661 }
662 }
663
664 #[inline]
668 pub fn replace_last_char(&mut self, pat: char, replacement: &str) {
669 if let Some(start) = self.as_str().rfind(pat) {
670 let end = start + pat.len_utf8();
671 self.replace_range(start..end, replacement);
672 }
673 }
674
675 pub fn from_utf16be(v: &[u8]) -> Result<Self, Utf16DecodeError> {
683 decode_utf16_bytes(v, u16::from_be_bytes, false)
684 }
685
686 #[must_use]
690 pub fn from_utf16be_lossy(v: &[u8]) -> Self {
691 decode_utf16_bytes(v, u16::from_be_bytes, true).unwrap()
692 }
693
694 pub fn from_utf16le(v: &[u8]) -> Result<Self, Utf16DecodeError> {
702 decode_utf16_bytes(v, u16::from_le_bytes, false)
703 }
704
705 #[must_use]
709 pub fn from_utf16le_lossy(v: &[u8]) -> Self {
710 decode_utf16_bytes(v, u16::from_le_bytes, true).unwrap()
711 }
712}
713
714fn decode_utf16_bytes<const N: usize>(
722 v: &[u8],
723 to_u16: fn([u8; 2]) -> u16,
724 lossy: bool,
725) -> Result<SmartString<N>, Utf16DecodeError> {
726 let pair_count = v.len() / 2;
727 let mut buf = String::with_capacity(pair_count); let mut i = 0;
730 while i < pair_count {
731 let code_unit = to_u16([v[i * 2], v[i * 2 + 1]]);
732
733 if (0xD800..=0xDBFF).contains(&code_unit) {
734 if i + 1 < pair_count {
736 let low = to_u16([v[(i + 1) * 2], v[(i + 1) * 2 + 1]]);
737 if (0xDC00..=0xDFFF).contains(&low) {
738 let cp = 0x10000 + ((code_unit as u32 - 0xD800) << 10) + (low as u32 - 0xDC00);
740 if let Some(ch) = char::from_u32(cp) {
741 buf.push(ch);
742 } else if lossy {
743 buf.push('\u{FFFD}');
744 } else {
745 return Err(Utf16DecodeError::new());
746 }
747 i += 2;
748 continue;
749 }
750 }
751 if lossy {
753 buf.push('\u{FFFD}');
754 } else {
755 return Err(Utf16DecodeError::new());
756 }
757 } else if (0xDC00..=0xDFFF).contains(&code_unit) {
758 if lossy {
760 buf.push('\u{FFFD}');
761 } else {
762 return Err(Utf16DecodeError::new());
763 }
764 } else {
765 debug_assert!(
767 code_unit <= 0xD7FF || code_unit >= 0xE000,
768 "surrogate {:#06X} reached BMP branch",
769 code_unit,
770 );
771 let ch = unsafe { char::from_u32_unchecked(code_unit as u32) };
774 buf.push(ch);
775 }
776 i += 1;
777 }
778
779 if v.len() % 2 != 0 {
781 if lossy {
782 buf.push('\u{FFFD}');
783 } else {
784 return Err(Utf16DecodeError::new());
785 }
786 }
787
788 Ok(SmartString::from(buf.as_str()))
789}
790
791impl<const N: usize> Default for SmartString<N> {
794 #[inline]
795 fn default() -> Self {
796 Self::new()
797 }
798}
799
800impl<T: ops::Deref<Target = str> + ?Sized, const CAPACITY: usize> PartialEq<T>
801 for SmartString<CAPACITY>
802{
803 #[inline(always)]
804 fn eq(&self, other: &T) -> bool {
805 self.as_str().eq(other.deref())
806 }
807}
808
809macro_rules! impl_reverse_eq_for_str_types {
810 ($($t:ty),*) => {
811 $(
812 impl<const N: usize> PartialEq<SmartString<N>> for $t {
813 #[inline(always)]
814 fn eq(&self, other: &SmartString<N>) -> bool {
815 let a: &str = self.as_ref();
816 let b = other.as_str();
817 a.eq(b)
818 }
819 }
820
821 impl<const N: usize> PartialEq<SmartString<N>> for &$t {
822 #[inline(always)]
823 fn eq(&self, other: &SmartString<N>) -> bool {
824 let a: &str = self.as_ref();
825 let b = other.as_str();
826 a.eq(b)
827 }
828 }
829
830 impl<const N: usize> PartialEq<SmartString<N>> for &mut $t {
831 #[inline(always)]
832 fn eq(&self, other: &SmartString<N>) -> bool {
833 let a: &str = self.as_ref();
834 let b = other.as_str();
835 a.eq(b)
836 }
837 }
838 )*
839 };
840}
841
842impl_reverse_eq_for_str_types!(String, str, Cow<'_, str>, Box<str>, Rc<str>, Arc<str>);
843
844impl<const M: usize, const N: usize> PartialEq<SmartString<N>> for &PascalString<M> {
845 #[inline(always)]
846 fn eq(&self, other: &SmartString<N>) -> bool {
847 let a: &str = self.as_ref();
848 let b = other.as_str();
849 a.eq(b)
850 }
851}
852
853impl<const M: usize, const N: usize> PartialEq<SmartString<N>> for &mut PascalString<M> {
854 #[inline(always)]
855 fn eq(&self, other: &SmartString<N>) -> bool {
856 let a: &str = self.as_ref();
857 let b = other.as_str();
858 a.eq(b)
859 }
860}
861
862impl<const N: usize> Eq for SmartString<N> {}
863
864impl<T: ops::Deref<Target = str>, const N: usize> PartialOrd<T> for SmartString<N> {
865 #[inline(always)]
866 fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
867 self.as_str().partial_cmp(other.deref())
868 }
869}
870
871impl<const N: usize> Ord for SmartString<N> {
872 #[inline(always)]
873 fn cmp(&self, other: &Self) -> cmp::Ordering {
874 self.as_str().cmp(other.as_str())
875 }
876}
877
878impl<const N: usize> Hash for SmartString<N> {
879 #[inline(always)]
880 fn hash<H: Hasher>(&self, state: &mut H) {
881 self.as_str().hash(state)
882 }
883}
884
885impl<const N: usize> fmt::Debug for SmartString<N> {
888 #[inline(always)]
889 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
890 let name: PascalString<39> = format_args!("SmartString<{N}>")
891 .try_to_fmt()
892 .unwrap_or_else(|_| "SmartString<?>".to_fmt());
893 f.debug_tuple(&name).field(&self.as_str()).finish()
894 }
895}
896
897impl<const N: usize> fmt::Display for SmartString<N> {
898 #[inline]
899 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
900 match self {
901 Self::Heap(s) => s.fmt(f),
902 Self::Stack(s) => s.fmt(f),
903 }
904 }
905}
906
907impl<const N: usize> ops::Deref for SmartString<N> {
910 type Target = str;
911
912 #[inline]
913 fn deref(&self) -> &Self::Target {
914 match self {
915 Self::Heap(s) => s.deref(),
916 Self::Stack(s) => s.deref(),
917 }
918 }
919}
920
921impl<const N: usize> ops::DerefMut for SmartString<N> {
922 #[inline]
923 fn deref_mut(&mut self) -> &mut Self::Target {
924 match self {
925 Self::Heap(s) => s.deref_mut(),
926 Self::Stack(s) => s.deref_mut(),
927 }
928 }
929}
930
931impl<const N: usize> Borrow<str> for SmartString<N> {
932 #[inline(always)]
933 fn borrow(&self) -> &str {
934 self
935 }
936}
937
938impl<const N: usize> AsRef<str> for SmartString<N> {
939 #[inline(always)]
940 fn as_ref(&self) -> &str {
941 self
942 }
943}
944
945impl<const N: usize> AsRef<[u8]> for SmartString<N> {
946 #[inline(always)]
947 fn as_ref(&self) -> &[u8] {
948 self.as_bytes()
949 }
950}
951
952impl<const N: usize> AsMut<str> for SmartString<N> {
953 #[inline(always)]
954 fn as_mut(&mut self) -> &mut str {
955 self
956 }
957}
958
959impl<const N: usize> BorrowMut<str> for SmartString<N> {
960 #[inline(always)]
961 fn borrow_mut(&mut self) -> &mut str {
962 self
963 }
964}
965
966impl<const N: usize> From<String> for SmartString<N> {
969 #[inline]
970 fn from(s: String) -> Self {
971 Self::Heap(s)
972 }
973}
974
975impl<const N: usize> From<SmartString<N>> for String {
976 #[inline]
977 fn from(s: SmartString<N>) -> Self {
978 match s {
979 SmartString::Heap(s) => s,
980 SmartString::Stack(s) => s.to_string(),
981 }
982 }
983}
984
985impl<const N: usize> std::str::FromStr for SmartString<N> {
986 type Err = Infallible;
987
988 #[inline]
989 fn from_str(s: &str) -> Result<Self, Self::Err> {
990 Ok(Self::from(s))
991 }
992}
993
994impl<const M: usize, const N: usize> From<PascalString<M>> for SmartString<N> {
995 #[inline]
996 fn from(s: PascalString<M>) -> Self {
997 PascalString::try_from(s.as_str())
998 .map(Self::Stack)
999 .unwrap_or_else(|pascal_string::TryFromStrError::TooLong| Self::Heap(s.to_string()))
1000 }
1001}
1002
1003impl<const N: usize> From<&str> for SmartString<N> {
1004 #[inline]
1005 fn from(s: &str) -> Self {
1006 PascalString::try_from(s)
1007 .map(Self::Stack)
1008 .unwrap_or_else(|pascal_string::TryFromStrError::TooLong| Self::Heap(String::from(s)))
1009 }
1010}
1011
1012impl<const N: usize> From<char> for SmartString<N> {
1013 #[inline]
1014 fn from(ch: char) -> Self {
1015 let mut s = Self::new();
1016 s.push(ch);
1017 s
1018 }
1019}
1020
1021impl<const N: usize> From<&String> for SmartString<N> {
1022 #[inline]
1023 fn from(s: &String) -> Self {
1024 Self::from(s.as_str())
1025 }
1026}
1027
1028impl<const N: usize> From<&mut str> for SmartString<N> {
1029 #[inline]
1030 fn from(s: &mut str) -> Self {
1031 Self::from(&*s)
1032 }
1033}
1034
1035impl<const N: usize> From<Box<str>> for SmartString<N> {
1036 #[inline]
1037 fn from(s: Box<str>) -> Self {
1038 Self::from(s.as_ref())
1039 }
1040}
1041
1042impl<const N: usize> From<&Box<str>> for SmartString<N> {
1043 #[inline]
1044 fn from(s: &Box<str>) -> Self {
1045 Self::from(s.as_ref())
1046 }
1047}
1048
1049impl<const N: usize> From<Rc<str>> for SmartString<N> {
1050 #[inline]
1051 fn from(s: Rc<str>) -> Self {
1052 Self::from(s.as_ref())
1053 }
1054}
1055
1056impl<const N: usize> From<&Rc<str>> for SmartString<N> {
1057 #[inline]
1058 fn from(s: &Rc<str>) -> Self {
1059 Self::from(s.as_ref())
1060 }
1061}
1062
1063impl<const N: usize> From<Arc<str>> for SmartString<N> {
1064 #[inline]
1065 fn from(s: Arc<str>) -> Self {
1066 Self::from(s.as_ref())
1067 }
1068}
1069
1070impl<const N: usize> From<&Arc<str>> for SmartString<N> {
1071 #[inline]
1072 fn from(s: &Arc<str>) -> Self {
1073 Self::from(s.as_ref())
1074 }
1075}
1076
1077impl<const N: usize> From<Cow<'_, str>> for SmartString<N> {
1078 #[inline]
1079 fn from(s: Cow<'_, str>) -> Self {
1080 match s {
1081 Cow::Borrowed(s) => Self::from(s),
1082 Cow::Owned(s) => Self::Heap(s),
1083 }
1084 }
1085}
1086
1087impl<const N: usize> From<&Cow<'_, str>> for SmartString<N> {
1088 #[inline]
1089 fn from(s: &Cow<'_, str>) -> Self {
1090 Self::from(s.as_ref())
1091 }
1092}
1093
1094impl<const N: usize> FromIterator<char> for SmartString<N> {
1095 fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
1096 let mut s = Self::new();
1097 s.extend(iter);
1098 s
1099 }
1100}
1101
1102impl<'a, const N: usize> FromIterator<&'a str> for SmartString<N> {
1103 fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
1104 let mut s = Self::new();
1105 s.extend(iter);
1106 s
1107 }
1108}
1109
1110impl<const N: usize> Extend<char> for SmartString<N> {
1111 #[inline]
1112 fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
1113 for ch in iter {
1114 self.push(ch);
1115 }
1116 }
1117}
1118
1119impl<'a, const N: usize> Extend<&'a str> for SmartString<N> {
1120 #[inline]
1121 fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) {
1122 for s in iter {
1123 self.push_str(s);
1124 }
1125 }
1126}
1127
1128impl<'a, const N: usize> Extend<&'a char> for SmartString<N> {
1129 #[inline]
1130 fn extend<T: IntoIterator<Item = &'a char>>(&mut self, iter: T) {
1131 for ch in iter {
1132 self.push(*ch);
1133 }
1134 }
1135}
1136
1137impl<const N: usize> Extend<String> for SmartString<N> {
1138 #[inline]
1139 fn extend<T: IntoIterator<Item = String>>(&mut self, iter: T) {
1140 for s in iter {
1141 self.push_str(&s);
1142 }
1143 }
1144}
1145
1146impl<'a, const N: usize> Extend<&'a String> for SmartString<N> {
1147 #[inline]
1148 fn extend<T: IntoIterator<Item = &'a String>>(&mut self, iter: T) {
1149 for s in iter {
1150 self.push_str(s.as_str());
1151 }
1152 }
1153}
1154
1155impl<const N: usize> fmt::Write for SmartString<N> {
1158 #[inline]
1159 fn write_str(&mut self, s: &str) -> fmt::Result {
1160 self.push_str(s);
1161 Ok(())
1162 }
1163}
1164
1165impl<const N: usize> From<SmartString<N>> for Box<str> {
1166 #[inline]
1167 fn from(s: SmartString<N>) -> Self {
1168 s.into_boxed_str()
1169 }
1170}
1171
1172impl<const N: usize> From<SmartString<N>> for Vec<u8> {
1173 #[inline]
1174 fn from(s: SmartString<N>) -> Self {
1175 s.into_bytes()
1176 }
1177}
1178
1179impl<const N: usize> From<SmartString<N>> for Rc<str> {
1180 #[inline]
1181 fn from(s: SmartString<N>) -> Self {
1182 Rc::from(s.into_string())
1186 }
1187}
1188
1189impl<const N: usize> From<SmartString<N>> for Arc<str> {
1190 #[inline]
1191 fn from(s: SmartString<N>) -> Self {
1192 Arc::from(s.into_string())
1194 }
1195}
1196
1197impl<const N: usize, T: ops::Deref<Target = str>> ops::Add<T> for SmartString<N> {
1200 type Output = Self;
1201
1202 #[inline]
1203 fn add(mut self, rhs: T) -> Self::Output {
1204 self.push_str(&rhs);
1205 self
1206 }
1207}
1208
1209impl<const N: usize, T: ops::Deref<Target = str>> ops::AddAssign<T> for SmartString<N> {
1210 #[inline]
1211 fn add_assign(&mut self, rhs: T) {
1212 self.push_str(rhs.deref());
1213 }
1214}
1215
1216#[cfg(test)]
1219mod tests {
1220 use std::mem;
1221
1222 use super::*;
1223
1224 #[test]
1225 fn test_size() {
1226 assert_eq!(mem::size_of::<SmartString>(), 32);
1228
1229 let small_sizes = [
1234 mem::size_of::<SmartString<0>>(),
1235 mem::size_of::<SmartString<1>>(),
1236 mem::size_of::<SmartString<15>>(),
1237 ];
1238 for size in small_sizes {
1239 assert!(
1240 size == 24 || size == 32,
1241 "unexpected SmartString small size: {size}"
1242 );
1243 }
1244
1245 assert_eq!(mem::size_of::<SmartString<16>>(), 32);
1248 assert_eq!(mem::size_of::<SmartString<22>>(), 32);
1249
1250 assert_eq!(mem::size_of::<SmartString<23>>(), 32);
1252 assert_eq!(mem::size_of::<SmartString<30>>(), 32);
1253
1254 assert_eq!(mem::size_of::<SmartString<31>>(), 40);
1257 assert_eq!(mem::size_of::<SmartString<38>>(), 40);
1258
1259 assert_eq!(mem::size_of::<SmartString<39>>(), 48);
1260 assert_eq!(mem::size_of::<SmartString<46>>(), 48);
1261 }
1262
1263 #[test]
1264 fn test_from_str_picks_stack_or_heap() {
1265 let s = SmartString::<4>::from("abcd");
1266 assert!(s.is_stack());
1267
1268 let s = SmartString::<4>::from("abcde");
1269 assert!(s.is_heap());
1270 }
1271
1272 #[test]
1273 fn test_push_str_transitions_stack_to_heap() {
1274 let mut s = SmartString::<4>::new();
1275 assert!(s.is_stack());
1276
1277 s.push_str("ab");
1278 assert!(s.is_stack());
1279 assert_eq!(s.as_str(), "ab");
1280
1281 s.push_str("cd");
1282 assert!(s.is_stack());
1283 assert_eq!(s.as_str(), "abcd");
1284
1285 s.push_str("e");
1287 assert!(s.is_heap());
1288 assert_eq!(s.as_str(), "abcde");
1289 }
1290
1291 #[test]
1292 fn test_push_char_and_unicode_boundaries() {
1293 let mut s = SmartString::<4>::new();
1294 s.push('€'); assert!(s.is_stack());
1296 assert_eq!(s.as_str(), "€");
1297
1298 s.push('a'); assert!(s.is_stack());
1300 assert_eq!(s.as_str(), "€a");
1301
1302 s.push('b');
1304 assert!(s.is_heap());
1305 assert_eq!(s.as_str(), "€ab");
1306
1307 s.truncate(3);
1309 assert_eq!(s.as_str(), "€");
1310 assert_eq!(s.pop(), Some('€'));
1311 assert_eq!(s.as_str(), "");
1312 assert_eq!(s.pop(), None);
1313 }
1314
1315 #[test]
1316 fn test_reserve_transitions_stack_to_heap() {
1317 let mut s = SmartString::<4>::from("ab");
1318 assert!(s.is_stack());
1319
1320 s.reserve(2);
1322 assert!(s.is_stack());
1323
1324 s.reserve(3);
1326 assert!(s.is_heap());
1327 assert_eq!(s.as_str(), "ab");
1328 }
1329
1330 #[test]
1331 fn test_try_into_stack_converts_short_heap_string() {
1332 let s = SmartString::<4>::from(String::from("abc"));
1333 assert!(s.is_heap());
1334
1335 let s = s.try_into_stack();
1336 assert!(s.is_stack());
1337 assert_eq!(s.as_str(), "abc");
1338 }
1339
1340 #[test]
1341 fn test_into_heap_always_returns_heap_variant() {
1342 let s = SmartString::<4>::from("abc");
1343 assert!(s.is_stack());
1344
1345 let s = s.into_heap();
1346 assert!(s.is_heap());
1347 assert_eq!(s.as_str(), "abc");
1348 }
1349
1350 #[test]
1351 fn test_truncate_does_not_demote_heap_to_stack() {
1352 let mut s = SmartString::<4>::from("abcde");
1353 assert!(s.is_heap());
1354
1355 s.truncate(2);
1356 assert_eq!(s.as_str(), "ab");
1357 assert!(s.is_heap());
1358
1359 let s = s.try_into_stack();
1360 assert_eq!(s.as_str(), "ab");
1361 assert!(s.is_stack());
1362 }
1363
1364 #[test]
1365 fn test_try_reserve_transitions_stack_to_heap() {
1366 let mut s = SmartString::<4>::from("ab");
1367 assert!(s.is_stack());
1368
1369 s.try_reserve(2).unwrap();
1371 assert!(s.is_stack());
1372
1373 s.try_reserve(3).unwrap();
1375 assert!(s.is_heap());
1376 assert_eq!(s.as_str(), "ab");
1377 }
1378
1379 #[test]
1380 fn test_try_reserve_exact_transitions_stack_to_heap() {
1381 let mut s = SmartString::<4>::from("ab");
1382 assert!(s.is_stack());
1383
1384 s.try_reserve_exact(3).unwrap();
1386 assert!(s.is_heap());
1387 assert_eq!(s.as_str(), "ab");
1388 }
1389
1390 #[test]
1391 fn test_extend_str_transitions_stack_to_heap() {
1392 let mut s = SmartString::<4>::new();
1393 s.extend(["ab", "cd"]);
1394 assert!(s.is_stack());
1395 assert_eq!(s.as_str(), "abcd");
1396
1397 s.extend(["e"]);
1398 assert!(s.is_heap());
1399 assert_eq!(s.as_str(), "abcde");
1400 }
1401
1402 #[test]
1403 fn test_extend_char_unicode_boundaries() {
1404 let mut s = SmartString::<4>::new();
1405 s.extend(['€', 'a']); assert!(s.is_stack());
1407 assert_eq!(s.as_str(), "€a");
1408
1409 s.extend(['b']);
1410 assert!(s.is_heap());
1411 assert_eq!(s.as_str(), "€ab");
1412 }
1413
1414 #[test]
1415 fn test_add_assign() {
1416 let mut s = SmartString::<4>::from("a");
1417 s += "bcd";
1418 assert!(s.is_stack());
1419 assert_eq!(s.as_str(), "abcd");
1420
1421 s += "e";
1422 assert!(s.is_heap());
1423 assert_eq!(s.as_str(), "abcde");
1424 }
1425
1426 #[test]
1427 fn test_insert_and_remove_promotes_to_heap() {
1428 let mut s = SmartString::<8>::from("ab");
1429 assert!(s.is_stack());
1430
1431 s.insert(1, '€');
1432 assert!(s.is_stack());
1433 assert_eq!(s.as_str(), "a€b");
1434
1435 let removed = s.remove(1);
1436 assert_eq!(removed, '€');
1437 assert_eq!(s.as_str(), "ab");
1438 }
1439
1440 #[test]
1441 fn test_insert_promotes_to_heap_when_overflow() {
1442 let mut s = SmartString::<4>::from("ab");
1443 assert!(s.is_stack());
1444
1445 s.insert(1, '€');
1447 assert!(s.is_heap());
1448 assert_eq!(s.as_str(), "a€b");
1449 }
1450
1451 #[test]
1452 fn test_insert_str_truncated_on_stack() {
1453 let mut s = SmartString::<4>::from("ab");
1454 assert!(s.is_stack());
1455
1456 let rem = s.insert_str_truncated(1, "cde");
1458 assert_eq!(s.as_str(), "acdb");
1459 assert_eq!(rem, "e");
1460 assert!(s.is_stack());
1461 }
1462
1463 #[test]
1464 fn test_split_off_returns_stack_when_possible() {
1465 let mut s = SmartString::<8>::from("hello!");
1466 assert!(s.is_stack());
1467
1468 let other = s.split_off(5);
1469 assert_eq!(s.as_str(), "hello");
1470 assert_eq!(other.as_str(), "!");
1471 assert!(other.is_stack());
1472 assert!(s.is_stack());
1473 }
1474
1475 #[test]
1476 fn test_split_off_on_heap_avoids_alloc_when_tail_fits_stack() {
1477 let mut s = SmartString::<4>::from("abcde"); assert!(s.is_heap());
1479
1480 let other = s.split_off(4); assert_eq!(s.as_str(), "abcd");
1482 assert!(s.is_heap()); assert_eq!(other.as_str(), "e");
1484 assert!(other.is_stack());
1485 }
1486
1487 #[test]
1488 fn test_retain_keeps_stack_when_possible() {
1489 let mut s = SmartString::<8>::from("a1b2c3");
1490 assert!(s.is_stack());
1491 s.retain(|ch| ch.is_ascii_alphabetic());
1492 assert_eq!(s.as_str(), "abc");
1493 assert!(s.is_stack());
1494 }
1495
1496 #[test]
1497 fn test_replace_range() {
1498 let mut s = SmartString::<8>::from("ab");
1499 s.replace_range(1..1, "cd");
1500 assert_eq!(s.as_str(), "acdb");
1501 assert!(s.is_stack());
1502 }
1503
1504 #[test]
1505 fn test_len_and_is_empty() {
1506 let s = SmartString::<4>::new();
1507 assert!(s.is_empty());
1508 assert_eq!(s.len(), 0);
1509
1510 let s = SmartString::<4>::from("ab");
1511 assert!(!s.is_empty());
1512 assert_eq!(s.len(), 2);
1513 }
1514
1515 #[test]
1516 fn test_from_string_refs_and_smart_extend_refs() {
1517 let base = String::from("ab");
1518 let s = SmartString::<4>::from(&base);
1519 assert!(s.is_stack());
1520 assert_eq!(s.as_str(), "ab");
1521
1522 let mut s = SmartString::<4>::new();
1523 let euro = '€';
1524 let a = 'a';
1525 s.extend([&euro, &a]);
1526 assert!(s.is_stack());
1527 assert_eq!(s.as_str(), "€a");
1528
1529 let b = String::from("b");
1530 s.extend([&b]);
1531 assert!(s.is_heap());
1532 assert_eq!(s.as_str(), "€ab");
1533 }
1534
1535 #[test]
1536 fn test_into_boxed_str() {
1537 let boxed = SmartString::<4>::from("ab").into_boxed_str();
1538 assert_eq!(&*boxed, "ab");
1539 }
1540
1541 #[rustversion::since(1.72)]
1542 #[test]
1543 fn test_leak() {
1544 let leaked: &'static mut str = SmartString::<4>::from("ab").leak();
1545 leaked.make_ascii_uppercase();
1546 assert_eq!(leaked, "AB");
1547 }
1548
1549 #[test]
1550 fn test_from_utf8_lossy() {
1551 let s = SmartString::<4>::from_utf8_lossy(&[0x66, 0x6f, 0x6f]);
1552 assert_eq!(s, "foo");
1553
1554 let s = SmartString::<4>::from_utf8_lossy(&[0xff]);
1555 assert!(matches!(s, Cow::Owned(_)));
1556 }
1557
1558 #[test]
1559 fn test_from_utf8_lossy_owned_picks_stack_when_possible() {
1560 let s = SmartString::<4>::from_utf8_lossy_owned(b"ab".to_vec());
1561 assert!(s.is_stack());
1562 assert_eq!(s.as_str(), "ab");
1563
1564 let s = SmartString::<4>::from_utf8_lossy_owned(vec![0xff]);
1566 assert!(s.is_stack());
1567 assert_eq!(s.as_str(), "�");
1568 }
1569
1570 #[test]
1571 fn test_try_with_capacity_picks_stack_or_heap() {
1572 let s = SmartString::<4>::try_with_capacity(3).unwrap();
1573 assert!(s.is_stack());
1574 assert_eq!(s.capacity(), 4);
1575
1576 let s = SmartString::<4>::try_with_capacity(10).unwrap();
1577 assert!(s.is_heap());
1578 assert!(s.capacity() >= 10);
1579 }
1580
1581 #[test]
1582 fn test_from_char_picks_stack_or_heap() {
1583 let s = SmartString::<4>::from('€'); assert!(s.is_stack());
1585 assert_eq!(s.as_str(), "€");
1586
1587 let s = SmartString::<2>::from('€'); assert!(s.is_heap());
1589 assert_eq!(s.as_str(), "€");
1590 }
1591
1592 #[test]
1593 fn test_from_ref_str_containers_and_into_box_str() {
1594 let b: Box<str> = "ab".into();
1595 let r: Rc<str> = Rc::from("ab");
1596 let a: Arc<str> = Arc::from("ab");
1597
1598 assert_eq!(SmartString::<4>::from(&b).as_str(), "ab");
1599 assert_eq!(SmartString::<4>::from(&r).as_str(), "ab");
1600 assert_eq!(SmartString::<4>::from(&a).as_str(), "ab");
1601
1602 let boxed: Box<str> = SmartString::<4>::from("ab").into();
1603 assert_eq!(&*boxed, "ab");
1604 }
1605
1606 #[test]
1607 fn test_from_cow_ref() {
1608 let borrowed: Cow<'_, str> = Cow::Borrowed("ab");
1609 let owned: Cow<'_, str> = Cow::Owned(String::from("ab"));
1610 assert_eq!(SmartString::<4>::from(&borrowed).as_str(), "ab");
1611 assert_eq!(SmartString::<4>::from(&owned).as_str(), "ab");
1612 }
1613
1614 #[test]
1615 fn test_into_vec_u8_rc_arc_str() {
1616 let bytes: Vec<u8> = SmartString::<4>::from("ab").into();
1617 assert_eq!(bytes, b"ab");
1618
1619 let rc: Rc<str> = SmartString::<4>::from("ab").into();
1620 assert_eq!(&*rc, "ab");
1621
1622 let arc: Arc<str> = SmartString::<4>::from("ab").into();
1623 assert_eq!(&*arc, "ab");
1624 }
1625
1626 #[test]
1629 fn test_utf16be_ascii_hi_lands_on_stack() {
1630 let bytes = [0x00u8, 0x48, 0x00, 0x69];
1632 let s = SmartString::<30>::from_utf16be(&bytes).unwrap();
1633 assert_eq!(s.as_str(), "Hi");
1634 assert!(s.is_stack());
1635 }
1636
1637 #[test]
1638 fn test_utf16be_long_string_lands_on_heap() {
1639 let code_unit: u16 = 0x4F60; let mut bytes = Vec::new();
1645 for _ in 0..11 {
1646 bytes.extend_from_slice(&code_unit.to_be_bytes());
1647 }
1648 let s = SmartString::<30>::from_utf16be(&bytes).unwrap();
1649 assert_eq!(s.as_str(), "你你你你你你你你你你你");
1650 assert!(s.is_heap());
1651 }
1652
1653 #[test]
1654 fn test_utf16be_surrogate_pair_musical_symbol() {
1655 let bytes = [0xD8u8, 0x34, 0xDD, 0x1E];
1657 let s = SmartString::<30>::from_utf16be(&bytes).unwrap();
1658 assert_eq!(s.as_str(), "\u{1D11E}");
1659 }
1660
1661 #[test]
1662 fn test_utf16be_unpaired_high_surrogate_is_err() {
1663 let bytes = [0xD8u8, 0x00, 0x00, 0x41]; assert!(SmartString::<30>::from_utf16be(&bytes).is_err());
1666 }
1667
1668 #[test]
1669 fn test_utf16be_unpaired_low_surrogate_is_err() {
1670 let bytes = [0xDCu8, 0x00, 0x00, 0x41]; assert!(SmartString::<30>::from_utf16be(&bytes).is_err());
1673 }
1674
1675 #[test]
1676 fn test_utf16be_empty_input() {
1677 let s = SmartString::<30>::from_utf16be(&[]).unwrap();
1678 assert_eq!(s.as_str(), "");
1679 assert!(s.is_stack());
1680 }
1681
1682 #[test]
1683 fn test_utf16be_odd_byte_count_is_err() {
1684 let bytes = [0x00u8, 0x48, 0x00]; assert!(SmartString::<30>::from_utf16be(&bytes).is_err());
1686 }
1687
1688 #[test]
1689 fn test_utf16be_bmp_chinese() {
1690 let bytes = [0x4Fu8, 0x60, 0x59, 0x7D];
1692 let s = SmartString::<30>::from_utf16be(&bytes).unwrap();
1693 assert_eq!(s.as_str(), "你好");
1694 }
1695
1696 #[test]
1699 fn test_utf16be_lossy_valid_input_unchanged() {
1700 let bytes = [0x00u8, 0x48, 0x00, 0x69]; let s = SmartString::<30>::from_utf16be_lossy(&bytes);
1702 assert_eq!(s.as_str(), "Hi");
1703 }
1704
1705 #[test]
1706 fn test_utf16be_lossy_unpaired_high_surrogate_replaced() {
1707 let bytes = [0xD8u8, 0x00, 0x00, 0x41];
1709 let s = SmartString::<30>::from_utf16be_lossy(&bytes);
1710 assert_eq!(s.as_str(), "\u{FFFD}A");
1711 }
1712
1713 #[test]
1714 fn test_utf16be_lossy_unpaired_low_surrogate_replaced() {
1715 let bytes = [0xDCu8, 0x00, 0x00, 0x41]; let s = SmartString::<30>::from_utf16be_lossy(&bytes);
1717 assert_eq!(s.as_str(), "\u{FFFD}A");
1718 }
1719
1720 #[test]
1721 fn test_utf16be_lossy_odd_trailing_byte_replaced() {
1722 let bytes = [0x00u8, 0x48, 0xFF]; let s = SmartString::<30>::from_utf16be_lossy(&bytes);
1724 assert_eq!(s.as_str(), "H\u{FFFD}");
1725 }
1726
1727 #[test]
1730 fn test_utf16le_ascii_hi_lands_on_stack() {
1731 let bytes = [0x48u8, 0x00, 0x69, 0x00];
1733 let s = SmartString::<30>::from_utf16le(&bytes).unwrap();
1734 assert_eq!(s.as_str(), "Hi");
1735 assert!(s.is_stack());
1736 }
1737
1738 #[test]
1739 fn test_utf16le_long_string_lands_on_heap() {
1740 let code_unit: u16 = 0x4F60; let mut bytes = Vec::new();
1743 for _ in 0..11 {
1744 bytes.extend_from_slice(&code_unit.to_le_bytes());
1745 }
1746 let s = SmartString::<30>::from_utf16le(&bytes).unwrap();
1747 assert_eq!(s.as_str(), "你你你你你你你你你你你");
1748 assert!(s.is_heap());
1749 }
1750
1751 #[test]
1752 fn test_utf16le_surrogate_pair_musical_symbol() {
1753 let bytes = [0x34u8, 0xD8, 0x1E, 0xDD];
1755 let s = SmartString::<30>::from_utf16le(&bytes).unwrap();
1756 assert_eq!(s.as_str(), "\u{1D11E}");
1757 }
1758
1759 #[test]
1760 fn test_utf16le_unpaired_high_surrogate_is_err() {
1761 let bytes = [0x00u8, 0xD8, 0x41, 0x00]; assert!(SmartString::<30>::from_utf16le(&bytes).is_err());
1763 }
1764
1765 #[test]
1766 fn test_utf16le_unpaired_low_surrogate_is_err() {
1767 let bytes = [0x00u8, 0xDC, 0x41, 0x00]; assert!(SmartString::<30>::from_utf16le(&bytes).is_err());
1769 }
1770
1771 #[test]
1772 fn test_utf16le_empty_input() {
1773 let s = SmartString::<30>::from_utf16le(&[]).unwrap();
1774 assert_eq!(s.as_str(), "");
1775 }
1776
1777 #[test]
1778 fn test_utf16le_odd_byte_count_is_err() {
1779 let bytes = [0x48u8, 0x00, 0x00]; assert!(SmartString::<30>::from_utf16le(&bytes).is_err());
1781 }
1782
1783 #[test]
1784 fn test_utf16le_bmp_chinese() {
1785 let bytes = [0x60u8, 0x4F, 0x7D, 0x59];
1787 let s = SmartString::<30>::from_utf16le(&bytes).unwrap();
1788 assert_eq!(s.as_str(), "你好");
1789 }
1790
1791 #[test]
1792 fn test_utf16le_lossy_unpaired_surrogate_replaced() {
1793 let bytes = [0x00u8, 0xD8, 0x41, 0x00]; let s = SmartString::<30>::from_utf16le_lossy(&bytes);
1795 assert_eq!(s.as_str(), "\u{FFFD}A");
1796 }
1797
1798 #[test]
1799 fn test_utf16le_lossy_odd_trailing_byte_replaced() {
1800 let bytes = [0x48u8, 0x00, 0xFF]; let s = SmartString::<30>::from_utf16le_lossy(&bytes);
1802 assert_eq!(s.as_str(), "H\u{FFFD}");
1803 }
1804
1805 #[test]
1806 fn test_utf16_decode_error_display() {
1807 let err = Utf16DecodeError::new();
1808 assert_eq!(err.to_string(), "invalid utf-16: lone surrogate found");
1809 }
1810
1811 #[test]
1812 fn test_utf16be_stack_vs_heap_awareness() {
1813 let bytes = [0x00u8, 0x48, 0x00, 0x69]; let stack = SmartString::<4>::from_utf16be(&bytes).unwrap();
1816 assert!(stack.is_stack());
1817
1818 let code_unit: u16 = 0x4F60;
1820 let mut long_bytes = Vec::new();
1821 for _ in 0..3 {
1822 long_bytes.extend_from_slice(&code_unit.to_be_bytes());
1823 }
1824 let heap = SmartString::<4>::from_utf16be(&long_bytes).unwrap();
1825 assert!(heap.is_heap());
1826 }
1827
1828 #[test]
1831 fn test_extend_from_within_stack() {
1832 let mut s = SmartString::<10>::from("abc");
1833 s.extend_from_within(0..2);
1834 assert_eq!(s.as_str(), "abcab");
1835 assert!(s.is_stack());
1836 }
1837
1838 #[test]
1839 fn test_extend_from_within_promotes_to_heap() {
1840 let mut s = SmartString::<4>::from("abc");
1841 s.extend_from_within(..); assert_eq!(s.as_str(), "abcabc");
1843 assert!(s.is_heap());
1844 }
1845
1846 #[test]
1847 fn test_extend_from_within_heap() {
1848 let mut s = SmartString::<2>::from("hello");
1849 assert!(s.is_heap());
1850 s.extend_from_within(1..3);
1851 assert_eq!(s.as_str(), "helloel");
1852 }
1853
1854 #[test]
1855 fn test_extend_from_within_empty_range() {
1856 let mut s = SmartString::<10>::from("abc");
1857 s.extend_from_within(1..1);
1858 assert_eq!(s.as_str(), "abc");
1859 }
1860
1861 #[test]
1862 fn test_extend_from_within_full_range() {
1863 let mut s = SmartString::<10>::from("ab");
1864 s.extend_from_within(..);
1865 assert_eq!(s.as_str(), "abab");
1866 }
1867
1868 #[test]
1869 #[should_panic]
1870 fn test_extend_from_within_non_char_boundary() {
1871 let mut s = SmartString::<10>::from("a€b"); s.extend_from_within(1..3); }
1874
1875 #[test]
1876 #[should_panic]
1877 fn test_extend_from_within_out_of_bounds() {
1878 let mut s = SmartString::<10>::from("abc");
1879 s.extend_from_within(0..10);
1880 }
1881
1882 #[test]
1885 fn test_remove_matches_stack_single() {
1886 let mut s = SmartString::<10>::from("abcabc");
1887 s.remove_matches("b");
1888 assert_eq!(s.as_str(), "acac");
1889 }
1890
1891 #[test]
1892 fn test_remove_matches_stack_multiple() {
1893 let mut s = SmartString::<20>::from("aXbXcX");
1894 s.remove_matches("X");
1895 assert_eq!(s.as_str(), "abc");
1896 }
1897
1898 #[test]
1899 fn test_remove_matches_no_match() {
1900 let mut s = SmartString::<10>::from("hello");
1901 s.remove_matches("xyz");
1902 assert_eq!(s.as_str(), "hello");
1903 }
1904
1905 #[test]
1906 fn test_remove_matches_empty_pattern() {
1907 let mut s = SmartString::<10>::from("hello");
1908 s.remove_matches("");
1909 assert_eq!(s.as_str(), "hello");
1910 }
1911
1912 #[test]
1913 fn test_remove_matches_entire_string() {
1914 let mut s = SmartString::<10>::from("aaa");
1915 s.remove_matches("aaa");
1916 assert_eq!(s.as_str(), "");
1917 }
1918
1919 #[test]
1920 fn test_remove_matches_heap() {
1921 let mut s = SmartString::<2>::from("hello world");
1922 assert!(s.is_heap());
1923 s.remove_matches("o");
1924 assert_eq!(s.as_str(), "hell wrld");
1925 }
1926
1927 #[test]
1928 fn test_remove_matches_multibyte() {
1929 let mut s = SmartString::<20>::from("a€b€c");
1930 s.remove_matches("€");
1931 assert_eq!(s.as_str(), "abc");
1932 }
1933
1934 #[test]
1935 fn test_remove_matches_char_variant() {
1936 let mut s = SmartString::<10>::from("abcabc");
1937 s.remove_matches_char('b');
1938 assert_eq!(s.as_str(), "acac");
1939 }
1940
1941 #[test]
1944 fn test_replace_first_same_length() {
1945 let mut s = SmartString::<10>::from("abcabc");
1946 s.replace_first("b", "X");
1947 assert_eq!(s.as_str(), "aXcabc");
1948 assert!(s.is_stack());
1949 }
1950
1951 #[test]
1952 fn test_replace_first_shorter() {
1953 let mut s = SmartString::<10>::from("abcabc");
1954 s.replace_first("bc", "Z");
1955 assert_eq!(s.as_str(), "aZabc");
1956 }
1957
1958 #[test]
1959 fn test_replace_first_longer_fits_stack() {
1960 let mut s = SmartString::<20>::from("abcabc");
1961 s.replace_first("b", "XYZ");
1962 assert_eq!(s.as_str(), "aXYZcabc");
1963 assert!(s.is_stack());
1964 }
1965
1966 #[test]
1967 fn test_replace_first_longer_promotes_to_heap() {
1968 let mut s = SmartString::<4>::from("abc");
1969 s.replace_first("b", "XXXX"); assert_eq!(s.as_str(), "aXXXXc");
1971 assert!(s.is_heap());
1972 }
1973
1974 #[test]
1975 fn test_replace_first_no_match() {
1976 let mut s = SmartString::<10>::from("hello");
1977 s.replace_first("xyz", "!");
1978 assert_eq!(s.as_str(), "hello");
1979 }
1980
1981 #[test]
1982 fn test_replace_last_gets_last() {
1983 let mut s = SmartString::<10>::from("abcabc");
1984 s.replace_last("b", "X");
1985 assert_eq!(s.as_str(), "abcaXc");
1986 }
1987
1988 #[test]
1989 fn test_replace_first_vs_last() {
1990 let mut s1 = SmartString::<20>::from("aXbXcXd");
1991 let mut s2 = s1.clone();
1992 s1.replace_first("X", ".");
1993 s2.replace_last("X", ".");
1994 assert_eq!(s1.as_str(), "a.bXcXd");
1995 assert_eq!(s2.as_str(), "aXbXc.d");
1996 }
1997
1998 #[test]
1999 fn test_replace_first_at_boundaries() {
2000 let mut s = SmartString::<10>::from("abc");
2001 s.replace_first("a", "X");
2002 assert_eq!(s.as_str(), "Xbc");
2003
2004 let mut s = SmartString::<10>::from("abc");
2005 s.replace_first("c", "X");
2006 assert_eq!(s.as_str(), "abX");
2007 }
2008
2009 #[test]
2010 fn test_replace_first_char_variant() {
2011 let mut s = SmartString::<10>::from("a€b€c");
2012 s.replace_first_char('€', "X");
2013 assert_eq!(s.as_str(), "aXb€c");
2014 }
2015
2016 #[test]
2017 fn test_replace_last_char_variant() {
2018 let mut s = SmartString::<10>::from("a€b€c");
2019 s.replace_last_char('€', "X");
2020 assert_eq!(s.as_str(), "a€bXc");
2021 }
2022
2023 #[test]
2024 fn test_from_utf16_stack_aware() {
2025 let s = SmartString::<4>::from_utf16(&[0x48u16, 0x69]).unwrap();
2027 assert!(s.is_stack());
2028 assert_eq!(s.as_str(), "Hi");
2029 }
2030
2031 #[test]
2032 fn test_from_utf16_lossy_stack_aware() {
2033 let s = SmartString::<4>::from_utf16_lossy(&[0x48u16, 0x69]);
2034 assert!(s.is_stack());
2035 assert_eq!(s.as_str(), "Hi");
2036 }
2037
2038 mod proptest_utf16 {
2041 use proptest::prelude::*;
2042
2043 use super::*;
2044
2045 fn encode_utf16be(s: &str) -> Vec<u8> {
2047 let mut bytes = Vec::new();
2048 for u in s.encode_utf16() {
2049 bytes.extend_from_slice(&u.to_be_bytes());
2050 }
2051 bytes
2052 }
2053
2054 fn encode_utf16le(s: &str) -> Vec<u8> {
2056 let mut bytes = Vec::new();
2057 for u in s.encode_utf16() {
2058 bytes.extend_from_slice(&u.to_le_bytes());
2059 }
2060 bytes
2061 }
2062
2063 fn reference_decode_be(bytes: &[u8]) -> Result<String, ()> {
2065 if bytes.len() % 2 != 0 {
2066 return Err(());
2067 }
2068 let u16s: Vec<u16> = bytes
2069 .chunks_exact(2)
2070 .map(|c| u16::from_be_bytes([c[0], c[1]]))
2071 .collect();
2072 String::from_utf16(&u16s).map_err(|_| ())
2073 }
2074
2075 fn reference_decode_le(bytes: &[u8]) -> Result<String, ()> {
2077 if bytes.len() % 2 != 0 {
2078 return Err(());
2079 }
2080 let u16s: Vec<u16> = bytes
2081 .chunks_exact(2)
2082 .map(|c| u16::from_le_bytes([c[0], c[1]]))
2083 .collect();
2084 String::from_utf16(&u16s).map_err(|_| ())
2085 }
2086
2087 proptest! {
2088 #[test]
2090 fn roundtrip_be(s in "\\PC{0,100}") {
2091 let bytes = encode_utf16be(&s);
2092 let decoded = SmartString::<30>::from_utf16be(&bytes).unwrap();
2093 prop_assert_eq!(decoded.as_str(), s.as_str());
2094 }
2095
2096 #[test]
2097 fn roundtrip_le(s in "\\PC{0,100}") {
2098 let bytes = encode_utf16le(&s);
2099 let decoded = SmartString::<30>::from_utf16le(&bytes).unwrap();
2100 prop_assert_eq!(decoded.as_str(), s.as_str());
2101 }
2102
2103 #[test]
2105 fn matches_reference_be(bytes in proptest::collection::vec(any::<u8>(), 0..200)) {
2106 let ref_result = reference_decode_be(&bytes);
2107 let our_result = SmartString::<30>::from_utf16be(&bytes);
2108 match (ref_result, our_result) {
2109 (Ok(ref_s), Ok(our_s)) => prop_assert_eq!(ref_s.as_str(), our_s.as_str()),
2110 (Err(_), Err(_)) => {} (Ok(_), Err(_)) => {
2112 prop_assert!(false, "reference accepted but we rejected");
2115 }
2116 (Err(_), Ok(_)) => {
2117 prop_assert!(false, "we accepted but reference rejected");
2118 }
2119 }
2120 }
2121
2122 #[test]
2123 fn matches_reference_le(bytes in proptest::collection::vec(any::<u8>(), 0..200)) {
2124 let ref_result = reference_decode_le(&bytes);
2125 let our_result = SmartString::<30>::from_utf16le(&bytes);
2126 match (ref_result, our_result) {
2127 (Ok(ref_s), Ok(our_s)) => prop_assert_eq!(ref_s.as_str(), our_s.as_str()),
2128 (Err(_), Err(_)) => {}
2129 (Ok(_), Err(_)) => prop_assert!(false, "reference accepted but we rejected"),
2130 (Err(_), Ok(_)) => prop_assert!(false, "we accepted but reference rejected"),
2131 }
2132 }
2133
2134 #[test]
2136 fn lossy_be_never_panics(bytes in proptest::collection::vec(any::<u8>(), 0..200)) {
2137 let result = SmartString::<30>::from_utf16be_lossy(&bytes);
2138 prop_assert!(result.len() <= result.as_str().len() + 1); }
2141
2142 #[test]
2143 fn lossy_le_never_panics(bytes in proptest::collection::vec(any::<u8>(), 0..200)) {
2144 let result = SmartString::<30>::from_utf16le_lossy(&bytes);
2145 prop_assert!(result.len() <= result.as_str().len() + 1);
2146 }
2147
2148 #[test]
2150 fn short_strings_on_stack_be(s in "[a-z]{0,10}") {
2151 let bytes = encode_utf16be(&s);
2152 let decoded = SmartString::<30>::from_utf16be(&bytes).unwrap();
2153 prop_assert!(decoded.is_stack(), "expected stack for {:?} (len {})", s, s.len());
2154 }
2155
2156 #[test]
2157 fn short_strings_on_stack_le(s in "[a-z]{0,10}") {
2158 let bytes = encode_utf16le(&s);
2159 let decoded = SmartString::<30>::from_utf16le(&bytes).unwrap();
2160 prop_assert!(decoded.is_stack(), "expected stack for {:?} (len {})", s, s.len());
2161 }
2162 }
2163 }
2164
2165 #[test]
2168 fn test_drain_full_range() {
2169 let mut s = SmartString::<10>::from("hello");
2170 let drained: String = s.drain(..).collect();
2171 assert_eq!(drained, "hello");
2172 assert_eq!(s.as_str(), "");
2173 }
2174
2175 #[test]
2176 fn test_drain_partial_range() {
2177 let mut s = SmartString::<10>::from("abcdef");
2178 let drained: String = s.drain(1..4).collect();
2179 assert_eq!(drained, "bcd");
2180 assert_eq!(s.as_str(), "aef");
2181 }
2182
2183 #[test]
2184 fn test_drain_promotes_stack_to_heap() {
2185 let mut s = SmartString::<10>::from("abc");
2186 assert!(s.is_stack());
2187 let drained: String = s.drain(1..2).collect();
2189 assert_eq!(drained, "b");
2190 assert_eq!(s.as_str(), "ac");
2191 assert!(s.is_heap()); }
2193
2194 #[test]
2195 fn test_drain_empty_range() {
2196 let mut s = SmartString::<10>::from("hello");
2197 let drained: String = s.drain(2..2).collect();
2198 assert_eq!(drained, "");
2199 assert_eq!(s.as_str(), "hello");
2200 }
2201
2202 #[test]
2203 fn test_drain_multibyte() {
2204 let mut s = SmartString::<20>::from("a€b");
2205 let drained: String = s.drain(1..4).collect(); assert_eq!(drained, "€");
2207 assert_eq!(s.as_str(), "ab");
2208 }
2209
2210 #[test]
2213 fn test_into_bytes_stack() {
2214 let s = SmartString::<10>::from("abc");
2215 assert!(s.is_stack());
2216 assert_eq!(s.into_bytes(), b"abc".to_vec());
2217 }
2218
2219 #[test]
2220 fn test_into_bytes_heap() {
2221 let s = SmartString::<2>::from("hello");
2222 assert!(s.is_heap());
2223 assert_eq!(s.into_bytes(), b"hello".to_vec());
2224 }
2225
2226 #[test]
2227 fn test_into_string_stack() {
2228 let s = SmartString::<10>::from("abc");
2229 assert_eq!(s.into_string(), "abc");
2230 }
2231
2232 #[test]
2233 fn test_into_string_heap() {
2234 let s = SmartString::<2>::from("hello");
2235 assert_eq!(s.into_string(), "hello");
2236 }
2237
2238 #[test]
2239 fn test_from_utf8_valid() {
2240 let s = SmartString::<10>::from_utf8(b"hello".to_vec()).unwrap();
2241 assert_eq!(s.as_str(), "hello");
2242 assert!(s.is_heap()); }
2244
2245 #[test]
2246 fn test_from_utf8_invalid() {
2247 let result = SmartString::<10>::from_utf8(vec![0xff, 0xfe]);
2248 assert!(result.is_err());
2249 }
2250
2251 #[test]
2252 fn test_from_utf8_empty() {
2253 let s = SmartString::<10>::from_utf8(Vec::new()).unwrap();
2254 assert_eq!(s.as_str(), "");
2255 }
2256
2257 #[test]
2260 fn test_shrink_to_fit_heap() {
2261 let mut s = SmartString::<4>::from("hello world, this is a long string");
2262 assert!(s.is_heap());
2263 let cap_before = s.capacity();
2264 s.shrink_to_fit();
2265 assert!(s.capacity() <= cap_before);
2266 assert_eq!(s.as_str(), "hello world, this is a long string");
2267 }
2268
2269 #[test]
2270 fn test_shrink_to_fit_stack_noop() {
2271 let mut s = SmartString::<10>::from("ab");
2272 assert!(s.is_stack());
2273 s.shrink_to_fit(); assert_eq!(s.as_str(), "ab");
2275 assert!(s.is_stack());
2276 }
2277
2278 #[test]
2279 fn test_shrink_to_heap() {
2280 let mut s = SmartString::<4>::with_capacity(100);
2281 s.push_str("hello");
2282 assert!(s.is_heap());
2283 let cap_before = s.capacity();
2284 s.shrink_to(10);
2285 assert!(s.capacity() <= cap_before);
2286 assert_eq!(s.as_str(), "hello");
2287 }
2288
2289 #[test]
2290 fn test_shrink_to_stack_noop() {
2291 let mut s = SmartString::<10>::from("ab");
2292 assert!(s.is_stack());
2293 s.shrink_to(0);
2294 assert_eq!(s.as_str(), "ab");
2295 assert!(s.is_stack());
2296 }
2297}