diff options
Diffstat (limited to 'src/mscorlib/shared')
-rw-r--r-- | src/mscorlib/shared/Interop/Unix/System.Native/Interop.Stat.cs | 2 | ||||
-rw-r--r-- | src/mscorlib/shared/Interop/Windows/Kernel32/Interop.GetCPInfo.cs | 24 | ||||
-rw-r--r-- | src/mscorlib/shared/System.Private.CoreLib.Shared.projitems | 7 | ||||
-rw-r--r-- | src/mscorlib/shared/System/CharEnumerator.cs | 1 | ||||
-rw-r--r-- | src/mscorlib/shared/System/Diagnostics/Tracing/EventCounter.cs | 436 | ||||
-rw-r--r-- | src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs | 18 | ||||
-rw-r--r-- | src/mscorlib/shared/System/ReadOnlySpan.cs | 323 | ||||
-rw-r--r-- | src/mscorlib/shared/System/Runtime/CompilerServices/IsReadOnlyAttribute.cs (renamed from src/mscorlib/shared/System/Runtime/CompilerServices/ReadOnlyAttribute.cs) | 4 | ||||
-rw-r--r-- | src/mscorlib/shared/System/Runtime/CompilerServices/RuntimeFeature.cs | 4 | ||||
-rw-r--r-- | src/mscorlib/shared/System/Span.NonGeneric.cs | 522 | ||||
-rw-r--r-- | src/mscorlib/shared/System/Span.cs | 413 | ||||
-rw-r--r-- | src/mscorlib/shared/System/Text/StringBuilder.cs | 78 | ||||
-rw-r--r-- | src/mscorlib/shared/System/Threading/Tasks/TaskExtensions.cs | 4 |
13 files changed, 1351 insertions, 485 deletions
diff --git a/src/mscorlib/shared/Interop/Unix/System.Native/Interop.Stat.cs b/src/mscorlib/shared/Interop/Unix/System.Native/Interop.Stat.cs index a8bc2ec7d1..cda00ac6c3 100644 --- a/src/mscorlib/shared/Interop/Unix/System.Native/Interop.Stat.cs +++ b/src/mscorlib/shared/Interop/Unix/System.Native/Interop.Stat.cs @@ -27,6 +27,8 @@ internal static partial class Interop internal long MTime; internal long CTime; internal long BirthTime; + internal long Dev; + internal long Ino; } internal static class FileTypes diff --git a/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.GetCPInfo.cs b/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.GetCPInfo.cs new file mode 100644 index 0000000000..1665119420 --- /dev/null +++ b/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.GetCPInfo.cs @@ -0,0 +1,24 @@ +// 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. + +using System.IO; +using System.Text; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + internal unsafe struct CPINFO + { + internal int MaxCharSize; + + internal fixed byte DefaultChar[2 /* MAX_DEFAULTCHAR */]; + internal fixed byte LeadByte[12 /* MAX_LEADBYTES */]; + } + + [DllImport(Libraries.Kernel32)] + internal static extern unsafe int GetCPInfo(uint codePage, CPINFO* lpCpInfo); + } +} diff --git a/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems b/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems index 6ef7fc1681..3443a2b339 100644 --- a/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems @@ -169,6 +169,7 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Progress.cs"/> <Compile Include="$(MSBuildThisFileDirectory)System\Random.cs"/> <Compile Include="$(MSBuildThisFileDirectory)System\RankException.cs"/> + <Compile Include="$(MSBuildThisFileDirectory)System\ReadOnlySpan.cs" Condition="'$(IsProjectNLibrary)' != 'true'"/> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\AmbiguousMatchException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\Assembly.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\AssemblyAlgorithmIdAttribute.cs" /> @@ -282,7 +283,7 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\LoadHint.cs"/> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\MethodCodeType.cs"/> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\MethodImplOptions.cs"/> - <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\ReadOnlyAttribute.cs"/> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\IsReadOnlyAttribute.cs"/> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\ReferenceAssemblyAttribute.cs"/> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\RuntimeCompatibilityAttribute.cs"/> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\RuntimeFeature.cs"/> @@ -338,6 +339,8 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Security\SuppressUnmanagedCodeSecurityAttribute.cs"/> <Compile Include="$(MSBuildThisFileDirectory)System\Security\UnverifiableCodeAttribute.cs"/> <Compile Include="$(MSBuildThisFileDirectory)System\Security\VerificationException.cs"/> + <Compile Include="$(MSBuildThisFileDirectory)System\Span.cs" Condition="'$(IsProjectNLibrary)' != 'true'"/> + <Compile Include="$(MSBuildThisFileDirectory)System\Span.NonGeneric.cs" Condition="'$(IsProjectNLibrary)' != 'true'"/> <Compile Include="$(MSBuildThisFileDirectory)System\StackOverflowException.cs"/> <Compile Include="$(MSBuildThisFileDirectory)System\StringComparer.cs"/> <Compile Include="$(MSBuildThisFileDirectory)System\StringComparison.cs"/> @@ -404,7 +407,6 @@ <ItemGroup> <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\ActivityTracker.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\EventActivityOptions.cs" /> - <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\EventCounter.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\EventDescriptor.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\EventProvider.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\EventSource.cs" /> @@ -456,6 +458,7 @@ <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FileTypes.cs"/> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FlushFileBuffers.cs"/> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FormatMessage.cs"/> + <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetCPInfo.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetFileInformationByHandleEx.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetFileType_SafeHandle.cs"/> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetFullPathNameW.cs"/> diff --git a/src/mscorlib/shared/System/CharEnumerator.cs b/src/mscorlib/shared/System/CharEnumerator.cs index ea9915a7c4..4dbd5cd587 100644 --- a/src/mscorlib/shared/System/CharEnumerator.cs +++ b/src/mscorlib/shared/System/CharEnumerator.cs @@ -17,6 +17,7 @@ using System.Collections.Generic; namespace System { + [Serializable] public sealed class CharEnumerator : IEnumerator, IEnumerator<char>, IDisposable, ICloneable { private String _str; diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/EventCounter.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/EventCounter.cs deleted file mode 100644 index b1f946464e..0000000000 --- a/src/mscorlib/shared/System/Diagnostics/Tracing/EventCounter.cs +++ /dev/null @@ -1,436 +0,0 @@ -// 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. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Threading; -#if ES_BUILD_PCL - using System.Threading.Tasks; -#endif - -#if ES_BUILD_STANDALONE -namespace Microsoft.Diagnostics.Tracing -#else -namespace System.Diagnostics.Tracing -#endif -{ - /// <summary> - /// Provides the ability to collect statistics through EventSource - /// </summary> - public class EventCounter - { - /// <summary> - /// Initializes a new instance of the <see cref="EventCounter"/> class. - /// </summary> - /// <param name="name">The name.</param> - /// <param name="eventSource">The event source.</param> - public EventCounter(string name, EventSource eventSource) - { - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } - - if (eventSource == null) - { - throw new ArgumentNullException(nameof(eventSource)); - } - - InitializeBuffer(); - _name = name; - EventCounterGroup.AddEventCounter(eventSource, this); - } - - /// <summary> - /// Writes the metric. - /// </summary> - /// <param name="value">The value.</param> - public void WriteMetric(float value) - { - Enqueue(value); - } - - #region private implementation - - private readonly string _name; - - #region Buffer Management - - // Values buffering - private const int BufferedSize = 10; - private const float UnusedBufferSlotValue = float.NegativeInfinity; - private const int UnsetIndex = -1; - private volatile float[] _bufferedValues; - private volatile int _bufferedValuesIndex; - - private void InitializeBuffer() - { - _bufferedValues = new float[BufferedSize]; - for (int i = 0; i < _bufferedValues.Length; i++) - { - _bufferedValues[i] = UnusedBufferSlotValue; - } - } - - private void Enqueue(float value) - { - // It is possible that two threads read the same bufferedValuesIndex, but only one will be able to write the slot, so that is okay. - int i = _bufferedValuesIndex; - while (true) - { - float result = Interlocked.CompareExchange(ref _bufferedValues[i], value, UnusedBufferSlotValue); - i++; - if (_bufferedValues.Length <= i) - { - // It is possible that two threads both think the buffer is full, but only one get to actually flush it, the other - // will eventually enter this code path and potentially calling Flushing on a buffer that is not full, and that's okay too. - lock (_bufferedValues) - { - Flush(); - } - i = 0; - } - - if (result == UnusedBufferSlotValue) - { - // CompareExchange succeeded - _bufferedValuesIndex = i; - return; - } - } - } - - private void Flush() - { - for (int i = 0; i < _bufferedValues.Length; i++) - { - var value = Interlocked.Exchange(ref _bufferedValues[i], UnusedBufferSlotValue); - if (value != UnusedBufferSlotValue) - { - OnMetricWritten(value); - } - } - - _bufferedValuesIndex = 0; - } - - #endregion // Buffer Management - - #region Statistics Calculation - - // Statistics - private int _count; - private float _sum; - private float _sumSquared; - private float _min; - private float _max; - - private void OnMetricWritten(float value) - { - _sum += value; - _sumSquared += value * value; - if (_count == 0 || value > _max) - { - _max = value; - } - - if (_count == 0 || value < _min) - { - _min = value; - } - - _count++; - } - - internal EventCounterPayload GetEventCounterPayload() - { - lock (_bufferedValues) - { - Flush(); - EventCounterPayload result = new EventCounterPayload(); - result.Name = _name; - result.Count = _count; - result.Mean = _sum / _count; - result.StandardDerivation = (float)Math.Sqrt(_sumSquared / _count - _sum * _sum / _count / _count); - result.Min = _min; - result.Max = _max; - ResetStatistics(); - return result; - } - } - - private void ResetStatistics() - { - _count = 0; - _sum = 0; - _sumSquared = 0; - _min = 0; - _max = 0; - } - - #endregion // Statistics Calculation - - #endregion // private implementation - } - - #region internal supporting classes - - [EventData] - internal class EventCounterPayload : IEnumerable<KeyValuePair<string, object>> - { - public string Name { get; set; } - - public float Mean { get; set; } - - public float StandardDerivation { get; set; } - - public int Count { get; set; } - - public float Min { get; set; } - - public float Max { get; set; } - - public float IntervalSec { get; internal set; } - - #region Implementation of the IEnumerable interface - - public IEnumerator<KeyValuePair<string, object>> GetEnumerator() - { - return ForEnumeration.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return ForEnumeration.GetEnumerator(); - } - - private IEnumerable<KeyValuePair<string, object>> ForEnumeration - { - get - { - yield return new KeyValuePair<string, object>("Name", Name); - yield return new KeyValuePair<string, object>("Mean", Mean); - yield return new KeyValuePair<string, object>("StandardDerivation", StandardDerivation); - yield return new KeyValuePair<string, object>("Count", Count); - yield return new KeyValuePair<string, object>("Min", Min); - yield return new KeyValuePair<string, object>("Max", Max); - } - } - - #endregion // Implementation of the IEnumerable interface - } - - internal class EventCounterGroup : IDisposable - { - private readonly EventSource _eventSource; - private readonly int _eventSourceIndex; - private readonly List<EventCounter> _eventCounters; - - internal EventCounterGroup(EventSource eventSource, int eventSourceIndex) - { - _eventSource = eventSource; - _eventSourceIndex = eventSourceIndex; - _eventCounters = new List<EventCounter>(); - RegisterCommandCallback(); - } - - private void Add(EventCounter eventCounter) - { - _eventCounters.Add(eventCounter); - } - - #region EventSource Command Processing - - private void RegisterCommandCallback() - { - _eventSource.EventCommandExecuted += OnEventSourceCommand; - } - - private void OnEventSourceCommand(object sender, EventCommandEventArgs e) - { - if (e.Command == EventCommand.Enable || e.Command == EventCommand.Update) - { - string valueStr; - float value; - if (e.Arguments.TryGetValue("EventCounterIntervalSec", out valueStr) && float.TryParse(valueStr, out value)) - { - EnableTimer(value); - } - } - } - - #endregion // EventSource Command Processing - - #region Global EventCounterGroup Array management - - private static EventCounterGroup[] s_eventCounterGroups; - - internal static void AddEventCounter(EventSource eventSource, EventCounter eventCounter) - { - int eventSourceIndex = EventListener.EventSourceIndex(eventSource); - - EventCounterGroup.EnsureEventSourceIndexAvailable(eventSourceIndex); - EventCounterGroup eventCounterGroup = GetEventCounterGroup(eventSource); - eventCounterGroup.Add(eventCounter); - } - - private static void EnsureEventSourceIndexAvailable(int eventSourceIndex) - { - if (EventCounterGroup.s_eventCounterGroups == null) - { - EventCounterGroup.s_eventCounterGroups = new EventCounterGroup[eventSourceIndex + 1]; - } - else if (eventSourceIndex >= EventCounterGroup.s_eventCounterGroups.Length) - { - EventCounterGroup[] newEventCounterGroups = new EventCounterGroup[eventSourceIndex + 1]; - Array.Copy(EventCounterGroup.s_eventCounterGroups, 0, newEventCounterGroups, 0, EventCounterGroup.s_eventCounterGroups.Length); - EventCounterGroup.s_eventCounterGroups = newEventCounterGroups; - } - } - - private static EventCounterGroup GetEventCounterGroup(EventSource eventSource) - { - int eventSourceIndex = EventListener.EventSourceIndex(eventSource); - EventCounterGroup result = EventCounterGroup.s_eventCounterGroups[eventSourceIndex]; - if (result == null) - { - result = new EventCounterGroup(eventSource, eventSourceIndex); - EventCounterGroup.s_eventCounterGroups[eventSourceIndex] = result; - } - - return result; - } - - #endregion // Global EventCounterGroup Array management - - #region Timer Processing - - private DateTime _timeStampSinceCollectionStarted; - private int _pollingIntervalInMilliseconds; - private Timer _pollingTimer; - - private void EnableTimer(float pollingIntervalInSeconds) - { - if (pollingIntervalInSeconds == 0) - { - if (_pollingTimer != null) - { - _pollingTimer.Dispose(); - _pollingTimer = null; - } - - _pollingIntervalInMilliseconds = 0; - } - else if (_pollingIntervalInMilliseconds == 0 || pollingIntervalInSeconds < _pollingIntervalInMilliseconds) - { - _pollingIntervalInMilliseconds = (int)(pollingIntervalInSeconds * 1000); - if (_pollingTimer != null) - { - _pollingTimer.Dispose(); - _pollingTimer = null; - } - - _timeStampSinceCollectionStarted = DateTime.Now; - _pollingTimer = new Timer(OnTimer, null, _pollingIntervalInMilliseconds, _pollingIntervalInMilliseconds); - } - } - - private void OnTimer(object state) - { - if (_eventSource.IsEnabled()) - { - DateTime now = DateTime.Now; - TimeSpan elapsed = now - _timeStampSinceCollectionStarted; - lock (_pollingTimer) - { - foreach (var eventCounter in _eventCounters) - { - EventCounterPayload payload = eventCounter.GetEventCounterPayload(); - payload.IntervalSec = (float)elapsed.TotalSeconds; - _eventSource.Write("EventCounters", new EventSourceOptions() { Level = EventLevel.LogAlways }, new { Payload = payload }); - } - - - _timeStampSinceCollectionStarted = now; - } - } - else - { - _pollingTimer.Dispose(); - _pollingTimer = null; - EventCounterGroup.s_eventCounterGroups[_eventSourceIndex] = null; - } - } - - #region PCL timer hack - -#if ES_BUILD_PCL - internal delegate void TimerCallback(object state); - - internal sealed class Timer : CancellationTokenSource, IDisposable - { - private int _period; - private TimerCallback _callback; - private object _state; - - internal Timer(TimerCallback callback, object state, int dueTime, int period) - { - _callback = callback; - _state = state; - _period = period; - Schedule(dueTime); - } - - private void Schedule(int dueTime) - { - Task.Delay(dueTime, Token).ContinueWith(OnTimer, null, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default); - } - - private void OnTimer(Task t, object s) - { - Schedule(_period); - _callback(_state); - } - - public new void Dispose() { base.Cancel(); } - } -#endif - #endregion // PCL timer hack - - #endregion // Timer Processing - - #region Implementation of the IDisposable interface - - private bool _disposed = false; - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (_disposed) - { - return; - } - - if (disposing) - { - if (_pollingTimer != null) - { - _pollingTimer.Dispose(); - _pollingTimer = null; - } - } - - _disposed = true; - } - - #endregion // Implementation of the IDisposable interface - } - - #endregion // internal supporting classes -} diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs index 57d550dc8c..e18574c1b4 100644 --- a/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs +++ b/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs @@ -555,21 +555,21 @@ namespace System.Diagnostics.Tracing { #if (!ES_BUILD_PCL && !ES_BUILD_PN && !FEATURE_PAL) string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerId + "}"; - if (Marshal.SizeOf(typeof(IntPtr)) == 8) - regKey = @"Software" + @"\Wow6432Node" + regKey; + if (System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8) + regKey = @"HKEY_LOCAL_MACHINE\Software" + @"\Wow6432Node" + regKey; else - regKey = @"Software" + regKey; + regKey = @"HKEY_LOCAL_MACHINE\Software" + regKey; string valueName = "ControllerData_Session_" + etwSessionId.ToString(CultureInfo.InvariantCulture); - using (RegistryKey key = Registry.LocalMachine.OpenSubKey(regKey, writable: false)) - { - data = key.GetValue(valueName) as byte[]; - } - + // we need to assert this permission for partial trust scenarios +#if !CORECLR + (new RegistryPermission(RegistryPermissionAccess.Read, regKey)).Assert(); +#endif + data = Microsoft.Win32.Registry.GetValue(regKey, valueName, null) as byte[]; if (data != null) { - // We only used the persisted data from the registry for updates. + // We only used the persisted data from the registry for updates. command = ControllerCommand.Update; return true; } diff --git a/src/mscorlib/shared/System/ReadOnlySpan.cs b/src/mscorlib/shared/System/ReadOnlySpan.cs new file mode 100644 index 0000000000..8ec431f6fb --- /dev/null +++ b/src/mscorlib/shared/System/ReadOnlySpan.cs @@ -0,0 +1,323 @@ +// 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. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using EditorBrowsableState = System.ComponentModel.EditorBrowsableState; +using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute; + +#pragma warning disable 0809 //warning CS0809: Obsolete member 'Span<T>.Equals(object)' overrides non-obsolete member 'object.Equals(object)' + +namespace System +{ + /// <summary> + /// ReadOnlySpan represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed + /// or native memory, or to memory allocated on the stack. It is type- and memory-safe. + /// </summary> + public struct ReadOnlySpan<T> + { + /// <summary>A byref or a native ptr.</summary> + private readonly ByReference<T> _pointer; + /// <summary>The number of elements this ReadOnlySpan contains.</summary> +#if PROJECTN + [Bound] +#endif + private readonly int _length; + + /// <summary> + /// Creates a new read-only span over the entirety of the target array. + /// </summary> + /// <param name="array">The target array.</param> + /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null + /// reference (Nothing in Visual Basic).</exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan(T[] array) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + + _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData())); + _length = array.Length; + } + + /// <summary> + /// Creates a new read-only span over the portion of the target array beginning + /// at 'start' index and covering the remainder of the array. + /// </summary> + /// <param name="array">The target array.</param> + /// <param name="start">The index at which to begin the read-only span.</param> + /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null + /// reference (Nothing in Visual Basic).</exception> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> is not in the range (<0 or >=Length). + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan(T[] array, int start) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + if ((uint)start > (uint)array.Length) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + _pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start)); + _length = array.Length - start; + } + + /// <summary> + /// Creates a new read-only span over the portion of the target array beginning + /// at 'start' index and ending at 'end' index (exclusive). + /// </summary> + /// <param name="array">The target array.</param> + /// <param name="start">The index at which to begin the read-only span.</param> + /// <param name="length">The number of items in the read-only span.</param> + /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null + /// reference (Nothing in Visual Basic).</exception> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> or end index is not in the range (<0 or >=Length). + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan(T[] array, int start, int length) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + _pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start)); + _length = length; + } + + /// <summary> + /// Creates a new read-only span over the target unmanaged buffer. Clearly this + /// is quite dangerous, because we are creating arbitrarily typed T's + /// out of a void*-typed block of memory. And the length is not checked. + /// But if this creation is correct, then all subsequent uses are correct. + /// </summary> + /// <param name="pointer">An unmanaged pointer to memory.</param> + /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param> + /// <exception cref="System.ArgumentException"> + /// Thrown when <typeparamref name="T"/> is reference type or contains pointers and hence cannot be stored in unmanaged memory. + /// </exception> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="length"/> is negative. + /// </exception> + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe ReadOnlySpan(void* pointer, int length) + { + if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); + if (length < 0) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref *(byte*)pointer)); + _length = length; + } + + /// <summary> + /// Create a new read-only span over a portion of a regular managed object. This can be useful + /// if part of a managed object represents a "fixed array." This is dangerous because neither the + /// <paramref name="length"/> is checked, nor <paramref name="obj"/> being null, nor the fact that + /// "rawPointer" actually lies within <paramref name="obj"/>. + /// </summary> + /// <param name="obj">The managed object that contains the data to span over.</param> + /// <param name="objectData">A reference to data within that object.</param> + /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan<T> DangerousCreate(object obj, ref T objectData, int length) => new ReadOnlySpan<T>(ref objectData, length); + + // Constructor for internal use only. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ReadOnlySpan(ref T ptr, int length) + { + Debug.Assert(length >= 0); + + _pointer = new ByReference<T>(ref ptr); + _length = length; + } + + /// <summary> + /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element + /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. + /// </summary> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref T DangerousGetPinnableReference() + { + return ref _pointer.Value; + } + + /// <summary> + /// The number of items in the read-only span. + /// </summary> + public int Length => _length; + + /// <summary> + /// Returns true if Length is 0. + /// </summary> + public bool IsEmpty => _length == 0; + + /// <summary> + /// Returns the specified element of the read-only span. + /// </summary> + /// <param name="index"></param> + /// <returns></returns> + /// <exception cref="System.IndexOutOfRangeException"> + /// Thrown when index less than 0 or index greater than or equal to Length + /// </exception> + public T this[int index] + { +#if PROJECTN + [BoundsChecking] + get + { + return Unsafe.Add(ref _pointer.Value, index); + } +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + if ((uint)index >= (uint)_length) + ThrowHelper.ThrowIndexOutOfRangeException(); + return Unsafe.Add(ref _pointer.Value, index); + } +#endif + } + + /// <summary> + /// Copies the contents of this read-only span into destination 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="destination">The span to copy items into.</param> + /// <exception cref="System.ArgumentException"> + /// Thrown when the destination Span is shorter than the source Span. + /// </exception> + /// </summary> + public void CopyTo(Span<T> destination) + { + if (!TryCopyTo(destination)) + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + } + + /// Copies the contents of this read-only span into destination span. If the source + /// and destinations overlap, this method behaves as if the original values in + /// a temporary location before the destination is overwritten. + /// </summary> + /// <returns>If the destination span is shorter than the source span, this method + /// return false and no data is written to the destination.</returns> + /// <param name="destination">The span to copy items into.</param> + public bool TryCopyTo(Span<T> destination) + { + if ((uint)_length > (uint)destination.Length) + return false; + + Span.CopyTo<T>(ref destination.DangerousGetPinnableReference(), ref _pointer.Value, _length); + return true; + } + + /// <summary> + /// Returns true if left and right point at the same memory and have the same length. Note that + /// this does *not* check to see if the *contents* are equal. + /// </summary> + public static bool operator ==(ReadOnlySpan<T> left, ReadOnlySpan<T> right) + { + return left._length == right._length && Unsafe.AreSame<T>(ref left._pointer.Value, ref right._pointer.Value); + } + + /// <summary> + /// Returns false if left and right point at the same memory and have the same length. Note that + /// this does *not* check to see if the *contents* are equal. + /// </summary> + public static bool operator !=(ReadOnlySpan<T> left, ReadOnlySpan<T> right) => !(left == right); + + /// <summary> + /// This method is not supported as spans cannot be boxed. To compare two spans, use operator==. + /// <exception cref="System.NotSupportedException"> + /// Always thrown by this method. + /// </exception> + /// </summary> + [Obsolete("Equals() on Span will always throw an exception. Use == instead.")] + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) + { + throw new NotSupportedException(SR.NotSupported_CannotCallEqualsOnSpan); + } + + /// <summary> + /// This method is not supported as spans cannot be boxed. + /// <exception cref="System.NotSupportedException"> + /// Always thrown by this method. + /// </exception> + /// </summary> + [Obsolete("GetHashCode() on Span will always throw an exception.")] + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() + { + throw new NotSupportedException(SR.NotSupported_CannotCallGetHashCodeOnSpan); + } + + /// <summary> + /// Defines an implicit conversion of an array to a <see cref="ReadOnlySpan{T}"/> + /// </summary> + public static implicit operator ReadOnlySpan<T>(T[] array) => new ReadOnlySpan<T>(array); + + /// <summary> + /// Defines an implicit conversion of a <see cref="ArraySegment{T}"/> to a <see cref="ReadOnlySpan{T}"/> + /// </summary> + public static implicit operator ReadOnlySpan<T>(ArraySegment<T> arraySegment) => new ReadOnlySpan<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count); + + /// <summary> + /// Forms a slice out of the given read-only span, beginning at 'start'. + /// </summary> + /// <param name="start">The index at which to begin this slice.</param> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> index is not in range (<0 or >=Length). + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan<T> Slice(int start) + { + if ((uint)start > (uint)_length) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + return new ReadOnlySpan<T>(ref Unsafe.Add(ref _pointer.Value, start), _length - start); + } + + /// <summary> + /// Forms a slice out of the given read-only span, beginning at 'start', of given length + /// </summary> + /// <param name="start">The index at which to begin this slice.</param> + /// <param name="length">The desired length for the slice (exclusive).</param> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> or end index is not in range (<0 or >=Length). + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan<T> Slice(int start, int length) + { + if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + 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. + /// </summary> + public T[] ToArray() + { + if (_length == 0) + return Array.Empty<T>(); + + var destination = new T[_length]; + Span.CopyTo<T>(ref Unsafe.As<byte, T>(ref destination.GetRawSzArrayData()), ref _pointer.Value, _length); + return destination; + } + + /// <summary> + /// Returns a 0-length read-only span whose base is the null pointer. + /// </summary> + public static ReadOnlySpan<T> Empty => default(ReadOnlySpan<T>); + } +} diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/ReadOnlyAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/IsReadOnlyAttribute.cs index aad7310412..657df43957 100644 --- a/src/mscorlib/shared/System/Runtime/CompilerServices/ReadOnlyAttribute.cs +++ b/src/mscorlib/shared/System/Runtime/CompilerServices/IsReadOnlyAttribute.cs @@ -12,9 +12,9 @@ namespace System.Runtime.CompilerServices /// </summary> [EditorBrowsable(EditorBrowsableState.Never)] [AttributeUsage(AttributeTargets.All, Inherited = false)] - public sealed class ReadOnlyAttribute : Attribute + public sealed class IsReadOnlyAttribute : Attribute { - public ReadOnlyAttribute() + public IsReadOnlyAttribute() { } } diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/RuntimeFeature.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/RuntimeFeature.cs index b93c435439..110a55fd2c 100644 --- a/src/mscorlib/shared/System/Runtime/CompilerServices/RuntimeFeature.cs +++ b/src/mscorlib/shared/System/Runtime/CompilerServices/RuntimeFeature.cs @@ -12,7 +12,9 @@ namespace System.Runtime.CompilerServices public static bool IsSupported(string feature) { // No features are supported for now. - // These features should be added as public static readonly string fields in the same class. + // These features should be added as public const string fields in the same class. + // Example: public const string FeatureName = nameof(FeatureName); + return false; } } diff --git a/src/mscorlib/shared/System/Span.NonGeneric.cs b/src/mscorlib/shared/System/Span.NonGeneric.cs new file mode 100644 index 0000000000..4cdba2181a --- /dev/null +++ b/src/mscorlib/shared/System/Span.NonGeneric.cs @@ -0,0 +1,522 @@ +// 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. + +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; + +#if BIT64 +using nuint = System.UInt64; +#else +using nuint = System.UInt32; +#endif + +namespace System +{ + /// <summary> + /// Extension methods and non-generic helpers for Span and ReadOnlySpan + /// </summary> + public static class Span + { + /// <summary> + /// Casts a Span of one primitive type <typeparamref name="T"/> to Span of bytes. + /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety. + /// </summary> + /// <param name="source">The source slice, of type <typeparamref name="T"/>.</param> + /// <exception cref="System.ArgumentException"> + /// Thrown when <typeparamref name="T"/> contains pointers. + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span<byte> AsBytes<T>(this Span<T> source) + where T : struct + { + if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); + + return new Span<byte>( + ref Unsafe.As<T, byte>(ref source.DangerousGetPinnableReference()), + checked(source.Length * Unsafe.SizeOf<T>())); + } + + /// <summary> + /// Casts a ReadOnlySpan of one primitive type <typeparamref name="T"/> to ReadOnlySpan of bytes. + /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety. + /// </summary> + /// <param name="source">The source slice, of type <typeparamref name="T"/>.</param> + /// <exception cref="System.ArgumentException"> + /// Thrown when <typeparamref name="T"/> contains pointers. + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan<byte> AsBytes<T>(this ReadOnlySpan<T> source) + where T : struct + { + if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); + + return new ReadOnlySpan<byte>( + ref Unsafe.As<T, byte>(ref source.DangerousGetPinnableReference()), + checked(source.Length * Unsafe.SizeOf<T>())); + } + + /// <summary> + /// Casts a Span of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>. + /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety. + /// </summary> + /// <remarks> + /// Supported only for platforms that support misaligned memory access. + /// </remarks> + /// <param name="source">The source slice, of type <typeparamref name="TFrom"/>.</param> + /// <exception cref="System.ArgumentException"> + /// Thrown when <typeparamref name="TFrom"/> or <typeparamref name="TTo"/> contains pointers. + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span<TTo> NonPortableCast<TFrom, TTo>(this Span<TFrom> source) + where TFrom : struct + where TTo : struct + { + if (RuntimeHelpers.IsReferenceOrContainsReferences<TFrom>()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TFrom)); + if (RuntimeHelpers.IsReferenceOrContainsReferences<TTo>()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo)); + + return new Span<TTo>( + ref Unsafe.As<TFrom, TTo>(ref source.DangerousGetPinnableReference()), + checked((int)((long)source.Length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>()))); + } + + /// <summary> + /// Casts a ReadOnlySpan of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>. + /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety. + /// </summary> + /// <remarks> + /// Supported only for platforms that support misaligned memory access. + /// </remarks> + /// <param name="source">The source slice, of type <typeparamref name="TFrom"/>.</param> + /// <exception cref="System.ArgumentException"> + /// Thrown when <typeparamref name="TFrom"/> or <typeparamref name="TTo"/> contains pointers. + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan<TTo> NonPortableCast<TFrom, TTo>(this ReadOnlySpan<TFrom> source) + where TFrom : struct + where TTo : struct + { + if (RuntimeHelpers.IsReferenceOrContainsReferences<TFrom>()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TFrom)); + if (RuntimeHelpers.IsReferenceOrContainsReferences<TTo>()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo)); + + return new ReadOnlySpan<TTo>( + ref Unsafe.As<TFrom, TTo>(ref source.DangerousGetPinnableReference()), + checked((int)((long)source.Length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>()))); + } + + /// <summary> + /// Creates a new readonly span over the portion of the target string. + /// </summary> + /// <param name="text">The target string.</param> + /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="text"/> is a null + /// reference (Nothing in Visual Basic).</exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan<char> AsSpan(this string text) + { + if (text == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); + + return new ReadOnlySpan<char>(ref text.GetRawStringData(), text.Length); + } + + internal static unsafe void CopyTo<T>(ref T destination, ref T source, int elementsCount) + { + if (Unsafe.AreSame(ref destination, ref source)) + return; + + if (elementsCount <= 1) + { + if (elementsCount == 1) + { + destination = source; + } + return; + } + + nuint byteCount = (nuint)elementsCount * (nuint)Unsafe.SizeOf<T>(); + if (!RuntimeHelpers.IsReferenceOrContainsReferences<T>()) + { + fixed (byte* pDestination = &Unsafe.As<T, byte>(ref destination)) + { + fixed (byte* pSource = &Unsafe.As<T, byte>(ref source)) + { + Buffer.Memmove(pDestination, pSource, byteCount); + } + } + } + else + { + RuntimeImports.RhBulkMoveWithWriteBarrier( + ref Unsafe.As<T, byte>(ref destination), + ref Unsafe.As<T, byte>(ref source), + byteCount); + } + } + + internal static unsafe void ClearWithoutReferences(ref byte b, nuint byteLength) + { + if (byteLength == 0) + return; + +#if AMD64 && CORECLR + if (byteLength > 4096) goto PInvoke; + Unsafe.InitBlockUnaligned(ref b, 0, (uint)byteLength); + return; +#else + // TODO: Optimize other platforms to be on par with AMD64 CoreCLR + // Note: It's important that this switch handles lengths at least up to 22. + // See notes below near the main loop for why. + + // The switch will be very fast since it can be implemented using a jump + // table in assembly. See http://stackoverflow.com/a/449297/4077294 for more info. + + switch (byteLength) + { + case 1: + b = 0; + return; + case 2: + Unsafe.As<byte, short>(ref b) = 0; + return; + case 3: + Unsafe.As<byte, short>(ref b) = 0; + Unsafe.Add<byte>(ref b, 2) = 0; + return; + case 4: + Unsafe.As<byte, int>(ref b) = 0; + return; + case 5: + Unsafe.As<byte, int>(ref b) = 0; + Unsafe.Add<byte>(ref b, 4) = 0; + return; + case 6: + Unsafe.As<byte, int>(ref b) = 0; + Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 4)) = 0; + return; + case 7: + Unsafe.As<byte, int>(ref b) = 0; + Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 4)) = 0; + Unsafe.Add<byte>(ref b, 6) = 0; + return; + case 8: +#if BIT64 + Unsafe.As<byte, long>(ref b) = 0; +#else + Unsafe.As<byte, int>(ref b) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0; +#endif + return; + case 9: +#if BIT64 + Unsafe.As<byte, long>(ref b) = 0; +#else + Unsafe.As<byte, int>(ref b) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0; +#endif + Unsafe.Add<byte>(ref b, 8) = 0; + return; + case 10: +#if BIT64 + Unsafe.As<byte, long>(ref b) = 0; +#else + Unsafe.As<byte, int>(ref b) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0; +#endif + Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 8)) = 0; + return; + case 11: +#if BIT64 + Unsafe.As<byte, long>(ref b) = 0; +#else + Unsafe.As<byte, int>(ref b) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0; +#endif + Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 8)) = 0; + Unsafe.Add<byte>(ref b, 10) = 0; + return; + case 12: +#if BIT64 + Unsafe.As<byte, long>(ref b) = 0; +#else + Unsafe.As<byte, int>(ref b) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0; +#endif + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0; + return; + case 13: +#if BIT64 + Unsafe.As<byte, long>(ref b) = 0; +#else + Unsafe.As<byte, int>(ref b) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0; +#endif + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0; + Unsafe.Add<byte>(ref b, 12) = 0; + return; + case 14: +#if BIT64 + Unsafe.As<byte, long>(ref b) = 0; +#else + Unsafe.As<byte, int>(ref b) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0; +#endif + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0; + Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 12)) = 0; + return; + case 15: +#if BIT64 + Unsafe.As<byte, long>(ref b) = 0; +#else + Unsafe.As<byte, int>(ref b) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0; +#endif + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0; + Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 12)) = 0; + Unsafe.Add<byte>(ref b, 14) = 0; + return; + case 16: +#if BIT64 + Unsafe.As<byte, long>(ref b) = 0; + Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0; +#else + Unsafe.As<byte, int>(ref b) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0; +#endif + return; + case 17: +#if BIT64 + Unsafe.As<byte, long>(ref b) = 0; + Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0; +#else + Unsafe.As<byte, int>(ref b) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0; +#endif + Unsafe.Add<byte>(ref b, 16) = 0; + return; + case 18: +#if BIT64 + Unsafe.As<byte, long>(ref b) = 0; + Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0; +#else + Unsafe.As<byte, int>(ref b) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0; +#endif + Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 16)) = 0; + return; + case 19: +#if BIT64 + Unsafe.As<byte, long>(ref b) = 0; + Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0; +#else + Unsafe.As<byte, int>(ref b) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0; +#endif + Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 16)) = 0; + Unsafe.Add<byte>(ref b, 18) = 0; + return; + case 20: +#if BIT64 + Unsafe.As<byte, long>(ref b) = 0; + Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0; +#else + Unsafe.As<byte, int>(ref b) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0; +#endif + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 16)) = 0; + return; + case 21: +#if BIT64 + Unsafe.As<byte, long>(ref b) = 0; + Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0; +#else + Unsafe.As<byte, int>(ref b) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0; +#endif + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 16)) = 0; + Unsafe.Add<byte>(ref b, 20) = 0; + return; + case 22: +#if BIT64 + Unsafe.As<byte, long>(ref b) = 0; + Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0; +#else + Unsafe.As<byte, int>(ref b) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0; + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0; +#endif + Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 16)) = 0; + Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 20)) = 0; + return; + } + + // P/Invoke into the native version for large lengths + if (byteLength >= 512) goto PInvoke; + + nuint i = 0; // byte offset at which we're copying + + if ((Unsafe.As<byte, int>(ref b) & 3) != 0) + { + if ((Unsafe.As<byte, int>(ref b) & 1) != 0) + { + Unsafe.AddByteOffset<byte>(ref b, i) = 0; + i += 1; + if ((Unsafe.As<byte, int>(ref b) & 2) != 0) + goto IntAligned; + } + Unsafe.As<byte, short>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0; + i += 2; + } + + IntAligned: + + // On 64-bit IntPtr.Size == 8, so we want to advance to the next 8-aligned address. If + // (int)b % 8 is 0, 5, 6, or 7, we will already have advanced by 0, 3, 2, or 1 + // bytes to the next aligned address (respectively), so do nothing. On the other hand, + // if it is 1, 2, 3, or 4 we will want to copy-and-advance another 4 bytes until + // we're aligned. + // The thing 1, 2, 3, and 4 have in common that the others don't is that if you + // subtract one from them, their 3rd lsb will not be set. Hence, the below check. + + if (((Unsafe.As<byte, int>(ref b) - 1) & 4) == 0) + { + Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0; + i += 4; + } + + nuint end = byteLength - 16; + byteLength -= i; // lower 4 bits of byteLength represent how many bytes are left *after* the unrolled loop + + // We know due to the above switch-case that this loop will always run 1 iteration; max + // bytes we clear before checking is 23 (7 to align the pointers, 16 for 1 iteration) so + // the switch handles lengths 0-22. + Debug.Assert(end >= 7 && i <= end); + + // This is separated out into a different variable, so the i + 16 addition can be + // performed at the start of the pipeline and the loop condition does not have + // a dependency on the writes. + nuint counter; + + do + { + counter = i + 16; + + // This loop looks very costly since there appear to be a bunch of temporary values + // being created with the adds, but the jit (for x86 anyways) will convert each of + // these to use memory addressing operands. + + // So the only cost is a bit of code size, which is made up for by the fact that + // we save on writes to b. + +#if BIT64 + Unsafe.As<byte, long>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0; + Unsafe.As<byte, long>(ref Unsafe.AddByteOffset<byte>(ref b, i + 8)) = 0; +#else + Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0; + Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i + 4)) = 0; + Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i + 8)) = 0; + Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i + 12)) = 0; +#endif + + i = counter; + + // See notes above for why this wasn't used instead + // i += 16; + } + while (counter <= end); + + if ((byteLength & 8) != 0) + { +#if BIT64 + Unsafe.As<byte, long>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0; +#else + Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0; + Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i + 4)) = 0; +#endif + i += 8; + } + if ((byteLength & 4) != 0) + { + Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0; + i += 4; + } + if ((byteLength & 2) != 0) + { + Unsafe.As<byte, short>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0; + i += 2; + } + if ((byteLength & 1) != 0) + { + Unsafe.AddByteOffset<byte>(ref b, i) = 0; + // We're not using i after this, so not needed + // i += 1; + } + + return; +#endif + + PInvoke: + RuntimeImports.RhZeroMemory(ref b, byteLength); + } + + internal static unsafe void ClearWithReferences(ref IntPtr ip, nuint pointerSizeLength) + { + if (pointerSizeLength == 0) + return; + + // TODO: Perhaps do switch casing to improve small size perf + + nuint i = 0; + nuint n = 0; + while ((n = i + 8) <= (pointerSizeLength)) + { + Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 1) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 2) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 3) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 4) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 5) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 6) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 7) * (nuint)sizeof(IntPtr)) = default(IntPtr); + i = n; + } + if ((n = i + 4) <= (pointerSizeLength)) + { + Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 1) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 2) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 3) * (nuint)sizeof(IntPtr)) = default(IntPtr); + i = n; + } + if ((n = i + 2) <= (pointerSizeLength)) + { + Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr); + Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 1) * (nuint)sizeof(IntPtr)) = default(IntPtr); + i = n; + } + if ((i + 1) <= (pointerSizeLength)) + { + Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr); + } + } + } +} diff --git a/src/mscorlib/shared/System/Span.cs b/src/mscorlib/shared/System/Span.cs new file mode 100644 index 0000000000..82ea35f670 --- /dev/null +++ b/src/mscorlib/shared/System/Span.cs @@ -0,0 +1,413 @@ +// 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. + +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; +using EditorBrowsableState = System.ComponentModel.EditorBrowsableState; +using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute; + +#pragma warning disable 0809 //warning CS0809: Obsolete member 'Span<T>.Equals(object)' overrides non-obsolete member 'object.Equals(object)' + +#if BIT64 +using nuint = System.UInt64; +#else +using nuint = System.UInt32; +#endif + +namespace System +{ + /// <summary> + /// Span represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed + /// or native memory, or to memory allocated on the stack. It is type- and memory-safe. + /// </summary> + public struct Span<T> + { + /// <summary>A byref or a native ptr.</summary> + private readonly ByReference<T> _pointer; + /// <summary>The number of elements this Span contains.</summary> +#if PROJECTN + [Bound] +#endif + private readonly int _length; + + /// <summary> + /// Creates a new span over the entirety of the target array. + /// </summary> + /// <param name="array">The target array.</param> + /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null + /// reference (Nothing in Visual Basic).</exception> + /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span(T[] array) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + if (default(T) == null && array.GetType() != typeof(T[])) + ThrowHelper.ThrowArrayTypeMismatchException(); + + _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData())); + _length = array.Length; + } + + /// <summary> + /// Creates a new span over the portion of the target array beginning + /// at 'start' index and covering the remainder of the array. + /// </summary> + /// <param name="array">The target array.</param> + /// <param name="start">The index at which to begin the span.</param> + /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null + /// reference (Nothing in Visual Basic).</exception> + /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> is not in the range (<0 or >=Length). + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span(T[] array, int start) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + if (default(T) == null && array.GetType() != typeof(T[])) + ThrowHelper.ThrowArrayTypeMismatchException(); + if ((uint)start > (uint)array.Length) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + _pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start)); + _length = array.Length - start; + } + + /// <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="array">The target array.</param> + /// <param name="start">The index at which to begin the span.</param> + /// <param name="length">The number of items in the span.</param> + /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null + /// reference (Nothing in Visual Basic).</exception> + /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> or end index is not in the range (<0 or >=Length). + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span(T[] array, int start, int length) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + if (default(T) == null && array.GetType() != typeof(T[])) + ThrowHelper.ThrowArrayTypeMismatchException(); + if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + _pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start)); + _length = length; + } + + /// <summary> + /// Creates a new span over the target unmanaged buffer. Clearly this + /// is quite dangerous, because we are creating arbitrarily typed T's + /// out of a void*-typed block of memory. And the length is not checked. + /// But if this creation is correct, then all subsequent uses are correct. + /// </summary> + /// <param name="pointer">An unmanaged pointer to memory.</param> + /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param> + /// <exception cref="System.ArgumentException"> + /// Thrown when <typeparamref name="T"/> is reference type or contains pointers and hence cannot be stored in unmanaged memory. + /// </exception> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="length"/> is negative. + /// </exception> + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe Span(void* pointer, int length) + { + if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); + if (length < 0) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref *(byte*)pointer)); + _length = length; + } + + /// <summary> + /// Create a new span over a portion of a regular managed object. This can be useful + /// if part of a managed object represents a "fixed array." This is dangerous because neither the + /// <paramref name="length"/> is checked, nor <paramref name="obj"/> being null, nor the fact that + /// "rawPointer" actually lies within <paramref name="obj"/>. + /// </summary> + /// <param name="obj">The managed object that contains the data to span over.</param> + /// <param name="objectData">A reference to data within that object.</param> + /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span<T> DangerousCreate(object obj, ref T objectData, int length) => new Span<T>(ref objectData, length); + + // Constructor for internal use only. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Span(ref T ptr, int length) + { + Debug.Assert(length >= 0); + + _pointer = new ByReference<T>(ref ptr); + _length = length; + } + + /// <summary> + /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element + /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. + /// </summary> + public ref T DangerousGetPinnableReference() + { + return ref _pointer.Value; + } + + /// <summary> + /// The number of items in the span. + /// </summary> + public int Length => _length; + + /// <summary> + /// Returns true if Length is 0. + /// </summary> + public bool IsEmpty => _length == 0; + + /// <summary> + /// Returns a reference to specified element of the Span. + /// </summary> + /// <param name="index"></param> + /// <returns></returns> + /// <exception cref="System.IndexOutOfRangeException"> + /// Thrown when index less than 0 or index greater than or equal to Length + /// </exception> + public ref T this[int index] + { +#if PROJECTN + [BoundsChecking] + get + { + return ref Unsafe.Add(ref _pointer.Value, index); + } +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + if ((uint)index >= (uint)_length) + ThrowHelper.ThrowIndexOutOfRangeException(); + return ref Unsafe.Add(ref _pointer.Value, index); + } +#endif + } + + /// <summary> + /// Clears the contents of this span. + /// </summary> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) + { + Span.ClearWithReferences(ref Unsafe.As<T, IntPtr>(ref _pointer.Value), (nuint)_length * (nuint)(Unsafe.SizeOf<T>() / sizeof(nuint))); + } + else + { + Span.ClearWithoutReferences(ref Unsafe.As<T, byte>(ref _pointer.Value), (nuint)_length * (nuint)Unsafe.SizeOf<T>()); + } + } + + /// <summary> + /// Fills the contents of this span with the given value. + /// </summary> + public void Fill(T value) + { + if (Unsafe.SizeOf<T>() == 1) + { + uint length = (uint)_length; + if (length == 0) + return; + + T tmp = value; // Avoid taking address of the "value" argument. It would regress performance of the loop below. + Unsafe.InitBlockUnaligned(ref Unsafe.As<T, byte>(ref _pointer.Value), Unsafe.As<T, byte>(ref tmp), length); + } + else + { + // Do all math as nuint to avoid unnecessary 64->32->64 bit integer truncations + nuint length = (uint)_length; + if (length == 0) + return; + + ref T r = ref DangerousGetPinnableReference(); + + // TODO: Create block fill for value types of power of two sizes e.g. 2,4,8,16 + + nuint elementSize = (uint)Unsafe.SizeOf<T>(); + nuint i = 0; + for (; i < (length & ~(nuint)7); i += 8) + { + Unsafe.AddByteOffset<T>(ref r, (i + 0) * elementSize) = value; + Unsafe.AddByteOffset<T>(ref r, (i + 1) * elementSize) = value; + Unsafe.AddByteOffset<T>(ref r, (i + 2) * elementSize) = value; + Unsafe.AddByteOffset<T>(ref r, (i + 3) * elementSize) = value; + Unsafe.AddByteOffset<T>(ref r, (i + 4) * elementSize) = value; + Unsafe.AddByteOffset<T>(ref r, (i + 5) * elementSize) = value; + Unsafe.AddByteOffset<T>(ref r, (i + 6) * elementSize) = value; + Unsafe.AddByteOffset<T>(ref r, (i + 7) * elementSize) = value; + } + if (i < (length & ~(nuint)3)) + { + Unsafe.AddByteOffset<T>(ref r, (i + 0) * elementSize) = value; + Unsafe.AddByteOffset<T>(ref r, (i + 1) * elementSize) = value; + Unsafe.AddByteOffset<T>(ref r, (i + 2) * elementSize) = value; + Unsafe.AddByteOffset<T>(ref r, (i + 3) * elementSize) = value; + i += 4; + } + for (; i < length; i++) + { + Unsafe.AddByteOffset<T>(ref r, i * elementSize) = value; + } + } + } + + /// <summary> + /// Copies the contents of this span into destination span. If the source + /// and destinations overlap, this method behaves as if the original values in + /// a temporary location before the destination is overwritten. + /// </summary> + /// <param name="destination">The span to copy items into.</param> + /// <exception cref="System.ArgumentException"> + /// Thrown when the destination Span is shorter than the source Span. + /// </exception> + public void CopyTo(Span<T> destination) + { + if (!TryCopyTo(destination)) + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + } + + /// <summary> + /// Copies the contents of this span into destination span. If the source + /// and destinations overlap, this method behaves as if the original values in + /// a temporary location before the destination is overwritten. + /// </summary> + /// <param name="destination">The span to copy items into.</param> + /// <returns>If the destination span is shorter than the source span, this method + /// return false and no data is written to the destination.</returns> + public bool TryCopyTo(Span<T> destination) + { + if ((uint)_length > (uint)destination.Length) + return false; + + Span.CopyTo<T>(ref destination._pointer.Value, ref _pointer.Value, _length); + return true; + } + + /// <summary> + /// Returns true if left and right point at the same memory and have the same length. Note that + /// this does *not* check to see if the *contents* are equal. + /// </summary> + public static bool operator ==(Span<T> left, Span<T> right) + { + return left._length == right._length && Unsafe.AreSame<T>(ref left._pointer.Value, ref right._pointer.Value); + } + + /// <summary> + /// Returns false if left and right point at the same memory and have the same length. Note that + /// this does *not* check to see if the *contents* are equal. + /// </summary> + public static bool operator !=(Span<T> left, Span<T> right) => !(left == right); + + /// <summary> + /// This method is not supported as spans cannot be boxed. To compare two spans, use operator==. + /// <exception cref="System.NotSupportedException"> + /// Always thrown by this method. + /// </exception> + /// </summary> + [Obsolete("Equals() on Span will always throw an exception. Use == instead.")] + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) + { + throw new NotSupportedException(SR.NotSupported_CannotCallEqualsOnSpan); + } + + /// <summary> + /// This method is not supported as spans cannot be boxed. + /// <exception cref="System.NotSupportedException"> + /// Always thrown by this method. + /// </exception> + /// </summary> + [Obsolete("GetHashCode() on Span will always throw an exception.")] + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() + { + throw new NotSupportedException(SR.NotSupported_CannotCallGetHashCodeOnSpan); + } + + /// <summary> + /// Defines an implicit conversion of an array to a <see cref="Span{T}"/> + /// </summary> + public static implicit operator Span<T>(T[] array) => new Span<T>(array); + + /// <summary> + /// Defines an implicit conversion of a <see cref="ArraySegment{T}"/> to a <see cref="Span{T}"/> + /// </summary> + public static implicit operator Span<T>(ArraySegment<T> arraySegment) => new Span<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count); + + /// <summary> + /// Defines an implicit conversion of a <see cref="Span{T}"/> to a <see cref="ReadOnlySpan{T}"/> + /// </summary> + public static implicit operator ReadOnlySpan<T>(Span<T> span) => new ReadOnlySpan<T>(ref span._pointer.Value, span._length); + + /// <summary> + /// Forms a slice out of the given span, beginning at 'start'. + /// </summary> + /// <param name="start">The index at which to begin this slice.</param> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> index is not in range (<0 or >=Length). + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span<T> Slice(int start) + { + if ((uint)start > (uint)_length) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + return new Span<T>(ref Unsafe.Add(ref _pointer.Value, start), _length - start); + } + + /// <summary> + /// Forms a slice out of the given span, beginning at 'start', of given length + /// </summary> + /// <param name="start">The index at which to begin this slice.</param> + /// <param name="length">The desired length for the slice (exclusive).</param> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> or end index is not in range (<0 or >=Length). + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span<T> Slice(int start, int length) + { + if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + 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. + /// </summary> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T[] ToArray() + { + if (_length == 0) + return Array.Empty<T>(); + + var destination = new T[_length]; + Span.CopyTo<T>(ref Unsafe.As<byte, T>(ref destination.GetRawSzArrayData()), ref _pointer.Value, _length); + return destination; + } + + // <summary> + /// Returns an empty <see cref="Span{T}"/> + /// </summary> + public static Span<T> Empty => default(Span<T>); + } +} diff --git a/src/mscorlib/shared/System/Text/StringBuilder.cs b/src/mscorlib/shared/System/Text/StringBuilder.cs index df1a889823..3a06114bff 100644 --- a/src/mscorlib/shared/System/Text/StringBuilder.cs +++ b/src/mscorlib/shared/System/Text/StringBuilder.cs @@ -1081,67 +1081,56 @@ namespace System.Text return this; } - // Append joined values with a separator between each value. - public unsafe StringBuilder AppendJoin<T>(char separator, params T[] values) - { - // Defer argument validation to the internal function - return AppendJoinCore(&separator, 1, values); - } + + #region AppendJoin - public unsafe StringBuilder AppendJoin<T>(string separator, params T[] values) + public unsafe StringBuilder AppendJoin(string separator, params object[] values) { separator = separator ?? string.Empty; fixed (char* pSeparator = separator) { - // Defer argument validation to the internal function return AppendJoinCore(pSeparator, separator.Length, values); } } - public unsafe StringBuilder AppendJoin<T>(char separator, IEnumerable<T> values) + public unsafe StringBuilder AppendJoin<T>(string separator, IEnumerable<T> values) { - // Defer argument validation to the internal function - return AppendJoinCore(&separator, 1, values); + separator = separator ?? string.Empty; + fixed (char* pSeparator = separator) + { + return AppendJoinCore(pSeparator, separator.Length, values); + } } - public unsafe StringBuilder AppendJoin<T>(string separator, IEnumerable<T> values) + public unsafe StringBuilder AppendJoin(string separator, params string[] values) { separator = separator ?? string.Empty; fixed (char* pSeparator = separator) { - // Defer argument validation to the internal function return AppendJoinCore(pSeparator, separator.Length, values); } } - private unsafe StringBuilder AppendJoinCore<T>(char* separator, int separatorLength, params T[] values) + public unsafe StringBuilder AppendJoin(char separator, params object[] values) { - if (values == null) - throw new ArgumentNullException(nameof(values)); - Contract.Ensures(Contract.Result<StringBuilder>() != null); - - if (values.Length == 0) - return this; + return AppendJoinCore(&separator, 1, values); + } - var value = values[0]; - if (value != null) - Append(value.ToString()); + public unsafe StringBuilder AppendJoin<T>(char separator, IEnumerable<T> values) + { + return AppendJoinCore(&separator, 1, values); + } - for (var i = 1; i < values.Length; i++) - { - Append(separator, separatorLength); - value = values[i]; - if (value != null) - Append(value.ToString()); - } - return this; + public unsafe StringBuilder AppendJoin(char separator, params string[] values) + { + return AppendJoinCore(&separator, 1, values); } + private unsafe StringBuilder AppendJoinCore<T>(char* separator, int separatorLength, IEnumerable<T> values) { if (values == null) - throw new ArgumentNullException(nameof(values)); - Contract.Ensures(Contract.Result<StringBuilder>() != null); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); using (var en = values.GetEnumerator()) { @@ -1163,6 +1152,29 @@ namespace System.Text return this; } + private unsafe StringBuilder AppendJoinCore<T>(char* separator, int separatorLength, T[] values) + { + if (values == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); + + if (values.Length == 0) + return this; + + if (values[0] != null) + Append(values[0].ToString()); + + for (var i = 1; i < values.Length; i++) + { + Append(separator, separatorLength); + if (values[i] != null) + Append(values[i].ToString()); + } + return this; + } + + #endregion + + /*====================================Insert==================================== ** ==============================================================================*/ diff --git a/src/mscorlib/shared/System/Threading/Tasks/TaskExtensions.cs b/src/mscorlib/shared/System/Threading/Tasks/TaskExtensions.cs index 1098299517..97f2013d79 100644 --- a/src/mscorlib/shared/System/Threading/Tasks/TaskExtensions.cs +++ b/src/mscorlib/shared/System/Threading/Tasks/TaskExtensions.cs @@ -21,7 +21,7 @@ namespace System.Threading.Tasks // it completed successfully. Return its inner task to avoid unnecessary wrapping, or if the inner // task is null, return a canceled task to match the same semantics as CreateUnwrapPromise. return - !task.IsRanToCompletion ? Task.CreateUnwrapPromise<VoidTaskResult>(task, lookForOce: false) : + !task.IsCompletedSuccessfully ? Task.CreateUnwrapPromise<VoidTaskResult>(task, lookForOce: false) : task.Result ?? Task.FromCanceled(new CancellationToken(true)); } @@ -40,7 +40,7 @@ namespace System.Threading.Tasks // it completed successfully. Return its inner task to avoid unnecessary wrapping, or if the inner // task is null, return a canceled task to match the same semantics as CreateUnwrapPromise. return - !task.IsRanToCompletion ? Task.CreateUnwrapPromise<TResult>(task, lookForOce: false) : + !task.IsCompletedSuccessfully ? Task.CreateUnwrapPromise<TResult>(task, lookForOce: false) : task.Result ?? Task.FromCanceled<TResult>(new CancellationToken(true)); } |