@@ -620,15 +620,13 @@ pub trait StrExt: private::Sealed {
620
620
/// potentially without allocating.
621
621
///
622
622
/// See [`str::replace`].
623
- // TODO: Use `Pattern` when stable.
624
623
#[ must_use = "this returns a new SmolStr without modifying the original" ]
625
624
fn replace_smolstr ( & self , from : & str , to : & str ) -> SmolStr ;
626
625
627
626
/// Replaces first N matches of a &str with another &str returning a new [`SmolStr`],
628
627
/// potentially without allocating.
629
628
///
630
629
/// See [`str::replacen`].
631
- // TODO: Use `Pattern` when stable.
632
630
#[ must_use = "this returns a new SmolStr without modifying the original" ]
633
631
fn replacen_smolstr ( & self , from : & str , to : & str , count : usize ) -> SmolStr ;
634
632
}
@@ -661,7 +659,7 @@ impl StrExt for str {
661
659
662
660
#[ inline]
663
661
fn replacen_smolstr ( & self , from : & str , to : & str , count : usize ) -> SmolStr {
664
- let mut result = Writer :: new ( ) ;
662
+ let mut result = SmolStrBuilder :: new ( ) ;
665
663
let mut last_end = 0 ;
666
664
for ( start, part) in self . match_indices ( from) . take ( count) {
667
665
// SAFETY: `start` is guaranteed to be within the bounds of `self` as per
@@ -677,6 +675,15 @@ impl StrExt for str {
677
675
}
678
676
}
679
677
678
+ impl < T > ToSmolStr for T
679
+ where
680
+ T : fmt:: Display + ?Sized ,
681
+ {
682
+ fn to_smolstr ( & self ) -> SmolStr {
683
+ format_smolstr ! ( "{}" , self )
684
+ }
685
+ }
686
+
680
687
mod private {
681
688
/// No downstream impls allowed.
682
689
pub trait Sealed { }
@@ -689,85 +696,94 @@ mod private {
689
696
#[ macro_export]
690
697
macro_rules! format_smolstr {
691
698
( $( $tt: tt) * ) => { {
692
- use :: core:: fmt:: Write ;
693
- let mut w = $crate:: Writer :: new( ) ;
694
- w. write_fmt( format_args!( $( $tt) * ) ) . expect( "a formatting trait implementation returned an error" ) ;
695
- $crate:: SmolStr :: from( w)
699
+ let mut w = $crate:: SmolStrBuilder :: new( ) ;
700
+ :: core:: fmt:: Write :: write_fmt( & mut w, format_args!( $( $tt) * ) ) . expect( "a formatting trait implementation returned an error" ) ;
701
+ w. finish( )
696
702
} } ;
697
703
}
698
704
699
- #[ doc( hidden) ]
700
- pub struct Writer {
701
- inline : [ u8 ; INLINE_CAP ] ,
702
- heap : String ,
703
- len : usize ,
705
+ /// A builder that can be used to efficiently build a [`SmolStr`].
706
+ ///
707
+ /// This won't allocate if the final string fits into the inline buffer.
708
+ #[ derive( Clone , Debug , PartialEq , Eq ) ]
709
+ pub enum SmolStrBuilder {
710
+ Inline { len : usize , buf : [ u8 ; INLINE_CAP ] } ,
711
+ Heap ( String ) ,
712
+ }
713
+
714
+ impl Default for SmolStrBuilder {
715
+ #[ inline]
716
+ fn default ( ) -> Self {
717
+ Self :: new ( )
718
+ }
704
719
}
705
720
706
- impl Writer {
721
+ impl SmolStrBuilder {
722
+ /// Creates a new empty [`SmolStrBuilder`].
707
723
#[ must_use]
708
724
pub const fn new ( ) -> Self {
709
- Writer {
710
- inline : [ 0 ; INLINE_CAP ] ,
711
- heap : String :: new ( ) ,
725
+ SmolStrBuilder :: Inline {
726
+ buf : [ 0 ; INLINE_CAP ] ,
712
727
len : 0 ,
713
728
}
714
729
}
715
730
716
- fn push_str ( & mut self , s : & str ) {
717
- // if currently on the stack
718
- if self . len <= INLINE_CAP {
719
- let old_len = self . len ;
720
- self . len += s. len ( ) ;
721
-
722
- // if the new length will fit on the stack (even if it fills it entirely)
723
- if self . len <= INLINE_CAP {
724
- self . inline [ old_len..self . len ] . copy_from_slice ( s. as_bytes ( ) ) ;
725
- return ; // skip the heap push below
731
+ /// Builds a [`SmolStr`] from `self`.
732
+ #[ must_use]
733
+ pub fn finish ( & self ) -> SmolStr {
734
+ SmolStr ( match self {
735
+ & SmolStrBuilder :: Inline { len, buf } => {
736
+ debug_assert ! ( len <= INLINE_CAP ) ;
737
+ Repr :: Inline {
738
+ // SAFETY: We know that `value.len` is less than or equal to the maximum value of `InlineSize`
739
+ len : unsafe { InlineSize :: transmute_from_u8 ( len as u8 ) } ,
740
+ buf,
741
+ }
726
742
}
743
+ SmolStrBuilder :: Heap ( heap) => Repr :: new ( heap) ,
744
+ } )
745
+ }
727
746
728
- self . heap . reserve ( self . len ) ;
729
-
730
- // copy existing inline bytes over to the heap
731
- // SAFETY: inline data is guaranteed to be valid utf8 for `old_len` bytes
732
- unsafe {
733
- self . heap
734
- . as_mut_vec ( )
735
- . extend_from_slice ( & self . inline [ ..old_len] ) ;
747
+ /// Appends a given string slice onto the end of `self`'s buffer.
748
+ pub fn push_str ( & mut self , s : & str ) {
749
+ // if currently on the stack
750
+ match self {
751
+ Self :: Inline { len, buf } => {
752
+ let old_len = * len;
753
+ * len += s. len ( ) ;
754
+
755
+ // if the new length will fit on the stack (even if it fills it entirely)
756
+ if * len <= INLINE_CAP {
757
+ buf[ old_len..* len] . copy_from_slice ( s. as_bytes ( ) ) ;
758
+ return ; // skip the heap push below
759
+ }
760
+
761
+ let mut heap = String :: with_capacity ( * len) ;
762
+
763
+ // copy existing inline bytes over to the heap
764
+ // SAFETY: inline data is guaranteed to be valid utf8 for `old_len` bytes
765
+ unsafe {
766
+ heap. as_mut_vec ( ) . extend_from_slice ( & buf[ ..old_len] ) ;
767
+ }
768
+ heap. push_str ( s) ;
769
+ * self = SmolStrBuilder :: Heap ( heap) ;
736
770
}
771
+ SmolStrBuilder :: Heap ( heap) => heap. push_str ( s) ,
737
772
}
738
-
739
- self . heap . push_str ( s) ;
740
773
}
741
774
}
742
775
743
- impl fmt:: Write for Writer {
776
+ impl fmt:: Write for SmolStrBuilder {
744
777
#[ inline]
745
778
fn write_str ( & mut self , s : & str ) -> fmt:: Result {
746
779
self . push_str ( s) ;
747
780
Ok ( ( ) )
748
781
}
749
782
}
750
783
751
- impl From < Writer > for SmolStr {
752
- fn from ( value : Writer ) -> Self {
753
- SmolStr ( if value. len <= INLINE_CAP {
754
- Repr :: Inline {
755
- // SAFETY: We know that `value.len` is less than or equal to the maximum value of `InlineSize`
756
- len : unsafe { InlineSize :: transmute_from_u8 ( value. len as u8 ) } ,
757
- buf : value. inline ,
758
- }
759
- } else {
760
- Repr :: new ( & value. heap )
761
- } )
762
- }
763
- }
764
-
765
- impl < T > ToSmolStr for T
766
- where
767
- T : fmt:: Display + ?Sized ,
768
- {
769
- fn to_smolstr ( & self ) -> SmolStr {
770
- format_smolstr ! ( "{}" , self )
784
+ impl From < SmolStrBuilder > for SmolStr {
785
+ fn from ( value : SmolStrBuilder ) -> Self {
786
+ value. finish ( )
771
787
}
772
788
}
773
789
0 commit comments