@@ -686,132 +686,91 @@ private static unsafe string JoinInternal(ReadOnlySpan<char> first, ReadOnlySpan
686
686
{
687
687
Debug . Assert ( first . Length > 0 && second . Length > 0 , "should have dealt with empty paths" ) ;
688
688
689
- bool hasSeparator = PathInternal . IsDirectorySeparator ( first [ first . Length - 1 ] )
690
- || PathInternal . IsDirectorySeparator ( second [ 0 ] ) ;
689
+ bool hasSeparator = PathInternal . IsDirectorySeparator ( first [ ^ 1 ] ) || PathInternal . IsDirectorySeparator ( second [ 0 ] ) ;
691
690
692
691
return hasSeparator ?
693
692
string . Concat ( first , second ) :
694
693
string . Concat ( first , PathInternal . DirectorySeparatorCharAsString , second ) ;
695
694
}
696
695
697
- private readonly unsafe struct Join3Payload
698
- {
699
- public Join3Payload ( char * first , int firstLength , char * second , int secondLength , char * third , int thirdLength , byte separators )
700
- {
701
- First = first ;
702
- FirstLength = firstLength ;
703
- Second = second ;
704
- SecondLength = secondLength ;
705
- Third = third ;
706
- ThirdLength = thirdLength ;
707
- Separators = separators ;
708
- }
709
-
710
- public readonly char * First ;
711
- public readonly int FirstLength ;
712
- public readonly char * Second ;
713
- public readonly int SecondLength ;
714
- public readonly char * Third ;
715
- public readonly int ThirdLength ;
716
- public readonly byte Separators ;
717
- }
718
-
719
696
private static unsafe string JoinInternal ( ReadOnlySpan < char > first , ReadOnlySpan < char > second , ReadOnlySpan < char > third )
720
697
{
721
698
Debug . Assert ( first . Length > 0 && second . Length > 0 && third . Length > 0 , "should have dealt with empty paths" ) ;
722
699
723
- byte firstNeedsSeparator = PathInternal . IsDirectorySeparator ( first [ first . Length - 1 ] )
724
- || PathInternal . IsDirectorySeparator ( second [ 0 ] ) ? ( byte ) 0 : ( byte ) 1 ;
725
- byte secondNeedsSeparator = PathInternal . IsDirectorySeparator ( second [ second . Length - 1 ] )
726
- || PathInternal . IsDirectorySeparator ( third [ 0 ] ) ? ( byte ) 0 : ( byte ) 1 ;
700
+ bool firstHasSeparator = PathInternal . IsDirectorySeparator ( first [ ^ 1 ] ) || PathInternal . IsDirectorySeparator ( second [ 0 ] ) ;
701
+ bool secondHasSeparator = PathInternal . IsDirectorySeparator ( second [ ^ 1 ] ) || PathInternal . IsDirectorySeparator ( third [ 0 ] ) ;
727
702
728
- fixed ( char * f = & MemoryMarshal . GetReference ( first ) , s = & MemoryMarshal . GetReference ( second ) , t = & MemoryMarshal . GetReference ( third ) )
703
+ return ( firstHasSeparator , secondHasSeparator ) switch
729
704
{
730
- var payload = new Join3Payload (
731
- f , first . Length , s , second . Length , t , third . Length ,
732
- ( byte ) ( firstNeedsSeparator | secondNeedsSeparator << 1 ) ) ;
733
-
734
- return string . Create (
735
- first . Length + second . Length + third . Length + firstNeedsSeparator + secondNeedsSeparator ,
736
- ( IntPtr ) ( & payload ) ,
737
- static ( destination , statePtr ) =>
738
- {
739
- ref Join3Payload state = ref * ( Join3Payload * ) statePtr ;
740
- new Span < char > ( state . First , state . FirstLength ) . CopyTo ( destination ) ;
741
- if ( ( state . Separators & 0b1 ) != 0 )
742
- destination[ state . FirstLength ] = PathInternal . DirectorySeparatorChar ;
743
- new Span < char > ( state . Second , state . SecondLength ) . CopyTo ( destination . Slice ( state . FirstLength + ( state . Separators & 0b1 ) ) ) ;
744
- if ( ( state . Separators & 0b10 ) != 0 )
745
- destination [ destination . Length - state . ThirdLength - 1 ] = PathInternal . DirectorySeparatorChar ;
746
- new Span < char > ( state . Third , state . ThirdLength ) . CopyTo ( destination . Slice ( destination . Length - state . ThirdLength ) ) ;
747
- } ) ;
748
- }
705
+ ( false , false ) => string . Concat ( first , PathInternal . DirectorySeparatorCharAsString , second , PathInternal . DirectorySeparatorCharAsString , third ) ,
706
+ ( false , true ) => string . Concat ( first , PathInternal . DirectorySeparatorCharAsString , second , third ) ,
707
+ ( true , false ) => string . Concat ( first , second , PathInternal . DirectorySeparatorCharAsString , third ) ,
708
+ ( true , true ) => string . Concat ( first , second , third ) ,
709
+ } ;
749
710
}
750
711
751
- private readonly unsafe struct Join4Payload
712
+ private static unsafe string JoinInternal ( ReadOnlySpan < char > first , ReadOnlySpan < char > second , ReadOnlySpan < char > third , ReadOnlySpan < char > fourth )
752
713
{
753
- public Join4Payload ( char * first , int firstLength , char * second , int secondLength , char * third , int thirdLength , char * fourth , int fourthLength , byte separators )
714
+ Debug . Assert ( first . Length > 0 && second . Length > 0 && third . Length > 0 && fourth . Length > 0 , "should have dealt with empty paths" ) ;
715
+
716
+ #pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type
717
+ var state = new JoinInternalState
754
718
{
755
- First = first ;
756
- FirstLength = firstLength ;
757
- Second = second ;
758
- SecondLength = secondLength ;
759
- Third = third ;
760
- ThirdLength = thirdLength ;
761
- Fourth = fourth ;
762
- FourthLength = fourthLength ;
763
- Separators = separators ;
764
- }
719
+ ReadOnlySpanPtr1 = ( IntPtr ) ( & first ) ,
720
+ ReadOnlySpanPtr2 = ( IntPtr ) ( & second ) ,
721
+ ReadOnlySpanPtr3 = ( IntPtr ) ( & third ) ,
722
+ ReadOnlySpanPtr4 = ( IntPtr ) ( & fourth ) ,
723
+ NeedSeparator1 = PathInternal . IsDirectorySeparator ( first [ ^ 1 ] ) || PathInternal . IsDirectorySeparator ( second [ 0 ] ) ? ( byte ) 0 : ( byte ) 1 ,
724
+ NeedSeparator2 = PathInternal . IsDirectorySeparator ( second [ ^ 1 ] ) || PathInternal . IsDirectorySeparator ( third [ 0 ] ) ? ( byte ) 0 : ( byte ) 1 ,
725
+ NeedSeparator3 = PathInternal . IsDirectorySeparator ( third [ ^ 1 ] ) || PathInternal . IsDirectorySeparator ( fourth [ 0 ] ) ? ( byte ) 0 : ( byte ) 1 ,
726
+ } ;
765
727
766
- public readonly char * First ;
767
- public readonly int FirstLength ;
768
- public readonly char * Second ;
769
- public readonly int SecondLength ;
770
- public readonly char * Third ;
771
- public readonly int ThirdLength ;
772
- public readonly char * Fourth ;
773
- public readonly int FourthLength ;
774
- public readonly byte Separators ;
775
- }
728
+ return string . Create (
729
+ first . Length + second . Length + third . Length + fourth . Length + state . NeedSeparator1 + state . NeedSeparator2 + state . NeedSeparator3 ,
730
+ state ,
731
+ static ( destination , state ) =>
732
+ {
733
+ ReadOnlySpan < char > first = * ( ReadOnlySpan < char > * ) state . ReadOnlySpanPtr1 ;
734
+ first. CopyTo ( destination ) ;
735
+ destination = destination. Slice ( first . Length ) ;
776
736
777
- private static unsafe string JoinInternal ( ReadOnlySpan < char > first , ReadOnlySpan < char > second , ReadOnlySpan < char > third , ReadOnlySpan < char > fourth )
778
- {
779
- Debug . Assert ( first . Length > 0 && second . Length > 0 && third . Length > 0 && fourth . Length > 0 , "should have dealt with empty paths" ) ;
737
+ if ( state . NeedSeparator1 != 0 )
738
+ {
739
+ destination [ 0 ] = PathInternal . DirectorySeparatorChar ;
740
+ destination = destination . Slice ( 1 ) ;
741
+ }
780
742
781
- byte firstNeedsSeparator = PathInternal . IsDirectorySeparator ( first [ first . Length - 1 ] )
782
- || PathInternal . IsDirectorySeparator ( second [ 0 ] ) ? ( byte ) 0 : ( byte ) 1 ;
783
- byte secondNeedsSeparator = PathInternal . IsDirectorySeparator ( second [ second . Length - 1 ] )
784
- || PathInternal . IsDirectorySeparator ( third [ 0 ] ) ? ( byte ) 0 : ( byte ) 1 ;
785
- byte thirdNeedsSeparator = PathInternal . IsDirectorySeparator ( third [ third . Length - 1 ] )
786
- || PathInternal . IsDirectorySeparator ( fourth [ 0 ] ) ? ( byte ) 0 : ( byte ) 1 ;
743
+ ReadOnlySpan < char > second = * ( ReadOnlySpan < char > * ) state . ReadOnlySpanPtr2 ;
744
+ second. CopyTo ( destination ) ;
745
+ destination = destination. Slice ( second . Length ) ;
787
746
788
- fixed ( char * f = & MemoryMarshal . GetReference ( first ) , s = & MemoryMarshal . GetReference ( second ) , t = & MemoryMarshal . GetReference ( third ) , u = & MemoryMarshal . GetReference ( fourth ) )
789
- {
790
- var payload = new Join4Payload (
791
- f , first . Length , s , second . Length , t , third . Length , u , fourth . Length ,
792
- ( byte ) ( firstNeedsSeparator | secondNeedsSeparator << 1 | thirdNeedsSeparator << 2 ) ) ;
793
-
794
- return string . Create (
795
- first . Length + second . Length + third . Length + fourth . Length + firstNeedsSeparator + secondNeedsSeparator + thirdNeedsSeparator ,
796
- ( IntPtr ) ( & payload ) ,
797
- static ( destination , statePtr ) =>
747
+ if ( state . NeedSeparator2 != 0 )
798
748
{
799
- ref Join4Payload state = ref * ( Join4Payload * ) statePtr ;
800
- new Span < char > ( state . First , state . FirstLength ) . CopyTo ( destination ) ;
801
- int insertionPoint = state. FirstLength ;
802
- if ( ( state . Separators & 0b1 ) != 0 )
803
- destination [ insertionPoint ++ ] = PathInternal . DirectorySeparatorChar ;
804
- new Span < char > ( state . Second , state . SecondLength ) . CopyTo ( destination . Slice ( insertionPoint ) ) ;
805
- insertionPoint += state . SecondLength ;
806
- if ( ( state . Separators & 0b10 ) != 0 )
807
- destination [ insertionPoint ++ ] = PathInternal . DirectorySeparatorChar ;
808
- new Span < char > ( state . Third , state . ThirdLength ) . CopyTo ( destination . Slice ( insertionPoint ) ) ;
809
- insertionPoint += state . ThirdLength ;
810
- if ( ( state . Separators & 0b100 ) != 0 )
811
- destination [ insertionPoint ++ ] = PathInternal . DirectorySeparatorChar ;
812
- new Span < char > ( state . Fourth , state . FourthLength ) . CopyTo ( destination . Slice ( insertionPoint ) ) ;
813
- } ) ;
814
- }
749
+ destination [ 0 ] = PathInternal . DirectorySeparatorChar ;
750
+ destination = destination . Slice ( 1 ) ;
751
+ }
752
+
753
+ ReadOnlySpan < char > third = * ( ReadOnlySpan < char > * ) state . ReadOnlySpanPtr3 ;
754
+ third. CopyTo ( destination ) ;
755
+ destination = destination. Slice ( third . Length ) ;
756
+
757
+ if ( state . NeedSeparator3 != 0 )
758
+ {
759
+ destination [ 0 ] = PathInternal . DirectorySeparatorChar ;
760
+ destination = destination . Slice ( 1 ) ;
761
+ }
762
+
763
+ ReadOnlySpan < char > fourth = * ( ReadOnlySpan < char > * ) state . ReadOnlySpanPtr4 ;
764
+ Debug. Assert ( fourth . Length == destination . Length ) ;
765
+ fourth. CopyTo ( destination ) ;
766
+ } ) ;
767
+ #pragma warning restore CS8500
768
+ }
769
+
770
+ private struct JoinInternalState // used to avoid rooting ValueTuple`7
771
+ {
772
+ public IntPtr ReadOnlySpanPtr1 , ReadOnlySpanPtr2 , ReadOnlySpanPtr3 , ReadOnlySpanPtr4 ;
773
+ public byte NeedSeparator1 , NeedSeparator2 , NeedSeparator3 ;
815
774
}
816
775
817
776
private static ReadOnlySpan < byte > Base32Char => "abcdefghijklmnopqrstuvwxyz012345"u8 ;
0 commit comments