Expose Index and Range types (#20899)
authorTarek Mahmoud Sayed <tarekms@microsoft.com>
Sat, 10 Nov 2018 01:46:06 +0000 (17:46 -0800)
committerGitHub <noreply@github.com>
Sat, 10 Nov 2018 01:46:06 +0000 (17:46 -0800)
* Expose Index and Range types

* Address Review Comments

* Address more feedback

* Addressing more comments

src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
src/System.Private.CoreLib/shared/System/Index.cs [new file with mode: 0644]
src/System.Private.CoreLib/shared/System/Range.cs [new file with mode: 0644]
src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs
src/System.Private.CoreLib/shared/System/Span.Fast.cs
src/System.Private.CoreLib/src/System/ThrowHelper.cs

index 050591f..c3067a5 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)System\IEquatable.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IFormatProvider.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IFormattable.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Index.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IndexOutOfRangeException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\InsufficientExecutionStackException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\InsufficientMemoryException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\PlatformNotSupportedException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Progress.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Random.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Range.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\RankException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\ReadOnlySpan.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\ReadOnlySpan.Fast.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\SemaphoreFullException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\SemaphoreSlim.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\SendOrPostCallback.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Threading\SpinLock.cs" />    
+    <Compile Include="$(MSBuildThisFileDirectory)System\Threading\SpinLock.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\SpinWait.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\SynchronizationLockException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadLocal.cs" />
diff --git a/src/System.Private.CoreLib/shared/System/Index.cs b/src/System.Private.CoreLib/shared/System/Index.cs
new file mode 100644 (file)
index 0000000..29be7e4
--- /dev/null
@@ -0,0 +1,40 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+    public readonly struct Index : IEquatable<Index>
+    {
+        private readonly int _value;
+
+        public Index(int value, bool fromEnd)
+        {
+            if (value < 0)
+            {
+                ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException();
+            }
+
+            _value = fromEnd ? ~value : value;
+        }
+
+        public int Value => _value < 0 ? ~_value : _value;
+        public bool FromEnd => _value < 0;
+        public override bool Equals(object value) => value is Index && _value == ((Index)value)._value;
+        public bool Equals (Index other) => _value == other._value;
+
+        public override int GetHashCode()
+        {
+            return _value;
+        }
+
+        public override string ToString()
+        {
+            string str = Value.ToString();
+            return FromEnd ? "^" + str : str;
+        }
+
+        public static implicit operator Index(int value)
+            => new Index(value, fromEnd: false);
+    }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Range.cs b/src/System.Private.CoreLib/shared/System/Range.cs
new file mode 100644 (file)
index 0000000..c03cf34
--- /dev/null
@@ -0,0 +1,46 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+    public readonly struct Range : IEquatable<Range>
+    {
+        public Index Start { get; }
+        public Index End { get; }
+
+        private Range(Index start, Index end)
+        {
+            Start = start;
+            End = end;
+        }
+
+        public override bool Equals(object value)
+        {
+            if (value is Range)
+            {
+                Range r = (Range)value;
+                return r.Start.Equals(Start) && r.End.Equals(End);
+            }
+
+            return false;
+        }
+
+        public bool Equals (Range other) => other.Start.Equals(Start) && other.End.Equals(End);
+
+        public override int GetHashCode()
+        {
+            return HashCode.Combine(Start.GetHashCode(), End.GetHashCode());
+        }
+
+        public override string ToString()
+        {
+            return Start + ".." + End;
+        }
+
+        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));
+    }
+}
index 3077e2f..2d04fd1 100644 (file)
@@ -154,6 +154,24 @@ namespace System
 #endif
         }
 
+        public ref readonly T this[Index index]
+        {
+            get
+            {
+                return ref this [index.FromEnd ? _length - index.Value : index.Value];
+            }
+        }
+
+        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);
+            }
+        }
+
         /// <summary>
         /// Returns a reference to the 0th element of the Span. If the Span is empty, returns null reference.
         /// It can be used for pinning and is required to support the use of span within a fixed statement.
index 626c06e..b6e6253 100644 (file)
@@ -159,6 +159,24 @@ namespace System
 #endif
         }
 
+        public ref T this[Index index]
+        {
+            get
+            {
+                return ref this [index.FromEnd ? _length - index.Value : index.Value];
+            }
+        }
+
+        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);
+            }
+        }
+
         /// <summary>
         /// Returns a reference to the 0th element of the Span. If the Span is empty, returns null reference.
         /// It can be used for pinning and is required to support the use of span within a fixed statement.
index e18520d..6928f67 100644 (file)
@@ -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
 //          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.
 //    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.Collections.Generic;
 using System.Diagnostics;
@@ -87,6 +87,12 @@ namespace System
                                                     ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
         }
 
+        internal static void ThrowValueArgumentOutOfRange_NeedNonNegNumException()
+        {
+            throw GetArgumentOutOfRangeException(ExceptionArgument.value,
+                                                    ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+        }
+
         internal static void ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum()
         {
             throw GetArgumentOutOfRangeException(ExceptionArgument.length,