1+ using System ;
2+ using System . Runtime . CompilerServices ;
3+ using System . Runtime . InteropServices ;
4+ using System . Text ;
5+
6+ namespace ServiceStack . Text
7+ {
8+ public static class CharMemoryExtensions
9+ {
10+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
11+ public static bool IsNullOrEmpty ( this ReadOnlyMemory < char > value ) => value . IsEmpty ;
12+
13+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
14+ public static bool IsWhiteSpace ( this ReadOnlyMemory < char > value )
15+ {
16+ var span = value . Span ;
17+ for ( int i = 0 ; i < span . Length ; i ++ )
18+ {
19+ if ( ! char . IsWhiteSpace ( span [ i ] ) )
20+ return false ;
21+ }
22+ return true ;
23+ }
24+
25+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
26+ public static bool IsNullOrWhiteSpace ( this ReadOnlyMemory < char > value ) => value . IsEmpty || value . IsWhiteSpace ( ) ;
27+
28+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
29+ public static ReadOnlyMemory < char > Advance ( this ReadOnlyMemory < char > text , int to ) => text . Slice ( to , text . Length - to ) ;
30+
31+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
32+ public static ReadOnlyMemory < char > AdvancePastWhitespace ( this ReadOnlyMemory < char > literal )
33+ {
34+ var span = literal . Span ;
35+ var i = 0 ;
36+ while ( i < span . Length && char . IsWhiteSpace ( span [ i ] ) )
37+ i ++ ;
38+
39+ return i == 0 ? literal : literal . Slice ( i < literal . Length ? i : literal . Length ) ;
40+ }
41+
42+ public static ReadOnlyMemory < char > AdvancePastChar ( this ReadOnlyMemory < char > literal , char delim )
43+ {
44+ var i = 0 ;
45+ var c = ( char ) 0 ;
46+ var span = literal . Span ;
47+ while ( i < span . Length && ( c = span [ i ] ) != delim )
48+ i ++ ;
49+
50+ if ( c == delim )
51+ return literal . Slice ( i + 1 ) ;
52+
53+ return i == 0 ? literal : literal . Slice ( i < literal . Length ? i : literal . Length ) ;
54+ }
55+
56+ public static bool TryReadLine ( this ReadOnlyMemory < char > text , out ReadOnlyMemory < char > line , ref int startIndex )
57+ {
58+ if ( startIndex >= text . Length )
59+ {
60+ line = TypeConstants . NullMemory ;
61+ return false ;
62+ }
63+
64+ text = text . Slice ( startIndex ) ;
65+
66+ var nextLinePos = text . Span . IndexOfAny ( '\r ' , '\n ' ) ;
67+ if ( nextLinePos == - 1 )
68+ {
69+ var nextLine = text . Slice ( 0 , text . Length ) ;
70+ startIndex += text . Length ;
71+ line = nextLine ;
72+ return true ;
73+ }
74+ else
75+ {
76+ var nextLine = text . Slice ( 0 , nextLinePos ) ;
77+
78+ startIndex += nextLinePos + 1 ;
79+
80+ var span = text . Span ;
81+ if ( span [ nextLinePos ] == '\r ' && span . Length > nextLinePos + 1 && span [ nextLinePos + 1 ] == '\n ' )
82+ startIndex += 1 ;
83+
84+ line = nextLine ;
85+ return true ;
86+ }
87+ }
88+
89+ public static bool TryReadPart ( this ReadOnlyMemory < char > text , ReadOnlyMemory < char > needle , out ReadOnlyMemory < char > part , ref int startIndex )
90+ {
91+ if ( startIndex >= text . Length )
92+ {
93+ part = TypeConstants . NullMemory ;
94+ return false ;
95+ }
96+
97+ text = text . Slice ( startIndex ) ;
98+ var nextPartPos = text . Span . IndexOf ( needle . Span ) ;
99+ if ( nextPartPos == - 1 )
100+ {
101+ var nextPart = text . Slice ( 0 , text . Length ) ;
102+ startIndex += text . Length ;
103+ part = nextPart ;
104+ return true ;
105+ }
106+ else
107+ {
108+ var nextPart = text . Slice ( 0 , nextPartPos ) ;
109+ startIndex += nextPartPos + needle . Length ;
110+ part = nextPart ;
111+ return true ;
112+ }
113+ }
114+
115+ public static void SplitOnFirst ( this ReadOnlyMemory < char > strVal , char needle , out ReadOnlyMemory < char > first , out ReadOnlyMemory < char > last )
116+ {
117+ first = default ;
118+ last = default ;
119+ if ( strVal . IsEmpty ) return ;
120+
121+ var pos = strVal . Span . IndexOf ( needle ) ;
122+ if ( pos == - 1 )
123+ {
124+ first = strVal ;
125+ }
126+ else
127+ {
128+ first = strVal . Slice ( 0 , pos ) ;
129+ last = strVal . Slice ( pos + 1 ) ;
130+ }
131+ }
132+
133+ public static void SplitOnFirst ( this ReadOnlyMemory < char > strVal , ReadOnlyMemory < char > needle , out ReadOnlyMemory < char > first , out ReadOnlyMemory < char > last )
134+ {
135+ first = default ;
136+ last = default ;
137+ if ( strVal . IsEmpty ) return ;
138+
139+ var pos = strVal . Span . IndexOf ( needle . Span ) ;
140+ if ( pos == - 1 )
141+ {
142+ first = strVal ;
143+ }
144+ else
145+ {
146+ first = strVal . Slice ( 0 , pos ) ;
147+ last = strVal . Slice ( pos + needle . Length ) ;
148+ }
149+ }
150+
151+ public static void SplitOnLast ( this ReadOnlyMemory < char > strVal , char needle , out ReadOnlyMemory < char > first , out ReadOnlyMemory < char > last )
152+ {
153+ first = default ;
154+ last = default ;
155+ if ( strVal . IsEmpty ) return ;
156+
157+ var pos = strVal . Span . LastIndexOf ( needle ) ;
158+ if ( pos == - 1 )
159+ {
160+ first = strVal ;
161+ }
162+ else
163+ {
164+ first = strVal . Slice ( 0 , pos ) ;
165+ last = strVal . Slice ( pos + 1 ) ;
166+ }
167+ }
168+
169+ public static void SplitOnLast ( this ReadOnlyMemory < char > strVal , ReadOnlyMemory < char > needle , out ReadOnlyMemory < char > first , out ReadOnlyMemory < char > last )
170+ {
171+ first = default ;
172+ last = default ;
173+ if ( strVal . IsEmpty ) return ;
174+
175+ var pos = strVal . Span . LastIndexOf ( needle . Span ) ;
176+ if ( pos == - 1 )
177+ {
178+ first = strVal ;
179+ }
180+ else
181+ {
182+ first = strVal . Slice ( 0 , pos ) ;
183+ last = strVal . Slice ( pos + needle . Length ) ;
184+ }
185+ }
186+
187+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
188+ public static int IndexOf ( this ReadOnlyMemory < char > value , string needle ) => value . Span . IndexOf ( needle . AsSpan ( ) ) ;
189+
190+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
191+ public static int IndexOf ( this ReadOnlyMemory < char > value , string needle , int start )
192+ {
193+ var pos = value . Slice ( start ) . Span . IndexOf ( needle . AsSpan ( ) ) ;
194+ return pos == - 1 ? - 1 : start + pos ;
195+ }
196+
197+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
198+ public static int LastIndexOf ( this ReadOnlyMemory < char > value , string needle ) => value . Span . LastIndexOf ( needle . AsSpan ( ) ) ;
199+
200+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
201+ public static int LastIndexOf ( this ReadOnlyMemory < char > value , string needle , int start )
202+ {
203+ var pos = value . Slice ( start ) . Span . LastIndexOf ( needle . AsSpan ( ) ) ;
204+ return pos == - 1 ? - 1 : start + pos ;
205+ }
206+
207+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
208+ public static bool StartsWith ( this ReadOnlyMemory < char > value , string other ) => value . Span . StartsWith ( other . AsSpan ( ) , StringComparison . OrdinalIgnoreCase ) ;
209+
210+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
211+ public static bool EndsWith ( this ReadOnlyMemory < char > value , string other ) => value . Span . EndsWith ( other . AsSpan ( ) , StringComparison . OrdinalIgnoreCase ) ;
212+
213+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
214+ public static bool EqualsOrdinal ( this ReadOnlyMemory < char > value , string other ) => value . Span . Equals ( other . AsSpan ( ) , StringComparison . Ordinal ) ;
215+
216+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
217+ public static bool EqualsOrdinal ( this ReadOnlyMemory < char > value , ReadOnlyMemory < char > other ) => value . Span . Equals ( other . Span , StringComparison . Ordinal ) ;
218+
219+ public static ReadOnlyMemory < char > Trim ( this ReadOnlyMemory < char > span )
220+ {
221+ return span . TrimStart ( ) . TrimEnd ( ) ;
222+ }
223+
224+ public static ReadOnlyMemory < char > TrimStart ( this ReadOnlyMemory < char > value )
225+ {
226+ var span = value . Span ;
227+ int start = 0 ;
228+ for ( ; start < span . Length ; start ++ )
229+ {
230+ if ( ! char . IsWhiteSpace ( span [ start ] ) )
231+ break ;
232+ }
233+ return value . Slice ( start ) ;
234+ }
235+
236+ public static ReadOnlyMemory < char > TrimEnd ( this ReadOnlyMemory < char > value )
237+ {
238+ var span = value . Span ;
239+ int end = span . Length - 1 ;
240+ for ( ; end >= 0 ; end -- )
241+ {
242+ if ( ! char . IsWhiteSpace ( span [ end ] ) )
243+ break ;
244+ }
245+ return value . Slice ( 0 , end + 1 ) ;
246+ }
247+
248+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
249+ public static ReadOnlyMemory < char > SafeSubsegment ( this ReadOnlyMemory < char > value , int startIndex ) => SafeSubsegment ( value , startIndex , value . Length ) ;
250+
251+ public static ReadOnlyMemory < char > SafeSubsegment ( this ReadOnlyMemory < char > value , int startIndex , int length )
252+ {
253+ if ( value . IsEmpty ) return TypeConstants . NullMemory ;
254+ if ( startIndex < 0 ) startIndex = 0 ;
255+ if ( value . Length >= startIndex + length )
256+ return value . Slice ( startIndex , length ) ;
257+
258+ return value . Length > startIndex ? value . Slice ( startIndex ) : TypeConstants . NullMemory ;
259+ }
260+
261+ public static ReadOnlyMemory < byte > ToUtf8 ( this ReadOnlyMemory < char > chars ) =>
262+ MemoryProvider . Instance . ToUtf8 ( chars . Span ) ;
263+
264+ public static ReadOnlyMemory < char > FromUtf8 ( this ReadOnlyMemory < byte > bytes ) =>
265+ MemoryProvider . Instance . FromUtf8 ( bytes . Span ) ;
266+ }
267+ }
0 commit comments