1extern crate alloc;
2
3use alloc::{string::String, vec::Vec};
4use core::str::FromStr;
5
6#[derive(Clone)]
45pub enum SmallString<const N: usize> {
46 Inline(heapless::String<N>),
48 Heap(alloc::string::String),
50}
51
52impl<const N: usize> SmallString<N> {
53 #[inline]
57 pub fn new() -> Self {
58 SmallString::Inline(heapless::String::new())
59 }
60
61 #[inline]
63 pub const fn inline_size() -> usize {
64 N
65 }
66
67 #[allow(clippy::should_implement_trait)]
86 #[inline]
87 pub fn from_str(s: &str) -> Self {
88 let mut out = Self::new();
89 out.push_str(s);
90 out
91 }
92
93 #[inline]
101 pub fn from_string(s: String) -> Self {
102 if s.capacity() == 0 { Self::new() } else { Self::Heap(s) }
103 }
104
105 #[inline]
113 pub fn from_small_string<const M: usize>(input: SmallString<M>) -> Self {
114 match input {
115 SmallString::Heap(s) if s.len() <= N => SmallString::from_str(&s),
116 SmallString::Heap(s) => SmallString::Heap(s),
117 SmallString::Inline(s) => SmallString::from_str(s.as_str()),
118 }
119 }
120
121 #[inline]
130 pub fn from_utf8(bytes: Vec<u8>) -> Result<Self, alloc::string::FromUtf8Error> {
131 let s = String::from_utf8(bytes)?;
132 Ok(if s.len() <= N {
133 Self::from_str(&s)
134 } else {
135 Self::from_string(s)
136 })
137 }
138
139 #[inline]
148 pub fn from_utf8_slice(bytes: &[u8]) -> Result<Self, core::str::Utf8Error> {
149 Ok(Self::from_str(core::str::from_utf8(bytes)?))
150 }
151
152 #[inline]
160 pub fn from_utf8_lossy(bytes: &[u8]) -> Self {
161 let mut out = Self::new();
162 for chunk in bytes.utf8_chunks() {
163 out.push_str(chunk.valid());
164 if !chunk.invalid().is_empty() {
165 out.push('\u{FFFD}');
166 }
167 }
168 out
169 }
170
171 #[inline]
174 pub fn is_inline(&self) -> bool {
175 matches!(self, SmallString::Inline(_))
176 }
177
178 #[inline]
183 pub fn capacity(&self) -> usize {
184 match self {
185 SmallString::Inline(_) => N,
186 SmallString::Heap(str) => str.capacity(),
187 }
188 }
189
190 #[inline]
193 pub fn len(&self) -> usize {
194 match self {
195 SmallString::Inline(str) => str.len(),
196 SmallString::Heap(str) => str.len(),
197 }
198 }
199
200 #[inline]
202 pub fn is_empty(&self) -> bool {
203 self.len() == 0
204 }
205
206 #[inline]
212 pub fn clear(&mut self) {
213 match self {
214 SmallString::Inline(str) => str.clear(),
215 SmallString::Heap(str) => str.clear(),
216 }
217 }
218
219 #[inline]
221 pub fn as_str(&self) -> &str {
222 self
224 }
225
226 #[inline]
231 pub fn as_mut_str(&mut self) -> &mut str {
232 self
234 }
235
236 #[inline]
238 pub fn as_bytes(&self) -> &[u8] {
239 self.as_str().as_bytes()
240 }
241
242 #[inline]
249 pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
250 unsafe { self.as_mut_str().as_bytes_mut() }
251 }
252
253 #[inline]
256 pub fn pop(&mut self) -> Option<char> {
257 match self {
258 SmallString::Inline(str) => str.pop(),
259 SmallString::Heap(str) => str.pop(),
260 }
261 }
262
263 #[inline]
273 pub fn truncate(&mut self, new_len: usize) {
274 match self {
275 SmallString::Inline(str) => str.truncate(new_len),
276 SmallString::Heap(str) => str.truncate(new_len),
277 }
278 }
279
280 #[inline]
283 pub fn push_str(&mut self, input: &str) {
284 match self {
285 SmallString::Heap(s) => s.push_str(input),
286 SmallString::Inline(s) => {
287 if input.len() <= N - s.len() {
289 let _ = s.push_str(input);
291 } else {
292 let new_capacity = s.len().saturating_add(input.len()).max(N.saturating_mul(2));
294 let mut heap = String::with_capacity(new_capacity);
295 heap.push_str(s.as_str());
296 heap.push_str(input);
297 *self = SmallString::Heap(heap);
298 }
299 }
300 }
301 }
302
303 #[inline]
306 pub fn push(&mut self, ch: char) {
307 match self {
308 SmallString::Heap(s) => s.push(ch),
309 SmallString::Inline(s) => {
310 let char_len = ch.len_utf8();
311 if char_len <= N - s.len() {
313 let _ = s.push(ch);
315 } else {
316 let new_capacity = s.len().saturating_add(char_len).max(N.saturating_mul(2));
318 let mut heap = String::with_capacity(new_capacity);
319 heap.push_str(s.as_str());
320 heap.push(ch);
321 *self = SmallString::Heap(heap);
322 }
323 }
324 }
325 }
326
327 #[inline]
338 pub fn reserve(&mut self, additional: usize) {
339 match self {
340 SmallString::Heap(s) => s.reserve(additional),
341 SmallString::Inline(s) => {
342 if additional > N - s.len() {
343 let new_capacity = s.len().saturating_add(additional).max(N.saturating_mul(2));
345 let mut heap = String::with_capacity(new_capacity);
346 heap.push_str(s);
347 *self = SmallString::Heap(heap);
348 }
349 }
350 }
351 }
352
353 #[inline]
363 pub fn reserve_exact(&mut self, additional: usize) {
364 match self {
365 SmallString::Heap(s) => s.reserve_exact(additional),
366 SmallString::Inline(s) => {
367 if additional > N - s.len() {
368 let new_capacity = s.len().saturating_add(additional).max(N.saturating_add(1));
370 let mut heap = String::with_capacity(new_capacity);
371 heap.push_str(s);
372 *self = SmallString::Heap(heap);
373 }
374 }
375 }
376 }
377
378 #[inline]
382 pub fn shrink_to_fit(&mut self) {
383 if let SmallString::Heap(s) = self {
384 if s.len() <= N {
385 *self = SmallString::from_str(s.as_str());
386 } else {
387 s.shrink_to_fit();
388 }
389 }
390 }
391
392 #[inline]
398 pub fn shrink_to(&mut self, min_capacity: usize) {
399 if let SmallString::Heap(s) = self
400 && s.capacity() > min_capacity
401 {
402 let target = s.len().max(min_capacity);
403 if target <= N {
404 *self = SmallString::from_str(s.as_str());
405 } else {
406 s.shrink_to(target);
407 }
408 }
409 }
410
411 #[inline]
423 pub fn into_string(self) -> String {
424 match self {
425 SmallString::Inline(s) => String::from(s.as_str()),
426 SmallString::Heap(s) => s,
427 }
428 }
429
430 #[inline]
433 pub fn into_bytes(self) -> Vec<u8> {
434 self.into_string().into_bytes()
435 }
436}
437
438impl<const N: usize> From<&str> for SmallString<N> {
439 #[inline]
440 fn from(s: &str) -> Self {
441 Self::from_str(s)
442 }
443}
444
445impl<const N: usize> From<alloc::string::String> for SmallString<N> {
446 #[inline]
447 fn from(s: alloc::string::String) -> Self {
448 Self::from_string(s)
449 }
450}
451
452impl<const N: usize> From<SmallString<N>> for alloc::string::String {
453 #[inline]
454 fn from(this: SmallString<N>) -> Self {
455 this.into_string()
456 }
457}
458
459impl<const N: usize> core::ops::Deref for SmallString<N> {
460 type Target = str;
461
462 #[inline]
463 fn deref(&self) -> &Self::Target {
464 match self {
465 SmallString::Inline(str) => str.as_str(),
466 SmallString::Heap(str) => str.as_str(),
467 }
468 }
469}
470
471impl<const N: usize> core::ops::DerefMut for SmallString<N> {
472 #[inline]
473 fn deref_mut(&mut self) -> &mut Self::Target {
474 match self {
475 SmallString::Inline(str) => str.as_mut_str(),
476 SmallString::Heap(str) => str.as_mut_str(),
477 }
478 }
479}
480
481impl<const N: usize> Default for SmallString<N> {
482 #[inline]
483 fn default() -> Self {
484 Self::new()
485 }
486}
487
488impl<const N: usize> core::fmt::Display for SmallString<N> {
489 #[inline]
490 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
491 core::fmt::Display::fmt(self.as_str(), f) }
493}
494
495impl<const N: usize> core::fmt::Debug for SmallString<N> {
496 #[inline]
497 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
498 core::fmt::Debug::fmt(self.as_str(), f)
501 }
502}
503
504impl<const N: usize> core::fmt::Write for SmallString<N> {
505 #[inline]
506 fn write_str(&mut self, s: &str) -> core::fmt::Result {
507 self.push_str(s);
508 Ok(())
509 }
510}
511
512impl<const N: usize, const M: usize> PartialEq<SmallString<M>> for SmallString<N> {
513 #[inline]
514 fn eq(&self, other: &SmallString<M>) -> bool {
515 self.as_str() == other.as_str()
516 }
517}
518
519impl<const N: usize> Eq for SmallString<N> {}
520
521impl<const N: usize> PartialEq<str> for SmallString<N> {
522 #[inline]
523 fn eq(&self, other: &str) -> bool {
524 self.as_str() == other
525 }
526}
527
528impl<'a, const N: usize> PartialEq<&'a str> for SmallString<N> {
529 #[inline]
530 fn eq(&self, other: &&'a str) -> bool {
531 self.as_str() == *other
532 }
533}
534
535impl<const N: usize> PartialEq<SmallString<N>> for &str {
536 #[inline]
537 fn eq(&self, other: &SmallString<N>) -> bool {
538 *self == other.as_str()
539 }
540}
541
542impl<const N: usize> PartialEq<alloc::string::String> for SmallString<N> {
543 #[inline]
544 fn eq(&self, other: &alloc::string::String) -> bool {
545 self.as_str() == other.as_str()
546 }
547}
548
549impl<const N: usize> PartialEq<SmallString<N>> for alloc::string::String {
550 #[inline]
551 fn eq(&self, other: &SmallString<N>) -> bool {
552 self.as_str() == other.as_str()
553 }
554}
555
556impl<const N: usize> PartialEq<SmallString<N>> for str {
557 #[inline]
558 fn eq(&self, other: &SmallString<N>) -> bool {
559 self == other.as_str()
560 }
561}
562
563impl<const N: usize, const M: usize> PartialOrd<SmallString<M>> for SmallString<N> {
564 #[inline]
565 fn partial_cmp(&self, other: &SmallString<M>) -> Option<core::cmp::Ordering> {
566 Some(self.as_str().cmp(other.as_str()))
567 }
568}
569
570impl<const N: usize> Ord for SmallString<N> {
571 #[inline]
572 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
573 self.as_str().cmp(other.as_str())
574 }
575}
576
577impl<const N: usize> core::hash::Hash for SmallString<N> {
578 #[inline]
579 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
580 self.as_str().hash(state);
581 }
582}
583
584impl<const N: usize> core::borrow::Borrow<str> for SmallString<N> {
585 fn borrow(&self) -> &str {
586 self.as_str()
587 }
588}
589
590impl<const N: usize> core::borrow::BorrowMut<str> for SmallString<N> {
591 #[inline]
592 fn borrow_mut(&mut self) -> &mut str {
593 self.as_mut_str()
594 }
595}
596
597impl<const N: usize> AsRef<str> for SmallString<N> {
598 fn as_ref(&self) -> &str {
599 self.as_str()
600 }
601}
602
603impl<const N: usize> AsRef<[u8]> for SmallString<N> {
604 #[inline]
605 fn as_ref(&self) -> &[u8] {
606 self.as_bytes()
607 }
608}
609
610impl<const N: usize> AsMut<str> for SmallString<N> {
611 #[inline]
612 fn as_mut(&mut self) -> &mut str {
613 self.as_mut_str()
614 }
615}
616
617impl<const N: usize> FromIterator<char> for SmallString<N> {
618 #[inline]
619 fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Self {
620 let mut s = Self::new();
621 let iter = iter.into_iter();
622 s.reserve(iter.size_hint().0);
623 for c in iter {
624 s.push(c);
625 }
626 s
627 }
628}
629
630impl<'a, const N: usize> FromIterator<&'a str> for SmallString<N> {
631 #[inline]
632 fn from_iter<I: IntoIterator<Item = &'a str>>(iter: I) -> Self {
633 let mut s = Self::new();
634 let iter = iter.into_iter();
635 s.reserve(iter.size_hint().0);
636 for str_slice in iter {
637 s.push_str(str_slice);
638 }
639 s
640 }
641}
642
643impl<const N: usize> Extend<char> for SmallString<N> {
644 #[inline]
645 fn extend<I: IntoIterator<Item = char>>(&mut self, iter: I) {
646 let iter = iter.into_iter();
647 self.reserve(iter.size_hint().0);
648 for c in iter {
649 self.push(c);
650 }
651 }
652}
653
654impl<'a, const N: usize> Extend<&'a str> for SmallString<N> {
655 #[inline]
656 fn extend<I: IntoIterator<Item = &'a str>>(&mut self, iter: I) {
657 let iter = iter.into_iter();
658 self.reserve(iter.size_hint().0);
659 for str_slice in iter {
660 self.push_str(str_slice);
661 }
662 }
663}
664
665impl<const N: usize> FromStr for SmallString<N> {
666 type Err = core::convert::Infallible;
667
668 #[inline]
669 fn from_str(s: &str) -> Result<Self, Self::Err> {
670 let mut out = Self::new();
671 out.push_str(s);
672 Ok(out)
673 }
674}
675
676#[cfg(feature = "serde")]
677impl<const N: usize> serde::Serialize for SmallString<N> {
678 #[inline]
679 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
680 serializer.serialize_str(self.as_str())
681 }
682}
683
684#[cfg(feature = "serde")]
685impl<'de, const N: usize> serde::Deserialize<'de> for SmallString<N> {
686 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
687 struct SmallStringVisitor<const N: usize>;
688
689 impl<'de, const N: usize> serde::de::Visitor<'de> for SmallStringVisitor<N> {
690 type Value = SmallString<N>;
691
692 fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
693 formatter.write_str("a string")
694 }
695
696 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
697 Ok(SmallString::from_str(v))
698 }
699
700 fn visit_borrowed_str<E: serde::de::Error>(self, v: &'de str) -> Result<Self::Value, E> {
701 Ok(SmallString::from_str(v))
702 }
703
704 fn visit_string<E: serde::de::Error>(self, v: String) -> Result<Self::Value, E> {
705 Ok(if v.len() <= N {
706 SmallString::from_str(&v)
707 } else {
708 SmallString::from_string(v)
709 })
710 }
711 }
712
713 deserializer.deserialize_str(SmallStringVisitor::<N>)
714 }
715}
716
717#[cfg(test)]
718mod tests {
719 use core::fmt::Write;
720
721 use crate::SmallString;
722
723 #[test]
728 fn test_basic_inline() {
729 let mut s: SmallString<16> = SmallString::new();
730 assert!(s.is_inline());
731 assert!(s.is_empty());
732 assert!(s.capacity() == 16);
733
734 s.push_str("Hello,");
735 assert_eq!(s.len(), 6);
736 assert_eq!(s.as_str(), "Hello,");
737 assert!(s.is_inline());
738 assert!(s.capacity() == 16);
739
740 s.push(' ');
741 s.push_str("World");
742 assert_eq!(s.len(), 12);
743 assert_eq!(&*s, "Hello, World");
744 assert!(s.is_inline());
745 assert!(s.capacity() == 16);
746 }
747
748 #[test]
749 fn test_basic_spill_to_heap() {
750 let mut s: SmallString<16> = SmallString::new();
751 assert!(s.is_inline());
752 assert!(s.is_empty());
753 assert!(s.capacity() == 16);
754
755 s.push_str("Hello, ");
756 assert_eq!(s.len(), 7);
757 assert_eq!(s.as_str(), "Hello, ");
758 assert!(s.is_inline());
759 assert!(s.capacity() == 16);
760
761 s.push_str(&"a".repeat(30));
762 assert_eq!(s.len(), 37);
763 assert_eq!(&s, &format!("Hello, {}", "a".repeat(30)));
764 assert!(!s.is_inline());
765 assert!(s.capacity() >= 37);
766 }
767
768 #[test]
773 fn test_new_is_empty_and_default() {
774 let s: SmallString<8> = SmallString::new();
775 assert!(s.is_empty());
776 assert!(s.is_inline());
777 let s2: SmallString<8> = Default::default();
778 assert!(s2.is_empty());
779 assert!(s2.is_inline());
780 }
781
782 #[test]
783 fn test_from_str_inline() {
784 let s: SmallString<32> = SmallString::from_str("hello");
785 assert!(s.is_inline());
786 assert_eq!(s.as_str(), "hello");
787 }
788
789 #[test]
790 fn test_from_str_spill() {
791 let s: SmallString<4> = SmallString::from_str("hello world");
792 assert!(!s.is_inline());
793 assert_eq!(s.as_str(), "hello world");
794 }
795
796 #[test]
797 fn test_from_str_exact_capacity() {
798 let s: SmallString<5> = SmallString::from_str("hello");
799 assert!(s.is_inline());
800 assert_eq!(s.as_str(), "hello");
801 }
802
803 #[test]
804 fn test_from_string_heap_preserved() {
805 let heap = String::from("hello world this is a long string");
806 let s: SmallString<4> = SmallString::from_string(heap.clone());
807 assert!(!s.is_inline());
808 assert_eq!(s.as_str(), heap);
809 }
810
811 #[test]
812 fn test_from_string_small() {
813 let heap = String::from("hi");
814 let s: SmallString<32> = SmallString::from_string(heap);
815 assert!(!s.is_inline());
817 assert_eq!(s.as_str(), "hi");
818 }
819
820 #[test]
821 fn test_from_utf8_valid() {
822 let s: SmallString<16> = SmallString::from_utf8(b"hello".to_vec()).unwrap();
823 assert!(s.is_inline());
824 assert_eq!(s.as_str(), "hello");
825 }
826
827 #[test]
828 fn test_from_utf8_invalid() {
829 let result: Result<SmallString<16>, _> = SmallString::from_utf8(vec![0xFF, 0xFE]);
830 assert!(result.is_err());
831 }
832
833 #[test]
834 fn test_from_utf8_slice_valid() {
835 let s: SmallString<16> = SmallString::from_utf8_slice(b"hello").unwrap();
836 assert!(s.is_inline());
837 assert_eq!(s.as_str(), "hello");
838 }
839
840 #[test]
841 fn test_from_utf8_slice_invalid() {
842 let result: Result<SmallString<16>, _> = SmallString::from_utf8_slice(&[0xFF, 0xFE]);
843 assert!(result.is_err());
844 }
845
846 #[test]
847 fn test_from_utf8_lossy_valid() {
848 let s: SmallString<16> = SmallString::from_utf8_lossy(b"hello");
849 assert!(s.is_inline());
850 assert_eq!(s.as_str(), "hello");
851 }
852
853 #[test]
854 fn test_from_utf8_lossy_invalid() {
855 let s: SmallString<16> = SmallString::from_utf8_lossy(&[0xFF, 0xFE]);
856 assert_eq!(s.as_str(), "\u{FFFD}\u{FFFD}");
858 }
859
860 #[test]
861 fn test_from_utf8_lossy_spill() {
862 let bytes = b"hello world this is long";
863 let s: SmallString<4> = SmallString::from_utf8_lossy(bytes);
864 assert!(!s.is_inline());
865 assert_eq!(s.as_str(), "hello world this is long");
866 }
867
868 #[test]
873 fn test_from_str_trait_inline() {
874 let s: SmallString<16> = SmallString::from("hi");
875 assert!(s.is_inline());
876 }
877
878 #[test]
879 fn test_from_str_trait_spill() {
880 let s: SmallString<4> = SmallString::from("long string");
881 assert!(!s.is_inline());
882 }
883
884 #[test]
885 fn test_from_string_trait() {
886 let s: SmallString<32> = SmallString::from(String::from("hi"));
887 assert!(!s.is_inline());
888 assert_eq!(s.as_str(), "hi");
889 }
890
891 #[test]
896 fn test_capacity_inline() {
897 let s: SmallString<64> = SmallString::from_str("hello");
898 assert_eq!(s.capacity(), 64);
899 }
900
901 #[test]
902 fn test_capacity_heap() {
903 let s: SmallString<4> = SmallString::from_str("hello world, this is a test!");
904 assert!(!s.is_inline());
905 assert!(s.capacity() >= s.len());
906 }
907
908 #[test]
909 fn test_len() {
910 let s: SmallString<32> = SmallString::from_str("héllo");
911 assert_eq!(s.len(), 6);
912 }
913
914 #[test]
919 fn test_clear_inline() {
920 let mut s: SmallString<16> = SmallString::from_str("hello");
921 assert!(s.is_inline());
922 s.clear();
923 assert!(s.is_empty());
924 assert!(s.is_inline());
925 assert_eq!(s.capacity(), 16);
926 }
927
928 #[test]
929 fn test_clear_heap() {
930 let mut s: SmallString<4> = SmallString::from_str("long string content");
931 assert!(!s.is_inline());
932 s.clear();
933 assert!(s.is_empty());
934 assert!(!s.is_inline());
935 }
936
937 #[test]
938 fn test_clear_and_reuse_inline() {
939 let mut s: SmallString<16> = SmallString::from_str("hello");
940 s.clear();
941 s.push_str("world");
942 assert!(s.is_inline());
943 assert_eq!(s.as_str(), "world");
944 }
945
946 #[test]
947 fn test_clear_and_reuse_heap() {
948 let mut s: SmallString<4> = SmallString::from_str("long string content");
949 s.clear();
950 s.push_str("abc");
951 assert!(!s.is_inline());
952 assert_eq!(s.as_str(), "abc");
953 }
954
955 #[test]
956 fn test_push_char_inline() {
957 let mut s: SmallString<16> = SmallString::new();
958 s.push('a');
959 s.push('b');
960 s.push('c');
961 assert!(s.is_inline());
962 assert_eq!(s.as_str(), "abc");
963 }
964
965 #[test]
966 fn test_push_char_spill() {
967 let mut s: SmallString<4> = SmallString::from_str("abc");
968 assert!(s.is_inline());
969 s.push('d');
970 assert!(s.is_inline());
972 assert_eq!(s.as_str(), "abcd");
973
974 s.push('e');
975 assert!(!s.is_inline());
976 assert_eq!(s.as_str(), "abcde");
977 }
978
979 #[test]
980 fn test_push_multibyte_char_stays_inline() {
981 let mut s: SmallString<8> = SmallString::new();
982 s.push('€');
983 assert!(s.is_inline());
984 assert_eq!(s.as_str(), "€");
985 }
986
987 #[test]
988 fn test_push_multibyte_char_spill() {
989 let mut s: SmallString<4> = SmallString::from_str("a");
990 s.push('🦀');
992 assert!(!s.is_inline());
993 assert_eq!(s.as_str(), "a🦀");
994 }
995
996 #[test]
997 fn test_push_str_spill_with_char() {
998 let mut s: SmallString<4> = SmallString::from_str("a");
999 s.push_str("bcd");
1000 assert!(s.is_inline());
1001 assert_eq!(s.as_str(), "abcd");
1002
1003 s.push_str("e");
1004 assert!(!s.is_inline());
1005 assert_eq!(s.as_str(), "abcde");
1006 }
1007
1008 #[test]
1009 fn test_push_str_exact_boundary() {
1010 let mut s: SmallString<5> = SmallString::from_str("hello");
1011 assert!(s.is_inline());
1012 s.push_str("");
1014 assert!(s.is_inline());
1015 assert_eq!(s.as_str(), "hello");
1016 }
1017
1018 #[test]
1019 fn test_pop_inline() {
1020 let mut s: SmallString<16> = SmallString::from_str("hello");
1021 assert_eq!(s.pop(), Some('o'));
1022 assert_eq!(s.pop(), Some('l'));
1023 assert_eq!(s.as_str(), "hel");
1024 }
1025
1026 #[test]
1027 fn test_pop_heap() {
1028 let mut s: SmallString<4> = SmallString::from_str("hello!!!");
1029 assert!(!s.is_inline());
1030 assert_eq!(s.pop(), Some('!'));
1031 assert_eq!(s.pop(), Some('!'));
1032 assert_eq!(s.as_str(), "hello!");
1033 }
1034
1035 #[test]
1036 fn test_pop_empty() {
1037 let mut s: SmallString<16> = SmallString::new();
1038 assert_eq!(s.pop(), None);
1039 }
1040
1041 #[test]
1042 fn test_pop_empty_after_clear() {
1043 let mut s: SmallString<16> = SmallString::from_str("a");
1044 s.clear();
1045 assert_eq!(s.pop(), None);
1046 }
1047
1048 #[test]
1049 fn test_pop_multibyte() {
1050 let mut s: SmallString<16> = SmallString::from_str("a🦀b");
1051 assert_eq!(s.pop(), Some('b'));
1052 assert_eq!(s.pop(), Some('🦀'));
1053 assert_eq!(s.as_str(), "a");
1054 }
1055
1056 #[test]
1057 fn test_truncate_inline() {
1058 let mut s: SmallString<16> = SmallString::from_str("hello world");
1059 s.truncate(5);
1060 assert_eq!(s.as_str(), "hello");
1061 assert!(s.is_inline());
1062 }
1063
1064 #[test]
1065 fn test_truncate_heap() {
1066 let mut s: SmallString<4> = SmallString::from_str("hello world");
1067 assert!(!s.is_inline());
1068 s.truncate(5);
1069 assert_eq!(s.as_str(), "hello");
1070 assert!(!s.is_inline());
1071 }
1072
1073 #[test]
1074 fn test_truncate_zero() {
1075 let mut s: SmallString<16> = SmallString::from_str("hello");
1076 s.truncate(0);
1077 assert!(s.is_empty());
1078 }
1079
1080 #[test]
1081 fn test_truncate_past_len() {
1082 let mut s: SmallString<16> = SmallString::from_str("hi");
1083 s.truncate(100);
1084 assert_eq!(s.as_str(), "hi");
1085 }
1086
1087 #[test]
1088 fn test_reserve_no_spill() {
1089 let mut s: SmallString<16> = SmallString::from_str("hi");
1090 s.reserve(4);
1091 assert!(s.is_inline());
1092 }
1093
1094 #[test]
1095 fn test_reserve_triggers_spill() {
1096 let mut s: SmallString<8> = SmallString::from_str("hi");
1097 s.reserve(10);
1098 assert!(!s.is_inline());
1099 assert_eq!(s.as_str(), "hi");
1100 }
1101
1102 #[test]
1103 fn test_reserve_on_heap() {
1104 let mut s: SmallString<4> = SmallString::from_str("hello world");
1105 assert!(!s.is_inline());
1106 s.reserve(50);
1107 assert!(s.capacity() >= s.len() + 50);
1110 assert_eq!(s.as_str(), "hello world");
1111 }
1112
1113 #[test]
1118 fn test_as_str() {
1119 let s: SmallString<16> = SmallString::from_str("hello");
1120 assert_eq!(s.as_str(), "hello");
1121 }
1122
1123 #[test]
1124 fn test_as_mut_str() {
1125 let mut s: SmallString<16> = SmallString::from_str("hello");
1126 let ms = s.as_mut_str();
1127 ms.make_ascii_uppercase();
1128 assert_eq!(s.as_str(), "HELLO");
1129 }
1130
1131 #[test]
1132 fn test_as_bytes() {
1133 let s: SmallString<16> = SmallString::from_str("hello");
1134 assert_eq!(s.as_bytes(), b"hello");
1135 }
1136
1137 #[test]
1138 fn test_as_bytes_mut() {
1139 let mut s: SmallString<16> = SmallString::from_str("hello");
1140 let bytes = unsafe { s.as_bytes_mut() };
1141 bytes[0] = b'H';
1142 assert_eq!(s.as_str(), "Hello");
1143 }
1144
1145 #[test]
1146 fn test_as_bytes_heap() {
1147 let s: SmallString<4> = SmallString::from_str("hello world");
1148 assert!(!s.is_inline());
1149 assert_eq!(s.as_bytes(), b"hello world");
1150 }
1151
1152 #[test]
1157 fn test_deref_inline() {
1158 let s: SmallString<16> = SmallString::from_str("hello");
1159 let r: &str = &s;
1160 assert_eq!(r, "hello");
1161 }
1162
1163 #[test]
1164 fn test_deref_heap() {
1165 let s: SmallString<4> = SmallString::from_str("long string");
1166 let r: &str = &s;
1167 assert_eq!(r, "long string");
1168 }
1169
1170 #[test]
1171 fn test_deref_mut() {
1172 let mut s: SmallString<16> = SmallString::from_str("hello");
1173 let r: &mut str = &mut s;
1174 r.make_ascii_uppercase();
1175 assert_eq!(s.as_str(), "HELLO");
1176 }
1177
1178 #[test]
1183 fn test_display() {
1184 let s: SmallString<16> = SmallString::from_str("hello");
1185 assert_eq!(format!("{}", s), "hello");
1186 }
1187
1188 #[test]
1189 fn test_display_heap() {
1190 let s: SmallString<4> = SmallString::from_str("hello world");
1191 assert_eq!(format!("{}", s), "hello world");
1192 }
1193
1194 #[test]
1195 fn test_debug_inline() {
1196 let s: SmallString<16> = SmallString::from_str("hello");
1197 assert_eq!(format!("{s:?}"), "\"hello\"");
1198 }
1199
1200 #[test]
1201 fn test_debug_heap() {
1202 let s: SmallString<4> = SmallString::from_str("hello world");
1203 assert_eq!(format!("{s:?}"), "\"hello world\"");
1204 }
1205
1206 #[test]
1207 fn test_fmt_write() {
1208 let mut s: SmallString<16> = SmallString::new();
1209 write!(&mut s, "hello world {}", 42).unwrap();
1210 assert_eq!(s.as_str(), "hello world 42");
1211 assert!(s.is_inline());
1212 }
1213
1214 #[test]
1215 fn test_fmt_write_spill() {
1216 let mut s: SmallString<4> = SmallString::new();
1217 write!(&mut s, "hello world").unwrap();
1218 assert!(!s.is_inline());
1219 assert_eq!(s.as_str(), "hello world");
1220 }
1221
1222 #[test]
1227 fn test_eq_inline_inline() {
1228 let a: SmallString<16> = SmallString::from_str("hello");
1229 let b: SmallString<16> = SmallString::from_str("hello");
1230 assert_eq!(a, b);
1231 }
1232
1233 #[test]
1234 fn test_eq_inline_heap() {
1235 let a: SmallString<4> = SmallString::from_str("hello");
1236 let b: SmallString<16> = SmallString::from_str("hello");
1237 assert!(!a.is_inline());
1238 assert!(b.is_inline());
1239 assert_eq!(a, b);
1240 }
1241
1242 #[test]
1243 fn test_eq_cross_n() {
1244 let a: SmallString<8> = SmallString::from_str("test");
1245 let b: SmallString<32> = SmallString::from_str("test");
1246 assert_eq!(a, b);
1247 }
1248
1249 #[test]
1250 fn test_eq_inequality() {
1251 let a: SmallString<16> = SmallString::from_str("abc");
1252 let b: SmallString<16> = SmallString::from_str("xyz");
1253 assert_ne!(a, b);
1254 }
1255
1256 #[test]
1257 fn test_partial_eq_str() {
1258 let s: SmallString<16> = SmallString::from_str("hello");
1259 assert_eq!(s, *"hello");
1260 }
1261
1262 #[test]
1263 fn test_partial_eq_ref_str() {
1264 let s: SmallString<16> = SmallString::from_str("hello");
1265 let r: &str = "hello";
1266 assert_eq!(s, r);
1267 }
1268
1269 #[test]
1270 fn test_partial_eq_str_ref_left() {
1271 let s: SmallString<16> = SmallString::from_str("hello");
1272 assert_eq!("hello", s);
1273 }
1274
1275 #[test]
1276 fn test_partial_eq_string() {
1277 let s: SmallString<16> = SmallString::from_str("hello");
1278 let heap = String::from("hello");
1279 assert_eq!(s, heap);
1280 }
1281
1282 #[test]
1287 fn test_ord() {
1288 let a: SmallString<16> = SmallString::from_str("abc");
1289 let b: SmallString<16> = SmallString::from_str("xyz");
1290 assert!(a < b);
1291 assert!(b > a);
1292 }
1293
1294 #[test]
1295 fn test_ord_cross_n() {
1296 let a: SmallString<4> = SmallString::from_str("abc");
1297 let b: SmallString<32> = SmallString::from_str("xyz");
1298 assert!(a < b);
1299 }
1300
1301 #[test]
1302 fn test_ord_equal() {
1303 let a: SmallString<16> = SmallString::from_str("same");
1304 let b: SmallString<16> = SmallString::from_str("same");
1305 assert_eq!(a.cmp(&b), core::cmp::Ordering::Equal);
1306 }
1307
1308 #[test]
1309 fn test_partial_ord_cross_m() {
1310 let a: SmallString<8> = SmallString::from_str("a");
1311 let b: SmallString<32> = SmallString::from_str("b");
1312 assert!(a < b);
1313 }
1314
1315 #[test]
1320 fn test_hash_inline() {
1321 use std::{
1322 collections::hash_map::DefaultHasher,
1323 hash::{Hash, Hasher},
1324 };
1325
1326 let s: SmallString<16> = SmallString::from_str("hello");
1327 let mut hasher = DefaultHasher::new();
1328 s.hash(&mut hasher);
1329 let h1 = hasher.finish();
1330
1331 let mut hasher = DefaultHasher::new();
1332 "hello".hash(&mut hasher);
1333 let h2 = hasher.finish();
1334 assert_eq!(h1, h2);
1335 }
1336
1337 #[test]
1338 fn test_hash_inline_and_heap_same() {
1339 use std::{
1340 collections::hash_map::DefaultHasher,
1341 hash::{Hash, Hasher},
1342 };
1343
1344 let inline: SmallString<32> = SmallString::from_str("hello world");
1345 let heap: SmallString<4> = SmallString::from_str("hello world");
1346
1347 let mut h1 = DefaultHasher::new();
1348 inline.hash(&mut h1);
1349 let mut h2 = DefaultHasher::new();
1350 heap.hash(&mut h2);
1351 assert_eq!(h1.finish(), h2.finish());
1352 }
1353
1354 #[test]
1355 fn test_hash_in_hashmap() {
1356 use std::collections::HashSet;
1357
1358 let a: SmallString<16> = SmallString::from_str("key1");
1359 let b: SmallString<16> = SmallString::from_str("key1");
1360
1361 let mut set = HashSet::new();
1362 set.insert(a);
1363 assert!(set.contains(&b));
1364 }
1365
1366 #[test]
1371 fn test_borrow_str() {
1372 use std::borrow::Borrow;
1373 let s: SmallString<16> = SmallString::from_str("hello");
1374 let r: &str = s.borrow();
1375 assert_eq!(r, "hello");
1376 }
1377
1378 #[test]
1379 fn test_borrow_mut_str() {
1380 use std::borrow::BorrowMut;
1381 let mut s: SmallString<16> = SmallString::from_str("hello");
1382 let r: &mut str = s.borrow_mut();
1383 r.make_ascii_uppercase();
1384 assert_eq!(s.as_str(), "HELLO");
1385 }
1386
1387 #[test]
1388 fn test_as_ref_str() {
1389 let s: SmallString<16> = SmallString::from_str("hello");
1390 let r: &str = s.as_ref();
1391 assert_eq!(r, "hello");
1392 }
1393
1394 #[test]
1395 fn test_as_ref_bytes() {
1396 let s: SmallString<16> = SmallString::from_str("hello");
1397 let r: &[u8] = s.as_ref();
1398 assert_eq!(r, b"hello");
1399 }
1400
1401 #[test]
1406 fn test_clone_inline() {
1407 let s: SmallString<16> = SmallString::from_str("hello");
1408 let c = s.clone();
1409 assert!(c.is_inline());
1410 assert_eq!(c, s);
1411 }
1412
1413 #[test]
1414 fn test_clone_heap() {
1415 let s: SmallString<4> = SmallString::from_str("hello world");
1416 assert!(!s.is_inline());
1417 let c = s.clone();
1418 assert!(!c.is_inline());
1419 assert_eq!(c, s);
1420 }
1421
1422 #[test]
1423 fn test_clone_independent() {
1424 let s: SmallString<16> = SmallString::from_str("hello");
1425 let mut c = s.clone();
1426 c.push_str(" world");
1427 assert_eq!(s.as_str(), "hello");
1428 assert_eq!(c.as_str(), "hello world");
1429 }
1430
1431 #[test]
1436 fn test_from_iter_chars_empty() {
1437 let s: SmallString<16> = SmallString::from_iter("".chars());
1438 assert!(s.is_empty());
1439 }
1440
1441 #[test]
1442 fn test_from_iter_chars_inline() {
1443 let s: SmallString<16> = SmallString::from_iter("hello".chars());
1444 assert!(s.is_inline());
1445 assert_eq!(s.as_str(), "hello");
1446 }
1447
1448 #[test]
1449 fn test_from_iter_chars_spill() {
1450 let s: SmallString<4> = SmallString::from_iter("hello world".chars());
1451 assert!(!s.is_inline());
1452 assert_eq!(s.as_str(), "hello world");
1453 }
1454
1455 #[test]
1456 fn test_from_iter_strs_empty() {
1457 let s: SmallString<16> = [""].iter().copied().collect::<SmallString<16>>();
1458 assert!(s.is_empty());
1459 }
1460
1461 #[test]
1462 fn test_from_iter_strs_inline() {
1463 let parts = ["hello", " ", "world"];
1464 let s: SmallString<32> = parts.iter().copied().collect();
1465 assert!(s.is_inline());
1466 assert_eq!(s.as_str(), "hello world");
1467 }
1468
1469 #[test]
1470 fn test_from_iter_strs_spill() {
1471 let parts = ["hello", " ", "world", " ", "this is long"];
1472 let s: SmallString<4> = parts.iter().copied().collect();
1473 assert!(!s.is_inline());
1474 assert_eq!(s.as_str(), "hello world this is long");
1475 }
1476
1477 #[test]
1482 fn test_extend_chars_inline() {
1483 let mut s: SmallString<16> = SmallString::from_str("he");
1484 s.extend("llo".chars());
1485 assert!(s.is_inline());
1486 assert_eq!(s.as_str(), "hello");
1487 }
1488
1489 #[test]
1490 fn test_extend_chars_spill() {
1491 let mut s: SmallString<4> = SmallString::from_str("a");
1492 s.extend("bcdef".chars());
1493 assert!(!s.is_inline());
1494 assert_eq!(s.as_str(), "abcdef");
1495 }
1496
1497 #[test]
1498 fn test_extend_strs_inline() {
1499 let mut s: SmallString<16> = SmallString::from_str("hello");
1500 s.extend([" ", "world"]);
1501 assert!(s.is_inline());
1502 assert_eq!(s.as_str(), "hello world");
1503 }
1504
1505 #[test]
1506 fn test_extend_strs_spill() {
1507 let mut s: SmallString<4> = SmallString::from_str("a");
1508 s.extend(["bc", "def"]);
1509 assert!(!s.is_inline());
1510 assert_eq!(s.as_str(), "abcdef");
1511 }
1512
1513 #[test]
1518 fn test_zero_capacity() {
1519 let mut s: SmallString<0> = SmallString::new();
1520 assert!(s.is_inline());
1521 assert!(s.is_empty());
1522 assert_eq!(s.capacity(), 0);
1523
1524 s.push_str("x");
1526 assert!(!s.is_inline());
1527 assert_eq!(s.as_str(), "x");
1528
1529 let mut s2: SmallString<0> = SmallString::new();
1531 s2.push('a');
1532 assert!(!s2.is_inline());
1533 assert_eq!(s2.as_str(), "a");
1534 }
1535
1536 #[test]
1537 fn test_zero_capacity_from_str() {
1538 let s: SmallString<0> = SmallString::from_str("");
1539 assert!(s.is_inline());
1540 assert!(s.is_empty());
1541
1542 let s2: SmallString<0> = SmallString::from_str("x");
1543 assert!(!s2.is_inline());
1544 assert_eq!(s2.as_str(), "x");
1545 }
1546
1547 #[test]
1548 fn test_multibyte_char_boundary() {
1549 let mut s: SmallString<8> = SmallString::new();
1550 s.push_str("a🦀b"); assert!(s.is_inline());
1552 assert_eq!(s.len(), 6);
1553 assert_eq!(s.as_str(), "a🦀b");
1554
1555 let chars: Vec<char> = s.chars().collect();
1557 assert_eq!(chars, vec!['a', '🦀', 'b']);
1558 }
1559
1560 #[test]
1561 fn test_deref_methods_available() {
1562 let s: SmallString<16> = SmallString::from_str("hello world");
1563 assert!(s.contains("world"));
1565 assert!(s.starts_with("hello"));
1566 assert_eq!(s.find('w'), Some(6));
1567 let words: Vec<&str> = s.split(' ').collect();
1568 assert_eq!(words, vec!["hello", "world"]);
1569 }
1570
1571 #[test]
1572 fn test_roundtrip_format() {
1573 let s: SmallString<16> = SmallString::from_str("test");
1574 let formatted = format!("{}", s);
1575 let back: SmallString<16> = SmallString::from_str(&formatted);
1576 assert_eq!(s, back);
1577 }
1578
1579 #[test]
1580 fn test_empty_str_operations() {
1581 let mut s: SmallString<16> = SmallString::from_str("");
1582 assert!(s.is_empty());
1583 s.push_str("");
1584 assert!(s.is_empty());
1585 s.push_str("a");
1586 assert_eq!(s.len(), 1);
1587 }
1588
1589 #[test]
1590 fn test_from_small_string_inline_to_inline() {
1591 let source: SmallString<16> = SmallString::from_str("hello");
1592 assert!(source.is_inline());
1593
1594 let dest: SmallString<32> = SmallString::from_small_string(source);
1595 assert!(dest.is_inline());
1596 assert_eq!(dest.as_str(), "hello");
1597 }
1598
1599 #[test]
1600 fn test_from_small_string_inline_to_spill() {
1601 let source: SmallString<16> = SmallString::from_str("hello world");
1602 assert!(source.is_inline());
1603
1604 let dest: SmallString<4> = SmallString::from_small_string(source);
1605 assert!(!dest.is_inline());
1606 assert_eq!(dest.as_str(), "hello world");
1607 }
1608
1609 #[test]
1610 fn test_from_small_string_heap_to_inline() {
1611 let source: SmallString<4> = SmallString::from_str("hello world this is long");
1613 assert!(!source.is_inline());
1614
1615 let dest: SmallString<64> = SmallString::from_small_string(source);
1617 assert!(dest.is_inline());
1618 assert_eq!(dest.as_str(), "hello world this is long");
1619 }
1620
1621 #[test]
1622 fn test_from_small_string_heap_to_heap() {
1623 let source: SmallString<16> = SmallString::from_str("hello world this is even longer string content");
1625 assert!(!source.is_inline());
1626
1627 let dest: SmallString<4> = SmallString::from_small_string(source);
1629 assert!(!dest.is_inline());
1630 assert_eq!(dest.as_str(), "hello world this is even longer string content");
1631 }
1632
1633 #[test]
1634 fn test_from_small_string_empty() {
1635 let source: SmallString<16> = SmallString::new();
1636 assert!(source.is_inline());
1637 assert!(source.is_empty());
1638
1639 let dest: SmallString<8> = SmallString::from_small_string(source);
1640 assert!(dest.is_inline());
1641 assert!(dest.is_empty());
1642 }
1643
1644 #[test]
1645 fn test_from_small_string_exact_capacity() {
1646 let source: SmallString<16> = SmallString::from_str("abcd");
1647 assert!(source.is_inline());
1648
1649 let dest: SmallString<4> = SmallString::from_small_string(source);
1650 assert!(dest.is_inline());
1651 assert_eq!(dest.as_str(), "abcd");
1652 }
1653
1654 #[cfg(feature = "serde")]
1655 #[test]
1656 fn test_serde_serializes_as_flat_string() {
1657 let inline: SmallString<16> = SmallString::from_str("hello");
1658 let heap: SmallString<2> = SmallString::from_str("hello");
1659 assert!(inline.is_inline());
1660 assert!(!heap.is_inline());
1661
1662 assert_eq!(serde_json::to_string(&inline).unwrap(), "\"hello\"");
1664 assert_eq!(serde_json::to_string(&heap).unwrap(), "\"hello\"");
1665 }
1666
1667 #[cfg(feature = "serde")]
1668 #[test]
1669 fn test_serde_deserializes_into_inline_or_heap() {
1670 let inline: SmallString<16> = serde_json::from_str("\"hello\"").unwrap();
1671 assert!(inline.is_inline());
1672 assert_eq!(inline.as_str(), "hello");
1673
1674 let heap: SmallString<2> = serde_json::from_str("\"hello\"").unwrap();
1677 assert!(!heap.is_inline());
1678 assert_eq!(heap.as_str(), "hello");
1679 }
1680
1681 #[cfg(feature = "serde")]
1682 #[test]
1683 fn test_serde_roundtrip_both_variants() {
1684 let inline: SmallString<16> = SmallString::from_str("hello");
1685 let json = serde_json::to_string(&inline).unwrap();
1686 let back: SmallString<16> = serde_json::from_str(&json).unwrap();
1687 assert_eq!(back, inline);
1688 assert!(back.is_inline());
1689
1690 let heap: SmallString<4> = SmallString::from_str("hello world");
1691 let json = serde_json::to_string(&heap).unwrap();
1692 let back: SmallString<4> = serde_json::from_str(&json).unwrap();
1693 assert_eq!(back, heap);
1694 assert!(!back.is_inline());
1695 }
1696
1697 #[cfg(feature = "serde")]
1698 #[test]
1699 fn test_serde_rejects_non_string() {
1700 let result: Result<SmallString<16>, _> = serde_json::from_str("42");
1701 assert!(result.is_err());
1702 }
1703
1704 #[test]
1709 fn test_from_utf8_inline_when_fits() {
1710 let s: SmallString<16> = SmallString::from_utf8(b"hello".to_vec()).unwrap();
1711 assert!(s.is_inline());
1712 assert_eq!(s.as_str(), "hello");
1713 }
1714
1715 #[test]
1716 fn test_from_utf8_spills_when_too_long() {
1717 let s: SmallString<2> = SmallString::from_utf8(b"hello".to_vec()).unwrap();
1718 assert!(!s.is_inline());
1719 assert_eq!(s.as_str(), "hello");
1720 }
1721
1722 #[test]
1723 fn test_from_utf8_empty_is_inline() {
1724 let s: SmallString<4> = SmallString::from_utf8(Vec::new()).unwrap();
1725 assert!(s.is_inline());
1726 assert!(s.is_empty());
1727 }
1728
1729 #[test]
1730 fn test_from_utf8_lossy_invalid_fits_inline() {
1731 let s: SmallString<16> = SmallString::from_utf8_lossy(&[0xFF, 0xFE]);
1733 assert!(s.is_inline());
1734 assert_eq!(s.as_str(), "\u{FFFD}\u{FFFD}");
1735 }
1736
1737 #[test]
1742 fn test_inline_size() {
1743 assert_eq!(<SmallString<8>>::inline_size(), 8);
1744 assert_eq!(<SmallString<0>>::inline_size(), 0);
1745 }
1746
1747 #[test]
1748 fn test_from_string_empty_has_no_allocation() {
1749 let s: SmallString<8> = SmallString::from_string(String::new());
1750 assert!(s.is_inline());
1751 assert!(s.is_empty());
1752 }
1753
1754 #[test]
1755 fn test_from_string_empty_with_capacity_stays_heap() {
1756 let source = String::with_capacity(8);
1757 let s: SmallString<64> = SmallString::from_string(source);
1758 assert!(!s.is_inline());
1759 assert!(s.is_empty());
1760 }
1761
1762 #[test]
1763 fn test_into_string() {
1764 let inline: SmallString<16> = SmallString::from_str("hi");
1765 assert_eq!(inline.into_string(), "hi");
1766
1767 let heap: SmallString<2> = SmallString::from_str("hello");
1768 assert!(!heap.is_inline());
1769 assert_eq!(heap.into_string(), "hello");
1770 }
1771
1772 #[test]
1773 fn test_into_bytes() {
1774 let s: SmallString<16> = SmallString::from_str("hé");
1775 assert_eq!(s.into_bytes(), b"h\xC3\xA9".to_vec());
1776 }
1777
1778 #[test]
1779 fn test_from_small_string_for_string() {
1780 let s: SmallString<4> = SmallString::from_str("hello");
1781 let owned: String = String::from(s);
1782 assert_eq!(owned, "hello");
1783 }
1784
1785 #[test]
1786 fn test_from_str_trait_and_parse() {
1787 use core::str::FromStr;
1788
1789 let s = <SmallString<8> as FromStr>::from_str("hi").unwrap();
1790 assert!(s.is_inline());
1791 assert_eq!(s.as_str(), "hi");
1792
1793 let parsed: SmallString<16> = "parsed".parse().unwrap();
1794 assert_eq!(parsed.as_str(), "parsed");
1795 }
1796
1797 #[test]
1798 fn test_as_mut_str_impl() {
1799 let mut s: SmallString<16> = SmallString::from_str("hello");
1800 let r: &mut str = s.as_mut();
1801 r.make_ascii_uppercase();
1802 assert_eq!(s.as_str(), "HELLO");
1803 }
1804
1805 #[test]
1810 fn test_shrink_to_fit_moves_back_inline() {
1811 let mut s: SmallString<8> = SmallString::from_str("hello world");
1812 assert!(!s.is_inline());
1813 s.truncate(5);
1814 assert!(!s.is_inline());
1815 s.shrink_to_fit();
1816 assert!(s.is_inline());
1817 assert_eq!(s.as_str(), "hello");
1818 }
1819
1820 #[test]
1821 fn test_shrink_to_fit_stays_heap_when_too_long() {
1822 let mut s: SmallString<2> = SmallString::from_str("hello");
1823 s.shrink_to_fit();
1824 assert!(!s.is_inline());
1825 assert_eq!(s.as_str(), "hello");
1826 }
1827
1828 #[test]
1829 fn test_shrink_to_moves_back_inline() {
1830 let mut s: SmallString<8> = SmallString::from_str("hello world");
1831 assert!(!s.is_inline());
1832 s.truncate(5);
1833 s.shrink_to(1);
1834 assert!(s.is_inline());
1835 assert_eq!(s.as_str(), "hello");
1836 }
1837
1838 #[test]
1839 fn test_shrink_to_on_inline_is_noop() {
1840 let mut s: SmallString<16> = SmallString::from_str("hello");
1841 s.shrink_to(100);
1842 assert!(s.is_inline());
1843 s.shrink_to_fit();
1844 assert!(s.is_inline());
1845 assert_eq!(s.as_str(), "hello");
1846 }
1847
1848 #[test]
1849 fn test_reserve_exact_spills() {
1850 let mut s: SmallString<4> = SmallString::from_str("ab");
1851 s.reserve_exact(10);
1852 assert!(!s.is_inline());
1853 assert!(s.capacity() >= 12);
1854 assert_eq!(s.as_str(), "ab");
1855 }
1856
1857 #[test]
1862 fn test_reverse_partial_eq_string_and_str() {
1863 let s: SmallString<16> = SmallString::from_str("hello");
1864 assert_eq!(String::from("hello"), s);
1865 assert!(*"hello" == s);
1866 assert!(*"nope" != s);
1867 }
1868
1869 #[test]
1870 fn test_borrow_str_hashmap_lookup() {
1871 use std::collections::HashMap;
1872
1873 let mut map = HashMap::new();
1874 let key: SmallString<16> = SmallString::from_str("key1");
1875 map.insert(key, 1);
1876 assert_eq!(map.get("key1"), Some(&1));
1877 }
1878
1879 #[test]
1884 #[should_panic]
1885 fn test_truncate_non_char_boundary_panics() {
1886 let mut s: SmallString<16> = SmallString::from_str("a🦀b");
1887 s.truncate(2);
1889 }
1890
1891 #[test]
1892 fn test_truncate_past_len_is_noop_even_off_boundary() {
1893 let mut s: SmallString<16> = SmallString::from_str("a🦀b");
1894 s.truncate(100);
1895 assert_eq!(s.as_str(), "a🦀b");
1896 }
1897
1898 #[test]
1899 fn test_push_multibyte_exact_boundary() {
1900 let mut s: SmallString<5> = SmallString::from_str("a");
1902 s.push('🦀');
1903 assert!(s.is_inline());
1904 assert_eq!(s.as_str(), "a🦀");
1905 }
1906
1907 #[test]
1908 fn test_fmt_write_char() {
1909 use core::fmt::Write;
1910
1911 let mut s: SmallString<16> = SmallString::new();
1912 s.write_char('é').unwrap();
1913 assert_eq!(s.as_str(), "é");
1914 }
1915
1916 #[test]
1917 fn test_pop_keeps_heap_storage() {
1918 let mut s: SmallString<2> = SmallString::from_str("hello");
1919 assert!(!s.is_inline());
1920 assert_eq!(s.pop(), Some('o'));
1921 assert!(!s.is_inline());
1922 assert_eq!(s.as_str(), "hell");
1923 }
1924}