Created
October 31, 2017 11:22
-
-
Save benaadams/deeb4add5c9b5e647449e3d0ed90eea8 to your computer and use it in GitHub Desktop.
StackSpace.cs for CoreCLR #9068 & #9066
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Diagnostics; | |
| using System.Runtime.CompilerServices; | |
| using System.Runtime.InteropServices; | |
| using System.Text; | |
| class Program | |
| { | |
| static void Main(string[] args) | |
| { | |
| var bytes = Encoding.ASCII.GetBytes("Hello World! Testing stack space!"); | |
| Console.WriteLine(OneSpantype(bytes)); | |
| Console.WriteLine(LotsOfSpans(bytes)); | |
| } | |
| [MethodImpl(MethodImplOptions.NoInlining)] | |
| private static byte OneSpantype(byte[] array) | |
| { | |
| var span = new SpanLike<byte>(array); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| span = span.Slice(1); | |
| return span[0]; | |
| } | |
| [MethodImpl(MethodImplOptions.NoInlining)] | |
| private static byte LotsOfSpans(byte[] array) | |
| { | |
| var span00 = new SpanLike<byte>(array); | |
| var span01 = span00.Slice(1); | |
| var span02 = span01.Slice(1); | |
| var span03 = span02.Slice(1); | |
| var span04 = span03.Slice(1); | |
| var span05 = span04.Slice(1); | |
| var span06 = span05.Slice(1); | |
| var span07 = span06.Slice(1); | |
| var span08 = span07.Slice(1); | |
| var span09 = span08.Slice(1); | |
| var span10 = span09.Slice(1); | |
| var span11 = span10.Slice(1); | |
| var span12 = span11.Slice(1); | |
| var span13 = span12.Slice(1); | |
| var span14 = span13.Slice(1); | |
| var span15 = span14.Slice(1); | |
| var span16 = span15.Slice(1); | |
| var span17 = span16.Slice(1); | |
| var span18 = span17.Slice(1); | |
| var span19 = span18.Slice(1); | |
| var span20 = span19.Slice(1); | |
| var span21 = span20.Slice(1); | |
| var span22 = span21.Slice(1); | |
| var span23 = span22.Slice(1); | |
| var span24 = span23.Slice(1); | |
| var span25 = span24.Slice(1); | |
| var span26 = span25.Slice(1); | |
| var span27 = span26.Slice(1); | |
| var span28 = span27.Slice(1); | |
| var span29 = span28.Slice(1); | |
| var span30 = span29.Slice(1); | |
| return span30[0]; | |
| } | |
| } | |
| public readonly ref struct SpanLike<T> | |
| { | |
| private readonly Pinnable<T> _pinnable; | |
| private readonly IntPtr _byteOffset; | |
| private readonly int _length; | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| public SpanLike(T[] array) | |
| { | |
| if (array == null) | |
| ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); | |
| if (default(T) == null && array.GetType() != typeof(T[])) | |
| ThrowHelper.ThrowArrayTypeMismatchException_ArrayTypeMustBeExactMatch(typeof(T)); | |
| _length = array.Length; | |
| _pinnable = Unsafe.As<Pinnable<T>>(array); | |
| _byteOffset = SpanHelpers.PerTypeValues<T>.ArrayAdjustment; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| internal SpanLike(Pinnable<T> pinnable, IntPtr byteOffset, int length) | |
| { | |
| Debug.Assert(length >= 0); | |
| _length = length; | |
| _pinnable = pinnable; | |
| _byteOffset = byteOffset; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| public SpanLike<T> Slice(int start) | |
| { | |
| if ((uint)start > (uint)_length) | |
| ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); | |
| IntPtr newOffset = _byteOffset.Add<T>(start); | |
| int length = _length - start; | |
| return new SpanLike<T>(_pinnable, newOffset, length); | |
| } | |
| public ref T this[int index] | |
| { | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| get | |
| { | |
| if ((uint)index >= ((uint)_length)) | |
| ThrowHelper.ThrowIndexOutOfRangeException(); | |
| if (_pinnable == null) | |
| unsafe { return ref Unsafe.Add<T>(ref Unsafe.AsRef<T>(_byteOffset.ToPointer()), index); } | |
| else | |
| return ref Unsafe.Add<T>(ref Unsafe.AddByteOffset<T>(ref _pinnable.Data, _byteOffset), index); | |
| } | |
| } | |
| } | |
| [StructLayout(LayoutKind.Sequential)] | |
| internal sealed class Pinnable<T> | |
| { | |
| public T Data; | |
| } | |
| internal static partial class SpanHelpers | |
| { | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| public static IntPtr Add<T>(this IntPtr start, int index) | |
| { | |
| Debug.Assert(start.ToInt64() >= 0); | |
| Debug.Assert(index >= 0); | |
| unsafe | |
| { | |
| if (sizeof(IntPtr) == sizeof(int)) | |
| { | |
| // 32-bit path. | |
| uint byteLength = (uint)index * (uint)Unsafe.SizeOf<T>(); | |
| return (IntPtr)(((byte*)start) + byteLength); | |
| } | |
| else | |
| { | |
| // 64-bit path. | |
| ulong byteLength = (ulong)index * (ulong)Unsafe.SizeOf<T>(); | |
| return (IntPtr)(((byte*)start) + byteLength); | |
| } | |
| } | |
| } | |
| public class PerTypeValues<T> | |
| { | |
| public static readonly IntPtr ArrayAdjustment = MeasureArrayAdjustment(); | |
| // Array header sizes are a runtime implementation detail and aren't the same across all runtimes. (The CLR made a tweak after 4.5, and Mono has an extra Bounds pointer.) | |
| private static IntPtr MeasureArrayAdjustment() | |
| { | |
| T[] sampleArray = new T[1]; | |
| return Unsafe.ByteOffset<T>(ref Unsafe.As<Pinnable<T>>(sampleArray).Data, ref sampleArray[0]); | |
| } | |
| } | |
| } | |
| internal static class ThrowHelper | |
| { | |
| internal static void ThrowArgumentNullException(ExceptionArgument argument) { throw CreateArgumentNullException(argument); } | |
| [MethodImpl(MethodImplOptions.NoInlining)] | |
| private static Exception CreateArgumentNullException(ExceptionArgument argument) { return new ArgumentNullException(argument.ToString()); } | |
| internal static void ThrowArrayTypeMismatchException_ArrayTypeMustBeExactMatch(Type type) { throw CreateArrayTypeMismatchException_ArrayTypeMustBeExactMatch(type); } | |
| [MethodImpl(MethodImplOptions.NoInlining)] | |
| private static Exception CreateArrayTypeMismatchException_ArrayTypeMustBeExactMatch(Type type) { return new ArrayTypeMismatchException(); } | |
| internal static void ThrowIndexOutOfRangeException() { throw CreateIndexOutOfRangeException(); } | |
| [MethodImpl(MethodImplOptions.NoInlining)] | |
| private static Exception CreateIndexOutOfRangeException() { return new IndexOutOfRangeException(); } | |
| internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument) { throw CreateArgumentOutOfRangeException(argument); } | |
| [MethodImpl(MethodImplOptions.NoInlining)] | |
| private static Exception CreateArgumentOutOfRangeException(ExceptionArgument argument) { return new ArgumentOutOfRangeException(argument.ToString()); } | |
| } | |
| internal enum ExceptionArgument | |
| { | |
| array, | |
| start | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment