// GetObjectValue is intended to allow value classes to be manipulated as 'Object'
// but have aliasing behavior of a value class. The intent is that you would use
// this function just before an assignment to a variable of type 'Object'. If the
- // value being assigned is a mutable value class, then a shallow copy is returned
+ // value being assigned is a mutable value class, then a shallow copy is returned
// (because value classes have copy semantics), but otherwise the object itself
- // is returned.
+ // is returned.
//
// Note: VB calls this method when they're about to assign to an Object
- // or pass it as a parameter. The goal is to make sure that boxed
- // value types work identical to unboxed value types - ie, they get
- // cloned when you pass them around, and are always passed by value.
+ // or pass it as a parameter. The goal is to make sure that boxed
+ // value types work identical to unboxed value types - ie, they get
+ // cloned when you pass them around, and are always passed by value.
// Of course, reference types are not cloned.
//
[MethodImplAttribute(MethodImplOptions.InternalCall)]
// have at least been started by some thread. In the absence of class constructor
// deadlock conditions, the call is further guaranteed to have completed.
//
- // This call will generate an exception if the specified class constructor threw an
- // exception when it ran.
+ // This call will generate an exception if the specified class constructor threw an
+ // exception when it ran.
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private static extern void _RunClassConstructor(RuntimeType type);
// have at least been started by some thread. In the absence of module constructor
// deadlock conditions, the call is further guaranteed to have completed.
//
- // This call will generate an exception if the specified module constructor threw an
- // exception when it ran.
+ // This call will generate an exception if the specified module constructor threw an
+ // exception when it ran.
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private static extern void _RunModuleConstructor(System.Reflection.RuntimeModule module);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private static extern unsafe void _PrepareMethod(IRuntimeMethodInfo method, IntPtr* pInstantiation, int cInstantiation);
- public static void PrepareMethod(RuntimeMethodHandle method)
+ public static void PrepareMethod(RuntimeMethodHandle method)
{
unsafe
{
get
{
// Number of bytes from the address pointed to by a reference to
- // a String to the first 16-bit character in the String. Skip
- // over the MethodTable pointer, & String
- // length. Of course, the String reference points to the memory
- // after the sync block, so don't count that.
+ // a String to the first 16-bit character in the String. Skip
+ // over the MethodTable pointer, & String
+ // length. Of course, the String reference points to the memory
+ // after the sync block, so don't count that.
// This property allows C#'s fixed statement to work on Strings.
// On 64 bit platforms, this should be 12 (8+4) and on 32 bit 8 (4+4).
#if BIT64
throw new InvalidOperationException();
}
+ /// <summary>
+ /// GetSubArray helper method for the compiler to slice an array using a range.
+ /// </summary>
+ public static T[] GetSubArray<T>(T[] array, Range range)
+ {
+ Type elementType = array.GetType().GetElementType();
+ Span<T> source = array.AsSpan(range);
+
+ if (elementType.IsValueType)
+ {
+ return source.ToArray();
+ }
+ else
+ {
+ T[] newArray = (T[])Array.CreateInstance(elementType, source.Length);
+ source.CopyTo(newArray);
+ return newArray;
+ }
+ }
+
// Returns true iff the object has a component size;
// i.e., is variable length like System.String or Array.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
// This is not ideal in terms of minimizing instruction count but is the best we can do at the moment.
return Unsafe.Add(ref Unsafe.As<byte, IntPtr>(ref obj.GetRawData()), -1);
-
+
// The JIT currently implements this as:
// lea tmp, [rax + 8h] ; assume rax contains the object reference, tmp is type IntPtr&
// mov tmp, qword ptr [tmp - 8h] ; tmp now contains the MethodTable* pointer
"name": "System.Tests.TimeSpanTests.Parse",
"reason": "Temporary disabling till merging the PR https://github.com/dotnet/corefx/pull/34561"
},
-
{
"name": "System.Tests.SingleTests.Test_ToString",
"reason" : "https://github.com/dotnet/coreclr/pull/22040"
<ExcludeList Include="$(XunitTestBinBase)/JIT/Regression/JitBlue/GitHub_11408/GitHub_11408/*">
<Issue>11408</Issue>
</ExcludeList>
+ <ExcludeList Include="$(XunitTestBinBase)/JIT/Regression/JitBlue/GitHub_20958/GitHub_20958/*">
+ <Issue>22410</Issue>
+ </ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/baseservices/exceptions/StackTracePreserve/StackTracePreserveTests/*">
<Issue>20322</Issue>
</ExcludeList>
</ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical/tailcall_v4/hijacking/*">
<Issue>needs triage</Issue>
- </ExcludeList>
+ </ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical/xxobj/sizeof/_il_dbgsizeof/*">
<Issue>needs triage</Issue>
- </ExcludeList>
+ </ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical/xxobj/sizeof/_il_dbgsizeof32/*">
<Issue>needs triage</Issue>
</ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical/xxobj/sizeof/_il_dbgsizeof64/*">
<Issue>needs triage</Issue>
- </ExcludeList>
+ </ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical/xxobj/sizeof/_il_relsizeof/*">
<Issue>needs triage</Issue>
</ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical/xxobj/sizeof/_il_relsizeof32/*">
<Issue>needs triage</Issue>
- </ExcludeList>
+ </ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical/xxobj/sizeof/_il_relsizeof64/*">
<Issue>needs triage</Issue>
</ExcludeList>
</ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/JIT/jit64/opt/cse/HugeArray/*">
<Issue>needs triage</Issue>
- </ExcludeList>
+ </ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/JIT/jit64/opt/cse/HugeArray1/*">
<Issue>needs triage</Issue>
- </ExcludeList>
+ </ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/JIT/jit64/opt/cse/HugeField1/*">
<Issue>needs triage</Issue>
</ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/JIT/jit64/opt/cse/HugeField2/*">
<Issue>needs triage</Issue>
- </ExcludeList>
+ </ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/CoreMangLib/cti/system/string/StringFormat1/*">
<Issue>needs triage</Issue>
</ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/CoreMangLib/cti/system/string/StringFormat2/*">
<Issue>needs triage</Issue>
- </ExcludeList>
+ </ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/JIT/HardwareIntrinsics/Arm64/Simd/*">
<Issue>18895</Issue>
</ExcludeList>
<Issue>2420. x86 JIT doesn't support implicit tail call optimization or tail. call pop ret sequence</Issue>
</ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/JIT/Regression/JitBlue/DevDiv_255294/DevDiv_255294/*">
- <Issue>11469, The test causes OutOfMemory exception in crossgen mode.</Issue>
+ <Issue>11469, The test causes OutOfMemory exception in crossgen mode.</Issue>
</ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/baseservices/varargs/varargsupport/*">
<Issue>Varargs supported on this platform</Issue>
<Issue>22015</Issue>
</ExcludeList>
</ItemGroup>
-
+
<!-- Tests that need to be triaged for vararg usage as that is not supported -->
<!-- Note these will only be excluded for unix platforms -->
</ItemGroup>
<!-- Failures while testing via ILLINK -->
-
+
<ItemGroup Condition="'$(XunitTestBinBase)' != '' and '$(RunTestsViaIllink)' == 'true'">
<ExcludeList Include="$(XunitTestBinBase)/JIT/superpmi/superpmicollect/*">
<!-- Linker runs reset CORE_ROOT to the linked directory based on superpmicollect.exe.
public static int IndexerWithRangeTest()
{
int returnVal = 100;
-
+
ReadOnlySpan<char> span = "Hello".AsSpan();
ReadOnlySpan<char> sliced = span[Range.Create(new Index(1, fromEnd: false), new Index(1, fromEnd: true))];
if (span.Slice(1, 3) != sliced)
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
+using System.Runtime.CompilerServices;
namespace System
{
+ /// <summary>Represent a type can be used to index a collection either from the start or the end.</summary>
+ /// <remarks>
+ /// Index is used by the C# compiler to support the new index syntax
+ /// <code>
+ /// int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ;
+ /// int lastElement = someArray[^1]; // lastElement = 5
+ /// </code>
+ /// </remarks>
public readonly struct Index : IEquatable<Index>
{
private readonly int _value;
- public Index(int value, bool fromEnd)
+ /// <summary>Construct an Index using a value and indicating if the index is from the start or from the end.</summary>
+ /// <param name="value">The index value. it has to be zero or positive number.</param>
+ /// <param name="fromEnd">Indicating if the index is from the start or from the end.</param>
+ /// <remarks>
+ /// If the Index constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Index(int value, bool fromEnd = false)
{
if (value < 0)
{
ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException();
}
- _value = fromEnd ? ~value : value;
+ if (fromEnd)
+ _value = ~value;
+ else
+ _value = value;
}
- public int Value => _value < 0 ? ~_value : _value;
- public bool FromEnd => _value < 0;
+ // The following private constructors mainly created for perf reason to avoid the checks
+ private Index(int value)
+ {
+ _value = value;
+ }
+
+ /// <summary>Create an Index pointing at first element.</summary>
+ public static Index Start => new Index(0);
+
+ /// <summary>Create an Index pointing at beyond last element.</summary>
+ public static Index End => new Index(~0);
+
+ /// <summary>Create an Index from the start at the position indicated by the value.</summary>
+ /// <param name="value">The index value from the start.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Index FromStart(int value)
+ {
+ if (value < 0)
+ {
+ ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException();
+ }
+
+ return new Index(value);
+ }
+
+ /// <summary>Create an Index from the end at the position indicated by the value.</summary>
+ /// <param name="value">The index value from the end.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Index FromEnd(int value)
+ {
+ if (value < 0)
+ {
+ ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException();
+ }
+
+ return new Index(~value);
+ }
+
+ /// <summary>Returns the index value.</summary>
+ public int Value
+ {
+ get
+ {
+ if (_value < 0)
+ return ~_value;
+ else
+ return _value;
+ }
+ }
+
+ /// <summary>Indicates whether the index is from the start or the end.</summary>
+ public bool IsFromEnd => _value < 0;
+
+ /// <summary>Calculate the offset from the start using the giving collection length.</summary>
+ /// <param name="length">The length of the collection that the Index will be used with. length has to be a positive value</param>
+ /// <remarks>
+ /// For performance reason, we don't validate the input length parameter and the returned offset value against negative values.
+ /// we don't validate either the returned offset is greater than the input length.
+ /// It is expected Index will be used with collections which always have non negative length/count. If the returned offset is negative and
+ /// then used to index a collection will get out of range exception which will be same affect as the validation.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int GetOffset(int length)
+ {
+ int offset;
+
+ if (IsFromEnd)
+ offset = length - (~_value);
+ else
+ offset = _value;
+
+ return offset;
+ }
+
+ /// <summary>Indicates whether the current Index object is equal to another object of the same type.</summary>
+ /// <param name="value">An object to compare with this object</param>
public override bool Equals(object value) => value is Index && _value == ((Index)value)._value;
+
+ /// <summary>Indicates whether the current Index object is equal to another Index object.</summary>
+ /// <param name="other">An object to compare with this object</param>
public bool Equals (Index other) => _value == other._value;
- public override int GetHashCode()
+ /// <summary>Returns the hash code for this instance.</summary>
+ public override int GetHashCode() => _value;
+
+ /// <summary>Converts integer number to an Index.</summary>
+ public static implicit operator Index(int value) => FromStart(value);
+
+ /// <summary>Converts the value of the current Index object to its equivalent string representation.</summary>
+ public override string ToString()
{
- return _value;
- }
+ if (IsFromEnd)
+ return ToStringFromEnd();
- public override string ToString() => FromEnd ? ToStringFromEnd() : ((uint)Value).ToString();
+ return ((uint)Value).ToString();
+ }
private string ToStringFromEnd()
{
return new string(span.Slice(0, charsWritten + 1));
}
- public static implicit operator Index(int value)
- => new Index(value, fromEnd: false);
}
}
private readonly object _object;
private readonly int _index;
private readonly int _length;
-
+
/// <summary>
/// Creates a new memory over the entirety of the target array.
/// </summary>
}
/// <summary>
+ /// Forms a slice out of the given memory, beginning at 'startIndex'
+ /// </summary>
+ /// <param name="startIndex">The index at which to begin this slice.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Memory<T> Slice(Index startIndex)
+ {
+ int actualIndex = startIndex.GetOffset(_length);
+ return Slice(actualIndex);
+ }
+
+ /// <summary>
+ /// Forms a slice out of the given memory using the range start and end indexes.
+ /// </summary>
+ /// <param name="range">The range used to slice the memory using its start and end indexes.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Memory<T> Slice(Range range)
+ {
+ (int start, int length) = range.GetOffsetAndLength(_length);
+ // It is expected for _index + start to be negative if the memory is already pre-pinned.
+ return new Memory<T>(_object, _index + start, length);
+ }
+
+ /// <summary>
+ /// Forms a slice out of the given memory using the range start and end indexes.
+ /// </summary>
+ /// <param name="range">The range used to slice the memory using its start and end indexes.</param>
+ public Memory<T> this[Range range] => Slice(range);
+
+ /// <summary>
/// Returns a span from the memory.
/// </summary>
public unsafe Span<T> Span
ThrowHelper.ThrowArgumentOutOfRangeException();
}
#endif
-
+
refToReturn = ref Unsafe.Add(ref refToReturn, desiredStartIndex);
lengthOfUnderlyingSpan = desiredLength;
}
}
/// <summary>
+ /// Creates a new span over the portion of the target array.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Span<T> AsSpan<T>(this T[] array, Index startIndex)
+ {
+ if (array == null)
+ {
+ if (!startIndex.Equals(Index.Start))
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+
+ return default;
+ }
+
+ if (default(T) == null && array.GetType() != typeof(T[]))
+ ThrowHelper.ThrowArrayTypeMismatchException();
+
+ int actualIndex = startIndex.GetOffset(array.Length);
+ if ((uint)actualIndex > (uint)array.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ return new Span<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), actualIndex), array.Length - actualIndex);
+ }
+
+ /// <summary>
+ /// Creates a new span over the portion of the target array.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Span<T> AsSpan<T>(this T[] array, Range range)
+ {
+ if (array == null)
+ {
+ Index startIndex = range.Start;
+ Index endIndex = range.End;
+
+ if (!startIndex.Equals(Index.Start) || !endIndex.Equals(Index.Start))
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+
+ return default;
+ }
+
+ if (default(T) == null && array.GetType() != typeof(T[]))
+ ThrowHelper.ThrowArrayTypeMismatchException();
+
+ (int start, int length) = range.GetOffsetAndLength(array.Length);
+ return new Span<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start), length);
+ }
+
+ /// <summary>
/// Creates a new readonly span over the portion of the target string.
/// </summary>
/// <param name="text">The target string.</param>
/// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target string.</summary>
/// <param name="text">The target string.</param>
+ /// <param name="startIndex">The index at which to begin this slice.</param>
+ public static ReadOnlyMemory<char> AsMemory(this string text, Index startIndex)
+ {
+ if (text == null)
+ {
+ if (!startIndex.Equals(Index.Start))
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
+
+ return default;
+ }
+
+ int actualIndex = startIndex.GetOffset(text.Length);
+ if ((uint)actualIndex > (uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ return new ReadOnlyMemory<char>(text, actualIndex, text.Length - actualIndex);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target string.</summary>
+ /// <param name="text">The target string.</param>
/// <param name="start">The index at which to begin this slice.</param>
/// <param name="length">The desired length for the slice (exclusive).</param>
/// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
return new ReadOnlyMemory<char>(text, start, length);
}
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target string.</summary>
+ /// <param name="text">The target string.</param>
+ /// <param name="range">The range used to indicate the start and length of the sliced string.</param>
+ public static ReadOnlyMemory<char> AsMemory(this string text, Range range)
+ {
+ if (text == null)
+ {
+ Index startIndex = range.Start;
+ Index endIndex = range.End;
+
+ if (!startIndex.Equals(Index.Start) || !endIndex.Equals(Index.Start))
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
+
+ return default;
+ }
+
+ (int start, int length) = range.GetOffsetAndLength(text.Length);
+ return new ReadOnlyMemory<char>(text, start, length);
+ }
}
}
}
/// <summary>
- /// Removes all leading and trailing occurrences of a set of characters specified
+ /// Removes all leading and trailing occurrences of a set of characters specified
/// in a readonly span from the span.
/// </summary>
/// <param name="span">The source span from which the characters are removed.</param>
}
/// <summary>
- /// Removes all leading occurrences of a set of characters specified
+ /// Removes all leading occurrences of a set of characters specified
/// in a readonly span from the span.
/// </summary>
/// <param name="span">The source span from which the characters are removed.</param>
}
/// <summary>
- /// Removes all trailing occurrences of a set of characters specified
+ /// Removes all trailing occurrences of a set of characters specified
/// in a readonly span from the span.
/// </summary>
/// <param name="span">The source span from which the characters are removed.</param>
}
/// <summary>
- /// Searches for the specified value and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
+ /// Searches for the specified value and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
/// </summary>
/// <param name="span">The span to search.</param>
/// <param name="value">The value to search for.</param>
}
/// <summary>
- /// Searches for the specified sequence and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
+ /// Searches for the specified sequence and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
/// </summary>
/// <param name="span">The span to search.</param>
/// <param name="value">The sequence to search for.</param>
}
/// <summary>
- /// Searches for the specified value and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
+ /// Searches for the specified value and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
/// </summary>
/// <param name="span">The span to search.</param>
/// <param name="value">The value to search for.</param>
}
/// <summary>
- /// Searches for the specified sequence and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
+ /// Searches for the specified sequence and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
/// </summary>
/// <param name="span">The span to search.</param>
/// <param name="value">The sequence to search for.</param>
}
/// <summary>
- /// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T).
+ /// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T).
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool SequenceEqual<T>(this Span<T> span, ReadOnlySpan<T> other)
}
/// <summary>
- /// Determines the relative order of the sequences being compared by comparing the elements using IComparable{T}.CompareTo(T).
+ /// Determines the relative order of the sequences being compared by comparing the elements using IComparable{T}.CompareTo(T).
/// </summary>
public static int SequenceCompareTo<T>(this Span<T> span, ReadOnlySpan<T> other)
where T : IComparable<T>
}
/// <summary>
- /// Searches for the specified value and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
+ /// Searches for the specified value and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
/// </summary>
/// <param name="span">The span to search.</param>
/// <param name="value">The value to search for.</param>
}
/// <summary>
- /// Searches for the specified sequence and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
+ /// Searches for the specified sequence and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
/// </summary>
/// <param name="span">The span to search.</param>
/// <param name="value">The sequence to search for.</param>
}
/// <summary>
- /// Searches for the specified value and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
+ /// Searches for the specified value and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
/// </summary>
/// <param name="span">The span to search.</param>
/// <param name="value">The value to search for.</param>
}
/// <summary>
- /// Searches for the specified sequence and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
+ /// Searches for the specified sequence and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
/// </summary>
/// <param name="span">The span to search.</param>
/// <param name="value">The sequence to search for.</param>
}
/// <summary>
- /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
+ /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
/// </summary>
/// <param name="span">The span to search.</param>
/// <param name="values">The set of values to search for.</param>
}
/// <summary>
- /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
+ /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
/// </summary>
/// <param name="span">The span to search.</param>
/// <param name="value0">One of the values to search for.</param>
}
/// <summary>
- /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
+ /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
/// </summary>
/// <param name="span">The span to search.</param>
/// <param name="values">The set of values to search for.</param>
}
/// <summary>
- /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
+ /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
/// </summary>
/// <param name="span">The span to search.</param>
/// <param name="values">The set of values to search for.</param>
}
/// <summary>
- /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
+ /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
/// </summary>
/// <param name="span">The span to search.</param>
/// <param name="value0">One of the values to search for.</param>
}
/// <summary>
- /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
+ /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
/// </summary>
/// <param name="span">The span to search.</param>
/// <param name="values">The set of values to search for.</param>
}
/// <summary>
- /// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T).
+ /// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T).
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool SequenceEqual<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> other)
}
/// <summary>
- /// Determines the relative order of the sequences being compared by comparing the elements using IComparable{T}.CompareTo(T).
+ /// Determines the relative order of the sequences being compared by comparing the elements using IComparable{T}.CompareTo(T).
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int SequenceCompareTo<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> other)
/// <summary>
/// Creates a new Span over the portion of the target array beginning
+ /// at 'startIndex' and ending at the end of the segment.
+ /// </summary>
+ /// <param name="segment">The target array.</param>
+ /// <param name="startIndex">The index at which to begin the Span.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Span<T> AsSpan<T>(this ArraySegment<T> segment, Index startIndex)
+ {
+ int actualIndex = startIndex.GetOffset(segment.Count);
+ return AsSpan(segment, actualIndex);
+ }
+
+ /// <summary>
+ /// Creates a new Span over the portion of the target array beginning
/// at 'start' index and ending at 'end' index (exclusive).
/// </summary>
/// <param name="segment">The target array.</param>
}
/// <summary>
+ /// Creates a new Span over the portion of the target array using the range start and end indexes
+ /// </summary>
+ /// <param name="segment">The target array.</param>
+ /// <param name="range">The range which has start and end indexes to use for slicing the array.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Span<T> AsSpan<T>(this ArraySegment<T> segment, Range range)
+ {
+ (int start, int length) = range.GetOffsetAndLength(segment.Count);
+ return new Span<T>(segment.Array, segment.Offset + start, length);
+ }
+
+ /// <summary>
/// Creates a new memory over the target array.
/// </summary>
public static Memory<T> AsMemory<T>(this T[] array) => new Memory<T>(array);
public static Memory<T> AsMemory<T>(this T[] array, int start) => new Memory<T>(array, start);
/// <summary>
+ /// Creates a new memory over the portion of the target array starting from
+ /// 'startIndex' to the end of the array.
+ /// </summary>
+ public static Memory<T> AsMemory<T>(this T[] array, Index startIndex)
+ {
+ if (array == null)
+ {
+ if (!startIndex.Equals(Index.Start))
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+
+ return default;
+ }
+
+ int actualIndex = startIndex.GetOffset(array.Length);
+ return new Memory<T>(array, actualIndex);
+ }
+
+ /// <summary>
/// Creates a new memory over the portion of the target array beginning
/// at 'start' index and ending at 'end' index (exclusive).
/// </summary>
public static Memory<T> AsMemory<T>(this T[] array, int start, int length) => new Memory<T>(array, start, length);
/// <summary>
+ /// Creates a new memory over the portion of the target array beginning at inclusive start index of the range
+ /// and ending at the exclusive end index of the range.
+ /// </summary>
+ public static Memory<T> AsMemory<T>(this T[] array, Range range)
+ {
+ if (array == null)
+ {
+ Index startIndex = range.Start;
+ Index endIndex = range.End;
+ if (!startIndex.Equals(Index.Start) || !endIndex.Equals(Index.Start))
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+
+ return default;
+ }
+
+ (int start, int length) = range.GetOffsetAndLength(array.Length);
+ return new Memory<T>(array, start, length);
+ }
+
+ /// <summary>
/// Creates a new memory over the portion of the target array.
/// </summary>
public static Memory<T> AsMemory<T>(this ArraySegment<T> segment) => new Memory<T>(segment.Array, segment.Offset, segment.Count);
/// Copies the contents of the array into the span. If the source
/// and destinations overlap, this method behaves as if the original values in
/// a temporary location before the destination is overwritten.
- ///
+ ///
///<param name="source">The array to copy items from.</param>
/// <param name="destination">The span to copy items into.</param>
/// <exception cref="System.ArgumentException">
/// Copies the contents of the array into the memory. If the source
/// and destinations overlap, this method behaves as if the original values are in
/// a temporary location before the destination is overwritten.
- ///
+ ///
///<param name="source">The array to copy items from.</param>
/// <param name="destination">The memory to copy items into.</param>
/// <exception cref="System.ArgumentException">
// nuint x2 = xLength
// nuint y1 = (nuint)Unsafe.ByteOffset(xRef, yRef)
// nuint y2 = y1 + yLength
- //
+ //
// xRef relative to xRef is 0.
- //
+ //
// x2 is simply x1 + xLength. This cannot overflow.
- //
+ //
// yRef relative to xRef is (yRef - xRef). If (yRef - xRef) is
// negative, casting it to an unsigned 32-bit integer turns it into
// (yRef - xRef + 2³²). So, in the example above, y1 moves to the right
// of x2.
- //
+ //
// y2 is simply y1 + yLength. Note that this can overflow, as in the
// example above, which must be avoided.
//
// integers:
//
// == (y1 < xLength) || (y1 > -yLength)
- //
+ //
// Due to modulo arithmetic, this gives exactly same result *except* if
// yLength is zero, since 2³² - 0 is 0 and not 2³². So the case
// y.IsEmpty must be handled separately first.
- //
+ //
/// <summary>
/// Determines whether two sequences overlap in memory.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
+using System.Runtime.CompilerServices;
namespace System
{
+ /// <summary>Represent a range has start and end indexes.</summary>
+ /// <remarks>
+ /// Range is used by the C# compiler to support the range syntax.
+ /// <code>
+ /// int[] someArray = new int[5] { 1, 2, 3, 4, 5 };
+ /// int[] subArray1 = someArray[0..2]; // { 1, 2 }
+ /// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 }
+ /// </code>
+ /// </remarks>
public readonly struct Range : IEquatable<Range>
{
+ /// <summary>Represent the inclusive start index of the Range.</summary>
public Index Start { get; }
+
+ /// <summary>Represent the exclusive end index of the Range.</summary>
public Index End { get; }
- private Range(Index start, Index end)
+ /// <summary>Construct a Range object using the start and end indexes.</summary>
+ /// <param name="start">Represent the inclusive start index of the range.</param>
+ /// <param name="end">Represent the exclusive end index of the range.</param>
+ public Range(Index start, Index end)
{
Start = start;
End = end;
}
+ /// <summary>Indicates whether the current Range object is equal to another object of the same type.</summary>
+ /// <param name="value">An object to compare with this object</param>
public override bool Equals(object value)
{
if (value is Range)
return false;
}
+ /// <summary>Indicates whether the current Range object is equal to another Range object.</summary>
+ /// <param name="other">An object to compare with this object</param>
public bool Equals (Range other) => other.Start.Equals(Start) && other.End.Equals(End);
+ /// <summary>Returns the hash code for this instance.</summary>
public override int GetHashCode()
{
return HashCode.Combine(Start.GetHashCode(), End.GetHashCode());
}
+ /// <summary>Converts the value of the current Range object to its equivalent string representation.</summary>
public override string ToString()
{
Span<char> span = stackalloc char[2 + (2 * 11)]; // 2 for "..", then for each index 1 for '^' and 10 for longest possible uint
int charsWritten;
int pos = 0;
- if (Start.FromEnd)
+ if (Start.IsFromEnd)
{
span[0] = '^';
pos = 1;
span[pos++] = '.';
span[pos++] = '.';
- if (End.FromEnd)
+ if (End.IsFromEnd)
{
span[pos++] = '^';
}
return new string(span.Slice(0, pos));
}
- public static Range Create(Index start, Index end) => new Range(start, end);
- public static Range FromStart(Index start) => new Range(start, new Index(0, fromEnd: true));
- public static Range ToEnd(Index end) => new Range(new Index(0, fromEnd: false), end);
- public static Range All() => new Range(new Index(0, fromEnd: false), new Index(0, fromEnd: true));
+ /// <summary>Create a Range object starting from start index to the end of the collection.</summary>
+ public static Range StartAt(Index start) => new Range(start, Index.End);
+
+ /// <summary>Create a Range object starting from first element in the collection to the end Index.</summary>
+ public static Range EndAt(Index end) => new Range(Index.Start, end);
+
+ /// <summary>Create a Range object starting from first element to the end.</summary>
+ public static Range All => new Range(Index.Start, Index.End);
+
+ /// <summary>Destruct the range object according to a collection length and return the start offset from the beginning and the length of this range.</summary>
+ /// <param name="length">The length of the collection that the range will be used with. length has to be a positive value</param>
+ /// <remarks>
+ /// For performance reason, we don't validate the input length parameter against negative values.
+ /// It is expected Range will be used with collections which always have non negative length/count.
+ /// We validate the range is inside the length scope though.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public OffsetAndLength GetOffsetAndLength(int length)
+ {
+ int start;
+ Index startIndex = Start;
+ if (startIndex.IsFromEnd)
+ start = length - startIndex.Value;
+ else
+ start = startIndex.Value;
+
+ int end;
+ Index endIndex = End;
+ if (endIndex.IsFromEnd)
+ end = length - endIndex.Value;
+ else
+ end = endIndex.Value;
+
+ if ((uint)end > (uint)length || (uint)start > (uint)end)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
+ }
+
+ return new OffsetAndLength(start, end - start);
+ }
+
+ public readonly struct OffsetAndLength
+ {
+ public int Offset { get; }
+ public int Length { get; }
+
+ public OffsetAndLength(int offset, int length)
+ {
+ Offset = offset;
+ Length = length;
+ }
+
+ public void Deconstruct(out int offset, out int length)
+ {
+ offset = Offset;
+ length = Length;
+ }
+ }
}
}
}
/// <summary>
+ /// Forms a slice out of the given memory, beginning at 'startIndex'
+ /// </summary>
+ /// <param name="startIndex">The index at which to begin this slice.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlyMemory<T> Slice(Index startIndex)
+ {
+ int actualIndex = startIndex.GetOffset(_length);
+ return Slice(actualIndex);
+ }
+
+ /// <summary>
+ /// Forms a slice out of the given memory using the range start and end indexes.
+ /// </summary>
+ /// <param name="range">The range used to slice the memory using its start and end indexes.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlyMemory<T> Slice(Range range)
+ {
+ (int start, int length) = range.GetOffsetAndLength(_length);
+ // It is expected for _index + start to be negative if the memory is already pre-pinned.
+ return new ReadOnlyMemory<T>(_object, _index + start, length);
+ }
+
+ /// <summary>
+ /// Forms a slice out of the given memory using the range start and end indexes.
+ /// </summary>
+ /// <param name="range">The range used to slice the memory using its start and end indexes.</param>
+ public ReadOnlyMemory<T> this[Range range] => Slice(range);
+
+ /// <summary>
/// Returns a span from the memory.
/// </summary>
public unsafe ReadOnlySpan<T> Span
// code is based on object identity and referential equality, not deep equality (as common with string).
return (_object != null) ? HashCode.Combine(RuntimeHelpers.GetHashCode(_object), _index, _length) : 0;
}
-
+
/// <summary>Gets the state of the memory as individual fields.</summary>
/// <param name="start">The offset.</param>
/// <param name="length">The count.</param>
get
{
// Evaluate the actual index first because it helps performance
- int actualIndex = index.FromEnd ? _length - index.Value : index.Value;
+ int actualIndex = index.GetOffset(_length);
return ref this [actualIndex];
}
}
- public ReadOnlySpan<T> this[Range range]
- {
- get
- {
- int start = range.Start.FromEnd ? _length - range.Start.Value : range.Start.Value;
- int end = range.End.FromEnd ? _length - range.End.Value : range.End.Value;
- return Slice(start, end - start);
- }
- }
+ public ReadOnlySpan<T> this[Range range] => Slice(range);
/// <summary>
/// Returns a reference to the 0th element of the Span. If the Span is empty, returns null reference.
}
/// <summary>
+ /// Forms a slice out of the given read-only span, beginning at 'startIndex'
+ /// </summary>
+ /// <param name="startIndex">The index at which to begin this slice.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlySpan<T> Slice(Index startIndex)
+ {
+ int actualIndex;
+ if (startIndex.IsFromEnd)
+ actualIndex = _length - startIndex.Value;
+ else
+ actualIndex = startIndex.Value;
+
+ return Slice(actualIndex);
+ }
+
+ /// <summary>
+ /// Forms a slice out of the given read-only span, beginning at range start index to the range end
+ /// </summary>
+ /// <param name="range">The range which has the start and end indexes used to slice the span.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlySpan<T> Slice(Range range)
+ {
+ (int start, int length) = range.GetOffsetAndLength(_length);
+ return new ReadOnlySpan<T>(ref Unsafe.Add(ref _pointer.Value, start), length);
+ }
+
+ /// <summary>
/// Copies the contents of this read-only span into a new array. This heap
/// allocates, so should generally be avoided, however it is sometimes
/// necessary to bridge the gap with APIs written in terms of arrays.
get
{
// Evaluate the actual index first because it helps performance
- int actualIndex = index.FromEnd ? _length - index.Value : index.Value;
+ int actualIndex = index.GetOffset(_length);
return ref this [actualIndex];
}
}
- public Span<T> this[Range range]
- {
- get
- {
- int start = range.Start.FromEnd ? _length - range.Start.Value : range.Start.Value;
- int end = range.End.FromEnd ? _length - range.End.Value : range.End.Value;
- return Slice(start, end - start);
- }
- }
+ public Span<T> this[Range range] => Slice(range);
/// <summary>
/// Returns a reference to the 0th element of the Span. If the Span is empty, returns null reference.
}
/// <summary>
+ /// Forms a slice out of the given span, beginning at 'startIndex'
+ /// </summary>
+ /// <param name="startIndex">The index at which to begin this slice.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Span<T> Slice(Index startIndex)
+ {
+ int actualIndex = startIndex.GetOffset(_length);
+ return Slice(actualIndex);
+ }
+
+ /// <summary>
+ /// Forms a slice out of the given span, beginning at range start index to the range end
+ /// </summary>
+ /// <param name="range">The range which has the start and end indexes used to slice the span.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Span<T> Slice(Range range)
+ {
+ (int start, int length) = range.GetOffsetAndLength(_length);
+ return new Span<T>(ref Unsafe.Add(ref _pointer.Value, start), length);
+ }
+
+ /// <summary>
/// Copies the contents of this span into a new array. This heap
/// allocates, so should generally be avoided, however it is sometimes
/// necessary to bridge the gap with APIs written in terms of arrays.
return InternalSubString(startIndex, length);
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public string Substring(Index startIndex)
+ {
+ int actualIndex = startIndex.GetOffset(Length);
+ return Substring(actualIndex);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public string Substring(Range range)
+ {
+ (int start, int length) = range.GetOffsetAndLength(Length);
+ return Substring(start, length);
+ }
+
private unsafe string InternalSubString(int startIndex, int length)
{
Debug.Assert(startIndex >= 0 && startIndex <= this.Length, "StartIndex is out of range!");
return (value == null || 0u >= (uint)value.Length) ? true : false;
}
+ [System.Runtime.CompilerServices.IndexerName("Chars")]
+ public char this[Index index]
+ {
+ get
+ {
+ int actualIndex = index.GetOffset(Length);
+ return this[actualIndex];
+ }
+ }
+
+ [System.Runtime.CompilerServices.IndexerName("Chars")]
+ public string this[Range range] => Substring(range);
+
public static bool IsNullOrWhiteSpace(string value)
{
if (value == null) return true;
//
// IConvertible implementation
- //
+ //
public TypeCode GetTypeCode()
{
internal static void ThrowArgumentOutOfRangeException_ArgumentOutOfRange_Enum()
{
- throw GetArgumentOutOfRangeException(ExceptionArgument.type,
+ throw GetArgumentOutOfRangeException(ExceptionArgument.type,
ExceptionResource.ArgumentOutOfRange_Enum);
}