More Corelib cleanup (dotnet/coreclr#26872)
[mono-project.git] / netcore / System.Private.CoreLib / shared / System / MemoryExtensions.Fast.cs
blobc23ae41efeeae9758512e03c0faa0e7514700f7a
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 using System.Diagnostics;
6 using System.Globalization;
7 using System.Runtime.CompilerServices;
8 using System.Runtime.InteropServices;
10 using Internal.Runtime.CompilerServices;
12 namespace System
14 /// <summary>
15 /// Extension methods for Span{T}, Memory{T}, and friends.
16 /// </summary>
17 public static partial class MemoryExtensions
19 /// <summary>
20 /// Returns a value indicating whether the specified <paramref name="value"/> occurs within the <paramref name="span"/>.
21 /// <param name="span">The source span.</param>
22 /// <param name="value">The value to seek within the source span.</param>
23 /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param>
24 /// </summary>
25 public static bool Contains(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
27 return IndexOf(span, value, comparisonType) >= 0;
30 /// <summary>
31 /// Determines whether this <paramref name="span"/> and the specified <paramref name="other"/> span have the same characters
32 /// when compared using the specified <paramref name="comparisonType"/> option.
33 /// <param name="span">The source span.</param>
34 /// <param name="other">The value to compare with the source span.</param>
35 /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="other"/> are compared.</param>
36 /// </summary>
37 public static bool Equals(this ReadOnlySpan<char> span, ReadOnlySpan<char> other, StringComparison comparisonType)
39 string.CheckStringComparison(comparisonType);
41 switch (comparisonType)
43 case StringComparison.CurrentCulture:
44 return CultureInfo.CurrentCulture.CompareInfo.CompareOptionNone(span, other) == 0;
46 case StringComparison.CurrentCultureIgnoreCase:
47 return CultureInfo.CurrentCulture.CompareInfo.CompareOptionIgnoreCase(span, other) == 0;
49 case StringComparison.InvariantCulture:
50 return CompareInfo.Invariant.CompareOptionNone(span, other) == 0;
52 case StringComparison.InvariantCultureIgnoreCase:
53 return CompareInfo.Invariant.CompareOptionIgnoreCase(span, other) == 0;
55 case StringComparison.Ordinal:
56 return EqualsOrdinal(span, other);
58 case StringComparison.OrdinalIgnoreCase:
59 return EqualsOrdinalIgnoreCase(span, other);
62 Debug.Fail("StringComparison outside range");
63 return false;
66 [MethodImpl(MethodImplOptions.AggressiveInlining)]
67 internal static bool EqualsOrdinal(this ReadOnlySpan<char> span, ReadOnlySpan<char> value)
69 if (span.Length != value.Length)
70 return false;
71 if (value.Length == 0) // span.Length == value.Length == 0
72 return true;
73 return span.SequenceEqual(value);
76 [MethodImpl(MethodImplOptions.AggressiveInlining)]
77 internal static bool EqualsOrdinalIgnoreCase(this ReadOnlySpan<char> span, ReadOnlySpan<char> value)
79 if (span.Length != value.Length)
80 return false;
81 if (value.Length == 0) // span.Length == value.Length == 0
82 return true;
83 return CompareInfo.EqualsOrdinalIgnoreCase(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(value), span.Length);
86 /// <summary>
87 /// Compares the specified <paramref name="span"/> and <paramref name="other"/> using the specified <paramref name="comparisonType"/>,
88 /// and returns an integer that indicates their relative position in the sort order.
89 /// <param name="span">The source span.</param>
90 /// <param name="other">The value to compare with the source span.</param>
91 /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="other"/> are compared.</param>
92 /// </summary>
93 public static int CompareTo(this ReadOnlySpan<char> span, ReadOnlySpan<char> other, StringComparison comparisonType)
95 string.CheckStringComparison(comparisonType);
97 switch (comparisonType)
99 case StringComparison.CurrentCulture:
100 return CultureInfo.CurrentCulture.CompareInfo.CompareOptionNone(span, other);
102 case StringComparison.CurrentCultureIgnoreCase:
103 return CultureInfo.CurrentCulture.CompareInfo.CompareOptionIgnoreCase(span, other);
105 case StringComparison.InvariantCulture:
106 return CompareInfo.Invariant.CompareOptionNone(span, other);
108 case StringComparison.InvariantCultureIgnoreCase:
109 return CompareInfo.Invariant.CompareOptionIgnoreCase(span, other);
111 case StringComparison.Ordinal:
112 if (span.Length == 0 || other.Length == 0)
113 return span.Length - other.Length;
114 return string.CompareOrdinal(span, other);
116 case StringComparison.OrdinalIgnoreCase:
117 return CompareInfo.CompareOrdinalIgnoreCase(span, other);
120 Debug.Fail("StringComparison outside range");
121 return 0;
124 /// <summary>
125 /// Reports the zero-based index of the first occurrence of the specified <paramref name="value"/> in the current <paramref name="span"/>.
126 /// <param name="span">The source span.</param>
127 /// <param name="value">The value to seek within the source span.</param>
128 /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param>
129 /// </summary>
130 public static int IndexOf(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
132 string.CheckStringComparison(comparisonType);
134 if (value.Length == 0)
136 return 0;
139 if (span.Length == 0)
141 return -1;
144 if (comparisonType == StringComparison.Ordinal)
146 return SpanHelpers.IndexOf(
147 ref MemoryMarshal.GetReference(span),
148 span.Length,
149 ref MemoryMarshal.GetReference(value),
150 value.Length);
153 if (GlobalizationMode.Invariant)
155 return CompareInfo.InvariantIndexOf(span, value, string.GetCaseCompareOfComparisonCulture(comparisonType) != CompareOptions.None);
158 switch (comparisonType)
160 case StringComparison.CurrentCulture:
161 case StringComparison.CurrentCultureIgnoreCase:
162 return CultureInfo.CurrentCulture.CompareInfo.IndexOf(span, value, string.GetCaseCompareOfComparisonCulture(comparisonType));
164 case StringComparison.InvariantCulture:
165 case StringComparison.InvariantCultureIgnoreCase:
166 return CompareInfo.Invariant.IndexOf(span, value, string.GetCaseCompareOfComparisonCulture(comparisonType));
168 default:
169 Debug.Assert(comparisonType == StringComparison.OrdinalIgnoreCase);
170 return CompareInfo.Invariant.IndexOfOrdinalIgnoreCase(span, value);
174 /// <summary>
175 /// Reports the zero-based index of the last occurrence of the specified <paramref name="value"/> in the current <paramref name="span"/>.
176 /// <param name="span">The source span.</param>
177 /// <param name="value">The value to seek within the source span.</param>
178 /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param>
179 /// </summary>
180 public static int LastIndexOf(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
182 string.CheckStringComparison(comparisonType);
184 if (value.Length == 0)
186 return span.Length > 0 ? span.Length - 1 : 0;
189 if (span.Length == 0)
191 return -1;
194 if (GlobalizationMode.Invariant)
196 return CompareInfo.InvariantIndexOf(span, value, string.GetCaseCompareOfComparisonCulture(comparisonType) != CompareOptions.None, fromBeginning: false);
199 switch (comparisonType)
201 case StringComparison.CurrentCulture:
202 case StringComparison.CurrentCultureIgnoreCase:
203 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf(span, value, string.GetCaseCompareOfComparisonCulture(comparisonType));
205 case StringComparison.InvariantCulture:
206 case StringComparison.InvariantCultureIgnoreCase:
207 return CompareInfo.Invariant.LastIndexOf(span, value, string.GetCaseCompareOfComparisonCulture(comparisonType));
209 default:
210 Debug.Assert(comparisonType == StringComparison.Ordinal || comparisonType == StringComparison.OrdinalIgnoreCase);
211 return CompareInfo.Invariant.LastIndexOfOrdinal(span, value, string.GetCaseCompareOfComparisonCulture(comparisonType) != CompareOptions.None);
215 /// <summary>
216 /// Copies the characters from the source span into the destination, converting each character to lowercase,
217 /// using the casing rules of the specified culture.
218 /// </summary>
219 /// <param name="source">The source span.</param>
220 /// <param name="destination">The destination span which contains the transformed characters.</param>
221 /// <param name="culture">An object that supplies culture-specific casing rules.</param>
222 /// <remarks>If <paramref name="culture"/> is null, <see cref="System.Globalization.CultureInfo.CurrentCulture"/> will be used.</remarks>
223 /// <returns>The number of characters written into the destination span. If the destination is too small, returns -1.</returns>
224 /// <exception cref="InvalidOperationException">The source and destination buffers overlap.</exception>
225 public static int ToLower(this ReadOnlySpan<char> source, Span<char> destination, CultureInfo? culture)
227 if (source.Overlaps(destination))
228 throw new InvalidOperationException(SR.InvalidOperation_SpanOverlappedOperation);
230 culture ??= CultureInfo.CurrentCulture;
232 // Assuming that changing case does not affect length
233 if (destination.Length < source.Length)
234 return -1;
236 if (GlobalizationMode.Invariant)
237 TextInfo.ToLowerAsciiInvariant(source, destination);
238 else
239 culture.TextInfo.ChangeCaseToLower(source, destination);
240 return source.Length;
243 /// <summary>
244 /// Copies the characters from the source span into the destination, converting each character to lowercase,
245 /// using the casing rules of the invariant culture.
246 /// </summary>
247 /// <param name="source">The source span.</param>
248 /// <param name="destination">The destination span which contains the transformed characters.</param>
249 /// <returns>The number of characters written into the destination span. If the destination is too small, returns -1.</returns>
250 /// <exception cref="InvalidOperationException">The source and destination buffers overlap.</exception>
251 public static int ToLowerInvariant(this ReadOnlySpan<char> source, Span<char> destination)
253 if (source.Overlaps(destination))
254 throw new InvalidOperationException(SR.InvalidOperation_SpanOverlappedOperation);
256 // Assuming that changing case does not affect length
257 if (destination.Length < source.Length)
258 return -1;
260 if (GlobalizationMode.Invariant)
261 TextInfo.ToLowerAsciiInvariant(source, destination);
262 else
263 CultureInfo.InvariantCulture.TextInfo.ChangeCaseToLower(source, destination);
264 return source.Length;
267 /// <summary>
268 /// Copies the characters from the source span into the destination, converting each character to uppercase,
269 /// using the casing rules of the specified culture.
270 /// </summary>
271 /// <param name="source">The source span.</param>
272 /// <param name="destination">The destination span which contains the transformed characters.</param>
273 /// <param name="culture">An object that supplies culture-specific casing rules.</param>
274 /// <remarks>If <paramref name="culture"/> is null, <see cref="System.Globalization.CultureInfo.CurrentCulture"/> will be used.</remarks>
275 /// <returns>The number of characters written into the destination span. If the destination is too small, returns -1.</returns>
276 /// <exception cref="InvalidOperationException">The source and destination buffers overlap.</exception>
277 public static int ToUpper(this ReadOnlySpan<char> source, Span<char> destination, CultureInfo? culture)
279 if (source.Overlaps(destination))
280 throw new InvalidOperationException(SR.InvalidOperation_SpanOverlappedOperation);
282 culture ??= CultureInfo.CurrentCulture;
284 // Assuming that changing case does not affect length
285 if (destination.Length < source.Length)
286 return -1;
288 if (GlobalizationMode.Invariant)
289 TextInfo.ToUpperAsciiInvariant(source, destination);
290 else
291 culture.TextInfo.ChangeCaseToUpper(source, destination);
292 return source.Length;
295 /// <summary>
296 /// Copies the characters from the source span into the destination, converting each character to uppercase
297 /// using the casing rules of the invariant culture.
298 /// </summary>
299 /// <param name="source">The source span.</param>
300 /// <param name="destination">The destination span which contains the transformed characters.</param>
301 /// <returns>The number of characters written into the destination span. If the destination is too small, returns -1.</returns>
302 /// <exception cref="InvalidOperationException">The source and destination buffers overlap.</exception>
303 public static int ToUpperInvariant(this ReadOnlySpan<char> source, Span<char> destination)
305 if (source.Overlaps(destination))
306 throw new InvalidOperationException(SR.InvalidOperation_SpanOverlappedOperation);
308 // Assuming that changing case does not affect length
309 if (destination.Length < source.Length)
310 return -1;
312 if (GlobalizationMode.Invariant)
313 TextInfo.ToUpperAsciiInvariant(source, destination);
314 else
315 CultureInfo.InvariantCulture.TextInfo.ChangeCaseToUpper(source, destination);
316 return source.Length;
319 /// <summary>
320 /// Determines whether the end of the <paramref name="span"/> matches the specified <paramref name="value"/> when compared using the specified <paramref name="comparisonType"/> option.
321 /// </summary>
322 /// <param name="span">The source span.</param>
323 /// <param name="value">The sequence to compare to the end of the source span.</param>
324 /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param>
325 public static bool EndsWith(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
327 string.CheckStringComparison(comparisonType);
329 if (value.Length == 0)
331 return true;
334 if (comparisonType >= StringComparison.Ordinal || GlobalizationMode.Invariant)
336 if (string.GetCaseCompareOfComparisonCulture(comparisonType) == CompareOptions.None)
337 return span.EndsWith(value);
339 return (span.Length >= value.Length) ? (CompareInfo.CompareOrdinalIgnoreCase(span.Slice(span.Length - value.Length), value) == 0) : false;
342 if (span.Length == 0)
344 return false;
347 return (comparisonType >= StringComparison.InvariantCulture) ?
348 CompareInfo.Invariant.IsSuffix(span, value, string.GetCaseCompareOfComparisonCulture(comparisonType)) :
349 CultureInfo.CurrentCulture.CompareInfo.IsSuffix(span, value, string.GetCaseCompareOfComparisonCulture(comparisonType));
352 /// <summary>
353 /// Determines whether the beginning of the <paramref name="span"/> matches the specified <paramref name="value"/> when compared using the specified <paramref name="comparisonType"/> option.
354 /// </summary>
355 /// <param name="span">The source span.</param>
356 /// <param name="value">The sequence to compare to the beginning of the source span.</param>
357 /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param>
358 public static bool StartsWith(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
360 string.CheckStringComparison(comparisonType);
362 if (value.Length == 0)
364 return true;
367 if (comparisonType >= StringComparison.Ordinal || GlobalizationMode.Invariant)
369 if (string.GetCaseCompareOfComparisonCulture(comparisonType) == CompareOptions.None)
370 return span.StartsWith(value);
372 return (span.Length >= value.Length) ? (CompareInfo.CompareOrdinalIgnoreCase(span.Slice(0, value.Length), value) == 0) : false;
375 if (span.Length == 0)
377 return false;
380 return (comparisonType >= StringComparison.InvariantCulture) ?
381 CompareInfo.Invariant.IsPrefix(span, value, string.GetCaseCompareOfComparisonCulture(comparisonType)) :
382 CultureInfo.CurrentCulture.CompareInfo.IsPrefix(span, value, string.GetCaseCompareOfComparisonCulture(comparisonType));
385 /// <summary>
386 /// Creates a new span over the portion of the target array.
387 /// </summary>
388 [MethodImpl(MethodImplOptions.AggressiveInlining)]
389 public static Span<T> AsSpan<T>(this T[]? array, int start)
391 if (array == null)
393 if (start != 0)
394 ThrowHelper.ThrowArgumentOutOfRangeException();
395 return default;
397 if (default(T)! == null && array.GetType() != typeof(T[])) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
398 ThrowHelper.ThrowArrayTypeMismatchException();
399 if ((uint)start > (uint)array.Length)
400 ThrowHelper.ThrowArgumentOutOfRangeException();
402 return new Span<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start), array.Length - start);
405 /// <summary>
406 /// Creates a new span over the portion of the target array.
407 /// </summary>
408 [MethodImpl(MethodImplOptions.AggressiveInlining)]
409 public static Span<T> AsSpan<T>(this T[]? array, Index startIndex)
411 if (array == null)
413 if (!startIndex.Equals(Index.Start))
414 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
416 return default;
419 if (default(T)! == null && array.GetType() != typeof(T[])) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
420 ThrowHelper.ThrowArrayTypeMismatchException();
422 int actualIndex = startIndex.GetOffset(array.Length);
423 if ((uint)actualIndex > (uint)array.Length)
424 ThrowHelper.ThrowArgumentOutOfRangeException();
426 return new Span<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), actualIndex), array.Length - actualIndex);
429 /// <summary>
430 /// Creates a new span over the portion of the target array.
431 /// </summary>
432 [MethodImpl(MethodImplOptions.AggressiveInlining)]
433 public static Span<T> AsSpan<T>(this T[]? array, Range range)
435 if (array == null)
437 Index startIndex = range.Start;
438 Index endIndex = range.End;
440 if (!startIndex.Equals(Index.Start) || !endIndex.Equals(Index.Start))
441 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
443 return default;
446 if (default(T)! == null && array.GetType() != typeof(T[])) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
447 ThrowHelper.ThrowArrayTypeMismatchException();
449 (int start, int length) = range.GetOffsetAndLength(array.Length);
450 return new Span<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start), length);
453 /// <summary>
454 /// Creates a new readonly span over the portion of the target string.
455 /// </summary>
456 /// <param name="text">The target string.</param>
457 /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
458 [MethodImpl(MethodImplOptions.AggressiveInlining)]
459 public static ReadOnlySpan<char> AsSpan(this string? text)
461 if (text == null)
462 return default;
464 return new ReadOnlySpan<char>(ref text.GetRawStringData(), text.Length);
467 /// <summary>
468 /// Creates a new readonly span over the portion of the target string.
469 /// </summary>
470 /// <param name="text">The target string.</param>
471 /// <param name="start">The index at which to begin this slice.</param>
472 /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="text"/> is null.</exception>
473 /// <exception cref="System.ArgumentOutOfRangeException">
474 /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;text.Length).
475 /// </exception>
476 [MethodImpl(MethodImplOptions.AggressiveInlining)]
477 public static ReadOnlySpan<char> AsSpan(this string? text, int start)
479 if (text == null)
481 if (start != 0)
482 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
483 return default;
486 if ((uint)start > (uint)text.Length)
487 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
489 return new ReadOnlySpan<char>(ref Unsafe.Add(ref text.GetRawStringData(), start), text.Length - start);
492 /// <summary>
493 /// Creates a new readonly span over the portion of the target string.
494 /// </summary>
495 /// <param name="text">The target string.</param>
496 /// <param name="start">The index at which to begin this slice.</param>
497 /// <param name="length">The desired length for the slice (exclusive).</param>
498 /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
499 /// <exception cref="System.ArgumentOutOfRangeException">
500 /// Thrown when the specified <paramref name="start"/> index or <paramref name="length"/> is not in range.
501 /// </exception>
502 [MethodImpl(MethodImplOptions.AggressiveInlining)]
503 public static ReadOnlySpan<char> AsSpan(this string? text, int start, int length)
505 if (text == null)
507 if (start != 0 || length != 0)
508 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
509 return default;
512 #if BIT64
513 // See comment in Span<T>.Slice for how this works.
514 if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length)
515 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
516 #else
517 if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
518 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
519 #endif
521 return new ReadOnlySpan<char>(ref Unsafe.Add(ref text.GetRawStringData(), start), length);
524 /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target string.</summary>
525 /// <param name="text">The target string.</param>
526 /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
527 public static ReadOnlyMemory<char> AsMemory(this string? text)
529 if (text == null)
530 return default;
532 return new ReadOnlyMemory<char>(text, 0, text.Length);
535 /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target string.</summary>
536 /// <param name="text">The target string.</param>
537 /// <param name="start">The index at which to begin this slice.</param>
538 /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
539 /// <exception cref="System.ArgumentOutOfRangeException">
540 /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;text.Length).
541 /// </exception>
542 public static ReadOnlyMemory<char> AsMemory(this string? text, int start)
544 if (text == null)
546 if (start != 0)
547 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
548 return default;
551 if ((uint)start > (uint)text.Length)
552 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
554 return new ReadOnlyMemory<char>(text, start, text.Length - start);
557 /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target string.</summary>
558 /// <param name="text">The target string.</param>
559 /// <param name="startIndex">The index at which to begin this slice.</param>
560 public static ReadOnlyMemory<char> AsMemory(this string? text, Index startIndex)
562 if (text == null)
564 if (!startIndex.Equals(Index.Start))
565 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
567 return default;
570 int actualIndex = startIndex.GetOffset(text.Length);
571 if ((uint)actualIndex > (uint)text.Length)
572 ThrowHelper.ThrowArgumentOutOfRangeException();
574 return new ReadOnlyMemory<char>(text, actualIndex, text.Length - actualIndex);
577 /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target string.</summary>
578 /// <param name="text">The target string.</param>
579 /// <param name="start">The index at which to begin this slice.</param>
580 /// <param name="length">The desired length for the slice (exclusive).</param>
581 /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
582 /// <exception cref="System.ArgumentOutOfRangeException">
583 /// Thrown when the specified <paramref name="start"/> index or <paramref name="length"/> is not in range.
584 /// </exception>
585 public static ReadOnlyMemory<char> AsMemory(this string? text, int start, int length)
587 if (text == null)
589 if (start != 0 || length != 0)
590 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
591 return default;
594 #if BIT64
595 // See comment in Span<T>.Slice for how this works.
596 if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length)
597 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
598 #else
599 if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
600 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
601 #endif
603 return new ReadOnlyMemory<char>(text, start, length);
606 /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target string.</summary>
607 /// <param name="text">The target string.</param>
608 /// <param name="range">The range used to indicate the start and length of the sliced string.</param>
609 public static ReadOnlyMemory<char> AsMemory(this string? text, Range range)
611 if (text == null)
613 Index startIndex = range.Start;
614 Index endIndex = range.End;
616 if (!startIndex.Equals(Index.Start) || !endIndex.Equals(Index.Start))
617 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
619 return default;
622 (int start, int length) = range.GetOffsetAndLength(text.Length);
623 return new ReadOnlyMemory<char>(text, start, length);