1extern crate alloc;
2
3#[derive(Clone, Debug)]
4#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
5pub enum SmallString<const N: usize> {
6 Inline(heapless::String<N>),
7 Heap(alloc::string::String),
8}
9
10impl<const N: usize> SmallString<N> {
11 #[inline(always)]
12 pub fn new() -> Self {
13 SmallString::Inline(heapless::String::new())
14 }
15
16 #[inline(always)]
19 pub fn from_str(s: &str) -> Self {
20 let mut str = Self::new();
21 str.push_str(s);
22 str
23 }
24
25 #[inline(always)]
27 pub fn from_string(s: alloc::string::String) -> Self {
28 Self::Heap(s)
29 }
30
31 #[inline(always)]
32 pub fn from_small_string<const M: usize>(input: SmallString<M>) -> Self {
33 match input {
34 SmallString::Heap(s) if s.len() <= N => SmallString::from_str(&s),
35 SmallString::Heap(s) => SmallString::Heap(s),
36 SmallString::Inline(s) => SmallString::from_str(s.as_str()),
37 }
38 }
39
40 #[inline(always)]
45 pub fn from_utf8(bytes: alloc::vec::Vec<u8>) -> Result<Self, alloc::string::FromUtf8Error> {
46 Ok(Self::Heap(alloc::string::String::from_utf8(bytes)?))
47 }
48
49 #[inline(always)]
54 pub fn from_utf8_slice(bytes: &[u8]) -> Result<Self, core::str::Utf8Error> {
55 Ok(Self::from_str(core::str::from_utf8(bytes)?))
56 }
57
58 #[inline(always)]
60 pub fn from_utf8_lossy(bytes: &[u8]) -> Self {
61 let str = alloc::string::String::from_utf8_lossy(bytes);
63 match str {
64 alloc::borrow::Cow::Borrowed(borrowed) => Self::from_str(borrowed),
65 alloc::borrow::Cow::Owned(owned) => Self::from_string(owned),
66 }
67 }
68
69 #[inline(always)]
71 pub fn is_inline(&self) -> bool {
72 matches!(self, SmallString::Inline(_))
73 }
74
75 #[inline(always)]
77 pub fn capacity(&self) -> usize {
78 match self {
79 SmallString::Inline(_) => N,
80 SmallString::Heap(str) => str.capacity(),
81 }
82 }
83
84 #[inline(always)]
86 pub fn len(&self) -> usize {
87 match self {
88 SmallString::Inline(str) => str.len(),
89 SmallString::Heap(str) => str.len(),
90 }
91 }
92
93 #[inline(always)]
95 pub fn is_empty(&self) -> bool {
96 self.len() == 0
97 }
98
99 #[inline(always)]
104 pub fn clear(&mut self) {
105 match self {
106 SmallString::Inline(str) => str.clear(),
107 SmallString::Heap(str) => str.clear(),
108 }
109 }
110
111 #[inline(always)]
112 pub fn as_str(&self) -> &str {
113 self
115 }
116
117 #[inline(always)]
118 pub fn as_mut_str(&mut self) -> &mut str {
119 self
121 }
122
123 #[inline(always)]
125 pub fn as_bytes(&self) -> &[u8] {
126 self.as_str().as_bytes()
127 }
128
129 #[inline(always)]
134 pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
135 unsafe { self.as_mut_str().as_bytes_mut() }
136 }
137
138 #[inline(always)]
141 pub fn pop(&mut self) -> Option<char> {
142 match self {
143 SmallString::Inline(str) => str.pop(),
144 SmallString::Heap(str) => str.pop(),
145 }
146 }
147
148 #[inline(always)]
155 pub fn truncate(&mut self, new_len: usize) {
156 match self {
157 SmallString::Inline(str) => str.truncate(new_len),
158 SmallString::Heap(str) => str.truncate(new_len),
159 }
160 }
161
162 #[inline]
163 pub fn push_str(&mut self, input: &str) {
164 match self {
165 SmallString::Heap(str) => str.push_str(input),
166 SmallString::Inline(str) => {
167 if str.len() + input.len() <= N {
168 let _ = str.push_str(input);
170 } else {
171 let new_capacity = core::cmp::max(str.len() + input.len(), N * 2);
173 let mut heap_str = alloc::string::String::with_capacity(new_capacity);
174 heap_str.push_str(str.as_str());
175 heap_str.push_str(input);
176 *self = SmallString::Heap(heap_str)
177 }
178 }
179 }
180 }
181
182 #[inline]
183 pub fn push(&mut self, ch: char) {
184 match self {
185 SmallString::Heap(str) => str.push(ch),
186 SmallString::Inline(str) => {
187 let char_len = ch.len_utf8();
188 if str.len() + char_len <= N {
189 let _ = str.push(ch);
191 } else {
192 let new_capacity = core::cmp::max(str.len() + char_len, N * 2);
194 let mut heap_str = alloc::string::String::with_capacity(new_capacity);
195 heap_str.push_str(str.as_str());
196 heap_str.push(ch);
197 *self = SmallString::Heap(heap_str)
198 }
199 }
200 }
201 }
202
203 #[inline]
204 pub fn reserve(&mut self, additional: usize) {
205 match self {
206 SmallString::Heap(str) => str.reserve(additional),
207 SmallString::Inline(str) => {
208 if str.len() + additional > N {
209 let new_capacity = core::cmp::max(str.len() + additional, N * 2);
211 let mut heap_str = alloc::string::String::with_capacity(new_capacity);
212 heap_str.push_str(str);
213 *self = SmallString::Heap(heap_str)
214 }
215 }
216 }
217 }
218}
219
220impl<const N: usize> From<&str> for SmallString<N> {
221 #[inline(always)]
222 fn from(s: &str) -> Self {
223 Self::from_str(s)
224 }
225}
226
227impl<const N: usize> From<alloc::string::String> for SmallString<N> {
228 #[inline(always)]
229 fn from(s: alloc::string::String) -> Self {
230 Self::from_string(s)
231 }
232}
233
234impl<const N: usize> core::ops::Deref for SmallString<N> {
235 type Target = str;
236
237 #[inline(always)]
238 fn deref(&self) -> &Self::Target {
239 match self {
240 SmallString::Inline(str) => str.as_str(),
241 SmallString::Heap(str) => str.as_str(),
242 }
243 }
244}
245
246impl<const N: usize> core::ops::DerefMut for SmallString<N> {
247 #[inline(always)]
248 fn deref_mut(&mut self) -> &mut Self::Target {
249 match self {
250 SmallString::Inline(str) => str.as_mut_str(),
251 SmallString::Heap(str) => str.as_mut_str(),
252 }
253 }
254}
255
256impl<const N: usize> Default for SmallString<N> {
257 #[inline(always)]
258 fn default() -> Self {
259 Self::new()
260 }
261}
262
263impl<const N: usize> core::fmt::Display for SmallString<N> {
264 #[inline(always)]
265 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
266 core::fmt::Display::fmt(self.as_str(), f) }
268}
269
270impl<const N: usize> core::fmt::Write for SmallString<N> {
271 #[inline(always)]
272 fn write_str(&mut self, s: &str) -> core::fmt::Result {
273 self.push_str(s);
274 Ok(())
275 }
276}
277
278impl<const N: usize, const M: usize> PartialEq<SmallString<M>> for SmallString<N> {
279 #[inline(always)]
280 fn eq(&self, other: &SmallString<M>) -> bool {
281 self.as_str() == other.as_str()
282 }
283}
284
285impl<const N: usize> Eq for SmallString<N> {}
286
287impl<const N: usize> PartialEq<str> for SmallString<N> {
288 #[inline(always)]
289 fn eq(&self, other: &str) -> bool {
290 self.as_str() == other
291 }
292}
293
294impl<'a, const N: usize> PartialEq<&'a str> for SmallString<N> {
295 #[inline(always)]
296 fn eq(&self, other: &&'a str) -> bool {
297 self.as_str() == *other
298 }
299}
300
301impl<const N: usize> PartialEq<SmallString<N>> for &str {
302 #[inline(always)]
303 fn eq(&self, other: &SmallString<N>) -> bool {
304 *self == other.as_str()
305 }
306}
307
308impl<const N: usize> PartialEq<alloc::string::String> for SmallString<N> {
309 #[inline(always)]
310 fn eq(&self, other: &alloc::string::String) -> bool {
311 self.as_str() == other.as_str()
312 }
313}
314
315impl<const N: usize, const M: usize> PartialOrd<SmallString<M>> for SmallString<N> {
316 #[inline(always)]
317 fn partial_cmp(&self, other: &SmallString<M>) -> Option<core::cmp::Ordering> {
318 Some(self.as_str().cmp(other.as_str()))
319 }
320}
321
322impl<const N: usize> Ord for SmallString<N> {
323 #[inline(always)]
324 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
325 self.as_str().cmp(other.as_str())
326 }
327}
328
329impl<const N: usize> core::hash::Hash for SmallString<N> {
330 #[inline(always)]
331 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
332 self.as_str().hash(state);
333 }
334}
335
336impl<const N: usize> core::borrow::Borrow<str> for SmallString<N> {
337 fn borrow(&self) -> &str {
338 self.as_str()
339 }
340}
341
342impl<const N: usize> core::borrow::BorrowMut<str> for SmallString<N> {
343 #[inline(always)]
344 fn borrow_mut(&mut self) -> &mut str {
345 self.as_mut_str()
346 }
347}
348
349impl<const N: usize> AsRef<str> for SmallString<N> {
350 fn as_ref(&self) -> &str {
351 self.as_str()
352 }
353}
354
355impl<const N: usize> AsRef<[u8]> for SmallString<N> {
356 #[inline(always)]
357 fn as_ref(&self) -> &[u8] {
358 self.as_bytes()
359 }
360}
361
362impl<const N: usize> FromIterator<char> for SmallString<N> {
363 #[inline]
364 fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Self {
365 let mut s = Self::new();
366 let iter = iter.into_iter();
367 s.reserve(iter.size_hint().0);
368 for c in iter {
369 s.push(c);
370 }
371 s
372 }
373}
374
375impl<'a, const N: usize> FromIterator<&'a str> for SmallString<N> {
376 #[inline]
377 fn from_iter<I: IntoIterator<Item = &'a str>>(iter: I) -> Self {
378 let mut s = Self::new();
379 let iter = iter.into_iter();
380 s.reserve(iter.size_hint().0);
381 for str_slice in iter {
382 s.push_str(str_slice);
383 }
384 s
385 }
386}
387
388impl<const N: usize> Extend<char> for SmallString<N> {
389 #[inline]
390 fn extend<I: IntoIterator<Item = char>>(&mut self, iter: I) {
391 let iter = iter.into_iter();
392 self.reserve(iter.size_hint().0);
393 for c in iter {
394 self.push(c);
395 }
396 }
397}
398
399impl<'a, const N: usize> Extend<&'a str> for SmallString<N> {
400 #[inline]
401 fn extend<I: IntoIterator<Item = &'a str>>(&mut self, iter: I) {
402 let iter = iter.into_iter();
403 self.reserve(iter.size_hint().0);
404 for str_slice in iter {
405 self.push_str(str_slice);
406 }
407 }
408}
409
410#[cfg(test)]
411mod tests {
412 use core::fmt::Write;
413
414 use crate::SmallString;
415
416 #[test]
421 fn test_basic_inline() {
422 let mut s: SmallString<16> = SmallString::new();
423 assert!(s.is_inline());
424 assert!(s.is_empty());
425 assert!(s.capacity() == 16);
426
427 s.push_str("Hello,");
428 assert_eq!(s.len(), 6);
429 assert_eq!(s.as_str(), "Hello,");
430 assert!(s.is_inline());
431 assert!(s.capacity() == 16);
432
433 s.push(' ');
434 s.push_str("World");
435 assert_eq!(s.len(), 12);
436 assert_eq!(&*s, "Hello, World");
437 assert!(s.is_inline());
438 assert!(s.capacity() == 16);
439 }
440
441 #[test]
442 fn test_basic_spill_to_heap() {
443 let mut s: SmallString<16> = SmallString::new();
444 assert!(s.is_inline());
445 assert!(s.is_empty());
446 assert!(s.capacity() == 16);
447
448 s.push_str("Hello, ");
449 assert_eq!(s.len(), 7);
450 assert_eq!(s.as_str(), "Hello, ");
451 assert!(s.is_inline());
452 assert!(s.capacity() == 16);
453
454 s.push_str(&"a".repeat(30));
455 assert_eq!(s.len(), 37);
456 assert_eq!(&s, &format!("Hello, {}", "a".repeat(30)));
457 assert!(!s.is_inline());
458 assert!(s.capacity() >= 37);
459 }
460
461 #[test]
466 fn test_new_is_empty_and_default() {
467 let s: SmallString<8> = SmallString::new();
468 assert!(s.is_empty());
469 assert!(s.is_inline());
470 let s2: SmallString<8> = Default::default();
471 assert!(s2.is_empty());
472 assert!(s2.is_inline());
473 }
474
475 #[test]
476 fn test_from_str_inline() {
477 let s: SmallString<32> = SmallString::from_str("hello");
478 assert!(s.is_inline());
479 assert_eq!(s.as_str(), "hello");
480 }
481
482 #[test]
483 fn test_from_str_spill() {
484 let s: SmallString<4> = SmallString::from_str("hello world");
485 assert!(!s.is_inline());
486 assert_eq!(s.as_str(), "hello world");
487 }
488
489 #[test]
490 fn test_from_str_exact_capacity() {
491 let s: SmallString<5> = SmallString::from_str("hello");
492 assert!(s.is_inline());
493 assert_eq!(s.as_str(), "hello");
494 }
495
496 #[test]
497 fn test_from_string_heap_preserved() {
498 let heap = String::from("hello world this is a long string");
499 let s: SmallString<4> = SmallString::from_string(heap.clone());
500 assert!(!s.is_inline());
501 assert_eq!(s.as_str(), heap);
502 }
503
504 #[test]
505 fn test_from_string_small() {
506 let heap = String::from("hi");
507 let s: SmallString<32> = SmallString::from_string(heap);
508 assert!(!s.is_inline());
510 assert_eq!(s.as_str(), "hi");
511 }
512
513 #[test]
514 fn test_from_utf8_valid() {
515 let s: SmallString<16> = SmallString::from_utf8(b"hello".to_vec()).unwrap();
516 assert!(s.is_inline() == false);
517 assert_eq!(s.as_str(), "hello");
518 }
519
520 #[test]
521 fn test_from_utf8_invalid() {
522 let result: Result<SmallString<16>, _> = SmallString::from_utf8(vec![0xFF, 0xFE]);
523 assert!(result.is_err());
524 }
525
526 #[test]
527 fn test_from_utf8_slice_valid() {
528 let s: SmallString<16> = SmallString::from_utf8_slice(b"hello").unwrap();
529 assert!(s.is_inline() == true);
530 assert_eq!(s.as_str(), "hello");
531 }
532
533 #[test]
534 fn test_from_utf8_slice_invalid() {
535 let result: Result<SmallString<16>, _> = SmallString::from_utf8_slice(&[0xFF, 0xFE]);
536 assert!(result.is_err());
537 }
538
539 #[test]
540 fn test_from_utf8_lossy_valid() {
541 let s: SmallString<16> = SmallString::from_utf8_lossy(b"hello");
542 assert!(s.is_inline());
543 assert_eq!(s.as_str(), "hello");
544 }
545
546 #[test]
547 fn test_from_utf8_lossy_invalid() {
548 let s: SmallString<16> = SmallString::from_utf8_lossy(&[0xFF, 0xFE]);
549 assert_eq!(s.as_str(), "\u{FFFD}\u{FFFD}");
551 }
552
553 #[test]
554 fn test_from_utf8_lossy_spill() {
555 let bytes = b"hello world this is long";
556 let s: SmallString<4> = SmallString::from_utf8_lossy(bytes);
557 assert!(!s.is_inline());
558 assert_eq!(s.as_str(), "hello world this is long");
559 }
560
561 #[test]
566 fn test_from_str_trait_inline() {
567 let s: SmallString<16> = SmallString::from("hi");
568 assert!(s.is_inline());
569 }
570
571 #[test]
572 fn test_from_str_trait_spill() {
573 let s: SmallString<4> = SmallString::from("long string");
574 assert!(!s.is_inline());
575 }
576
577 #[test]
578 fn test_from_string_trait() {
579 let s: SmallString<32> = SmallString::from(String::from("hi"));
580 assert!(!s.is_inline());
581 assert_eq!(s.as_str(), "hi");
582 }
583
584 #[test]
589 fn test_capacity_inline() {
590 let s: SmallString<64> = SmallString::from_str("hello");
591 assert_eq!(s.capacity(), 64);
592 }
593
594 #[test]
595 fn test_capacity_heap() {
596 let s: SmallString<4> = SmallString::from_str("hello world, this is a test!");
597 assert!(!s.is_inline());
598 assert!(s.capacity() >= s.len());
599 }
600
601 #[test]
602 fn test_len() {
603 let s: SmallString<32> = SmallString::from_str("héllo");
604 assert_eq!(s.len(), 6);
605 }
606
607 #[test]
612 fn test_clear_inline() {
613 let mut s: SmallString<16> = SmallString::from_str("hello");
614 assert!(s.is_inline());
615 s.clear();
616 assert!(s.is_empty());
617 assert!(s.is_inline());
618 assert_eq!(s.capacity(), 16);
619 }
620
621 #[test]
622 fn test_clear_heap() {
623 let mut s: SmallString<4> = SmallString::from_str("long string content");
624 assert!(!s.is_inline());
625 s.clear();
626 assert!(s.is_empty());
627 assert!(!s.is_inline());
628 }
629
630 #[test]
631 fn test_clear_and_reuse_inline() {
632 let mut s: SmallString<16> = SmallString::from_str("hello");
633 s.clear();
634 s.push_str("world");
635 assert!(s.is_inline());
636 assert_eq!(s.as_str(), "world");
637 }
638
639 #[test]
640 fn test_clear_and_reuse_heap() {
641 let mut s: SmallString<4> = SmallString::from_str("long string content");
642 s.clear();
643 s.push_str("abc");
644 assert!(!s.is_inline());
645 assert_eq!(s.as_str(), "abc");
646 }
647
648 #[test]
649 fn test_push_char_inline() {
650 let mut s: SmallString<16> = SmallString::new();
651 s.push('a');
652 s.push('b');
653 s.push('c');
654 assert!(s.is_inline());
655 assert_eq!(s.as_str(), "abc");
656 }
657
658 #[test]
659 fn test_push_char_spill() {
660 let mut s: SmallString<4> = SmallString::from_str("abc");
661 assert!(s.is_inline());
662 s.push('d');
663 assert!(s.is_inline());
665 assert_eq!(s.as_str(), "abcd");
666
667 s.push('e');
668 assert!(!s.is_inline());
669 assert_eq!(s.as_str(), "abcde");
670 }
671
672 #[test]
673 fn test_push_multibyte_char_stays_inline() {
674 let mut s: SmallString<8> = SmallString::new();
675 s.push('€');
676 assert!(s.is_inline());
677 assert_eq!(s.as_str(), "€");
678 }
679
680 #[test]
681 fn test_push_multibyte_char_spill() {
682 let mut s: SmallString<4> = SmallString::from_str("a");
683 s.push('🦀');
685 assert!(!s.is_inline());
686 assert_eq!(s.as_str(), "a🦀");
687 }
688
689 #[test]
690 fn test_push_str_spill_with_char() {
691 let mut s: SmallString<4> = SmallString::from_str("a");
692 s.push_str("bcd");
693 assert!(s.is_inline());
694 assert_eq!(s.as_str(), "abcd");
695
696 s.push_str("e");
697 assert!(!s.is_inline());
698 assert_eq!(s.as_str(), "abcde");
699 }
700
701 #[test]
702 fn test_push_str_exact_boundary() {
703 let mut s: SmallString<5> = SmallString::from_str("hello");
704 assert!(s.is_inline());
705 s.push_str("");
707 assert!(s.is_inline());
708 assert_eq!(s.as_str(), "hello");
709 }
710
711 #[test]
712 fn test_pop_inline() {
713 let mut s: SmallString<16> = SmallString::from_str("hello");
714 assert_eq!(s.pop(), Some('o'));
715 assert_eq!(s.pop(), Some('l'));
716 assert_eq!(s.as_str(), "hel");
717 }
718
719 #[test]
720 fn test_pop_heap() {
721 let mut s: SmallString<4> = SmallString::from_str("hello!!!");
722 assert!(!s.is_inline());
723 assert_eq!(s.pop(), Some('!'));
724 assert_eq!(s.pop(), Some('!'));
725 assert_eq!(s.as_str(), "hello!");
726 }
727
728 #[test]
729 fn test_pop_empty() {
730 let mut s: SmallString<16> = SmallString::new();
731 assert_eq!(s.pop(), None);
732 }
733
734 #[test]
735 fn test_pop_empty_after_clear() {
736 let mut s: SmallString<16> = SmallString::from_str("a");
737 s.clear();
738 assert_eq!(s.pop(), None);
739 }
740
741 #[test]
742 fn test_pop_multibyte() {
743 let mut s: SmallString<16> = SmallString::from_str("a🦀b");
744 assert_eq!(s.pop(), Some('b'));
745 assert_eq!(s.pop(), Some('🦀'));
746 assert_eq!(s.as_str(), "a");
747 }
748
749 #[test]
750 fn test_truncate_inline() {
751 let mut s: SmallString<16> = SmallString::from_str("hello world");
752 s.truncate(5);
753 assert_eq!(s.as_str(), "hello");
754 assert!(s.is_inline());
755 }
756
757 #[test]
758 fn test_truncate_heap() {
759 let mut s: SmallString<4> = SmallString::from_str("hello world");
760 assert!(!s.is_inline());
761 s.truncate(5);
762 assert_eq!(s.as_str(), "hello");
763 assert!(!s.is_inline());
764 }
765
766 #[test]
767 fn test_truncate_zero() {
768 let mut s: SmallString<16> = SmallString::from_str("hello");
769 s.truncate(0);
770 assert!(s.is_empty());
771 }
772
773 #[test]
774 fn test_truncate_past_len() {
775 let mut s: SmallString<16> = SmallString::from_str("hi");
776 s.truncate(100);
777 assert_eq!(s.as_str(), "hi");
778 }
779
780 #[test]
781 fn test_reserve_no_spill() {
782 let mut s: SmallString<16> = SmallString::from_str("hi");
783 s.reserve(4);
784 assert!(s.is_inline());
785 }
786
787 #[test]
788 fn test_reserve_triggers_spill() {
789 let mut s: SmallString<8> = SmallString::from_str("hi");
790 s.reserve(10);
791 assert!(!s.is_inline());
792 assert_eq!(s.as_str(), "hi");
793 }
794
795 #[test]
796 fn test_reserve_on_heap() {
797 let mut s: SmallString<4> = SmallString::from_str("hello world");
798 assert!(!s.is_inline());
799 let cap_before = s.capacity();
800 s.reserve(50);
801 assert!(s.capacity() >= cap_before + 50);
802 assert_eq!(s.as_str(), "hello world");
803 }
804
805 #[test]
810 fn test_as_str() {
811 let s: SmallString<16> = SmallString::from_str("hello");
812 assert_eq!(s.as_str(), "hello");
813 }
814
815 #[test]
816 fn test_as_mut_str() {
817 let mut s: SmallString<16> = SmallString::from_str("hello");
818 let ms = s.as_mut_str();
819 ms.make_ascii_uppercase();
820 assert_eq!(s.as_str(), "HELLO");
821 }
822
823 #[test]
824 fn test_as_bytes() {
825 let s: SmallString<16> = SmallString::from_str("hello");
826 assert_eq!(s.as_bytes(), b"hello");
827 }
828
829 #[test]
830 fn test_as_bytes_mut() {
831 let mut s: SmallString<16> = SmallString::from_str("hello");
832 let bytes = unsafe { s.as_bytes_mut() };
833 bytes[0] = b'H';
834 assert_eq!(s.as_str(), "Hello");
835 }
836
837 #[test]
838 fn test_as_bytes_heap() {
839 let s: SmallString<4> = SmallString::from_str("hello world");
840 assert!(!s.is_inline());
841 assert_eq!(s.as_bytes(), b"hello world");
842 }
843
844 #[test]
849 fn test_deref_inline() {
850 let s: SmallString<16> = SmallString::from_str("hello");
851 let r: &str = &*s;
852 assert_eq!(r, "hello");
853 }
854
855 #[test]
856 fn test_deref_heap() {
857 let s: SmallString<4> = SmallString::from_str("long string");
858 let r: &str = &*s;
859 assert_eq!(r, "long string");
860 }
861
862 #[test]
863 fn test_deref_mut() {
864 let mut s: SmallString<16> = SmallString::from_str("hello");
865 let r: &mut str = &mut *s;
866 r.make_ascii_uppercase();
867 assert_eq!(s.as_str(), "HELLO");
868 }
869
870 #[test]
875 fn test_display() {
876 let s: SmallString<16> = SmallString::from_str("hello");
877 assert_eq!(format!("{}", s), "hello");
878 }
879
880 #[test]
881 fn test_display_heap() {
882 let s: SmallString<4> = SmallString::from_str("hello world");
883 assert_eq!(format!("{}", s), "hello world");
884 }
885
886 #[test]
887 fn test_debug_inline() {
888 let s: SmallString<16> = SmallString::from_str("hello");
889 assert_eq!(format!("{:?}", s), "Inline(\"hello\")");
890 }
891
892 #[test]
893 fn test_debug_heap() {
894 let s: SmallString<4> = SmallString::from_str("hello world");
895 assert_eq!(format!("{:?}", s), "Heap(\"hello world\")");
896 }
897
898 #[test]
899 fn test_fmt_write() {
900 let mut s: SmallString<16> = SmallString::new();
901 write!(&mut s, "hello {} {}", "world", 42).unwrap();
902 assert_eq!(s.as_str(), "hello world 42");
903 assert!(s.is_inline());
904 }
905
906 #[test]
907 fn test_fmt_write_spill() {
908 let mut s: SmallString<4> = SmallString::new();
909 write!(&mut s, "hello world").unwrap();
910 assert!(!s.is_inline());
911 assert_eq!(s.as_str(), "hello world");
912 }
913
914 #[test]
919 fn test_eq_inline_inline() {
920 let a: SmallString<16> = SmallString::from_str("hello");
921 let b: SmallString<16> = SmallString::from_str("hello");
922 assert_eq!(a, b);
923 }
924
925 #[test]
926 fn test_eq_inline_heap() {
927 let a: SmallString<4> = SmallString::from_str("hello");
928 let b: SmallString<16> = SmallString::from_str("hello");
929 assert!(!a.is_inline());
930 assert!(b.is_inline());
931 assert_eq!(a, b);
932 }
933
934 #[test]
935 fn test_eq_cross_n() {
936 let a: SmallString<8> = SmallString::from_str("test");
937 let b: SmallString<32> = SmallString::from_str("test");
938 assert_eq!(a, b);
939 }
940
941 #[test]
942 fn test_eq_inequality() {
943 let a: SmallString<16> = SmallString::from_str("abc");
944 let b: SmallString<16> = SmallString::from_str("xyz");
945 assert_ne!(a, b);
946 }
947
948 #[test]
949 fn test_partial_eq_str() {
950 let s: SmallString<16> = SmallString::from_str("hello");
951 assert_eq!(s, *"hello");
952 }
953
954 #[test]
955 fn test_partial_eq_ref_str() {
956 let s: SmallString<16> = SmallString::from_str("hello");
957 let r: &str = "hello";
958 assert_eq!(s, r);
959 }
960
961 #[test]
962 fn test_partial_eq_str_ref_left() {
963 let s: SmallString<16> = SmallString::from_str("hello");
964 assert_eq!("hello", s);
965 }
966
967 #[test]
968 fn test_partial_eq_string() {
969 let s: SmallString<16> = SmallString::from_str("hello");
970 let heap = String::from("hello");
971 assert_eq!(s, heap);
972 }
973
974 #[test]
979 fn test_ord() {
980 let a: SmallString<16> = SmallString::from_str("abc");
981 let b: SmallString<16> = SmallString::from_str("xyz");
982 assert!(a < b);
983 assert!(b > a);
984 }
985
986 #[test]
987 fn test_ord_cross_n() {
988 let a: SmallString<4> = SmallString::from_str("abc");
989 let b: SmallString<32> = SmallString::from_str("xyz");
990 assert!(a < b);
991 }
992
993 #[test]
994 fn test_ord_equal() {
995 let a: SmallString<16> = SmallString::from_str("same");
996 let b: SmallString<16> = SmallString::from_str("same");
997 assert_eq!(a.cmp(&b), core::cmp::Ordering::Equal);
998 }
999
1000 #[test]
1001 fn test_partial_ord_cross_m() {
1002 let a: SmallString<8> = SmallString::from_str("a");
1003 let b: SmallString<32> = SmallString::from_str("b");
1004 assert!(a < b);
1005 }
1006
1007 #[test]
1012 fn test_hash_inline() {
1013 use std::{
1014 collections::hash_map::DefaultHasher,
1015 hash::{Hash, Hasher},
1016 };
1017
1018 let s: SmallString<16> = SmallString::from_str("hello");
1019 let mut hasher = DefaultHasher::new();
1020 s.hash(&mut hasher);
1021 let h1 = hasher.finish();
1022
1023 let mut hasher = DefaultHasher::new();
1024 "hello".hash(&mut hasher);
1025 let h2 = hasher.finish();
1026 assert_eq!(h1, h2);
1027 }
1028
1029 #[test]
1030 fn test_hash_inline_and_heap_same() {
1031 use std::{
1032 collections::hash_map::DefaultHasher,
1033 hash::{Hash, Hasher},
1034 };
1035
1036 let inline: SmallString<32> = SmallString::from_str("hello world");
1037 let heap: SmallString<4> = SmallString::from_str("hello world");
1038
1039 let mut h1 = DefaultHasher::new();
1040 inline.hash(&mut h1);
1041 let mut h2 = DefaultHasher::new();
1042 heap.hash(&mut h2);
1043 assert_eq!(h1.finish(), h2.finish());
1044 }
1045
1046 #[test]
1047 fn test_hash_in_hashmap() {
1048 use std::collections::HashSet;
1049
1050 let a: SmallString<16> = SmallString::from_str("key1");
1051 let b: SmallString<16> = SmallString::from_str("key1");
1052
1053 let mut set = HashSet::new();
1054 set.insert(a);
1055 assert!(set.contains(&b));
1056 }
1057
1058 #[test]
1063 fn test_borrow_str() {
1064 use std::borrow::Borrow;
1065 let s: SmallString<16> = SmallString::from_str("hello");
1066 let r: &str = s.borrow();
1067 assert_eq!(r, "hello");
1068 }
1069
1070 #[test]
1071 fn test_borrow_mut_str() {
1072 use std::borrow::BorrowMut;
1073 let mut s: SmallString<16> = SmallString::from_str("hello");
1074 let r: &mut str = s.borrow_mut();
1075 r.make_ascii_uppercase();
1076 assert_eq!(s.as_str(), "HELLO");
1077 }
1078
1079 #[test]
1080 fn test_as_ref_str() {
1081 let s: SmallString<16> = SmallString::from_str("hello");
1082 let r: &str = s.as_ref();
1083 assert_eq!(r, "hello");
1084 }
1085
1086 #[test]
1087 fn test_as_ref_bytes() {
1088 let s: SmallString<16> = SmallString::from_str("hello");
1089 let r: &[u8] = s.as_ref();
1090 assert_eq!(r, b"hello");
1091 }
1092
1093 #[test]
1098 fn test_clone_inline() {
1099 let s: SmallString<16> = SmallString::from_str("hello");
1100 let c = s.clone();
1101 assert!(c.is_inline());
1102 assert_eq!(c, s);
1103 }
1104
1105 #[test]
1106 fn test_clone_heap() {
1107 let s: SmallString<4> = SmallString::from_str("hello world");
1108 assert!(!s.is_inline());
1109 let c = s.clone();
1110 assert!(!c.is_inline());
1111 assert_eq!(c, s);
1112 }
1113
1114 #[test]
1115 fn test_clone_independent() {
1116 let s: SmallString<16> = SmallString::from_str("hello");
1117 let mut c = s.clone();
1118 c.push_str(" world");
1119 assert_eq!(s.as_str(), "hello");
1120 assert_eq!(c.as_str(), "hello world");
1121 }
1122
1123 #[test]
1128 fn test_from_iter_chars_empty() {
1129 let s: SmallString<16> = SmallString::from_iter("".chars());
1130 assert!(s.is_empty());
1131 }
1132
1133 #[test]
1134 fn test_from_iter_chars_inline() {
1135 let s: SmallString<16> = SmallString::from_iter("hello".chars());
1136 assert!(s.is_inline());
1137 assert_eq!(s.as_str(), "hello");
1138 }
1139
1140 #[test]
1141 fn test_from_iter_chars_spill() {
1142 let s: SmallString<4> = SmallString::from_iter("hello world".chars());
1143 assert!(!s.is_inline());
1144 assert_eq!(s.as_str(), "hello world");
1145 }
1146
1147 #[test]
1148 fn test_from_iter_strs_empty() {
1149 let s: SmallString<16> = [""].iter().copied().collect::<SmallString<16>>();
1150 assert!(s.is_empty());
1151 }
1152
1153 #[test]
1154 fn test_from_iter_strs_inline() {
1155 let parts = ["hello", " ", "world"];
1156 let s: SmallString<32> = parts.iter().copied().collect();
1157 assert!(s.is_inline());
1158 assert_eq!(s.as_str(), "hello world");
1159 }
1160
1161 #[test]
1162 fn test_from_iter_strs_spill() {
1163 let parts = ["hello", " ", "world", " ", "this is long"];
1164 let s: SmallString<4> = parts.iter().copied().collect();
1165 assert!(!s.is_inline());
1166 assert_eq!(s.as_str(), "hello world this is long");
1167 }
1168
1169 #[test]
1174 fn test_extend_chars_inline() {
1175 let mut s: SmallString<16> = SmallString::from_str("he");
1176 s.extend("llo".chars());
1177 assert!(s.is_inline());
1178 assert_eq!(s.as_str(), "hello");
1179 }
1180
1181 #[test]
1182 fn test_extend_chars_spill() {
1183 let mut s: SmallString<4> = SmallString::from_str("a");
1184 s.extend("bcdef".chars());
1185 assert!(!s.is_inline());
1186 assert_eq!(s.as_str(), "abcdef");
1187 }
1188
1189 #[test]
1190 fn test_extend_strs_inline() {
1191 let mut s: SmallString<16> = SmallString::from_str("hello");
1192 s.extend([" ", "world"]);
1193 assert!(s.is_inline());
1194 assert_eq!(s.as_str(), "hello world");
1195 }
1196
1197 #[test]
1198 fn test_extend_strs_spill() {
1199 let mut s: SmallString<4> = SmallString::from_str("a");
1200 s.extend(["bc", "def"]);
1201 assert!(!s.is_inline());
1202 assert_eq!(s.as_str(), "abcdef");
1203 }
1204
1205 #[test]
1210 fn test_zero_capacity() {
1211 let mut s: SmallString<0> = SmallString::new();
1212 assert!(s.is_inline());
1213 assert!(s.is_empty());
1214 assert_eq!(s.capacity(), 0);
1215
1216 s.push_str("x");
1218 assert!(!s.is_inline());
1219 assert_eq!(s.as_str(), "x");
1220
1221 let mut s2: SmallString<0> = SmallString::new();
1223 s2.push('a');
1224 assert!(!s2.is_inline());
1225 assert_eq!(s2.as_str(), "a");
1226 }
1227
1228 #[test]
1229 fn test_zero_capacity_from_str() {
1230 let s: SmallString<0> = SmallString::from_str("");
1231 assert!(s.is_inline());
1232 assert!(s.is_empty());
1233
1234 let s2: SmallString<0> = SmallString::from_str("x");
1235 assert!(!s2.is_inline());
1236 assert_eq!(s2.as_str(), "x");
1237 }
1238
1239 #[test]
1240 fn test_multibyte_char_boundary() {
1241 let mut s: SmallString<8> = SmallString::new();
1242 s.push_str("a🦀b"); assert!(s.is_inline());
1244 assert_eq!(s.len(), 6);
1245 assert_eq!(s.as_str(), "a🦀b");
1246
1247 let chars: Vec<char> = s.chars().collect();
1249 assert_eq!(chars, vec!['a', '🦀', 'b']);
1250 }
1251
1252 #[test]
1253 fn test_deref_methods_available() {
1254 let s: SmallString<16> = SmallString::from_str("hello world");
1255 assert!(s.contains("world"));
1257 assert!(s.starts_with("hello"));
1258 assert_eq!(s.find('w'), Some(6));
1259 let words: Vec<&str> = s.split(' ').collect();
1260 assert_eq!(words, vec!["hello", "world"]);
1261 }
1262
1263 #[test]
1264 fn test_roundtrip_format() {
1265 let s: SmallString<16> = SmallString::from_str("test");
1266 let formatted = format!("{}", s);
1267 let back: SmallString<16> = SmallString::from_str(&formatted);
1268 assert_eq!(s, back);
1269 }
1270
1271 #[test]
1272 fn test_empty_str_operations() {
1273 let mut s: SmallString<16> = SmallString::from_str("");
1274 assert!(s.is_empty());
1275 s.push_str("");
1276 assert!(s.is_empty());
1277 s.push_str("a");
1278 assert_eq!(s.len(), 1);
1279 }
1280
1281 #[test]
1282 fn test_from_small_string_inline_to_inline() {
1283 let source: SmallString<16> = SmallString::from_str("hello");
1284 assert!(source.is_inline());
1285
1286 let dest: SmallString<32> = SmallString::from_small_string(source);
1287 assert!(dest.is_inline());
1288 assert_eq!(dest.as_str(), "hello");
1289 }
1290
1291 #[test]
1292 fn test_from_small_string_inline_to_spill() {
1293 let source: SmallString<16> = SmallString::from_str("hello world");
1294 assert!(source.is_inline());
1295
1296 let dest: SmallString<4> = SmallString::from_small_string(source);
1297 assert!(!dest.is_inline());
1298 assert_eq!(dest.as_str(), "hello world");
1299 }
1300
1301 #[test]
1302 fn test_from_small_string_heap_to_inline() {
1303 let source: SmallString<4> = SmallString::from_str("hello world this is long");
1305 assert!(!source.is_inline());
1306
1307 let dest: SmallString<64> = SmallString::from_small_string(source);
1309 assert!(dest.is_inline());
1310 assert_eq!(dest.as_str(), "hello world this is long");
1311 }
1312
1313 #[test]
1314 fn test_from_small_string_heap_to_heap() {
1315 let source: SmallString<16> = SmallString::from_str("hello world this is even longer string content");
1317 assert!(!source.is_inline());
1318
1319 let dest: SmallString<4> = SmallString::from_small_string(source);
1321 assert!(!dest.is_inline());
1322 assert_eq!(dest.as_str(), "hello world this is even longer string content");
1323 }
1324
1325 #[test]
1326 fn test_from_small_string_empty() {
1327 let source: SmallString<16> = SmallString::new();
1328 assert!(source.is_inline());
1329 assert!(source.is_empty());
1330
1331 let dest: SmallString<8> = SmallString::from_small_string(source);
1332 assert!(dest.is_inline());
1333 assert!(dest.is_empty());
1334 }
1335
1336 #[test]
1337 fn test_from_small_string_exact_capacity() {
1338 let source: SmallString<16> = SmallString::from_str("abcd");
1339 assert!(source.is_inline());
1340
1341 let dest: SmallString<4> = SmallString::from_small_string(source);
1342 assert!(dest.is_inline());
1343 assert_eq!(dest.as_str(), "abcd");
1344 }
1345
1346 #[cfg(feature = "serde")]
1347 #[test]
1348 fn test_serde_roundtrip_inline() {
1349 let s: SmallString<16> = SmallString::from_str("hello");
1350 let json = serde_json::to_string(&s).unwrap();
1351 let deserialized: SmallString<16> = serde_json::from_str(&json).unwrap();
1352 assert_eq!(s, deserialized);
1353 assert!(deserialized.is_inline());
1354 }
1355
1356 #[cfg(feature = "serde")]
1357 #[test]
1358 fn test_serde_roundtrip_heap() {
1359 let s: SmallString<4> = SmallString::from_str("hello world long");
1360 let json = serde_json::to_string(&s).unwrap();
1361 let deserialized: SmallString<4> = serde_json::from_str(&json).unwrap();
1362 assert_eq!(s, deserialized);
1363 }
1365}