@@ -25,7 +25,7 @@ public static int IndexOf(this ReadOnlySpan<char> source, char search, StringCom
2525 }
2626
2727 var searchUpper = toUpper ( search ) ;
28- if ( searchUpper == search )
28+ if ( searchUpper == search )
2929 return source . IndexOf ( search ) ;
3030
3131 for ( var i = 0 ; i < source . Length ; i ++ )
@@ -39,6 +39,15 @@ public static int IndexOf(this ReadOnlySpan<char> source, char search, StringCom
3939 return - 1 ;
4040 }
4141
42+ /// <inheritdoc cref="string.IndexOf(char)"/>
43+ public static int IndexOf ( this ReadOnlySpan < char > source , char search , int startIndex , StringComparison comparisonType )
44+ {
45+ if ( startIndex >= source . Length )
46+ throw new ArgumentOutOfRangeException ( nameof ( startIndex ) , startIndex , "Must be less than the length of the source." ) ;
47+ var i = source . Slice ( startIndex ) . IndexOf ( search , comparisonType ) ;
48+ return i == - 1 ? - 1 : i + startIndex ;
49+ }
50+
4251 /// <inheritdoc cref="string.LastIndexOf(char)"/>
4352 public static int LastIndexOf ( this ReadOnlySpan < char > source , char search , StringComparison comparisonType )
4453 {
@@ -77,22 +86,30 @@ public static int LastIndexOf(this ReadOnlySpan<char> source, char search, Strin
7786 return - 1 ;
7887 }
7988
89+ /// <inheritdoc cref="string.LastIndexOf(char)"/>
90+ public static int LastIndexOf ( this string source , char search , StringComparison comparisonType )
91+ => source . AsSpan ( ) . LastIndexOf ( search , comparisonType ) ;
92+
93+ /// <inheritdoc cref="string.LastIndexOf(char)"/>
94+ public static int LastIndexOf ( this StringSegment source , char search , StringComparison comparisonType )
95+ => source . AsSpan ( ) . LastIndexOf ( search , comparisonType ) ;
96+
8097 /// <inheritdoc cref="StringSegment.IndexOf(char)"/>
8198 public static int IndexOf ( this StringSegment source , char search , StringComparison comparisonType )
8299 => source . HasValue ? source . AsSpan ( ) . IndexOf ( search , comparisonType ) : - 1 ;
83100
84- /// <inheritdoc cref="StringSegment.LastIndexOf (char)"/>
85- public static int LastIndexOf ( this StringSegment source , char search , StringComparison comparisonType )
86- => source . HasValue ? source . AsSpan ( ) . LastIndexOf ( search , comparisonType ) : - 1 ;
101+ /// <inheritdoc cref="StringSegment.IndexOf (char)"/>
102+ public static int IndexOf ( this StringSegment source , char search , int startIndex , StringComparison comparisonType )
103+ => source . HasValue ? source . AsSpan ( ) . IndexOf ( search , startIndex , comparisonType ) : - 1 ;
87104
88105#if NETSTANDARD2_0
89106 /// <inheritdoc cref="StringSegment.IndexOf(char)"/>
90107 public static int IndexOf ( this string source , char search , StringComparison comparisonType )
91- => source is null ? throw new ArgumentNullException ( nameof ( source ) ) : source . AsSpan ( ) . IndexOf ( search , comparisonType ) ;
108+ => source . AsSpan ( ) . IndexOf ( search , comparisonType ) ;
92109
93110 /// <inheritdoc cref="StringSegment.IndexOf(char)"/>
94- public static int LastIndexOf ( this string source , char search , StringComparison comparisonType )
95- => source is null ? throw new ArgumentNullException ( nameof ( source ) ) : source . AsSpan ( ) . LastIndexOf ( search , comparisonType ) ;
111+ public static int LastIndexOf ( this string source , char search )
112+ => source . AsSpan ( ) . LastIndexOf ( search ) ;
96113#endif
97114
98115 /// <summary>
@@ -106,6 +123,9 @@ public static int IndexOf(
106123 ReadOnlySpan < char > sequence ,
107124 StringComparison comparisonType = StringComparison . Ordinal )
108125 {
126+ // This is a weird decision, but must be this way to maintain parity with other methods.
127+ if ( sequence . IsEmpty ) return 0 ;
128+
109129 switch ( comparisonType )
110130 {
111131 case StringComparison . OrdinalIgnoreCase :
@@ -126,9 +146,6 @@ public static int IndexOf(
126146 }
127147
128148 int sequenceLength = sequence . Length ;
129- if ( sequenceLength == 0 )
130- throw new ArgumentException ( "Sequence must have at least one character." , nameof ( sequence ) ) ;
131-
132149 int sourceLength = source . Length ;
133150 if ( sourceLength == 0 ) return - 1 ;
134151 if ( sequenceLength > sourceLength ) return - 1 ;
@@ -175,6 +192,10 @@ public static int LastIndexOf(
175192 ReadOnlySpan < char > sequence ,
176193 StringComparison comparisonType )
177194 {
195+ // This is a weird decision, but must be this way to maintain parity with other methods.
196+ if ( sequence . IsEmpty )
197+ return source . Length ;
198+
178199 switch ( comparisonType )
179200 {
180201 case StringComparison . OrdinalIgnoreCase :
@@ -195,9 +216,6 @@ public static int LastIndexOf(
195216 }
196217
197218 int sequenceLength = sequence . Length ;
198- if ( sequenceLength == 0 )
199- throw new ArgumentException ( "Sequence must have at least one character." , nameof ( sequence ) ) ;
200-
201219 int sourceLength = source . Length ;
202220 if ( sourceLength == 0 ) return - 1 ;
203221 if ( sequenceLength > sourceLength ) return - 1 ;
@@ -234,13 +252,21 @@ public static int LastIndexOf(this StringSegment source, ReadOnlySpan<char> sequ
234252 => source . AsSpan ( ) . LastIndexOf ( sequence , comparisonType ) ;
235253#pragma warning restore CS1587 // XML comment is not placed on a valid language element
236254
255+ /// <inheritdoc cref="LastIndexOf(StringSegment, ReadOnlySpan{char}, StringComparison)"/>
256+ public static int LastIndexOf ( this string source , StringSegment sequence , StringComparison comparisonType )
257+ => sequence . HasValue ? source . AsSpan ( ) . LastIndexOf ( sequence . AsSpan ( ) , comparisonType ) : - 1 ;
258+
259+ /// <inheritdoc cref="LastIndexOf(StringSegment, ReadOnlySpan{char}, StringComparison)"/>
260+ public static int LastIndexOf ( this string source , ReadOnlySpan < char > sequence , StringComparison comparisonType )
261+ => source . AsSpan ( ) . LastIndexOf ( sequence , comparisonType ) ;
262+
237263 /// <inheritdoc cref="LastIndexOf(StringSegment, ReadOnlySpan{char}, StringComparison)"/>
238264 public static int LastIndexOf ( this ReadOnlySpan < char > source , StringSegment sequence , StringComparison comparisonType )
239- => source . LastIndexOf ( sequence . AsSpan ( ) , comparisonType ) ;
265+ => sequence . HasValue ? source . LastIndexOf ( sequence . AsSpan ( ) , comparisonType ) : - 1 ;
240266
241267 /// <inheritdoc cref="LastIndexOf(StringSegment, ReadOnlySpan{char}, StringComparison)"/>
242268 public static int LastIndexOf ( this StringSegment source , StringSegment sequence , StringComparison comparisonType )
243- => source . AsSpan ( ) . LastIndexOf ( sequence . AsSpan ( ) , comparisonType ) ;
269+ => sequence . HasValue ? source . AsSpan ( ) . LastIndexOf ( sequence . AsSpan ( ) , comparisonType ) : - 1 ;
244270
245271 /// <summary>
246272 /// Reports the zero-based index of the first occurrence
@@ -261,7 +287,10 @@ public static int IndexOf(
261287 if ( startIndex < 0 )
262288 throw new ArgumentOutOfRangeException ( nameof ( startIndex ) , startIndex , "Must be at least zero." ) ;
263289
264- if ( startIndex == 0 )
290+ if ( sequence . IsEmpty )
291+ return 0 ;
292+
293+ if ( startIndex == 0 )
265294 return IndexOf ( source , sequence , comparisonType ) ;
266295
267296 var span = source . Slice ( startIndex ) ;
@@ -275,7 +304,7 @@ public static int IndexOf(
275304 StringSegment sequence ,
276305 int startIndex = 0 ,
277306 StringComparison comparisonType = StringComparison . Ordinal )
278- => source . IndexOf ( sequence . AsSpan ( ) , startIndex , comparisonType ) ;
307+ => sequence . HasValue ? source . IndexOf ( sequence . AsSpan ( ) , startIndex , comparisonType ) : - 1 ;
279308
280309 /// <inheritdoc cref="IndexOf(ReadOnlySpan{char}, ReadOnlySpan{char}, int, StringComparison)"/>
281310 public static int IndexOf (
@@ -293,6 +322,14 @@ public static int IndexOf(
293322 StringComparison comparisonType = StringComparison . Ordinal )
294323 => source . AsSpan ( ) . IndexOf ( sequence , startIndex , comparisonType ) ;
295324
325+ /// <inheritdoc cref="IndexOf(ReadOnlySpan{char}, ReadOnlySpan{char}, int, StringComparison)"/>
326+ public static int IndexOf (
327+ this string source ,
328+ StringSegment sequence ,
329+ int startIndex = 0 ,
330+ StringComparison comparisonType = StringComparison . Ordinal )
331+ => source . AsSpan ( ) . IndexOf ( sequence , startIndex , comparisonType ) ;
332+
296333 /// <inheritdoc cref="IndexOf(ReadOnlySpan{char}, ReadOnlySpan{char}, int, StringComparison)"/>
297334 public static int IndexOf (
298335 this StringSegment source ,
@@ -310,7 +347,7 @@ public static int IndexOf(
310347 this StringSegment source ,
311348 StringSegment sequence ,
312349 StringComparison comparisonType )
313- => IndexOf ( source . AsSpan ( ) , sequence . AsSpan ( ) , comparisonType ) ;
350+ => sequence . HasValue ? IndexOf ( source . AsSpan ( ) , sequence . AsSpan ( ) , comparisonType ) : - 1 ;
314351
315352 /// <inheritdoc cref="IndexOf(StringSegment, StringSegment, StringComparison)"/>
316353 public static int IndexOf (
@@ -331,14 +368,39 @@ public static int IndexOf(
331368 this ReadOnlySpan < char > source ,
332369 StringSegment sequence ,
333370 StringComparison comparisonType )
334- => IndexOf ( source , sequence . AsSpan ( ) , comparisonType ) ;
371+ => sequence . HasValue ? IndexOf ( source , sequence . AsSpan ( ) , comparisonType ) : - 1 ;
335372
336373 /// <inheritdoc cref="IndexOf(StringSegment, StringSegment, StringComparison)"/>
337374 public static int IndexOf (
338375 this string source ,
339376 StringSegment sequence ,
340377 StringComparison comparisonType = StringComparison . Ordinal )
341- => IndexOf ( source . AsSpan ( ) , sequence . AsSpan ( ) , comparisonType ) ;
378+ => sequence . HasValue ? IndexOf ( source . AsSpan ( ) , sequence . AsSpan ( ) , comparisonType ) : - 1 ;
379+
380+ /// <summary>
381+ /// Checks if the <paramref name="value"/> is contained
382+ /// within the <paramref name="source"/> using the <paramref name="comparisonType"/>.
383+ /// </summary>
384+ /// <returns>
385+ /// <see langword="true"/> if the <paramref name="value"/> is contained;
386+ /// otherwise <see langword="false"/>.
387+ /// </returns>
388+ public static bool Contains (
389+ this ReadOnlySpan < char > source ,
390+ char value , StringComparison comparisonType = StringComparison . Ordinal )
391+ => IndexOf ( source , value , comparisonType ) != - 1 ;
392+
393+ /// <inheritdoc cref="Contains(ReadOnlySpan{char}, char, StringComparison)"/>
394+ public static bool Contains (
395+ this StringSegment source ,
396+ char value , StringComparison comparisonType = StringComparison . Ordinal )
397+ => IndexOf ( source , value , comparisonType ) != - 1 ;
398+
399+ #if NETSTANDARD2_0
400+ /// <inheritdoc cref="Contains(ReadOnlySpan{char}, char, StringComparison)"/>
401+ public static bool Contains ( this string source , char value , StringComparison comparisonType )
402+ => source . IndexOf ( value , comparisonType ) != - 1 ;
403+ #endif
342404
343405 /// <summary>
344406 /// Checks if the <paramref name="sequence"/> is contained
@@ -351,29 +413,29 @@ public static int IndexOf(
351413 public static bool Contains (
352414 this StringSegment source ,
353415 StringSegment sequence , StringComparison comparisonType = StringComparison . Ordinal )
354- => IndexOf ( source . AsSpan ( ) , sequence . AsSpan ( ) , comparisonType ) != - 1 ;
416+ => IndexOf ( source , sequence , comparisonType ) != - 1 ;
355417
356418 /// <inheritdoc cref="Contains(StringSegment, StringSegment, StringComparison)"/>
357419 public static bool Contains (
358420 this StringSegment source ,
359421 ReadOnlySpan < char > sequence , StringComparison comparisonType = StringComparison . Ordinal )
360- => IndexOf ( source . AsSpan ( ) , sequence , comparisonType ) != - 1 ;
422+ => IndexOf ( source , sequence , comparisonType ) != - 1 ;
361423
362424 /// <inheritdoc cref="Contains(StringSegment, StringSegment, StringComparison)"/>
363425 public static bool Contains (
364426 this ReadOnlySpan < char > source ,
365427 StringSegment sequence , StringComparison comparisonType = StringComparison . Ordinal )
366- => IndexOf ( source , sequence . AsSpan ( ) , comparisonType ) != - 1 ;
428+ => IndexOf ( source , sequence , comparisonType ) != - 1 ;
367429
368430 /// <inheritdoc cref="Contains(StringSegment, StringSegment, StringComparison)"/>
369431 public static bool Contains (
370432 this string source ,
371433 StringSegment sequence , StringComparison comparisonType = StringComparison . Ordinal )
372- => IndexOf ( source . AsSpan ( ) , sequence . AsSpan ( ) , comparisonType ) != - 1 ;
434+ => IndexOf ( source , sequence , comparisonType ) != - 1 ;
373435
374436 /// <inheritdoc cref="Contains(StringSegment, StringSegment, StringComparison)"/>
375437 public static bool Contains (
376438 this string source ,
377439 ReadOnlySpan < char > sequence , StringComparison comparisonType = StringComparison . Ordinal )
378- => IndexOf ( source . AsSpan ( ) , sequence , comparisonType ) != - 1 ;
440+ => IndexOf ( source , sequence , comparisonType ) != - 1 ;
379441}
0 commit comments