diff --git a/netcore/System.Private.CoreLib/shared/System/Index.cs b/netcore/System.Private.CoreLib/shared/System/Index.cs index 887506ec629a..94d7be198916 100644 --- a/netcore/System.Private.CoreLib/shared/System/Index.cs +++ b/netcore/System.Private.CoreLib/shared/System/Index.cs @@ -3,34 +3,138 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Runtime.CompilerServices; namespace System { + /// Represent a type can be used to index a collection either from the start or the end. + /// + /// Index is used by the C# compiler to support the new index syntax + /// + /// int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ; + /// int lastElement = someArray[^1]; // lastElement = 5 + /// + /// public readonly struct Index : IEquatable { private readonly int _value; - public Index(int value, bool fromEnd) + /// Construct an Index using a value and indicating if the index is from the start or from the end. + /// The index value. it has to be zero or positive number. + /// Indicating if the index is from the start or from the end. + /// + /// 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. + /// + [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; + } + + /// Create an Index pointing at first element. + public static Index Start => new Index(0); + + /// Create an Index pointing at beyond last element. + public static Index End => new Index(~0); + + /// Create an Index from the start at the position indicated by the value. + /// The index value from the start. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Index FromStart(int value) + { + if (value < 0) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + + return new Index(value); + } + + /// Create an Index from the end at the position indicated by the value. + /// The index value from the end. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Index FromEnd(int value) + { + if (value < 0) + { + ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException(); + } + + return new Index(~value); + } + + /// Returns the index value. + public int Value + { + get + { + if (_value < 0) + return ~_value; + else + return _value; + } + } + + /// Indicates whether the index is from the start or the end. + public bool IsFromEnd => _value < 0; + + /// Calculate the offset from the start using the giving collection length. + /// The length of the collection that the Index will be used with. length has to be a positive value + /// + /// 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. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetOffset(int length) + { + int offset; + + if (IsFromEnd) + offset = length - (~_value); + else + offset = _value; + + return offset; + } + + /// Indicates whether the current Index object is equal to another object of the same type. + /// An object to compare with this object public override bool Equals(object value) => value is Index && _value == ((Index)value)._value; + + /// Indicates whether the current Index object is equal to another Index object. + /// An object to compare with this object public bool Equals (Index other) => _value == other._value; - public override int GetHashCode() + /// Returns the hash code for this instance. + public override int GetHashCode() => _value; + + /// Converts integer number to an Index. + public static implicit operator Index(int value) => FromStart(value); + + /// Converts the value of the current Index object to its equivalent string representation. + 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() { @@ -41,7 +145,5 @@ private string ToStringFromEnd() return new string(span.Slice(0, charsWritten + 1)); } - public static implicit operator Index(int value) - => new Index(value, fromEnd: false); } -} +} \ No newline at end of file diff --git a/netcore/System.Private.CoreLib/shared/System/Memory.cs b/netcore/System.Private.CoreLib/shared/System/Memory.cs index 033b806c20d5..2f5d75ec894e 100644 --- a/netcore/System.Private.CoreLib/shared/System/Memory.cs +++ b/netcore/System.Private.CoreLib/shared/System/Memory.cs @@ -30,7 +30,7 @@ public readonly struct Memory private readonly object _object; private readonly int _index; private readonly int _length; - + /// /// Creates a new memory over the entirety of the target array. /// @@ -258,6 +258,35 @@ public Memory Slice(int start, int length) return new Memory(_object, _index + start, length); } + /// + /// Forms a slice out of the given memory, beginning at 'startIndex' + /// + /// The index at which to begin this slice. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Memory Slice(Index startIndex) + { + int actualIndex = startIndex.GetOffset(_length); + return Slice(actualIndex); + } + + /// + /// Forms a slice out of the given memory using the range start and end indexes. + /// + /// The range used to slice the memory using its start and end indexes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Memory 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(_object, _index + start, length); + } + + /// + /// Forms a slice out of the given memory using the range start and end indexes. + /// + /// The range used to slice the memory using its start and end indexes. + public Memory this[Range range] => Slice(range); + /// /// Returns a span from the memory. /// @@ -336,7 +365,7 @@ public unsafe Span Span ThrowHelper.ThrowArgumentOutOfRangeException(); } #endif - + refToReturn = ref Unsafe.Add(ref refToReturn, desiredStartIndex); lengthOfUnderlyingSpan = desiredLength; } @@ -477,4 +506,4 @@ public override int GetHashCode() return (_object != null) ? HashCode.Combine(RuntimeHelpers.GetHashCode(_object), _index, _length) : 0; } } -} +} \ No newline at end of file diff --git a/netcore/System.Private.CoreLib/shared/System/MemoryExtensions.cs b/netcore/System.Private.CoreLib/shared/System/MemoryExtensions.cs index 34b49d41234e..99cbef8a35c7 100644 --- a/netcore/System.Private.CoreLib/shared/System/MemoryExtensions.cs +++ b/netcore/System.Private.CoreLib/shared/System/MemoryExtensions.cs @@ -125,7 +125,7 @@ public static ReadOnlySpan TrimEnd(this ReadOnlySpan span, char trim } /// - /// 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. /// /// The source span from which the characters are removed. @@ -137,7 +137,7 @@ public static ReadOnlySpan Trim(this ReadOnlySpan span, ReadOnlySpan } /// - /// 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. /// /// The source span from which the characters are removed. @@ -166,7 +166,7 @@ public static ReadOnlySpan TrimStart(this ReadOnlySpan span, ReadOnl } /// - /// 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. /// /// The source span from which the characters are removed. @@ -258,7 +258,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(span)), } /// - /// 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). /// /// The span to search. /// The value to search for. @@ -282,7 +282,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(span)), } /// - /// 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). /// /// The span to search. /// The sequence to search for. @@ -307,7 +307,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(value)), } /// - /// 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). /// /// The span to search. /// The value to search for. @@ -331,7 +331,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(span)), } /// - /// 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). /// /// The span to search. /// The sequence to search for. @@ -350,7 +350,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(value)), } /// - /// 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). /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool SequenceEqual(this Span span, ReadOnlySpan other) @@ -369,7 +369,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(other)), } /// - /// 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). /// public static int SequenceCompareTo(this Span span, ReadOnlySpan other) where T : IComparable @@ -392,7 +392,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(other)), } /// - /// 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). /// /// The span to search. /// The value to search for. @@ -416,7 +416,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(span)), } /// - /// 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). /// /// The span to search. /// The sequence to search for. @@ -441,7 +441,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(value)), } /// - /// 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). /// /// The span to search. /// The value to search for. @@ -465,7 +465,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(span)), } /// - /// 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). /// /// The span to search. /// The sequence to search for. @@ -539,7 +539,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(span)), } /// - /// 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. /// /// The span to search. /// The set of values to search for. @@ -661,7 +661,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(span)), } /// - /// 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. /// /// The span to search. /// One of the values to search for. @@ -690,7 +690,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(span)), } /// - /// 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. /// /// The span to search. /// The set of values to search for. @@ -829,7 +829,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(span)), } /// - /// 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. /// /// The span to search. /// The set of values to search for. @@ -868,7 +868,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(span)), } /// - /// 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. /// /// The span to search. /// One of the values to search for. @@ -890,7 +890,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(span)), } /// - /// 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. /// /// The span to search. /// The set of values to search for. @@ -909,7 +909,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(values)), } /// - /// 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). /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool SequenceEqual(this ReadOnlySpan span, ReadOnlySpan other) @@ -927,7 +927,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(other)), } /// - /// 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). /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int SequenceCompareTo(this ReadOnlySpan span, ReadOnlySpan other) @@ -1132,6 +1132,19 @@ public static Span AsSpan(this ArraySegment segment, int start) return new Span(segment.Array, segment.Offset + start, segment.Count - start); } + /// + /// Creates a new Span over the portion of the target array beginning + /// at 'startIndex' and ending at the end of the segment. + /// + /// The target array. + /// The index at which to begin the Span. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span AsSpan(this ArraySegment segment, Index startIndex) + { + int actualIndex = startIndex.GetOffset(segment.Count); + return AsSpan(segment, actualIndex); + } + /// /// Creates a new Span over the portion of the target array beginning /// at 'start' index and ending at 'end' index (exclusive). @@ -1155,6 +1168,18 @@ public static Span AsSpan(this ArraySegment segment, int start, int len return new Span(segment.Array, segment.Offset + start, length); } + /// + /// Creates a new Span over the portion of the target array using the range start and end indexes + /// + /// The target array. + /// The range which has start and end indexes to use for slicing the array. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span AsSpan(this ArraySegment segment, Range range) + { + (int start, int length) = range.GetOffsetAndLength(segment.Count); + return new Span(segment.Array, segment.Offset + start, length); + } + /// /// Creates a new memory over the target array. /// @@ -1173,6 +1198,24 @@ public static Span AsSpan(this ArraySegment segment, int start, int len /// public static Memory AsMemory(this T[] array, int start) => new Memory(array, start); + /// + /// Creates a new memory over the portion of the target array starting from + /// 'startIndex' to the end of the array. + /// + public static Memory AsMemory(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(array, actualIndex); + } + /// /// Creates a new memory over the portion of the target array beginning /// at 'start' index and ending at 'end' index (exclusive). @@ -1187,6 +1230,26 @@ public static Span AsSpan(this ArraySegment segment, int start, int len /// public static Memory AsMemory(this T[] array, int start, int length) => new Memory(array, start, length); + /// + /// 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. + /// + public static Memory AsMemory(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(array, start, length); + } + /// /// Creates a new memory over the portion of the target array. /// @@ -1237,7 +1300,7 @@ public static Memory AsMemory(this ArraySegment segment, int start, int /// 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. - /// + /// ///The array to copy items from. /// The span to copy items into. /// @@ -1254,7 +1317,7 @@ public static void CopyTo(this T[] source, Span destination) /// 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. - /// + /// ///The array to copy items from. /// The memory to copy items into. /// @@ -1353,16 +1416,16 @@ public static void CopyTo(this T[] source, Memory destination) // 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. // @@ -1389,11 +1452,11 @@ public static void CopyTo(this T[] source, Memory destination) // 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. - // + // /// /// Determines whether two sequences overlap in memory. @@ -1674,4 +1737,4 @@ private static bool IsTypeComparableAsBytes(out nuint size) return false; } } -} +} \ No newline at end of file diff --git a/netcore/System.Private.CoreLib/shared/System/Range.cs b/netcore/System.Private.CoreLib/shared/System/Range.cs index b858da2fb438..db3523c1e453 100644 --- a/netcore/System.Private.CoreLib/shared/System/Range.cs +++ b/netcore/System.Private.CoreLib/shared/System/Range.cs @@ -3,20 +3,38 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Runtime.CompilerServices; namespace System { + /// Represent a range has start and end indexes. + /// + /// Range is used by the C# compiler to support the range syntax. + /// + /// 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 } + /// + /// public readonly struct Range : IEquatable { + /// Represent the inclusive start index of the Range. public Index Start { get; } + + /// Represent the exclusive end index of the Range. public Index End { get; } - private Range(Index start, Index end) + /// Construct a Range object using the start and end indexes. + /// Represent the inclusive start index of the range. + /// Represent the exclusive end index of the range. + public Range(Index start, Index end) { Start = start; End = end; } + /// Indicates whether the current Range object is equal to another object of the same type. + /// An object to compare with this object public override bool Equals(object value) { if (value is Range) @@ -28,20 +46,24 @@ public override bool Equals(object value) return false; } + /// Indicates whether the current Range object is equal to another Range object. + /// An object to compare with this object public bool Equals (Range other) => other.Start.Equals(Start) && other.End.Equals(End); + /// Returns the hash code for this instance. public override int GetHashCode() { return HashCode.Combine(Start.GetHashCode(), End.GetHashCode()); } + /// Converts the value of the current Range object to its equivalent string representation. public override string ToString() { Span 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; @@ -53,7 +75,7 @@ public override string ToString() span[pos++] = '.'; span[pos++] = '.'; - if (End.FromEnd) + if (End.IsFromEnd) { span[pos++] = '^'; } @@ -64,9 +86,63 @@ public override string ToString() 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)); + /// Create a Range object starting from start index to the end of the collection. + public static Range StartAt(Index start) => new Range(start, Index.End); + + /// Create a Range object starting from first element in the collection to the end Index. + public static Range EndAt(Index end) => new Range(Index.Start, end); + + /// Create a Range object starting from first element to the end. + public static Range All => new Range(Index.Start, Index.End); + + /// Destruct the range object according to a collection length and return the start offset from the beginning and the length of this range. + /// The length of the collection that the range will be used with. length has to be a positive value + /// + /// 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. + /// + [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; + } + } } -} +} \ No newline at end of file diff --git a/netcore/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs b/netcore/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs index 8fd659aeaa0c..5cb2da0ebc7c 100644 --- a/netcore/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs +++ b/netcore/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs @@ -187,6 +187,35 @@ public ReadOnlyMemory Slice(int start, int length) return new ReadOnlyMemory(_object, _index + start, length); } + /// + /// Forms a slice out of the given memory, beginning at 'startIndex' + /// + /// The index at which to begin this slice. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlyMemory Slice(Index startIndex) + { + int actualIndex = startIndex.GetOffset(_length); + return Slice(actualIndex); + } + + /// + /// Forms a slice out of the given memory using the range start and end indexes. + /// + /// The range used to slice the memory using its start and end indexes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlyMemory 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(_object, _index + start, length); + } + + /// + /// Forms a slice out of the given memory using the range start and end indexes. + /// + /// The range used to slice the memory using its start and end indexes. + public ReadOnlyMemory this[Range range] => Slice(range); + /// /// Returns a span from the memory. /// @@ -386,7 +415,7 @@ public override int GetHashCode() // 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; } - + /// Gets the state of the memory as individual fields. /// The offset. /// The count. @@ -399,4 +428,4 @@ internal object GetObjectStartLength(out int start, out int length) return _object; } } -} +} \ No newline at end of file diff --git a/netcore/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs b/netcore/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs index b2ce53be2cd0..9426ce820104 100644 --- a/netcore/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs +++ b/netcore/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs @@ -157,20 +157,12 @@ public ref readonly T this[Index index] 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 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 this[Range range] => Slice(range); /// /// Returns a reference to the 0th element of the Span. If the Span is empty, returns null reference. @@ -296,6 +288,33 @@ public ReadOnlySpan Slice(int start, int length) return new ReadOnlySpan(ref Unsafe.Add(ref _pointer.Value, start), length); } + /// + /// Forms a slice out of the given read-only span, beginning at 'startIndex' + /// + /// The index at which to begin this slice. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan Slice(Index startIndex) + { + int actualIndex; + if (startIndex.IsFromEnd) + actualIndex = _length - startIndex.Value; + else + actualIndex = startIndex.Value; + + return Slice(actualIndex); + } + + /// + /// Forms a slice out of the given read-only span, beginning at range start index to the range end + /// + /// The range which has the start and end indexes used to slice the span. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan Slice(Range range) + { + (int start, int length) = range.GetOffsetAndLength(_length); + return new ReadOnlySpan(ref Unsafe.Add(ref _pointer.Value, start), length); + } + /// /// Copies the contents of this read-only span into a new array. This heap /// allocates, so should generally be avoided, however it is sometimes @@ -311,4 +330,4 @@ public T[] ToArray() return destination; } } -} +} \ No newline at end of file diff --git a/netcore/System.Private.CoreLib/shared/System/ReadOnlySpan.cs b/netcore/System.Private.CoreLib/shared/System/ReadOnlySpan.cs index 17b7134e27c0..bd22bb9c3f8f 100644 --- a/netcore/System.Private.CoreLib/shared/System/ReadOnlySpan.cs +++ b/netcore/System.Private.CoreLib/shared/System/ReadOnlySpan.cs @@ -134,4 +134,4 @@ public ref readonly T Current } } } -} +} \ No newline at end of file diff --git a/netcore/System.Private.CoreLib/shared/System/Span.Fast.cs b/netcore/System.Private.CoreLib/shared/System/Span.Fast.cs index 490767dc53c8..01148369ef94 100644 --- a/netcore/System.Private.CoreLib/shared/System/Span.Fast.cs +++ b/netcore/System.Private.CoreLib/shared/System/Span.Fast.cs @@ -163,20 +163,12 @@ public ref T this[Index index] 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 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 this[Range range] => Slice(range); /// /// Returns a reference to the 0th element of the Span. If the Span is empty, returns null reference. @@ -380,6 +372,28 @@ public Span Slice(int start, int length) return new Span(ref Unsafe.Add(ref _pointer.Value, start), length); } + /// + /// Forms a slice out of the given span, beginning at 'startIndex' + /// + /// The index at which to begin this slice. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span Slice(Index startIndex) + { + int actualIndex = startIndex.GetOffset(_length); + return Slice(actualIndex); + } + + /// + /// Forms a slice out of the given span, beginning at range start index to the range end + /// + /// The range which has the start and end indexes used to slice the span. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span Slice(Range range) + { + (int start, int length) = range.GetOffsetAndLength(_length); + return new Span(ref Unsafe.Add(ref _pointer.Value, start), length); + } + /// /// Copies the contents of this span into a new array. This heap /// allocates, so should generally be avoided, however it is sometimes @@ -396,4 +410,4 @@ public T[] ToArray() return destination; } } -} +} \ No newline at end of file diff --git a/netcore/System.Private.CoreLib/shared/System/String.Manipulation.cs b/netcore/System.Private.CoreLib/shared/System/String.Manipulation.cs index f183ed6a59dc..125529b2b1e8 100644 --- a/netcore/System.Private.CoreLib/shared/System/String.Manipulation.cs +++ b/netcore/System.Private.CoreLib/shared/System/String.Manipulation.cs @@ -1686,6 +1686,20 @@ public string Substring(int startIndex, int length) 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!"); @@ -1909,4 +1923,4 @@ private enum TrimType Both = 2 } } -} +} \ No newline at end of file diff --git a/netcore/System.Private.CoreLib/shared/System/String.cs b/netcore/System.Private.CoreLib/shared/System/String.cs index 0958e865db7f..8d8ab93123c3 100644 --- a/netcore/System.Private.CoreLib/shared/System/String.cs +++ b/netcore/System.Private.CoreLib/shared/System/String.cs @@ -444,6 +444,19 @@ public static bool IsNullOrEmpty(string value) 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; @@ -672,7 +685,7 @@ private static void ThrowMustBeNullTerminatedString() // // IConvertible implementation - // + // public TypeCode GetTypeCode() { @@ -798,4 +811,4 @@ public string Normalize(NormalizationForm normalizationForm) return Normalization.Normalize(this, normalizationForm); } } -} +} \ No newline at end of file diff --git a/netcore/System.Private.CoreLib/shared/System/ThrowHelper.cs b/netcore/System.Private.CoreLib/shared/System/ThrowHelper.cs index 0c80cd3cd8e0..6cc1236253de 100644 --- a/netcore/System.Private.CoreLib/shared/System/ThrowHelper.cs +++ b/netcore/System.Private.CoreLib/shared/System/ThrowHelper.cs @@ -4,8 +4,8 @@ // This file defines an internal class used to throw exceptions in BCL code. -// The main purpose is to reduce code size. -// +// The main purpose is to reduce code size. +// // The old way to throw an exception generates quite a lot IL code and assembly code. // Following is an example: // C# source @@ -17,10 +17,10 @@ // IL_0012: newobj instance void System.ArgumentNullException::.ctor(string,string) // IL_0017: throw // which is 21bytes in IL. -// +// // So we want to get rid of the ldstr and call to Environment.GetResource in IL. // In order to do that, I created two enums: ExceptionResource, ExceptionArgument to represent the -// argument name and resource name in a small integer. The source code will be changed to +// argument name and resource name in a small integer. The source code will be changed to // ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key, ExceptionResource.ArgumentNull_Key); // // The IL code will be 7 bytes. @@ -29,11 +29,11 @@ // IL_000a: call void System.ThrowHelper::ThrowArgumentNullException(valuetype System.ExceptionArgument) // IL_000f: ldarg.0 // -// This will also reduce the Jitted code size a lot. +// This will also reduce the Jitted code size a lot. +// +// It is very important we do this for generic classes because we can easily generate the same code +// multiple times for different instantiation. // -// It is very important we do this for generic classes because we can easily generate the same code -// multiple times for different instantiation. -// using System.Buffers; using System.Collections.Generic; @@ -66,34 +66,11 @@ internal static void ThrowArgumentOutOfRangeException() throw new ArgumentOutOfRangeException(); } - internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument) - { - throw new ArgumentOutOfRangeException(GetArgumentName(argument)); - } - - private static ArgumentOutOfRangeException GetArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource) - { - return new ArgumentOutOfRangeException(GetArgumentName(argument), GetResourceString(resource)); - } - internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource) - { - throw GetArgumentOutOfRangeException(argument, resource); - } - internal static void ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index() - { - throw GetArgumentOutOfRangeException(ExceptionArgument.startIndex, - ExceptionResource.ArgumentOutOfRange_Index); - } - internal static void ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count() - { - throw GetArgumentOutOfRangeException(ExceptionArgument.count, - ExceptionResource.ArgumentOutOfRange_Count); - } - internal static void ThrowArgumentException_DestinationTooShort() { throw new ArgumentException(SR.Argument_DestinationTooShort); } + internal static void ThrowArgumentException_OverlapAlignmentMismatch() { throw new ArgumentException(SR.Argument_OverlapAlignmentMismatch); @@ -109,85 +86,120 @@ internal static void ThrowArgumentOutOfRange_IndexException() throw GetArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index); } + internal static void ThrowIndexArgumentOutOfRange_NeedNonNegNumException() { throw GetArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); } + internal static void ThrowValueArgumentOutOfRange_NeedNonNegNumException() { throw GetArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); } - private static ArgumentException GetWrongKeyTypeArgumentException(object key, Type targetType) + internal static void ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum() { - return new ArgumentException(SR.Format(SR.Arg_WrongType, key, targetType), nameof(key)); + throw GetArgumentOutOfRangeException(ExceptionArgument.length, + ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); } - internal static void ThrowWrongKeyTypeArgumentException(object key, Type targetType) + + internal static void ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index() { - throw GetWrongKeyTypeArgumentException(key, targetType); + throw GetArgumentOutOfRangeException(ExceptionArgument.startIndex, + ExceptionResource.ArgumentOutOfRange_Index); } - private static ArgumentException GetWrongValueTypeArgumentException(object value, Type targetType) + internal static void ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count() { - return new ArgumentException(SR.Format(SR.Arg_WrongType, value, targetType), nameof(value)); + throw GetArgumentOutOfRangeException(ExceptionArgument.count, + ExceptionResource.ArgumentOutOfRange_Count); } - internal static void ThrowWrongValueTypeArgumentException(object value, Type targetType) + + internal static void ThrowArgumentOutOfRangeException_ArgumentOutOfRange_Enum() { - throw GetWrongValueTypeArgumentException(value, targetType); + throw GetArgumentOutOfRangeException(ExceptionArgument.type, + ExceptionResource.ArgumentOutOfRange_Enum); } - private static ArgumentException GetAddingDuplicateWithKeyArgumentException(object key) + internal static void ThrowWrongKeyTypeArgumentException(T key, Type targetType) { - return new ArgumentException(SR.Format(SR.Argument_AddingDuplicate, key)); + // Generic key to move the boxing to the right hand side of throw + throw GetWrongKeyTypeArgumentException((object)key, targetType); } - internal static void ThrowAddingDuplicateWithKeyArgumentException(object key) + + internal static void ThrowWrongValueTypeArgumentException(T value, Type targetType) { - throw GetAddingDuplicateWithKeyArgumentException(key); + // Generic key to move the boxing to the right hand side of throw + throw GetWrongValueTypeArgumentException((object)value, targetType); } - private static KeyNotFoundException GetKeyNotFoundException(object key) + private static ArgumentException GetAddingDuplicateWithKeyArgumentException(object key) { - throw new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); + return new ArgumentException(SR.Format(SR.Argument_AddingDuplicateWithKey, key)); } - internal static void ThrowKeyNotFoundException(object key) + + internal static void ThrowAddingDuplicateWithKeyArgumentException(T key) { - throw GetKeyNotFoundException(key); + // Generic key to move the boxing to the right hand side of throw + throw GetAddingDuplicateWithKeyArgumentException((object)key); } - internal static void ThrowArgumentException(ExceptionResource resource) + internal static void ThrowKeyNotFoundException(T key) { - throw new ArgumentException(GetResourceString(resource)); + // Generic key to move the boxing to the right hand side of throw + throw GetKeyNotFoundException((object)key); } - private static ArgumentException GetArgumentException(ExceptionResource resource, ExceptionArgument argument) + internal static void ThrowArgumentException(ExceptionResource resource) { - return new ArgumentException(GetResourceString(resource), GetArgumentName(argument)); + throw GetArgumentException(resource); } + internal static void ThrowArgumentException(ExceptionResource resource, ExceptionArgument argument) { throw GetArgumentException(resource, argument); } - internal static void ThrowAggregateException(List exceptions) + private static ArgumentNullException GetArgumentNullException(ExceptionArgument argument) { - throw new AggregateException(exceptions); + return new ArgumentNullException(GetArgumentName(argument)); } - internal static void ThrowArgumentException_Argument_InvalidArrayType() + internal static void ThrowArgumentNullException(ExceptionArgument argument) { - throw new ArgumentException(SR.Argument_InvalidArrayType); + throw GetArgumentNullException(argument); } - internal static void ThrowArgumentNullException(ExceptionArgument argument) + internal static void ThrowArgumentNullException(ExceptionResource resource) + { + throw new ArgumentNullException(GetResourceString(resource)); + } + + internal static void ThrowArgumentNullException(ExceptionArgument argument, ExceptionResource resource) + { + throw new ArgumentNullException(GetArgumentName(argument), GetResourceString(resource)); + } + + internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument) { - throw new ArgumentNullException(GetArgumentName(argument)); + throw new ArgumentOutOfRangeException(GetArgumentName(argument)); + } + + internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource) + { + throw GetArgumentOutOfRangeException(argument, resource); + } + + internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument, int paramNumber, ExceptionResource resource) + { + throw GetArgumentOutOfRangeException(argument, paramNumber, resource); } internal static void ThrowInvalidOperationException(ExceptionResource resource) { - throw new InvalidOperationException(GetResourceString(resource)); + throw GetInvalidOperationException(resource); } internal static void ThrowInvalidOperationException_OutstandingReferences() @@ -195,14 +207,64 @@ internal static void ThrowInvalidOperationException_OutstandingReferences() throw new InvalidOperationException(SR.Memory_OutstandingReferences); } - internal static void ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion() + internal static void ThrowInvalidOperationException(ExceptionResource resource, Exception e) { - throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + throw new InvalidOperationException(GetResourceString(resource), e); } - internal static void ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen() + internal static void ThrowSerializationException(ExceptionResource resource) { - throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + throw new SerializationException(GetResourceString(resource)); + } + + internal static void ThrowSecurityException(ExceptionResource resource) + { + throw new System.Security.SecurityException(GetResourceString(resource)); + } + + internal static void ThrowRankException(ExceptionResource resource) + { + throw new RankException(GetResourceString(resource)); + } + + internal static void ThrowNotSupportedException(ExceptionResource resource) + { + throw new NotSupportedException(GetResourceString(resource)); + } + + internal static void ThrowUnauthorizedAccessException(ExceptionResource resource) + { + throw new UnauthorizedAccessException(GetResourceString(resource)); + } + + internal static void ThrowObjectDisposedException(string objectName, ExceptionResource resource) + { + throw new ObjectDisposedException(objectName, GetResourceString(resource)); + } + + internal static void ThrowObjectDisposedException(ExceptionResource resource) + { + throw new ObjectDisposedException(null, GetResourceString(resource)); + } + + internal static void ThrowNotSupportedException() + { + throw new NotSupportedException(); + } + + internal static void ThrowAggregateException(List exceptions) + { + throw new AggregateException(exceptions); + } + + internal static void ThrowOutOfMemoryException() + { + throw new OutOfMemoryException(); + } + + internal static void ThrowArgumentException_Argument_InvalidArrayType() + { + throw new ArgumentException(SR.Argument_InvalidArrayType); } internal static void ThrowInvalidOperationException_InvalidOperation_EnumNotStarted() @@ -215,6 +277,21 @@ internal static void ThrowInvalidOperationException_InvalidOperation_EnumEnded() throw new InvalidOperationException(SR.InvalidOperation_EnumEnded); } + internal static void ThrowInvalidOperationException_EnumCurrent(int index) + { + throw GetInvalidOperationException_EnumCurrent(index); + } + + internal static void ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion() + { + throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + } + + internal static void ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen() + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + internal static void ThrowInvalidOperationException_InvalidOperation_NoValue() { throw new InvalidOperationException(SR.InvalidOperation_NoValue); @@ -225,24 +302,29 @@ internal static void ThrowInvalidOperationException_ConcurrentOperationsNotSuppo throw new InvalidOperationException(SR.InvalidOperation_ConcurrentOperationsNotSupported); } - internal static void ThrowSerializationException(ExceptionResource resource) + internal static void ThrowInvalidOperationException_HandleIsNotInitialized() { - throw new SerializationException(GetResourceString(resource)); + throw new InvalidOperationException(SR.InvalidOperation_HandleIsNotInitialized); } - internal static void ThrowObjectDisposedException(ExceptionResource resource) + internal static void ThrowArraySegmentCtorValidationFailedExceptions(Array array, int offset, int count) { - throw new ObjectDisposedException(null, GetResourceString(resource)); + throw GetArraySegmentCtorValidationFailedException(array, offset, count); } - internal static void ThrowNotSupportedException() + internal static void ThrowFormatException_BadFormatSpecifier() { - throw new NotSupportedException(); + throw new FormatException(SR.Argument_BadFormatSpecifier); } - internal static void ThrowNotSupportedException(ExceptionResource resource) + internal static void ThrowArgumentOutOfRangeException_PrecisionTooLarge() { - throw new NotSupportedException(GetResourceString(resource)); + throw new ArgumentOutOfRangeException("precision", SR.Format(SR.Argument_PrecisionTooLarge, StandardFormat.MaxPrecision)); + } + + internal static void ThrowArgumentOutOfRangeException_SymbolDoesNotFit() + { + throw new ArgumentOutOfRangeException("symbol", SR.Argument_BadFormatSpecifier); } private static Exception GetArraySegmentCtorValidationFailedException(Array array, int offset, int count) @@ -257,24 +339,53 @@ private static Exception GetArraySegmentCtorValidationFailedException(Array arra Debug.Assert(array.Length - offset < count); return new ArgumentException(SR.Argument_InvalidOffLen); } - internal static void ThrowArraySegmentCtorValidationFailedExceptions(Array array, int offset, int count) + + private static ArgumentException GetArgumentException(ExceptionResource resource) { - throw GetArraySegmentCtorValidationFailedException(array, offset, count); + return new ArgumentException(GetResourceString(resource)); } - internal static void ThrowFormatException_BadFormatSpecifier() + private static InvalidOperationException GetInvalidOperationException(ExceptionResource resource) { - throw new FormatException(SR.Argument_BadFormatSpecifier); + return new InvalidOperationException(GetResourceString(resource)); } - internal static void ThrowArgumentOutOfRangeException_PrecisionTooLarge() + private static ArgumentException GetWrongKeyTypeArgumentException(object key, Type targetType) { - throw new ArgumentOutOfRangeException("precision", SR.Format(SR.Argument_PrecisionTooLarge, StandardFormat.MaxPrecision)); + return new ArgumentException(SR.Format(SR.Arg_WrongType, key, targetType), nameof(key)); } - internal static void ThrowArgumentOutOfRangeException_SymbolDoesNotFit() + private static ArgumentException GetWrongValueTypeArgumentException(object value, Type targetType) { - throw new ArgumentOutOfRangeException("symbol", SR.Argument_BadFormatSpecifier); + return new ArgumentException(SR.Format(SR.Arg_WrongType, value, targetType), nameof(value)); + } + + private static KeyNotFoundException GetKeyNotFoundException(object key) + { + return new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); + } + + private static ArgumentOutOfRangeException GetArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource) + { + return new ArgumentOutOfRangeException(GetArgumentName(argument), GetResourceString(resource)); + } + + private static ArgumentException GetArgumentException(ExceptionResource resource, ExceptionArgument argument) + { + return new ArgumentException(GetResourceString(resource), GetArgumentName(argument)); + } + + private static ArgumentOutOfRangeException GetArgumentOutOfRangeException(ExceptionArgument argument, int paramNumber, ExceptionResource resource) + { + return new ArgumentOutOfRangeException(GetArgumentName(argument) + "[" + paramNumber.ToString() + "]", GetResourceString(resource)); + } + + private static InvalidOperationException GetInvalidOperationException_EnumCurrent(int index) + { + return new InvalidOperationException( + index < 0 ? + SR.InvalidOperation_EnumNotStarted : + SR.InvalidOperation_EnumEnded); } // Allow nulls for reference types and Nullable, but not for value types. @@ -283,11 +394,36 @@ internal static void ThrowArgumentOutOfRangeException_SymbolDoesNotFit() [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void IfNullAndNullsAreIllegalThenThrow(object value, ExceptionArgument argName) { - // Note that default(T) is not equal to null for value types except when T is Nullable. + // Note that default(T) is not equal to null for value types except when T is Nullable. if (!(default(T) == null) && value == null) ThrowHelper.ThrowArgumentNullException(argName); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ThrowForUnsupportedVectorBaseType() where T : struct + { + if (typeof(T) != typeof(byte) && typeof(T) != typeof(sbyte) && + typeof(T) != typeof(short) && typeof(T) != typeof(ushort) && + typeof(T) != typeof(int) && typeof(T) != typeof(uint) && + typeof(T) != typeof(long) && typeof(T) != typeof(ulong) && + typeof(T) != typeof(float) && typeof(T) != typeof(double)) + { + ThrowNotSupportedException(ExceptionResource.Arg_TypeNotSupported); + } + } + +#if false // Reflection-based implementation does not work for CoreRT/ProjectN + // This function will convert an ExceptionArgument enum value to the argument name string. + [MethodImpl(MethodImplOptions.NoInlining)] + private static string GetArgumentName(ExceptionArgument argument) + { + Debug.Assert(Enum.IsDefined(typeof(ExceptionArgument), argument), + "The enum value is not defined, please check the ExceptionArgument Enum."); + + return argument.ToString(); + } +#endif + private static string GetArgumentName(ExceptionArgument argument) { switch (argument) @@ -404,18 +540,72 @@ private static string GetArgumentName(ExceptionArgument argument) return "stateMachine"; case ExceptionArgument.timeout: return "timeout"; + case ExceptionArgument.type: + return "type"; + case ExceptionArgument.sourceIndex: + return "sourceIndex"; + case ExceptionArgument.sourceArray: + return "sourceArray"; + case ExceptionArgument.destinationIndex: + return "destinationIndex"; + case ExceptionArgument.destinationArray: + return "destinationArray"; + case ExceptionArgument.pHandle: + return "pHandle"; + case ExceptionArgument.other: + return "other"; + case ExceptionArgument.newSize: + return "newSize"; + case ExceptionArgument.lowerBounds: + return "lowerBounds"; + case ExceptionArgument.lengths: + return "lengths"; + case ExceptionArgument.len: + return "len"; + case ExceptionArgument.keys: + return "keys"; + case ExceptionArgument.indices: + return "indices"; + case ExceptionArgument.index1: + return "index1"; + case ExceptionArgument.index2: + return "index2"; + case ExceptionArgument.index3: + return "index3"; + case ExceptionArgument.length1: + return "length1"; + case ExceptionArgument.length2: + return "length2"; + case ExceptionArgument.length3: + return "length3"; + case ExceptionArgument.endIndex: + return "endIndex"; + case ExceptionArgument.elementType: + return "elementType"; + case ExceptionArgument.arrayIndex: + return "arrayIndex"; default: Debug.Fail("The enum value is not defined, please check the ExceptionArgument Enum."); return ""; } } +#if false // Reflection-based implementation does not work for CoreRT/ProjectN + // This function will convert an ExceptionResource enum value to the resource string. + [MethodImpl(MethodImplOptions.NoInlining)] + private static string GetResourceString(ExceptionResource resource) + { + Debug.Assert(Enum.IsDefined(typeof(ExceptionResource), resource), + "The enum value is not defined, please check the ExceptionResource Enum."); + + return SR.GetResourceString(resource.ToString()); + } +#endif + private static string GetResourceString(ExceptionResource resource) { switch (resource) { - case ExceptionResource.Argument_InvalidArgumentForComparison: - return SR.Argument_InvalidArgumentForComparison; case ExceptionResource.ArgumentOutOfRange_Index: return SR.ArgumentOutOfRange_Index; case ExceptionResource.ArgumentOutOfRange_Count: @@ -498,6 +688,44 @@ private static string GetResourceString(ExceptionResource resource) return SR.Task_ThrowIfDisposed; case ExceptionResource.Task_WaitMulti_NullTask: return SR.Task_WaitMulti_NullTask; + case ExceptionResource.ArgumentException_OtherNotArrayOfCorrectLength: + return SR.ArgumentException_OtherNotArrayOfCorrectLength; + case ExceptionResource.ArgumentNull_SafeHandle: + return SR.ArgumentNull_SafeHandle; + case ExceptionResource.ArgumentOutOfRange_EndIndexStartIndex: + return SR.ArgumentOutOfRange_EndIndexStartIndex; + case ExceptionResource.ArgumentOutOfRange_Enum: + return SR.ArgumentOutOfRange_Enum; + case ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported: + return SR.ArgumentOutOfRange_HugeArrayNotSupported; + case ExceptionResource.Argument_AddingDuplicate: + return SR.Argument_AddingDuplicate; + case ExceptionResource.Argument_InvalidArgumentForComparison: + return SR.Argument_InvalidArgumentForComparison; + case ExceptionResource.Arg_LowerBoundsMustMatch: + return SR.Arg_LowerBoundsMustMatch; + case ExceptionResource.Arg_MustBeType: + return SR.Arg_MustBeType; + case ExceptionResource.Arg_Need1DArray: + return SR.Arg_Need1DArray; + case ExceptionResource.Arg_Need2DArray: + return SR.Arg_Need2DArray; + case ExceptionResource.Arg_Need3DArray: + return SR.Arg_Need3DArray; + case ExceptionResource.Arg_NeedAtLeast1Rank: + return SR.Arg_NeedAtLeast1Rank; + case ExceptionResource.Arg_RankIndices: + return SR.Arg_RankIndices; + case ExceptionResource.Arg_RanksAndBounds: + return SR.Arg_RanksAndBounds; + case ExceptionResource.InvalidOperation_IComparerFailed: + return SR.InvalidOperation_IComparerFailed; + case ExceptionResource.NotSupported_FixedSizeCollection: + return SR.NotSupported_FixedSizeCollection; + case ExceptionResource.Rank_MultiDimNotSupported: + return SR.Rank_MultiDimNotSupported; + case ExceptionResource.Arg_TypeNotSupported: + return SR.Arg_TypeNotSupported; default: Debug.Assert(false, "The enum value is not defined, please check the ExceptionResource Enum."); @@ -508,7 +736,7 @@ private static string GetResourceString(ExceptionResource resource) // // The convention for this enum is using the argument name as the enum name - // + // internal enum ExceptionArgument { obj, @@ -567,14 +795,35 @@ internal enum ExceptionArgument millisecondsTimeout, stateMachine, timeout, + type, + sourceIndex, + sourceArray, + destinationIndex, + destinationArray, + pHandle, + other, + newSize, + lowerBounds, + lengths, + len, + keys, + indices, + index1, + index2, + index3, + length1, + length2, + length3, + endIndex, + elementType, + arrayIndex, } // // The convention for this enum is using the resource name as the enum name - // + // internal enum ExceptionResource { - Argument_InvalidArgumentForComparison, ArgumentOutOfRange_Index, ArgumentOutOfRange_Count, Arg_ArrayPlusOffTooSmall, @@ -616,5 +865,24 @@ internal enum ExceptionResource Task_Dispose_NotCompleted, Task_ThrowIfDisposed, Task_WaitMulti_NullTask, + ArgumentException_OtherNotArrayOfCorrectLength, + ArgumentNull_SafeHandle, + ArgumentOutOfRange_EndIndexStartIndex, + ArgumentOutOfRange_Enum, + ArgumentOutOfRange_HugeArrayNotSupported, + Argument_AddingDuplicate, + Argument_InvalidArgumentForComparison, + Arg_LowerBoundsMustMatch, + Arg_MustBeType, + Arg_Need1DArray, + Arg_Need2DArray, + Arg_Need3DArray, + Arg_NeedAtLeast1Rank, + Arg_RankIndices, + Arg_RanksAndBounds, + InvalidOperation_IComparerFailed, + NotSupported_FixedSizeCollection, + Rank_MultiDimNotSupported, + Arg_TypeNotSupported, } -} +} \ No newline at end of file