Skip to content

Commit c5e88b1

Browse files
IndexOf, LastIndexOf, and Contains extensions coverage.
1 parent 1974e3e commit c5e88b1

2 files changed

Lines changed: 233 additions & 26 deletions

File tree

Source/Extensions.IndexOf.cs

Lines changed: 87 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)