1use core::mem::ManuallyDrop;
12use std::borrow::{Borrow, BorrowMut};
13use std::cmp::Ordering;
14use std::fmt;
15use std::hash::{Hash, Hasher};
16use std::ops::{Deref, DerefMut};
17
18pub trait AnyString {
20 fn as_str(&self) -> &str;
22 fn len(&self) -> usize;
24 fn is_empty(&self) -> bool {
26 self.len() == 0
27 }
28 fn push_str(&mut self, s: &str);
30 fn push(&mut self, ch: char);
32 fn clear(&mut self);
34 fn pop(&mut self) -> Option<char>;
36 fn truncate(&mut self, new_len: usize);
38}
39
40impl AnyString for String {
41 fn as_str(&self) -> &str {
42 self.as_str()
43 }
44 fn len(&self) -> usize {
45 self.len()
46 }
47 fn push_str(&mut self, s: &str) {
48 self.push_str(s);
49 }
50 fn push(&mut self, ch: char) {
51 self.push(ch);
52 }
53 fn clear(&mut self) {
54 self.clear();
55 }
56 fn pop(&mut self) -> Option<char> {
57 self.pop()
58 }
59 fn truncate(&mut self, new_len: usize) {
60 self.truncate(new_len);
61 }
62}
63
64pub struct SmallString<const N: usize> {
75 on_stack: bool,
76 data: StringData<N>,
77}
78
79impl<const N: usize> AnyString for SmallString<N> {
80 fn as_str(&self) -> &str {
81 self.as_str()
82 }
83 fn len(&self) -> usize {
84 self.len()
85 }
86 fn push_str(&mut self, s: &str) {
87 self.push_str(s);
88 }
89 fn push(&mut self, ch: char) {
90 self.push(ch);
91 }
92 fn clear(&mut self) {
93 self.clear();
94 }
95 fn pop(&mut self) -> Option<char> {
96 self.pop()
97 }
98 fn truncate(&mut self, new_len: usize) {
99 self.truncate(new_len);
100 }
101}
102
103union StringData<const N: usize> {
108 stack: ManuallyDrop<heapless::String<N>>,
109 heap: ManuallyDrop<std::string::String>,
110}
111
112impl<const N: usize> SmallString<N> {
113 pub const MAX_STACK_SIZE: usize = 16 * 1024;
115
116 pub fn new() -> Self {
135 const {
137 assert!(
138 std::mem::size_of::<Self>() <= Self::MAX_STACK_SIZE,
139 "SmallString is too large! The struct size exceeds the 16KB limit. Reduce N."
140 );
141 }
142
143 Self {
144 on_stack: true,
145 data: StringData {
146 stack: ManuallyDrop::new(heapless::String::new()),
147 },
148 }
149 }
150
151 pub fn from_str(s: &str) -> Self {
153 let mut str = Self::new();
154 str.push_str(s);
155 str
156 }
157
158 #[inline(always)]
160 pub fn is_on_stack(&self) -> bool {
161 self.on_stack
162 }
163
164 #[inline(always)]
170 pub fn push(&mut self, ch: char) {
171 unsafe {
172 if self.on_stack {
173 let stack_str = &mut *self.data.stack;
175
176 if stack_str.len() + ch.len_utf8() > N {
179 self.spill_to_heap_and_push_char(ch);
180 } else {
181 match stack_str.push(ch) {
185 Ok(()) => return, Err(_) => unreachable!("Stack capacity check failed in push"),
187 }
188 }
189 } else {
190 (*self.data.heap).push(ch);
191 }
192 }
193 }
194
195 #[inline(always)]
199 pub fn push_str(&mut self, s: &str) {
200 unsafe {
201 if self.on_stack {
202 let stack_str = &mut *self.data.stack;
203
204 if stack_str.len() + s.len() > N {
207 self.spill_to_heap_and_push_str(s);
208 } else {
209 match stack_str.push_str(s) {
211 Ok(()) => return, Err(_) => unreachable!("Stack capacity check failed in push str"),
213 }
214 }
215 } else {
216 (*self.data.heap).push_str(s);
217 }
218 }
219 }
220
221 #[inline(always)]
223 pub fn len(&self) -> usize {
224 unsafe {
227 if self.on_stack {
228 self.data.stack.len()
229 } else {
230 self.data.heap.len()
231 }
232 }
233 }
234
235 #[inline(always)]
237 pub fn is_empty(&self) -> bool {
238 self.len() == 0
239 }
240
241 #[inline(always)]
246 pub fn clear(&mut self) {
247 unsafe {
248 if self.on_stack {
249 (*self.data.stack).clear();
250 } else {
251 (*self.data.heap).clear();
252 }
253 }
254 }
255
256 #[inline(never)]
259 unsafe fn spill_to_heap_and_push_str(&mut self, pending_str: &str) {
260 unsafe {
261 let stack_bytes = self.data.stack.as_bytes();
263 let pending_bytes = pending_str.as_bytes();
264
265 let total_len = stack_bytes.len() + pending_bytes.len();
268 let cap = std::cmp::max(total_len, N * 2);
269
270 let mut heap_vec = Vec::with_capacity(cap);
273
274 heap_vec.extend_from_slice(stack_bytes);
277 heap_vec.extend_from_slice(pending_bytes);
278
279 let new_heap = String::from_utf8_unchecked(heap_vec);
283
284 ManuallyDrop::drop(&mut self.data.stack);
286 self.data.heap = ManuallyDrop::new(new_heap);
287 self.on_stack = false;
288 }
289 }
290
291 #[inline(never)]
292 unsafe fn spill_to_heap_and_push_char(&mut self, pending_char: char) {
293 unsafe {
294 let stack_bytes = self.data.stack.as_bytes();
295
296 let cap = std::cmp::max(stack_bytes.len() + 4, N * 2);
298 let mut heap_vec = Vec::with_capacity(cap);
299
300 heap_vec.extend_from_slice(stack_bytes);
301
302 let char_len = pending_char.len_utf8();
305 let old_len = heap_vec.len();
306
307 heap_vec.set_len(old_len + char_len);
309 pending_char.encode_utf8(&mut heap_vec[old_len..]);
310
311 let new_heap = String::from_utf8_unchecked(heap_vec);
312
313 ManuallyDrop::drop(&mut self.data.stack);
314 self.data.heap = ManuallyDrop::new(new_heap);
315 self.on_stack = false;
316 }
317 }
318}
319
320impl<const N: usize> Deref for SmallString<N> {
325 type Target = str;
326
327 fn deref(&self) -> &Self::Target {
328 unsafe {
329 if self.on_stack {
330 self.data.stack.as_str()
331 } else {
332 self.data.heap.as_str()
333 }
334 }
335 }
336}
337
338impl<const N: usize> DerefMut for SmallString<N> {
339 fn deref_mut(&mut self) -> &mut Self::Target {
340 unsafe {
341 if self.on_stack {
342 (*self.data.stack).as_mut_str()
343 } else {
344 (*self.data.heap).as_mut_str()
345 }
346 }
347 }
348}
349
350impl<const N: usize> SmallString<N> {
351 #[inline(always)]
355 pub fn as_str(&self) -> &str {
356 &**self
358 }
359
360 #[inline(always)]
364 pub fn as_mut_str(&mut self) -> &mut str {
365 &mut **self
367 }
368
369 #[inline(always)]
371 pub fn as_bytes(&self) -> &[u8] {
372 self.as_str().as_bytes()
373 }
374
375 #[inline(always)]
381 pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
382 unsafe { self.as_mut_str().as_bytes_mut() }
383 }
384}
385
386impl<const N: usize> Clone for SmallString<N> {
391 fn clone(&self) -> Self {
392 unsafe {
393 if self.on_stack {
394 let stack_clone = (*self.data.stack).clone();
396 SmallString {
397 on_stack: true,
398 data: StringData {
399 stack: ManuallyDrop::new(stack_clone),
400 },
401 }
402 } else {
403 let heap_clone = (*self.data.heap).clone();
405 SmallString {
406 on_stack: false,
407 data: StringData {
408 heap: ManuallyDrop::new(heap_clone),
409 },
410 }
411 }
412 }
413 }
414}
415
416impl<const N: usize> Drop for SmallString<N> {
417 fn drop(&mut self) {
418 unsafe {
419 if self.on_stack {
420 ManuallyDrop::drop(&mut self.data.stack);
421 } else {
422 ManuallyDrop::drop(&mut self.data.heap);
423 }
424 }
425 }
426}
427
428impl<const N: usize> Default for SmallString<N> {
429 fn default() -> Self {
430 Self::new()
431 }
432}
433
434impl<const N: usize> fmt::Display for SmallString<N> {
435 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
436 fmt::Display::fmt(self.as_str(), f) }
438}
439
440impl<const N: usize> fmt::Debug for SmallString<N> {
441 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
442 fmt::Debug::fmt(self.as_str(), f)
443 }
444}
445
446impl<const N: usize> From<&str> for SmallString<N> {
448 fn from(s: &str) -> Self {
449 Self::from_str(s)
450 }
451}
452
453impl<const N: usize> fmt::Write for SmallString<N> {
455 fn write_str(&mut self, s: &str) -> fmt::Result {
456 self.push_str(s);
457 Ok(())
458 }
459}
460
461impl<const N: usize, const M: usize> PartialEq<SmallString<M>> for SmallString<N> {
466 fn eq(&self, other: &SmallString<M>) -> bool {
467 self.as_str() == other.as_str()
468 }
469}
470
471impl<const N: usize> Eq for SmallString<N> {}
473
474impl<const N: usize> PartialEq<str> for SmallString<N> {
477 fn eq(&self, other: &str) -> bool {
478 self.as_str() == other
479 }
480}
481
482impl<'a, const N: usize> PartialEq<&'a str> for SmallString<N> {
484 fn eq(&self, other: &&'a str) -> bool {
485 self.as_str() == *other
486 }
487}
488
489impl<const N: usize> PartialEq<SmallString<N>> for &str {
490 fn eq(&self, other: &SmallString<N>) -> bool {
491 *self == other.as_str()
492 }
493}
494
495impl<const N: usize> PartialEq<String> for SmallString<N> {
496 fn eq(&self, other: &String) -> bool {
497 self.as_str() == other.as_str()
498 }
499}
500
501impl<const N: usize, const M: usize> PartialOrd<SmallString<M>> for SmallString<N> {
505 fn partial_cmp(&self, other: &SmallString<M>) -> Option<Ordering> {
506 Some(self.as_str().cmp(other.as_str()))
507 }
508}
509
510impl<const N: usize> Ord for SmallString<N> {
511 fn cmp(&self, other: &Self) -> Ordering {
512 self.as_str().cmp(other.as_str())
513 }
514}
515
516impl<const N: usize> Hash for SmallString<N> {
519 fn hash<H: Hasher>(&self, state: &mut H) {
520 self.as_str().hash(state);
523 }
524}
525
526impl<const N: usize> Borrow<str> for SmallString<N> {
529 fn borrow(&self) -> &str {
530 self.as_str()
531 }
532}
533
534impl<const N: usize> BorrowMut<str> for SmallString<N> {
535 fn borrow_mut(&mut self) -> &mut str {
536 self.as_mut_str()
537 }
538}
539
540impl<const N: usize> AsRef<str> for SmallString<N> {
541 fn as_ref(&self) -> &str {
542 self.as_str()
543 }
544}
545
546impl<const N: usize> AsRef<[u8]> for SmallString<N> {
547 fn as_ref(&self) -> &[u8] {
548 self.as_bytes()
549 }
550}
551
552impl<const N: usize> FromIterator<char> for SmallString<N> {
556 fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Self {
557 let mut s = Self::new();
558 for c in iter {
559 s.push(c); }
561 s
562 }
563}
564
565impl<'a, const N: usize> FromIterator<&'a str> for SmallString<N> {
567 fn from_iter<I: IntoIterator<Item = &'a str>>(iter: I) -> Self {
568 let mut s = Self::new();
569 for str_slice in iter {
570 s.push_str(str_slice);
571 }
572 s
573 }
574}
575
576impl<const N: usize> Extend<char> for SmallString<N> {
578 fn extend<I: IntoIterator<Item = char>>(&mut self, iter: I) {
579 for c in iter {
580 self.push(c);
581 }
582 }
583}
584
585impl<'a, const N: usize> Extend<&'a str> for SmallString<N> {
587 fn extend<I: IntoIterator<Item = &'a str>>(&mut self, iter: I) {
588 for str_slice in iter {
589 self.push_str(str_slice);
590 }
591 }
592}
593
594impl<const N: usize> SmallString<N> {
597 #[inline(always)]
600 pub fn pop(&mut self) -> Option<char> {
601 unsafe {
602 if self.on_stack {
603 (*self.data.stack).pop()
604 } else {
605 (*self.data.heap).pop()
606 }
607 }
608 }
609
610 #[inline(always)]
614 pub fn truncate(&mut self, new_len: usize) {
615 unsafe {
616 if self.on_stack {
617 (*self.data.stack).truncate(new_len);
618 } else {
619 (*self.data.heap).truncate(new_len);
620 }
621 }
622 }
623
624 #[inline(always)]
626 pub fn capacity(&self) -> usize {
627 unsafe {
628 if self.on_stack {
629 N } else {
631 (*self.data.heap).capacity()
632 }
633 }
634 }
635}
636
637impl<const N: usize> SmallString<N> {
640 #[inline(always)]
643 pub fn reserve(&mut self, additional: usize) {
644 unsafe {
645 if self.on_stack {
646 let len = self.len();
647 if len + additional > N {
648 let stack_bytes = self.data.stack.as_bytes();
654
655 let cap = std::cmp::max(len + additional, N * 2);
657 let mut heap_vec = Vec::with_capacity(cap);
658 heap_vec.extend_from_slice(stack_bytes);
659
660 let new_heap = String::from_utf8_unchecked(heap_vec);
661
662 ManuallyDrop::drop(&mut self.data.stack);
664 self.data.heap = ManuallyDrop::new(new_heap);
665 self.on_stack = false;
666 }
667 } else {
669 (*self.data.heap).reserve(additional);
670 }
671 }
672 }
673}
674
675impl<const N: usize> SmallString<N> {
678 pub fn from_utf8(vec: Vec<u8>) -> Result<Self, std::string::FromUtf8Error> {
683 let s_std = String::from_utf8(vec)?;
685
686 if s_std.len() <= N {
688 let mut small = Self::new();
689 small.push_str(&s_std);
690 Ok(small)
691 } else {
692 Ok(Self {
694 on_stack: false,
695 data: StringData {
696 heap: ManuallyDrop::new(s_std),
697 },
698 })
699 }
700 }
701
702 pub fn from_utf8_lossy(bytes: &[u8]) -> Self {
704 let cow = String::from_utf8_lossy(bytes);
705 match cow {
706 std::borrow::Cow::Borrowed(s) => Self::from(s),
708 std::borrow::Cow::Owned(s) => {
710 if s.len() <= N {
711 Self::from(s.as_str())
712 } else {
713 Self {
714 on_stack: false,
715 data: StringData {
716 heap: ManuallyDrop::new(s),
717 },
718 }
719 }
720 }
721 }
722 }
723}
724
725impl<const N: usize> SmallString<N> {
728 pub fn into_string(self) -> String {
733 let mut this = ManuallyDrop::new(self);
735
736 unsafe {
737 if this.on_stack {
738 let stack_str = &*this.data.stack;
739 stack_str.as_str().to_string()
740 } else {
741 ManuallyDrop::take(&mut this.data.heap)
743 }
744 }
745 }
746
747 pub fn into_bytes(self) -> Vec<u8> {
749 self.into_string().into_bytes()
750 }
751}
752
753impl<const N: usize> SmallString<N> {
756 pub fn retain<F>(&mut self, mut f: F)
759 where
760 F: FnMut(char) -> bool,
761 {
762 unsafe {
763 if self.on_stack {
764 let stack_str = &mut *self.data.stack;
770 let mut temp: heapless::String<N> = heapless::String::new();
777 for c in stack_str.chars() {
778 if f(c) {
779 match temp.push(c) {
780 Ok(()) => continue,
781 Err(_) => unreachable!("temp string capacity check failed in push"),
782 }
783 }
784 }
785 *stack_str = temp;
786 } else {
787 (*self.data.heap).retain(f);
788 }
789 }
790 }
791}
792
793impl<const N: usize> SmallString<N> {
796 pub fn shrink_to_fit(&mut self) {
799 unsafe {
800 if !self.on_stack {
801 (*self.data.heap).shrink_to_fit();
802 }
803 }
804 }
805}
806
807#[cfg(test)]
808mod string_basic_tests {
809
810 use super::*;
811 use std::borrow::Borrow;
812 use std::collections::HashSet;
813 use std::collections::hash_map::DefaultHasher;
814 use std::fmt::Write; use std::hash::{Hash, Hasher};
816
817 #[test]
818 fn test_string_traits_borrow() {
819 use std::borrow::{Borrow, BorrowMut};
820 let mut s: SmallString<16> = SmallString::from("abc");
821
822 let b: &str = s.borrow();
824 assert_eq!(b, "abc");
825
826 let b_mut: &mut str = s.borrow_mut();
828 b_mut.make_ascii_uppercase();
829 assert_eq!(s.as_str(), "ABC");
830 }
831
832 #[test]
833 fn test_string_stack_ops_basic() {
834 let mut s: SmallString<16> = SmallString::new();
835
836 assert!(s.is_on_stack());
837 assert!(s.is_empty());
838
839 s.push_str("Hello");
840 assert_eq!(s.len(), 5);
841 assert_eq!(s.as_str(), "Hello");
842 assert!(s.is_on_stack());
843
844 s.push(' ');
845 s.push_str("World");
846 assert_eq!(s.len(), 11);
847 assert_eq!(&*s, "Hello World"); assert!(s.is_on_stack());
849 }
850
851 #[test]
853 fn test_string_spill_trigger_on_exact_capacity() {
854 let mut s: SmallString<5> = SmallString::new();
855
856 s.push_str("12345");
858
859 assert_eq!(s.len(), 5);
860 assert!(s.is_on_stack(), "Should remain on stack at exact capacity");
861
862 s.push('6');
864 assert!(!s.is_on_stack(), "Should spill after N+1");
865 assert_eq!(s.as_str(), "123456");
866 }
867
868 #[test]
870 fn test_string_spill_trigger_on_push_str() {
871 let mut s: SmallString<4> = SmallString::new();
872 s.push_str("Hi"); s.push_str(" there"); assert!(!s.is_on_stack());
878 assert_eq!(s.as_str(), "Hi there");
879 assert_eq!(s.len(), 8);
880 }
881
882 #[test]
884 fn test_string_spill_trigger_on_push_char() {
885 let mut s: SmallString<3> = SmallString::new();
886 s.push('A');
887 s.push('B');
888 s.push('C'); s.push('D'); assert!(!s.is_on_stack());
893 assert_eq!(s.as_str(), "ABCD");
894 }
895
896 #[test]
898 fn test_string_spill_trigger_on_multibyte_char() {
899 let mut s: SmallString<6> = SmallString::new();
903
904 s.push('🦀'); assert!(s.is_on_stack());
906 assert_eq!(s.len(), 4);
907
908 s.push('🚀');
911
912 assert!(!s.is_on_stack());
913 assert_eq!(s.as_str(), "🦀🚀");
914 assert_eq!(s.len(), 8);
915 }
916
917 #[test]
918 fn test_string_spill_trigger_on_multibyte_str() {
919 let mut s: SmallString<5> = SmallString::new();
921 s.push_str("hi");
922
923 s.push_str("_👋");
926
927 assert!(!s.is_on_stack());
928 assert_eq!(s.as_str(), "hi_👋");
929 }
930
931 #[test]
933 fn test_string_traits_fmt_write() {
934 let mut s: SmallString<16> = SmallString::new();
935
936 write!(s, "Value: {}", 100).unwrap();
938 assert_eq!(s.as_str(), "Value: 100");
939 assert!(s.is_on_stack());
940
941 write!(s, " and a very long suffix to force spill").unwrap();
943 assert!(!s.is_on_stack());
944 assert!(s.contains("Value: 100"));
945 assert!(s.contains("suffix"));
946 }
947
948 #[test]
950 fn test_string_spill_trigger_on_large_growth() {
951 let mut s: SmallString<4> = SmallString::new();
952 s.push_str("12");
953
954 let huge_chunk = "a".repeat(100);
957 s.push_str(&huge_chunk);
958
959 assert!(!s.is_on_stack());
960 assert_eq!(s.len(), 102);
961 assert!(s.ends_with("aaaa"));
962 }
963
964 #[test]
966 fn test_string_any_storage_zero_capacity() {
967 let mut s: SmallString<0> = SmallString::new();
969
970 assert!(s.is_on_stack()); s.push('a');
973 assert!(!s.is_on_stack()); assert_eq!(s.as_str(), "a");
975 }
976
977 #[test]
979 fn test_string_any_storage_clear_reuse() {
980 let mut s: SmallString<4> = SmallString::new();
981
982 s.push_str("abc");
984 s.clear();
985 assert!(s.is_empty());
986 assert!(s.is_on_stack());
987 s.push_str("xyz");
988 assert_eq!(s.as_str(), "xyz");
989
990 s.push_str("12345"); assert!(!s.is_on_stack());
993
994 s.clear();
995 assert!(s.is_empty());
996 assert!(!s.is_on_stack()); s.push_str("HeapReuse");
1000 assert_eq!(s.as_str(), "HeapReuse");
1001 }
1002
1003 #[test]
1005 fn test_string_traits_deref_methods() {
1006 let s: SmallString<10> = SmallString::from("hello");
1007
1008 assert!(s.starts_with("he"));
1010 assert!(s.contains("ll"));
1011 assert_eq!(s.to_uppercase(), "HELLO"); assert_eq!(s.find('o'), Some(4));
1013 }
1014
1015 #[test]
1016 fn test_string_traits_clone_on_stack() {
1017 let mut original: SmallString<16> = SmallString::new();
1019 original.push_str("Hello");
1020
1021 assert!(original.is_on_stack());
1022
1023 let mut copy = original.clone();
1025
1026 assert!(copy.is_on_stack());
1028 assert_eq!(original.as_str(), copy.as_str());
1029
1030 copy.push_str(" World");
1032
1033 assert_eq!(original.as_str(), "Hello");
1034 assert_eq!(copy.as_str(), "Hello World");
1035 }
1036
1037 #[test]
1038 fn test_string_traits_clone_on_heap() {
1039 let mut original: SmallString<4> = SmallString::new();
1041 original.push_str("Hello");
1042
1043 assert!(!original.is_on_stack());
1044
1045 let mut copy = original.clone();
1047
1048 assert!(!copy.is_on_stack());
1050 assert_eq!(original.as_str(), copy.as_str());
1051
1052 copy.push_str(" World");
1054
1055 assert_eq!(original.as_str(), "Hello");
1056 assert_eq!(copy.as_str(), "Hello World");
1057 }
1058
1059 fn calculate_hash<T: Hash>(t: &T) -> u64 {
1061 let mut s = DefaultHasher::new();
1062 t.hash(&mut s);
1063 s.finish()
1064 }
1065
1066 #[test]
1068 fn test_string_traits_equality() {
1069 let s1: SmallString<16> = SmallString::from("hello");
1071 let s2: SmallString<16> = SmallString::from("hello");
1072 let s3: SmallString<16> = SmallString::from("world");
1073 assert_eq!(s1, s2);
1074 assert_ne!(s1, s3);
1075
1076 let h1: SmallString<2> = SmallString::from("hello");
1078 let h2: SmallString<2> = SmallString::from("hello");
1079 assert_eq!(h1, h2);
1080 assert!(!h1.is_on_stack());
1081
1082 assert_eq!(s1, h1);
1085
1086 assert_eq!(s1, "hello");
1088 assert_eq!("hello", s1);
1089 assert_eq!(s1, String::from("hello"));
1090 }
1091
1092 #[test]
1093 fn test_string_traits_ordering() {
1094 let apple: SmallString<16> = SmallString::from("Apple");
1095 let banana: SmallString<16> = SmallString::from("Banana");
1096
1097 assert!(apple < banana);
1099
1100 let mut list = vec![banana.clone(), apple.clone()];
1102 list.sort();
1103
1104 assert_eq!(list[0], "Apple");
1105 assert_eq!(list[1], "Banana");
1106 }
1107
1108 #[test]
1110 fn test_string_traits_hashing() {
1111 let s_stack: SmallString<16> = SmallString::from("testing");
1112 let s_heap: SmallString<2> = SmallString::from("testing"); let s_std: String = String::from("testing");
1114 let s_str: &str = "testing";
1115
1116 let h1 = calculate_hash(&s_stack);
1119 let h2 = calculate_hash(&s_heap);
1120 let h3 = calculate_hash(&s_std);
1121 let h4 = calculate_hash(&s_str);
1122
1123 assert_eq!(h1, h2, "Stack and Heap hashing differ!");
1124 assert_eq!(h1, h3, "SmallString hash differs from String hash!");
1125 assert_eq!(h1, h4, "SmallString hash differs from &str hash!");
1126
1127 let mut set = HashSet::new();
1129 set.insert(s_stack.clone());
1130
1131 assert!(set.contains("testing")); assert!(set.contains(s_heap.as_str())); }
1134
1135 #[test]
1137 fn test_string_traits_borrow_as_ref() {
1138 let s: SmallString<16> = SmallString::from("hello");
1139
1140 fn takes_str(_: &str) {}
1142 takes_str(s.as_ref());
1143 takes_str(s.borrow());
1144
1145 fn takes_as_ref<T: AsRef<str>>(_: T) {}
1147 takes_as_ref(s); }
1149
1150 #[test]
1152 fn test_string_traits_from_iterator() {
1153 let chars = vec!['a', 'b', 'c'];
1155 let s_stack: SmallString<16> = chars.into_iter().collect();
1156 assert_eq!(s_stack, "abc");
1157 assert!(s_stack.is_on_stack());
1158
1159 let many_chars = vec!['a'; 100];
1161 let s_heap: SmallString<16> = many_chars.into_iter().collect();
1162 assert_eq!(s_heap.len(), 100);
1163 assert!(!s_heap.is_on_stack());
1164
1165 let strings = vec!["Hello", " ", "World"];
1167 let s_str: SmallString<32> = strings.into_iter().collect();
1168 assert_eq!(s_str, "Hello World");
1169 }
1170
1171 #[test]
1172 fn test_string_traits_extend() {
1173 let mut s: SmallString<4> = SmallString::new();
1174
1175 s.extend(vec!['H', 'i']);
1177 assert_eq!(s, "Hi");
1178 assert!(s.is_on_stack());
1179
1180 s.extend(vec!['!'; 20]);
1182 assert!(!s.is_on_stack());
1183 assert_eq!(s.len(), 22);
1184 }
1185
1186 #[test]
1188 fn test_string_any_storage_pop() {
1189 let mut s: SmallString<16> = SmallString::from("abc");
1191 assert_eq!(s.pop(), Some('c'));
1192 assert_eq!(s.pop(), Some('b'));
1193 assert_eq!(s, "a");
1194
1195 let mut h: SmallString<2> = SmallString::from("abc"); assert!(!h.is_on_stack());
1198 assert_eq!(h.pop(), Some('c'));
1199 assert_eq!(h, "ab");
1200
1201 let mut empty: SmallString<4> = SmallString::new();
1203 assert_eq!(empty.pop(), None);
1204 }
1205
1206 #[test]
1207 fn test_string_any_storage_truncate_v2() {
1208 let mut s: SmallString<16> = SmallString::from("Hello World");
1210 s.truncate(5);
1211 assert_eq!(s, "Hello");
1212 assert_eq!(s.len(), 5);
1213
1214 s.truncate(100);
1216 assert_eq!(s, "Hello");
1217
1218 let mut h: SmallString<2> = SmallString::from("Hello World"); assert!(!h.is_on_stack());
1221 h.truncate(2);
1222 assert_eq!(h, "He");
1223 }
1224
1225 #[test]
1226 #[should_panic]
1227 fn test_string_any_storage_truncate_panic() {
1228 let mut s: SmallString<16> = SmallString::from("🦀"); s.truncate(2);
1231 }
1232
1233 #[test]
1235 fn test_string_any_storage_reserve() {
1236 let mut s: SmallString<16> = SmallString::new();
1238 s.push_str("Hi");
1239 s.reserve(4); assert!(s.is_on_stack());
1241
1242 s.reserve(20);
1246
1247 assert!(!s.is_on_stack());
1248 assert_eq!(s.as_str(), "Hi");
1249 assert!(s.capacity() >= 22); s.reserve(100);
1255
1256 assert!(s.capacity() >= 102);
1258 }
1259
1260 #[test]
1261 fn test_string_any_storage_into_string() {
1262 let s_stack: SmallString<16> = SmallString::from("stack");
1264 let std_str = s_stack.into_string();
1265 assert_eq!(std_str, "stack");
1266 let _: String = std_str;
1268
1269 let s_heap: SmallString<2> = SmallString::from("heap");
1271 let std_str2 = s_heap.into_string();
1272 assert_eq!(std_str2, "heap");
1273 }
1274
1275 #[test]
1276 fn test_string_any_storage_from_utf8() {
1277 let bytes = vec![104, 101, 108, 108, 111]; let s: SmallString<16> = SmallString::from_utf8(bytes.clone()).unwrap();
1281 assert!(s.is_on_stack());
1282 assert_eq!(s, "hello");
1283
1284 let s2: SmallString<2> = SmallString::from_utf8(bytes).unwrap();
1286 assert!(!s2.is_on_stack());
1287 assert_eq!(s2, "hello");
1288
1289 let invalid = vec![0, 159, 146, 150];
1291 assert!(SmallString::<16>::from_utf8(invalid).is_err());
1292 }
1293
1294 #[test]
1295 fn test_string_any_storage_retain() {
1296 let mut s: SmallString<16> = SmallString::from("AbCdEf");
1298 s.retain(|c| c.is_lowercase());
1299 assert_eq!(s, "bdf");
1300 assert!(s.is_on_stack());
1301
1302 let mut h: SmallString<2> = SmallString::from("AbCdEf"); h.retain(|c| c.is_uppercase());
1305 assert_eq!(h, "ACE");
1306 assert!(!h.is_on_stack());
1307 }
1308
1309 #[test]
1310 fn test_string_any_storage_into_bytes() {
1311 let s: SmallString<16> = SmallString::from("ABC");
1312 let bytes = s.into_bytes();
1313 assert_eq!(bytes, vec![65, 66, 67]);
1314 }
1315
1316 #[test]
1317 fn test_string_any_storage_from_utf8_lossy() {
1318 let bytes = b"hello \xF0\x90\x80world"; let s: SmallString<16> = SmallString::from_utf8_lossy(bytes);
1320 assert!(s.contains("hello "));
1321 assert!(s.contains("world"));
1322 assert!(s.is_on_stack());
1323
1324 let huge_invalid = b"a".repeat(100);
1325 let s2: SmallString<16> = SmallString::from_utf8_lossy(&huge_invalid);
1326 assert!(!s2.is_on_stack());
1327 }
1328
1329 #[test]
1330 fn test_string_any_storage_as_bytes_mut() {
1331 let mut s: SmallString<16> = SmallString::from("abc");
1332 unsafe {
1333 let bytes = s.as_bytes_mut();
1334 bytes[0] = b'z';
1335 }
1336 assert_eq!(s, "zbc");
1337 }
1338
1339 #[test]
1340 fn test_string_traits_debug_display() {
1341 let h: SmallString<2> = SmallString::from("a");
1342 let debug = format!("{:?}", h);
1343 assert_eq!(debug, "\"a\"");
1344 let display = format!("{}", h);
1345 assert_eq!(display, "a");
1346 }
1347
1348 #[test]
1349 fn test_string_any_storage_truncate() {
1350 let mut s: SmallString<16> = SmallString::from("hello");
1352 s.truncate(2);
1353 assert_eq!(s, "he");
1354
1355 let mut h: SmallString<2> = SmallString::from("abc");
1357 h.truncate(1);
1358 assert_eq!(h, "a");
1359 }
1360
1361 #[test]
1362 fn test_string_any_storage_deref_mut() {
1363 let mut s_mut = SmallString::<16>::from("abc");
1364 s_mut.as_mut_str().make_ascii_uppercase();
1365 assert_eq!(s_mut, "ABC");
1366
1367 let mut h: SmallString<1> = SmallString::from_str("abc");
1368 h.as_mut_str().make_ascii_uppercase();
1369 assert_eq!(h, "ABC");
1370 }
1371
1372 #[test]
1373 fn test_string_any_storage_partial_eq_variants() {
1374 let h: SmallString<1> = SmallString::from_str("abc");
1375 assert!(h == "abc");
1376 let s_ref = "abc";
1377 assert!(h == s_ref);
1378 }
1379
1380 #[test]
1381 fn test_string_any_storage_extend_char() {
1382 let mut h: SmallString<1> = SmallString::from_str("abc");
1383 h.extend(['!', '?'].iter().cloned());
1384 assert_eq!(h, "abc!?");
1385 }
1386
1387 #[test]
1388 fn test_string_any_storage_from_utf8_long() {
1389 let long_bytes = b"a".repeat(100);
1390 let s_long = SmallString::<16>::from_utf8(long_bytes).unwrap();
1391 assert!(!s_long.is_on_stack());
1392 }
1393}
1394
1395#[cfg(test)]
1396mod string_coverage_tests {
1397 use super::*;
1398
1399 #[test]
1400 fn test_any_string_trait_std_string_implementation() {
1401 let mut s = String::from("abc");
1402 let any: &mut dyn AnyString = &mut s;
1403 assert_eq!(any.as_str(), "abc");
1404 assert_eq!(any.len(), 3);
1405 assert!(!any.is_empty());
1406 any.push('d');
1407 any.push_str("ef");
1408 assert_eq!(any.as_str(), "abcdef");
1409 assert_eq!(any.pop(), Some('f'));
1410 any.truncate(3);
1411 assert_eq!(any.as_str(), "abc");
1412 any.clear();
1413 assert!(any.is_empty());
1414 }
1415
1416 #[test]
1417 fn test_any_string_trait_small_string_implementation() {
1418 let mut s: SmallString<4> = SmallString::new();
1419 let any: &mut dyn AnyString = &mut s;
1420 any.push_str("abc");
1421 assert_eq!(any.len(), 3);
1422 any.push('d');
1423 assert_eq!(any.as_str(), "abcd");
1424 any.pop();
1425 any.truncate(1);
1426 any.clear();
1427 assert!(any.is_empty());
1428 }
1429
1430 #[test]
1431 fn test_small_string_heap_storage_utf8_validity_and_retain() {
1432 let mut s: SmallString<2> = SmallString::new();
1433 s.push_str("abc"); assert!(!s.is_on_stack());
1435 assert!(s.capacity() >= 2);
1436
1437 let long_vec = vec![b'a'; 10];
1439 let s2 = SmallString::<2>::from_utf8(long_vec).unwrap();
1440 assert!(!s2.is_on_stack());
1441
1442 let s3 = SmallString::<2>::from_utf8_lossy(&[b'a'; 10]);
1444 assert!(!s3.is_on_stack());
1445
1446 let mut s4: SmallString<2> = SmallString::from("abcde");
1448 s4.retain(|c| c != 'b');
1449 assert_eq!(s4.as_str(), "acde");
1450
1451 s4.shrink_to_fit();
1453 }
1454
1455 #[test]
1456 fn test_small_string_formatting_comparison_and_extension() {
1457 let mut s: SmallString<4> = SmallString::from("abc");
1458
1459 assert_eq!(format!("{}", s), "abc");
1461 assert_eq!(format!("{:?}", s), "\"abc\"");
1462
1463 let s2 = SmallString::<8>::from("abc");
1465 assert_eq!(s, s2);
1466 assert_eq!(s, "abc");
1467 assert_eq!("abc", s);
1468 assert_eq!(s, String::from("abc"));
1469
1470 let s3 = SmallString::<4>::from("abd");
1471 assert!(s < s3);
1472 assert_eq!(s.cmp(&s3), std::cmp::Ordering::Less);
1473
1474 let mut h = std::collections::hash_map::DefaultHasher::new();
1476 s.hash(&mut h);
1477
1478 let b: &str = s.borrow();
1480 assert_eq!(b, "abc");
1481 let b_mut: &mut str = s.borrow_mut();
1482 b_mut.make_ascii_uppercase();
1483 assert_eq!(s.as_str(), "ABC");
1484
1485 let r: &str = s.as_ref();
1486 assert_eq!(r, "ABC");
1487 let r_bytes: &[u8] = s.as_ref();
1488 assert_eq!(r_bytes, b"ABC");
1489
1490 let mut s4: SmallString<4> = SmallString::new();
1492 s4.extend(['a', 'b']);
1493 s4.extend(["cd", "ef"]); assert_eq!(s4.as_str(), "abcdef");
1495
1496 let s5 = SmallString::<4>::from_iter(['x', 'y']);
1497 assert_eq!(s5.as_str(), "xy");
1498 let s6 = SmallString::<4>::from_iter(["hi", "ho"]);
1499 assert_eq!(s6.as_str(), "hiho");
1500 }
1501
1502 #[test]
1503 fn test_small_string_fmt_write_trait() {
1504 use std::fmt::Write;
1505 let mut s: SmallString<4> = SmallString::new();
1506 write!(s, "{}", 12345).unwrap();
1507 assert_eq!(s.as_str(), "12345");
1508 }
1509}