summaryrefslogtreecommitdiff
path: root/src/mscorlib
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib')
-rw-r--r--src/mscorlib/Resources/Strings.resx12
-rw-r--r--src/mscorlib/System.Private.CoreLib.csproj7
-rw-r--r--src/mscorlib/shared/Interop/Unix/System.Native/Interop.Stat.cs2
-rw-r--r--src/mscorlib/shared/Interop/Windows/Kernel32/Interop.GetCPInfo.cs24
-rw-r--r--src/mscorlib/shared/System.Private.CoreLib.Shared.projitems7
-rw-r--r--src/mscorlib/shared/System/CharEnumerator.cs1
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/EventCounter.cs436
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs18
-rw-r--r--src/mscorlib/shared/System/ReadOnlySpan.cs (renamed from src/mscorlib/src/System/ReadOnlySpan.cs)22
-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.cs4
-rw-r--r--src/mscorlib/shared/System/Span.NonGeneric.cs (renamed from src/mscorlib/src/System/Span.cs)400
-rw-r--r--src/mscorlib/shared/System/Span.cs413
-rw-r--r--src/mscorlib/shared/System/Text/StringBuilder.cs78
-rw-r--r--src/mscorlib/shared/System/Threading/Tasks/TaskExtensions.cs4
-rw-r--r--src/mscorlib/src/Microsoft/Win32/Registry.cs134
-rw-r--r--src/mscorlib/src/Microsoft/Win32/RegistryKey.cs672
-rw-r--r--src/mscorlib/src/Microsoft/Win32/RegistryValueKind.cs10
-rw-r--r--src/mscorlib/src/Microsoft/Win32/RegistryView.cs22
-rw-r--r--src/mscorlib/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.cs10
-rw-r--r--src/mscorlib/src/System/Collections/Generic/Dictionary.cs89
-rw-r--r--src/mscorlib/src/System/Collections/Generic/List.cs1
-rw-r--r--src/mscorlib/src/System/Delegate.cs5
-rw-r--r--src/mscorlib/src/System/Environment.cs2
-rw-r--r--src/mscorlib/src/System/Globalization/CompareInfo.cs48
-rw-r--r--src/mscorlib/src/System/Globalization/HijriCalendar.Win32.cs61
-rw-r--r--src/mscorlib/src/System/Globalization/JapaneseCalendar.Win32.cs38
-rw-r--r--src/mscorlib/src/System/IO/Stream.cs4
-rw-r--r--src/mscorlib/src/System/Reflection/Assembly.CoreCLR.cs24
-rw-r--r--src/mscorlib/src/System/Runtime/CompilerServices/TaskAwaiter.cs2
-rw-r--r--src/mscorlib/src/System/Runtime/CompilerServices/jithelpers.cs9
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/Marshal.cs3
-rw-r--r--src/mscorlib/src/System/String.Manipulation.cs7
-rw-r--r--src/mscorlib/src/System/String.cs2
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/Task.cs3
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs2
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/future.cs6
-rw-r--r--src/mscorlib/src/System/ThrowHelper.cs1
-rw-r--r--src/mscorlib/src/System/TimeZoneInfo.Win32.cs30
-rw-r--r--src/mscorlib/src/System/TimeZoneInfo.cs114
40 files changed, 1606 insertions, 1125 deletions
diff --git a/src/mscorlib/Resources/Strings.resx b/src/mscorlib/Resources/Strings.resx
index cb9474fe00..791d1ca04d 100644
--- a/src/mscorlib/Resources/Strings.resx
+++ b/src/mscorlib/Resources/Strings.resx
@@ -2864,9 +2864,6 @@
<data name="NotSupported_AppX" xml:space="preserve">
<value>{0} is not supported in AppX.</value>
</data>
- <data name="NotSupported_AssemblyLoadCodeBase" xml:space="preserve">
- <value>Assembly.Load with a Codebase is not supported.</value>
- </data>
<data name="NotSupported_AssemblyLoadFromHash" xml:space="preserve">
<value>Assembly.LoadFrom with hashValue is not supported.</value>
</data>
@@ -3575,18 +3572,9 @@
<data name="LockRecursionException_RecursiveWriteNotAllowed" xml:space="preserve">
<value>Recursive write lock acquisitions not allowed in this mode.</value>
</data>
- <data name="LockRecursionException_ReadAfterWriteNotAllowed" xml:space="preserve">
- <value>A read lock may not be acquired with the write lock held in this mode.</value>
- </data>
<data name="LockRecursionException_RecursiveUpgradeNotAllowed" xml:space="preserve">
<value>Recursive upgradeable lock acquisitions not allowed in this mode.</value>
</data>
- <data name="LockRecursionException_RecursiveReadNotAllowed" xml:space="preserve">
- <value>Recursive read lock acquisitions not allowed in this mode.</value>
- </data>
- <data name="LockRecursionException_WriteAfterReadNotAllowed" xml:space="preserve">
- <value>Write lock may not be acquired with read lock held. This pattern is prone to deadlocks. Please ensure that read locks are released before taking a write lock. If an upgrade is necessary, use an upgrade lock in place of the read lock.</value>
- </data>
<data name="LockRecursionException_WriteAfterReadNotAllowed" xml:space="preserve">
<value>Write lock may not be acquired with read lock held. This pattern is prone to deadlocks. Please ensure that read locks are released before taking a write lock. If an upgrade is necessary, use an upgrade lock in place of the read lock.</value>
</data>
diff --git a/src/mscorlib/System.Private.CoreLib.csproj b/src/mscorlib/System.Private.CoreLib.csproj
index 3a0917f75e..df86533182 100644
--- a/src/mscorlib/System.Private.CoreLib.csproj
+++ b/src/mscorlib/System.Private.CoreLib.csproj
@@ -86,9 +86,11 @@
<PropertyGroup Condition="'$(OsEnvironment)' == 'Unix'">
<DebugType>portable</DebugType>
</PropertyGroup>
+
<PropertyGroup Condition="'$(TargetsOSX)' == 'true'">
<DefineConstants>PLATFORM_OSX;$(DefineConstants)</DefineConstants>
</PropertyGroup>
+
<!-- Assembly attributes -->
<PropertyGroup>
<AssemblyName>System.Private.CoreLib</AssemblyName>
@@ -379,8 +381,6 @@
<Compile Condition="'$(FeatureCominterop)' == 'true'" Include="$(BclSourcesRoot)\System\Variant.cs" />
<Compile Condition="'$(FeatureClassicCominterop)' == 'true'" Include="$(BclSourcesRoot)\System\OleAutBinder.cs" />
<Compile Include="$(BclSourcesRoot)\System\ByReference.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Span.cs" />
- <Compile Include="$(BclSourcesRoot)\System\ReadOnlySpan.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\Internal\Runtime\Augments\EnvironmentAugments.cs" />
@@ -625,6 +625,7 @@
<Compile Condition="'$(FeatureWin32Registry)' == 'true'" Include="$(BclSourcesRoot)\Microsoft\Win32\Registry.cs" />
<Compile Condition="'$(FeatureWin32Registry)' == 'true'" Include="$(BclSourcesRoot)\Microsoft\Win32\RegistryKey.cs" />
<Compile Condition="'$(FeatureWin32Registry)' == 'true'" Include="$(BclSourcesRoot)\Microsoft\Win32\RegistryValueKind.cs" />
+ <Compile Condition="'$(FeatureWin32Registry)' == 'true'" Include="$(BclSourcesRoot)\Microsoft\Win32\RegistryView.cs" />
<Compile Condition="'$(FeatureClassicCominterop)' == 'true'" Include="$(BclSourcesRoot)\Microsoft\Win32\OAVariantLib.cs" />
</ItemGroup>
<ItemGroup>
@@ -757,4 +758,4 @@
<Win32Resource Condition="'$(GenerateNativeVersionInfo)'=='true'">$(IntermediateOutputPath)\System.Private.CoreLib.res</Win32Resource>
</PropertyGroup>
<Import Project="GenerateCompilerResponseFile.targets" />
-</Project> \ No newline at end of file
+</Project>
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/src/System/ReadOnlySpan.cs b/src/mscorlib/shared/System/ReadOnlySpan.cs
index 8d0fbad0fc..8ec431f6fb 100644
--- a/src/mscorlib/src/System/ReadOnlySpan.cs
+++ b/src/mscorlib/shared/System/ReadOnlySpan.cs
@@ -20,6 +20,9 @@ namespace System
/// <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>
@@ -34,7 +37,7 @@ namespace System
if (array == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
- _pointer = new ByReference<T>(ref JitHelpers.GetArrayData(array));
+ _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()));
_length = array.Length;
}
@@ -57,7 +60,7 @@ namespace System
if ((uint)start > (uint)array.Length)
ThrowHelper.ThrowArgumentOutOfRangeException();
- _pointer = new ByReference<T>(ref Unsafe.Add(ref JitHelpers.GetArrayData(array), start));
+ _pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start));
_length = array.Length - start;
}
@@ -81,7 +84,7 @@ namespace System
if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();
- _pointer = new ByReference<T>(ref Unsafe.Add(ref JitHelpers.GetArrayData(array), start));
+ _pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start));
_length = length;
}
@@ -164,14 +167,21 @@ namespace System
/// </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>
@@ -202,7 +212,7 @@ namespace System
if ((uint)_length > (uint)destination.Length)
return false;
- SpanHelper.CopyTo<T>(ref destination.DangerousGetPinnableReference(), ref _pointer.Value, _length);
+ Span.CopyTo<T>(ref destination.DangerousGetPinnableReference(), ref _pointer.Value, _length);
return true;
}
@@ -301,7 +311,7 @@ namespace System
return Array.Empty<T>();
var destination = new T[_length];
- SpanHelper.CopyTo<T>(ref JitHelpers.GetArrayData(destination), ref _pointer.Value, _length);
+ Span.CopyTo<T>(ref Unsafe.As<byte, T>(ref destination.GetRawSzArrayData()), ref _pointer.Value, _length);
return destination;
}
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/src/System/Span.cs b/src/mscorlib/shared/System/Span.NonGeneric.cs
index 4211083def..4cdba2181a 100644
--- a/src/mscorlib/src/System/Span.cs
+++ b/src/mscorlib/shared/System/Span.NonGeneric.cs
@@ -5,11 +5,6 @@
using System.Diagnostics;
using System.Runtime;
using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-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;
@@ -20,388 +15,8 @@ using nuint = System.UInt32;
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.
+ /// Extension methods and non-generic helpers for Span and ReadOnlySpan
/// </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>
- 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 JitHelpers.GetArrayData(array));
- _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 (&lt;0 or &gt;=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 JitHelpers.GetArrayData(array), 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 (&lt;0 or &gt;=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 JitHelpers.GetArrayData(array), 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]
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- if ((uint)index >= (uint)_length)
- ThrowHelper.ThrowIndexOutOfRangeException();
-
- return ref Unsafe.Add(ref _pointer.Value, index);
- }
- }
-
- /// <summary>
- /// Clears the contents of this span.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Clear()
- {
- if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
- {
- SpanHelper.ClearWithReferences(ref Unsafe.As<T, IntPtr>(ref _pointer.Value), (nuint)_length * (nuint)(Unsafe.SizeOf<T>() / sizeof(nuint)));
- }
- else
- {
- SpanHelper.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;
-
- SpanHelper.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 (&lt;0 or &gt;=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 (&lt;0 or &gt;=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];
- SpanHelper.CopyTo<T>(ref JitHelpers.GetArrayData(destination), ref _pointer.Value, _length);
- return destination;
- }
-
- // <summary>
- /// Returns an empty <see cref="Span{T}"/>
- /// </summary>
- public static Span<T> Empty => default(Span<T>);
- }
-
public static class Span
{
/// <summary>
@@ -508,12 +123,9 @@ namespace System
if (text == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
- return new ReadOnlySpan<char>(ref text.GetFirstCharRef(), text.Length);
+ return new ReadOnlySpan<char>(ref text.GetRawStringData(), text.Length);
}
- }
- internal static class SpanHelper
- {
internal static unsafe void CopyTo<T>(ref T destination, ref T source, int elementsCount)
{
if (Unsafe.AreSame(ref destination, ref source))
@@ -553,12 +165,12 @@ namespace System
if (byteLength == 0)
return;
-#if AMD64
+#if AMD64 && CORECLR
if (byteLength > 4096) goto PInvoke;
Unsafe.InitBlockUnaligned(ref b, 0, (uint)byteLength);
return;
-#else // AMD64
- // TODO: Optimize this method on X86 machine
+#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.
@@ -860,7 +472,7 @@ namespace System
}
return;
-#endif // AMD64
+#endif
PInvoke:
RuntimeImports.RhZeroMemory(ref b, byteLength);
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 (&lt;0 or &gt;=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 (&lt;0 or &gt;=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 (&lt;0 or &gt;=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 (&lt;0 or &gt;=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));
}
diff --git a/src/mscorlib/src/Microsoft/Win32/Registry.cs b/src/mscorlib/src/Microsoft/Win32/Registry.cs
index aa2dd9b396..d0dbb0ff7f 100644
--- a/src/mscorlib/src/Microsoft/Win32/Registry.cs
+++ b/src/mscorlib/src/Microsoft/Win32/Registry.cs
@@ -2,12 +2,142 @@
// 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.Runtime.InteropServices;
+using System.Runtime.Versioning;
+
namespace Microsoft.Win32
{
+ /**
+ * Registry encapsulation. Contains members representing all top level system
+ * keys.
+ *
+ * @security(checkClassLinking=on)
+ */
+ //This class contains only static members and does not need to be serializable.
internal static class Registry
{
- public static readonly RegistryKey CurrentUser = RegistryKey.CurrentUser;
- public static readonly RegistryKey LocalMachine = RegistryKey.LocalMachine;
+ /**
+ * Current User Key.
+ *
+ * This key should be used as the root for all user specific settings.
+ */
+ public static readonly RegistryKey CurrentUser = RegistryKey.GetBaseKey(RegistryKey.HKEY_CURRENT_USER);
+
+ /**
+ * Local Machine Key.
+ *
+ * This key should be used as the root for all machine specific settings.
+ */
+ public static readonly RegistryKey LocalMachine = RegistryKey.GetBaseKey(RegistryKey.HKEY_LOCAL_MACHINE);
+
+ /**
+ * Classes Root Key.
+ *
+ * This is the root key of class information.
+ */
+ public static readonly RegistryKey ClassesRoot = RegistryKey.GetBaseKey(RegistryKey.HKEY_CLASSES_ROOT);
+
+ /**
+ * Users Root Key.
+ *
+ * This is the root of users.
+ */
+ public static readonly RegistryKey Users = RegistryKey.GetBaseKey(RegistryKey.HKEY_USERS);
+
+ /**
+ * Performance Root Key.
+ *
+ * This is where dynamic performance data is stored on NT.
+ */
+ public static readonly RegistryKey PerformanceData = RegistryKey.GetBaseKey(RegistryKey.HKEY_PERFORMANCE_DATA);
+
+ /**
+ * Current Config Root Key.
+ *
+ * This is where current configuration information is stored.
+ */
+ public static readonly RegistryKey CurrentConfig = RegistryKey.GetBaseKey(RegistryKey.HKEY_CURRENT_CONFIG);
+
+ //
+ // Following function will parse a keyName and returns the basekey for it.
+ // It will also store the subkey name in the out parameter.
+ // If the keyName is not valid, we will throw ArgumentException.
+ // The return value shouldn't be null.
+ //
+ private static RegistryKey GetBaseKeyFromKeyName(string keyName, out string subKeyName)
+ {
+ if (keyName == null)
+ {
+ throw new ArgumentNullException(nameof(keyName));
+ }
+
+ string basekeyName;
+ int i = keyName.IndexOf('\\');
+ if (i != -1)
+ {
+ basekeyName = keyName.Substring(0, i).ToUpper(System.Globalization.CultureInfo.InvariantCulture);
+ }
+ else
+ {
+ basekeyName = keyName.ToUpper(System.Globalization.CultureInfo.InvariantCulture);
+ }
+ RegistryKey basekey = null;
+
+ switch (basekeyName)
+ {
+ case "HKEY_CURRENT_USER":
+ basekey = Registry.CurrentUser;
+ break;
+ case "HKEY_LOCAL_MACHINE":
+ basekey = Registry.LocalMachine;
+ break;
+ case "HKEY_CLASSES_ROOT":
+ basekey = Registry.ClassesRoot;
+ break;
+ case "HKEY_USERS":
+ basekey = Registry.Users;
+ break;
+ case "HKEY_PERFORMANCE_DATA":
+ basekey = Registry.PerformanceData;
+ break;
+ case "HKEY_CURRENT_CONFIG":
+ basekey = Registry.CurrentConfig;
+ break;
+ default:
+ throw new ArgumentException(SR.Format(SR.Arg_RegInvalidKeyName, nameof(keyName)));
+ }
+ if (i == -1 || i == keyName.Length)
+ {
+ subKeyName = string.Empty;
+ }
+ else
+ {
+ subKeyName = keyName.Substring(i + 1, keyName.Length - i - 1);
+ }
+ return basekey;
+ }
+
+ public static object GetValue(string keyName, string valueName, object defaultValue)
+ {
+ string subKeyName;
+ RegistryKey basekey = GetBaseKeyFromKeyName(keyName, out subKeyName);
+ BCLDebug.Assert(basekey != null, "basekey can't be null.");
+ RegistryKey key = basekey.OpenSubKey(subKeyName);
+ if (key == null)
+ { // if the key doesn't exist, do nothing
+ return null;
+ }
+ try
+ {
+ return key.GetValue(valueName, defaultValue);
+ }
+ finally
+ {
+ key.Close();
+ }
+ }
}
}
diff --git a/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs b/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs
index e39b95903e..521ea65e10 100644
--- a/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs
+++ b/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs
@@ -2,6 +2,28 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+
+/*
+ Note on transaction support:
+ Eventually we will want to add support for NT's transactions to our
+ RegistryKey API's (possibly Whidbey M3?). When we do this, here's
+ the list of API's we need to make transaction-aware:
+
+ RegCreateKeyEx
+ RegDeleteKey
+ RegDeleteValue
+ RegEnumKeyEx
+ RegEnumValue
+ RegOpenKeyEx
+ RegQueryInfoKey
+ RegQueryValueEx
+ RegSetValueEx
+
+ We can ignore RegConnectRegistry (remote registry access doesn't yet have
+ transaction support) and RegFlushKey. RegCloseKey doesn't require any
+ additional work. .
+ */
+
/*
Note on ACL support:
The key thing to note about ACL's is you set them on a kernel object like a
@@ -30,28 +52,34 @@ using Microsoft.Win32.SafeHandles;
using System;
using System.Buffers;
using System.Collections.Generic;
-using System.Diagnostics;
+using System.Diagnostics.Contracts;
using System.IO;
+using System.Text;
namespace Microsoft.Win32
{
/**
* Registry encapsulation. To get an instance of a RegistryKey use the
* Registry class's static members then call OpenSubKey.
+ *
+ * @see Registry
+ * @security(checkDllCalls=off)
+ * @security(checkClassLinking=on)
*/
internal sealed class RegistryKey : MarshalByRefObject, IDisposable
{
- // Use the public Registry.CurrentUser
- internal static readonly RegistryKey CurrentUser =
- GetBaseKey(new IntPtr(unchecked((int)0x80000001)), "HKEY_CURRENT_USER");
-
- // Use the public Registry.LocalMachine
- internal static readonly RegistryKey LocalMachine =
- GetBaseKey(new IntPtr(unchecked((int)0x80000002)), "HKEY_LOCAL_MACHINE");
-
// We could use const here, if C# supported ELEMENT_TYPE_I fully.
- private static readonly IntPtr HKEY_CURRENT_USER = new IntPtr(unchecked((int)0x80000001));
- private static readonly IntPtr HKEY_LOCAL_MACHINE = new IntPtr(unchecked((int)0x80000002));
+ internal static readonly IntPtr HKEY_CLASSES_ROOT = new IntPtr(unchecked((int)0x80000000));
+ internal static readonly IntPtr HKEY_CURRENT_USER = new IntPtr(unchecked((int)0x80000001));
+ internal static readonly IntPtr HKEY_LOCAL_MACHINE = new IntPtr(unchecked((int)0x80000002));
+ internal static readonly IntPtr HKEY_USERS = new IntPtr(unchecked((int)0x80000003));
+ internal static readonly IntPtr HKEY_PERFORMANCE_DATA = new IntPtr(unchecked((int)0x80000004));
+ internal static readonly IntPtr HKEY_CURRENT_CONFIG = new IntPtr(unchecked((int)0x80000005));
+
+ // Dirty indicates that we have munged data that should be potentially
+ // written to disk.
+ //
+ private const int STATE_DIRTY = 0x0001;
// SystemKey indicates that this is a "SYSTEMKEY" and shouldn't be "opened"
// or "closed".
@@ -62,6 +90,20 @@ namespace Microsoft.Win32
//
private const int STATE_WRITEACCESS = 0x0004;
+ // Indicates if this key is for HKEY_PERFORMANCE_DATA
+ private const int STATE_PERF_DATA = 0x0008;
+
+ // Names of keys. This array must be in the same order as the HKEY values listed above.
+ //
+ private static readonly String[] hkeyNames = new String[] {
+ "HKEY_CLASSES_ROOT",
+ "HKEY_CURRENT_USER",
+ "HKEY_LOCAL_MACHINE",
+ "HKEY_USERS",
+ "HKEY_PERFORMANCE_DATA",
+ "HKEY_CURRENT_CONFIG",
+ };
+
// MSDN defines the following limits for registry key names & values:
// Key Name: 255 characters
// Value name: 16,383 Unicode characters
@@ -71,7 +113,31 @@ namespace Microsoft.Win32
private volatile SafeRegistryHandle hkey = null;
private volatile int state = 0;
- private volatile string keyName;
+ private volatile String keyName;
+ private volatile bool remoteKey = false;
+ private volatile RegistryKeyPermissionCheck checkMode;
+ private volatile RegistryView regView = RegistryView.Default;
+
+ /**
+ * RegistryInternalCheck values. Useful only for CheckPermission
+ */
+ private enum RegistryInternalCheck
+ {
+ CheckSubKeyWritePermission = 0,
+ CheckSubKeyReadPermission = 1,
+ CheckSubKeyCreatePermission = 2,
+ CheckSubTreeReadPermission = 3,
+ CheckSubTreeWritePermission = 4,
+ CheckSubTreeReadWritePermission = 5,
+ CheckValueWritePermission = 6,
+ CheckValueCreatePermission = 7,
+ CheckValueReadPermission = 8,
+ CheckKeyReadPermission = 9,
+ CheckSubTreePermission = 10,
+ CheckOpenSubKeyWithWritablePermission = 11,
+ CheckOpenSubKeyPermission = 12
+ };
+
/**
* Creates a RegistryKey.
@@ -82,10 +148,12 @@ namespace Microsoft.Win32
* The remoteKey flag when set to true indicates that we are dealing with registry entries
* on a remote machine and requires the program making these calls to have full trust.
*/
- private RegistryKey(SafeRegistryHandle hkey, bool writable, bool systemkey)
+ private RegistryKey(SafeRegistryHandle hkey, bool writable, bool systemkey, bool remoteKey, bool isPerfData, RegistryView view)
{
this.hkey = hkey;
keyName = "";
+ this.remoteKey = remoteKey;
+ regView = view;
if (systemkey)
{
state |= STATE_SYSTEMKEY;
@@ -94,6 +162,17 @@ namespace Microsoft.Win32
{
state |= STATE_WRITEACCESS;
}
+ if (isPerfData)
+ state |= STATE_PERF_DATA;
+ ValidateKeyView(view);
+ }
+
+ /**
+ * Closes this key, flushes it to disk if the contents have been modified.
+ */
+ public void Close()
+ {
+ Dispose(true);
}
private void Dispose(bool disposing)
@@ -115,6 +194,21 @@ namespace Microsoft.Win32
hkey = null;
}
}
+ else if (disposing && IsPerfDataKey())
+ {
+ // System keys should never be closed. However, we want to call RegCloseKey
+ // on HKEY_PERFORMANCE_DATA when called from PerformanceCounter.CloseSharedResources
+ // (i.e. when disposing is true) so that we release the PERFLIB cache and cause it
+ // to be refreshed (by re-reading the registry) when accessed subsequently.
+ // This is the only way we can see the just installed perf counter.
+ // NOTE: since HKEY_PERFORMANCE_DATA is process wide, there is inherent race condition in closing
+ // the key asynchronously. While Vista is smart enough to rebuild the PERFLIB resources
+ // in this situation the down level OSes are not. We have a small window between
+ // the dispose below and usage elsewhere (other threads). This is By Design.
+ // This is less of an issue when OS > NT5 (i.e Vista & higher), we can close the perfkey
+ // (to release & refresh PERFLIB resources) and the OS will rebuild PERFLIB as necessary.
+ SafeRegistryHandle.RegCloseKey(RegistryKey.HKEY_PERFORMANCE_DATA);
+ }
}
}
@@ -123,13 +217,13 @@ namespace Microsoft.Win32
Dispose(true);
}
- public void DeleteValue(string name, bool throwOnMissingValue)
+ public void DeleteValue(String name, bool throwOnMissingValue)
{
EnsureWriteable();
int errorCode = Win32Native.RegDeleteValue(hkey, name);
//
- // From windows 2003 server, if the name is too long we will get error code ERROR_FILENAME_EXCED_RANGE
+ // From windows 2003 server, if the name is too long we will get error code ERROR_FILENAME_EXCED_RANGE
// This still means the name doesn't exist. We need to be consistent with previous OS.
//
if (errorCode == Win32Native.ERROR_FILE_NOT_FOUND || errorCode == Win32Native.ERROR_FILENAME_EXCED_RANGE)
@@ -138,44 +232,77 @@ namespace Microsoft.Win32
{
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSubKeyValueAbsent);
}
-
// Otherwise, just return giving no indication to the user.
// (For compatibility)
}
-
// We really should throw an exception here if errorCode was bad,
// but we can't for compatibility reasons.
- Debug.Assert(errorCode == 0, "RegDeleteValue failed. Here's your error code: " + errorCode);
+ BCLDebug.Correctness(errorCode == 0, "RegDeleteValue failed. Here's your error code: " + errorCode);
+ }
+
+ /**
+ * Retrieves a new RegistryKey that represents the requested key. Valid
+ * values are:
+ *
+ * HKEY_CLASSES_ROOT,
+ * HKEY_CURRENT_USER,
+ * HKEY_LOCAL_MACHINE,
+ * HKEY_USERS,
+ * HKEY_PERFORMANCE_DATA,
+ * HKEY_CURRENT_CONFIG,
+ * HKEY_DYN_DATA.
+ *
+ * @param hKey HKEY_* to open.
+ *
+ * @return the RegistryKey requested.
+ */
+ internal static RegistryKey GetBaseKey(IntPtr hKey)
+ {
+ return GetBaseKey(hKey, RegistryView.Default);
}
- private static RegistryKey GetBaseKey(IntPtr hKey, string keyName)
+ internal static RegistryKey GetBaseKey(IntPtr hKey, RegistryView view)
{
- SafeRegistryHandle srh = new SafeRegistryHandle(hKey, ownsHandle: false);
+ int index = ((int)hKey) & 0x0FFFFFFF;
+ BCLDebug.Assert(index >= 0 && index < hkeyNames.Length, "index is out of range!");
+ BCLDebug.Assert((((int)hKey) & 0xFFFFFFF0) == 0x80000000, "Invalid hkey value!");
+
+ bool isPerf = hKey == HKEY_PERFORMANCE_DATA;
+ // only mark the SafeHandle as ownsHandle if the key is HKEY_PERFORMANCE_DATA.
+ SafeRegistryHandle srh = new SafeRegistryHandle(hKey, isPerf);
- RegistryKey key = new RegistryKey(srh, true, true);
- key.keyName = keyName;
+ RegistryKey key = new RegistryKey(srh, true, true, false, isPerf, view);
+ key.checkMode = RegistryKeyPermissionCheck.Default;
+ key.keyName = hkeyNames[index];
return key;
}
- /// <summary>
- /// Retrieves a subkey or null if the operation failed.
- /// </summary>
- /// <param name="writable">True to open writable, otherwise opens the key read-only.</param>
+ /**
+ * Retrieves a subkey. If readonly is <b>true</b>, then the subkey is opened with
+ * read-only access.
+ *
+ * @param name Name or path of subkey to open.
+ * @param readonly Set to <b>true</b> if you only need readonly access.
+ *
+ * @return the Subkey requested, or <b>null</b> if the operation failed.
+ */
public RegistryKey OpenSubKey(string name, bool writable)
{
ValidateKeyName(name);
EnsureNotDisposed();
+ name = FixupName(name); // Fixup multiple slashes to a single slash
SafeRegistryHandle result = null;
int ret = Win32Native.RegOpenKeyEx(hkey,
name,
0,
- writable ? Win32Native.KEY_READ | Win32Native.KEY_WRITE : Win32Native.KEY_READ,
+ GetRegistryKeyAccess(writable) | (int)regView,
out result);
if (ret == 0 && !result.IsInvalid)
{
- RegistryKey key = new RegistryKey(result, writable, false);
+ RegistryKey key = new RegistryKey(result, writable, false, remoteKey, false, regView);
+ key.checkMode = GetSubKeyPermissonCheck(writable);
key.keyName = keyName + "\\" + name;
return key;
}
@@ -191,6 +318,41 @@ namespace Microsoft.Win32
return null;
}
+ // This required no security checks. This is to get around the Deleting SubKeys which only require
+ // write permission. They call OpenSubKey which required read. Now instead call this function w/o security checks
+ internal RegistryKey InternalOpenSubKey(String name, bool writable)
+ {
+ ValidateKeyName(name);
+ EnsureNotDisposed();
+
+ SafeRegistryHandle result = null;
+ int ret = Win32Native.RegOpenKeyEx(hkey,
+ name,
+ 0,
+ GetRegistryKeyAccess(writable) | (int)regView,
+ out result);
+
+ if (ret == 0 && !result.IsInvalid)
+ {
+ RegistryKey key = new RegistryKey(result, writable, false, remoteKey, false, regView);
+ key.keyName = keyName + "\\" + name;
+ return key;
+ }
+ return null;
+ }
+
+ /**
+ * Returns a subkey with read only permissions.
+ *
+ * @param name Name or path of subkey to open.
+ *
+ * @return the Subkey requested, or <b>null</b> if the operation failed.
+ */
+ public RegistryKey OpenSubKey(String name)
+ {
+ return OpenSubKey(name, false);
+ }
+
/// <summary>
/// Retrieves an array of strings containing all the subkey names.
/// </summary>
@@ -319,6 +481,22 @@ namespace Microsoft.Win32
}
/**
+ * Retrieves the specified value. <b>null</b> is returned if the value
+ * doesn't exist.
+ *
+ * Note that <var>name</var> can be null or "", at which point the
+ * unnamed or default value of this Registry key is returned, if any.
+ *
+ * @param name Name of value to retrieve.
+ *
+ * @return the data associated with the value.
+ */
+ public Object GetValue(String name)
+ {
+ return InternalGetValue(name, null, false, true);
+ }
+
+ /**
* Retrieves the specified value. <i>defaultValue</i> is returned if the value doesn't exist.
*
* Note that <var>name</var> can be null or "", at which point the
@@ -333,11 +511,30 @@ namespace Microsoft.Win32
*
* @return the data associated with the value.
*/
- public object GetValue(string name, object defaultValue = null, bool doNotExpand = false)
+ public Object GetValue(String name, Object defaultValue)
{
- EnsureNotDisposed();
+ return InternalGetValue(name, defaultValue, false, true);
+ }
+
+ public Object GetValue(String name, Object defaultValue, RegistryValueOptions options)
+ {
+ if (options < RegistryValueOptions.None || options > RegistryValueOptions.DoNotExpandEnvironmentNames)
+ {
+ throw new ArgumentException(SR.Format(SR.Arg_EnumIllegalVal, (int)options), nameof(options));
+ }
+ bool doNotExpand = (options == RegistryValueOptions.DoNotExpandEnvironmentNames);
+ return InternalGetValue(name, defaultValue, doNotExpand, true);
+ }
+
+ internal Object InternalGetValue(String name, Object defaultValue, bool doNotExpand, bool checkSecurity)
+ {
+ if (checkSecurity)
+ {
+ // Name can be null! It's the most common use of RegQueryValueEx
+ EnsureNotDisposed();
+ }
- object data = defaultValue;
+ Object data = defaultValue;
int type = 0;
int datasize = 0;
@@ -345,20 +542,54 @@ namespace Microsoft.Win32
if (ret != 0)
{
- // For stuff like ERROR_FILE_NOT_FOUND, we want to return null (data).
- // Some OS's returned ERROR_MORE_DATA even in success cases, so we
- // want to continue on through the function.
- if (ret != Win32Native.ERROR_MORE_DATA)
- return data;
+ if (IsPerfDataKey())
+ {
+ int size = 65000;
+ int sizeInput = size;
+
+ int r;
+ byte[] blob = new byte[size];
+ while (Win32Native.ERROR_MORE_DATA == (r = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref sizeInput)))
+ {
+ if (size == Int32.MaxValue)
+ {
+ // ERROR_MORE_DATA was returned however we cannot increase the buffer size beyond Int32.MaxValue
+ Win32Error(r, name);
+ }
+ else if (size > (Int32.MaxValue / 2))
+ {
+ // at this point in the loop "size * 2" would cause an overflow
+ size = Int32.MaxValue;
+ }
+ else
+ {
+ size *= 2;
+ }
+ sizeInput = size;
+ blob = new byte[size];
+ }
+ if (r != 0)
+ Win32Error(r, name);
+ return blob;
+ }
+ else
+ {
+ // For stuff like ERROR_FILE_NOT_FOUND, we want to return null (data).
+ // Some OS's returned ERROR_MORE_DATA even in success cases, so we
+ // want to continue on through the function.
+ if (ret != Win32Native.ERROR_MORE_DATA)
+ return data;
+ }
}
if (datasize < 0)
{
// unexpected code path
- Debug.Assert(false, "[InternalGetValue] RegQueryValue returned ERROR_SUCCESS but gave a negative datasize");
+ BCLDebug.Assert(false, "[InternalGetValue] RegQueryValue returned ERROR_SUCCESS but gave a negative datasize");
datasize = 0;
}
+
switch (type)
{
case Win32Native.REG_NONE:
@@ -378,7 +609,7 @@ namespace Microsoft.Win32
goto case Win32Native.REG_BINARY;
}
long blob = 0;
- Debug.Assert(datasize == 8, "datasize==8");
+ BCLDebug.Assert(datasize == 8, "datasize==8");
// Here, datasize must be 8 when calling this
ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, ref blob, ref datasize);
@@ -393,7 +624,7 @@ namespace Microsoft.Win32
goto case Win32Native.REG_QWORD;
}
int blob = 0;
- Debug.Assert(datasize == 4, "datasize==4");
+ BCLDebug.Assert(datasize == 4, "datasize==4");
// Here, datasize must be four when calling this
ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, ref blob, ref datasize);
@@ -420,13 +651,13 @@ namespace Microsoft.Win32
ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref datasize);
if (blob.Length > 0 && blob[blob.Length - 1] == (char)0)
{
- data = new string(blob, 0, blob.Length - 1);
+ data = new String(blob, 0, blob.Length - 1);
}
else
{
// in the very unlikely case the data is missing null termination,
// pass in the whole char[] to prevent truncating a character
- data = new string(blob);
+ data = new String(blob);
}
}
break;
@@ -451,17 +682,17 @@ namespace Microsoft.Win32
if (blob.Length > 0 && blob[blob.Length - 1] == (char)0)
{
- data = new string(blob, 0, blob.Length - 1);
+ data = new String(blob, 0, blob.Length - 1);
}
else
{
// in the very unlikely case the data is missing null termination,
// pass in the whole char[] to prevent truncating a character
- data = new string(blob);
+ data = new String(blob);
}
if (!doNotExpand)
- data = Environment.ExpandEnvironmentVariables((string)data);
+ data = Environment.ExpandEnvironmentVariables((String)data);
}
break;
case Win32Native.REG_MULTI_SZ:
@@ -502,7 +733,8 @@ namespace Microsoft.Win32
blob[blob.Length - 1] = (char)0;
}
- IList<string> strings = new List<string>();
+
+ IList<String> strings = new List<String>();
int cur = 0;
int len = blob.Length;
@@ -516,28 +748,28 @@ namespace Microsoft.Win32
if (nextNull < len)
{
- Debug.Assert(blob[nextNull] == (char)0, "blob[nextNull] should be 0");
+ BCLDebug.Assert(blob[nextNull] == (char)0, "blob[nextNull] should be 0");
if (nextNull - cur > 0)
{
- strings.Add(new string(blob, cur, nextNull - cur));
+ strings.Add(new String(blob, cur, nextNull - cur));
}
else
{
// we found an empty string. But if we're at the end of the data,
// it's just the extra null terminator.
if (nextNull != len - 1)
- strings.Add(string.Empty);
+ strings.Add(String.Empty);
}
}
else
{
- strings.Add(new string(blob, cur, len - cur));
+ strings.Add(new String(blob, cur, len - cur));
}
cur = nextNull + 1;
}
- data = new string[strings.Count];
- strings.CopyTo((string[])data, 0);
+ data = new String[strings.Count];
+ strings.CopyTo((String[])data, 0);
}
break;
case Win32Native.REG_LINK:
@@ -560,10 +792,26 @@ namespace Microsoft.Win32
private bool IsPerfDataKey()
{
- return false;
+ return (state & STATE_PERF_DATA) != 0;
+ }
+
+ private void SetDirty()
+ {
+ state |= STATE_DIRTY;
+ }
+
+ /**
+ * Sets the specified value.
+ *
+ * @param name Name of value to store data in.
+ * @param value Data to store.
+ */
+ public void SetValue(String name, Object value)
+ {
+ SetValue(name, value, RegistryValueKind.Unknown);
}
- public unsafe void SetStringValue(string name, string value)
+ public unsafe void SetValue(String name, Object value, RegistryValueKind valueKind)
{
if (value == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
@@ -573,17 +821,167 @@ namespace Microsoft.Win32
throw new ArgumentException(SR.Arg_RegValStrLenBug);
}
+ if (!Enum.IsDefined(typeof(RegistryValueKind), valueKind))
+ throw new ArgumentException(SR.Arg_RegBadKeyKind, nameof(valueKind));
+
EnsureWriteable();
- int result = Win32Native.RegSetValueEx(hkey,
- name,
- 0,
- RegistryValueKind.String,
- value,
- checked(value.Length * 2 + 2));
+ if (valueKind == RegistryValueKind.Unknown)
+ {
+ // this is to maintain compatibility with the old way of autodetecting the type.
+ // SetValue(string, object) will come through this codepath.
+ valueKind = CalculateValueKind(value);
+ }
+
+ int ret = 0;
+ try
+ {
+ switch (valueKind)
+ {
+ case RegistryValueKind.ExpandString:
+ case RegistryValueKind.String:
+ {
+ String data = value.ToString();
+ ret = Win32Native.RegSetValueEx(hkey,
+ name,
+ 0,
+ valueKind,
+ data,
+ checked(data.Length * 2 + 2));
+ break;
+ }
+
+ case RegistryValueKind.MultiString:
+ {
+ // Other thread might modify the input array after we calculate the buffer length.
+ // Make a copy of the input array to be safe.
+ string[] dataStrings = (string[])(((string[])value).Clone());
+ int sizeInBytes = 0;
+
+ // First determine the size of the array
+ //
+ for (int i = 0; i < dataStrings.Length; i++)
+ {
+ if (dataStrings[i] == null)
+ {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetStrArrNull);
+ }
+ sizeInBytes = checked(sizeInBytes + (dataStrings[i].Length + 1) * 2);
+ }
+ sizeInBytes = checked(sizeInBytes + 2);
+
+ byte[] basePtr = new byte[sizeInBytes];
+ fixed (byte* b = basePtr)
+ {
+ IntPtr currentPtr = new IntPtr((void*)b);
+
+ // Write out the strings...
+ //
+ for (int i = 0; i < dataStrings.Length; i++)
+ {
+ // Assumes that the Strings are always null terminated.
+ String.InternalCopy(dataStrings[i], currentPtr, (checked(dataStrings[i].Length * 2)));
+ currentPtr = new IntPtr((long)currentPtr + (checked(dataStrings[i].Length * 2)));
+ *(char*)(currentPtr.ToPointer()) = '\0';
+ currentPtr = new IntPtr((long)currentPtr + 2);
+ }
+
+ *(char*)(currentPtr.ToPointer()) = '\0';
+ currentPtr = new IntPtr((long)currentPtr + 2);
+
+ ret = Win32Native.RegSetValueEx(hkey,
+ name,
+ 0,
+ RegistryValueKind.MultiString,
+ basePtr,
+ sizeInBytes);
+ }
+ break;
+ }
+
+ case RegistryValueKind.None:
+ case RegistryValueKind.Binary:
+ byte[] dataBytes = (byte[])value;
+ ret = Win32Native.RegSetValueEx(hkey,
+ name,
+ 0,
+ (valueKind == RegistryValueKind.None ? Win32Native.REG_NONE : RegistryValueKind.Binary),
+ dataBytes,
+ dataBytes.Length);
+ break;
+
+ case RegistryValueKind.DWord:
+ {
+ // We need to use Convert here because we could have a boxed type cannot be
+ // unboxed and cast at the same time. I.e. ((int)(object)(short) 5) will fail.
+ int data = Convert.ToInt32(value, System.Globalization.CultureInfo.InvariantCulture);
+
+ ret = Win32Native.RegSetValueEx(hkey,
+ name,
+ 0,
+ RegistryValueKind.DWord,
+ ref data,
+ 4);
+ break;
+ }
- if (result != 0)
- Win32Error(result, null);
+ case RegistryValueKind.QWord:
+ {
+ long data = Convert.ToInt64(value, System.Globalization.CultureInfo.InvariantCulture);
+
+ ret = Win32Native.RegSetValueEx(hkey,
+ name,
+ 0,
+ RegistryValueKind.QWord,
+ ref data,
+ 8);
+ break;
+ }
+ }
+ }
+ catch (OverflowException)
+ {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetMismatchedKind);
+ }
+ catch (InvalidOperationException)
+ {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetMismatchedKind);
+ }
+ catch (FormatException)
+ {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetMismatchedKind);
+ }
+ catch (InvalidCastException)
+ {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetMismatchedKind);
+ }
+
+ if (ret == 0)
+ {
+ SetDirty();
+ }
+ else
+ Win32Error(ret, null);
+ }
+
+ private RegistryValueKind CalculateValueKind(Object value)
+ {
+ // This logic matches what used to be in SetValue(string name, object value) in the v1.0 and v1.1 days.
+ // Even though we could add detection for an int64 in here, we want to maintain compatibility with the
+ // old behavior.
+ if (value is Int32)
+ return RegistryValueKind.DWord;
+ else if (value is Array)
+ {
+ if (value is byte[])
+ return RegistryValueKind.Binary;
+ else if (value is String[])
+ return RegistryValueKind.MultiString;
+ else
+ throw new ArgumentException(SR.Format(SR.Arg_RegSetBadArrType, value.GetType().Name));
+ }
+ else
+ return RegistryValueKind.String;
}
/**
@@ -591,7 +989,7 @@ namespace Microsoft.Win32
*
* @return a string representing the key.
*/
- public override string ToString()
+ public override String ToString()
{
EnsureNotDisposed();
return keyName;
@@ -604,7 +1002,7 @@ namespace Microsoft.Win32
* error, and depending on the error, insert a string into the message
* gotten from the ResourceManager.
*/
- internal void Win32Error(int errorCode, string str)
+ internal void Win32Error(int errorCode, String str)
{
switch (errorCode)
{
@@ -613,6 +1011,27 @@ namespace Microsoft.Win32
throw new UnauthorizedAccessException(SR.Format(SR.UnauthorizedAccess_RegistryKeyGeneric_Key, str));
else
throw new UnauthorizedAccessException();
+
+ case Win32Native.ERROR_INVALID_HANDLE:
+ /**
+ * For normal RegistryKey instances we dispose the SafeRegHandle and throw IOException.
+ * However, for HKEY_PERFORMANCE_DATA (on a local or remote machine) we avoid disposing the
+ * SafeRegHandle and only throw the IOException. This is to workaround reentrancy issues
+ * in PerformanceCounter.NextValue() where the API could throw {NullReference, ObjectDisposed, ArgumentNull}Exception
+ * on reentrant calls because of this error code path in RegistryKey
+ *
+ * Normally we'd make our caller synchronize access to a shared RegistryKey instead of doing something like this,
+ * however we shipped PerformanceCounter.NextValue() un-synchronized in v2.0RTM and customers have taken a dependency on
+ * this behavior (being able to simultaneously query multiple remote-machine counters on multiple threads, instead of
+ * having serialized access).
+ */
+ if (!IsPerfDataKey())
+ {
+ hkey.SetHandleAsInvalid();
+ hkey = null;
+ }
+ goto default;
+
case Win32Native.ERROR_FILE_NOT_FOUND:
throw new IOException(SR.Arg_RegKeyNotFound, errorCode);
@@ -621,6 +1040,76 @@ namespace Microsoft.Win32
}
}
+ internal static String FixupName(String name)
+ {
+ BCLDebug.Assert(name != null, "[FixupName]name!=null");
+ if (name.IndexOf('\\') == -1)
+ return name;
+
+ StringBuilder sb = new StringBuilder(name);
+ FixupPath(sb);
+ int temp = sb.Length - 1;
+ if (temp >= 0 && sb[temp] == '\\') // Remove trailing slash
+ sb.Length = temp;
+ return sb.ToString();
+ }
+
+
+ private static void FixupPath(StringBuilder path)
+ {
+ Contract.Requires(path != null);
+ int length = path.Length;
+ bool fixup = false;
+ char markerChar = (char)0xFFFF;
+
+ int i = 1;
+ while (i < length - 1)
+ {
+ if (path[i] == '\\')
+ {
+ i++;
+ while (i < length)
+ {
+ if (path[i] == '\\')
+ {
+ path[i] = markerChar;
+ i++;
+ fixup = true;
+ }
+ else
+ break;
+ }
+ }
+ i++;
+ }
+
+ if (fixup)
+ {
+ i = 0;
+ int j = 0;
+ while (i < length)
+ {
+ if (path[i] == markerChar)
+ {
+ i++;
+ continue;
+ }
+ path[j] = path[i];
+ i++;
+ j++;
+ }
+ path.Length += j - i;
+ }
+ }
+
+ private bool ContainsRegistryValue(string name)
+ {
+ int type = 0;
+ int datasize = 0;
+ int retval = Win32Native.RegQueryValueEx(hkey, name, null, ref type, (byte[])null, ref datasize);
+ return retval == 0;
+ }
+
private void EnsureNotDisposed()
{
if (hkey == null)
@@ -638,8 +1127,41 @@ namespace Microsoft.Win32
}
}
+ private static int GetRegistryKeyAccess(bool isWritable)
+ {
+ int winAccess;
+ if (!isWritable)
+ {
+ winAccess = Win32Native.KEY_READ;
+ }
+ else
+ {
+ winAccess = Win32Native.KEY_READ | Win32Native.KEY_WRITE;
+ }
+
+ return winAccess;
+ }
+
+ private RegistryKeyPermissionCheck GetSubKeyPermissonCheck(bool subkeyWritable)
+ {
+ if (checkMode == RegistryKeyPermissionCheck.Default)
+ {
+ return checkMode;
+ }
+
+ if (subkeyWritable)
+ {
+ return RegistryKeyPermissionCheck.ReadWriteSubTree;
+ }
+ else
+ {
+ return RegistryKeyPermissionCheck.ReadSubTree;
+ }
+ }
+
static private void ValidateKeyName(string name)
{
+ Contract.Ensures(name != null);
if (name == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.name);
@@ -660,9 +1182,33 @@ namespace Microsoft.Win32
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegKeyStrLenBug);
}
+ static private void ValidateKeyView(RegistryView view)
+ {
+ if (view != RegistryView.Default && view != RegistryView.Registry32 && view != RegistryView.Registry64)
+ {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidRegistryViewCheck, ExceptionArgument.view);
+ }
+ }
+
// Win32 constants for error handling
private const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
private const int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
private const int FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000;
}
+
+ [Flags]
+ internal enum RegistryValueOptions
+ {
+ None = 0,
+ DoNotExpandEnvironmentNames = 1
+ }
+
+ // the name for this API is meant to mimic FileMode, which has similar values
+
+ internal enum RegistryKeyPermissionCheck
+ {
+ Default = 0,
+ ReadSubTree = 1,
+ ReadWriteSubTree = 2
+ }
}
diff --git a/src/mscorlib/src/Microsoft/Win32/RegistryValueKind.cs b/src/mscorlib/src/Microsoft/Win32/RegistryValueKind.cs
index 90c880b278..6e2a4f3b6e 100644
--- a/src/mscorlib/src/Microsoft/Win32/RegistryValueKind.cs
+++ b/src/mscorlib/src/Microsoft/Win32/RegistryValueKind.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+
namespace Microsoft.Win32
{
internal enum RegistryValueKind
@@ -12,11 +13,8 @@ namespace Microsoft.Win32
DWord = Win32Native.REG_DWORD,
MultiString = Win32Native.REG_MULTI_SZ,
QWord = Win32Native.REG_QWORD,
- Unknown = 0,
-
- // REG_NONE is defined as zero but BCL, mistakingly overrode this value.
- // Now instead of using Win32Native.REG_NONE we use "-1" and play games internally.
- None = unchecked((int)0xFFFFFFFF),
- }
+ Unknown = 0, // REG_NONE is defined as zero but BCL
+ None = unchecked((int)0xFFFFFFFF), // mistakingly overrode this value.
+ } // Now instead of using Win32Native.REG_NONE we use "-1" and play games internally.
}
diff --git a/src/mscorlib/src/Microsoft/Win32/RegistryView.cs b/src/mscorlib/src/Microsoft/Win32/RegistryView.cs
new file mode 100644
index 0000000000..e4158656a8
--- /dev/null
+++ b/src/mscorlib/src/Microsoft/Win32/RegistryView.cs
@@ -0,0 +1,22 @@
+// 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.
+
+//
+//
+//
+// Implements Microsoft.Win32.RegistryView
+//
+// ======================================================================================
+
+using System;
+
+namespace Microsoft.Win32
+{
+ internal enum RegistryView
+ {
+ Default = 0, // 0x0000 operate on the default registry view
+ Registry64 = Win32Native.KEY_WOW64_64KEY, // 0x0100 operate on the 64-bit registry view
+ Registry32 = Win32Native.KEY_WOW64_32KEY, // 0x0200 operate on the 32-bit registry view
+ };
+}
diff --git a/src/mscorlib/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.cs b/src/mscorlib/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.cs
index 1215000ec5..64dbb2c638 100644
--- a/src/mscorlib/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.cs
+++ b/src/mscorlib/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.cs
@@ -2,9 +2,19 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+//
+//
+//
+// Implements Microsoft.Win32.SafeHandles.SafeRegistryHandle
+//
+// ======================================================================================
+
using System;
using System.Security;
using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+using System.Runtime.ConstrainedExecution;
+using System.Runtime.Versioning;
namespace Microsoft.Win32.SafeHandles
{
diff --git a/src/mscorlib/src/System/Collections/Generic/Dictionary.cs b/src/mscorlib/src/System/Collections/Generic/Dictionary.cs
index 409b23b541..e360eef897 100644
--- a/src/mscorlib/src/System/Collections/Generic/Dictionary.cs
+++ b/src/mscorlib/src/System/Collections/Generic/Dictionary.cs
@@ -48,6 +48,7 @@ namespace System.Collections.Generic
using System.Collections;
using System.Diagnostics;
using System.Diagnostics.Contracts;
+ using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
/// <summary>
@@ -428,8 +429,7 @@ namespace System.Collections.Generic
if (buckets == null) Initialize(0);
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
- int targetBucket = hashCode % buckets.Length;
-
+ int targetBucket = hashCode % buckets.Length;
#if FEATURE_RANDOMIZED_STRING_HASHING
int collisionCount = 0;
#endif
@@ -452,7 +452,6 @@ namespace System.Collections.Generic
return false;
}
-
#if FEATURE_RANDOMIZED_STRING_HASHING
collisionCount++;
#endif
@@ -599,27 +598,40 @@ namespace System.Collections.Generic
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
int bucket = hashCode % buckets.Length;
int last = -1;
- for (int i = buckets[bucket]; i >= 0; last = i, i = entries[i].next)
+ int i = buckets[bucket];
+ while (i >= 0)
{
- if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key))
+ ref Entry entry = ref entries[i];
+
+ if (entry.hashCode == hashCode && comparer.Equals(entry.key, key))
{
if (last < 0)
{
- buckets[bucket] = entries[i].next;
+ buckets[bucket] = entry.next;
}
else
{
- entries[last].next = entries[i].next;
+ entries[last].next = entry.next;
+ }
+ entry.hashCode = -1;
+ entry.next = freeList;
+
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<TKey>())
+ {
+ entry.key = default(TKey);
+ }
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<TValue>())
+ {
+ entry.value = default(TValue);
}
- entries[i].hashCode = -1;
- entries[i].next = freeList;
- entries[i].key = default(TKey);
- entries[i].value = default(TValue);
freeList = i;
freeCount++;
version++;
return true;
}
+
+ last = i;
+ i = entry.next;
}
}
return false;
@@ -640,30 +652,43 @@ namespace System.Collections.Generic
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
int bucket = hashCode % buckets.Length;
int last = -1;
- for (int i = buckets[bucket]; i >= 0; last = i, i = entries[i].next)
+ int i = buckets[bucket];
+ while (i >= 0)
{
- if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key))
+ ref Entry entry = ref entries[i];
+
+ if (entry.hashCode == hashCode && comparer.Equals(entry.key, key))
{
if (last < 0)
{
- buckets[bucket] = entries[i].next;
+ buckets[bucket] = entry.next;
}
else
{
- entries[last].next = entries[i].next;
+ entries[last].next = entry.next;
}
- value = entries[i].value;
+ value = entry.value;
- entries[i].hashCode = -1;
- entries[i].next = freeList;
- entries[i].key = default(TKey);
- entries[i].value = default(TValue);
+ entry.hashCode = -1;
+ entry.next = freeList;
+
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<TKey>())
+ {
+ entry.key = default(TKey);
+ }
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<TValue>())
+ {
+ entry.value = default(TValue);
+ }
freeList = i;
freeCount++;
version++;
return true;
}
+
+ last = i;
+ i = entry.next;
}
}
value = default(TValue);
@@ -955,13 +980,13 @@ namespace System.Collections.Generic
// dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue
while ((uint)index < (uint)dictionary.count)
{
- if (dictionary.entries[index].hashCode >= 0)
+ ref Entry entry = ref dictionary.entries[index++];
+
+ if (entry.hashCode >= 0)
{
- current = new KeyValuePair<TKey, TValue>(dictionary.entries[index].key, dictionary.entries[index].value);
- index++;
+ current = new KeyValuePair<TKey, TValue>(entry.key, entry.value);
return true;
}
- index++;
}
index = dictionary.count + 1;
@@ -1231,13 +1256,13 @@ namespace System.Collections.Generic
while ((uint)index < (uint)dictionary.count)
{
- if (dictionary.entries[index].hashCode >= 0)
+ ref Entry entry = ref dictionary.entries[index++];
+
+ if (entry.hashCode >= 0)
{
- currentKey = dictionary.entries[index].key;
- index++;
+ currentKey = entry.key;
return true;
}
- index++;
}
index = dictionary.count + 1;
@@ -1459,13 +1484,13 @@ namespace System.Collections.Generic
while ((uint)index < (uint)dictionary.count)
{
- if (dictionary.entries[index].hashCode >= 0)
+ ref Entry entry = ref dictionary.entries[index++];
+
+ if (entry.hashCode >= 0)
{
- currentValue = dictionary.entries[index].value;
- index++;
+ currentValue = entry.value;
return true;
}
- index++;
}
index = dictionary.count + 1;
currentValue = default(TValue);
diff --git a/src/mscorlib/src/System/Collections/Generic/List.cs b/src/mscorlib/src/System/Collections/Generic/List.cs
index 67d1668aad..4e480885ef 100644
--- a/src/mscorlib/src/System/Collections/Generic/List.cs
+++ b/src/mscorlib/src/System/Collections/Generic/List.cs
@@ -350,6 +350,7 @@ namespace System.Collections.Generic
// Clears the contents of List.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
diff --git a/src/mscorlib/src/System/Delegate.cs b/src/mscorlib/src/System/Delegate.cs
index de0ff6532c..75ec57a003 100644
--- a/src/mscorlib/src/System/Delegate.cs
+++ b/src/mscorlib/src/System/Delegate.cs
@@ -179,7 +179,10 @@ namespace System
else
return unchecked((int)((long)this._methodPtrAux));
*/
- return GetType().GetHashCode();
+ if (_methodPtrAux.IsNull())
+ return ( _target != null ? RuntimeHelpers.GetHashCode(_target) * 33 : 0) + GetType().GetHashCode();
+ else
+ return GetType().GetHashCode();
}
public static Delegate Combine(Delegate a, Delegate b)
diff --git a/src/mscorlib/src/System/Environment.cs b/src/mscorlib/src/System/Environment.cs
index b9070ae713..dddbdc242d 100644
--- a/src/mscorlib/src/System/Environment.cs
+++ b/src/mscorlib/src/System/Environment.cs
@@ -820,7 +820,7 @@ namespace System
}
else
{
- environmentKey.SetStringValue(variable, value);
+ environmentKey.SetValue(variable, value);
}
}
}
diff --git a/src/mscorlib/src/System/Globalization/CompareInfo.cs b/src/mscorlib/src/System/Globalization/CompareInfo.cs
index 285a81d906..b2c2208db7 100644
--- a/src/mscorlib/src/System/Globalization/CompareInfo.cs
+++ b/src/mscorlib/src/System/Globalization/CompareInfo.cs
@@ -752,7 +752,6 @@ namespace System.Globalization
return IndexOfCore(source, new string(value, 1), startIndex, count, options, null);
}
-
public unsafe virtual int IndexOf(string source, string value, int startIndex, int count, CompareOptions options)
{
// Validate inputs
@@ -802,6 +801,53 @@ namespace System.Globalization
return IndexOfCore(source, value, startIndex, count, options, null);
}
+ // The following IndexOf overload is mainly used by String.Replace. This overload assumes the parameters are already validated
+ // and the caller is passing a valid matchLengthPtr pointer.
+ internal unsafe int IndexOf(string source, string value, int startIndex, int count, CompareOptions options, int* matchLengthPtr)
+ {
+ Debug.Assert(source != null);
+ Debug.Assert(value != null);
+ Debug.Assert(startIndex >= 0);
+ Debug.Assert(matchLengthPtr != null);
+ *matchLengthPtr = 0;
+
+ if (source.Length == 0)
+ {
+ if (value.Length == 0)
+ {
+ return 0;
+ }
+ return -1;
+ }
+
+ if (startIndex >= source.Length)
+ {
+ return -1;
+ }
+
+ if (options == CompareOptions.OrdinalIgnoreCase)
+ {
+ int res = IndexOfOrdinal(source, value, startIndex, count, ignoreCase: true);
+ if (res >= 0)
+ {
+ *matchLengthPtr = value.Length;
+ }
+ return res;
+ }
+
+ if (_invariantMode)
+ {
+ int res = IndexOfOrdinal(source, value, startIndex, count, ignoreCase: (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0);
+ if (res >= 0)
+ {
+ *matchLengthPtr = value.Length;
+ }
+ return res;
+ }
+
+ return IndexOfCore(source, value, startIndex, count, options, matchLengthPtr);
+ }
+
internal int IndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase)
{
if (_invariantMode)
diff --git a/src/mscorlib/src/System/Globalization/HijriCalendar.Win32.cs b/src/mscorlib/src/System/Globalization/HijriCalendar.Win32.cs
index 4ba95c8fa8..869b809bff 100644
--- a/src/mscorlib/src/System/Globalization/HijriCalendar.Win32.cs
+++ b/src/mscorlib/src/System/Globalization/HijriCalendar.Win32.cs
@@ -42,41 +42,54 @@ namespace System.Globalization
int hijriAdvance = 0;
Microsoft.Win32.RegistryKey key = null;
- using (key = Registry.CurrentUser.OpenSubKey(InternationalRegKey, writable: false))
+ try
{
- if (key == null)
- return 0;
+ // Open in read-only mode.
+ // Use InternalOpenSubKey so that we avoid the security check.
+ key = RegistryKey.GetBaseKey(RegistryKey.HKEY_CURRENT_USER).OpenSubKey(InternationalRegKey, false);
+ }
+ //If this fails for any reason, we'll just return 0.
+ catch (ObjectDisposedException) { return 0; }
+ catch (ArgumentException) { return 0; }
- Object value = key.GetValue(HijriAdvanceRegKeyEntry);
- if (value == null)
- {
- return (0);
- }
- String str = value.ToString();
- if (String.Compare(str, 0, HijriAdvanceRegKeyEntry, 0, HijriAdvanceRegKeyEntry.Length, StringComparison.OrdinalIgnoreCase) == 0)
+ if (key != null)
+ {
+ try
{
- if (str.Length == HijriAdvanceRegKeyEntry.Length)
- hijriAdvance = -1;
- else
+ Object value = key.InternalGetValue(HijriAdvanceRegKeyEntry, null, false, false);
+ if (value == null)
{
- str = str.Substring(HijriAdvanceRegKeyEntry.Length);
- try
+ return (0);
+ }
+ String str = value.ToString();
+ if (String.Compare(str, 0, HijriAdvanceRegKeyEntry, 0, HijriAdvanceRegKeyEntry.Length, StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ if (str.Length == HijriAdvanceRegKeyEntry.Length)
+ hijriAdvance = -1;
+ else
{
- int advance = Int32.Parse(str.ToString(), CultureInfo.InvariantCulture);
- if ((advance >= MinAdvancedHijri) && (advance <= MaxAdvancedHijri))
+ str = str.Substring(HijriAdvanceRegKeyEntry.Length);
+ try
{
- hijriAdvance = advance;
+ int advance = Int32.Parse(str.ToString(), CultureInfo.InvariantCulture);
+ if ((advance >= MinAdvancedHijri) && (advance <= MaxAdvancedHijri))
+ {
+ hijriAdvance = advance;
+ }
}
+ // If we got garbage from registry just ignore it.
+ // hijriAdvance = 0 because of declaraction assignment up above.
+ catch (ArgumentException) { }
+ catch (FormatException) { }
+ catch (OverflowException) { }
}
- // If we got garbage from registry just ignore it.
- // hijriAdvance = 0 because of declaraction assignment up above.
- catch (ArgumentException) { }
- catch (FormatException) { }
- catch (OverflowException) { }
}
}
+ finally
+ {
+ key.Close();
+ }
}
-
return (hijriAdvance);
}
}
diff --git a/src/mscorlib/src/System/Globalization/JapaneseCalendar.Win32.cs b/src/mscorlib/src/System/Globalization/JapaneseCalendar.Win32.cs
index fe8b1b5896..a83c4fad9e 100644
--- a/src/mscorlib/src/System/Globalization/JapaneseCalendar.Win32.cs
+++ b/src/mscorlib/src/System/Globalization/JapaneseCalendar.Win32.cs
@@ -36,30 +36,30 @@ namespace System.Globalization
try
{
- using (RegistryKey key = Registry.LocalMachine.OpenSubKey(c_japaneseErasHive, writable: false))
+ // Need to access registry
+ RegistryKey key = RegistryKey.GetBaseKey(RegistryKey.HKEY_LOCAL_MACHINE).OpenSubKey(c_japaneseErasHive, false);
+
+ // Abort if we didn't find anything
+ if (key == null) return null;
+
+ // Look up the values in our reg key
+ String[] valueNames = key.GetValueNames();
+ if (valueNames != null && valueNames.Length > 0)
{
- // Abort if we didn't find anything
- if (key == null) return null;
+ registryEraRanges = new EraInfo[valueNames.Length];
- // Look up the values in our reg key
- String[] valueNames = key.GetValueNames();
- if (valueNames != null && valueNames.Length > 0)
+ // Loop through the registry and read in all the values
+ for (int i = 0; i < valueNames.Length; i++)
{
- registryEraRanges = new EraInfo[valueNames.Length];
-
- // Loop through the registry and read in all the values
- for (int i = 0; i < valueNames.Length; i++)
- {
- // See if the era is a valid date
- EraInfo era = GetEraFromValue(valueNames[i], key.GetValue(valueNames[i]).ToString());
+ // See if the era is a valid date
+ EraInfo era = GetEraFromValue(valueNames[i], key.GetValue(valueNames[i]).ToString());
- // continue if not valid
- if (era == null) continue;
+ // continue if not valid
+ if (era == null) continue;
- // Remember we found one.
- registryEraRanges[iFoundEras] = era;
- iFoundEras++;
- }
+ // Remember we found one.
+ registryEraRanges[iFoundEras] = era;
+ iFoundEras++;
}
}
}
diff --git a/src/mscorlib/src/System/IO/Stream.cs b/src/mscorlib/src/System/IO/Stream.cs
index 92fe374f19..65cc4ddcb9 100644
--- a/src/mscorlib/src/System/IO/Stream.cs
+++ b/src/mscorlib/src/System/IO/Stream.cs
@@ -522,14 +522,14 @@ namespace System.IO
// If the wait has already completed, run the task.
if (asyncWaiter.IsCompleted)
{
- Debug.Assert(asyncWaiter.IsRanToCompletion, "The semaphore wait should always complete successfully.");
+ Debug.Assert(asyncWaiter.IsCompletedSuccessfully, "The semaphore wait should always complete successfully.");
RunReadWriteTask(readWriteTask);
}
else // Otherwise, wait for our turn, and then run the task.
{
asyncWaiter.ContinueWith((t, state) =>
{
- Debug.Assert(t.IsRanToCompletion, "The semaphore wait should always complete successfully.");
+ Debug.Assert(t.IsCompletedSuccessfully, "The semaphore wait should always complete successfully.");
var rwt = (ReadWriteTask)state;
rwt._stream.RunReadWriteTask(rwt); // RunReadWriteTask(readWriteTask);
}, readWriteTask, default(CancellationToken), TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
diff --git a/src/mscorlib/src/System/Reflection/Assembly.CoreCLR.cs b/src/mscorlib/src/System/Reflection/Assembly.CoreCLR.cs
index 82966dba60..708f79b64f 100644
--- a/src/mscorlib/src/System/Reflection/Assembly.CoreCLR.cs
+++ b/src/mscorlib/src/System/Reflection/Assembly.CoreCLR.cs
@@ -101,14 +101,20 @@ namespace System.Reflection
{
Contract.Ensures(Contract.Result<Assembly>() != null);
Contract.Ensures(!Contract.Result<Assembly>().ReflectionOnly);
-
+
+ AssemblyName modifiedAssemblyRef = null;
if (assemblyRef != null && assemblyRef.CodeBase != null)
{
- throw new NotSupportedException(SR.NotSupported_AssemblyLoadCodeBase);
+ modifiedAssemblyRef = (AssemblyName)assemblyRef.Clone();
+ modifiedAssemblyRef.CodeBase = null;
}
-
+ else
+ {
+ modifiedAssemblyRef = assemblyRef;
+ }
+
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
- return RuntimeAssembly.InternalLoadAssemblyName(assemblyRef, null, null, ref stackMark, true /*thrownOnFileNotFound*/, false /*forIntrospection*/);
+ return RuntimeAssembly.InternalLoadAssemblyName(modifiedAssemblyRef, null, null, ref stackMark, true /*thrownOnFileNotFound*/, false /*forIntrospection*/);
}
// Locate an assembly by its name. The name can be strong or
@@ -119,13 +125,19 @@ namespace System.Reflection
Contract.Ensures(Contract.Result<Assembly>() != null);
Contract.Ensures(!Contract.Result<Assembly>().ReflectionOnly);
+ AssemblyName modifiedAssemblyRef = null;
if (assemblyRef != null && assemblyRef.CodeBase != null)
{
- throw new NotSupportedException(SR.NotSupported_AssemblyLoadCodeBase);
+ modifiedAssemblyRef = (AssemblyName)assemblyRef.Clone();
+ modifiedAssemblyRef.CodeBase = null;
+ }
+ else
+ {
+ modifiedAssemblyRef = assemblyRef;
}
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
- return RuntimeAssembly.InternalLoadAssemblyName(assemblyRef, null, null, ref stackMark, true /*thrownOnFileNotFound*/, false /*forIntrospection*/, ptrLoadContextBinder);
+ return RuntimeAssembly.InternalLoadAssemblyName(modifiedAssemblyRef, null, null, ref stackMark, true /*thrownOnFileNotFound*/, false /*forIntrospection*/, ptrLoadContextBinder);
}
// Loads the assembly with a COFF based IMAGE containing
diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/TaskAwaiter.cs b/src/mscorlib/src/System/Runtime/CompilerServices/TaskAwaiter.cs
index e2fa6caa2d..c35658e54c 100644
--- a/src/mscorlib/src/System/Runtime/CompilerServices/TaskAwaiter.cs
+++ b/src/mscorlib/src/System/Runtime/CompilerServices/TaskAwaiter.cs
@@ -146,7 +146,7 @@ namespace System.Runtime.CompilerServices
task.NotifyDebuggerOfWaitCompletionIfNecessary();
// And throw an exception if the task is faulted or canceled.
- if (!task.IsRanToCompletion) ThrowForNonSuccess(task);
+ if (!task.IsCompletedSuccessfully) ThrowForNonSuccess(task);
}
/// <summary>Throws an exception to handle a task that completed in a state other than RanToCompletion.</summary>
diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/jithelpers.cs b/src/mscorlib/src/System/Runtime/CompilerServices/jithelpers.cs
index b86835f778..d5201350ec 100644
--- a/src/mscorlib/src/System/Runtime/CompilerServices/jithelpers.cs
+++ b/src/mscorlib/src/System/Runtime/CompilerServices/jithelpers.cs
@@ -216,14 +216,7 @@ namespace System.Runtime.CompilerServices
private extern static bool IsAddressInStack(IntPtr ptr);
#endif
- static internal bool ByRefLessThan<T>(ref T refA, ref T refB)
- {
- // The body of this function will be replaced by the EE with unsafe code!!!
- // See getILIntrinsicImplementation for how this happens.
- throw new InvalidOperationException();
- }
-
- static internal ref T GetArrayData<T>(T[] array)
+ static internal ref byte GetRawSzArrayData(this Array array)
{
// The body of this function will be replaced by the EE with unsafe code!!!
// See getILIntrinsicImplementation for how this happens.
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/Marshal.cs b/src/mscorlib/src/System/Runtime/InteropServices/Marshal.cs
index 248e0d5778..6fb631121b 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/Marshal.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/Marshal.cs
@@ -824,6 +824,9 @@ namespace System.Runtime.InteropServices
}
[MethodImplAttribute(MethodImplOptions.InternalCall)]
+ public static extern /* struct _EXCEPTION_POINTERS* */ IntPtr GetExceptionPointers();
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
public static extern int GetExceptionCode();
diff --git a/src/mscorlib/src/System/String.Manipulation.cs b/src/mscorlib/src/System/String.Manipulation.cs
index 33e341c58e..b71ffa2f63 100644
--- a/src/mscorlib/src/System/String.Manipulation.cs
+++ b/src/mscorlib/src/System/String.Manipulation.cs
@@ -1022,8 +1022,10 @@ namespace System
{
if (oldValue == null)
throw new ArgumentNullException(nameof(oldValue));
+ if (oldValue.Length == 0)
+ throw new ArgumentException(SR.Argument_StringZeroLength, nameof(oldValue));
- // If they asked to replace oldValue with a null, replace all occurences
+ // If they asked to replace oldValue with a null, replace all occurrences
// with the empty string.
if (newValue == null)
newValue = string.Empty;
@@ -1037,10 +1039,11 @@ namespace System
int matchLength = 0;
bool hasDoneAnyReplacements = false;
+ CompareInfo ci = referenceCulture.CompareInfo;
do
{
- index = referenceCulture.CompareInfo.IndexOfCore(this, oldValue, startIndex, m_stringLength - startIndex, options, &matchLength);
+ index = ci.IndexOf(this, oldValue, startIndex, m_stringLength - startIndex, options, &matchLength);
if (index >= 0)
{
// append the unmodified portion of string
diff --git a/src/mscorlib/src/System/String.cs b/src/mscorlib/src/System/String.cs
index f3a4d0f197..0a17c1ee24 100644
--- a/src/mscorlib/src/System/String.cs
+++ b/src/mscorlib/src/System/String.cs
@@ -876,7 +876,7 @@ namespace System
}
}
- internal ref char GetFirstCharRef()
+ internal ref char GetRawStringData()
{
return ref m_firstChar;
}
diff --git a/src/mscorlib/src/System/Threading/Tasks/Task.cs b/src/mscorlib/src/System/Threading/Tasks/Task.cs
index 8e2e6a4cb0..0a9248cba8 100644
--- a/src/mscorlib/src/System/Threading/Tasks/Task.cs
+++ b/src/mscorlib/src/System/Threading/Tasks/Task.cs
@@ -1467,8 +1467,7 @@ namespace System.Threading.Tasks
return (flags & TASK_STATE_COMPLETED_MASK) != 0;
}
- // For use in InternalWait -- marginally faster than (Task.Status == TaskStatus.RanToCompletion)
- internal bool IsRanToCompletion
+ public bool IsCompletedSuccessfully
{
get { return (m_stateFlags & TASK_STATE_COMPLETED_MASK) == TASK_STATE_RAN_TO_COMPLETION; }
}
diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs b/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs
index 848a0ecbc2..de222352c9 100644
--- a/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs
+++ b/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs
@@ -320,7 +320,7 @@ namespace System.Threading.Tasks
// activation criteria of the TaskContinuationOptions.
TaskContinuationOptions options = m_options;
bool isRightKind =
- completedTask.IsRanToCompletion ?
+ completedTask.IsCompletedSuccessfully ?
(options & TaskContinuationOptions.NotOnRanToCompletion) == 0 :
(completedTask.IsCanceled ?
(options & TaskContinuationOptions.NotOnCanceled) == 0 :
diff --git a/src/mscorlib/src/System/Threading/Tasks/future.cs b/src/mscorlib/src/System/Threading/Tasks/future.cs
index 26c2388e6b..bf9000ea00 100644
--- a/src/mscorlib/src/System/Threading/Tasks/future.cs
+++ b/src/mscorlib/src/System/Threading/Tasks/future.cs
@@ -374,7 +374,7 @@ namespace System.Threading.Tasks
{
get
{
- return IsRanToCompletion ? "" + m_result : SR.TaskT_DebuggerNoResult;
+ return IsCompletedSuccessfully ? "" + m_result : SR.TaskT_DebuggerNoResult;
}
}
@@ -491,10 +491,10 @@ namespace System.Threading.Tasks
if (waitCompletionNotification) NotifyDebuggerOfWaitCompletionIfNecessary();
// Throw an exception if appropriate.
- if (!IsRanToCompletion) ThrowIfExceptional(includeTaskCanceledExceptions: true);
+ if (!IsCompletedSuccessfully) ThrowIfExceptional(includeTaskCanceledExceptions: true);
// We shouldn't be here if the result has not been set.
- Debug.Assert(IsRanToCompletion, "Task<T>.Result getter: Expected result to have been set.");
+ Debug.Assert(IsCompletedSuccessfully, "Task<T>.Result getter: Expected result to have been set.");
return m_result;
}
diff --git a/src/mscorlib/src/System/ThrowHelper.cs b/src/mscorlib/src/System/ThrowHelper.cs
index 4dcf8d4511..ff76738e8e 100644
--- a/src/mscorlib/src/System/ThrowHelper.cs
+++ b/src/mscorlib/src/System/ThrowHelper.cs
@@ -416,6 +416,7 @@ namespace System
type,
stateMachine,
pHandle,
+ values
}
//
diff --git a/src/mscorlib/src/System/TimeZoneInfo.Win32.cs b/src/mscorlib/src/System/TimeZoneInfo.Win32.cs
index b6585bd0a0..4f740bd355 100644
--- a/src/mscorlib/src/System/TimeZoneInfo.Win32.cs
+++ b/src/mscorlib/src/System/TimeZoneInfo.Win32.cs
@@ -398,7 +398,7 @@ namespace System
baseOffset = baseOffset + match.Rule.BaseUtcOffsetDelta;
if (match.Rule.HasDaylightSaving)
{
- isDaylightSavings = GetIsDaylightSavingsFromUtc(time, timeYear, match.Offset, match.Rule, out isAmbiguousLocalDst, Local);
+ isDaylightSavings = GetIsDaylightSavingsFromUtc(time, timeYear, match.Offset, match.Rule, null, out isAmbiguousLocalDst, Local);
baseOffset += (isDaylightSavings ? match.Rule.DaylightDelta : TimeSpan.Zero /* FUTURE: rule.StandardDelta */);
}
}
@@ -576,8 +576,8 @@ namespace System
// read LastEntry {(yearN, 1, 1) - MaxValue }
// read the FirstEntry and LastEntry key values (ex: "1980", "2038")
- int first = (int)dynamicKey.GetValue(FirstEntryValue, -1);
- int last = (int)dynamicKey.GetValue(LastEntryValue, -1);
+ int first = (int)dynamicKey.GetValue(FirstEntryValue, -1, RegistryValueOptions.None);
+ int last = (int)dynamicKey.GetValue(LastEntryValue, -1, RegistryValueOptions.None);
if (first == -1 || last == -1 || first > last)
{
@@ -587,7 +587,7 @@ namespace System
// read the first year entry
Win32Native.RegistryTimeZoneInformation dtzi;
- byte[] regValue = dynamicKey.GetValue(first.ToString(CultureInfo.InvariantCulture)) as byte[];
+ byte[] regValue = dynamicKey.GetValue(first.ToString(CultureInfo.InvariantCulture), null, RegistryValueOptions.None) as byte[];
if (regValue == null || regValue.Length != RegByteLength)
{
rules = null;
@@ -620,7 +620,7 @@ namespace System
// read the middle year entries
for (int i = first + 1; i < last; i++)
{
- regValue = dynamicKey.GetValue(i.ToString(CultureInfo.InvariantCulture)) as byte[];
+ regValue = dynamicKey.GetValue(i.ToString(CultureInfo.InvariantCulture), null, RegistryValueOptions.None) as byte[];
if (regValue == null || regValue.Length != RegByteLength)
{
rules = null;
@@ -640,7 +640,7 @@ namespace System
}
// read the last year entry
- regValue = dynamicKey.GetValue(last.ToString(CultureInfo.InvariantCulture)) as byte[];
+ regValue = dynamicKey.GetValue(last.ToString(CultureInfo.InvariantCulture), null, RegistryValueOptions.None) as byte[];
dtzi = new Win32Native.RegistryTimeZoneInformation(regValue);
if (regValue == null || regValue.Length != RegByteLength)
{
@@ -719,7 +719,7 @@ namespace System
}
Win32Native.RegistryTimeZoneInformation registryTimeZoneInfo;
- byte[] regValue = key.GetValue(TimeZoneInfoValue) as byte[];
+ byte[] regValue = key.GetValue(TimeZoneInfoValue, null, RegistryValueOptions.None) as byte[];
if (regValue == null || regValue.Length != RegByteLength) return false;
registryTimeZoneInfo = new Win32Native.RegistryTimeZoneInformation(regValue);
@@ -756,7 +756,7 @@ namespace System
//
if (result)
{
- string registryStandardName = key.GetValue(StandardValue, string.Empty) as string;
+ string registryStandardName = key.GetValue(StandardValue, string.Empty, RegistryValueOptions.None) as string;
result = string.Equals(registryStandardName, timeZone.StandardName, StringComparison.Ordinal);
}
return result;
@@ -884,9 +884,9 @@ namespace System
daylightName = string.Empty;
// read the MUI_ registry keys
- string displayNameMuiResource = key.GetValue(MuiDisplayValue, string.Empty) as string;
- string standardNameMuiResource = key.GetValue(MuiStandardValue, string.Empty) as string;
- string daylightNameMuiResource = key.GetValue(MuiDaylightValue, string.Empty) as string;
+ string displayNameMuiResource = key.GetValue(MuiDisplayValue, string.Empty, RegistryValueOptions.None) as string;
+ string standardNameMuiResource = key.GetValue(MuiStandardValue, string.Empty, RegistryValueOptions.None) as string;
+ string daylightNameMuiResource = key.GetValue(MuiDaylightValue, string.Empty, RegistryValueOptions.None) as string;
// try to load the strings from the native resource DLL(s)
if (!string.IsNullOrEmpty(displayNameMuiResource))
@@ -907,15 +907,15 @@ namespace System
// fallback to using the standard registry keys
if (string.IsNullOrEmpty(displayName))
{
- displayName = key.GetValue(DisplayValue, string.Empty) as string;
+ displayName = key.GetValue(DisplayValue, string.Empty, RegistryValueOptions.None) as string;
}
if (string.IsNullOrEmpty(standardName))
{
- standardName = key.GetValue(StandardValue, string.Empty) as string;
+ standardName = key.GetValue(StandardValue, string.Empty, RegistryValueOptions.None) as string;
}
if (string.IsNullOrEmpty(daylightName))
{
- daylightName = key.GetValue(DaylightValue, string.Empty) as string;
+ daylightName = key.GetValue(DaylightValue, string.Empty, RegistryValueOptions.None) as string;
}
return true;
@@ -964,7 +964,7 @@ namespace System
}
Win32Native.RegistryTimeZoneInformation defaultTimeZoneInformation;
- byte[] regValue = key.GetValue(TimeZoneInfoValue) as byte[];
+ byte[] regValue = key.GetValue(TimeZoneInfoValue, null, RegistryValueOptions.None) as byte[];
if (regValue == null || regValue.Length != RegByteLength)
{
// the registry value could not be cast to a byte array
diff --git a/src/mscorlib/src/System/TimeZoneInfo.cs b/src/mscorlib/src/System/TimeZoneInfo.cs
index 29ea33a8ad..8ed7e229c0 100644
--- a/src/mscorlib/src/System/TimeZoneInfo.cs
+++ b/src/mscorlib/src/System/TimeZoneInfo.cs
@@ -172,10 +172,11 @@ namespace System
DateTime adjustedTime = ConvertTime(dateTimeOffset, this).DateTime;
bool isAmbiguous = false;
- AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets(adjustedTime);
+ int? ruleIndex;
+ AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets(adjustedTime, out ruleIndex);
if (rule != null && rule.HasDaylightSaving)
{
- DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule);
+ DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule, ruleIndex);
isAmbiguous = GetIsAmbiguousTime(adjustedTime, rule, daylightTime);
}
@@ -232,10 +233,11 @@ namespace System
}
bool isAmbiguous = false;
- AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets(adjustedTime);
+ int? ruleIndex;
+ AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets(adjustedTime, out ruleIndex);
if (rule != null && rule.HasDaylightSaving)
{
- DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule);
+ DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule, ruleIndex);
isAmbiguous = GetIsAmbiguousTime(adjustedTime, rule, daylightTime);
}
@@ -263,15 +265,15 @@ namespace System
}
// note the time is already adjusted
- private AdjustmentRule GetAdjustmentRuleForAmbiguousOffsets(DateTime adjustedTime)
+ private AdjustmentRule GetAdjustmentRuleForAmbiguousOffsets(DateTime adjustedTime, out int? ruleIndex)
{
- AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime);
+ AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime, out ruleIndex);
if (rule != null && rule.NoDaylightTransitions && !rule.HasDaylightSaving)
{
// When using NoDaylightTransitions rules, each rule is only for one offset.
// When looking for the Daylight savings rules, and we found the non-DST rule,
// then we get the rule right before this rule.
- return GetPreviousAdjustmentRule(rule);
+ return GetPreviousAdjustmentRule(rule, ruleIndex);
}
return rule;
@@ -282,12 +284,23 @@ namespace System
/// If the specified rule is the first AdjustmentRule, or it isn't in _adjustmentRules,
/// then the specified rule is returned.
/// </summary>
- private AdjustmentRule GetPreviousAdjustmentRule(AdjustmentRule rule)
+ private AdjustmentRule GetPreviousAdjustmentRule(AdjustmentRule rule, int? ruleIndex)
{
+ Debug.Assert(rule.NoDaylightTransitions, "GetPreviousAdjustmentRule should only be used with NoDaylightTransitions rules.");
+
+ if (ruleIndex.HasValue && 0 < ruleIndex.Value && ruleIndex.Value < _adjustmentRules.Length)
+ {
+ return _adjustmentRules[ruleIndex.Value - 1];
+ }
+
AdjustmentRule result = rule;
for (int i = 1; i < _adjustmentRules.Length; i++)
{
- if (rule.Equals(_adjustmentRules[i]))
+ // use ReferenceEquals here instead of AdjustmentRule.Equals because
+ // ReferenceEquals is much faster. This is safe because all the callers
+ // of GetPreviousAdjustmentRule pass in a rule that was retrieved from
+ // _adjustmentRules. A different approach will be needed if this ever changes.
+ if (ReferenceEquals(rule, _adjustmentRules[i]))
{
result = _adjustmentRules[i - 1];
break;
@@ -407,10 +420,11 @@ namespace System
dateTime.Kind == DateTimeKind.Utc ? ConvertTime(dateTime, s_utcTimeZone, this, flags, cachedData) :
dateTime;
- AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime);
+ int? ruleIndex;
+ AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime, out ruleIndex);
if (rule != null && rule.HasDaylightSaving)
{
- DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule);
+ DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule, ruleIndex);
return GetIsAmbiguousTime(adjustedTime, rule, daylightTime);
}
return false;
@@ -492,10 +506,11 @@ namespace System
//
// handle the normal cases...
//
- AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime);
+ int? ruleIndex;
+ AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime, out ruleIndex);
if (rule != null && rule.HasDaylightSaving)
{
- DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule);
+ DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule, ruleIndex);
return GetIsDaylightSavings(adjustedTime, rule, daylightTime, flags);
}
else
@@ -515,11 +530,12 @@ namespace System
(dateTime.Kind == DateTimeKind.Local && s_cachedData.GetCorrespondingKind(this) == DateTimeKind.Local))
{
// only check Unspecified and (Local when this TimeZoneInfo instance is Local)
- AdjustmentRule rule = GetAdjustmentRuleForTime(dateTime);
+ int? ruleIndex;
+ AdjustmentRule rule = GetAdjustmentRuleForTime(dateTime, out ruleIndex);
if (rule != null && rule.HasDaylightSaving)
{
- DaylightTimeStruct daylightTime = GetDaylightTime(dateTime.Year, rule);
+ DaylightTimeStruct daylightTime = GetDaylightTime(dateTime.Year, rule, ruleIndex);
isInvalid = GetIsInvalidTime(dateTime, rule, daylightTime);
}
else
@@ -661,7 +677,8 @@ namespace System
// performance for the normal case at the expense of the 'ArgumentException'
// case and Loss-less Local special cases.
//
- AdjustmentRule sourceRule = sourceTimeZone.GetAdjustmentRuleForTime(dateTime);
+ int? sourceRuleIndex;
+ AdjustmentRule sourceRule = sourceTimeZone.GetAdjustmentRuleForTime(dateTime, out sourceRuleIndex);
TimeSpan sourceOffset = sourceTimeZone.BaseUtcOffset;
if (sourceRule != null)
@@ -670,7 +687,7 @@ namespace System
if (sourceRule.HasDaylightSaving)
{
bool sourceIsDaylightSavings = false;
- DaylightTimeStruct sourceDaylightTime = sourceTimeZone.GetDaylightTime(dateTime.Year, sourceRule);
+ DaylightTimeStruct sourceDaylightTime = sourceTimeZone.GetDaylightTime(dateTime.Year, sourceRule, sourceRuleIndex);
// 'dateTime' might be in an invalid time range since it is in an AdjustmentRule
// period that supports DST
@@ -1048,10 +1065,19 @@ namespace System
_supportsDaylightSavingTime = (bool)info.GetValue("SupportsDaylightSavingTime", typeof(bool));
}
- private AdjustmentRule GetAdjustmentRuleForTime(DateTime dateTime, bool dateTimeisUtc = false)
+ private AdjustmentRule GetAdjustmentRuleForTime(DateTime dateTime, out int? ruleIndex)
+ {
+ AdjustmentRule result = GetAdjustmentRuleForTime(dateTime, dateTimeisUtc: false, ruleIndex: out ruleIndex);
+ Debug.Assert(result == null || ruleIndex.HasValue, "If an AdjustmentRule was found, ruleIndex should also be set.");
+
+ return result;
+ }
+
+ private AdjustmentRule GetAdjustmentRuleForTime(DateTime dateTime, bool dateTimeisUtc, out int? ruleIndex)
{
if (_adjustmentRules == null || _adjustmentRules.Length == 0)
{
+ ruleIndex = null;
return null;
}
@@ -1076,6 +1102,7 @@ namespace System
int compareResult = CompareAdjustmentRuleToDateTime(rule, previousRule, dateTime, date, dateTimeisUtc);
if (compareResult == 0)
{
+ ruleIndex = median;
return rule;
}
else if (compareResult < 0)
@@ -1088,6 +1115,7 @@ namespace System
}
}
+ ruleIndex = null;
return null;
}
@@ -1199,7 +1227,7 @@ namespace System
/// <summary>
/// Helper function that returns a DaylightTime from a year and AdjustmentRule.
/// </summary>
- private DaylightTimeStruct GetDaylightTime(int year, AdjustmentRule rule)
+ private DaylightTimeStruct GetDaylightTime(int year, AdjustmentRule rule, int? ruleIndex)
{
TimeSpan delta = rule.DaylightDelta;
DateTime startTime;
@@ -1211,7 +1239,7 @@ namespace System
// Convert the UTC times into adjusted time zone times.
// use the previous rule to calculate the startTime, since the DST change happens w.r.t. the previous rule
- AdjustmentRule previousRule = GetPreviousAdjustmentRule(rule);
+ AdjustmentRule previousRule = GetPreviousAdjustmentRule(rule, ruleIndex);
startTime = ConvertFromUtc(rule.DateStart, previousRule.DaylightDelta, previousRule.BaseUtcOffsetDelta);
endTime = ConvertFromUtc(rule.DateEnd, rule.DaylightDelta, rule.BaseUtcOffsetDelta);
@@ -1301,12 +1329,12 @@ namespace System
/// <summary>
/// Gets the offset that should be used to calculate DST start times from a UTC time.
/// </summary>
- private TimeSpan GetDaylightSavingsStartOffsetFromUtc(TimeSpan baseUtcOffset, AdjustmentRule rule)
+ private TimeSpan GetDaylightSavingsStartOffsetFromUtc(TimeSpan baseUtcOffset, AdjustmentRule rule, int? ruleIndex)
{
if (rule.NoDaylightTransitions)
{
// use the previous rule to calculate the startTime, since the DST change happens w.r.t. the previous rule
- AdjustmentRule previousRule = GetPreviousAdjustmentRule(rule);
+ AdjustmentRule previousRule = GetPreviousAdjustmentRule(rule, ruleIndex);
return baseUtcOffset + previousRule.BaseUtcOffsetDelta + previousRule.DaylightDelta;
}
else
@@ -1328,7 +1356,7 @@ namespace System
/// Helper function that checks if a given dateTime is in Daylight Saving Time (DST).
/// This function assumes the dateTime is in UTC and AdjustmentRule is in a different time zone.
/// </summary>
- private static bool GetIsDaylightSavingsFromUtc(DateTime time, int year, TimeSpan utc, AdjustmentRule rule, out bool isAmbiguousLocalDst, TimeZoneInfo zone)
+ private static bool GetIsDaylightSavingsFromUtc(DateTime time, int year, TimeSpan utc, AdjustmentRule rule, int? ruleIndex, out bool isAmbiguousLocalDst, TimeZoneInfo zone)
{
isAmbiguousLocalDst = false;
@@ -1338,7 +1366,7 @@ namespace System
}
// Get the daylight changes for the year of the specified time.
- DaylightTimeStruct daylightTime = zone.GetDaylightTime(year, rule);
+ DaylightTimeStruct daylightTime = zone.GetDaylightTime(year, rule, ruleIndex);
// The start and end times represent the range of universal times that are in DST for that year.
// Within that there is an ambiguous hour, usually right at the end, but at the beginning in
@@ -1352,14 +1380,20 @@ namespace System
// Note we handle the similar case when rule year start with daylight saving and previous year end with daylight saving.
bool ignoreYearAdjustment = false;
- TimeSpan dstStartOffset = zone.GetDaylightSavingsStartOffsetFromUtc(utc, rule);
+ TimeSpan dstStartOffset = zone.GetDaylightSavingsStartOffsetFromUtc(utc, rule, ruleIndex);
DateTime startTime;
if (rule.IsStartDateMarkerForBeginningOfYear() && daylightTime.Start.Year > DateTime.MinValue.Year)
{
- AdjustmentRule previousYearRule = zone.GetAdjustmentRuleForTime(new DateTime(daylightTime.Start.Year - 1, 12, 31));
+ int? previousYearRuleIndex;
+ AdjustmentRule previousYearRule = zone.GetAdjustmentRuleForTime(
+ new DateTime(daylightTime.Start.Year - 1, 12, 31),
+ out previousYearRuleIndex);
if (previousYearRule != null && previousYearRule.IsEndDateMarkerForEndOfYear())
{
- DaylightTimeStruct previousDaylightTime = zone.GetDaylightTime(daylightTime.Start.Year - 1, previousYearRule);
+ DaylightTimeStruct previousDaylightTime = zone.GetDaylightTime(
+ daylightTime.Start.Year - 1,
+ previousYearRule,
+ previousYearRuleIndex);
startTime = previousDaylightTime.Start - utc - previousYearRule.BaseUtcOffsetDelta;
ignoreYearAdjustment = true;
}
@@ -1377,7 +1411,10 @@ namespace System
DateTime endTime;
if (rule.IsEndDateMarkerForEndOfYear() && daylightTime.End.Year < DateTime.MaxValue.Year)
{
- AdjustmentRule nextYearRule = zone.GetAdjustmentRuleForTime(new DateTime(daylightTime.End.Year + 1, 1, 1));
+ int? nextYearRuleIndex;
+ AdjustmentRule nextYearRule = zone.GetAdjustmentRuleForTime(
+ new DateTime(daylightTime.End.Year + 1, 1, 1),
+ out nextYearRuleIndex);
if (nextYearRule != null && nextYearRule.IsStartDateMarkerForBeginningOfYear())
{
if (nextYearRule.IsEndDateMarkerForEndOfYear())
@@ -1387,7 +1424,10 @@ namespace System
}
else
{
- DaylightTimeStruct nextdaylightTime = zone.GetDaylightTime(daylightTime.End.Year + 1, nextYearRule);
+ DaylightTimeStruct nextdaylightTime = zone.GetDaylightTime(
+ daylightTime.End.Year + 1,
+ nextYearRule,
+ nextYearRuleIndex);
endTime = nextdaylightTime.End - utc - nextYearRule.BaseUtcOffsetDelta - nextYearRule.DaylightDelta;
}
ignoreYearAdjustment = true;
@@ -1644,14 +1684,15 @@ namespace System
private static TimeSpan GetUtcOffset(DateTime time, TimeZoneInfo zone, TimeZoneInfoOptions flags)
{
TimeSpan baseOffset = zone.BaseUtcOffset;
- AdjustmentRule rule = zone.GetAdjustmentRuleForTime(time);
+ int? ruleIndex;
+ AdjustmentRule rule = zone.GetAdjustmentRuleForTime(time, out ruleIndex);
if (rule != null)
{
baseOffset = baseOffset + rule.BaseUtcOffsetDelta;
if (rule.HasDaylightSaving)
{
- DaylightTimeStruct daylightTime = zone.GetDaylightTime(time.Year, rule);
+ DaylightTimeStruct daylightTime = zone.GetDaylightTime(time.Year, rule, ruleIndex);
bool isDaylightSavings = GetIsDaylightSavings(time, rule, daylightTime, flags);
baseOffset += (isDaylightSavings ? rule.DaylightDelta : TimeSpan.Zero /* FUTURE: rule.StandardDelta */);
}
@@ -1690,21 +1731,24 @@ namespace System
isAmbiguousLocalDst = false;
TimeSpan baseOffset = zone.BaseUtcOffset;
int year;
+ int? ruleIndex;
AdjustmentRule rule;
if (time > s_maxDateOnly)
{
- rule = zone.GetAdjustmentRuleForTime(DateTime.MaxValue);
+ rule = zone.GetAdjustmentRuleForTime(DateTime.MaxValue, out ruleIndex);
year = 9999;
}
else if (time < s_minDateOnly)
{
- rule = zone.GetAdjustmentRuleForTime(DateTime.MinValue);
+ rule = zone.GetAdjustmentRuleForTime(DateTime.MinValue, out ruleIndex);
year = 1;
}
else
{
- rule = zone.GetAdjustmentRuleForTime(time, dateTimeisUtc: true);
+ rule = zone.GetAdjustmentRuleForTime(time, dateTimeisUtc: true, ruleIndex: out ruleIndex);
+ Debug.Assert(rule == null || ruleIndex.HasValue,
+ "If GetAdjustmentRuleForTime returned an AdjustmentRule, ruleIndex should also be set.");
// As we get the associated rule using the adjusted targetTime, we should use the adjusted year (targetTime.Year) too as after adding the baseOffset,
// sometimes the year value can change if the input datetime was very close to the beginning or the end of the year. Examples of such cases:
@@ -1719,7 +1763,7 @@ namespace System
baseOffset = baseOffset + rule.BaseUtcOffsetDelta;
if (rule.HasDaylightSaving)
{
- isDaylightSavings = GetIsDaylightSavingsFromUtc(time, year, zone._baseUtcOffset, rule, out isAmbiguousLocalDst, zone);
+ isDaylightSavings = GetIsDaylightSavingsFromUtc(time, year, zone._baseUtcOffset, rule, ruleIndex, out isAmbiguousLocalDst, zone);
baseOffset += (isDaylightSavings ? rule.DaylightDelta : TimeSpan.Zero /* FUTURE: rule.StandardDelta */);
}
}