diff options
author | David Wrighton <davidwr@microsoft.com> | 2017-09-13 14:50:39 -0700 |
---|---|---|
committer | David Wrighton <davidwr@microsoft.com> | 2017-09-13 14:50:39 -0700 |
commit | d68f0916d0a2bf3787bc85261ef4a4f1f27f1f24 (patch) | |
tree | 6c21ac239ae268096f20d98a8db16a4b80394fd9 /src/mscorlib/shared | |
parent | 96fa98525e0d64459148228cde5211c475b0c25c (diff) | |
parent | e866d072042f4ad9e0811aa36e338dac781c09a5 (diff) | |
download | coreclr-d68f0916d0a2bf3787bc85261ef4a4f1f27f1f24.tar.gz coreclr-d68f0916d0a2bf3787bc85261ef4a4f1f27f1f24.tar.bz2 coreclr-d68f0916d0a2bf3787bc85261ef4a4f1f27f1f24.zip |
Merge branch 'master' into update_from_master
Diffstat (limited to 'src/mscorlib/shared')
206 files changed, 13933 insertions, 1530 deletions
diff --git a/src/mscorlib/shared/Interop/Unix/System.Globalization.Native/Interop.Collation.cs b/src/mscorlib/shared/Interop/Unix/System.Globalization.Native/Interop.Collation.cs index 79aedd74d3..683845dbc1 100644 --- a/src/mscorlib/shared/Interop/Unix/System.Globalization.Native/Interop.Collation.cs +++ b/src/mscorlib/shared/Interop/Unix/System.Globalization.Native/Interop.Collation.cs @@ -44,7 +44,7 @@ internal static partial class Interop internal unsafe static extern int CompareStringOrdinalIgnoreCase(char* lpStr1, int cwStr1Len, char* lpStr2, int cwStr2Len); [DllImport(Libraries.GlobalizationInterop, EntryPoint = "GlobalizationNative_GetSortVersion")] - internal static extern int GetSortVersion(); + internal static extern int GetSortVersion(SafeSortHandle sortHandle); internal class SafeSortHandle : SafeHandle { diff --git a/src/mscorlib/shared/Interop/Unix/System.Native/Interop.PosixFAdvise.cs b/src/mscorlib/shared/Interop/Unix/System.Native/Interop.PosixFAdvise.cs index 69e39b30d2..ad8b73aed2 100644 --- a/src/mscorlib/shared/Interop/Unix/System.Native/Interop.PosixFAdvise.cs +++ b/src/mscorlib/shared/Interop/Unix/System.Native/Interop.PosixFAdvise.cs @@ -16,7 +16,7 @@ internal static partial class Interop POSIX_FADV_SEQUENTIAL = 2, /* sequential I/O access */ POSIX_FADV_WILLNEED = 3, /* will need specified pages */ POSIX_FADV_DONTNEED = 4, /* don't need the specified pages */ - POSIX_FADV_NOREUSE = 5, /* data will only be acessed once */ + POSIX_FADV_NOREUSE = 5, /* data will only be accessed once */ } /// <summary> diff --git a/src/mscorlib/shared/Interop/Windows/Interop.Libraries.cs b/src/mscorlib/shared/Interop/Windows/Interop.Libraries.cs index f65e05cfdd..45d910bfcc 100644 --- a/src/mscorlib/shared/Interop/Windows/Interop.Libraries.cs +++ b/src/mscorlib/shared/Interop/Windows/Interop.Libraries.cs @@ -9,6 +9,7 @@ internal static partial class Interop internal const string BCrypt = "BCrypt.dll"; internal const string Crypt32 = "crypt32.dll"; internal const string Kernel32 = "kernel32.dll"; + internal const string Ole32 = "ole32.dll"; internal const string OleAut32 = "oleaut32.dll"; internal const string User32 = "user32.dll"; } diff --git a/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs b/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs new file mode 100644 index 0000000000..16365ee651 --- /dev/null +++ b/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.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 Microsoft.Win32.SafeHandles; +using System; +using System.IO; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + internal unsafe struct CREATEFILE2_EXTENDED_PARAMETERS + { + internal uint dwSize; + internal uint dwFileAttributes; + internal uint dwFileFlags; + internal uint dwSecurityQosFlags; + internal SECURITY_ATTRIBUTES* lpSecurityAttributes; + internal IntPtr hTemplateFile; + } + } +} diff --git a/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.CreateFile2.cs b/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.CreateFile2.cs index 0909d3a6c8..ddc18f6c42 100644 --- a/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.CreateFile2.cs +++ b/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.CreateFile2.cs @@ -3,29 +3,30 @@ // See the LICENSE file in the project root for more information. using Microsoft.Win32.SafeHandles; -using System; +using System.IO; using System.Runtime.InteropServices; internal partial class Interop { internal partial class Kernel32 { - [DllImport(Libraries.Kernel32, EntryPoint = "CreateFile2", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)] - internal static extern unsafe SafeFileHandle CreateFile2( + [DllImport(Libraries.Kernel32, EntryPoint = "CreateFile2", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern SafeFileHandle CreateFile2Private( string lpFileName, int dwDesiredAccess, - System.IO.FileShare dwShareMode, - System.IO.FileMode dwCreationDisposition, - CREATEFILE2_EXTENDED_PARAMETERS* pCreateExParams); + FileShare dwShareMode, + FileMode dwCreationDisposition, + ref Kernel32.CREATEFILE2_EXTENDED_PARAMETERS pCreateExParams); - internal unsafe struct CREATEFILE2_EXTENDED_PARAMETERS + internal static SafeFileHandle CreateFile2( + string lpFileName, + int dwDesiredAccess, + FileShare dwShareMode, + FileMode dwCreationDisposition, + ref Kernel32.CREATEFILE2_EXTENDED_PARAMETERS pCreateExParams) { - internal uint dwSize; - internal uint dwFileAttributes; - internal uint dwFileFlags; - internal uint dwSecurityQosFlags; - internal SECURITY_ATTRIBUTES* lpSecurityAttributes; - internal IntPtr hTemplateFile; + lpFileName = PathInternal.EnsureExtendedPrefixOverMaxPath(lpFileName); + return CreateFile2Private(lpFileName, dwDesiredAccess, dwShareMode, dwCreationDisposition, ref pCreateExParams); } } -}
\ No newline at end of file +} diff --git a/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.FreeLibrary.cs b/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.FreeLibrary.cs new file mode 100644 index 0000000000..c70865350a --- /dev/null +++ b/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.FreeLibrary.cs @@ -0,0 +1,15 @@ +// 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.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)] + internal static extern bool FreeLibrary(IntPtr hModule); + } +} diff --git a/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.LoadLibraryEx.cs b/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.LoadLibraryEx.cs new file mode 100644 index 0000000000..4eef5852fe --- /dev/null +++ b/src/mscorlib/shared/Interop/Windows/Kernel32/Interop.LoadLibraryEx.cs @@ -0,0 +1,18 @@ +// 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 Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + internal const int LOAD_LIBRARY_AS_DATAFILE = 0x00000002; + + [DllImport(Libraries.Kernel32, EntryPoint = "LoadLibraryExW", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern SafeLibraryHandle LoadLibraryEx(string libFilename, IntPtr reserved, int flags); + } +} diff --git a/src/mscorlib/shared/Interop/Windows/User32/Interop.LoadString.cs b/src/mscorlib/shared/Interop/Windows/User32/Interop.LoadString.cs new file mode 100644 index 0000000000..d3d575e221 --- /dev/null +++ b/src/mscorlib/shared/Interop/Windows/User32/Interop.LoadString.cs @@ -0,0 +1,16 @@ +// 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.Runtime.InteropServices; +using System.Text; +using Microsoft.Win32.SafeHandles; + +internal partial class Interop +{ + internal partial class User32 + { + [DllImport(Libraries.User32, SetLastError = true, EntryPoint = "LoadStringW", CharSet = CharSet.Unicode)] + internal static extern int LoadString(SafeLibraryHandle handle, int id, [Out] StringBuilder buffer, int bufferLength); + } +} diff --git a/src/mscorlib/shared/Microsoft/Win32/SafeHandles/SafeLibraryHandle.cs b/src/mscorlib/shared/Microsoft/Win32/SafeHandles/SafeLibraryHandle.cs new file mode 100644 index 0000000000..3be2e354ab --- /dev/null +++ b/src/mscorlib/shared/Microsoft/Win32/SafeHandles/SafeLibraryHandle.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Win32.SafeHandles +{ + sealed internal class SafeLibraryHandle : SafeHandleZeroOrMinusOneIsInvalid + { + internal SafeLibraryHandle() : base(true) { } + + override protected bool ReleaseHandle() + { + return Interop.Kernel32.FreeLibrary(handle); + } + } +} diff --git a/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems b/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems index 07e15cf65d..a13543ea93 100644 --- a/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems @@ -13,13 +13,20 @@ <TargetsUnix Condition="'$(TargetsUnix)' != 'true'">false</TargetsUnix> <TargetsOSX Condition="'$(TargetsOSX)' != 'true'">false</TargetsOSX> </PropertyGroup> + <ItemDefinitionGroup> + <Compile> + <Visible>true</Visible> + </Compile> + </ItemDefinitionGroup> <ItemGroup> <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\CriticalHandleMinusOneIsInvalid.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\CriticalHandleZeroOrMinusOneIsInvalid.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeHandleMinusOneIsInvalid.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeHandleZeroOrMinusOneIsInvalid.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Action.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\AccessViolationException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\ApplicationException.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\AggregateException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\ArgumentException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\ArgumentNullException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\ArgumentOutOfRangeException.cs" /> @@ -32,10 +39,16 @@ <Compile Include="$(MSBuildThisFileDirectory)System\AttributeUsageAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\BadImageFormatException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\BitConverter.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Boolean.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\ArrayPool.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\ArrayPoolEventSource.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\ConfigurableArrayPool.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\IRetainable.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\MemoryHandle.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\OwnedMemory.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\TlsOverPerCoreLockedStacksArrayPool.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Utilities.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Byte.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Char.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\CharEnumerator.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\CLSCompliantAttribute.cs" /> @@ -62,6 +75,9 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Collections\IList.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Collections\IStructuralComparable.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Collections\IStructuralEquatable.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Collections\ListDictionaryInternal.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Collections\ObjectModel\Collection.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Collections\ObjectModel\ReadOnlyCollection.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\ComponentModel\DefaultValueAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\ComponentModel\EditorBrowsableAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Configuration\Assemblies\AssemblyHashAlgorithm.cs" /> @@ -80,6 +96,7 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Debug.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\DivideByZeroException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\DllNotFoundException.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Double.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\DuplicateWaitObjectException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\EntryPointNotFoundException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\EventArgs.cs" /> @@ -127,9 +144,13 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TaiwanLunisolarCalendar.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TextElementEnumerator.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\ThaiBuddhistCalendar.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TimeSpanFormat.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TimeSpanParse.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TimeSpanStyles.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\UmAlQuraCalendar.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\UnicodeCategory.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Guid.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\HResults.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\IAsyncResult.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\ICloneable.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\IComparable.cs" /> @@ -147,6 +168,7 @@ <Compile Include="$(MSBuildThisFileDirectory)System\InvalidTimeZoneException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\IO\BinaryWriter.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\IO\DirectoryNotFoundException.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\IO\DriveNotFoundException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\IO\EncodingCache.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\IO\EndOfStreamException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\IO\Error.cs" /> @@ -157,6 +179,7 @@ <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileOptions.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileShare.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\IO\IOException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\IO\Path.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\IO\PathInternal.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\IO\PathTooLongException.cs" /> @@ -168,15 +191,20 @@ <Compile Include="$(MSBuildThisFileDirectory)System\IObservable.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\IObserver.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\IProgress.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Int16.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Int32.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Int64.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Lazy.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\MarshalByRefObject.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\MemberAccessException.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Memory.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\MethodAccessException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\MidpointRounding.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\MissingMethodException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\MulticastNotSupportedException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\NotFiniteNumberException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\NotImplementedException.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\NonSerializedAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\NotSupportedException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\NullReferenceException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\ObjectDisposedException.cs" /> @@ -190,6 +218,7 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Random.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\RankException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\ReadOnlySpan.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\ReadOnlyMemory.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\AmbiguousMatchException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\Assembly.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\AssemblyAlgorithmIdAttribute.cs" /> @@ -241,6 +270,7 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\MethodBase.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\MethodImplAttributes.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\MethodInfo.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\MethodInfo.Internal.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\Missing.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\Module.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\ModuleResolveEventHandler.cs" /> @@ -258,6 +288,15 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\ReflectionTypeLoadException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\ResourceAttributes.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\ResourceLocation.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\SignatureArrayType.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\SignatureByRefType.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\SignatureConstructedGenericType.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\SignatureGenericMethodParameterType.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\SignatureGenericParameterType.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\SignatureHasElementType.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\SignaturePointerType.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\SignatureType.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\SignatureTypeExtensions.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\StrongNameKeyPair.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\TargetException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Reflection\TargetInvocationException.cs" /> @@ -278,7 +317,9 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Resources\SatelliteContractVersionAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Resources\UltimateResourceFallbackLocation.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\AccessedThroughPropertyAttribute.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\AsyncMethodBuilderAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\AsyncStateMachineAttribute.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\AsyncValueTaskMethodBuilder.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CallerFilePathAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CallerLineNumberAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CallerMemberNameAttribute.cs" /> @@ -286,6 +327,10 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CompilationRelaxationsAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CompilerGeneratedAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CompilerGlobalScopeAttribute.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\ConfiguredValueTaskAwaitable.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CustomConstantAttribute.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\DateTimeConstantAttribute.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\DecimalConstantAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\DefaultDependencyAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\DependencyAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\DisablePrivateReflectionAttribute.cs" /> @@ -298,6 +343,7 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\IndexerNameAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\INotifyCompletion.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\InternalsVisibleToAttribute.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\IntrinsicAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\IsConst.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\IsByRefLikeAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\IsVolatile.cs" /> @@ -305,11 +351,13 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\ITuple.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\LoadHint.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\MethodCodeType.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\MethodImplAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\MethodImplOptions.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" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\RuntimeWrappedException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\SpecialNameAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\StateMachineAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\StringFreezingAttribute.cs" /> @@ -319,15 +367,29 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\TypeForwardedFromAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\TypeForwardedToAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\UnsafeValueTypeAttribute.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\ValueTaskAwaiter.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ConstrainedExecution\Cer.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ConstrainedExecution\Consistency.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ConstrainedExecution\ReliabilityContractAttribute.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ExceptionServices\HandleProcessCorruptedStateExceptionsAttribute.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\BestFitMappingAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\CallingConvention.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\CharSet.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\ComVisibleAttribute.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\DefaultCharSetAttribute.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\DefaultDllImportSearchPathsAttribute.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\DllImportAttribute.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\DllImportSearchPath.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\ExternalException.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\FieldOffsetAttribute.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\InAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\LayoutKind.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\MarshalAsAttribute.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\OptionalAttribute.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\OutAttribute.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\PreserveSigAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\StringBuffer.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\StructLayoutAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\UnmanagedFunctionPointerAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\UnmanagedType.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\VarEnum.cs" /> @@ -347,6 +409,7 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Serialization\StreamingContext.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Versioning\NonVersionableAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Versioning\TargetFrameworkAttribute.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\SByte.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Security\AllowPartiallyTrustedCallersAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Security\CryptographicException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Security\PartialTrustVisibilityLevel.cs" /> @@ -362,8 +425,11 @@ <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\SerializableAttribute.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Single.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Span.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Span.NonGeneric.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\StringSpanHelpers.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\StackOverflowException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\StringComparer.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\StringComparison.cs" /> @@ -391,7 +457,7 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Text\StringBuilder.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Text\UnicodeEncoding.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Text\UTF32Encoding.cs" /> - <Compile Include="$(MSBuildThisFileDirectory)System\Text\UTF7Encoding.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Text\UTF7Encoding.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Text\UTF8Encoding.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\TimeSpan.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\ThreadAttributes.cs" /> @@ -415,6 +481,7 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\TaskCanceledException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\TaskExtensions.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\TaskSchedulerException.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Tasks\ValueTask.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadAbortException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadPriority.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadStart.cs" /> @@ -436,9 +503,13 @@ <Compile Include="$(MSBuildThisFileDirectory)System\TypeCode.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\TypeInitializationException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\TypeUnloadedException.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\UInt16.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\UInt32.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\UInt64.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\UnauthorizedAccessException.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\UnhandledExceptionEventArgs.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\UnhandledExceptionEventHandler.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\UnitySerializationHolder.cs"/> <Compile Include="$(MSBuildThisFileDirectory)System\ValueTuple.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Version.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Void.cs" /> @@ -488,19 +559,15 @@ <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\BCrypt\Interop.BCryptGenRandom.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Crypt32\Interop.CryptProtectMemory.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Interop.BOOL.cs" /> - <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Interop.Errors.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Interop.Libraries.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CancelIoEx.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CloseHandle.cs" /> - <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CreateFile.cs" Condition="'$(EnableWinRT)' != 'true'" /> - <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CreateFile2.cs" Condition="'$(EnableWinRT)' == 'true'" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FileAttributes.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FILE_INFO_BY_HANDLE_CLASS.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FileTypes.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FindClose.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FindFirstFileEx.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.FreeEnvironmentStrings.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetCPInfo.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetEnvironmentStrings.cs" /> @@ -528,25 +595,41 @@ <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\OleAut32\Interop.SysAllocStringLen.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\OleAut32\Interop.SysFreeString.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\OleAut32\Interop.SysStringLen.cs" /> - <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\User32\Interop.Constants.cs" Condition="'$(EnableWinRT)' != 'true'" /> - <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\User32\Interop.SendMessageTimeout.cs" Condition="'$(EnableWinRT)' != 'true'" /> <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeFileHandle.Windows.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.Win32.cs" Condition="'$(EnableWinRT)' != 'true' and '$(EnableDummyGlobalizationImplementation)' != 'true'" /> <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.WinRT.cs" Condition="'$(EnableWinRT)' == 'true'" /> <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\JapaneseCalendar.Win32.cs" Condition="'$(EnableWinRT)' != 'true' and '$(EnableDummyGlobalizationImplementation)' != 'true'" /> <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\JapaneseCalendar.WinRT.cs" Condition="'$(EnableWinRT)' == 'true'" /> - <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Win32.cs" Condition="'$(EnableWinRT)' != 'true'" /> <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Windows.cs" /> - <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.WinRT.cs" Condition="'$(EnableWinRT)' == 'true'" /> <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStreamCompletionSource.Win32.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\IO\Path.Windows.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\IO\PathHelper.Windows.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\IO\PathInternal.Windows.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\IO\PathInternal.Windows.StringBuffer.cs" /> - <Compile Include="$(MSBuildThisFileDirectory)System\IO\Win32Marshal.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\IO\DisableMediaInsertionPrompt.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Security\SafeBSTRHandle.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Security\SecureString.Windows.cs" /> </ItemGroup> + <ItemGroup Condition="$(TargetsWindows) and '$(EnableWinRT)' != 'true'"> + <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Win32.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeLibraryHandle.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.LoadLibraryEx.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FreeLibrary.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CreateFile.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\User32\Interop.Constants.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\User32\Interop.SendMessageTimeout.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\User32\Interop.LoadString.cs" /> + </ItemGroup> + <ItemGroup Condition="$(TargetsWindows) and '$(EnableWinRT)' == 'true'"> + <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.WinRT.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CreateFile2.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs" /> + </ItemGroup> + <ItemGroup Condition="$(TargetsWindows) or '$(FeaturePal)'=='true'"> + <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Interop.Errors.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FormatMessage.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\IO\Win32Marshal.cs" /> + </ItemGroup> <ItemGroup Condition="$(TargetsUnix)"> <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\Interop.Errors.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\Interop.IOErrors.cs" /> diff --git a/src/mscorlib/shared/System.Private.CoreLib.Shared.shproj b/src/mscorlib/shared/System.Private.CoreLib.Shared.shproj deleted file mode 100644 index 20c3de100b..0000000000 --- a/src/mscorlib/shared/System.Private.CoreLib.Shared.shproj +++ /dev/null @@ -1,17 +0,0 @@ -<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <!-- Set the properties to prevent any items from being excluded from the IDE view --> - <PropertyGroup Label="Globals"> - <ProjectGuid>c5ed3c1d-b572-46f1-8f96-522a85ce1179</ProjectGuid> - </PropertyGroup> - <PropertyGroup> - <TargetsWindows>true</TargetsWindows> - <TargetsUnix>true</TargetsUnix> - <TargetsOSX>true</TargetsOSX> - </PropertyGroup> - <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> - <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" /> - <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" /> - <PropertyGroup /> - <Import Project="System.Private.CoreLib.Shared.projitems" Label="Shared" /> - <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" /> -</Project>
\ No newline at end of file diff --git a/src/mscorlib/shared/System/AccessViolationException.cs b/src/mscorlib/shared/System/AccessViolationException.cs new file mode 100644 index 0000000000..103a4c0a9d --- /dev/null +++ b/src/mscorlib/shared/System/AccessViolationException.cs @@ -0,0 +1,50 @@ +// 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. + +/*============================================================================= +** +** +** +** Purpose: Exception class representing an AV that was deemed unsafe and may have corrupted the application. +** +** +=============================================================================*/ + +using System; +using System.Runtime.Serialization; + +namespace System +{ + public class AccessViolationException : SystemException + { + public AccessViolationException() + : base(SR.Arg_AccessViolationException) + { + HResult = HResults.E_POINTER; + } + + public AccessViolationException(String message) + : base(message) + { + HResult = HResults.E_POINTER; + } + + public AccessViolationException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.E_POINTER; + } + + protected AccessViolationException(SerializationInfo info, StreamingContext context) : base(info, context) + { + throw new PlatformNotSupportedException(); + } + +#pragma warning disable 169 // Field is not used from managed. + private IntPtr _ip; // Address of faulting instruction. + private IntPtr _target; // Address that could not be accessed. + private int _accessType; // 0:read, 1:write +#pragma warning restore 169 + } +} diff --git a/src/mscorlib/shared/System/Action.cs b/src/mscorlib/shared/System/Action.cs index b82c14d9dc..da6813829e 100644 --- a/src/mscorlib/shared/System/Action.cs +++ b/src/mscorlib/shared/System/Action.cs @@ -33,3 +33,9 @@ namespace System public delegate bool Predicate<in T>(T obj); } + +namespace System.Buffers +{ + public delegate void SpanAction<T, in TArg>(Span<T> span, TArg arg); + public delegate void ReadOnlySpanAction<T, in TArg>(ReadOnlySpan<T> span, TArg arg); +} diff --git a/src/mscorlib/shared/System/AggregateException.cs b/src/mscorlib/shared/System/AggregateException.cs new file mode 100644 index 0000000000..36b9494980 --- /dev/null +++ b/src/mscorlib/shared/System/AggregateException.cs @@ -0,0 +1,484 @@ +// 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.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.ExceptionServices; +using System.Runtime.Serialization; +using System.Security; +using System.Text; +using System.Threading; + +namespace System +{ + /// <summary>Represents one or more errors that occur during application execution.</summary> + /// <remarks> + /// <see cref="AggregateException"/> is used to consolidate multiple failures into a single, throwable + /// exception object. + /// </remarks> + [Serializable] + [DebuggerDisplay("Count = {InnerExceptionCount}")] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class AggregateException : Exception + { + private ReadOnlyCollection<Exception> m_innerExceptions; // Complete set of exceptions. Do not rename (binary serialization) + + /// <summary> + /// Initializes a new instance of the <see cref="AggregateException"/> class. + /// </summary> + public AggregateException() + : base(SR.AggregateException_ctor_DefaultMessage) + { + m_innerExceptions = new ReadOnlyCollection<Exception>(Array.Empty<Exception>()); + } + + /// <summary> + /// Initializes a new instance of the <see cref="AggregateException"/> class with + /// a specified error message. + /// </summary> + /// <param name="message">The error message that explains the reason for the exception.</param> + public AggregateException(string message) + : base(message) + { + m_innerExceptions = new ReadOnlyCollection<Exception>(Array.Empty<Exception>()); + } + + /// <summary> + /// Initializes a new instance of the <see cref="AggregateException"/> class with a specified error + /// message and a reference to the inner exception that is the cause of this exception. + /// </summary> + /// <param name="message">The error message that explains the reason for the exception.</param> + /// <param name="innerException">The exception that is the cause of the current exception.</param> + /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerException"/> argument + /// is null.</exception> + public AggregateException(string message, Exception innerException) + : base(message, innerException) + { + if (innerException == null) + { + throw new ArgumentNullException(nameof(innerException)); + } + + m_innerExceptions = new ReadOnlyCollection<Exception>(new Exception[] { innerException }); + } + + /// <summary> + /// Initializes a new instance of the <see cref="AggregateException"/> class with + /// references to the inner exceptions that are the cause of this exception. + /// </summary> + /// <param name="innerExceptions">The exceptions that are the cause of the current exception.</param> + /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptions"/> argument + /// is null.</exception> + /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptions"/> is + /// null.</exception> + public AggregateException(IEnumerable<Exception> innerExceptions) : + this(SR.AggregateException_ctor_DefaultMessage, innerExceptions) + { + } + + /// <summary> + /// Initializes a new instance of the <see cref="AggregateException"/> class with + /// references to the inner exceptions that are the cause of this exception. + /// </summary> + /// <param name="innerExceptions">The exceptions that are the cause of the current exception.</param> + /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptions"/> argument + /// is null.</exception> + /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptions"/> is + /// null.</exception> + public AggregateException(params Exception[] innerExceptions) : + this(SR.AggregateException_ctor_DefaultMessage, innerExceptions) + { + } + + /// <summary> + /// Initializes a new instance of the <see cref="AggregateException"/> class with a specified error + /// message and references to the inner exceptions that are the cause of this exception. + /// </summary> + /// <param name="message">The error message that explains the reason for the exception.</param> + /// <param name="innerExceptions">The exceptions that are the cause of the current exception.</param> + /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptions"/> argument + /// is null.</exception> + /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptions"/> is + /// null.</exception> + public AggregateException(string message, IEnumerable<Exception> innerExceptions) + // If it's already an IList, pass that along (a defensive copy will be made in the delegated ctor). If it's null, just pass along + // null typed correctly. Otherwise, create an IList from the enumerable and pass that along. + : this(message, innerExceptions as IList<Exception> ?? (innerExceptions == null ? (List<Exception>)null : new List<Exception>(innerExceptions))) + { + } + + /// <summary> + /// Initializes a new instance of the <see cref="AggregateException"/> class with a specified error + /// message and references to the inner exceptions that are the cause of this exception. + /// </summary> + /// <param name="message">The error message that explains the reason for the exception.</param> + /// <param name="innerExceptions">The exceptions that are the cause of the current exception.</param> + /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptions"/> argument + /// is null.</exception> + /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptions"/> is + /// null.</exception> + public AggregateException(string message, params Exception[] innerExceptions) : + this(message, (IList<Exception>)innerExceptions) + { + } + + /// <summary> + /// Allocates a new aggregate exception with the specified message and list of inner exceptions. + /// </summary> + /// <param name="message">The error message that explains the reason for the exception.</param> + /// <param name="innerExceptions">The exceptions that are the cause of the current exception.</param> + /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptions"/> argument + /// is null.</exception> + /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptions"/> is + /// null.</exception> + private AggregateException(string message, IList<Exception> innerExceptions) + : base(message, innerExceptions != null && innerExceptions.Count > 0 ? innerExceptions[0] : null) + { + if (innerExceptions == null) + { + throw new ArgumentNullException(nameof(innerExceptions)); + } + + // Copy exceptions to our internal array and validate them. We must copy them, + // because we're going to put them into a ReadOnlyCollection which simply reuses + // the list passed in to it. We don't want callers subsequently mutating. + Exception[] exceptionsCopy = new Exception[innerExceptions.Count]; + + for (int i = 0; i < exceptionsCopy.Length; i++) + { + exceptionsCopy[i] = innerExceptions[i]; + + if (exceptionsCopy[i] == null) + { + throw new ArgumentException(SR.AggregateException_ctor_InnerExceptionNull); + } + } + + m_innerExceptions = new ReadOnlyCollection<Exception>(exceptionsCopy); + } + + /// <summary> + /// Initializes a new instance of the <see cref="AggregateException"/> class with + /// references to the inner exception dispatch info objects that represent the cause of this exception. + /// </summary> + /// <param name="innerExceptionInfos"> + /// Information about the exceptions that are the cause of the current exception. + /// </param> + /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptionInfos"/> argument + /// is null.</exception> + /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptionInfos"/> is + /// null.</exception> + internal AggregateException(IEnumerable<ExceptionDispatchInfo> innerExceptionInfos) : + this(SR.AggregateException_ctor_DefaultMessage, innerExceptionInfos) + { + } + + /// <summary> + /// Initializes a new instance of the <see cref="AggregateException"/> class with a specified error + /// message and references to the inner exception dispatch info objects that represent the cause of + /// this exception. + /// </summary> + /// <param name="message">The error message that explains the reason for the exception.</param> + /// <param name="innerExceptionInfos"> + /// Information about the exceptions that are the cause of the current exception. + /// </param> + /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptionInfos"/> argument + /// is null.</exception> + /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptionInfos"/> is + /// null.</exception> + internal AggregateException(string message, IEnumerable<ExceptionDispatchInfo> innerExceptionInfos) + // If it's already an IList, pass that along (a defensive copy will be made in the delegated ctor). If it's null, just pass along + // null typed correctly. Otherwise, create an IList from the enumerable and pass that along. + : this(message, innerExceptionInfos as IList<ExceptionDispatchInfo> ?? + (innerExceptionInfos == null ? + (List<ExceptionDispatchInfo>)null : + new List<ExceptionDispatchInfo>(innerExceptionInfos))) + { + } + + /// <summary> + /// Allocates a new aggregate exception with the specified message and list of inner + /// exception dispatch info objects. + /// </summary> + /// <param name="message">The error message that explains the reason for the exception.</param> + /// <param name="innerExceptionInfos"> + /// Information about the exceptions that are the cause of the current exception. + /// </param> + /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptionInfos"/> argument + /// is null.</exception> + /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptionInfos"/> is + /// null.</exception> + private AggregateException(string message, IList<ExceptionDispatchInfo> innerExceptionInfos) + : base(message, innerExceptionInfos != null && innerExceptionInfos.Count > 0 && innerExceptionInfos[0] != null ? + innerExceptionInfos[0].SourceException : null) + { + if (innerExceptionInfos == null) + { + throw new ArgumentNullException(nameof(innerExceptionInfos)); + } + + // Copy exceptions to our internal array and validate them. We must copy them, + // because we're going to put them into a ReadOnlyCollection which simply reuses + // the list passed in to it. We don't want callers subsequently mutating. + Exception[] exceptionsCopy = new Exception[innerExceptionInfos.Count]; + + for (int i = 0; i < exceptionsCopy.Length; i++) + { + var edi = innerExceptionInfos[i]; + if (edi != null) exceptionsCopy[i] = edi.SourceException; + + if (exceptionsCopy[i] == null) + { + throw new ArgumentException(SR.AggregateException_ctor_InnerExceptionNull); + } + } + + m_innerExceptions = new ReadOnlyCollection<Exception>(exceptionsCopy); + } + + /// <summary> + /// Initializes a new instance of the <see cref="AggregateException"/> class with serialized data. + /// </summary> + /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds + /// the serialized object data about the exception being thrown.</param> + /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that + /// contains contextual information about the source or destination. </param> + /// <exception cref="T:System.ArgumentNullException">The <paramref name="info"/> argument is null.</exception> + /// <exception cref="T:System.Runtime.Serialization.SerializationException">The exception could not be deserialized correctly.</exception> + protected AggregateException(SerializationInfo info, StreamingContext context) : + base(info, context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + Exception[] innerExceptions = info.GetValue("InnerExceptions", typeof(Exception[])) as Exception[]; + if (innerExceptions == null) + { + throw new SerializationException(SR.AggregateException_DeserializationFailure); + } + + m_innerExceptions = new ReadOnlyCollection<Exception>(innerExceptions); + } + + /// <summary> + /// Sets the <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with information about + /// the exception. + /// </summary> + /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds + /// the serialized object data about the exception being thrown.</param> + /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that + /// contains contextual information about the source or destination. </param> + /// <exception cref="T:System.ArgumentNullException">The <paramref name="info"/> argument is null.</exception> + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + + Exception[] innerExceptions = new Exception[m_innerExceptions.Count]; + m_innerExceptions.CopyTo(innerExceptions, 0); + info.AddValue("InnerExceptions", innerExceptions, typeof(Exception[])); + } + + /// <summary> + /// Returns the <see cref="System.AggregateException"/> that is the root cause of this exception. + /// </summary> + public override Exception GetBaseException() + { + // Returns the first inner AggregateException that contains more or less than one inner exception + + // Recursively traverse the inner exceptions as long as the inner exception of type AggregateException and has only one inner exception + Exception back = this; + AggregateException backAsAggregate = this; + while (backAsAggregate != null && backAsAggregate.InnerExceptions.Count == 1) + { + back = back.InnerException; + backAsAggregate = back as AggregateException; + } + return back; + } + + /// <summary> + /// Gets a read-only collection of the <see cref="T:System.Exception"/> instances that caused the + /// current exception. + /// </summary> + public ReadOnlyCollection<Exception> InnerExceptions + { + get { return m_innerExceptions; } + } + + + /// <summary> + /// Invokes a handler on each <see cref="T:System.Exception"/> contained by this <see + /// cref="AggregateException"/>. + /// </summary> + /// <param name="predicate">The predicate to execute for each exception. The predicate accepts as an + /// argument the <see cref="T:System.Exception"/> to be processed and returns a Boolean to indicate + /// whether the exception was handled.</param> + /// <remarks> + /// Each invocation of the <paramref name="predicate"/> returns true or false to indicate whether the + /// <see cref="T:System.Exception"/> was handled. After all invocations, if any exceptions went + /// unhandled, all unhandled exceptions will be put into a new <see cref="AggregateException"/> + /// which will be thrown. Otherwise, the <see cref="Handle"/> method simply returns. If any + /// invocations of the <paramref name="predicate"/> throws an exception, it will halt the processing + /// of any more exceptions and immediately propagate the thrown exception as-is. + /// </remarks> + /// <exception cref="AggregateException">An exception contained by this <see + /// cref="AggregateException"/> was not handled.</exception> + /// <exception cref="T:System.ArgumentNullException">The <paramref name="predicate"/> argument is + /// null.</exception> + public void Handle(Func<Exception, bool> predicate) + { + if (predicate == null) + { + throw new ArgumentNullException(nameof(predicate)); + } + + List<Exception> unhandledExceptions = null; + for (int i = 0; i < m_innerExceptions.Count; i++) + { + // If the exception was not handled, lazily allocate a list of unhandled + // exceptions (to be rethrown later) and add it. + if (!predicate(m_innerExceptions[i])) + { + if (unhandledExceptions == null) + { + unhandledExceptions = new List<Exception>(); + } + + unhandledExceptions.Add(m_innerExceptions[i]); + } + } + + // If there are unhandled exceptions remaining, throw them. + if (unhandledExceptions != null) + { + throw new AggregateException(Message, unhandledExceptions); + } + } + + + /// <summary> + /// Flattens the inner instances of <see cref="AggregateException"/> by expanding its contained <see cref="Exception"/> instances + /// into a new <see cref="AggregateException"/> + /// </summary> + /// <returns>A new, flattened <see cref="AggregateException"/>.</returns> + /// <remarks> + /// If any inner exceptions are themselves instances of + /// <see cref="AggregateException"/>, this method will recursively flatten all of them. The + /// inner exceptions returned in the new <see cref="AggregateException"/> + /// will be the union of all of the inner exceptions from exception tree rooted at the provided + /// <see cref="AggregateException"/> instance. + /// </remarks> + public AggregateException Flatten() + { + // Initialize a collection to contain the flattened exceptions. + List<Exception> flattenedExceptions = new List<Exception>(); + + // Create a list to remember all aggregates to be flattened, this will be accessed like a FIFO queue + List<AggregateException> exceptionsToFlatten = new List<AggregateException>(); + exceptionsToFlatten.Add(this); + int nDequeueIndex = 0; + + // Continue removing and recursively flattening exceptions, until there are no more. + while (exceptionsToFlatten.Count > nDequeueIndex) + { + // dequeue one from exceptionsToFlatten + IList<Exception> currentInnerExceptions = exceptionsToFlatten[nDequeueIndex++].InnerExceptions; + + for (int i = 0; i < currentInnerExceptions.Count; i++) + { + Exception currentInnerException = currentInnerExceptions[i]; + + if (currentInnerException == null) + { + continue; + } + + AggregateException currentInnerAsAggregate = currentInnerException as AggregateException; + + // If this exception is an aggregate, keep it around for later. Otherwise, + // simply add it to the list of flattened exceptions to be returned. + if (currentInnerAsAggregate != null) + { + exceptionsToFlatten.Add(currentInnerAsAggregate); + } + else + { + flattenedExceptions.Add(currentInnerException); + } + } + } + + + return new AggregateException(Message, flattenedExceptions); + } + + /// <summary>Gets a message that describes the exception.</summary> + public override string Message + { + get + { + if (m_innerExceptions.Count == 0) + { + return base.Message; + } + + StringBuilder sb = StringBuilderCache.Acquire(); + sb.Append(base.Message); + sb.Append(' '); + for (int i = 0; i < m_innerExceptions.Count; i++) + { + sb.Append('('); + sb.Append(m_innerExceptions[i].Message); + sb.Append(") "); + } + sb.Length -= 1; + return StringBuilderCache.GetStringAndRelease(sb); + } + } + + /// <summary> + /// Creates and returns a string representation of the current <see cref="AggregateException"/>. + /// </summary> + /// <returns>A string representation of the current exception.</returns> + public override string ToString() + { + StringBuilder text = new StringBuilder(); + text.Append(base.ToString()); + + for (int i = 0; i < m_innerExceptions.Count; i++) + { + text.Append(Environment.NewLine); + text.Append("---> "); + text.Append(string.Format(CultureInfo.InvariantCulture, SR.AggregateException_InnerException, i)); + text.Append(m_innerExceptions[i].ToString()); + text.Append("<---"); + text.Append(Environment.NewLine); + } + + return text.ToString(); + } + + /// <summary> + /// This helper property is used by the DebuggerDisplay. + /// + /// Note that we don't want to remove this property and change the debugger display to {InnerExceptions.Count} + /// because DebuggerDisplay should be a single property access or parameterless method call, so that the debugger + /// can use a fast path without using the expression evaluator. + /// + /// See http://msdn.microsoft.com/en-us/library/x810d419.aspx + /// </summary> + private int InnerExceptionCount + { + get + { + return InnerExceptions.Count; + } + } + } +} diff --git a/src/mscorlib/shared/System/ApplicationException.cs b/src/mscorlib/shared/System/ApplicationException.cs index cb98902de1..83ced79876 100644 --- a/src/mscorlib/shared/System/ApplicationException.cs +++ b/src/mscorlib/shared/System/ApplicationException.cs @@ -31,7 +31,7 @@ namespace System public ApplicationException() : base(SR.Arg_ApplicationException) { - HResult = __HResults.COR_E_APPLICATION; + HResult = HResults.COR_E_APPLICATION; } // Creates a new ApplicationException with its message string set to @@ -41,13 +41,13 @@ namespace System public ApplicationException(String message) : base(message) { - HResult = __HResults.COR_E_APPLICATION; + HResult = HResults.COR_E_APPLICATION; } public ApplicationException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_APPLICATION; + HResult = HResults.COR_E_APPLICATION; } protected ApplicationException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/ArgumentException.cs b/src/mscorlib/shared/System/ArgumentException.cs index de2d775c84..fe65d6443a 100644 --- a/src/mscorlib/shared/System/ArgumentException.cs +++ b/src/mscorlib/shared/System/ArgumentException.cs @@ -29,7 +29,7 @@ namespace System public ArgumentException() : base(SR.Arg_ArgumentException) { - HResult = __HResults.COR_E_ARGUMENT; + HResult = HResults.COR_E_ARGUMENT; } // Creates a new ArgumentException with its message @@ -38,27 +38,27 @@ namespace System public ArgumentException(String message) : base(message) { - HResult = __HResults.COR_E_ARGUMENT; + HResult = HResults.COR_E_ARGUMENT; } public ArgumentException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_ARGUMENT; + HResult = HResults.COR_E_ARGUMENT; } public ArgumentException(String message, String paramName, Exception innerException) : base(message, innerException) { _paramName = paramName; - HResult = __HResults.COR_E_ARGUMENT; + HResult = HResults.COR_E_ARGUMENT; } public ArgumentException(String message, String paramName) : base(message) { _paramName = paramName; - HResult = __HResults.COR_E_ARGUMENT; + HResult = HResults.COR_E_ARGUMENT; } protected ArgumentException(SerializationInfo info, StreamingContext context) diff --git a/src/mscorlib/shared/System/ArgumentNullException.cs b/src/mscorlib/shared/System/ArgumentNullException.cs index 74b39fed8e..9b0732b2d2 100644 --- a/src/mscorlib/shared/System/ArgumentNullException.cs +++ b/src/mscorlib/shared/System/ArgumentNullException.cs @@ -26,25 +26,25 @@ namespace System : base(SR.ArgumentNull_Generic) { // Use E_POINTER - COM used that for null pointers. Description is "invalid pointer" - HResult = __HResults.E_POINTER; + HResult = HResults.E_POINTER; } public ArgumentNullException(String paramName) : base(SR.ArgumentNull_Generic, paramName) { - HResult = __HResults.E_POINTER; + HResult = HResults.E_POINTER; } public ArgumentNullException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.E_POINTER; + HResult = HResults.E_POINTER; } public ArgumentNullException(String paramName, String message) : base(message, paramName) { - HResult = __HResults.E_POINTER; + HResult = HResults.E_POINTER; } protected ArgumentNullException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/ArgumentOutOfRangeException.cs b/src/mscorlib/shared/System/ArgumentOutOfRangeException.cs index 4721a503b6..a25cd57763 100644 --- a/src/mscorlib/shared/System/ArgumentOutOfRangeException.cs +++ b/src/mscorlib/shared/System/ArgumentOutOfRangeException.cs @@ -27,25 +27,25 @@ namespace System public ArgumentOutOfRangeException() : base(SR.Arg_ArgumentOutOfRangeException) { - HResult = __HResults.COR_E_ARGUMENTOUTOFRANGE; + HResult = HResults.COR_E_ARGUMENTOUTOFRANGE; } public ArgumentOutOfRangeException(String paramName) : base(SR.Arg_ArgumentOutOfRangeException, paramName) { - HResult = __HResults.COR_E_ARGUMENTOUTOFRANGE; + HResult = HResults.COR_E_ARGUMENTOUTOFRANGE; } public ArgumentOutOfRangeException(String paramName, String message) : base(message, paramName) { - HResult = __HResults.COR_E_ARGUMENTOUTOFRANGE; + HResult = HResults.COR_E_ARGUMENTOUTOFRANGE; } public ArgumentOutOfRangeException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_ARGUMENTOUTOFRANGE; + HResult = HResults.COR_E_ARGUMENTOUTOFRANGE; } // We will not use this in the classlibs, but we'll provide it for @@ -55,7 +55,7 @@ namespace System : base(message, paramName) { _actualValue = actualValue; - HResult = __HResults.COR_E_ARGUMENTOUTOFRANGE; + HResult = HResults.COR_E_ARGUMENTOUTOFRANGE; } protected ArgumentOutOfRangeException(SerializationInfo info, StreamingContext context) diff --git a/src/mscorlib/shared/System/ArithmeticException.cs b/src/mscorlib/shared/System/ArithmeticException.cs index 2c8abe51fa..6285c8120e 100644 --- a/src/mscorlib/shared/System/ArithmeticException.cs +++ b/src/mscorlib/shared/System/ArithmeticException.cs @@ -26,7 +26,7 @@ namespace System public ArithmeticException() : base(SR.Arg_ArithmeticException) { - HResult = __HResults.COR_E_ARITHMETIC; + HResult = HResults.COR_E_ARITHMETIC; } // Creates a new ArithmeticException with its message string set to @@ -36,13 +36,13 @@ namespace System public ArithmeticException(String message) : base(message) { - HResult = __HResults.COR_E_ARITHMETIC; + HResult = HResults.COR_E_ARITHMETIC; } public ArithmeticException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_ARITHMETIC; + HResult = HResults.COR_E_ARITHMETIC; } protected ArithmeticException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/ArrayTypeMismatchException.cs b/src/mscorlib/shared/System/ArrayTypeMismatchException.cs index d06a450603..6964b1fa12 100644 --- a/src/mscorlib/shared/System/ArrayTypeMismatchException.cs +++ b/src/mscorlib/shared/System/ArrayTypeMismatchException.cs @@ -26,7 +26,7 @@ namespace System public ArrayTypeMismatchException() : base(SR.Arg_ArrayTypeMismatchException) { - HResult = __HResults.COR_E_ARRAYTYPEMISMATCH; + HResult = HResults.COR_E_ARRAYTYPEMISMATCH; } // Creates a new ArrayMismatchException with its message string set to @@ -36,13 +36,13 @@ namespace System public ArrayTypeMismatchException(String message) : base(message) { - HResult = __HResults.COR_E_ARRAYTYPEMISMATCH; + HResult = HResults.COR_E_ARRAYTYPEMISMATCH; } public ArrayTypeMismatchException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_ARRAYTYPEMISMATCH; + HResult = HResults.COR_E_ARRAYTYPEMISMATCH; } protected ArrayTypeMismatchException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/BadImageFormatException.cs b/src/mscorlib/shared/System/BadImageFormatException.cs index adedcb2a3f..a4661fc799 100644 --- a/src/mscorlib/shared/System/BadImageFormatException.cs +++ b/src/mscorlib/shared/System/BadImageFormatException.cs @@ -25,31 +25,31 @@ namespace System public BadImageFormatException() : base(SR.Arg_BadImageFormatException) { - HResult = __HResults.COR_E_BADIMAGEFORMAT; + HResult = HResults.COR_E_BADIMAGEFORMAT; } public BadImageFormatException(String message) : base(message) { - HResult = __HResults.COR_E_BADIMAGEFORMAT; + HResult = HResults.COR_E_BADIMAGEFORMAT; } public BadImageFormatException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_BADIMAGEFORMAT; + HResult = HResults.COR_E_BADIMAGEFORMAT; } public BadImageFormatException(String message, String fileName) : base(message) { - HResult = __HResults.COR_E_BADIMAGEFORMAT; + HResult = HResults.COR_E_BADIMAGEFORMAT; _fileName = fileName; } public BadImageFormatException(String message, String fileName, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_BADIMAGEFORMAT; + HResult = HResults.COR_E_BADIMAGEFORMAT; _fileName = fileName; } @@ -78,7 +78,7 @@ namespace System if (_message == null) { if ((_fileName == null) && - (HResult == __HResults.COR_E_EXCEPTION)) + (HResult == HResults.COR_E_EXCEPTION)) _message = SR.Arg_BadImageFormatException; else diff --git a/src/mscorlib/shared/System/BitConverter.cs b/src/mscorlib/shared/System/BitConverter.cs index e19229dd37..edcdd1ec93 100644 --- a/src/mscorlib/shared/System/BitConverter.cs +++ b/src/mscorlib/shared/System/BitConverter.cs @@ -30,6 +30,16 @@ namespace System return r; } + // Converts a Boolean into a Span of bytes with length one. + public static bool TryWriteBytes(Span<byte> destination, bool value) + { + if (destination.Length < sizeof(byte)) + return false; + + Unsafe.WriteUnaligned(ref destination.DangerousGetPinnableReference(), value ? (byte)1: (byte)0); + return true; + } + // Converts a char into an array of bytes with length two. public static byte[] GetBytes(char value) { @@ -38,6 +48,16 @@ namespace System return bytes; } + // Converts a char into a Span + public static bool TryWriteBytes(Span<byte> destination, char value) + { + if (destination.Length < sizeof(char)) + return false; + + Unsafe.WriteUnaligned(ref destination.DangerousGetPinnableReference(), value); + return true; + } + // Converts a short into an array of bytes with length // two. public static byte[] GetBytes(short value) @@ -47,6 +67,16 @@ namespace System return bytes; } + // Converts a short into a Span + public static bool TryWriteBytes(Span<byte> destination, short value) + { + if (destination.Length < sizeof(short)) + return false; + + Unsafe.WriteUnaligned(ref destination.DangerousGetPinnableReference(), value); + return true; + } + // Converts an int into an array of bytes with length // four. public static byte[] GetBytes(int value) @@ -56,6 +86,16 @@ namespace System return bytes; } + // Converts an int into a Span + public static bool TryWriteBytes(Span<byte> destination, int value) + { + if (destination.Length < sizeof(int)) + return false; + + Unsafe.WriteUnaligned(ref destination.DangerousGetPinnableReference(), value); + return true; + } + // Converts a long into an array of bytes with length // eight. public static byte[] GetBytes(long value) @@ -65,6 +105,16 @@ namespace System return bytes; } + // Converts a long into a Span + public static bool TryWriteBytes(Span<byte> destination, long value) + { + if (destination.Length < sizeof(long)) + return false; + + Unsafe.WriteUnaligned(ref destination.DangerousGetPinnableReference(), value); + return true; + } + // Converts an ushort into an array of bytes with // length two. [CLSCompliant(false)] @@ -75,6 +125,17 @@ namespace System return bytes; } + // Converts a ushort into a Span + [CLSCompliant(false)] + public static bool TryWriteBytes(Span<byte> destination, ushort value) + { + if (destination.Length < sizeof(ushort)) + return false; + + Unsafe.WriteUnaligned(ref destination.DangerousGetPinnableReference(), value); + return true; + } + // Converts an uint into an array of bytes with // length four. [CLSCompliant(false)] @@ -85,6 +146,17 @@ namespace System return bytes; } + // Converts a uint into a Span + [CLSCompliant(false)] + public static bool TryWriteBytes(Span<byte> destination, uint value) + { + if (destination.Length < sizeof(uint)) + return false; + + Unsafe.WriteUnaligned(ref destination.DangerousGetPinnableReference(), value); + return true; + } + // Converts an unsigned long into an array of bytes with // length eight. [CLSCompliant(false)] @@ -95,6 +167,17 @@ namespace System return bytes; } + // Converts a ulong into a Span + [CLSCompliant(false)] + public static bool TryWriteBytes(Span<byte> destination, ulong value) + { + if (destination.Length < sizeof(ulong)) + return false; + + Unsafe.WriteUnaligned(ref destination.DangerousGetPinnableReference(), value); + return true; + } + // Converts a float into an array of bytes with length // four. public static byte[] GetBytes(float value) @@ -104,6 +187,16 @@ namespace System return bytes; } + // Converts a float into a Span + public static bool TryWriteBytes(Span<byte> destination, float value) + { + if (destination.Length < sizeof(float)) + return false; + + Unsafe.WriteUnaligned(ref destination.DangerousGetPinnableReference(), value); + return true; + } + // Converts a double into an array of bytes with length // eight. public static byte[] GetBytes(double value) @@ -113,9 +206,27 @@ namespace System return bytes; } + // Converts a double into a Span + public static bool TryWriteBytes(Span<byte> destination, double value) + { + if (destination.Length < sizeof(double)) + return false; + + Unsafe.WriteUnaligned(ref destination.DangerousGetPinnableReference(), value); + return true; + } + // Converts an array of bytes into a char. public static char ToChar(byte[] value, int startIndex) => unchecked((char)ReadInt16(value, startIndex)); + // Converts a Span into a char + public static char ToChar(ReadOnlySpan<byte> value) + { + if (value.Length < sizeof(char)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned<char>(ref value.DangerousGetPinnableReference()); + } + private static short ReadInt16(byte[] value, int startIndex) { if (value == null) @@ -155,27 +266,78 @@ namespace System // Converts an array of bytes into a short. public static short ToInt16(byte[] value, int startIndex) => ReadInt16(value, startIndex); + // Converts a Span into a short + public static short ToInt16(ReadOnlySpan<byte> value) + { + if (value.Length < sizeof(short)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned<short>(ref value.DangerousGetPinnableReference()); + } + // Converts an array of bytes into an int. public static int ToInt32(byte[] value, int startIndex) => ReadInt32(value, startIndex); + // Converts a Span into an int + public static int ToInt32(ReadOnlySpan<byte> value) + { + if (value.Length < sizeof(int)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned<int>(ref value.DangerousGetPinnableReference()); + } + // Converts an array of bytes into a long. public static long ToInt64(byte[] value, int startIndex) => ReadInt64(value, startIndex); + // Converts a Span into a long + public static long ToInt64(ReadOnlySpan<byte> value) + { + if (value.Length < sizeof(long)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned<long>(ref value.DangerousGetPinnableReference()); + } + // Converts an array of bytes into an ushort. // [CLSCompliant(false)] public static ushort ToUInt16(byte[] value, int startIndex) => unchecked((ushort)ReadInt16(value, startIndex)); + // Converts a Span into a ushort + [CLSCompliant(false)] + public static ushort ToUInt16(ReadOnlySpan<byte> value) + { + if (value.Length < sizeof(ushort)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned<ushort>(ref value.DangerousGetPinnableReference()); + } + // Converts an array of bytes into an uint. // [CLSCompliant(false)] public static uint ToUInt32(byte[] value, int startIndex) => unchecked((uint)ReadInt32(value, startIndex)); + // Convert a Span into a uint + [CLSCompliant(false)] + public static uint ToUInt32(ReadOnlySpan<byte> value) + { + if (value.Length < sizeof(uint)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned<uint>(ref value.DangerousGetPinnableReference()); + } + // Converts an array of bytes into an unsigned long. // [CLSCompliant(false)] public static ulong ToUInt64(byte[] value, int startIndex) => unchecked((ulong)ReadInt64(value, startIndex)); + // Converts a Span into an unsigned long + [CLSCompliant(false)] + public static ulong ToUInt64(ReadOnlySpan<byte> value) + { + if (value.Length < sizeof(ulong)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned<ulong>(ref value.DangerousGetPinnableReference()); + } + // Converts an array of bytes into a float. public static unsafe float ToSingle(byte[] value, int startIndex) { @@ -183,6 +345,14 @@ namespace System return *(float*)&val; } + // Converts a Span into a float + public static float ToSingle(ReadOnlySpan<byte> value) + { + if (value.Length < sizeof(float)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned<float>(ref value.DangerousGetPinnableReference()); + } + // Converts an array of bytes into a double. public static unsafe double ToDouble(byte[] value, int startIndex) { @@ -190,6 +360,14 @@ namespace System return *(double*)&val; } + // Converts a Span into a double + public static double ToDouble(ReadOnlySpan<byte> value) + { + if (value.Length < sizeof(double)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned<double>(ref value.DangerousGetPinnableReference()); + } + private static char GetHexValue(int i) { Debug.Assert(i >= 0 && i < 16, "i is out of range."); @@ -298,6 +476,13 @@ namespace System return value[startIndex] != 0; } + public static bool ToBoolean(ReadOnlySpan<byte> value) + { + if (value.Length < sizeof(byte)) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + return Unsafe.ReadUnaligned<byte>(ref value.DangerousGetPinnableReference()) != 0; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe long DoubleToInt64Bits(double value) { diff --git a/src/mscorlib/shared/System/Boolean.cs b/src/mscorlib/shared/System/Boolean.cs new file mode 100644 index 0000000000..a6ffb6de83 --- /dev/null +++ b/src/mscorlib/shared/System/Boolean.cs @@ -0,0 +1,335 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: The boolean class serves as a wrapper for the primitive +** type boolean. +** +** +===========================================================*/ + +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Boolean : IComparable, IConvertible, IComparable<Boolean>, IEquatable<Boolean> + { + // + // Member Variables + // + private bool m_value; // Do not rename (binary serialization) + + // The true value. + // + internal const int True = 1; + + // The false value. + // + internal const int False = 0; + + + // + // Internal Constants are real consts for performance. + // + + // The internal string representation of true. + // + internal const String TrueLiteral = "True"; + + // The internal string representation of false. + // + internal const String FalseLiteral = "False"; + + + // + // Public Constants + // + + // The public string representation of true. + // + public static readonly String TrueString = TrueLiteral; + + // The public string representation of false. + // + public static readonly String FalseString = FalseLiteral; + + // + // Overriden Instance Methods + // + /*=================================GetHashCode================================== + **Args: None + **Returns: 1 or 0 depending on whether this instance represents true or false. + **Exceptions: None + **Overriden From: Value + ==============================================================================*/ + // Provides a hash code for this instance. + public override int GetHashCode() + { + return (m_value) ? True : False; + } + + /*===================================ToString=================================== + **Args: None + **Returns: "True" or "False" depending on the state of the boolean. + **Exceptions: None. + ==============================================================================*/ + // Converts the boolean value of this instance to a String. + public override String ToString() + { + if (false == m_value) + { + return FalseLiteral; + } + return TrueLiteral; + } + + public String ToString(IFormatProvider provider) + { + return ToString(); + } + + // Determines whether two Boolean objects are equal. + public override bool Equals(Object obj) + { + //If it's not a boolean, we're definitely not equal + if (!(obj is Boolean)) + { + return false; + } + + return (m_value == ((Boolean)obj).m_value); + } + + [NonVersionable] + public bool Equals(Boolean obj) + { + return m_value == obj; + } + + // Compares this object to another object, returning an integer that + // indicates the relationship. For booleans, false sorts before true. + // null is considered to be less than any instance. + // If object is not of type boolean, this method throws an ArgumentException. + // + // Returns a value less than zero if this object + // + public int CompareTo(Object obj) + { + if (obj == null) + { + return 1; + } + if (!(obj is Boolean)) + { + throw new ArgumentException(SR.Arg_MustBeBoolean); + } + + if (m_value == ((Boolean)obj).m_value) + { + return 0; + } + else if (m_value == false) + { + return -1; + } + return 1; + } + + public int CompareTo(Boolean value) + { + if (m_value == value) + { + return 0; + } + else if (m_value == false) + { + return -1; + } + return 1; + } + + // + // Static Methods + // + + // Determines whether a String represents true or false. + // + public static Boolean Parse(String value) + { + if (value == null) throw new ArgumentNullException(nameof(value)); + return Parse(value.AsReadOnlySpan()); + } + + public static bool Parse(ReadOnlySpan<char> value) => + TryParse(value, out bool result) ? result : throw new FormatException(SR.Format_BadBoolean); + + // Determines whether a String represents true or false. + // + public static Boolean TryParse(String value, out Boolean result) + { + if (value == null) + { + result = false; + return false; + } + + return TryParse(value.AsReadOnlySpan(), out result); + } + + public static bool TryParse(ReadOnlySpan<char> value, out bool result) + { + ReadOnlySpan<char> trueSpan = TrueLiteral.AsReadOnlySpan(); + if (StringSpanHelpers.Equals(trueSpan, value, StringComparison.OrdinalIgnoreCase)) + { + result = true; + return true; + } + + ReadOnlySpan<char> falseSpan = FalseLiteral.AsReadOnlySpan(); + if (StringSpanHelpers.Equals(falseSpan, value, StringComparison.OrdinalIgnoreCase)) + { + result = false; + return true; + } + + // Special case: Trim whitespace as well as null characters. + value = TrimWhiteSpaceAndNull(value); + + if (StringSpanHelpers.Equals(trueSpan, value, StringComparison.OrdinalIgnoreCase)) + { + result = true; + return true; + } + + if (StringSpanHelpers.Equals(falseSpan, value, StringComparison.OrdinalIgnoreCase)) + { + result = false; + return true; + } + + result = false; + return false; + } + + private static ReadOnlySpan<char> TrimWhiteSpaceAndNull(ReadOnlySpan<char> value) + { + const char nullChar = (char)0x0000; + + int start = 0; + while (start < value.Length) + { + if (!Char.IsWhiteSpace(value[start]) && value[start] != nullChar) + { + break; + } + start++; + } + + int end = value.Length - 1; + while (end >= start) + { + if (!Char.IsWhiteSpace(value[end]) && value[end] != nullChar) + { + break; + } + end--; + } + + return value.Slice(start, end - start + 1); + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.Boolean; + } + + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return m_value; + } + + char IConvertible.ToChar(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Boolean", "Char")); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Boolean", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/src/mscorlib/shared/System/Buffers/ArrayPoolEventSource.cs b/src/mscorlib/shared/System/Buffers/ArrayPoolEventSource.cs new file mode 100644 index 0000000000..9482744144 --- /dev/null +++ b/src/mscorlib/shared/System/Buffers/ArrayPoolEventSource.cs @@ -0,0 +1,78 @@ +// 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.Tracing; + +namespace System.Buffers +{ + [EventSource(Name = "System.Buffers.ArrayPoolEventSource")] + internal sealed class ArrayPoolEventSource : EventSource + { + internal readonly static ArrayPoolEventSource Log = new ArrayPoolEventSource(); + + /// <summary>The reason for a BufferAllocated event.</summary> + internal enum BufferAllocatedReason : int + { + /// <summary>The pool is allocating a buffer to be pooled in a bucket.</summary> + Pooled, + /// <summary>The requested buffer size was too large to be pooled.</summary> + OverMaximumSize, + /// <summary>The pool has already allocated for pooling as many buffers of a particular size as it's allowed.</summary> + PoolExhausted + } + + /// <summary> + /// Event for when a buffer is rented. This is invoked once for every successful call to Rent, + /// regardless of whether a buffer is allocated or a buffer is taken from the pool. In a + /// perfect situation where all rented buffers are returned, we expect to see the number + /// of BufferRented events exactly match the number of BuferReturned events, with the number + /// of BufferAllocated events being less than or equal to those numbers (ideally significantly + /// less than). + /// </summary> + [Event(1, Level = EventLevel.Verbose)] + internal unsafe void BufferRented(int bufferId, int bufferSize, int poolId, int bucketId) + { + EventData* payload = stackalloc EventData[4]; + payload[0].Size = sizeof(int); + payload[0].DataPointer = ((IntPtr)(&bufferId)); + payload[1].Size = sizeof(int); + payload[1].DataPointer = ((IntPtr)(&bufferSize)); + payload[2].Size = sizeof(int); + payload[2].DataPointer = ((IntPtr)(&poolId)); + payload[3].Size = sizeof(int); + payload[3].DataPointer = ((IntPtr)(&bucketId)); + WriteEventCore(1, 4, payload); + } + + /// <summary> + /// Event for when a buffer is allocated by the pool. In an ideal situation, the number + /// of BufferAllocated events is significantly smaller than the number of BufferRented and + /// BufferReturned events. + /// </summary> + [Event(2, Level = EventLevel.Informational)] + internal unsafe void BufferAllocated(int bufferId, int bufferSize, int poolId, int bucketId, BufferAllocatedReason reason) + { + EventData* payload = stackalloc EventData[5]; + payload[0].Size = sizeof(int); + payload[0].DataPointer = ((IntPtr)(&bufferId)); + payload[1].Size = sizeof(int); + payload[1].DataPointer = ((IntPtr)(&bufferSize)); + payload[2].Size = sizeof(int); + payload[2].DataPointer = ((IntPtr)(&poolId)); + payload[3].Size = sizeof(int); + payload[3].DataPointer = ((IntPtr)(&bucketId)); + payload[4].Size = sizeof(BufferAllocatedReason); + payload[4].DataPointer = ((IntPtr)(&reason)); + WriteEventCore(2, 5, payload); + } + + /// <summary> + /// Event raised when a buffer is returned to the pool. This event is raised regardless of whether + /// the returned buffer is stored or dropped. In an ideal situation, the number of BufferReturned + /// events exactly matches the number of BufferRented events. + /// </summary> + [Event(3, Level = EventLevel.Verbose)] + internal void BufferReturned(int bufferId, int bufferSize, int poolId) => WriteEvent(3, bufferId, bufferSize, poolId); + } +} diff --git a/src/mscorlib/shared/System/Buffers/IRetainable.cs b/src/mscorlib/shared/System/Buffers/IRetainable.cs new file mode 100644 index 0000000000..8d71fc614a --- /dev/null +++ b/src/mscorlib/shared/System/Buffers/IRetainable.cs @@ -0,0 +1,15 @@ +// 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.Runtime; +using System.Runtime.CompilerServices; + +namespace System.Buffers +{ + public interface IRetainable + { + void Retain(); + bool Release(); + } +}
\ No newline at end of file diff --git a/src/mscorlib/shared/System/Buffers/MemoryHandle.cs b/src/mscorlib/shared/System/Buffers/MemoryHandle.cs new file mode 100644 index 0000000000..fcdc7fe17f --- /dev/null +++ b/src/mscorlib/shared/System/Buffers/MemoryHandle.cs @@ -0,0 +1,44 @@ +// 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.Runtime; +using System.Runtime.InteropServices; + +namespace System.Buffers +{ + public unsafe struct MemoryHandle : IDisposable + { + private IRetainable _owner; + private void* _pointer; + private GCHandle _handle; + + [CLSCompliant(false)] + public MemoryHandle(IRetainable owner, void* pinnedPointer = null, GCHandle handle = default(GCHandle)) + { + _owner = owner; + _pointer = pinnedPointer; + _handle = handle; + } + + [CLSCompliant(false)] + public void* PinnedPointer => _pointer; + + public void Dispose() + { + if (_handle.IsAllocated) + { + _handle.Free(); + } + + if (_owner != null) + { + _owner.Release(); + _owner = null; + } + + _pointer = null; + } + + } +}
\ No newline at end of file diff --git a/src/mscorlib/shared/System/Buffers/OwnedMemory.cs b/src/mscorlib/shared/System/Buffers/OwnedMemory.cs new file mode 100644 index 0000000000..e3a89f7ef4 --- /dev/null +++ b/src/mscorlib/shared/System/Buffers/OwnedMemory.cs @@ -0,0 +1,53 @@ +// 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.Runtime; +using System.Runtime.CompilerServices; + +namespace System.Buffers +{ + public abstract class OwnedMemory<T> : IDisposable, IRetainable + { + public abstract int Length { get; } + + public abstract Span<T> AsSpan(); + + public Memory<T> AsMemory + { + get + { + if (IsDisposed) + { + ThrowHelper.ThrowObjectDisposedException(nameof(OwnedMemory<T>), ExceptionResource.Memory_ThrowIfDisposed); + } + return new Memory<T>(this, 0, Length); + } + } + + public abstract MemoryHandle Pin(); + + protected internal abstract bool TryGetArray(out ArraySegment<T> arraySegment); + + public void Dispose() + { + if (IsRetained) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Memory_OutstandingReferences); + } + Dispose(true); + GC.SuppressFinalize(this); + } + + protected abstract void Dispose(bool disposing); + + protected abstract bool IsRetained { get; } + + public abstract bool IsDisposed { get; } + + public abstract void Retain(); + + public abstract bool Release(); + + } +}
\ No newline at end of file diff --git a/src/mscorlib/shared/System/Byte.cs b/src/mscorlib/shared/System/Byte.cs new file mode 100644 index 0000000000..a0f8ff8c29 --- /dev/null +++ b/src/mscorlib/shared/System/Byte.cs @@ -0,0 +1,298 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: This class will encapsulate a byte and provide an +** Object representation of it. +** +** +===========================================================*/ + +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Byte : IComparable, IConvertible, IFormattable, IComparable<Byte>, IEquatable<Byte> + { + private byte m_value; // Do not rename (binary serialization) + + // The maximum value that a Byte may represent: 255. + public const byte MaxValue = (byte)0xFF; + + // The minimum value that a Byte may represent: 0. + public const byte MinValue = 0; + + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns a value less than zero if this object + // null is considered to be less than any instance. + // If object is not of type byte, this method throws an ArgumentException. + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + if (!(value is Byte)) + { + throw new ArgumentException(SR.Arg_MustBeByte); + } + + return m_value - (((Byte)value).m_value); + } + + public int CompareTo(Byte value) + { + return m_value - value; + } + + // Determines whether two Byte objects are equal. + public override bool Equals(Object obj) + { + if (!(obj is Byte)) + { + return false; + } + return m_value == ((Byte)obj).m_value; + } + + [NonVersionable] + public bool Equals(Byte obj) + { + return m_value == obj; + } + + // Gets a hash code for this instance. + public override int GetHashCode() + { + return m_value; + } + + [Pure] + public static byte Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + } + + [Pure] + public static byte Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo); + } + + [Pure] + public static byte Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + } + + // Parses an unsigned byte from a String in the given style. If + // a NumberFormatInfo isn't specified, the current culture's + // NumberFormatInfo is assumed. + [Pure] + public static byte Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider)); + } + + public static byte Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Parse(s, style, NumberFormatInfo.GetInstance(provider)); + } + + private static byte Parse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info) + { + int i = 0; + try + { + i = Number.ParseInt32(s, style, info); + } + catch (OverflowException e) + { + throw new OverflowException(SR.Overflow_Byte, e); + } + + if (i < MinValue || i > MaxValue) throw new OverflowException(SR.Overflow_Byte); + return (byte)i; + } + + public static bool TryParse(String s, out Byte result) + { + if (s == null) + { + result = 0; + return false; + } + + return TryParse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out Byte result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return TryParse(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result); + } + + public static bool TryParse(ReadOnlySpan<char> s, out byte result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + private static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out Byte result) + { + result = 0; + int i; + if (!Number.TryParseInt32(s, style, info, out i)) + { + return false; + } + if (i < MinValue || i > MaxValue) + { + return false; + } + result = (byte)i; + return true; + } + + [Pure] + public override String ToString() + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatInt32(m_value, null, NumberFormatInfo.CurrentInfo); + } + + [Pure] + public String ToString(String format) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatInt32(m_value, format, NumberFormatInfo.CurrentInfo); + } + + [Pure] + public String ToString(IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatInt32(m_value, null, NumberFormatInfo.GetInstance(provider)); + } + + [Pure] + public String ToString(String format, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatInt32(m_value, format, NumberFormatInfo.GetInstance(provider)); + } + + // + // IConvertible implementation + // + [Pure] + public TypeCode GetTypeCode() + { + return TypeCode.Byte; + } + + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return Convert.ToChar(m_value); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return m_value; + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Byte", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/src/mscorlib/shared/System/Collections/Generic/KeyNotFoundException.cs b/src/mscorlib/shared/System/Collections/Generic/KeyNotFoundException.cs index cdd6faf030..c32bc623ba 100644 --- a/src/mscorlib/shared/System/Collections/Generic/KeyNotFoundException.cs +++ b/src/mscorlib/shared/System/Collections/Generic/KeyNotFoundException.cs @@ -12,19 +12,19 @@ namespace System.Collections.Generic public KeyNotFoundException() : base(SR.Arg_KeyNotFound) { - HResult = __HResults.COR_E_KEYNOTFOUND; + HResult = HResults.COR_E_KEYNOTFOUND; } public KeyNotFoundException(String message) : base(message) { - HResult = __HResults.COR_E_KEYNOTFOUND; + HResult = HResults.COR_E_KEYNOTFOUND; } public KeyNotFoundException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_KEYNOTFOUND; + HResult = HResults.COR_E_KEYNOTFOUND; } protected KeyNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/Collections/ICollection.cs b/src/mscorlib/shared/System/Collections/ICollection.cs index 80ea092363..8fd6a96b48 100644 --- a/src/mscorlib/shared/System/Collections/ICollection.cs +++ b/src/mscorlib/shared/System/Collections/ICollection.cs @@ -29,7 +29,7 @@ namespace System.Collections // implementation of a collection, and use one of the internal objects // found in that code. // - // In the absense of a static Synchronized method on a collection, + // In the absence of a static Synchronized method on a collection, // the expected usage for SyncRoot would look like this: // // ICollection col = ... diff --git a/src/mscorlib/shared/System/Collections/ListDictionaryInternal.cs b/src/mscorlib/shared/System/Collections/ListDictionaryInternal.cs new file mode 100644 index 0000000000..681e51a329 --- /dev/null +++ b/src/mscorlib/shared/System/Collections/ListDictionaryInternal.cs @@ -0,0 +1,521 @@ +// 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. +/*============================================================ +** +** +** +** +** +** Purpose: List for exceptions. +** +** +===========================================================*/ + +using System.Diagnostics.Contracts; + +namespace System.Collections +{ + /// This is a simple implementation of IDictionary using a singly linked list. This + /// will be smaller and faster than a Hashtable if the number of elements is 10 or less. + /// This should not be used if performance is important for large numbers of elements. + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] +#if CORECLR + internal +#else + public +#endif + class ListDictionaryInternal : IDictionary + { + private DictionaryNode head; // Do not rename (binary serialization) + private int version; // Do not rename (binary serialization) + private int count; // Do not rename (binary serialization) + [NonSerialized] + private Object _syncRoot; + + public ListDictionaryInternal() + { + } + + public Object this[Object key] + { + get + { + if (key == null) + { + throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); + } + Contract.EndContractBlock(); + DictionaryNode node = head; + + while (node != null) + { + if (node.key.Equals(key)) + { + return node.value; + } + node = node.next; + } + return null; + } + set + { + if (key == null) + { + throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); + } + Contract.EndContractBlock(); + + + version++; + DictionaryNode last = null; + DictionaryNode node; + for (node = head; node != null; node = node.next) + { + if (node.key.Equals(key)) + { + break; + } + last = node; + } + if (node != null) + { + // Found it + node.value = value; + return; + } + // Not found, so add a new one + DictionaryNode newNode = new DictionaryNode(); + newNode.key = key; + newNode.value = value; + if (last != null) + { + last.next = newNode; + } + else + { + head = newNode; + } + count++; + } + } + + public int Count + { + get + { + return count; + } + } + + public ICollection Keys + { + get + { + return new NodeKeyValueCollection(this, true); + } + } + + public bool IsReadOnly + { + get + { + return false; + } + } + + public bool IsFixedSize + { + get + { + return false; + } + } + + public bool IsSynchronized + { + get + { + return false; + } + } + + public Object SyncRoot + { + get + { + if (_syncRoot == null) + { + System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null); + } + return _syncRoot; + } + } + + public ICollection Values + { + get + { + return new NodeKeyValueCollection(this, false); + } + } + + public void Add(Object key, Object value) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); + } + Contract.EndContractBlock(); + + + version++; + DictionaryNode last = null; + DictionaryNode node; + for (node = head; node != null; node = node.next) + { + if (node.key.Equals(key)) + { + throw new ArgumentException(SR.Format(SR.Argument_AddingDuplicate__, node.key, key)); + } + last = node; + } + if (node != null) + { + // Found it + node.value = value; + return; + } + // Not found, so add a new one + DictionaryNode newNode = new DictionaryNode(); + newNode.key = key; + newNode.value = value; + if (last != null) + { + last.next = newNode; + } + else + { + head = newNode; + } + count++; + } + + public void Clear() + { + count = 0; + head = null; + version++; + } + + public bool Contains(Object key) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); + } + Contract.EndContractBlock(); + for (DictionaryNode node = head; node != null; node = node.next) + { + if (node.key.Equals(key)) + { + return true; + } + } + return false; + } + + public void CopyTo(Array array, int index) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + + if (array.Rank != 1) + throw new ArgumentException(SR.Arg_RankMultiDimNotSupported); + + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum); + + if (array.Length - index < this.Count) + throw new ArgumentException(SR.ArgumentOutOfRange_Index, nameof(index)); + Contract.EndContractBlock(); + + for (DictionaryNode node = head; node != null; node = node.next) + { + array.SetValue(new DictionaryEntry(node.key, node.value), index); + index++; + } + } + + public IDictionaryEnumerator GetEnumerator() + { + return new NodeEnumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new NodeEnumerator(this); + } + + public void Remove(Object key) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); + } + Contract.EndContractBlock(); + version++; + DictionaryNode last = null; + DictionaryNode node; + for (node = head; node != null; node = node.next) + { + if (node.key.Equals(key)) + { + break; + } + last = node; + } + if (node == null) + { + return; + } + if (node == head) + { + head = node.next; + } + else + { + last.next = node.next; + } + count--; + } + + private class NodeEnumerator : IDictionaryEnumerator + { + private ListDictionaryInternal list; + private DictionaryNode current; + private int version; + private bool start; + + + public NodeEnumerator(ListDictionaryInternal list) + { + this.list = list; + version = list.version; + start = true; + current = null; + } + + public Object Current + { + get + { + return Entry; + } + } + + public DictionaryEntry Entry + { + get + { + if (current == null) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + return new DictionaryEntry(current.key, current.value); + } + } + + public Object Key + { + get + { + if (current == null) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + return current.key; + } + } + + public Object Value + { + get + { + if (current == null) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + return current.value; + } + } + + public bool MoveNext() + { + if (version != list.version) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + } + if (start) + { + current = list.head; + start = false; + } + else + { + if (current != null) + { + current = current.next; + } + } + return (current != null); + } + + public void Reset() + { + if (version != list.version) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + } + start = true; + current = null; + } + } + + + private class NodeKeyValueCollection : ICollection + { + private ListDictionaryInternal list; + private bool isKeys; + + public NodeKeyValueCollection(ListDictionaryInternal list, bool isKeys) + { + this.list = list; + this.isKeys = isKeys; + } + + void ICollection.CopyTo(Array array, int index) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + if (array.Rank != 1) + throw new ArgumentException(SR.Arg_RankMultiDimNotSupported); + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum); + Contract.EndContractBlock(); + if (array.Length - index < list.Count) + throw new ArgumentException(SR.ArgumentOutOfRange_Index, nameof(index)); + for (DictionaryNode node = list.head; node != null; node = node.next) + { + array.SetValue(isKeys ? node.key : node.value, index); + index++; + } + } + + int ICollection.Count + { + get + { + int count = 0; + for (DictionaryNode node = list.head; node != null; node = node.next) + { + count++; + } + return count; + } + } + + bool ICollection.IsSynchronized + { + get + { + return false; + } + } + + Object ICollection.SyncRoot + { + get + { + return list.SyncRoot; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new NodeKeyValueEnumerator(list, isKeys); + } + + + private class NodeKeyValueEnumerator : IEnumerator + { + private ListDictionaryInternal list; + private DictionaryNode current; + private int version; + private bool isKeys; + private bool start; + + public NodeKeyValueEnumerator(ListDictionaryInternal list, bool isKeys) + { + this.list = list; + this.isKeys = isKeys; + version = list.version; + start = true; + current = null; + } + + public Object Current + { + get + { + if (current == null) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + return isKeys ? current.key : current.value; + } + } + + public bool MoveNext() + { + if (version != list.version) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + } + if (start) + { + current = list.head; + start = false; + } + else + { + if (current != null) + { + current = current.next; + } + } + return (current != null); + } + + public void Reset() + { + if (version != list.version) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); + } + start = true; + current = null; + } + } + } + + [Serializable] + private class DictionaryNode + { + public Object key; + public Object value; + public DictionaryNode next; + } + } +} diff --git a/src/mscorlib/shared/System/Collections/ObjectModel/Collection.cs b/src/mscorlib/shared/System/Collections/ObjectModel/Collection.cs new file mode 100644 index 0000000000..75e88eccb3 --- /dev/null +++ b/src/mscorlib/shared/System/Collections/ObjectModel/Collection.cs @@ -0,0 +1,396 @@ +// 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.Collections.Generic; +using System.Diagnostics; + +namespace System.Collections.ObjectModel +{ + [Serializable] + [DebuggerTypeProxy(typeof(Mscorlib_CollectionDebugView<>))] + [DebuggerDisplay("Count = {Count}")] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class Collection<T> : IList<T>, IList, IReadOnlyList<T> + { + private IList<T> items; // Do not rename (binary serialization) + [NonSerialized] + private Object _syncRoot; + + public Collection() + { + items = new List<T>(); + } + + public Collection(IList<T> list) + { + if (list == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.list); + } + items = list; + } + + public int Count + { + get { return items.Count; } + } + + protected IList<T> Items + { + get { return items; } + } + + public T this[int index] + { + get { return items[index]; } + set + { + if (items.IsReadOnly) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + if (index < 0 || index >= items.Count) + { + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + } + + SetItem(index, value); + } + } + + public void Add(T item) + { + if (items.IsReadOnly) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + int index = items.Count; + InsertItem(index, item); + } + + public void Clear() + { + if (items.IsReadOnly) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + ClearItems(); + } + + public void CopyTo(T[] array, int index) + { + items.CopyTo(array, index); + } + + public bool Contains(T item) + { + return items.Contains(item); + } + + public IEnumerator<T> GetEnumerator() + { + return items.GetEnumerator(); + } + + public int IndexOf(T item) + { + return items.IndexOf(item); + } + + public void Insert(int index, T item) + { + if (items.IsReadOnly) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + if (index < 0 || index > items.Count) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert); + } + + InsertItem(index, item); + } + + public bool Remove(T item) + { + if (items.IsReadOnly) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + int index = items.IndexOf(item); + if (index < 0) return false; + RemoveItem(index); + return true; + } + + public void RemoveAt(int index) + { + if (items.IsReadOnly) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + if (index < 0 || index >= items.Count) + { + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + } + + RemoveItem(index); + } + + protected virtual void ClearItems() + { + items.Clear(); + } + + protected virtual void InsertItem(int index, T item) + { + items.Insert(index, item); + } + + protected virtual void RemoveItem(int index) + { + items.RemoveAt(index); + } + + protected virtual void SetItem(int index, T item) + { + items[index] = item; + } + + bool ICollection<T>.IsReadOnly + { + get + { + return items.IsReadOnly; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)items).GetEnumerator(); + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get + { + if (_syncRoot == null) + { + ICollection c = items as ICollection; + if (c != null) + { + _syncRoot = c.SyncRoot; + } + else + { + System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null); + } + } + return _syncRoot; + } + } + + void ICollection.CopyTo(Array array, int index) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (array.Rank != 1) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); + } + + if (array.GetLowerBound(0) != 0) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); + } + + if (index < 0) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if (array.Length - index < Count) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); + } + + T[] tArray = array as T[]; + if (tArray != null) + { + items.CopyTo(tArray, index); + } + else + { + // + // Catch the obvious case assignment will fail. + // We can't find all possible problems by doing the check though. + // For example, if the element type of the Array is derived from T, + // we can't figure out if we can successfully copy the element beforehand. + // + Type targetType = array.GetType().GetElementType(); + Type sourceType = typeof(T); + if (!(targetType.IsAssignableFrom(sourceType) || sourceType.IsAssignableFrom(targetType))) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + + // + // We can't cast array of value type to object[], so we don't support + // widening of primitive types here. + // + object[] objects = array as object[]; + if (objects == null) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + + int count = items.Count; + try + { + for (int i = 0; i < count; i++) + { + objects[index++] = items[i]; + } + } + catch (ArrayTypeMismatchException) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + } + } + + object IList.this[int index] + { + get { return items[index]; } + set + { + ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(value, ExceptionArgument.value); + + try + { + this[index] = (T)value; + } + catch (InvalidCastException) + { + ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T)); + } + } + } + + bool IList.IsReadOnly + { + get + { + return items.IsReadOnly; + } + } + + bool IList.IsFixedSize + { + get + { + // There is no IList<T>.IsFixedSize, so we must assume that only + // readonly collections are fixed size, if our internal item + // collection does not implement IList. Note that Array implements + // IList, and therefore T[] and U[] will be fixed-size. + IList list = items as IList; + if (list != null) + { + return list.IsFixedSize; + } + return items.IsReadOnly; + } + } + + int IList.Add(object value) + { + if (items.IsReadOnly) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(value, ExceptionArgument.value); + + try + { + Add((T)value); + } + catch (InvalidCastException) + { + ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T)); + } + + return this.Count - 1; + } + + bool IList.Contains(object value) + { + if (IsCompatibleObject(value)) + { + return Contains((T)value); + } + return false; + } + + int IList.IndexOf(object value) + { + if (IsCompatibleObject(value)) + { + return IndexOf((T)value); + } + return -1; + } + + void IList.Insert(int index, object value) + { + if (items.IsReadOnly) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(value, ExceptionArgument.value); + + try + { + Insert(index, (T)value); + } + catch (InvalidCastException) + { + ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T)); + } + } + + void IList.Remove(object value) + { + if (items.IsReadOnly) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + if (IsCompatibleObject(value)) + { + Remove((T)value); + } + } + + private static bool IsCompatibleObject(object value) + { + // Non-null values are fine. Only accept nulls if T is a class or Nullable<U>. + // Note that default(T) is not equal to null for value types except when T is Nullable<U>. + return ((value is T) || (value == null && default(T) == null)); + } + } +} diff --git a/src/mscorlib/shared/System/Collections/ObjectModel/ReadOnlyCollection.cs b/src/mscorlib/shared/System/Collections/ObjectModel/ReadOnlyCollection.cs new file mode 100644 index 0000000000..f1d2a0969b --- /dev/null +++ b/src/mscorlib/shared/System/Collections/ObjectModel/ReadOnlyCollection.cs @@ -0,0 +1,279 @@ +// 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.Collections.Generic; +using System.Diagnostics; + +namespace System.Collections.ObjectModel +{ + [Serializable] + [DebuggerTypeProxy(typeof(Mscorlib_CollectionDebugView<>))] + [DebuggerDisplay("Count = {Count}")] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class ReadOnlyCollection<T> : IList<T>, IList, IReadOnlyList<T> + { + private IList<T> list; // Do not rename (binary serialization) + [NonSerialized] + private Object _syncRoot; + + public ReadOnlyCollection(IList<T> list) + { + if (list == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.list); + } + this.list = list; + } + + public int Count + { + get { return list.Count; } + } + + public T this[int index] + { + get { return list[index]; } + } + + public bool Contains(T value) + { + return list.Contains(value); + } + + public void CopyTo(T[] array, int index) + { + list.CopyTo(array, index); + } + + public IEnumerator<T> GetEnumerator() + { + return list.GetEnumerator(); + } + + public int IndexOf(T value) + { + return list.IndexOf(value); + } + + protected IList<T> Items + { + get + { + return list; + } + } + + bool ICollection<T>.IsReadOnly + { + get { return true; } + } + + T IList<T>.this[int index] + { + get { return list[index]; } + set + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + } + + void ICollection<T>.Add(T value) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + void ICollection<T>.Clear() + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + void IList<T>.Insert(int index, T value) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + bool ICollection<T>.Remove(T value) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + return false; + } + + void IList<T>.RemoveAt(int index) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)list).GetEnumerator(); + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get + { + if (_syncRoot == null) + { + ICollection c = list as ICollection; + if (c != null) + { + _syncRoot = c.SyncRoot; + } + else + { + System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null); + } + } + return _syncRoot; + } + } + + void ICollection.CopyTo(Array array, int index) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (array.Rank != 1) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); + } + + if (array.GetLowerBound(0) != 0) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); + } + + if (index < 0) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if (array.Length - index < Count) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); + } + + T[] items = array as T[]; + if (items != null) + { + list.CopyTo(items, index); + } + else + { + // + // Catch the obvious case assignment will fail. + // We can't find all possible problems by doing the check though. + // For example, if the element type of the Array is derived from T, + // we can't figure out if we can successfully copy the element beforehand. + // + Type targetType = array.GetType().GetElementType(); + Type sourceType = typeof(T); + if (!(targetType.IsAssignableFrom(sourceType) || sourceType.IsAssignableFrom(targetType))) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + + // + // We can't cast array of value type to object[], so we don't support + // widening of primitive types here. + // + object[] objects = array as object[]; + if (objects == null) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + + int count = list.Count; + try + { + for (int i = 0; i < count; i++) + { + objects[index++] = list[i]; + } + } + catch (ArrayTypeMismatchException) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + } + } + + bool IList.IsFixedSize + { + get { return true; } + } + + bool IList.IsReadOnly + { + get { return true; } + } + + object IList.this[int index] + { + get { return list[index]; } + set + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + } + + int IList.Add(object value) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + return -1; + } + + void IList.Clear() + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + private static bool IsCompatibleObject(object value) + { + // Non-null values are fine. Only accept nulls if T is a class or Nullable<U>. + // Note that default(T) is not equal to null for value types except when T is Nullable<U>. + return ((value is T) || (value == null && default(T) == null)); + } + + bool IList.Contains(object value) + { + if (IsCompatibleObject(value)) + { + return Contains((T)value); + } + return false; + } + + int IList.IndexOf(object value) + { + if (IsCompatibleObject(value)) + { + return IndexOf((T)value); + } + return -1; + } + + void IList.Insert(int index, object value) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + void IList.Remove(object value) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + + void IList.RemoveAt(int index) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); + } + } +} diff --git a/src/mscorlib/shared/System/Convert.cs b/src/mscorlib/shared/System/Convert.cs index 576f78f1f1..02fbbdbc3e 100644 --- a/src/mscorlib/shared/System/Convert.cs +++ b/src/mscorlib/shared/System/Convert.cs @@ -84,7 +84,7 @@ namespace System // of the conversion method. // Constant representing the database null value. This value is used in - // database applications to indicate the absense of a known value. Note + // database applications to indicate the absence of a known value. Note // that Value.DBNull is NOT the same as a null object reference, which is // represented by Value.Empty. // @@ -2670,7 +2670,7 @@ namespace System /// Converts the specified string, which encodes binary data as Base64 digits, to the equivalent byte array. /// </summary> /// <param name="s">The string to convert</param> - /// <returns>The array of bytes represented by the specifed Base64 string.</returns> + /// <returns>The array of bytes represented by the specified Base64 string.</returns> public static Byte[] FromBase64String(String s) { // "s" is an unfortunate parameter name, but we need to keep it for backward compat. diff --git a/src/mscorlib/shared/System/DBNull.cs b/src/mscorlib/shared/System/DBNull.cs index 4f4d64bf66..3cee2b15c8 100644 --- a/src/mscorlib/shared/System/DBNull.cs +++ b/src/mscorlib/shared/System/DBNull.cs @@ -6,17 +6,23 @@ using System.Runtime.Serialization; namespace System { + [Serializable] public sealed class DBNull : ISerializable, IConvertible { private DBNull() { } - + + private DBNull(SerializationInfo info, StreamingContext context) + { + throw new NotSupportedException(SR.NotSupported_DBNullSerial); + } + public static readonly DBNull Value = new DBNull(); public void GetObjectData(SerializationInfo info, StreamingContext context) { - throw new PlatformNotSupportedException(); + UnitySerializationHolder.GetUnitySerializationInfo(info, UnitySerializationHolder.NullUnity); } public override string ToString() diff --git a/src/mscorlib/shared/System/DataMisalignedException.cs b/src/mscorlib/shared/System/DataMisalignedException.cs index ff5b29f1cf..d8d36b5cb7 100644 --- a/src/mscorlib/shared/System/DataMisalignedException.cs +++ b/src/mscorlib/shared/System/DataMisalignedException.cs @@ -18,19 +18,19 @@ namespace System public DataMisalignedException() : base(SR.Arg_DataMisalignedException) { - HResult = __HResults.COR_E_DATAMISALIGNED; + HResult = HResults.COR_E_DATAMISALIGNED; } public DataMisalignedException(String message) : base(message) { - HResult = __HResults.COR_E_DATAMISALIGNED; + HResult = HResults.COR_E_DATAMISALIGNED; } public DataMisalignedException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_DATAMISALIGNED; + HResult = HResults.COR_E_DATAMISALIGNED; } } } diff --git a/src/mscorlib/shared/System/DateTime.cs b/src/mscorlib/shared/System/DateTime.cs index 16a75fdfa6..e72654a8d6 100644 --- a/src/mscorlib/shared/System/DateTime.cs +++ b/src/mscorlib/shared/System/DateTime.cs @@ -129,7 +129,7 @@ namespace System private const String TicksField = "ticks"; // Do not rename (binary serialization) private const String DateDataField = "dateData"; // Do not rename (binary serialization) - // The data is stored as an unsigned 64-bit integeter + // The data is stored as an unsigned 64-bit integer // Bits 01-62: The value of 100-nanosecond ticks where 0 represents 1/1/0001 12:00am, up until the value // 12/31/9999 23:59:59.9999999 // Bits 63-64: A four-state value that describes the DateTimeKind value of the date time, with a 2nd @@ -1025,11 +1025,7 @@ namespace System } // Returns a DateTime representing the current date and time. The - // resolution of the returned value depends on the system timer. For - // Windows NT 3.5 and later the timer resolution is approximately 10ms, - // for Windows NT 3.1 it is approximately 16ms, and for Windows 95 and 98 - // it is approximately 55ms. - // + // resolution of the returned value depends on the system timer. public static DateTime Now { get diff --git a/src/mscorlib/shared/System/DateTimeOffset.cs b/src/mscorlib/shared/System/DateTimeOffset.cs index ab35bdb0fe..e3366e2a7d 100644 --- a/src/mscorlib/shared/System/DateTimeOffset.cs +++ b/src/mscorlib/shared/System/DateTimeOffset.cs @@ -129,11 +129,7 @@ namespace System } // Returns a DateTimeOffset representing the current date and time. The - // resolution of the returned value depends on the system timer. For - // Windows NT 3.5 and later the timer resolution is approximately 10ms, - // for Windows NT 3.1 it is approximately 16ms, and for Windows 95 and 98 - // it is approximately 55ms. - // + // resolution of the returned value depends on the system timer. public static DateTimeOffset Now { get diff --git a/src/mscorlib/shared/System/DefaultBinder.cs b/src/mscorlib/shared/System/DefaultBinder.cs index 9adf702a02..b6c12b121d 100644 --- a/src/mscorlib/shared/System/DefaultBinder.cs +++ b/src/mscorlib/shared/System/DefaultBinder.cs @@ -42,7 +42,7 @@ namespace System state = null; -#region Map named parameters to candidate parameter postions +#region Map named parameters to candidate parameter positions // We are creating an paramOrder array to act as a mapping // between the order of the args and the actual order of the // parameters in the method. This order may differ because @@ -530,7 +530,7 @@ namespace System for (i = 0; i < types.Length; i++) { realTypes[i] = types[i].UnderlyingSystemType; - if (!(realTypes[i].IsRuntimeImplemented())) + if (!(realTypes[i].IsRuntimeImplemented() || realTypes[i] is SignatureType)) throw new ArgumentException(SR.Arg_MustBeType, nameof(types)); } types = realTypes; @@ -552,19 +552,30 @@ namespace System for (j = 0; j < types.Length; j++) { Type pCls = par[j].ParameterType; - if (pCls == types[j]) + if (types[j].MatchesParameterTypeExactly(par[j])) continue; if (pCls == typeof(object)) continue; + + Type type = types[j]; + if (type is SignatureType signatureType) + { + if (!(candidates[i] is MethodInfo methodInfo)) + break; + type = signatureType.TryResolveAgainstGenericMethod(methodInfo); + if (type == null) + break; + } + if (pCls.IsPrimitive) { - if (!(types[j].UnderlyingSystemType.IsRuntimeImplemented()) || - !CanChangePrimitive(types[j].UnderlyingSystemType, pCls.UnderlyingSystemType)) + if (!(type.UnderlyingSystemType.IsRuntimeImplemented()) || + !CanChangePrimitive(type.UnderlyingSystemType, pCls.UnderlyingSystemType)) break; } else { - if (!pCls.IsAssignableFrom(types[j])) + if (!pCls.IsAssignableFrom(type)) break; } } @@ -918,11 +929,22 @@ namespace System if (c1 == c2) return 0; - if (c1 == t) - return 1; + if (t is SignatureType signatureType) + { + if (signatureType.MatchesExactly(c1)) + return 1; - if (c2 == t) - return 2; + if (signatureType.MatchesExactly(c2)) + return 2; + } + else + { + if (c1 == t) + return 1; + + if (c2 == t) + return 2; + } bool c1FromC2; bool c2FromC1; diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs index 5292551314..a74125a35a 100644 --- a/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs +++ b/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs @@ -87,7 +87,8 @@ namespace System.Diagnostics.Tracing private long m_allKeywordMask; // Match all keyword private List<SessionInfo> m_liveSessions; // current live sessions (Tuple<sessionIdBit, etwSessionId>) private bool m_enabled; // Enabled flag from Trace callback - private Guid m_providerId; // Control Guid + private string m_providerName; // Control name + private Guid m_providerId; // Control Guid internal bool m_disposed; // when true provider has unregistered [ThreadStatic] @@ -140,16 +141,19 @@ namespace System.Diagnostics.Tracing // <SatisfiesLinkDemand Name="Win32Exception..ctor(System.Int32)" /> // <ReferencesCritical Name="Method: EtwEnableCallBack(Guid&, Int32, Byte, Int64, Int64, Void*, Void*):Void" Ring="1" /> // </SecurityKernel> - internal unsafe void Register(Guid providerGuid) + internal unsafe void Register(EventSource eventSource) { - m_providerId = providerGuid; uint status; m_etwCallback = new UnsafeNativeMethods.ManifestEtw.EtwEnableCallback(EtwEnableCallBack); - status = EventRegister(ref m_providerId, m_etwCallback); + status = EventRegister(eventSource, m_etwCallback); if (status != 0) { - throw new ArgumentException(Win32Native.GetMessage(unchecked((int)status))); +#if PLATFORM_WINDOWS + throw new ArgumentException(Interop.Kernel32.GetMessage(unchecked((int)status))); +#else + throw new ArgumentException(Convert.ToString(unchecked((int)status))); +#endif } } @@ -488,7 +492,7 @@ namespace System.Diagnostics.Tracing // at least some of the time. // Determine our session from what is in the registry. - string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerId + "}"; + string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerName + "}"; if (System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8) regKey = @"Software" + @"\Wow6432Node" + regKey; else @@ -563,7 +567,7 @@ namespace System.Diagnostics.Tracing if (filterData == null) { #if (!ES_BUILD_PCL && !ES_BUILD_PN && PLATFORM_WINDOWS) - string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerId + "}"; + string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerName + "}"; if (System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8) regKey = @"HKEY_LOCAL_MACHINE\Software" + @"\Wow6432Node" + regKey; else @@ -1184,11 +1188,12 @@ namespace System.Diagnostics.Tracing // These are look-alikes to the Manifest based ETW OS APIs that have been shimmed to work // either with Manifest ETW or Classic ETW (if Manifest based ETW is not available). - private unsafe uint EventRegister(ref Guid providerId, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback) + private unsafe uint EventRegister(EventSource eventSource, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback) { - m_providerId = providerId; + m_providerName = eventSource.Name; + m_providerId = eventSource.Guid; m_etwCallback = enableCallback; - return m_eventProvider.EventRegister(ref providerId, enableCallback, null, ref m_regHandle); + return m_eventProvider.EventRegister(eventSource, enableCallback, null, ref m_regHandle); } private uint EventUnregister(long registrationHandle) @@ -1221,11 +1226,12 @@ namespace System.Diagnostics.Tracing { // Register an event provider. unsafe uint IEventProvider.EventRegister( - ref Guid providerId, + EventSource eventSource, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback, void* callbackContext, ref long registrationHandle) { + Guid providerId = eventSource.Guid; return UnsafeNativeMethods.ManifestEtw.EventRegister( ref providerId, enableCallback, @@ -1278,7 +1284,7 @@ namespace System.Diagnostics.Tracing internal sealed class NoOpEventProvider : IEventProvider { unsafe uint IEventProvider.EventRegister( - ref Guid providerId, + EventSource eventSource, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback, void* callbackContext, ref long registrationHandle) diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/EventSource.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/EventSource.cs index d8eec73931..4e436e7baf 100644 --- a/src/mscorlib/shared/System/Diagnostics/Tracing/EventSource.cs +++ b/src/mscorlib/shared/System/Diagnostics/Tracing/EventSource.cs @@ -265,7 +265,7 @@ namespace System.Diagnostics.Tracing public Guid Guid { get { return m_guid; } } /// <summary> - /// Returns true if the eventSource has been enabled at all. This is the prefered test + /// Returns true if the eventSource has been enabled at all. This is the preferred test /// to be performed before a relatively expensive EventSource operation. /// </summary> [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")] @@ -572,11 +572,11 @@ namespace System.Diagnostics.Tracing /// <summary> /// EventSources can have arbitrary string key-value pairs associated with them called Traits. /// These traits are not interpreted by the EventSource but may be interpreted by EventListeners - /// (e.g. like the built in ETW listener). These traits are specififed at EventSource + /// (e.g. like the built in ETW listener). These traits are specified at EventSource /// construction time and can be retrieved by using this GetTrait API. /// </summary> /// <param name="key">The key to look up in the set of key-value pairs passed to the EventSource constructor</param> - /// <returns>The value string associated iwth key. Will return null if there is no such key.</returns> + /// <returns>The value string associated with key. Will return null if there is no such key.</returns> public string GetTrait(string key) { if (m_traits != null) @@ -661,7 +661,7 @@ namespace System.Diagnostics.Tracing /// /// Also specify a list of key-value pairs called traits (you must pass an even number of strings). /// The first string is the key and the second is the value. These are not interpreted by EventSource - /// itself but may be interprated the listeners. Can be fetched with GetTrait(string). + /// itself but may be interpreted the listeners. Can be fetched with GetTrait(string). /// </summary> /// <param name="settings">See EventSourceSettings for more.</param> /// <param name="traits">A collection of key-value strings (must be an even number).</param> @@ -1560,7 +1560,7 @@ namespace System.Diagnostics.Tracing // Register the provider with ETW var provider = new OverideEventProvider(this); - provider.Register(eventSourceGuid); + provider.Register(this); #endif // Add the eventSource to the global (weak) list. // This also sets m_id, which is the index in the list. @@ -4942,10 +4942,10 @@ namespace System.Diagnostics.Tracing get { // For contract based events we create the list lazily. - if (m_payloadNames == null) + // You can have m_payloadNames be null in the TraceLogging case (EventID < 0) so only + // do the lazy init if you know it is contract based (EventID >= 0) + if (EventId >= 0 && m_payloadNames == null) { - // Self described events are identified by id -1. - Debug.Assert(EventId != -1); var names = new List<string>(); foreach (var parameter in m_eventSource.m_eventData[EventId].Parameters) @@ -5521,7 +5521,7 @@ namespace System.Diagnostics.Tracing /// Returns a session mask representing all sessions in which the activity /// associated with the current thread is allowed through the activity filter. /// If 'triggeringEvent' is true the event MAY be a triggering event. Ideally - /// most of the time this is false as you can guarentee this event is NOT a + /// most of the time this is false as you can guarantee this event is NOT a /// triggering event. If 'triggeringEvent' is true, then it checks the /// 'EventSource' and 'eventID' of the event being logged to see if it is actually /// a trigger. If so it activates the current activity. @@ -5622,7 +5622,7 @@ namespace System.Diagnostics.Tracing /// <summary> /// For the EventListener/EtwSession associated with 'filterList', add 'childActivityid' /// to list of active activities IF 'currentActivityId' is also active. Passing in a null - /// value for 'currentActivityid' is an indication tha caller has already verified + /// value for 'currentActivityid' is an indication the caller has already verified /// that the current activity is active. /// </summary> unsafe public static void FlowActivityIfNeeded(ActivityFilter filterList, Guid* currentActivityId, Guid* childActivityID) @@ -5642,7 +5642,7 @@ namespace System.Diagnostics.Tracing // make sure current activity is still in the set: activeActivities[EventSource.InternalCurrentThreadActivityId] = Environment.TickCount; } - // add child activity to list of actives + // add child activity to list of activities activeActivities[*childActivityID] = Environment.TickCount; } @@ -6070,11 +6070,11 @@ namespace System.Diagnostics.Tracing /// (m_EventEnabled) for a particular EventSource X EventListener tuple /// /// Thus a single EventListener may have many EventDispatchers (one for every EventSource - /// that that EventListener has activate) and a Single EventSource may also have many + /// that EventListener has activate) and a Single EventSource may also have many /// event Dispatchers (one for every EventListener that has activated it). /// /// Logically a particular EventDispatcher belongs to exactly one EventSource and exactly - /// one EventListener (alhtough EventDispatcher does not 'remember' the EventSource it is + /// one EventListener (although EventDispatcher does not 'remember' the EventSource it is /// associated with. /// </summary> internal class EventDispatcher diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/IEventProvider.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/IEventProvider.cs index 71a2fe4d44..966dac2fef 100644 --- a/src/mscorlib/shared/System/Diagnostics/Tracing/IEventProvider.cs +++ b/src/mscorlib/shared/System/Diagnostics/Tracing/IEventProvider.cs @@ -15,7 +15,7 @@ namespace System.Diagnostics.Tracing { // Register an event provider. unsafe uint EventRegister( - ref Guid providerId, + EventSource eventSource, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback, void* callbackContext, ref long registrationHandle); diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs index 38c1767462..acc3eeb233 100644 --- a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs +++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs @@ -77,8 +77,8 @@ namespace System.Diagnostics.Tracing /// <summary> /// Writes a Start event with the specified name and data. If the start event is not active (because the provider - /// is not on or keyword-level indiates the event is off, then the returned activity is simply the 'this' poitner - /// and it is effectively like the Start d + /// is not on or keyword-level indicates the event is off, then the returned activity is simply the 'this' pointer + /// and it is effectively like start did not get called. /// /// A new activityID GUID is generated and the returned /// EventSourceActivity remembers this activity and will mark every event (including the start stop and any writes) @@ -139,7 +139,7 @@ namespace System.Diagnostics.Tracing } /// <summary> /// Used if you wish to use the non-default stop name (which is the start name with Start replace with 'Stop') - /// This can be useful to indicate unusual ways of stoping (but it is still STRONGLY recommeded that + /// This can be useful to indicate unusual ways of stopping (but it is still STRONGLY recommended that /// you start with the same prefix used for the start event and you end with the 'Stop' suffix. /// </summary> public void Stop<T>(string eventName) @@ -149,7 +149,7 @@ namespace System.Diagnostics.Tracing } /// <summary> /// Used if you wish to use the non-default stop name (which is the start name with Start replace with 'Stop') - /// This can be useful to indicate unusual ways of stoping (but it is still STRONGLY recommeded that + /// This can be useful to indicate unusual ways of stopping (but it is still STRONGLY recommended that /// you start with the same prefix used for the start event and you end with the 'Stop' suffix. /// </summary> public void Stop<T>(string eventName, T data) @@ -158,7 +158,7 @@ namespace System.Diagnostics.Tracing } /// <summary> - /// Writes an event associated with this activity to the eventSource associted with this activity. + /// Writes an event associated with this activity to the eventSource associated with this activity. /// May only be called when the activity is in the Started state. /// </summary> /// <param name="eventName"> diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs index 529948daf8..cc416a96d9 100644 --- a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs +++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs @@ -182,7 +182,7 @@ namespace System.Diagnostics.Tracing /// <summary> /// Core type. - /// Special case: Struct indicates that this field plus the the + /// Special case: Struct indicates that this field plus the /// subsequent N logical fields are to be considered as one logical /// field (i.e. a nested structure). The OutType is used to encode N. /// The maximum value for N is 127. This field has no payload by diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs index aba1671ae2..a1218d1a7f 100644 --- a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs +++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs @@ -80,7 +80,7 @@ namespace System.Diagnostics.Tracing /// /// Also specify a list of key-value pairs called traits (you must pass an even number of strings). /// The first string is the key and the second is the value. These are not interpreted by EventSource - /// itself but may be interprated the listeners. Can be fetched with GetTrait(string). + /// itself but may be interpreted the listeners. Can be fetched with GetTrait(string). /// </summary> /// <param name="eventSourceName"> /// The name of the event source. Must not be null. @@ -442,7 +442,10 @@ namespace System.Diagnostics.Tracing var pinCount = eventTypes.pinCount; var scratch = stackalloc byte[eventTypes.scratchSize]; var descriptors = stackalloc EventData[eventTypes.dataCount + 3]; + var pins = stackalloc GCHandle[pinCount]; + for (int i = 0; i < pinCount; i++) + pins[i] = default(GCHandle); fixed (byte* pMetadata0 = this.providerMetadata, @@ -619,7 +622,10 @@ namespace System.Diagnostics.Tracing var pinCount = eventTypes.pinCount; var scratch = stackalloc byte[eventTypes.scratchSize]; var descriptors = stackalloc EventData[eventTypes.dataCount + 3]; + var pins = stackalloc GCHandle[pinCount]; + for (int i = 0; i < pinCount; i++) + pins[i] = default(GCHandle); fixed (byte* pMetadata0 = this.providerMetadata, @@ -744,9 +750,9 @@ namespace System.Diagnostics.Tracing { DataCollector.ThreadInstance.Disable(); - for (int i = 0; i != cPins; i++) + for (int i = 0; i < cPins; i++) { - if (IntPtr.Zero != (IntPtr)pPins[i]) + if (pPins[i].IsAllocated) { pPins[i].Free(); } diff --git a/src/mscorlib/shared/System/DivideByZeroException.cs b/src/mscorlib/shared/System/DivideByZeroException.cs index 0fad2f8d56..ad74bde0fd 100644 --- a/src/mscorlib/shared/System/DivideByZeroException.cs +++ b/src/mscorlib/shared/System/DivideByZeroException.cs @@ -20,19 +20,19 @@ namespace System public DivideByZeroException() : base(SR.Arg_DivideByZero) { - HResult = __HResults.COR_E_DIVIDEBYZERO; + HResult = HResults.COR_E_DIVIDEBYZERO; } public DivideByZeroException(String message) : base(message) { - HResult = __HResults.COR_E_DIVIDEBYZERO; + HResult = HResults.COR_E_DIVIDEBYZERO; } public DivideByZeroException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_DIVIDEBYZERO; + HResult = HResults.COR_E_DIVIDEBYZERO; } protected DivideByZeroException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/DllNotFoundException.cs b/src/mscorlib/shared/System/DllNotFoundException.cs index 8c69e45eda..82d5bdd0c5 100644 --- a/src/mscorlib/shared/System/DllNotFoundException.cs +++ b/src/mscorlib/shared/System/DllNotFoundException.cs @@ -21,19 +21,19 @@ namespace System public DllNotFoundException() : base(SR.Arg_DllNotFoundException) { - HResult = __HResults.COR_E_DLLNOTFOUND; + HResult = HResults.COR_E_DLLNOTFOUND; } public DllNotFoundException(String message) : base(message) { - HResult = __HResults.COR_E_DLLNOTFOUND; + HResult = HResults.COR_E_DLLNOTFOUND; } public DllNotFoundException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_DLLNOTFOUND; + HResult = HResults.COR_E_DLLNOTFOUND; } protected DllNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/Double.cs b/src/mscorlib/shared/System/Double.cs new file mode 100644 index 0000000000..7ee52027f3 --- /dev/null +++ b/src/mscorlib/shared/System/Double.cs @@ -0,0 +1,451 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: A representation of an IEEE double precision +** floating point number. +** +** +===========================================================*/ + +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Double : IComparable, IConvertible, IFormattable, IComparable<Double>, IEquatable<Double> + { + private double m_value; // Do not rename (binary serialization) + + // + // Public Constants + // + public const double MinValue = -1.7976931348623157E+308; + public const double MaxValue = 1.7976931348623157E+308; + + // Note Epsilon should be a double whose hex representation is 0x1 + // on little endian machines. + public const double Epsilon = 4.9406564584124654E-324; + public const double NegativeInfinity = (double)-1.0 / (double)(0.0); + public const double PositiveInfinity = (double)1.0 / (double)(0.0); + public const double NaN = (double)0.0 / (double)0.0; + + // We use this explicit definition to avoid the confusion between 0.0 and -0.0. + internal const double NegativeZero = -0.0; + + /// <summary>Determines whether the specified value is finite (zero, subnormal, or normal).</summary> + [Pure] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsFinite(double d) + { + var bits = BitConverter.DoubleToInt64Bits(d); + return (bits & 0x7FFFFFFFFFFFFFFF) < 0x7FF0000000000000; + } + + /// <summary>Determines whether the specified value is infinite.</summary> + [Pure] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsInfinity(double d) + { + var bits = BitConverter.DoubleToInt64Bits(d); + return (bits & 0x7FFFFFFFFFFFFFFF) == 0x7FF0000000000000; + } + + /// <summary>Determines whether the specified value is NaN.</summary> + [Pure] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsNaN(double d) + { + var bits = BitConverter.DoubleToInt64Bits(d); + return (bits & 0x7FFFFFFFFFFFFFFF) > 0x7FF0000000000000; + } + + /// <summary>Determines whether the specified value is negative.</summary> + [Pure] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsNegative(double d) + { + var bits = unchecked((ulong)BitConverter.DoubleToInt64Bits(d)); + return (bits & 0x8000000000000000) == 0x8000000000000000; + } + + /// <summary>Determines whether the specified value is negative infinity.</summary> + [Pure] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNegativeInfinity(double d) + { + return (d == double.NegativeInfinity); + } + + /// <summary>Determines whether the specified value is normal.</summary> + [Pure] + [NonVersionable] + // This is probably not worth inlining, it has branches and should be rarely called + public unsafe static bool IsNormal(double d) + { + var bits = BitConverter.DoubleToInt64Bits(d); + bits &= 0x7FFFFFFFFFFFFFFF; + return (bits < 0x7FF0000000000000) && (bits != 0) && ((bits & 0x7FF0000000000000) != 0); + } + + /// <summary>Determines whether the specified value is positive infinity.</summary> + [Pure] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsPositiveInfinity(double d) + { + return (d == double.PositiveInfinity); + } + + /// <summary>Determines whether the specified value is subnormal.</summary> + [Pure] + [NonVersionable] + // This is probably not worth inlining, it has branches and should be rarely called + public unsafe static bool IsSubnormal(double d) + { + var bits = BitConverter.DoubleToInt64Bits(d); + bits &= 0x7FFFFFFFFFFFFFFF; + return (bits < 0x7FF0000000000000) && (bits != 0) && ((bits & 0x7FF0000000000000) == 0); + } + + // Compares this object to another object, returning an instance of System.Relation. + // Null is considered less than any instance. + // + // If object is not of type Double, this method throws an ArgumentException. + // + // Returns a value less than zero if this object + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + if (value is Double) + { + double d = (double)value; + if (m_value < d) return -1; + if (m_value > d) return 1; + if (m_value == d) return 0; + + // At least one of the values is NaN. + if (IsNaN(m_value)) + return (IsNaN(d) ? 0 : -1); + else + return 1; + } + throw new ArgumentException(SR.Arg_MustBeDouble); + } + + public int CompareTo(Double value) + { + if (m_value < value) return -1; + if (m_value > value) return 1; + if (m_value == value) return 0; + + // At least one of the values is NaN. + if (IsNaN(m_value)) + return (IsNaN(value) ? 0 : -1); + else + return 1; + } + + // True if obj is another Double with the same value as the current instance. This is + // a method of object equality, that only returns true if obj is also a double. + public override bool Equals(Object obj) + { + if (!(obj is Double)) + { + return false; + } + double temp = ((Double)obj).m_value; + // This code below is written this way for performance reasons i.e the != and == check is intentional. + if (temp == m_value) + { + return true; + } + return IsNaN(temp) && IsNaN(m_value); + } + + [NonVersionable] + public static bool operator ==(Double left, Double right) + { + return left == right; + } + + [NonVersionable] + public static bool operator !=(Double left, Double right) + { + return left != right; + } + + [NonVersionable] + public static bool operator <(Double left, Double right) + { + return left < right; + } + + [NonVersionable] + public static bool operator >(Double left, Double right) + { + return left > right; + } + + [NonVersionable] + public static bool operator <=(Double left, Double right) + { + return left <= right; + } + + [NonVersionable] + public static bool operator >=(Double left, Double right) + { + return left >= right; + } + + public bool Equals(Double obj) + { + if (obj == m_value) + { + return true; + } + return IsNaN(obj) && IsNaN(m_value); + } + + //The hashcode for a double is the absolute value of the integer representation + //of that double. + // + public unsafe override int GetHashCode() + { + double d = m_value; + if (d == 0) + { + // Ensure that 0 and -0 have the same hash code + return 0; + } + long value = *(long*)(&d); + return unchecked((int)value) ^ ((int)(value >> 32)); + } + + public override String ToString() + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatDouble(m_value, null, NumberFormatInfo.CurrentInfo); + } + + public String ToString(String format) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatDouble(m_value, format, NumberFormatInfo.CurrentInfo); + } + + public String ToString(IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatDouble(m_value, null, NumberFormatInfo.GetInstance(provider)); + } + + public String ToString(String format, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatDouble(m_value, format, NumberFormatInfo.GetInstance(provider)); + } + + public static double Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseDouble(s.AsReadOnlySpan(), NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo); + } + + public static double Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseDouble(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo); + } + + public static double Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseDouble(s.AsReadOnlySpan(), NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.GetInstance(provider)); + } + + public static double Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseDouble(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider)); + } + + // Parses a double from a String in the given style. If + // a NumberFormatInfo isn't specified, the current culture's + // NumberFormatInfo is assumed. + // + // This method will not throw an OverflowException, but will return + // PositiveInfinity or NegativeInfinity for a number that is too + // large or too small. + + public static double Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return Number.ParseDouble(s, style, NumberFormatInfo.GetInstance(provider)); + } + + + + public static bool TryParse(String s, out double result) + { + if (s == null) + { + result = 0; + return false; + } + + return TryParse(s.AsReadOnlySpan(), NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out double result) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + + if (s == null) + { + result = 0; + return false; + } + + return TryParse(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result); + } + + public static bool TryParse(ReadOnlySpan<char> s, out double result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + private static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out double result) + { + bool success = Number.TryParseDouble(s, style, info, out result); + if (!success) + { + ReadOnlySpan<char> sTrim = StringSpanHelpers.Trim(s); + if (StringSpanHelpers.Equals(sTrim, info.PositiveInfinitySymbol)) + { + result = PositiveInfinity; + } + else if (StringSpanHelpers.Equals(sTrim, info.NegativeInfinitySymbol)) + { + result = NegativeInfinity; + } + else if (StringSpanHelpers.Equals(sTrim, info.NaNSymbol)) + { + result = NaN; + } + else + { + return false; // We really failed + } + } + return true; + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.Double; + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Double", "Char")); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return m_value; + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Double", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/src/mscorlib/shared/System/DuplicateWaitObjectException.cs b/src/mscorlib/shared/System/DuplicateWaitObjectException.cs index 7eadead8c7..95bdedd526 100644 --- a/src/mscorlib/shared/System/DuplicateWaitObjectException.cs +++ b/src/mscorlib/shared/System/DuplicateWaitObjectException.cs @@ -37,25 +37,25 @@ namespace System public DuplicateWaitObjectException() : base(DuplicateWaitObjectMessage) { - HResult = __HResults.COR_E_DUPLICATEWAITOBJECT; + HResult = HResults.COR_E_DUPLICATEWAITOBJECT; } public DuplicateWaitObjectException(String parameterName) : base(DuplicateWaitObjectMessage, parameterName) { - HResult = __HResults.COR_E_DUPLICATEWAITOBJECT; + HResult = HResults.COR_E_DUPLICATEWAITOBJECT; } public DuplicateWaitObjectException(String parameterName, String message) : base(message, parameterName) { - HResult = __HResults.COR_E_DUPLICATEWAITOBJECT; + HResult = HResults.COR_E_DUPLICATEWAITOBJECT; } public DuplicateWaitObjectException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_DUPLICATEWAITOBJECT; + HResult = HResults.COR_E_DUPLICATEWAITOBJECT; } protected DuplicateWaitObjectException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/EntryPointNotFoundException.cs b/src/mscorlib/shared/System/EntryPointNotFoundException.cs index 0b881cec05..e62ca0e11d 100644 --- a/src/mscorlib/shared/System/EntryPointNotFoundException.cs +++ b/src/mscorlib/shared/System/EntryPointNotFoundException.cs @@ -21,19 +21,19 @@ namespace System public EntryPointNotFoundException() : base(SR.Arg_EntryPointNotFoundException) { - HResult = __HResults.COR_E_ENTRYPOINTNOTFOUND; + HResult = HResults.COR_E_ENTRYPOINTNOTFOUND; } public EntryPointNotFoundException(String message) : base(message) { - HResult = __HResults.COR_E_ENTRYPOINTNOTFOUND; + HResult = HResults.COR_E_ENTRYPOINTNOTFOUND; } public EntryPointNotFoundException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_ENTRYPOINTNOTFOUND; + HResult = HResults.COR_E_ENTRYPOINTNOTFOUND; } protected EntryPointNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/ExecutionEngineException.cs b/src/mscorlib/shared/System/ExecutionEngineException.cs index b89f6d9a8d..c33435875e 100644 --- a/src/mscorlib/shared/System/ExecutionEngineException.cs +++ b/src/mscorlib/shared/System/ExecutionEngineException.cs @@ -26,19 +26,19 @@ namespace System public ExecutionEngineException() : base(SR.Arg_ExecutionEngineException) { - HResult = __HResults.COR_E_EXECUTIONENGINE; + HResult = HResults.COR_E_EXECUTIONENGINE; } public ExecutionEngineException(String message) : base(message) { - HResult = __HResults.COR_E_EXECUTIONENGINE; + HResult = HResults.COR_E_EXECUTIONENGINE; } public ExecutionEngineException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_EXECUTIONENGINE; + HResult = HResults.COR_E_EXECUTIONENGINE; } } } diff --git a/src/mscorlib/shared/System/FieldAccessException.cs b/src/mscorlib/shared/System/FieldAccessException.cs index b56d749771..883bbd8bc2 100644 --- a/src/mscorlib/shared/System/FieldAccessException.cs +++ b/src/mscorlib/shared/System/FieldAccessException.cs @@ -18,19 +18,19 @@ namespace System public FieldAccessException() : base(SR.Arg_FieldAccessException) { - HResult = __HResults.COR_E_FIELDACCESS; + HResult = HResults.COR_E_FIELDACCESS; } public FieldAccessException(String message) : base(message) { - HResult = __HResults.COR_E_FIELDACCESS; + HResult = HResults.COR_E_FIELDACCESS; } public FieldAccessException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_FIELDACCESS; + HResult = HResults.COR_E_FIELDACCESS; } protected FieldAccessException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/FormatException.cs b/src/mscorlib/shared/System/FormatException.cs index 9baaac2353..4af45cdd94 100644 --- a/src/mscorlib/shared/System/FormatException.cs +++ b/src/mscorlib/shared/System/FormatException.cs @@ -20,19 +20,19 @@ namespace System public FormatException() : base(SR.Arg_FormatException) { - HResult = __HResults.COR_E_FORMAT; + HResult = HResults.COR_E_FORMAT; } public FormatException(String message) : base(message) { - HResult = __HResults.COR_E_FORMAT; + HResult = HResults.COR_E_FORMAT; } public FormatException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_FORMAT; + HResult = HResults.COR_E_FORMAT; } protected FormatException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/Globalization/DateTimeFormatInfo.cs b/src/mscorlib/shared/System/Globalization/DateTimeFormatInfo.cs index 5d3239ebfc..9f8c072c93 100644 --- a/src/mscorlib/shared/System/Globalization/DateTimeFormatInfo.cs +++ b/src/mscorlib/shared/System/Globalization/DateTimeFormatInfo.cs @@ -31,7 +31,7 @@ namespace System.Globalization // This is an internal flag. // // This flag is different from MonthNameStyles because this flag - // can be expanded to accomodate parsing behaviors like CJK month names + // can be expanded to accommodate parsing behaviors like CJK month names // or alternative month names, etc. [Flags] @@ -2645,7 +2645,7 @@ namespace System.Globalization { String str; // We have to call public methods here to work with inherited DTFI. - // Insert the month name first, so that they are at the front of abbrevaited + // Insert the month name first, so that they are at the front of abbreviated // month names. str = InvariantInfo.GetMonthName(i); InsertHash(temp, str, TokenType.MonthToken, i); @@ -2688,7 +2688,7 @@ namespace System.Globalization String str; //str = internalGetMonthName(i, MonthNameStyles.Regular, false); // We have to call public methods here to work with inherited DTFI. - // Insert the month name first, so that they are at the front of abbrevaited + // Insert the month name first, so that they are at the front of abbreviated // month names. str = GetMonthName(i); if (str.Length > 0) diff --git a/src/mscorlib/shared/System/Globalization/DateTimeParse.cs b/src/mscorlib/shared/System/Globalization/DateTimeParse.cs index 910fbf2ff0..f821a95412 100644 --- a/src/mscorlib/shared/System/Globalization/DateTimeParse.cs +++ b/src/mscorlib/shared/System/Globalization/DateTimeParse.cs @@ -81,23 +81,23 @@ namespace System { if (s == null) { - result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(s)); + result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), null, nameof(s)); return false; } if (format == null) { - result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(format)); + result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), null, nameof(format)); return false; } if (s.Length == 0) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } if (format.Length == 0) { - result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier), null); return false; } @@ -177,24 +177,24 @@ namespace System { if (s == null) { - result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(s)); + result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), null, nameof(s)); return false; } if (formats == null) { - result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(formats)); + result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), null, nameof(formats)); return false; } if (s.Length == 0) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } if (formats.Length == 0) { - result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier), null); return false; } @@ -208,7 +208,7 @@ namespace System { if (formats[i] == null || formats[i].Length == 0) { - result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier), null); return false; } // Create a new result each time to ensure the runs are independent. Carry through @@ -223,7 +223,7 @@ namespace System return (true); } } - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return (false); } @@ -605,13 +605,13 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if ((result.flags & ParseFlags.TimeZoneUsed) != 0) { // Should not have two timezone offsets. - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } result.flags |= ParseFlags.TimeZoneUsed; if (!ParseTimeZone(ref str, ref result.timeZoneOffset)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } } @@ -657,7 +657,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, case TokenType.YearNumberToken: if (raw.numCount == 3 || tokenValue == -1) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); LexTraceExit("0010", dps); return false; } @@ -724,7 +724,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, } else { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); LexTraceExit("0030 (TM.AM/TM.PM Happened more than 1x)", dps); } break; @@ -737,7 +737,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, case TokenType.SEP_Time: if (!raw.hasSameDateAndTimeSeparators) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); LexTraceExit("0040 (Invalid separator after number)", dps); return false; } @@ -775,7 +775,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, break; default: // Invalid separator after number number. - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); LexTraceExit("0040 (Invalid separator after number)", dps); return false; } @@ -785,7 +785,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, LexTraceExit("0050 (success)", dps); return true; } - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); LexTraceExit("0060", dps); return false; } @@ -818,7 +818,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, } else { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); break; } if (dps == DS.T_NNt || dps == DS.T_Nt) @@ -873,7 +873,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, } catch (ArgumentOutOfRangeException e) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", e); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), e); LexTraceExit("0075 (Calendar.ToFourDigitYear failed)", dps); return false; } @@ -897,7 +897,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, break; default: // Invalid separator after number number. - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); LexTraceExit("0080", dps); return false; } @@ -935,7 +935,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, goto default; default: // Invalid separator after number number. - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); LexTraceExit("0090", dps); return false; } @@ -943,7 +943,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, else { // Invalid separator after number number. - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); LexTraceExit("0100", dps); return false; } @@ -984,7 +984,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, break; default: // Invalid separator after number number. - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); LexTraceExit("0110", dps); return false; } @@ -1001,7 +1001,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, } else { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); LexTraceExit("0120 (DayOfWeek seen more than 1x)", dps); return false; } @@ -1026,7 +1026,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, case TokenType.SEP_Time: if (!raw.hasSameDateAndTimeSeparators) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); LexTraceExit("0130 (Invalid separator after month name)", dps); return false; } @@ -1052,7 +1052,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, break; default: //Invalid separator after month name - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); LexTraceExit("0130 (Invalid separator after month name)", dps); return false; } @@ -1060,7 +1060,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, } else { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); LexTraceExit("0140 (MonthToken seen more than 1x)", dps); return false; } @@ -1073,7 +1073,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, } else { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); LexTraceExit("0150 (EraToken seen when result.era already set)", dps); return false; } @@ -1089,7 +1089,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, } else { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); LexTraceExit("0160 (JapaneseEraToken seen when result.era already set)", dps); return false; } @@ -1104,7 +1104,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, } else { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); LexTraceExit("0170 (TEraToken seen when result.era already set)", dps); return false; } @@ -1118,7 +1118,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if ((result.flags & ParseFlags.TimeZoneUsed) != 0) { // Should not have two timezone offsets. - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); LexTraceExit("0180 (seen GMT or Z more than 1x)", dps); return false; } @@ -1142,7 +1142,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, } else { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); LexTraceExit("0190 (AM/PM timeMark already set)", dps); return false; } @@ -1150,7 +1150,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, case TokenType.UnknownToken: if (Char.IsLetter(str.m_current)) { - result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_UnknowDateTimeWord", str.Index); + result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_UnknowDateTimeWord), str.Index); LexTraceExit("0200", dps); return (false); } @@ -1180,7 +1180,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, return true; } - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); LexTraceExit("0240", dps); return false; } @@ -1573,7 +1573,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if ((result.flags & ParseFlags.HaveDate) != 0) { // Multiple dates in the input string - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -1585,7 +1585,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, int order; if (!GetMonthDayOrder(dtfi.MonthDayPattern, dtfi, out order)) { - result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.MonthDayPattern); + result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDatePattern), dtfi.MonthDayPattern); return false; } @@ -1606,7 +1606,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, return true; } } - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -1616,7 +1616,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if ((result.flags & ParseFlags.HaveDate) != 0) { // Multiple dates in the input string - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -1627,7 +1627,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, int order; if (!GetYearMonthDayOrder(dtfi.ShortDatePattern, dtfi, out order)) { - result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.ShortDatePattern); + result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDatePattern), dtfi.ShortDatePattern); return false; } int year; @@ -1664,7 +1664,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, return true; } } - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -1673,7 +1673,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if ((result.flags & ParseFlags.HaveDate) != 0) { // Multiple dates in the input string - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -1692,7 +1692,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, int monthDayOrder; if (!GetMonthDayOrder(dtfi.MonthDayPattern, dtfi, out monthDayOrder)) { - result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.MonthDayPattern); + result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDatePattern), dtfi.MonthDayPattern); return false; } if (monthDayOrder == ORDER_DM) @@ -1700,7 +1700,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, int yearMonthOrder; if (!GetYearMonthOrder(dtfi.YearMonthPattern, dtfi, out yearMonthOrder)) { - result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.YearMonthPattern); + result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDatePattern), dtfi.YearMonthPattern); return false; } if (yearMonthOrder == ORDER_MY) @@ -1708,7 +1708,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, int year; if (!TryAdjustYear(ref result, raw.GetNumber(0), out year) || !SetDateYMD(ref result, year, raw.month, 1)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } return true; @@ -1718,7 +1718,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, GetDefaultYear(ref result, ref styles); if (!SetDateYMD(ref result, result.Year, raw.month, raw.GetNumber(0))) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } return true; @@ -1735,7 +1735,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, int monthDayOrder; if (!GetMonthDayOrder(dtfi.MonthDayPattern, dtfi, out monthDayOrder)) { - result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.MonthDayPattern); + result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDatePattern), dtfi.MonthDayPattern); return false; } result.Month = raw.month; @@ -1747,7 +1747,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, return true; } } - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -1756,7 +1756,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if ((result.flags & ParseFlags.HaveDate) != 0) { // Multiple dates in the input string - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -1775,7 +1775,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, int monthDayOrder; if (!GetMonthDayOrder(dtfi.MonthDayPattern, dtfi, out monthDayOrder)) { - result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.MonthDayPattern); + result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDatePattern), dtfi.MonthDayPattern); return false; } if (monthDayOrder == ORDER_MD) @@ -1783,7 +1783,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, int yearMonthOrder; if (!GetYearMonthOrder(dtfi.YearMonthPattern, dtfi, out yearMonthOrder)) { - result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.YearMonthPattern); + result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDatePattern), dtfi.YearMonthPattern); return false; } if (yearMonthOrder == ORDER_YM) @@ -1791,7 +1791,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, int year; if (!TryAdjustYear(ref result, raw.GetNumber(0), out year) || !SetDateYMD(ref result, year, raw.month, 1)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } return true; @@ -1801,7 +1801,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, GetDefaultYear(ref result, ref styles); if (!SetDateYMD(ref result, result.Year, raw.month, raw.GetNumber(0))) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } return true; @@ -1812,7 +1812,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if ((result.flags & ParseFlags.HaveDate) != 0) { // Multiple dates in the input string - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -1822,7 +1822,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, int order; if (!GetYearMonthDayOrder(dtfi.ShortDatePattern, dtfi, out order)) { - result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.ShortDatePattern); + result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDatePattern), dtfi.ShortDatePattern); return false; } int year; @@ -1873,7 +1873,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, } } - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -1882,7 +1882,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if ((result.flags & ParseFlags.HaveDate) != 0) { // Multiple dates in the input string - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -1908,7 +1908,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, return true; // Year + MD } } - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -1917,7 +1917,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if ((result.flags & ParseFlags.HaveDate) != 0) { // Multiple dates in the input string - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -1927,7 +1927,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, int order; if (!GetYearMonthDayOrder(dtfi.ShortDatePattern, dtfi, out order)) { - result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.ShortDatePattern); + result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDatePattern), dtfi.ShortDatePattern); return false; } @@ -1947,7 +1947,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, return true; // DM + Year } } - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -1957,7 +1957,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if ((result.flags & ParseFlags.HaveDate) != 0) { // Multiple dates in the input string - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -1966,7 +1966,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, result.flags |= ParseFlags.HaveDate; return true; } - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -1975,7 +1975,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if ((result.flags & ParseFlags.HaveDate) != 0) { // Multiple dates in the input string - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -1984,7 +1984,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, result.flags |= ParseFlags.HaveDate; return true; } - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -1993,7 +1993,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if ((result.flags & ParseFlags.HaveDate) != 0) { // Multiple dates in the input string - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -2002,7 +2002,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, result.flags |= ParseFlags.HaveDate; return true; } - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -2065,7 +2065,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if ((result.flags & ParseFlags.HaveTime) != 0) { // Multiple times in the input string - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } // @@ -2073,7 +2073,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, // if (raw.timeMark == TM.NotSet) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } result.Hour = raw.GetNumber(0); @@ -2087,7 +2087,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if ((result.flags & ParseFlags.HaveTime) != 0) { // Multiple times in the input string - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -2102,7 +2102,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if ((result.flags & ParseFlags.HaveTime) != 0) { // Multiple times in the input string - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } Debug.Assert(raw.numCount >= 3, "raw.numCount >= 3"); @@ -2120,7 +2120,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, { if (raw.numCount != 1 || result.Day != -1) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } result.Day = raw.GetNumber(0); @@ -2132,19 +2132,19 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if (result.Month == -1) { //Should have a month suffix - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } if (result.Year != -1) { // Aleady has a year suffix - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } if (!TryAdjustYear(ref result, raw.GetNumber(0), out result.Year)) { // the year value is out of range - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } result.Day = 1; @@ -2174,7 +2174,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, int order; if (!GetYearMonthDayOrder(dtfi.ShortDatePattern, dtfi, out order)) { - result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.ShortDatePattern); + result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadDatePattern), dtfi.ShortDatePattern); return false; } int year; @@ -2194,7 +2194,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, } } } - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -2275,7 +2275,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, raw.year = raw.GetNumber(1); if (!dtfi.YearMonthAdjustment(ref raw.year, ref raw.month, true)) { - result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null); + result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null); return false; } if (!GetDayOfMNN(ref result, ref raw, dtfi)) @@ -2287,7 +2287,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, // Deal with the default long/short date format when the year number is NOT ambigous (i.e. year >= 100). if (!dtfi.YearMonthAdjustment(ref raw.year, ref raw.month, true)) { - result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null); + result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null); return false; } if (!GetDayOfYMN(ref result, ref raw, dtfi)) @@ -2295,13 +2295,30 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, return false; } break; + case DS.DX_NNY: + // When formatting, we only format up to the hundred digit of the Hebrew year, although Hebrew year is now over 5000. + // E.g. if the year is 5763, we only format as 763. so we do the reverse when parsing. + if (raw.year < 1000) + { + raw.year += 5000; + } + if (!GetDayOfNNY(ref result, ref raw, dtfi)) + { + return false; + } + if (!dtfi.YearMonthAdjustment(ref result.Year, ref raw.month, true)) + { + result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null); + return false; + } + break; case DS.DX_NM: case DS.DX_MN: // Deal with Month/Day pattern. GetDefaultYear(ref result, ref styles); if (!dtfi.YearMonthAdjustment(ref result.Year, ref raw.month, true)) { - result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null); + result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null); return false; } if (!GetHebrewDayOfNM(ref result, ref raw, dtfi)) @@ -2313,7 +2330,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, // Deal with Year/Month pattern. if (!dtfi.YearMonthAdjustment(ref raw.year, ref raw.month, true)) { - result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null); + result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null); return false; } if (!GetDayOfYM(ref result, ref raw, dtfi)) @@ -2341,7 +2358,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, } break; default: - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } if (dps > DS.ERROR) @@ -2505,12 +2522,12 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, { if (s == null) { - result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(s)); + result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), null, nameof(s)); return false; } if (s.Length == 0) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -2579,7 +2596,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, { if (!ProcessDateTimeSuffix(ref result, ref raw, ref dtok)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); TPTraceExit("0010", dps); return false; } @@ -2598,7 +2615,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, } else { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); TPTraceExit("0030", dps); return false; } @@ -2643,7 +2660,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if (dps == DS.ERROR) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); TPTraceExit("0040 (invalid state transition)", dps); return false; } @@ -2679,7 +2696,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if (!reachTerminalState) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); TPTraceExit("0070 (did not reach terminal state)", dps); return false; } @@ -2687,7 +2704,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, AdjustTimeMark(dtfi, ref raw); if (!AdjustHour(ref result.Hour, raw.timeMark)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); TPTraceExit("0080 (AdjustHour)", dps); return false; } @@ -2708,7 +2725,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if (!result.calendar.TryToDateTime(result.Year, result.Month, result.Day, result.Hour, result.Minute, result.Second, 0, result.era, out time)) { - result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null); + result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null); TPTraceExit("0100 (result.calendar.TryToDateTime)", dps); return false; } @@ -2728,7 +2745,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, // if (raw.dayOfWeek != (int)result.calendar.GetDayOfWeek(time)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDayOfWeek", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDayOfWeek), null); TPTraceExit("0110 (dayOfWeek check)", dps); return false; } @@ -2762,7 +2779,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, // the DateTime offset must be within +- 14:00 hours. if (offsetTicks < DateTimeOffset.MinOffset || offsetTicks > DateTimeOffset.MaxOffset) { - result.SetFailure(ParseFailureKind.Format, "Format_OffsetOutOfRange", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_OffsetOutOfRange), null); return false; } } @@ -2853,14 +2870,14 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, // of a DateTime instance. if (utcTicks < DateTime.MinTicks || utcTicks > DateTime.MaxTicks) { - result.SetFailure(ParseFailureKind.Format, "Format_UTCOutOfRange", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_UTCOutOfRange), null); return false; } // the offset must be within +- 14:00 hours. if (offsetTicks < DateTimeOffset.MinOffset || offsetTicks > DateTimeOffset.MaxOffset) { - result.SetFailure(ParseFailureKind.Format, "Format_OffsetOutOfRange", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_OffsetOutOfRange), null); return false; } @@ -2903,7 +2920,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if (resultTicks < DateTime.MinTicks || resultTicks > DateTime.MaxTicks) { - result.SetFailure(ParseFailureKind.Format, "Format_DateOutOfRange", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_DateOutOfRange), null); return false; } result.parsedDate = new DateTime(resultTicks, DateTimeKind.Utc); @@ -2960,7 +2977,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if (resultTicks < DateTime.MinTicks || resultTicks > DateTime.MaxTicks) { result.parsedDate = DateTime.MinValue; - result.SetFailure(ParseFailureKind.Format, "Format_DateOutOfRange", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_DateOutOfRange), null); return false; } result.parsedDate = new DateTime(resultTicks, DateTimeKind.Local, isAmbiguousLocalDst); @@ -2984,19 +3001,19 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, str.SkipWhiteSpaces(); if (!ParseDigits(ref str, 2, out hour)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } str.SkipWhiteSpaces(); if (!str.Match(':')) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } str.SkipWhiteSpaces(); if (!ParseDigits(ref str, 2, out minute)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } str.SkipWhiteSpaces(); @@ -3005,14 +3022,14 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, str.SkipWhiteSpaces(); if (!ParseDigits(ref str, 2, out second)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } if (str.Match('.')) { if (!ParseFraction(ref str, out partSecond)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } str.Index--; @@ -3027,7 +3044,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, result.flags |= ParseFlags.TimeZoneUsed; if (!ParseTimeZone(ref str, ref result.timeZoneOffset)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } } @@ -3046,7 +3063,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, { if (!VerifyValidPunctuation(ref str)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } str.SkipWhiteSpaces(); @@ -3055,14 +3072,14 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, { if (!VerifyValidPunctuation(ref str)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } } if (str.GetNext()) { // If this is true, there were non-white space characters remaining in the DateTime - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } } @@ -3072,7 +3089,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if (!calendar.TryToDateTime(raw.year, raw.GetNumber(0), raw.GetNumber(1), hour, minute, second, 0, result.era, out time)) { - result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null); + result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null); return false; } @@ -3658,7 +3675,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, { if (newValue != currentValue) { - result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", patternChar); + result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_RepeatDateTimePattern), patternChar); return (false); } } @@ -3702,7 +3719,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if (((result.Month != -1) || (result.Day != -1)) && ((result.Year == -1 || ((result.flags & ParseFlags.YearDefault) != 0)) && (result.flags & ParseFlags.TimeZoneUsed) != 0)) { - result.SetFailure(ParseFailureKind.Format, "Format_MissingIncompleteDate", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_MissingIncompleteDate), null); return false; } } @@ -3885,7 +3902,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, } if (!parseResult) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return (false); } if (!CheckNewValue(ref result.Year, tempYear, ch, ref result)) @@ -3902,7 +3919,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if (!parseInfo.fCustomNumberParser || !parseInfo.parseNumberDelegate(ref str, tokenLen, out tempMonth)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return (false); } } @@ -3913,7 +3930,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, { if (!MatchAbbreviatedMonthName(ref str, dtfi, ref tempMonth)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return (false); } } @@ -3921,7 +3938,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, { if (!MatchMonthName(ref str, dtfi, ref tempMonth)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return (false); } } @@ -3944,7 +3961,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if (!parseInfo.fCustomNumberParser || !parseInfo.parseNumberDelegate(ref str, tokenLen, out tempDay)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return (false); } } @@ -3960,7 +3977,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, // "ddd" if (!MatchAbbreviatedDayName(ref str, dtfi, ref tempDayOfWeek)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return (false); } } @@ -3969,7 +3986,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, // "dddd*" if (!MatchDayName(ref str, dtfi, ref tempDayOfWeek)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return (false); } } @@ -3984,7 +4001,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, // Put the era value in result.era. if (!MatchEraName(ref str, dtfi, ref result.era)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return (false); } break; @@ -3993,7 +4010,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, tokenLen = format.GetRepeatCount(); if (!ParseDigits(ref str, (tokenLen < 2 ? 1 : 2), out tempHour)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return (false); } if (!CheckNewValue(ref result.Hour, tempHour, ch, ref result)) @@ -4005,7 +4022,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, tokenLen = format.GetRepeatCount(); if (!ParseDigits(ref str, (tokenLen < 2 ? 1 : 2), out tempHour)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return (false); } if (!CheckNewValue(ref result.Hour, tempHour, ch, ref result)) @@ -4017,7 +4034,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, tokenLen = format.GetRepeatCount(); if (!ParseDigits(ref str, (tokenLen < 2 ? 1 : 2), out tempMinute)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return (false); } if (!CheckNewValue(ref result.Minute, tempMinute, ch, ref result)) @@ -4029,7 +4046,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, tokenLen = format.GetRepeatCount(); if (!ParseDigits(ref str, (tokenLen < 2 ? 1 : 2), out tempSecond)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return (false); } if (!CheckNewValue(ref result.Second, tempSecond, ch, ref result)) @@ -4046,7 +4063,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, { if (ch == 'f') { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return (false); } } @@ -4058,14 +4075,14 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, { if (tempFraction != result.fraction) { - result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", ch); + result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_RepeatDateTimePattern), ch); return (false); } } } else { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return (false); } break; @@ -4076,7 +4093,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, { if (!MatchAbbreviatedTimeMark(ref str, dtfi, ref tempTimeMark)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return (false); } } @@ -4084,7 +4101,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, { if (!MatchTimeMark(ref str, dtfi, ref tempTimeMark)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return (false); } } @@ -4097,7 +4114,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, { if (parseInfo.timeMark != tempTimeMark) { - result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", ch); + result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_RepeatDateTimePattern), ch); return (false); } } @@ -4109,12 +4126,12 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, TimeSpan tempTimeZoneOffset = new TimeSpan(0); if (!ParseTimeZoneOffset(ref str, tokenLen, ref tempTimeZoneOffset)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return (false); } if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && tempTimeZoneOffset != result.timeZoneOffset) { - result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", 'z'); + result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_RepeatDateTimePattern), 'z'); return (false); } result.timeZoneOffset = tempTimeZoneOffset; @@ -4124,7 +4141,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, case 'Z': if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && result.timeZoneOffset != TimeSpan.Zero) { - result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", 'Z'); + result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_RepeatDateTimePattern), 'Z'); return (false); } @@ -4139,7 +4156,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, str.Index++; if (!GetTimeZoneName(ref str)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } str.Index--; @@ -4150,7 +4167,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, { if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && result.timeZoneOffset != TimeSpan.Zero) { - result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", 'K'); + result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_RepeatDateTimePattern), 'K'); return (false); } @@ -4164,12 +4181,12 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, TimeSpan tempTimeZoneOffset = new TimeSpan(0); if (!ParseTimeZoneOffset(ref str, 3, ref tempTimeZoneOffset)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return (false); } if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && tempTimeZoneOffset != result.timeZoneOffset) { - result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", 'K'); + result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_RepeatDateTimePattern), 'K'); return (false); } result.timeZoneOffset = tempTimeZoneOffset; @@ -4184,7 +4201,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, !str.Match(dtfi.TimeSeparator)) { // A time separator is expected. - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } break; @@ -4195,7 +4212,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, !str.Match(dtfi.DateSeparator)) { // A date separator is expected. - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } break; @@ -4205,7 +4222,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, // Use ParseQuoteString so that we can handle escape characters within the quoted string. if (!TryParseQuoteString(format.Value, format.Index, enquotedString, out tokenLen)) { - result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadQuote", ch); + result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadQuote), ch); return (false); } format.Index += tokenLen - 1; @@ -4224,7 +4241,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, else if (!str.Match(quotedStr[i])) { // Can not find the matching quoted string. - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } } @@ -4255,7 +4272,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, // Make sure the next character is not a '%' again. if (format.Index >= format.Value.Length - 1 || format.Value[format.Index + 1] == '%') { - result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier), null); return false; } break; @@ -4268,13 +4285,13 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if (!str.Match(format.GetChar())) { // Can not find a match for the escaped character. - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } } else { - result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier), null); return false; } break; @@ -4291,7 +4308,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, break; } } - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } break; @@ -4326,7 +4343,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, } } } - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } // Found a macth. @@ -4343,14 +4360,14 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, result.timeZoneOffset = TimeSpan.Zero; if (!str.Match(GMTName)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } } else if (!str.Match(ch)) { // ch is expected. - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } } @@ -4461,7 +4478,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if (((result.flags & ParseFlags.CaptureOffset) != 0) && formatParam[0] == 'U') { // The 'U' format is not allowed for DateTimeOffset - result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier), null); return false; } formatParam = ExpandPredefinedFormat(formatParam, ref dtfi, ref parseInfo, ref result); @@ -4518,7 +4535,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if (str.Index < str.Value.Length - 1) { // There are still remaining character in str. - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } @@ -4527,7 +4544,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, // A two digit year value is expected. Check if the parsed year value is valid. if (result.Year >= 100) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } try @@ -4536,7 +4553,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, } catch (ArgumentOutOfRangeException e) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", e); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), e); return false; } } @@ -4555,7 +4572,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if (result.Hour > 12) { // AM/PM is used, but the value for HH is too big. - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } if (parseInfo.timeMark == TM.AM) @@ -4579,7 +4596,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, if ((parseInfo.timeMark == TM.AM && result.Hour >= 12) || (parseInfo.timeMark == TM.PM && result.Hour < 12)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDateTime), null); return false; } } @@ -4596,14 +4613,14 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, { if (!dtfi.YearMonthAdjustment(ref result.Year, ref result.Month, ((result.flags & ParseFlags.ParsedMonthName) != 0))) { - result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null); + result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null); return false; } } if (!parseInfo.calendar.TryToDateTime(result.Year, result.Month, result.Day, result.Hour, result.Minute, result.Second, 0, result.era, out result.parsedDate)) { - result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null); + result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, nameof(SR.Format_BadDateTimeCalendar), null); return false; } if (result.fraction > 0) @@ -4623,7 +4640,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, // if (parseInfo.dayOfWeek != (int)parseInfo.calendar.GetDayOfWeek(result.parsedDate)) { - result.SetFailure(ParseFailureKind.Format, "Format_BadDayOfWeek", null); + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadDayOfWeek), null); return false; } } diff --git a/src/mscorlib/shared/System/Globalization/EastAsianLunisolarCalendar.cs b/src/mscorlib/shared/System/Globalization/EastAsianLunisolarCalendar.cs index 0697b602db..383e0cd17c 100644 --- a/src/mscorlib/shared/System/Globalization/EastAsianLunisolarCalendar.cs +++ b/src/mscorlib/shared/System/Globalization/EastAsianLunisolarCalendar.cs @@ -67,7 +67,7 @@ namespace System.Globalization return ((sexagenaryYear - 1) % 10) + 1; } - // Return the Terrestial Branch from the the 60-year cycle. + // Return the Terrestial Branch from the 60-year cycle. // The returned value is from 1 ~ 12. // diff --git a/src/mscorlib/shared/System/Globalization/HijriCalendar.Win32.cs b/src/mscorlib/shared/System/Globalization/HijriCalendar.Win32.cs index 869b809bff..09b1f20c48 100644 --- a/src/mscorlib/shared/System/Globalization/HijriCalendar.Win32.cs +++ b/src/mscorlib/shared/System/Globalization/HijriCalendar.Win32.cs @@ -45,7 +45,6 @@ namespace System.Globalization try { // 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. @@ -68,10 +67,9 @@ namespace System.Globalization hijriAdvance = -1; else { - str = str.Substring(HijriAdvanceRegKeyEntry.Length); try { - int advance = Int32.Parse(str.ToString(), CultureInfo.InvariantCulture); + int advance = Int32.Parse(str.AsReadOnlySpan().Slice(HijriAdvanceRegKeyEntry.Length), provider:CultureInfo.InvariantCulture); if ((advance >= MinAdvancedHijri) && (advance <= MaxAdvancedHijri)) { hijriAdvance = advance; diff --git a/src/mscorlib/shared/System/Globalization/HijriCalendar.cs b/src/mscorlib/shared/System/Globalization/HijriCalendar.cs index 125248a685..59b354f534 100644 --- a/src/mscorlib/shared/System/Globalization/HijriCalendar.cs +++ b/src/mscorlib/shared/System/Globalization/HijriCalendar.cs @@ -187,7 +187,7 @@ namespace System.Globalization set { - // NOTE: Check the value of Min/MaxAdavncedHijri with Arabic speakers to see if the assumption is good. + // NOTE: Check the value of Min/MaxAdvancedHijri with Arabic speakers to see if the assumption is good. if (value < MinAdvancedHijri || value > MaxAdvancedHijri) { throw new ArgumentOutOfRangeException( @@ -303,7 +303,7 @@ namespace System.Globalization // HijriYear = (int)(((NumDays - 227013) * 30) / 10631) + 1; - long daysToHijriYear = DaysUpToHijriYear(HijriYear); // The absoulte date for HijriYear + long daysToHijriYear = DaysUpToHijriYear(HijriYear); // The absolute date for HijriYear long daysOfHijriYear = GetDaysInYear(HijriYear, CurrentEra); // The number of days for (HijriYear+1) year. if (NumDays < daysToHijriYear) diff --git a/src/mscorlib/shared/System/Globalization/JapaneseCalendar.Win32.cs b/src/mscorlib/shared/System/Globalization/JapaneseCalendar.Win32.cs index a83c4fad9e..94e0668de1 100644 --- a/src/mscorlib/shared/System/Globalization/JapaneseCalendar.Win32.cs +++ b/src/mscorlib/shared/System/Globalization/JapaneseCalendar.Win32.cs @@ -159,9 +159,10 @@ namespace System.Globalization int month; int day; - if (!Int32.TryParse(value.Substring(0, 4), NumberStyles.None, NumberFormatInfo.InvariantInfo, out year) || - !Int32.TryParse(value.Substring(5, 2), NumberStyles.None, NumberFormatInfo.InvariantInfo, out month) || - !Int32.TryParse(value.Substring(8, 2), NumberStyles.None, NumberFormatInfo.InvariantInfo, out day)) + ReadOnlySpan<char> valueSpan = value.AsReadOnlySpan(); + if (!Int32.TryParse(valueSpan.Slice(0, 4), out year, style:NumberStyles.None, provider: NumberFormatInfo.InvariantInfo) || + !Int32.TryParse(valueSpan.Slice(5, 2), out month, style:NumberStyles.None, provider: NumberFormatInfo.InvariantInfo) || + !Int32.TryParse(valueSpan.Slice(8, 2), out day, style:NumberStyles.None, provider: NumberFormatInfo.InvariantInfo)) { // Couldn't convert integer, fail return null; diff --git a/src/mscorlib/shared/System/Globalization/NumberFormatInfo.cs b/src/mscorlib/shared/System/Globalization/NumberFormatInfo.cs index 9fea694cca..d7412bfcf3 100644 --- a/src/mscorlib/shared/System/Globalization/NumberFormatInfo.cs +++ b/src/mscorlib/shared/System/Globalization/NumberFormatInfo.cs @@ -238,35 +238,30 @@ namespace System.Globalization public static NumberFormatInfo GetInstance(IFormatProvider formatProvider) { - // Fast case for a regular CultureInfo - NumberFormatInfo info; - CultureInfo cultureProvider = formatProvider as CultureInfo; - if (cultureProvider != null && !cultureProvider._isInherited) + if (formatProvider != null) { - info = cultureProvider.numInfo; - if (info != null) + // Fast case for a regular CultureInfo + NumberFormatInfo info; + CultureInfo cultureProvider = formatProvider as CultureInfo; + if (cultureProvider != null && !cultureProvider._isInherited) { - return info; + return cultureProvider.numInfo ?? cultureProvider.NumberFormat; } - else + + // Fast case for an NFI; + info = formatProvider as NumberFormatInfo; + if (info != null) { - return cultureProvider.NumberFormat; + return info; } - } - // Fast case for an NFI; - info = formatProvider as NumberFormatInfo; - if (info != null) - { - return info; - } - if (formatProvider != null) - { + info = formatProvider.GetFormat(typeof(NumberFormatInfo)) as NumberFormatInfo; if (info != null) { return info; } } + return CurrentInfo; } diff --git a/src/mscorlib/shared/System/Globalization/TimeSpanFormat.cs b/src/mscorlib/shared/System/Globalization/TimeSpanFormat.cs new file mode 100644 index 0000000000..6801ea8e1b --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/TimeSpanFormat.cs @@ -0,0 +1,523 @@ +// 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.Text; +using System.Diagnostics; + +namespace System.Globalization +{ + internal static class TimeSpanFormat + { + private static unsafe void AppendNonNegativeInt32(StringBuilder sb, int n, int digits) + { + Debug.Assert(n >= 0); + uint value = (uint)n; + + const int MaxUInt32Digits = 10; + char* buffer = stackalloc char[MaxUInt32Digits]; + + int index = 0; + do + { + uint div = value / 10; + buffer[index++] = (char)(value - (div * 10) + '0'); + value = div; + } + while (value != 0); + Debug.Assert(index <= MaxUInt32Digits); + + for (int i = digits - index; i > 0; --i) sb.Append('0'); + for (int i = index - 1; i >= 0; --i) sb.Append(buffer[i]); + } + + internal static readonly FormatLiterals PositiveInvariantFormatLiterals = TimeSpanFormat.FormatLiterals.InitInvariant(isNegative: false); + internal static readonly FormatLiterals NegativeInvariantFormatLiterals = TimeSpanFormat.FormatLiterals.InitInvariant(isNegative: true); + + internal enum Pattern + { + None = 0, + Minimum = 1, + Full = 2, + } + + /// <summary>Main method called from TimeSpan.ToString.</summary> + internal static string Format(TimeSpan value, string format, IFormatProvider formatProvider) => + StringBuilderCache.GetStringAndRelease(FormatToBuilder(value, format, formatProvider)); + + /// <summary>Main method called from TimeSpan.TryFormat.</summary> + internal static bool TryFormat(TimeSpan value, Span<char> destination, out int charsWritten, string format, IFormatProvider formatProvider) + { + StringBuilder sb = FormatToBuilder(value, format, formatProvider); + if (sb.Length <= destination.Length) + { + charsWritten = sb.Length; + sb.CopyTo(0, destination, sb.Length); + StringBuilderCache.Release(sb); + return true; + } + else + { + StringBuilderCache.Release(sb); + charsWritten = 0; + return false; + } + } + + private static StringBuilder FormatToBuilder(TimeSpan value, string format, IFormatProvider formatProvider) + { + if (format == null || format.Length == 0) + { + format = "c"; + } + + // Standard formats + if (format.Length == 1) + { + char f = format[0]; + switch (f) + { + case 'c': + case 't': + case 'T': + return FormatStandard( + value, + isInvariant: true, + format: format, + pattern: Pattern.Minimum); + + case 'g': + case 'G': + DateTimeFormatInfo dtfi = DateTimeFormatInfo.GetInstance(formatProvider); + return FormatStandard( + value, + isInvariant: false, + format: value.Ticks < 0 ? dtfi.FullTimeSpanNegativePattern : dtfi.FullTimeSpanPositivePattern, + pattern: f == 'g' ? Pattern.Minimum : Pattern.Full); + + default: + throw new FormatException(SR.Format_InvalidString); + } + } + + // Custom formats + return FormatCustomized(value, format, DateTimeFormatInfo.GetInstance(formatProvider)); + } + + /// <summary>Format the TimeSpan instance using the specified format.</summary> + private static StringBuilder FormatStandard(TimeSpan value, bool isInvariant, string format, Pattern pattern) + { + StringBuilder sb = StringBuilderCache.Acquire(InternalGlobalizationHelper.StringBuilderDefaultCapacity); + int day = (int)(value.Ticks / TimeSpan.TicksPerDay); + long time = value.Ticks % TimeSpan.TicksPerDay; + + if (value.Ticks < 0) + { + day = -day; + time = -time; + } + int hours = (int)(time / TimeSpan.TicksPerHour % 24); + int minutes = (int)(time / TimeSpan.TicksPerMinute % 60); + int seconds = (int)(time / TimeSpan.TicksPerSecond % 60); + int fraction = (int)(time % TimeSpan.TicksPerSecond); + + FormatLiterals literal; + if (isInvariant) + { + literal = value.Ticks < 0 ? + NegativeInvariantFormatLiterals : + PositiveInvariantFormatLiterals; + } + else + { + literal = new FormatLiterals(); + literal.Init(format, pattern == Pattern.Full); + } + + if (fraction != 0) + { + // truncate the partial second to the specified length + fraction = (int)(fraction / TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - literal.ff)); + } + + // Pattern.Full: [-]dd.hh:mm:ss.fffffff + // Pattern.Minimum: [-][d.]hh:mm:ss[.fffffff] + + sb.Append(literal.Start); // [-] + if (pattern == Pattern.Full || day != 0) + { + sb.Append(day); // [dd] + sb.Append(literal.DayHourSep); // [.] + } // + AppendNonNegativeInt32(sb, hours, literal.hh); // hh + sb.Append(literal.HourMinuteSep); // : + AppendNonNegativeInt32(sb, minutes, literal.mm); // mm + sb.Append(literal.MinuteSecondSep); // : + AppendNonNegativeInt32(sb, seconds, literal.ss); // ss + if (!isInvariant && pattern == Pattern.Minimum) + { + int effectiveDigits = literal.ff; + while (effectiveDigits > 0) + { + if (fraction % 10 == 0) + { + fraction = fraction / 10; + effectiveDigits--; + } + else + { + break; + } + } + if (effectiveDigits > 0) + { + sb.Append(literal.SecondFractionSep); // [.FFFFFFF] + sb.Append((fraction).ToString(DateTimeFormat.fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture)); + } + } + else if (pattern == Pattern.Full || fraction != 0) + { + sb.Append(literal.SecondFractionSep); // [.] + AppendNonNegativeInt32(sb, fraction, literal.ff); // [fffffff] + } + sb.Append(literal.End); + + return sb; + } + + /// <summary>Format the TimeSpan instance using the specified format.</summary> + private static StringBuilder FormatCustomized(TimeSpan value, string format, DateTimeFormatInfo dtfi) + { + Debug.Assert(dtfi != null); + + int day = (int)(value.Ticks / TimeSpan.TicksPerDay); + long time = value.Ticks % TimeSpan.TicksPerDay; + + if (value.Ticks < 0) + { + day = -day; + time = -time; + } + int hours = (int)(time / TimeSpan.TicksPerHour % 24); + int minutes = (int)(time / TimeSpan.TicksPerMinute % 60); + int seconds = (int)(time / TimeSpan.TicksPerSecond % 60); + int fraction = (int)(time % TimeSpan.TicksPerSecond); + + long tmp = 0; + int i = 0; + int tokenLen; + StringBuilder result = StringBuilderCache.Acquire(InternalGlobalizationHelper.StringBuilderDefaultCapacity); + + while (i < format.Length) + { + char ch = format[i]; + int nextChar; + switch (ch) + { + case 'h': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > 2) + { + throw new FormatException(SR.Format_InvalidString); + } + DateTimeFormat.FormatDigits(result, hours, tokenLen); + break; + case 'm': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > 2) + { + throw new FormatException(SR.Format_InvalidString); + } + DateTimeFormat.FormatDigits(result, minutes, tokenLen); + break; + case 's': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > 2) + { + throw new FormatException(SR.Format_InvalidString); + } + DateTimeFormat.FormatDigits(result, seconds, tokenLen); + break; + case 'f': + // + // The fraction of a second in single-digit precision. The remaining digits are truncated. + // + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits) + { + throw new FormatException(SR.Format_InvalidString); + } + + tmp = fraction; + tmp /= TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen); + result.Append((tmp).ToString(DateTimeFormat.fixedNumberFormats[tokenLen - 1], CultureInfo.InvariantCulture)); + break; + case 'F': + // + // Displays the most significant digit of the seconds fraction. Nothing is displayed if the digit is zero. + // + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits) + { + throw new FormatException(SR.Format_InvalidString); + } + + tmp = fraction; + tmp /= TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen); + int effectiveDigits = tokenLen; + while (effectiveDigits > 0) + { + if (tmp % 10 == 0) + { + tmp = tmp / 10; + effectiveDigits--; + } + else + { + break; + } + } + if (effectiveDigits > 0) + { + result.Append((tmp).ToString(DateTimeFormat.fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture)); + } + break; + case 'd': + // + // tokenLen == 1 : Day as digits with no leading zero. + // tokenLen == 2+: Day as digits with leading zero for single-digit days. + // + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > 8) + { + throw new FormatException(SR.Format_InvalidString); + } + + DateTimeFormat.FormatDigits(result, day, tokenLen, true); + break; + case '\'': + case '\"': + tokenLen = DateTimeFormat.ParseQuoteString(format, i, result); + break; + case '%': + // Optional format character. + // For example, format string "%d" will print day + // Most of the cases, "%" can be ignored. + nextChar = DateTimeFormat.ParseNextChar(format, i); + // nextChar will be -1 if we already reach the end of the format string. + // Besides, we will not allow "%%" appear in the pattern. + if (nextChar >= 0 && nextChar != (int)'%') + { + result.Append(TimeSpanFormat.FormatCustomized(value, ((char)nextChar).ToString(), dtfi)); + tokenLen = 2; + } + else + { + // + // This means that '%' is at the end of the format string or + // "%%" appears in the format string. + // + throw new FormatException(SR.Format_InvalidString); + } + break; + case '\\': + // Escaped character. Can be used to insert character into the format string. + // For example, "\d" will insert the character 'd' into the string. + // + nextChar = DateTimeFormat.ParseNextChar(format, i); + if (nextChar >= 0) + { + result.Append(((char)nextChar)); + tokenLen = 2; + } + else + { + // + // This means that '\' is at the end of the formatting string. + // + throw new FormatException(SR.Format_InvalidString); + } + break; + default: + throw new FormatException(SR.Format_InvalidString); + } + i += tokenLen; + } + return result; + } + + internal struct FormatLiterals + { + internal string AppCompatLiteral; + internal int dd; + internal int hh; + internal int mm; + internal int ss; + internal int ff; + + private string[] _literals; + + internal string Start => _literals[0]; + internal string DayHourSep => _literals[1]; + internal string HourMinuteSep => _literals[2]; + internal string MinuteSecondSep => _literals[3]; + internal string SecondFractionSep => _literals[4]; + internal string End => _literals[5]; + + /* factory method for static invariant FormatLiterals */ + internal static FormatLiterals InitInvariant(bool isNegative) + { + FormatLiterals x = new FormatLiterals(); + x._literals = new string[6]; + x._literals[0] = isNegative ? "-" : string.Empty; + x._literals[1] = "."; + x._literals[2] = ":"; + x._literals[3] = ":"; + x._literals[4] = "."; + x._literals[5] = string.Empty; + x.AppCompatLiteral = ":."; // MinuteSecondSep+SecondFractionSep; + x.dd = 2; + x.hh = 2; + x.mm = 2; + x.ss = 2; + x.ff = DateTimeFormat.MaxSecondsFractionDigits; + return x; + } + + // For the "v1" TimeSpan localized patterns, the data is simply literal field separators with + // the constants guaranteed to include DHMSF ordered greatest to least significant. + // Once the data becomes more complex than this we will need to write a proper tokenizer for + // parsing and formatting + internal void Init(string format, bool useInvariantFieldLengths) + { + dd = hh = mm = ss = ff = 0; + _literals = new string[6]; + for (int i = 0; i < _literals.Length; i++) + { + _literals[i] = string.Empty; + } + + StringBuilder sb = StringBuilderCache.Acquire(InternalGlobalizationHelper.StringBuilderDefaultCapacity); + bool inQuote = false; + char quote = '\''; + int field = 0; + + for (int i = 0; i < format.Length; i++) + { + switch (format[i]) + { + case '\'': + case '\"': + if (inQuote && (quote == format[i])) + { + /* we were in a quote and found a matching exit quote, so we are outside a quote now */ + if (field >= 0 && field <= 5) + { + _literals[field] = sb.ToString(); + sb.Length = 0; + inQuote = false; + } + else + { + Debug.Fail($"Unexpected field value: {field}"); + return; // how did we get here? + } + } + else if (!inQuote) + { + /* we are at the start of a new quote block */ + quote = format[i]; + inQuote = true; + } + else + { + /* we were in a quote and saw the other type of quote character, so we are still in a quote */ + } + break; + case '%': + Debug.Fail("Unexpected special token '%', Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + goto default; + case '\\': + if (!inQuote) + { + i++; /* skip next character that is escaped by this backslash or percent sign */ + break; + } + goto default; + case 'd': + if (!inQuote) + { + Debug.Assert((field == 0 && sb.Length == 0) || field == 1, "field == 0 || field == 1, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + field = 1; // DayHourSep + dd++; + } + break; + case 'h': + if (!inQuote) + { + Debug.Assert((field == 1 && sb.Length == 0) || field == 2, "field == 1 || field == 2, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + field = 2; // HourMinuteSep + hh++; + } + break; + case 'm': + if (!inQuote) + { + Debug.Assert((field == 2 && sb.Length == 0) || field == 3, "field == 2 || field == 3, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + field = 3; // MinuteSecondSep + mm++; + } + break; + case 's': + if (!inQuote) + { + Debug.Assert((field == 3 && sb.Length == 0) || field == 4, "field == 3 || field == 4, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + field = 4; // SecondFractionSep + ss++; + } + break; + case 'f': + case 'F': + if (!inQuote) + { + Debug.Assert((field == 4 && sb.Length == 0) || field == 5, "field == 4 || field == 5, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + field = 5; // End + ff++; + } + break; + default: + sb.Append(format[i]); + break; + } + } + + Debug.Assert(field == 5); + AppCompatLiteral = MinuteSecondSep + SecondFractionSep; + + Debug.Assert(0 < dd && dd < 3, "0 < dd && dd < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + Debug.Assert(0 < hh && hh < 3, "0 < hh && hh < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + Debug.Assert(0 < mm && mm < 3, "0 < mm && mm < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + Debug.Assert(0 < ss && ss < 3, "0 < ss && ss < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + Debug.Assert(0 < ff && ff < 8, "0 < ff && ff < 8, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern"); + + if (useInvariantFieldLengths) + { + dd = 2; + hh = 2; + mm = 2; + ss = 2; + ff = DateTimeFormat.MaxSecondsFractionDigits; + } + else + { + if (dd < 1 || dd > 2) dd = 2; // The DTFI property has a problem. let's try to make the best of the situation. + if (hh < 1 || hh > 2) hh = 2; + if (mm < 1 || mm > 2) mm = 2; + if (ss < 1 || ss > 2) ss = 2; + if (ff < 1 || ff > 7) ff = 7; + } + StringBuilderCache.Release(sb); + } + } + } +} diff --git a/src/mscorlib/shared/System/Globalization/TimeSpanParse.cs b/src/mscorlib/shared/System/Globalization/TimeSpanParse.cs new file mode 100644 index 0000000000..f8443a1ea6 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/TimeSpanParse.cs @@ -0,0 +1,1675 @@ +// 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. + +//////////////////////////////////////////////////////////////////////////// +// +// Purpose: Used by TimeSpan to parse a time interval string. +// +// Standard Format: +// -=-=-=-=-=-=-=- +// "c": Constant format. [-][d'.']hh':'mm':'ss['.'fffffff] +// Not culture sensitive. Default format (and null/empty format string) map to this format. +// +// "g": General format, short: [-][d':']h':'mm':'ss'.'FFFFFFF +// Only print what's needed. Localized (if you want Invariant, pass in Invariant). +// The fractional seconds separator is localized, equal to the culture's DecimalSeparator. +// +// "G": General format, long: [-]d':'hh':'mm':'ss'.'fffffff +// Always print days and 7 fractional digits. Localized (if you want Invariant, pass in Invariant). +// The fractional seconds separator is localized, equal to the culture's DecimalSeparator. +// +// * "TryParseTimeSpan" is the main method for Parse/TryParse +// +// - TimeSpanTokenizer.GetNextToken() is used to split the input string into number and literal tokens. +// - TimeSpanRawInfo.ProcessToken() adds the next token into the parsing intermediary state structure +// - ProcessTerminalState() uses the fully initialized TimeSpanRawInfo to find a legal parse match. +// The terminal states are attempted as follows: +// foreach (+InvariantPattern, -InvariantPattern, +LocalizedPattern, -LocalizedPattern) try +// 1 number => d +// 2 numbers => h:m +// 3 numbers => h:m:s | d.h:m | h:m:.f +// 4 numbers => h:m:s.f | d.h:m:s | d.h:m:.f +// 5 numbers => d.h:m:s.f +// +// Custom Format: +// -=-=-=-=-=-=-= +// +// * "TryParseExactTimeSpan" is the main method for ParseExact/TryParseExact methods +// * "TryParseExactMultipleTimeSpan" is the main method for ParseExact/TryparseExact +// methods that take a string[] of formats +// +// - For single-letter formats "TryParseTimeSpan" is called (see above) +// - For multi-letter formats "TryParseByFormat" is called +// - TryParseByFormat uses helper methods (ParseExactLiteral, ParseExactDigits, etc) +// which drive the underlying TimeSpanTokenizer. However, unlike standard formatting which +// operates on whole-tokens, ParseExact operates at the character-level. As such, +// TimeSpanTokenizer.NextChar and TimeSpanTokenizer.BackOne() are called directly. +// +//////////////////////////////////////////////////////////////////////////// + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text; + +namespace System.Globalization +{ + internal static class TimeSpanParse + { + private const int MaxFractionDigits = 7; + private const int MaxDays = 10675199; + private const int MaxHours = 23; + private const int MaxMinutes = 59; + private const int MaxSeconds = 59; + private const int MaxFraction = 9999999; + + private enum ParseFailureKind : byte + { + None = 0, + ArgumentNull = 1, + Format = 2, + FormatWithParameter = 3, + Overflow = 4, + } + + [Flags] + private enum TimeSpanStandardStyles : byte + { + // Standard Format Styles + None = 0x00000000, + Invariant = 0x00000001, //Allow Invariant Culture + Localized = 0x00000002, //Allow Localized Culture + RequireFull = 0x00000004, //Require the input to be in DHMSF format + Any = Invariant | Localized, + } + + // TimeSpan Token Types + private enum TTT : byte + { + None = 0, // None of the TimeSpanToken fields are set + End = 1, // '\0' + Num = 2, // Number + Sep = 3, // literal + NumOverflow = 4, // Number that overflowed + } + + [IsByRefLike] + private struct TimeSpanToken + { + internal TTT _ttt; + internal int _num; // Store the number that we are parsing (if any) + internal int _zeroes; // Store the number of leading zeroes (if any) + internal ReadOnlySpan<char> _sep; // Store the literal that we are parsing (if any) + + public TimeSpanToken(TTT type) : this(type, 0, 0, default(ReadOnlySpan<char>)) { } + + public TimeSpanToken(int number) : this(TTT.Num, number, 0, default(ReadOnlySpan<char>)) { } + + public TimeSpanToken(int number, int leadingZeroes) : this(TTT.Num, number, leadingZeroes, default(ReadOnlySpan<char>)) { } + + public TimeSpanToken(TTT type, int number, int leadingZeroes, ReadOnlySpan<char> separator) + { + _ttt = type; + _num = number; + _zeroes = leadingZeroes; + _sep = separator; + } + + public bool IsInvalidFraction() + { + Debug.Assert(_ttt == TTT.Num); + Debug.Assert(_num > -1); + + if (_num > MaxFraction || _zeroes > MaxFractionDigits) + return true; + + if (_num == 0 || _zeroes == 0) + return false; + + // num > 0 && zeroes > 0 && num <= maxValue && zeroes <= maxPrecision + return _num >= MaxFraction / Pow10(_zeroes - 1); + } + } + + [IsByRefLike] + private struct TimeSpanTokenizer + { + private ReadOnlySpan<char> _value; + private int _pos; + + internal TimeSpanTokenizer(ReadOnlySpan<char> input) : this(input, 0) { } + + internal TimeSpanTokenizer(ReadOnlySpan<char> input, int startPosition) + { + _value = input; + _pos = startPosition; + } + + /// <summary>Returns the next token in the input string</summary> + /// <remarks>Used by the parsing routines that operate on standard-formats.</remarks> + internal TimeSpanToken GetNextToken() + { + // Get the position of the next character to be processed. If there is no + // next character, we're at the end. + int pos = _pos; + Debug.Assert(pos > -1); + if (pos >= _value.Length) + { + return new TimeSpanToken(TTT.End); + } + + // Now retrieve that character. If it's a digit, we're processing a number. + int num = _value[pos] - '0'; + if ((uint)num <= 9) + { + int zeroes = 0; + if (num == 0) + { + // Read all leading zeroes. + zeroes = 1; + while (true) + { + int digit; + if (++_pos >= _value.Length || (uint)(digit = _value[_pos] - '0') > 9) + { + return new TimeSpanToken(TTT.Num, 0, zeroes, default(ReadOnlySpan<char>)); + } + + if (digit == 0) + { + zeroes++; + continue; + } + + num = digit; + break; + } + } + + // Continue to read as long as we're reading digits. + while (++_pos < _value.Length) + { + int digit = _value[_pos] - '0'; + if ((uint)digit > 9) + { + break; + } + + num = num * 10 + digit; + if ((num & 0xF0000000) != 0) + { + return new TimeSpanToken(TTT.NumOverflow); + } + } + + return new TimeSpanToken(TTT.Num, num, zeroes, default(ReadOnlySpan<char>)); + } + + // Otherwise, we're processing a separator, and we've already processed the first + // character of it. Continue processing characters as long as they're not digits. + int length = 1; + while (true) + { + if (++_pos >= _value.Length || (uint)(_value[_pos] - '0') <= 9) + { + break; + } + length++; + } + + // Return the separator. + return new TimeSpanToken(TTT.Sep, 0, 0, _value.Slice(pos, length)); + } + + internal bool EOL => _pos >= (_value.Length - 1); + + internal void BackOne() + { + if (_pos > 0) --_pos; + } + + internal char NextChar + { + get + { + int pos = ++_pos; + return (uint)pos < (uint)_value.Length ? + _value[pos] : + (char)0; + } + } + } + + /// <summary>Stores intermediary parsing state for the standard formats.</summary> + [IsByRefLike] + private struct TimeSpanRawInfo + { + internal TimeSpanFormat.FormatLiterals PositiveInvariant => TimeSpanFormat.PositiveInvariantFormatLiterals; + internal TimeSpanFormat.FormatLiterals NegativeInvariant => TimeSpanFormat.NegativeInvariantFormatLiterals; + + internal TimeSpanFormat.FormatLiterals PositiveLocalized + { + get + { + if (!_posLocInit) + { + _posLoc = new TimeSpanFormat.FormatLiterals(); + _posLoc.Init(_fullPosPattern, false); + _posLocInit = true; + } + return _posLoc; + } + } + + internal TimeSpanFormat.FormatLiterals NegativeLocalized + { + get + { + if (!_negLocInit) + { + _negLoc = new TimeSpanFormat.FormatLiterals(); + _negLoc.Init(_fullNegPattern, false); + _negLocInit = true; + } + return _negLoc; + } + } + + internal bool FullAppCompatMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 5 + && _numCount == 4 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.DayHourSep) + && StringSpanHelpers.Equals(_literals2, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals3, pattern.AppCompatLiteral) + && StringSpanHelpers.Equals(_literals4, pattern.End); + + internal bool PartialAppCompatMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 4 + && _numCount == 3 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals2, pattern.AppCompatLiteral) + && StringSpanHelpers.Equals(_literals3, pattern.End); + + /// <summary>DHMSF (all values matched)</summary> + internal bool FullMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == MaxLiteralTokens + && _numCount == MaxNumericTokens + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.DayHourSep) + && StringSpanHelpers.Equals(_literals2, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals3, pattern.MinuteSecondSep) + && StringSpanHelpers.Equals(_literals4, pattern.SecondFractionSep) + && StringSpanHelpers.Equals(_literals5, pattern.End); + + /// <summary>D (no hours, minutes, seconds, or fractions)</summary> + internal bool FullDMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 2 + && _numCount == 1 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.End); + + /// <summary>HM (no days, seconds, or fractions)</summary> + internal bool FullHMMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 3 + && _numCount == 2 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals2, pattern.End); + + /// <summary>DHM (no seconds or fraction)</summary> + internal bool FullDHMMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 4 + && _numCount == 3 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.DayHourSep) + && StringSpanHelpers.Equals(_literals2, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals3, pattern.End); + + /// <summary>HMS (no days or fraction)</summary> + internal bool FullHMSMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 4 + && _numCount == 3 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals2, pattern.MinuteSecondSep) + && StringSpanHelpers.Equals(_literals3, pattern.End); + + /// <summary>DHMS (no fraction)</summary> + internal bool FullDHMSMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 5 + && _numCount == 4 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.DayHourSep) + && StringSpanHelpers.Equals(_literals2, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals3, pattern.MinuteSecondSep) + && StringSpanHelpers.Equals(_literals4, pattern.End); + + /// <summary>HMSF (no days)</summary> + internal bool FullHMSFMatch(TimeSpanFormat.FormatLiterals pattern) => + _sepCount == 5 + && _numCount == 4 + && StringSpanHelpers.Equals(_literals0, pattern.Start) + && StringSpanHelpers.Equals(_literals1, pattern.HourMinuteSep) + && StringSpanHelpers.Equals(_literals2, pattern.MinuteSecondSep) + && StringSpanHelpers.Equals(_literals3, pattern.SecondFractionSep) + && StringSpanHelpers.Equals(_literals4, pattern.End); + + internal TTT _lastSeenTTT; + internal int _tokenCount; + internal int _sepCount; + internal int _numCount; + + private TimeSpanFormat.FormatLiterals _posLoc; + private TimeSpanFormat.FormatLiterals _negLoc; + private bool _posLocInit; + private bool _negLocInit; + private string _fullPosPattern; + private string _fullNegPattern; + + private const int MaxTokens = 11; + private const int MaxLiteralTokens = 6; + private const int MaxNumericTokens = 5; + + internal TimeSpanToken _numbers0, _numbers1, _numbers2, _numbers3, _numbers4; // MaxNumbericTokens = 5 + internal ReadOnlySpan<char> _literals0, _literals1, _literals2, _literals3, _literals4, _literals5; // MaxLiteralTokens=6 + + internal void Init(DateTimeFormatInfo dtfi) + { + Debug.Assert(dtfi != null); + + _lastSeenTTT = TTT.None; + _tokenCount = 0; + _sepCount = 0; + _numCount = 0; + + _fullPosPattern = dtfi.FullTimeSpanPositivePattern; + _fullNegPattern = dtfi.FullTimeSpanNegativePattern; + _posLocInit = false; + _negLocInit = false; + } + + internal bool ProcessToken(ref TimeSpanToken tok, ref TimeSpanResult result) + { + switch (tok._ttt) + { + case TTT.Num: + if ((_tokenCount == 0 && !AddSep(default(ReadOnlySpan<char>), ref result)) || !AddNum(tok, ref result)) + { + return false; + } + break; + + case TTT.Sep: + if (!AddSep(tok._sep, ref result)) + { + return false; + } + break; + + case TTT.NumOverflow: + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + + default: + // Some unknown token or a repeat token type in the input + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + _lastSeenTTT = tok._ttt; + Debug.Assert(_tokenCount == (_sepCount + _numCount), "tokenCount == (SepCount + NumCount)"); + return true; + } + + private bool AddSep(ReadOnlySpan<char> sep, ref TimeSpanResult result) + { + if (_sepCount >= MaxLiteralTokens || _tokenCount >= MaxTokens) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + switch (_sepCount++) + { + case 0: _literals0 = sep; break; + case 1: _literals1 = sep; break; + case 2: _literals2 = sep; break; + case 3: _literals3 = sep; break; + case 4: _literals4 = sep; break; + default: _literals5 = sep; break; + } + + _tokenCount++; + return true; + } + private bool AddNum(TimeSpanToken num, ref TimeSpanResult result) + { + if (_numCount >= MaxNumericTokens || _tokenCount >= MaxTokens) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + switch (_numCount++) + { + case 0: _numbers0 = num; break; + case 1: _numbers1 = num; break; + case 2: _numbers2 = num; break; + case 3: _numbers3 = num; break; + default: _numbers4 = num; break; + } + + _tokenCount++; + return true; + } + } + + /// <summary>Store the result of the parsing.</summary> + private struct TimeSpanResult + { + internal TimeSpan parsedTimeSpan; + private readonly bool _throwOnFailure; + + internal TimeSpanResult(bool throwOnFailure) + { + parsedTimeSpan = default(TimeSpan); + _throwOnFailure = throwOnFailure; + } + + internal bool SetFailure(ParseFailureKind kind, string resourceKey, object messageArgument = null, string argumentName = null) + { + if (!_throwOnFailure) + { + return false; + } + + string message = SR.GetResourceString(resourceKey); + switch (kind) + { + case ParseFailureKind.ArgumentNull: + Debug.Assert(argumentName != null); + throw new ArgumentNullException(argumentName, message); + + case ParseFailureKind.FormatWithParameter: + throw new FormatException(SR.Format(message, messageArgument)); + + case ParseFailureKind.Overflow: + throw new OverflowException(message); + + default: + Debug.Assert(kind == ParseFailureKind.Format, $"Unexpected failure {kind}"); + throw new FormatException(message); + } + } + } + + internal static long Pow10(int pow) + { + switch (pow) + { + case 0: return 1; + case 1: return 10; + case 2: return 100; + case 3: return 1000; + case 4: return 10000; + case 5: return 100000; + case 6: return 1000000; + case 7: return 10000000; + default: return (long)Math.Pow(10, pow); + } + } + + private static bool TryTimeToTicks(bool positive, TimeSpanToken days, TimeSpanToken hours, TimeSpanToken minutes, TimeSpanToken seconds, TimeSpanToken fraction, out long result) + { + if (days._num > MaxDays || + hours._num > MaxHours || + minutes._num > MaxMinutes || + seconds._num > MaxSeconds || + fraction.IsInvalidFraction()) + { + result = 0; + return false; + } + + long ticks = ((long)days._num * 3600 * 24 + (long)hours._num * 3600 + (long)minutes._num * 60 + seconds._num) * 1000; + if (ticks > InternalGlobalizationHelper.MaxMilliSeconds || ticks < InternalGlobalizationHelper.MinMilliSeconds) + { + result = 0; + return false; + } + + // Normalize the fraction component + // + // string representation => (zeroes,num) => resultant fraction ticks + // --------------------- ------------ ------------------------ + // ".9999999" => (0,9999999) => 9,999,999 ticks (same as constant maxFraction) + // ".1" => (0,1) => 1,000,000 ticks + // ".01" => (1,1) => 100,000 ticks + // ".001" => (2,1) => 10,000 ticks + long f = fraction._num; + if (f != 0) + { + long lowerLimit = InternalGlobalizationHelper.TicksPerTenthSecond; + if (fraction._zeroes > 0) + { + long divisor = Pow10(fraction._zeroes); + lowerLimit = lowerLimit / divisor; + } + + while (f < lowerLimit) + { + f *= 10; + } + } + + result = ticks * TimeSpan.TicksPerMillisecond + f; + if (positive && result < 0) + { + result = 0; + return false; + } + + return true; + } + + internal static TimeSpan Parse(ReadOnlySpan<char> input, IFormatProvider formatProvider) + { + var parseResult = new TimeSpanResult(throwOnFailure: true); + bool success = TryParseTimeSpan(input, TimeSpanStandardStyles.Any, formatProvider, ref parseResult); + Debug.Assert(success, "Should have thrown on failure"); + return parseResult.parsedTimeSpan; + } + + internal static bool TryParse(ReadOnlySpan<char> input, IFormatProvider formatProvider, out TimeSpan result) + { + var parseResult = new TimeSpanResult(throwOnFailure: false); + + if (TryParseTimeSpan(input, TimeSpanStandardStyles.Any, formatProvider, ref parseResult)) + { + result = parseResult.parsedTimeSpan; + return true; + } + + result = default(TimeSpan); + return false; + } + + internal static TimeSpan ParseExact(ReadOnlySpan<char> input, string format, IFormatProvider formatProvider, TimeSpanStyles styles) + { + var parseResult = new TimeSpanResult(throwOnFailure: true); + bool success = TryParseExactTimeSpan(input, format, formatProvider, styles, ref parseResult); + Debug.Assert(success, "Should have thrown on failure"); + return parseResult.parsedTimeSpan; + } + + internal static bool TryParseExact(ReadOnlySpan<char> input, string format, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) + { + var parseResult = new TimeSpanResult(throwOnFailure: false); + + if (TryParseExactTimeSpan(input, format, formatProvider, styles, ref parseResult)) + { + result = parseResult.parsedTimeSpan; + return true; + } + + result = default(TimeSpan); + return false; + } + + internal static TimeSpan ParseExactMultiple(ReadOnlySpan<char> input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles) + { + var parseResult = new TimeSpanResult(throwOnFailure: true); + bool success = TryParseExactMultipleTimeSpan(input, formats, formatProvider, styles, ref parseResult); + Debug.Assert(success, "Should have thrown on failure"); + return parseResult.parsedTimeSpan; + } + + internal static bool TryParseExactMultiple(ReadOnlySpan<char> input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) + { + var parseResult = new TimeSpanResult(throwOnFailure: false); + + if (TryParseExactMultipleTimeSpan(input, formats, formatProvider, styles, ref parseResult)) + { + result = parseResult.parsedTimeSpan; + return true; + } + + result = default(TimeSpan); + return false; + } + + /// <summary>Common private Parse method called by both Parse and TryParse.</summary> + private static bool TryParseTimeSpan(ReadOnlySpan<char> input, TimeSpanStandardStyles style, IFormatProvider formatProvider, ref TimeSpanResult result) + { + input = input.Trim(); + if (input.IsEmpty) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + var tokenizer = new TimeSpanTokenizer(input); + + var raw = new TimeSpanRawInfo(); + raw.Init(DateTimeFormatInfo.GetInstance(formatProvider)); + + TimeSpanToken tok = tokenizer.GetNextToken(); + + // The following loop will break out when we reach the end of the str or + // when we can determine that the input is invalid. + while (tok._ttt != TTT.End) + { + if (!raw.ProcessToken(ref tok, ref result)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + tok = tokenizer.GetNextToken(); + } + Debug.Assert(tokenizer.EOL); + + if (!ProcessTerminalState(ref raw, style, ref result)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + return true; + } + + /// <summary> + /// Validate the terminal state of a standard format parse. + /// Sets result.parsedTimeSpan on success. + /// Calculates the resultant TimeSpan from the TimeSpanRawInfo. + /// </summary> + /// <remarks> + /// try => +InvariantPattern, -InvariantPattern, +LocalizedPattern, -LocalizedPattern + /// 1) Verify Start matches + /// 2) Verify End matches + /// 3) 1 number => d + /// 2 numbers => h:m + /// 3 numbers => h:m:s | d.h:m | h:m:.f + /// 4 numbers => h:m:s.f | d.h:m:s | d.h:m:.f + /// 5 numbers => d.h:m:s.f + /// </remarks> + private static bool ProcessTerminalState(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) + { + if (raw._lastSeenTTT == TTT.Num) + { + TimeSpanToken tok = new TimeSpanToken(); + tok._ttt = TTT.Sep; + if (!raw.ProcessToken(ref tok, ref result)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + } + + switch (raw._numCount) + { + case 1: return ProcessTerminal_D(ref raw, style, ref result); + case 2: return ProcessTerminal_HM(ref raw, style, ref result); + case 3: return ProcessTerminal_HM_S_D(ref raw, style, ref result); + case 4: return ProcessTerminal_HMS_F_D(ref raw, style, ref result); + case 5: return ProcessTerminal_DHMSF(ref raw, style, ref result); + default: return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + } + + /// <summary>Validate the 5-number "Days.Hours:Minutes:Seconds.Fraction" terminal case.</summary> + private static bool ProcessTerminal_DHMSF(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) + { + if (raw._sepCount != 6) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + Debug.Assert(raw._numCount == 5); + + bool inv = (style & TimeSpanStandardStyles.Invariant) != 0; + bool loc = (style & TimeSpanStandardStyles.Localized) != 0; + bool positive = false; + bool match = false; + + if (inv) + { + if (raw.FullMatch(raw.PositiveInvariant)) + { + match = true; + positive = true; + } + if (!match && raw.FullMatch(raw.NegativeInvariant)) + { + match = true; + positive = false; + } + } + + if (loc) + { + if (!match && raw.FullMatch(raw.PositiveLocalized)) + { + match = true; + positive = true; + } + if (!match && raw.FullMatch(raw.NegativeLocalized)) + { + match = true; + positive = false; + } + } + + if (match) + { + long ticks; + + if (!TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, raw._numbers4, out ticks)) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + + if (!positive) + { + ticks = -ticks; + if (ticks > 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + result.parsedTimeSpan._ticks = ticks; + return true; + } + + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + + /// <summary> + /// Validate the ambiguous 4-number "Hours:Minutes:Seconds.Fraction", "Days.Hours:Minutes:Seconds", + /// or "Days.Hours:Minutes:.Fraction" terminal case. + /// </summary> + private static bool ProcessTerminal_HMS_F_D(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) + { + if (raw._sepCount != 5 || (style & TimeSpanStandardStyles.RequireFull) != 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + Debug.Assert(raw._numCount == 4); + + bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0); + bool loc = ((style & TimeSpanStandardStyles.Localized) != 0); + + long ticks = 0; + bool positive = false, match = false, overflow = false; + var zero = new TimeSpanToken(0); + + if (inv) + { + if (raw.FullHMSFMatch(raw.PositiveInvariant)) + { + positive = true; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMSMatch(raw.PositiveInvariant)) + { + positive = true; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullAppCompatMatch(raw.PositiveInvariant)) + { + positive = true; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, raw._numbers3, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullHMSFMatch(raw.NegativeInvariant)) + { + positive = false; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMSMatch(raw.NegativeInvariant)) + { + positive = false; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullAppCompatMatch(raw.NegativeInvariant)) + { + positive = false; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, raw._numbers3, out ticks); + overflow = overflow || !match; + } + } + + if (loc) + { + if (!match && raw.FullHMSFMatch(raw.PositiveLocalized)) + { + positive = true; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMSMatch(raw.PositiveLocalized)) + { + positive = true; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullAppCompatMatch(raw.PositiveLocalized)) + { + positive = true; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, raw._numbers3, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullHMSFMatch(raw.NegativeLocalized)) + { + positive = false; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMSMatch(raw.NegativeLocalized)) + { + positive = false; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, raw._numbers3, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullAppCompatMatch(raw.NegativeLocalized)) + { + positive = false; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, raw._numbers3, out ticks); + overflow = overflow || !match; + } + } + + if (match) + { + if (!positive) + { + ticks = -ticks; + if (ticks > 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + result.parsedTimeSpan._ticks = ticks; + return true; + } + + return overflow ? + result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)) : // we found at least one literal pattern match but the numbers just didn't fit + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); // we couldn't find a thing + } + + /// <summary>Validate the ambiguous 3-number "Hours:Minutes:Seconds", "Days.Hours:Minutes", or "Hours:Minutes:.Fraction" terminal case.</summary> + private static bool ProcessTerminal_HM_S_D(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) + { + if (raw._sepCount != 4 || (style & TimeSpanStandardStyles.RequireFull) != 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + Debug.Assert(raw._numCount == 3); + + bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0); + bool loc = ((style & TimeSpanStandardStyles.Localized) != 0); + + bool positive = false, match = false, overflow = false; + var zero = new TimeSpanToken(0); + long ticks = 0; + + if (inv) + { + if (raw.FullHMSMatch(raw.PositiveInvariant)) + { + positive = true; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMMatch(raw.PositiveInvariant)) + { + positive = true; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.PartialAppCompatMatch(raw.PositiveInvariant)) + { + positive = true; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, zero, raw._numbers2, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullHMSMatch(raw.NegativeInvariant)) + { + positive = false; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMMatch(raw.NegativeInvariant)) + { + positive = false; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.PartialAppCompatMatch(raw.NegativeInvariant)) + { + positive = false; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, zero, raw._numbers2, out ticks); + overflow = overflow || !match; + } + } + + if (loc) + { + if (!match && raw.FullHMSMatch(raw.PositiveLocalized)) + { + positive = true; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMMatch(raw.PositiveLocalized)) + { + positive = true; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.PartialAppCompatMatch(raw.PositiveLocalized)) + { + positive = true; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, zero, raw._numbers2, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullHMSMatch(raw.NegativeLocalized)) + { + positive = false; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, raw._numbers2, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.FullDHMMatch(raw.NegativeLocalized)) + { + positive = false; + match = TryTimeToTicks(positive, raw._numbers0, raw._numbers1, raw._numbers2, zero, zero, out ticks); + overflow = overflow || !match; + } + + if (!match && raw.PartialAppCompatMatch(raw.NegativeLocalized)) + { + positive = false; + match = TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, zero, raw._numbers2, out ticks); + overflow = overflow || !match; + } + } + + if (match) + { + if (!positive) + { + ticks = -ticks; + if (ticks > 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + result.parsedTimeSpan._ticks = ticks; + return true; + } + + return overflow ? + result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)) : // we found at least one literal pattern match but the numbers just didn't fit + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); // we couldn't find a thing + } + + /// <summary>Validate the 2-number "Hours:Minutes" terminal case.</summary> + private static bool ProcessTerminal_HM(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) + { + if (raw._sepCount != 3 || (style & TimeSpanStandardStyles.RequireFull) != 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + Debug.Assert(raw._numCount == 2); + + bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0); + bool loc = ((style & TimeSpanStandardStyles.Localized) != 0); + + bool positive = false, match = false; + + if (inv) + { + if (raw.FullHMMatch(raw.PositiveInvariant)) + { + match = true; + positive = true; + } + + if (!match && raw.FullHMMatch(raw.NegativeInvariant)) + { + match = true; + positive = false; + } + } + + if (loc) + { + if (!match && raw.FullHMMatch(raw.PositiveLocalized)) + { + match = true; + positive = true; + } + + if (!match && raw.FullHMMatch(raw.NegativeLocalized)) + { + match = true; + positive = false; + } + } + + if (match) + { + long ticks = 0; + var zero = new TimeSpanToken(0); + + if (!TryTimeToTicks(positive, zero, raw._numbers0, raw._numbers1, zero, zero, out ticks)) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + + if (!positive) + { + ticks = -ticks; + if (ticks > 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + result.parsedTimeSpan._ticks = ticks; + return true; + } + + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + /// <summary>Validate the 1-number "Days" terminal case.</summary> + private static bool ProcessTerminal_D(ref TimeSpanRawInfo raw, TimeSpanStandardStyles style, ref TimeSpanResult result) + { + if (raw._sepCount != 2 || (style & TimeSpanStandardStyles.RequireFull) != 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + Debug.Assert(raw._numCount == 1); + + bool inv = ((style & TimeSpanStandardStyles.Invariant) != 0); + bool loc = ((style & TimeSpanStandardStyles.Localized) != 0); + + bool positive = false, match = false; + + if (inv) + { + if (raw.FullDMatch(raw.PositiveInvariant)) + { + match = true; + positive = true; + } + + if (!match && raw.FullDMatch(raw.NegativeInvariant)) + { + match = true; + positive = false; + } + } + + if (loc) + { + if (!match && raw.FullDMatch(raw.PositiveLocalized)) + { + match = true; + positive = true; + } + + if (!match && raw.FullDMatch(raw.NegativeLocalized)) + { + match = true; + positive = false; + } + } + + if (match) + { + long ticks = 0; + var zero = new TimeSpanToken(0); + + if (!TryTimeToTicks(positive, raw._numbers0, zero, zero, zero, zero, out ticks)) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + + if (!positive) + { + ticks = -ticks; + if (ticks > 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + result.parsedTimeSpan._ticks = ticks; + return true; + } + + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + /// <summary>Common private ParseExact method called by both ParseExact and TryParseExact.</summary> + private static bool TryParseExactTimeSpan(ReadOnlySpan<char> input, string format, IFormatProvider formatProvider, TimeSpanStyles styles, ref TimeSpanResult result) + { + if (format == null) + { + return result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), argumentName: nameof(format)); + } + + if (format.Length == 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier)); + } + + if (format.Length == 1) + { + switch (format[0]) + { + case 'c': + case 't': + case 'T': + return TryParseTimeSpanConstant(input, ref result); // fast path for legacy style TimeSpan formats. + + case 'g': + return TryParseTimeSpan(input, TimeSpanStandardStyles.Localized, formatProvider, ref result); + + case 'G': + return TryParseTimeSpan(input, TimeSpanStandardStyles.Localized | TimeSpanStandardStyles.RequireFull, formatProvider, ref result); + + default: + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier)); + } + } + + return TryParseByFormat(input, format, styles, ref result); + } + + /// <summary>Parse the TimeSpan instance using the specified format. Used by TryParseExactTimeSpan.</summary> + private static bool TryParseByFormat(ReadOnlySpan<char> input, string format, TimeSpanStyles styles, ref TimeSpanResult result) + { + Debug.Assert(format != null, "format != null"); + + bool seenDD = false; // already processed days? + bool seenHH = false; // already processed hours? + bool seenMM = false; // already processed minutes? + bool seenSS = false; // already processed seconds? + bool seenFF = false; // already processed fraction? + + int dd = 0; // parsed days + int hh = 0; // parsed hours + int mm = 0; // parsed minutes + int ss = 0; // parsed seconds + int leadingZeroes = 0; // number of leading zeroes in the parsed fraction + int ff = 0; // parsed fraction + int i = 0; // format string position + int tokenLen = 0; // length of current format token, used to update index 'i' + + var tokenizer = new TimeSpanTokenizer(input, -1); + + while (i < format.Length) + { + char ch = format[i]; + int nextFormatChar; + switch (ch) + { + case 'h': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > 2 || seenHH || !ParseExactDigits(ref tokenizer, tokenLen, out hh)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + seenHH = true; + break; + + case 'm': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > 2 || seenMM || !ParseExactDigits(ref tokenizer, tokenLen, out mm)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + seenMM = true; + break; + + case 's': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > 2 || seenSS || !ParseExactDigits(ref tokenizer, tokenLen, out ss)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + seenSS = true; + break; + + case 'f': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits || seenFF || !ParseExactDigits(ref tokenizer, tokenLen, tokenLen, out leadingZeroes, out ff)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + seenFF = true; + break; + + case 'F': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits || seenFF) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + ParseExactDigits(ref tokenizer, tokenLen, tokenLen, out leadingZeroes, out ff); + seenFF = true; + break; + + case 'd': + tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch); + int tmp = 0; + if (tokenLen > 8 || seenDD || !ParseExactDigits(ref tokenizer, (tokenLen < 2) ? 1 : tokenLen, (tokenLen < 2) ? 8 : tokenLen, out tmp, out dd)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + seenDD = true; + break; + + case '\'': + case '\"': + StringBuilder enquotedString = new StringBuilder(); + if (!DateTimeParse.TryParseQuoteString(format, i, enquotedString, out tokenLen)) + { + return result.SetFailure(ParseFailureKind.FormatWithParameter, nameof(SR.Format_BadQuote), ch); + } + if (!ParseExactLiteral(ref tokenizer, enquotedString)) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + break; + + case '%': + // Optional format character. + // For example, format string "%d" will print day + // Most of the cases, "%" can be ignored. + nextFormatChar = DateTimeFormat.ParseNextChar(format, i); + + // nextFormatChar will be -1 if we already reach the end of the format string. + // Besides, we will not allow "%%" appear in the pattern. + if (nextFormatChar >= 0 && nextFormatChar != '%') + { + tokenLen = 1; // skip the '%' and process the format character + break; + } + else + { + // This means that '%' is at the end of the format string or + // "%%" appears in the format string. + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + + case '\\': + // Escaped character. Can be used to insert character into the format string. + // For example, "\d" will insert the character 'd' into the string. + // + nextFormatChar = DateTimeFormat.ParseNextChar(format, i); + if (nextFormatChar >= 0 && tokenizer.NextChar == (char)nextFormatChar) + { + tokenLen = 2; + } + else + { + // This means that '\' is at the end of the format string or the literal match failed. + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + break; + + default: + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_InvalidString)); + } + + i += tokenLen; + } + + + if (!tokenizer.EOL) + { + // the custom format didn't consume the entire input + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + bool positive = (styles & TimeSpanStyles.AssumeNegative) == 0; + if (TryTimeToTicks(positive, new TimeSpanToken(dd), + new TimeSpanToken(hh), + new TimeSpanToken(mm), + new TimeSpanToken(ss), + new TimeSpanToken(ff, leadingZeroes), + out long ticks)) + { + if (!positive) + { + ticks = -ticks; + } + + result.parsedTimeSpan._ticks = ticks; + return true; + } + else + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + private static bool ParseExactDigits(ref TimeSpanTokenizer tokenizer, int minDigitLength, out int result) + { + result = 0; + int zeroes = 0; + int maxDigitLength = (minDigitLength == 1) ? 2 : minDigitLength; + return ParseExactDigits(ref tokenizer, minDigitLength, maxDigitLength, out zeroes, out result); + } + + private static bool ParseExactDigits(ref TimeSpanTokenizer tokenizer, int minDigitLength, int maxDigitLength, out int zeroes, out int result) + { + int tmpResult = 0, tmpZeroes = 0; + + int tokenLength = 0; + while (tokenLength < maxDigitLength) + { + char ch = tokenizer.NextChar; + if (ch < '0' || ch > '9') + { + tokenizer.BackOne(); + break; + } + + tmpResult = tmpResult * 10 + (ch - '0'); + if (tmpResult == 0) tmpZeroes++; + tokenLength++; + } + + zeroes = tmpZeroes; + result = tmpResult; + return tokenLength >= minDigitLength; + } + + private static bool ParseExactLiteral(ref TimeSpanTokenizer tokenizer, StringBuilder enquotedString) + { + for (int i = 0; i < enquotedString.Length; i++) + { + if (enquotedString[i] != tokenizer.NextChar) + { + return false; + } + } + + return true; + } + + /// <summary> + /// Parses the "c" (constant) format. This code is 100% identical to the non-globalized v1.0-v3.5 TimeSpan.Parse() routine + /// and exists for performance/appcompat with legacy callers who cannot move onto the globalized Parse overloads. + /// </summary> + private static bool TryParseTimeSpanConstant(ReadOnlySpan<char> input, ref TimeSpanResult result) => + new StringParser().TryParse(input, ref result); + + [IsByRefLike] + private struct StringParser + { + private ReadOnlySpan<char> _str; + private char _ch; + private int _pos; + private int _len; + + internal void NextChar() + { + if (_pos < _len) + { + _pos++; + } + + _ch = _pos < _len ? + _str[_pos] : + (char)0; + } + + internal char NextNonDigit() + { + int i = _pos; + while (i < _len) + { + char ch = _str[i]; + if (ch < '0' || ch > '9') return ch; + i++; + } + + return (char)0; + } + + internal bool TryParse(ReadOnlySpan<char> input, ref TimeSpanResult result) + { + result.parsedTimeSpan._ticks = 0; + + _str = input; + _len = input.Length; + _pos = -1; + NextChar(); + SkipBlanks(); + + bool negative = false; + if (_ch == '-') + { + negative = true; + NextChar(); + } + + long time; + if (NextNonDigit() == ':') + { + if (!ParseTime(out time, ref result)) + { + return false; + }; + } + else + { + int days; + if (!ParseInt((int)(0x7FFFFFFFFFFFFFFFL / TimeSpan.TicksPerDay), out days, ref result)) + { + return false; + } + + time = days * TimeSpan.TicksPerDay; + + if (_ch == '.') + { + NextChar(); + long remainingTime; + if (!ParseTime(out remainingTime, ref result)) + { + return false; + }; + time += remainingTime; + } + } + + if (negative) + { + time = -time; + // Allow -0 as well + if (time > 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + else + { + if (time < 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + } + + SkipBlanks(); + + if (_pos < _len) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + result.parsedTimeSpan._ticks = time; + return true; + } + + internal bool ParseInt(int max, out int i, ref TimeSpanResult result) + { + i = 0; + int p = _pos; + while (_ch >= '0' && _ch <= '9') + { + if ((i & 0xF0000000) != 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + + i = i * 10 + _ch - '0'; + if (i < 0) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + + NextChar(); + } + + if (p == _pos) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + if (i > max) + { + return result.SetFailure(ParseFailureKind.Overflow, nameof(SR.Overflow_TimeSpanElementTooLarge)); + } + + return true; + } + + internal bool ParseTime(out long time, ref TimeSpanResult result) + { + time = 0; + int unit; + + if (!ParseInt(23, out unit, ref result)) + { + return false; + } + + time = unit * TimeSpan.TicksPerHour; + if (_ch != ':') + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + NextChar(); + + if (!ParseInt(59, out unit, ref result)) + { + return false; + } + + time += unit * TimeSpan.TicksPerMinute; + + if (_ch == ':') + { + NextChar(); + + // allow seconds with the leading zero + if (_ch != '.') + { + if (!ParseInt(59, out unit, ref result)) + { + return false; + } + time += unit * TimeSpan.TicksPerSecond; + } + + if (_ch == '.') + { + NextChar(); + int f = (int)TimeSpan.TicksPerSecond; + while (f > 1 && _ch >= '0' && _ch <= '9') + { + f /= 10; + time += (_ch - '0') * f; + NextChar(); + } + } + } + + return true; + } + + internal void SkipBlanks() + { + while (_ch == ' ' || _ch == '\t') NextChar(); + } + } + + /// <summary>Common private ParseExactMultiple method called by both ParseExactMultiple and TryParseExactMultiple.</summary> + private static bool TryParseExactMultipleTimeSpan(ReadOnlySpan<char> input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, ref TimeSpanResult result) + { + if (formats == null) + { + return result.SetFailure(ParseFailureKind.ArgumentNull, nameof(SR.ArgumentNull_String), argumentName: nameof(formats)); + } + + if (input.Length == 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + + if (formats.Length == 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier)); + } + + // Do a loop through the provided formats and see if we can parse succesfully in + // one of the formats. + for (int i = 0; i < formats.Length; i++) + { + if (formats[i] == null || formats[i].Length == 0) + { + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadFormatSpecifier)); + } + + // Create a new non-throwing result each time to ensure the runs are independent. + TimeSpanResult innerResult = new TimeSpanResult(throwOnFailure: false); + + if (TryParseExactTimeSpan(input, formats[i], formatProvider, styles, ref innerResult)) + { + result.parsedTimeSpan = innerResult.parsedTimeSpan; + return true; + } + } + + return result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_BadTimeSpan)); + } + } +} diff --git a/src/mscorlib/shared/System/Guid.cs b/src/mscorlib/shared/System/Guid.cs new file mode 100644 index 0000000000..b8b4dd5f0f --- /dev/null +++ b/src/mscorlib/shared/System/Guid.cs @@ -0,0 +1,1409 @@ +// 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.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Diagnostics; +using System.Diagnostics.Contracts; + +namespace System +{ + // Represents a Globally Unique Identifier. + [StructLayout(LayoutKind.Sequential)] + [Serializable] + [Runtime.Versioning.NonVersionable] // This only applies to field layout + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public partial struct Guid : IFormattable, IComparable, IComparable<Guid>, IEquatable<Guid> + { + public static readonly Guid Empty = new Guid(); + + //////////////////////////////////////////////////////////////////////////////// + // Member variables + //////////////////////////////////////////////////////////////////////////////// + private int _a; // Do not rename (binary serialization) + private short _b; // Do not rename (binary serialization) + private short _c; // Do not rename (binary serialization) + private byte _d; // Do not rename (binary serialization) + private byte _e; // Do not rename (binary serialization) + private byte _f; // Do not rename (binary serialization) + private byte _g; // Do not rename (binary serialization) + private byte _h; // Do not rename (binary serialization) + private byte _i; // Do not rename (binary serialization) + private byte _j; // Do not rename (binary serialization) + private byte _k; // Do not rename (binary serialization) + + //////////////////////////////////////////////////////////////////////////////// + // Constructors + //////////////////////////////////////////////////////////////////////////////// + + // Creates a new guid from an array of bytes. + public Guid(byte[] b) : + this(new ReadOnlySpan<byte>(b ?? throw new ArgumentNullException(nameof(b)))) + { + } + + // Creates a new guid from a read-only span. + public Guid(ReadOnlySpan<byte> b) + { + if (b.Length != 16) + throw new ArgumentException(SR.Format(SR.Arg_GuidArrayCtor, "16"), nameof(b)); + + _a = b[3] << 24 | b[2] << 16 | b[1] << 8 | b[0]; + _b = (short)(b[5] << 8 | b[4]); + _c = (short)(b[7] << 8 | b[6]); + _d = b[8]; + _e = b[9]; + _f = b[10]; + _g = b[11]; + _h = b[12]; + _i = b[13]; + _j = b[14]; + _k = b[15]; + } + + [CLSCompliant(false)] + public Guid(uint a, ushort b, ushort c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k) + { + _a = (int)a; + _b = (short)b; + _c = (short)c; + _d = d; + _e = e; + _f = f; + _g = g; + _h = h; + _i = i; + _j = j; + _k = k; + } + + // Creates a new GUID initialized to the value represented by the arguments. + // + public Guid(int a, short b, short c, byte[] d) + { + if (d == null) + throw new ArgumentNullException(nameof(d)); + // Check that array is not too big + if (d.Length != 8) + throw new ArgumentException(SR.Format(SR.Arg_GuidArrayCtor, "8"), nameof(d)); + Contract.EndContractBlock(); + + _a = a; + _b = b; + _c = c; + _d = d[0]; + _e = d[1]; + _f = d[2]; + _g = d[3]; + _h = d[4]; + _i = d[5]; + _j = d[6]; + _k = d[7]; + } + + // Creates a new GUID initialized to the value represented by the + // arguments. The bytes are specified like this to avoid endianness issues. + // + public Guid(int a, short b, short c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k) + { + _a = a; + _b = b; + _c = c; + _d = d; + _e = e; + _f = f; + _g = g; + _h = h; + _i = i; + _j = j; + _k = k; + } + + [Flags] + private enum GuidStyles + { + None = 0x00000000, + AllowParenthesis = 0x00000001, //Allow the guid to be enclosed in parens + AllowBraces = 0x00000002, //Allow the guid to be enclosed in braces + AllowDashes = 0x00000004, //Allow the guid to contain dash group separators + AllowHexPrefix = 0x00000008, //Allow the guid to contain {0xdd,0xdd} + RequireParenthesis = 0x00000010, //Require the guid to be enclosed in parens + RequireBraces = 0x00000020, //Require the guid to be enclosed in braces + RequireDashes = 0x00000040, //Require the guid to contain dash group separators + RequireHexPrefix = 0x00000080, //Require the guid to contain {0xdd,0xdd} + + HexFormat = RequireBraces | RequireHexPrefix, /* X */ + NumberFormat = None, /* N */ + DigitFormat = RequireDashes, /* D */ + BraceFormat = RequireBraces | RequireDashes, /* B */ + ParenthesisFormat = RequireParenthesis | RequireDashes, /* P */ + + Any = AllowParenthesis | AllowBraces | AllowDashes | AllowHexPrefix, + } + private enum GuidParseThrowStyle + { + None = 0, + All = 1, + AllButOverflow = 2 + } + private enum ParseFailureKind + { + None = 0, + ArgumentNull = 1, + Format = 2, + FormatWithParameter = 3, + NativeException = 4, + FormatWithInnerException = 5 + } + + // This will store the result of the parsing. And it will eventually be used to construct a Guid instance. + private struct GuidResult + { + internal Guid _parsedGuid; + internal GuidParseThrowStyle _throwStyle; + + private ParseFailureKind _failure; + private string _failureMessageID; + private object _failureMessageFormatArgument; + private string _failureArgumentName; + private Exception _innerException; + + internal void Init(GuidParseThrowStyle canThrow) + { + _throwStyle = canThrow; + } + + internal void SetFailure(Exception nativeException) + { + _failure = ParseFailureKind.NativeException; + _innerException = nativeException; + } + + internal void SetFailure(ParseFailureKind failure, string failureMessageID) + { + SetFailure(failure, failureMessageID, null, null, null); + } + + internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument) + { + SetFailure(failure, failureMessageID, failureMessageFormatArgument, null, null); + } + + internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument, + string failureArgumentName, Exception innerException) + { + Debug.Assert(failure != ParseFailureKind.NativeException, "ParseFailureKind.NativeException should not be used with this overload"); + _failure = failure; + _failureMessageID = failureMessageID; + _failureMessageFormatArgument = failureMessageFormatArgument; + _failureArgumentName = failureArgumentName; + _innerException = innerException; + if (_throwStyle != GuidParseThrowStyle.None) + { + throw GetGuidParseException(); + } + } + + internal Exception GetGuidParseException() + { + switch (_failure) + { + case ParseFailureKind.ArgumentNull: + return new ArgumentNullException(_failureArgumentName, SR.GetResourceString(_failureMessageID)); + + case ParseFailureKind.FormatWithInnerException: + return new FormatException(SR.GetResourceString(_failureMessageID), _innerException); + + case ParseFailureKind.FormatWithParameter: + return new FormatException(SR.Format(SR.GetResourceString(_failureMessageID), _failureMessageFormatArgument)); + + case ParseFailureKind.Format: + return new FormatException(SR.GetResourceString(_failureMessageID)); + + case ParseFailureKind.NativeException: + return _innerException; + + default: + Debug.Assert(false, "Unknown GuidParseFailure: " + _failure); + return new FormatException(SR.Format_GuidUnrecognized); + } + } + } + + // Creates a new guid based on the value in the string. The value is made up + // of hex digits speared by the dash ("-"). The string may begin and end with + // brackets ("{", "}"). + // + // The string must be of the form dddddddd-dddd-dddd-dddd-dddddddddddd. where + // d is a hex digit. (That is 8 hex digits, followed by 4, then 4, then 4, + // then 12) such as: "CA761232-ED42-11CE-BACD-00AA0057B223" + // + public Guid(string g) + { + if (g == null) + { + throw new ArgumentNullException(nameof(g)); + } + Contract.EndContractBlock(); + + GuidResult result = new GuidResult(); + result.Init(GuidParseThrowStyle.All); + if (TryParseGuid(g, GuidStyles.Any, ref result)) + { + this = result._parsedGuid; + } + else + { + throw result.GetGuidParseException(); + } + } + + public static Guid Parse(string input) + { + if (input == null) + { + throw new ArgumentNullException(nameof(input)); + } + Contract.EndContractBlock(); + + GuidResult result = new GuidResult(); + result.Init(GuidParseThrowStyle.AllButOverflow); + if (TryParseGuid(input, GuidStyles.Any, ref result)) + { + return result._parsedGuid; + } + else + { + throw result.GetGuidParseException(); + } + } + + public static bool TryParse(string input, out Guid result) + { + GuidResult parseResult = new GuidResult(); + parseResult.Init(GuidParseThrowStyle.None); + if (TryParseGuid(input, GuidStyles.Any, ref parseResult)) + { + result = parseResult._parsedGuid; + return true; + } + else + { + result = Empty; + return false; + } + } + + public static Guid ParseExact(string input, string format) + { + if (input == null) + throw new ArgumentNullException(nameof(input)); + + if (format == null) + throw new ArgumentNullException(nameof(format)); + + if (format.Length != 1) + { + // all acceptable format strings are of length 1 + throw new FormatException(SR.Format_InvalidGuidFormatSpecification); + } + + GuidStyles style; + char formatCh = format[0]; + if (formatCh == 'D' || formatCh == 'd') + { + style = GuidStyles.DigitFormat; + } + else if (formatCh == 'N' || formatCh == 'n') + { + style = GuidStyles.NumberFormat; + } + else if (formatCh == 'B' || formatCh == 'b') + { + style = GuidStyles.BraceFormat; + } + else if (formatCh == 'P' || formatCh == 'p') + { + style = GuidStyles.ParenthesisFormat; + } + else if (formatCh == 'X' || formatCh == 'x') + { + style = GuidStyles.HexFormat; + } + else + { + throw new FormatException(SR.Format_InvalidGuidFormatSpecification); + } + + GuidResult result = new GuidResult(); + result.Init(GuidParseThrowStyle.AllButOverflow); + if (TryParseGuid(input, style, ref result)) + { + return result._parsedGuid; + } + else + { + throw result.GetGuidParseException(); + } + } + + public static bool TryParseExact(string input, string format, out Guid result) + { + if (format == null || format.Length != 1) + { + result = Empty; + return false; + } + + GuidStyles style; + char formatCh = format[0]; + + if (formatCh == 'D' || formatCh == 'd') + { + style = GuidStyles.DigitFormat; + } + else if (formatCh == 'N' || formatCh == 'n') + { + style = GuidStyles.NumberFormat; + } + else if (formatCh == 'B' || formatCh == 'b') + { + style = GuidStyles.BraceFormat; + } + else if (formatCh == 'P' || formatCh == 'p') + { + style = GuidStyles.ParenthesisFormat; + } + else if (formatCh == 'X' || formatCh == 'x') + { + style = GuidStyles.HexFormat; + } + else + { + // invalid guid format specification + result = Empty; + return false; + } + + GuidResult parseResult = new GuidResult(); + parseResult.Init(GuidParseThrowStyle.None); + if (TryParseGuid(input, style, ref parseResult)) + { + result = parseResult._parsedGuid; + return true; + } + else + { + result = Empty; + return false; + } + } + + private static bool TryParseGuid(string g, GuidStyles flags, ref GuidResult result) + { + if (g == null) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); + return false; + } + string guidString = g.Trim(); //Remove Whitespace + + if (guidString.Length == 0) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); + return false; + } + + // Check for dashes + bool dashesExistInString = (guidString.IndexOf('-', 0) >= 0); + + if (dashesExistInString) + { + if ((flags & (GuidStyles.AllowDashes | GuidStyles.RequireDashes)) == 0) + { + // dashes are not allowed + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); + return false; + } + } + else + { + if ((flags & GuidStyles.RequireDashes) != 0) + { + // dashes are required + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); + return false; + } + } + + // Check for braces + bool bracesExistInString = (guidString.IndexOf('{', 0) >= 0); + + if (bracesExistInString) + { + if ((flags & (GuidStyles.AllowBraces | GuidStyles.RequireBraces)) == 0) + { + // braces are not allowed + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); + return false; + } + } + else + { + if ((flags & GuidStyles.RequireBraces) != 0) + { + // braces are required + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); + return false; + } + } + + // Check for parenthesis + bool parenthesisExistInString = (guidString.IndexOf('(', 0) >= 0); + + if (parenthesisExistInString) + { + if ((flags & (GuidStyles.AllowParenthesis | GuidStyles.RequireParenthesis)) == 0) + { + // parenthesis are not allowed + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); + return false; + } + } + else + { + if ((flags & GuidStyles.RequireParenthesis) != 0) + { + // parenthesis are required + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidUnrecognized)); + return false; + } + } + + try + { + // let's get on with the parsing + if (dashesExistInString) + { + // Check if it's of the form [{|(]dddddddd-dddd-dddd-dddd-dddddddddddd[}|)] + return TryParseGuidWithDashes(guidString, ref result); + } + else if (bracesExistInString) + { + // Check if it's of the form {0xdddddddd,0xdddd,0xdddd,{0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd}} + return TryParseGuidWithHexPrefix(guidString, ref result); + } + else + { + // Check if it's of the form dddddddddddddddddddddddddddddddd + return TryParseGuidWithNoStyle(guidString, ref result); + } + } + catch (IndexOutOfRangeException ex) + { + result.SetFailure(ParseFailureKind.FormatWithInnerException, nameof(SR.Format_GuidUnrecognized), null, null, ex); + return false; + } + catch (ArgumentException ex) + { + result.SetFailure(ParseFailureKind.FormatWithInnerException, nameof(SR.Format_GuidUnrecognized), null, null, ex); + return false; + } + } + + // Check if it's of the form {0xdddddddd,0xdddd,0xdddd,{0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd}} + private static bool TryParseGuidWithHexPrefix(string guidString, ref GuidResult result) + { + int numStart = 0; + int numLen = 0; + + // Eat all of the whitespace + guidString = EatAllWhitespace(guidString); + + // Check for leading '{' + if (string.IsNullOrEmpty(guidString) || guidString[0] != '{') + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidBrace)); + return false; + } + + // Check for '0x' + if (!IsHexPrefix(guidString, 1)) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidHexPrefix), "{0xdddddddd, etc}"); + return false; + } + + // Find the end of this hex number (since it is not fixed length) + numStart = 3; + numLen = guidString.IndexOf(',', numStart) - numStart; + if (numLen <= 0) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma)); + return false; + } + + if (!StringToInt(guidString.Substring(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._a, ref result)) + return false; + + // Check for '0x' + if (!IsHexPrefix(guidString, numStart + numLen + 1)) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidHexPrefix), "{0xdddddddd, 0xdddd, etc}"); + return false; + } + // +3 to get by ',0x' + numStart = numStart + numLen + 3; + numLen = guidString.IndexOf(',', numStart) - numStart; + if (numLen <= 0) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma)); + return false; + } + + // Read in the number + if (!StringToShort(guidString.Substring(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._b, ref result)) + return false; + // Check for '0x' + if (!IsHexPrefix(guidString, numStart + numLen + 1)) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidHexPrefix), "{0xdddddddd, 0xdddd, 0xdddd, etc}"); + return false; + } + // +3 to get by ',0x' + numStart = numStart + numLen + 3; + numLen = guidString.IndexOf(',', numStart) - numStart; + if (numLen <= 0) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma)); + return false; + } + + // Read in the number + if (!StringToShort(guidString.Substring(numStart, numLen) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._c, ref result)) + return false; + + // Check for '{' + if (guidString.Length <= numStart + numLen + 1 || guidString[numStart + numLen + 1] != '{') + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidBrace)); + return false; + } + + // Prepare for loop + numLen++; + byte[] bytes = new byte[8]; + + for (int i = 0; i < 8; i++) + { + // Check for '0x' + if (!IsHexPrefix(guidString, numStart + numLen + 1)) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidHexPrefix), "{... { ... 0xdd, ...}}"); + return false; + } + + // +3 to get by ',0x' or '{0x' for first case + numStart = numStart + numLen + 3; + + // Calculate number length + if (i < 7) // first 7 cases + { + numLen = guidString.IndexOf(',', numStart) - numStart; + if (numLen <= 0) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidComma)); + return false; + } + } + else // last case ends with '}', not ',' + { + numLen = guidString.IndexOf('}', numStart) - numStart; + if (numLen <= 0) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidBraceAfterLastNumber)); + return false; + } + } + + // Read in the number + int signedNumber; + if (!StringToInt(guidString.Substring(numStart, numLen), -1, ParseNumbers.IsTight, out signedNumber, ref result)) + { + return false; + } + uint number = (uint)signedNumber; + + // check for overflow + if (number > 255) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Overflow_Byte)); + return false; + } + bytes[i] = (byte)number; + } + + result._parsedGuid._d = bytes[0]; + result._parsedGuid._e = bytes[1]; + result._parsedGuid._f = bytes[2]; + result._parsedGuid._g = bytes[3]; + result._parsedGuid._h = bytes[4]; + result._parsedGuid._i = bytes[5]; + result._parsedGuid._j = bytes[6]; + result._parsedGuid._k = bytes[7]; + + // Check for last '}' + if (numStart + numLen + 1 >= guidString.Length || guidString[numStart + numLen + 1] != '}') + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidEndBrace)); + return false; + } + + // Check if we have extra characters at the end + if (numStart + numLen + 1 != guidString.Length - 1) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_ExtraJunkAtEnd)); + return false; + } + + return true; + } + + // Check if it's of the form dddddddddddddddddddddddddddddddd + private static bool TryParseGuidWithNoStyle(string guidString, ref GuidResult result) + { + int startPos = 0; + int temp; + long templ; + int currentPos = 0; + + if (guidString.Length != 32) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); + return false; + } + + for (int i = 0; i < guidString.Length; i++) + { + char ch = guidString[i]; + if (ch >= '0' && ch <= '9') + { + continue; + } + else + { + char upperCaseCh = char.ToUpperInvariant(ch); + if (upperCaseCh >= 'A' && upperCaseCh <= 'F') + { + continue; + } + } + + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvalidChar)); + return false; + } + + if (!StringToInt(guidString.Substring(startPos, 8) /*first DWORD*/, -1, ParseNumbers.IsTight, out result._parsedGuid._a, ref result)) + return false; + + startPos += 8; + if (!StringToShort(guidString.Substring(startPos, 4), -1, ParseNumbers.IsTight, out result._parsedGuid._b, ref result)) + return false; + + startPos += 4; + if (!StringToShort(guidString.Substring(startPos, 4), -1, ParseNumbers.IsTight, out result._parsedGuid._c, ref result)) + return false; + + startPos += 4; + if (!StringToInt(guidString.Substring(startPos, 4), -1, ParseNumbers.IsTight, out temp, ref result)) + return false; + + startPos += 4; + currentPos = startPos; + + if (!StringToLong(guidString, ref currentPos, ParseNumbers.NoSpace, out templ, ref result)) + return false; + + if (currentPos - startPos != 12) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); + return false; + } + + result._parsedGuid._d = (byte)(temp >> 8); + result._parsedGuid._e = (byte)(temp); + temp = (int)(templ >> 32); + result._parsedGuid._f = (byte)(temp >> 8); + result._parsedGuid._g = (byte)(temp); + temp = (int)(templ); + result._parsedGuid._h = (byte)(temp >> 24); + result._parsedGuid._i = (byte)(temp >> 16); + result._parsedGuid._j = (byte)(temp >> 8); + result._parsedGuid._k = (byte)(temp); + + return true; + } + + // Check if it's of the form [{|(]dddddddd-dddd-dddd-dddd-dddddddddddd[}|)] + private static bool TryParseGuidWithDashes(string guidString, ref GuidResult result) + { + int startPos = 0; + int temp; + long templ; + int currentPos = 0; + + // check to see that it's the proper length + if (guidString[0] == '{') + { + if (guidString.Length != 38 || guidString[37] != '}') + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); + return false; + } + startPos = 1; + } + else if (guidString[0] == '(') + { + if (guidString.Length != 38 || guidString[37] != ')') + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); + return false; + } + startPos = 1; + } + else if (guidString.Length != 36) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); + return false; + } + + if (guidString[8 + startPos] != '-' || + guidString[13 + startPos] != '-' || + guidString[18 + startPos] != '-' || + guidString[23 + startPos] != '-') + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidDashes)); + return false; + } + + currentPos = startPos; + if (!StringToInt(guidString, ref currentPos, 8, ParseNumbers.NoSpace, out temp, ref result)) + return false; + result._parsedGuid._a = temp; + ++currentPos; //Increment past the '-'; + + if (!StringToInt(guidString, ref currentPos, 4, ParseNumbers.NoSpace, out temp, ref result)) + return false; + result._parsedGuid._b = (short)temp; + ++currentPos; //Increment past the '-'; + + if (!StringToInt(guidString, ref currentPos, 4, ParseNumbers.NoSpace, out temp, ref result)) + return false; + result._parsedGuid._c = (short)temp; + ++currentPos; //Increment past the '-'; + + if (!StringToInt(guidString, ref currentPos, 4, ParseNumbers.NoSpace, out temp, ref result)) + return false; + ++currentPos; //Increment past the '-'; + startPos = currentPos; + + if (!StringToLong(guidString, ref currentPos, ParseNumbers.NoSpace, out templ, ref result)) + return false; + + if (currentPos - startPos != 12) + { + result.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvLen)); + return false; + } + result._parsedGuid._d = (byte)(temp >> 8); + result._parsedGuid._e = (byte)(temp); + temp = (int)(templ >> 32); + result._parsedGuid._f = (byte)(temp >> 8); + result._parsedGuid._g = (byte)(temp); + temp = (int)(templ); + result._parsedGuid._h = (byte)(temp >> 24); + result._parsedGuid._i = (byte)(temp >> 16); + result._parsedGuid._j = (byte)(temp >> 8); + result._parsedGuid._k = (byte)(temp); + + return true; + } + + private static bool StringToShort(string str, int requiredLength, int flags, out short result, ref GuidResult parseResult) + { + int parsePos = 0; + return StringToShort(str, ref parsePos, requiredLength, flags, out result, ref parseResult); + } + + private static bool StringToShort(string str, ref int parsePos, int requiredLength, int flags, out short result, ref GuidResult parseResult) + { + result = 0; + int x; + bool retValue = StringToInt(str, ref parsePos, requiredLength, flags, out x, ref parseResult); + result = (short)x; + return retValue; + } + + private static bool StringToInt(string str, int requiredLength, int flags, out int result, ref GuidResult parseResult) + { + int parsePos = 0; + return StringToInt(str, ref parsePos, requiredLength, flags, out result, ref parseResult); + } + + private static bool StringToInt(string str, ref int parsePos, int requiredLength, int flags, out int result, ref GuidResult parseResult) + { + result = 0; + + int currStart = parsePos; + try + { + result = ParseNumbers.StringToInt(str, 16, flags, ref parsePos); + } + catch (OverflowException ex) + { + if (parseResult._throwStyle == GuidParseThrowStyle.All) + { + throw; + } + else if (parseResult._throwStyle == GuidParseThrowStyle.AllButOverflow) + { + throw new FormatException(SR.Format_GuidUnrecognized, ex); + } + else + { + parseResult.SetFailure(ex); + return false; + } + } + catch (Exception ex) + { + if (parseResult._throwStyle == GuidParseThrowStyle.None) + { + parseResult.SetFailure(ex); + return false; + } + else + { + throw; + } + } + + //If we didn't parse enough characters, there's clearly an error. + if (requiredLength != -1 && parsePos - currStart != requiredLength) + { + parseResult.SetFailure(ParseFailureKind.Format, nameof(SR.Format_GuidInvalidChar)); + return false; + } + return true; + } + + private static unsafe bool StringToLong(string str, ref int parsePos, int flags, out long result, ref GuidResult parseResult) + { + result = 0; + + try + { + result = ParseNumbers.StringToLong(str, 16, flags, ref parsePos); + } + catch (OverflowException ex) + { + if (parseResult._throwStyle == GuidParseThrowStyle.All) + { + throw; + } + else if (parseResult._throwStyle == GuidParseThrowStyle.AllButOverflow) + { + throw new FormatException(SR.Format_GuidUnrecognized, ex); + } + else + { + parseResult.SetFailure(ex); + return false; + } + } + catch (Exception ex) + { + if (parseResult._throwStyle == GuidParseThrowStyle.None) + { + parseResult.SetFailure(ex); + return false; + } + else + { + throw; + } + } + return true; + } + + private static string EatAllWhitespace(string str) + { + int newLength = 0; + char[] chArr = new char[str.Length]; + char curChar; + + // Now get each char from str and if it is not whitespace add it to chArr + for (int i = 0; i < str.Length; i++) + { + curChar = str[i]; + if (!char.IsWhiteSpace(curChar)) + { + chArr[newLength++] = curChar; + } + } + + // Return a new string based on chArr + return new string(chArr, 0, newLength); + } + + private static bool IsHexPrefix(string str, int i) + { + if (str.Length > i + 1 && str[i] == '0' && (char.ToLowerInvariant(str[i + 1]) == 'x')) + return true; + else + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteByteHelper(Span<byte> destination) + { + destination[0] = (byte)(_a); + destination[1] = (byte)(_a >> 8); + destination[2] = (byte)(_a >> 16); + destination[3] = (byte)(_a >> 24); + destination[4] = (byte)(_b); + destination[5] = (byte)(_b >> 8); + destination[6] = (byte)(_c); + destination[7] = (byte)(_c >> 8); + destination[8] = _d; + destination[9] = _e; + destination[10] = _f; + destination[11] = _g; + destination[12] = _h; + destination[13] = _i; + destination[14] = _j; + destination[15] = _k; + } + + // Returns an unsigned byte array containing the GUID. + public byte[] ToByteArray() + { + var g = new byte[16]; + WriteByteHelper(g); + return g; + } + + // Returns whether bytes are sucessfully written to given span. + public bool TryWriteBytes(Span<byte> destination) + { + if (destination.Length < 16) + return false; + + WriteByteHelper(destination); + return true; + } + + // Returns the guid in "registry" format. + public override string ToString() + { + return ToString("D", null); + } + + public override int GetHashCode() + { + // Simply XOR all the bits of the GUID 32 bits at a time. + return _a ^ Unsafe.Add(ref _a, 1) ^ Unsafe.Add(ref _a, 2) ^ Unsafe.Add(ref _a, 3); + } + + // Returns true if and only if the guid represented + // by o is the same as this instance. + public override bool Equals(object o) + { + Guid g; + // Check that o is a Guid first + if (o == null || !(o is Guid)) + return false; + else g = (Guid)o; + + // Now compare each of the elements + return g._a == _a && + Unsafe.Add(ref g._a, 1) == Unsafe.Add(ref _a, 1) && + Unsafe.Add(ref g._a, 2) == Unsafe.Add(ref _a, 2) && + Unsafe.Add(ref g._a, 3) == Unsafe.Add(ref _a, 3); + } + + public bool Equals(Guid g) + { + // Now compare each of the elements + return g._a == _a && + Unsafe.Add(ref g._a, 1) == Unsafe.Add(ref _a, 1) && + Unsafe.Add(ref g._a, 2) == Unsafe.Add(ref _a, 2) && + Unsafe.Add(ref g._a, 3) == Unsafe.Add(ref _a, 3); + } + + private int GetResult(uint me, uint them) + { + if (me < them) + { + return -1; + } + return 1; + } + + public int CompareTo(object value) + { + if (value == null) + { + return 1; + } + if (!(value is Guid)) + { + throw new ArgumentException(SR.Arg_MustBeGuid, nameof(value)); + } + Guid g = (Guid)value; + + if (g._a != _a) + { + return GetResult((uint)_a, (uint)g._a); + } + + if (g._b != _b) + { + return GetResult((uint)_b, (uint)g._b); + } + + if (g._c != _c) + { + return GetResult((uint)_c, (uint)g._c); + } + + if (g._d != _d) + { + return GetResult(_d, g._d); + } + + if (g._e != _e) + { + return GetResult(_e, g._e); + } + + if (g._f != _f) + { + return GetResult(_f, g._f); + } + + if (g._g != _g) + { + return GetResult(_g, g._g); + } + + if (g._h != _h) + { + return GetResult(_h, g._h); + } + + if (g._i != _i) + { + return GetResult(_i, g._i); + } + + if (g._j != _j) + { + return GetResult(_j, g._j); + } + + if (g._k != _k) + { + return GetResult(_k, g._k); + } + + return 0; + } + + public int CompareTo(Guid value) + { + if (value._a != _a) + { + return GetResult((uint)_a, (uint)value._a); + } + + if (value._b != _b) + { + return GetResult((uint)_b, (uint)value._b); + } + + if (value._c != _c) + { + return GetResult((uint)_c, (uint)value._c); + } + + if (value._d != _d) + { + return GetResult(_d, value._d); + } + + if (value._e != _e) + { + return GetResult(_e, value._e); + } + + if (value._f != _f) + { + return GetResult(_f, value._f); + } + + if (value._g != _g) + { + return GetResult(_g, value._g); + } + + if (value._h != _h) + { + return GetResult(_h, value._h); + } + + if (value._i != _i) + { + return GetResult(_i, value._i); + } + + if (value._j != _j) + { + return GetResult(_j, value._j); + } + + if (value._k != _k) + { + return GetResult(_k, value._k); + } + + return 0; + } + + public static bool operator ==(Guid a, Guid b) + { + // Now compare each of the elements + return a._a == b._a && + Unsafe.Add(ref a._a, 1) == Unsafe.Add(ref b._a, 1) && + Unsafe.Add(ref a._a, 2) == Unsafe.Add(ref b._a, 2) && + Unsafe.Add(ref a._a, 3) == Unsafe.Add(ref b._a, 3); + } + + public static bool operator !=(Guid a, Guid b) + { + // Now compare each of the elements + return a._a != b._a || + Unsafe.Add(ref a._a, 1) != Unsafe.Add(ref b._a, 1) || + Unsafe.Add(ref a._a, 2) != Unsafe.Add(ref b._a, 2) || + Unsafe.Add(ref a._a, 3) != Unsafe.Add(ref b._a, 3); + } + + public string ToString(string format) + { + return ToString(format, null); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static char HexToChar(int a) + { + a = a & 0xf; + return (char)((a > 9) ? a - 10 + 0x61 : a + 0x30); + } + + unsafe private static int HexsToChars(char* guidChars, int a, int b) + { + guidChars[0] = HexToChar(a >> 4); + guidChars[1] = HexToChar(a); + + guidChars[2] = HexToChar(b >> 4); + guidChars[3] = HexToChar(b); + + return 4; + } + + unsafe private static int HexsToCharsHexOutput(char* guidChars, int a, int b) + { + guidChars[0] = '0'; + guidChars[1] = 'x'; + + guidChars[2] = HexToChar(a >> 4); + guidChars[3] = HexToChar(a); + + guidChars[4] = ','; + guidChars[5] = '0'; + guidChars[6] = 'x'; + + guidChars[7] = HexToChar(b >> 4); + guidChars[8] = HexToChar(b); + + return 9; + } + + // IFormattable interface + // We currently ignore provider + public string ToString(string format, IFormatProvider provider) + { + if (format == null || format.Length == 0) + format = "D"; + + // all acceptable format strings are of length 1 + if (format.Length != 1) + throw new FormatException(SR.Format_InvalidGuidFormatSpecification); + + int guidSize; + switch (format[0]) + { + case 'D': + case 'd': + guidSize = 36; + break; + case 'N': + case 'n': + guidSize = 32; + break; + case 'B': + case 'b': + case 'P': + case 'p': + guidSize = 38; + break; + case 'X': + case 'x': + guidSize = 68; + break; + default: + throw new FormatException(SR.Format_InvalidGuidFormatSpecification); + } + + string guidString = string.FastAllocateString(guidSize); + + int bytesWritten; + bool result = TryFormat(new Span<char>(ref guidString.GetRawStringData(), guidString.Length), out bytesWritten, format); + Debug.Assert(result && bytesWritten == guidString.Length, "Formatting guid should have succeeded."); + + return guidString; + } + + // Returns whether the guid is successfully formatted as a span. + public bool TryFormat(Span<char> destination, out int charsWritten, string format = null) + { + if (format == null || format.Length == 0) + format = "D"; + + // all acceptable format strings are of length 1 + if (format.Length != 1) + throw new FormatException(SR.Format_InvalidGuidFormatSpecification); + + bool dash = true; + bool hex = false; + int braces = 0; + + int guidSize; + + switch (format[0]) + { + case 'D': + case 'd': + guidSize = 36; + break; + case 'N': + case 'n': + dash = false; + guidSize = 32; + break; + case 'B': + case 'b': + braces = '{' + ('}' << 16); + guidSize = 38; + break; + case 'P': + case 'p': + braces = '(' + (')' << 16); + guidSize = 38; + break; + case 'X': + case 'x': + braces = '{' + ('}' << 16); + dash = false; + hex = true; + guidSize = 68; + break; + default: + throw new FormatException(SR.Format_InvalidGuidFormatSpecification); + } + + if (destination.Length < guidSize) + { + charsWritten = 0; + return false; + } + + unsafe + { + fixed (char* guidChars = &destination.DangerousGetPinnableReference()) + { + char * p = guidChars; + + if (braces != 0) + *p++ = (char)braces; + + if (hex) + { + // {0xdddddddd,0xdddd,0xdddd,{0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd}} + *p++ = '0'; + *p++ = 'x'; + p += HexsToChars(p, _a >> 24, _a >> 16); + p += HexsToChars(p, _a >> 8, _a); + *p++ = ','; + *p++ = '0'; + *p++ = 'x'; + p += HexsToChars(p, _b >> 8, _b); + *p++ = ','; + *p++ = '0'; + *p++ = 'x'; + p += HexsToChars(p, _c >> 8, _c); + *p++ = ','; + *p++ = '{'; + p += HexsToCharsHexOutput(p, _d, _e); + *p++ = ','; + p += HexsToCharsHexOutput(p, _f, _g); + *p++ = ','; + p += HexsToCharsHexOutput(p, _h, _i); + *p++ = ','; + p += HexsToCharsHexOutput(p, _j, _k); + *p++ = '}'; + } + else + { + // [{|(]dddddddd[-]dddd[-]dddd[-]dddd[-]dddddddddddd[}|)] + p += HexsToChars(p, _a >> 24, _a >> 16); + p += HexsToChars(p, _a >> 8, _a); + if (dash) + *p++ = '-'; + p += HexsToChars(p, _b >> 8, _b); + if (dash) + *p++ = '-'; + p += HexsToChars(p, _c >> 8, _c); + if (dash) + *p++ = '-'; + p += HexsToChars(p, _d, _e); + if (dash) + *p++ = '-'; + p += HexsToChars(p, _f, _g); + p += HexsToChars(p, _h, _i); + p += HexsToChars(p, _j, _k); + } + + if (braces != 0) + *p++ = (char)(braces >> 16); + + Debug.Assert(p - guidChars == guidSize); + } + } + + charsWritten = guidSize; + return true; + } + } +} diff --git a/src/mscorlib/shared/System/HResults.cs b/src/mscorlib/shared/System/HResults.cs new file mode 100644 index 0000000000..ffc47d85bf --- /dev/null +++ b/src/mscorlib/shared/System/HResults.cs @@ -0,0 +1,126 @@ +// 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. + +//============================================================================= +// +// +// Purpose: Define HResult constants. Every exception has one of these. +// +// +//===========================================================================*/ +// Note: FACILITY_URT is defined as 0x13 (0x8013xxxx). Within that +// range, 0x1yyy is for Runtime errors (used for Security, Metadata, etc). +// In that subrange, 0x15zz and 0x16zz have been allocated for classlib-type +// HResults. Also note that some of our HResults have to map to certain +// COM HR's, etc. + +// Reflection will use 0x1600 -> 0x161f. IO will use 0x1620 -> 0x163f. +// Security will use 0x1640 -> 0x165f + + +using System; + +namespace System +{ + internal static partial class HResults + { + internal const int COR_E_ABANDONEDMUTEX = unchecked((int)0x8013152D); + internal const int COR_E_AMBIGUOUSMATCH = unchecked((int)0x8000211D); + internal const int COR_E_APPDOMAINUNLOADED = unchecked((int)0x80131014); + internal const int COR_E_APPLICATION = unchecked((int)0x80131600); + internal const int COR_E_ARGUMENT = unchecked((int)0x80070057); + internal const int COR_E_ARGUMENTOUTOFRANGE = unchecked((int)0x80131502); + internal const int COR_E_ARITHMETIC = unchecked((int)0x80070216); + internal const int COR_E_ARRAYTYPEMISMATCH = unchecked((int)0x80131503); + internal const int COR_E_BADEXEFORMAT = unchecked((int)0x800700C1); + internal const int COR_E_BADIMAGEFORMAT = unchecked((int)0x8007000B); + internal const int COR_E_CANNOTUNLOADAPPDOMAIN = unchecked((int)0x80131015); + internal const int COR_E_COMEMULATE = unchecked((int)0x80131535); + internal const int COR_E_CONTEXTMARSHAL = unchecked((int)0x80131504); + internal const int COR_E_CUSTOMATTRIBUTEFORMAT = unchecked((int)0x80131605); + internal const int COR_E_DATAMISALIGNED = unchecked((int)0x80131541); + internal const int COR_E_DIRECTORYNOTFOUND = unchecked((int)0x80070003); + internal const int COR_E_DIVIDEBYZERO = unchecked((int)0x80020012); // DISP_E_DIVBYZERO + internal const int COR_E_DLLNOTFOUND = unchecked((int)0x80131524); + internal const int COR_E_DUPLICATEWAITOBJECT = unchecked((int)0x80131529); + internal const int COR_E_ENDOFSTREAM = unchecked((int)0x80070026); // OS defined + internal const int COR_E_ENTRYPOINTNOTFOUND = unchecked((int)0x80131523); + internal const int COR_E_EXCEPTION = unchecked((int)0x80131500); + internal const int COR_E_EXECUTIONENGINE = unchecked((int)0x80131506); + internal const int COR_E_FIELDACCESS = unchecked((int)0x80131507); + internal const int COR_E_FILELOAD = unchecked((int)0x80131621); + internal const int COR_E_FILENOTFOUND = unchecked((int)0x80070002); + internal const int COR_E_FORMAT = unchecked((int)0x80131537); + internal const int COR_E_HOSTPROTECTION = unchecked((int)0x80131640); + internal const int COR_E_INDEXOUTOFRANGE = unchecked((int)0x80131508); + internal const int COR_E_INSUFFICIENTEXECUTIONSTACK = unchecked((int)0x80131578); + internal const int COR_E_INSUFFICIENTMEMORY = unchecked((int)0x8013153D); + internal const int COR_E_INVALIDCAST = unchecked((int)0x80004002); + internal const int COR_E_INVALIDCOMOBJECT = unchecked((int)0x80131527); + internal const int COR_E_INVALIDFILTERCRITERIA = unchecked((int)0x80131601); + internal const int COR_E_INVALIDOLEVARIANTTYPE = unchecked((int)0x80131531); + internal const int COR_E_INVALIDOPERATION = unchecked((int)0x80131509); + internal const int COR_E_INVALIDPROGRAM = unchecked((int)0x8013153A); + internal const int COR_E_IO = unchecked((int)0x80131620); + internal const int COR_E_KEYNOTFOUND = unchecked((int)0x80131577); + internal const int COR_E_MARSHALDIRECTIVE = unchecked((int)0x80131535); + internal const int COR_E_MEMBERACCESS = unchecked((int)0x8013151A); + internal const int COR_E_METHODACCESS = unchecked((int)0x80131510); + internal const int COR_E_MISSINGFIELD = unchecked((int)0x80131511); + internal const int COR_E_MISSINGMANIFESTRESOURCE = unchecked((int)0x80131532); + internal const int COR_E_MISSINGMEMBER = unchecked((int)0x80131512); + internal const int COR_E_MISSINGMETHOD = unchecked((int)0x80131513); + internal const int COR_E_MISSINGSATELLITEASSEMBLY = unchecked((int)0x80131536); + internal const int COR_E_MULTICASTNOTSUPPORTED = unchecked((int)0x80131514); + internal const int COR_E_NOTFINITENUMBER = unchecked((int)0x80131528); + internal const int COR_E_NOTSUPPORTED = unchecked((int)0x80131515); + internal const int COR_E_NULLREFERENCE = unchecked((int)0x80004003); + internal const int COR_E_OBJECTDISPOSED = unchecked((int)0x80131622); + internal const int COR_E_OPERATIONCANCELED = unchecked((int)0x8013153B); + internal const int COR_E_OUTOFMEMORY = unchecked((int)0x8007000E); + internal const int COR_E_OVERFLOW = unchecked((int)0x80131516); + internal const int COR_E_PATHTOOLONG = unchecked((int)0x800700CE); + internal const int COR_E_PLATFORMNOTSUPPORTED = unchecked((int)0x80131539); + internal const int COR_E_RANK = unchecked((int)0x80131517); + internal const int COR_E_REFLECTIONTYPELOAD = unchecked((int)0x80131602); + internal const int COR_E_RUNTIMEWRAPPED = unchecked((int)0x8013153E); + internal const int COR_E_SAFEARRAYRANKMISMATCH = unchecked((int)0x80131538); + internal const int COR_E_SAFEARRAYTYPEMISMATCH = unchecked((int)0x80131533); + internal const int COR_E_SAFEHANDLEMISSINGATTRIBUTE = unchecked((int)0x80131623); + internal const int COR_E_SECURITY = unchecked((int)0x8013150A); + internal const int COR_E_SEMAPHOREFULL = unchecked((int)0x8013152B); + internal const int COR_E_SERIALIZATION = unchecked((int)0x8013150C); + internal const int COR_E_STACKOVERFLOW = unchecked((int)0x800703E9); + internal const int COR_E_SYNCHRONIZATIONLOCK = unchecked((int)0x80131518); + internal const int COR_E_SYSTEM = unchecked((int)0x80131501); + internal const int COR_E_TARGET = unchecked((int)0x80131603); + internal const int COR_E_TARGETINVOCATION = unchecked((int)0x80131604); + internal const int COR_E_TARGETPARAMCOUNT = unchecked((int)0x8002000E); + internal const int COR_E_THREADABORTED = unchecked((int)0x80131530); + internal const int COR_E_THREADINTERRUPTED = unchecked((int)0x80131519); + internal const int COR_E_THREADSTART = unchecked((int)0x80131525); + internal const int COR_E_THREADSTATE = unchecked((int)0x80131520); + internal const int COR_E_THREADSTOP = unchecked((int)0x80131521); + internal const int COR_E_TIMEOUT = unchecked((int)0x80131505); + internal const int COR_E_TYPEACCESS = unchecked((int)0x80131543); + internal const int COR_E_TYPEINITIALIZATION = unchecked((int)0x80131534); + internal const int COR_E_TYPELOAD = unchecked((int)0x80131522); + internal const int COR_E_TYPEUNLOADED = unchecked((int)0x80131013); + internal const int COR_E_UNAUTHORIZEDACCESS = unchecked((int)0x80070005); + internal const int COR_E_UNSUPPORTEDFORMAT = unchecked((int)0x80131523); + internal const int COR_E_VERIFICATION = unchecked((int)0x8013150D); + internal const int COR_E_WAITHANDLECANNOTBEOPENED = unchecked((int)0x8013152C); + internal const int DISP_E_OVERFLOW = unchecked((int)0x8002000A); + internal const int E_BOUNDS = unchecked((int)0x8000000B); + internal const int E_CHANGED_STATE = unchecked((int)0x8000000C); + internal const int E_FAIL = unchecked((int)0x80004005); + internal const int E_HANDLE = unchecked((int)0x80070006); + internal const int E_INVALIDARG = unchecked((int)0x80070057); + internal const int E_NOTIMPL = unchecked((int)0x80004001); + internal const int E_POINTER = unchecked((int)0x80004003); + internal const int ERROR_MRM_MAP_NOT_FOUND = unchecked((int)0x80073B1F); + internal const int RO_E_CLOSED = unchecked((int)0x80000013); + internal const int TYPE_E_TYPEMISMATCH = unchecked((int)0x80028CA0); + } +} diff --git a/src/mscorlib/shared/System/IO/BinaryWriter.cs b/src/mscorlib/shared/System/IO/BinaryWriter.cs index 90b920854f..675e80922e 100644 --- a/src/mscorlib/shared/System/IO/BinaryWriter.cs +++ b/src/mscorlib/shared/System/IO/BinaryWriter.cs @@ -4,6 +4,7 @@ using System.Text; using System.Diagnostics; +using System.Buffers; namespace System.IO { @@ -389,6 +390,41 @@ namespace System.IO } } + public virtual void Write(ReadOnlySpan<byte> value) + { + if (GetType() == typeof(BinaryWriter)) + { + OutStream.Write(value); + } + else + { + byte[] buffer = ArrayPool<byte>.Shared.Rent(value.Length); + try + { + value.CopyTo(buffer); + Write(buffer, 0, value.Length); + } + finally + { + ArrayPool<byte>.Shared.Return(buffer); + } + } + } + + public virtual void Write(ReadOnlySpan<char> value) + { + byte[] bytes = ArrayPool<byte>.Shared.Rent(_encoding.GetMaxByteCount(value.Length)); + try + { + int bytesWritten = _encoding.GetBytes(value, bytes); + Write(bytes, 0, bytesWritten); + } + finally + { + ArrayPool<byte>.Shared.Return(bytes); + } + } + protected void Write7BitEncodedInt(int value) { // Write out an int 7 bits at a time. The high bit of the byte, diff --git a/src/mscorlib/shared/System/IO/DirectoryNotFoundException.cs b/src/mscorlib/shared/System/IO/DirectoryNotFoundException.cs index de09760fcb..87e610b86e 100644 --- a/src/mscorlib/shared/System/IO/DirectoryNotFoundException.cs +++ b/src/mscorlib/shared/System/IO/DirectoryNotFoundException.cs @@ -17,19 +17,19 @@ namespace System.IO public DirectoryNotFoundException() : base(SR.Arg_DirectoryNotFoundException) { - HResult = __HResults.COR_E_DIRECTORYNOTFOUND; + HResult = HResults.COR_E_DIRECTORYNOTFOUND; } public DirectoryNotFoundException(string message) : base(message) { - HResult = __HResults.COR_E_DIRECTORYNOTFOUND; + HResult = HResults.COR_E_DIRECTORYNOTFOUND; } public DirectoryNotFoundException(string message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_DIRECTORYNOTFOUND; + HResult = HResults.COR_E_DIRECTORYNOTFOUND; } protected DirectoryNotFoundException(SerializationInfo info, StreamingContext context) diff --git a/src/mscorlib/shared/System/IO/DisableMediaInsertionPrompt.cs b/src/mscorlib/shared/System/IO/DisableMediaInsertionPrompt.cs new file mode 100644 index 0000000000..aa10e8d883 --- /dev/null +++ b/src/mscorlib/shared/System/IO/DisableMediaInsertionPrompt.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.IO +{ + /// <summary> + /// Simple wrapper to safely disable the normal media insertion prompt for + /// removable media (floppies, cds, memory cards, etc.) + /// </summary> + /// <remarks> + /// Note that removable media file systems lazily load. After starting the OS + /// they won't be loaded until you have media in the drive- and as such the + /// prompt won't happen. You have to have had media in at least once to get + /// the file system to load and then have removed it. + /// </remarks> + internal struct DisableMediaInsertionPrompt : IDisposable + { + private bool _disableSuccess; + private uint _oldMode; + + public static DisableMediaInsertionPrompt Create() + { + DisableMediaInsertionPrompt prompt = new DisableMediaInsertionPrompt(); + prompt._disableSuccess = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out prompt._oldMode); + return prompt; + } + + public void Dispose() + { + uint ignore; + if (_disableSuccess) + Interop.Kernel32.SetThreadErrorMode(_oldMode, out ignore); + } + } +} diff --git a/src/mscorlib/shared/System/IO/DriveNotFoundException.cs b/src/mscorlib/shared/System/IO/DriveNotFoundException.cs new file mode 100644 index 0000000000..c0f8c55af8 --- /dev/null +++ b/src/mscorlib/shared/System/IO/DriveNotFoundException.cs @@ -0,0 +1,35 @@ +// 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.Runtime.Serialization; + +namespace System.IO +{ + //Thrown when trying to access a drive that is not available. + internal class DriveNotFoundException : IOException + { + public DriveNotFoundException() + : base(SR.Arg_DriveNotFoundException) + { + HResult = HResults.COR_E_DIRECTORYNOTFOUND; + } + + public DriveNotFoundException(string message) + : base(message) + { + HResult = HResults.COR_E_DIRECTORYNOTFOUND; + } + + public DriveNotFoundException(string message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_DIRECTORYNOTFOUND; + } + + protected DriveNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) + { + throw new PlatformNotSupportedException(); + } + } +} diff --git a/src/mscorlib/shared/System/IO/EndOfStreamException.cs b/src/mscorlib/shared/System/IO/EndOfStreamException.cs index 68e1b2c882..ee37818dc5 100644 --- a/src/mscorlib/shared/System/IO/EndOfStreamException.cs +++ b/src/mscorlib/shared/System/IO/EndOfStreamException.cs @@ -11,19 +11,19 @@ namespace System.IO public EndOfStreamException() : base(SR.Arg_EndOfStreamException) { - HResult = __HResults.COR_E_ENDOFSTREAM; + HResult = HResults.COR_E_ENDOFSTREAM; } public EndOfStreamException(string message) : base(message) { - HResult = __HResults.COR_E_ENDOFSTREAM; + HResult = HResults.COR_E_ENDOFSTREAM; } public EndOfStreamException(string message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_ENDOFSTREAM; + HResult = HResults.COR_E_ENDOFSTREAM; } protected EndOfStreamException(SerializationInfo info, StreamingContext context) diff --git a/src/mscorlib/shared/System/IO/FileLoadException.cs b/src/mscorlib/shared/System/IO/FileLoadException.cs index 6fdf2d58cf..b126424c22 100644 --- a/src/mscorlib/shared/System/IO/FileLoadException.cs +++ b/src/mscorlib/shared/System/IO/FileLoadException.cs @@ -11,31 +11,31 @@ namespace System.IO public FileLoadException() : base(SR.IO_FileLoad) { - HResult = __HResults.COR_E_FILELOAD; + HResult = HResults.COR_E_FILELOAD; } public FileLoadException(string message) : base(message) { - HResult = __HResults.COR_E_FILELOAD; + HResult = HResults.COR_E_FILELOAD; } public FileLoadException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_FILELOAD; + HResult = HResults.COR_E_FILELOAD; } public FileLoadException(string message, string fileName) : base(message) { - HResult = __HResults.COR_E_FILELOAD; + HResult = HResults.COR_E_FILELOAD; FileName = fileName; } public FileLoadException(string message, string fileName, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_FILELOAD; + HResult = HResults.COR_E_FILELOAD; FileName = fileName; } diff --git a/src/mscorlib/shared/System/IO/FileNotFoundException.cs b/src/mscorlib/shared/System/IO/FileNotFoundException.cs index 374c976055..dc1ccf577a 100644 --- a/src/mscorlib/shared/System/IO/FileNotFoundException.cs +++ b/src/mscorlib/shared/System/IO/FileNotFoundException.cs @@ -12,32 +12,32 @@ namespace System.IO public FileNotFoundException() : base(SR.IO_FileNotFound) { - HResult = __HResults.COR_E_FILENOTFOUND; + HResult = HResults.COR_E_FILENOTFOUND; } public FileNotFoundException(string message) : base(message) { - HResult = __HResults.COR_E_FILENOTFOUND; + HResult = HResults.COR_E_FILENOTFOUND; } public FileNotFoundException(string message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_FILENOTFOUND; + HResult = HResults.COR_E_FILENOTFOUND; } public FileNotFoundException(string message, string fileName) : base(message) { - HResult = __HResults.COR_E_FILENOTFOUND; + HResult = HResults.COR_E_FILENOTFOUND; FileName = fileName; } public FileNotFoundException(string message, string fileName, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_FILENOTFOUND; + HResult = HResults.COR_E_FILENOTFOUND; FileName = fileName; } @@ -55,7 +55,7 @@ namespace System.IO if (_message == null) { if ((FileName == null) && - (HResult == System.__HResults.COR_E_EXCEPTION)) + (HResult == System.HResults.COR_E_EXCEPTION)) _message = SR.IO_FileNotFound; else if (FileName != null) diff --git a/src/mscorlib/shared/System/IO/FileStream.Unix.cs b/src/mscorlib/shared/System/IO/FileStream.Unix.cs index ad68a001cf..8499595361 100644 --- a/src/mscorlib/shared/System/IO/FileStream.Unix.cs +++ b/src/mscorlib/shared/System/IO/FileStream.Unix.cs @@ -286,13 +286,20 @@ namespace System.IO } } + private void FlushWriteBufferForWriteByte() + { + _asyncState?.Wait(); + try { FlushWriteBuffer(); } + finally { _asyncState?.Release(); } + } + /// <summary>Writes any data in the write buffer to the underlying stream and resets the buffer.</summary> private void FlushWriteBuffer() { AssertBufferInvariants(); if (_writePos > 0) { - WriteNative(GetBuffer(), 0, _writePos); + WriteNative(new ReadOnlySpan<byte>(GetBuffer(), 0, _writePos)); _writePos = 0; } } @@ -375,44 +382,7 @@ namespace System.IO } /// <summary>Reads a block of bytes from the stream and writes the data in a given buffer.</summary> - /// <param name="array"> - /// When this method returns, contains the specified byte array with the values between offset and - /// (offset + count - 1) replaced by the bytes read from the current source. - /// </param> - /// <param name="offset">The byte offset in array at which the read bytes will be placed.</param> - /// <param name="count">The maximum number of bytes to read. </param> - /// <returns> - /// The total number of bytes read into the buffer. This might be less than the number of bytes requested - /// if that number of bytes are not currently available, or zero if the end of the stream is reached. - /// </returns> - public override int Read(byte[] array, int offset, int count) - { - ValidateReadWriteArgs(array, offset, count); - - if (_useAsyncIO) - { - _asyncState.Wait(); - try { return ReadCore(array, offset, count); } - finally { _asyncState.Release(); } - } - else - { - return ReadCore(array, offset, count); - } - } - - /// <summary>Reads a block of bytes from the stream and writes the data in a given buffer.</summary> - /// <param name="array"> - /// When this method returns, contains the specified byte array with the values between offset and - /// (offset + count - 1) replaced by the bytes read from the current source. - /// </param> - /// <param name="offset">The byte offset in array at which the read bytes will be placed.</param> - /// <param name="count">The maximum number of bytes to read. </param> - /// <returns> - /// The total number of bytes read into the buffer. This might be less than the number of bytes requested - /// if that number of bytes are not currently available, or zero if the end of the stream is reached. - /// </returns> - private int ReadCore(byte[] array, int offset, int count) + private int ReadSpan(Span<byte> destination) { PrepareForReading(); @@ -429,16 +399,16 @@ namespace System.IO // If we're not able to seek, then we're not able to rewind the stream (i.e. flushing // a read buffer), in which case we don't want to use a read buffer. Similarly, if // the user has asked for more data than we can buffer, we also want to skip the buffer. - if (!CanSeek || (count >= _bufferLength)) + if (!CanSeek || (destination.Length >= _bufferLength)) { // Read directly into the user's buffer _readPos = _readLength = 0; - return ReadNative(array, offset, count); + return ReadNative(destination); } else { // Read into our buffer. - _readLength = numBytesAvailable = ReadNative(GetBuffer(), 0, _bufferLength); + _readLength = numBytesAvailable = ReadNative(GetBuffer()); _readPos = 0; if (numBytesAvailable == 0) { @@ -454,8 +424,8 @@ namespace System.IO // Now that we know there's data in the buffer, read from it into the user's buffer. Debug.Assert(numBytesAvailable > 0, "Data must be in the buffer to be here"); - int bytesRead = Math.Min(numBytesAvailable, count); - Buffer.BlockCopy(GetBuffer(), _readPos, array, offset, bytesRead); + int bytesRead = Math.Min(numBytesAvailable, destination.Length); + new Span<byte>(GetBuffer(), _readPos, bytesRead).CopyTo(destination); _readPos += bytesRead; // We may not have had enough data in the buffer to completely satisfy the user's request. @@ -466,38 +436,33 @@ namespace System.IO // behavior, we do the same thing here on Unix. Note that we may still get less the requested // amount, as the OS may give us back fewer than we request, either due to reaching the end of // file, or due to its own whims. - if (!readFromOS && bytesRead < count) + if (!readFromOS && bytesRead < destination.Length) { - Debug.Assert(_readPos == _readLength, "bytesToRead should only be < count if numBytesAvailable < count"); + Debug.Assert(_readPos == _readLength, "bytesToRead should only be < destination.Length if numBytesAvailable < destination.Length"); _readPos = _readLength = 0; // no data left in the read buffer - bytesRead += ReadNative(array, offset + bytesRead, count - bytesRead); + bytesRead += ReadNative(destination.Slice(bytesRead)); } return bytesRead; } - /// <summary>Unbuffered, reads a block of bytes from the stream and writes the data in a given buffer.</summary> - /// <param name="array"> - /// When this method returns, contains the specified byte array with the values between offset and - /// (offset + count - 1) replaced by the bytes read from the current source. - /// </param> - /// <param name="offset">The byte offset in array at which the read bytes will be placed.</param> - /// <param name="count">The maximum number of bytes to read. </param> + /// <summary>Unbuffered, reads a block of bytes from the file handle into the given buffer.</summary> + /// <param name="buffer">The buffer into which data from the file is read.</param> /// <returns> /// The total number of bytes read into the buffer. This might be less than the number of bytes requested /// if that number of bytes are not currently available, or zero if the end of the stream is reached. /// </returns> - private unsafe int ReadNative(byte[] array, int offset, int count) + private unsafe int ReadNative(Span<byte> buffer) { FlushWriteBuffer(); // we're about to read; dump the write buffer VerifyOSHandlePosition(); int bytesRead; - fixed (byte* bufPtr = array) + fixed (byte* bufPtr = &buffer.DangerousGetPinnableReference()) { - bytesRead = CheckFileCall(Interop.Sys.Read(_fileHandle, bufPtr + offset, count)); - Debug.Assert(bytesRead <= count); + bytesRead = CheckFileCall(Interop.Sys.Read(_fileHandle, bufPtr, buffer.Length)); + Debug.Assert(bytesRead <= buffer.Length); } _filePosition += bytesRead; return bytesRead; @@ -507,135 +472,97 @@ namespace System.IO /// Asynchronously reads a sequence of bytes from the current stream and advances /// the position within the stream by the number of bytes read. /// </summary> - /// <param name="buffer">The buffer to write the data into.</param> - /// <param name="offset">The byte offset in buffer at which to begin writing data from the stream.</param> - /// <param name="count">The maximum number of bytes to read.</param> + /// <param name="destination">The buffer to write the data into.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param> + /// <param name="synchronousResult">If the operation completes synchronously, the number of bytes read.</param> /// <returns>A task that represents the asynchronous read operation.</returns> - private Task<int> ReadAsyncInternal(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + private Task<int> ReadAsyncInternal(Memory<byte> destination, CancellationToken cancellationToken, out int synchronousResult) { - if (_useAsyncIO) + Debug.Assert(_useAsyncIO); + + if (!CanRead) // match Windows behavior; this gets thrown synchronously { - if (!CanRead) // match Windows behavior; this gets thrown synchronously - { - throw Error.GetReadNotSupported(); - } + throw Error.GetReadNotSupported(); + } - // Serialize operations using the semaphore. - Task waitTask = _asyncState.WaitAsync(); + // Serialize operations using the semaphore. + Task waitTask = _asyncState.WaitAsync(); - // If we got ownership immediately, and if there's enough data in our buffer - // to satisfy the full request of the caller, hand back the buffered data. - // While it would be a legal implementation of the Read contract, we don't - // hand back here less than the amount requested so as to match the behavior - // in ReadCore that will make a native call to try to fulfill the remainder - // of the request. - if (waitTask.Status == TaskStatus.RanToCompletion) + // If we got ownership immediately, and if there's enough data in our buffer + // to satisfy the full request of the caller, hand back the buffered data. + // While it would be a legal implementation of the Read contract, we don't + // hand back here less than the amount requested so as to match the behavior + // in ReadCore that will make a native call to try to fulfill the remainder + // of the request. + if (waitTask.Status == TaskStatus.RanToCompletion) + { + int numBytesAvailable = _readLength - _readPos; + if (numBytesAvailable >= destination.Length) { - int numBytesAvailable = _readLength - _readPos; - if (numBytesAvailable >= count) + try { - try - { - PrepareForReading(); - - Buffer.BlockCopy(GetBuffer(), _readPos, buffer, offset, count); - _readPos += count; - - return _asyncState._lastSuccessfulReadTask != null && _asyncState._lastSuccessfulReadTask.Result == count ? - _asyncState._lastSuccessfulReadTask : - (_asyncState._lastSuccessfulReadTask = Task.FromResult(count)); - } - catch (Exception exc) - { - return Task.FromException<int>(exc); - } - finally - { - _asyncState.Release(); - } - } - } + PrepareForReading(); - // Otherwise, issue the whole request asynchronously. - _asyncState.Update(buffer, offset, count); - return waitTask.ContinueWith((t, s) => - { - // The options available on Unix for writing asynchronously to an arbitrary file - // handle typically amount to just using another thread to do the synchronous write, - // which is exactly what this implementation does. This does mean there are subtle - // differences in certain FileStream behaviors between Windows and Unix when multiple - // asynchronous operations are issued against the stream to execute concurrently; on - // Unix the operations will be serialized due to the usage of a semaphore, but the - // position /length information won't be updated until after the write has completed, - // whereas on Windows it may happen before the write has completed. - - Debug.Assert(t.Status == TaskStatus.RanToCompletion); - var thisRef = (FileStream)s; - try + new Span<byte>(GetBuffer(), _readPos, destination.Length).CopyTo(destination.Span); + _readPos += destination.Length; + + synchronousResult = destination.Length; + return null; + } + catch (Exception exc) { - byte[] b = thisRef._asyncState._buffer; - thisRef._asyncState._buffer = null; // remove reference to user's buffer - return thisRef.ReadCore(b, thisRef._asyncState._offset, thisRef._asyncState._count); + synchronousResult = 0; + return Task.FromException<int>(exc); } - finally { thisRef._asyncState.Release(); } - }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default); - } - else - { - return base.ReadAsync(buffer, offset, count, cancellationToken); + finally + { + _asyncState.Release(); + } + } } - } - /// <summary> - /// Reads a byte from the stream and advances the position within the stream - /// by one byte, or returns -1 if at the end of the stream. - /// </summary> - /// <returns>The unsigned byte cast to an Int32, or -1 if at the end of the stream.</returns> - public override int ReadByte() - { - if (_useAsyncIO) - { - _asyncState.Wait(); - try { return ReadByteCore(); } - finally { _asyncState.Release(); } - } - else + // Otherwise, issue the whole request asynchronously. + synchronousResult = 0; + _asyncState.Memory = destination; + return waitTask.ContinueWith((t, s) => { - return ReadByteCore(); - } + // The options available on Unix for writing asynchronously to an arbitrary file + // handle typically amount to just using another thread to do the synchronous write, + // which is exactly what this implementation does. This does mean there are subtle + // differences in certain FileStream behaviors between Windows and Unix when multiple + // asynchronous operations are issued against the stream to execute concurrently; on + // Unix the operations will be serialized due to the usage of a semaphore, but the + // position /length information won't be updated until after the write has completed, + // whereas on Windows it may happen before the write has completed. + + Debug.Assert(t.Status == TaskStatus.RanToCompletion); + var thisRef = (FileStream)s; + try + { + Memory<byte> memory = thisRef._asyncState.Memory; + thisRef._asyncState.Memory = default(Memory<byte>); + return thisRef.ReadSpan(memory.Span); + } + finally { thisRef._asyncState.Release(); } + }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default); } - /// <summary>Writes a block of bytes to the file stream.</summary> - /// <param name="array">The buffer containing data to write to the stream.</param> - /// <param name="offset">The zero-based byte offset in array from which to begin copying bytes to the stream.</param> - /// <param name="count">The maximum number of bytes to write.</param> - public override void Write(byte[] array, int offset, int count) + /// <summary>Reads from the file handle into the buffer, overwriting anything in it.</summary> + private int FillReadBufferForReadByte() { - ValidateReadWriteArgs(array, offset, count); - - if (_useAsyncIO) - { - _asyncState.Wait(); - try { WriteCore(array, offset, count); } - finally { _asyncState.Release(); } - } - else - { - WriteCore(array, offset, count); - } + _asyncState?.Wait(); + try { return ReadNative(_buffer); } + finally { _asyncState?.Release(); } } /// <summary>Writes a block of bytes to the file stream.</summary> - /// <param name="array">The buffer containing data to write to the stream.</param> - /// <param name="offset">The zero-based byte offset in array from which to begin copying bytes to the stream.</param> - /// <param name="count">The maximum number of bytes to write.</param> - private void WriteCore(byte[] array, int offset, int count) + /// <param name="source">The buffer containing data to write to the stream.</param> + private void WriteSpan(ReadOnlySpan<byte> source) { PrepareForWriting(); // If no data is being written, nothing more to do. - if (count == 0) + if (source.Length == 0) { return; } @@ -647,21 +574,17 @@ namespace System.IO // If there's space remaining in the buffer, then copy as much as // we can from the user's buffer into ours. int spaceRemaining = _bufferLength - _writePos; - if (spaceRemaining > 0) + if (spaceRemaining >= source.Length) { - int bytesToCopy = Math.Min(spaceRemaining, count); - Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, bytesToCopy); - _writePos += bytesToCopy; - - // If we've successfully copied all of the user's data, we're done. - if (count == bytesToCopy) - { - return; - } - - // Otherwise, keep track of how much more data needs to be handled. - offset += bytesToCopy; - count -= bytesToCopy; + source.CopyTo(new Span<byte>(GetBuffer()).Slice(_writePos)); + _writePos += source.Length; + return; + } + else if (spaceRemaining > 0) + { + source.Slice(0, spaceRemaining).CopyTo(new Span<byte>(GetBuffer()).Slice(_writePos)); + _writePos += spaceRemaining; + source = source.Slice(spaceRemaining); } // At this point, the buffer is full, so flush it out. @@ -672,35 +595,33 @@ namespace System.IO // the user's looking to write more data than we can store in the buffer), // skip the buffer. Otherwise, put the remaining data into the buffer. Debug.Assert(_writePos == 0); - if (count >= _bufferLength) + if (source.Length >= _bufferLength) { - WriteNative(array, offset, count); + WriteNative(source); } else { - Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, count); - _writePos = count; + source.CopyTo(new Span<byte>(GetBuffer())); + _writePos = source.Length; } } /// <summary>Unbuffered, writes a block of bytes to the file stream.</summary> - /// <param name="array">The buffer containing data to write to the stream.</param> - /// <param name="offset">The zero-based byte offset in array from which to begin copying bytes to the stream.</param> - /// <param name="count">The maximum number of bytes to write.</param> - private unsafe void WriteNative(byte[] array, int offset, int count) + /// <param name="source">The buffer containing data to write to the stream.</param> + private unsafe void WriteNative(ReadOnlySpan<byte> source) { VerifyOSHandlePosition(); - fixed (byte* bufPtr = array) + fixed (byte* bufPtr = &source.DangerousGetPinnableReference()) { + int offset = 0; + int count = source.Length; while (count > 0) { int bytesWritten = CheckFileCall(Interop.Sys.Write(_fileHandle, bufPtr + offset, count)); - Debug.Assert(bytesWritten <= count); - _filePosition += bytesWritten; - count -= bytesWritten; offset += bytesWritten; + count -= bytesWritten; } } } @@ -710,103 +631,77 @@ namespace System.IO /// the current position within this stream by the number of bytes written, and /// monitors cancellation requests. /// </summary> - /// <param name="buffer">The buffer to write data from.</param> - /// <param name="offset">The zero-based byte offset in buffer from which to begin copying bytes to the stream.</param> - /// <param name="count">The maximum number of bytes to write.</param> + /// <param name="source">The buffer to write data from.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <returns>A task that represents the asynchronous write operation.</returns> - private Task WriteAsyncInternal(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + private Task WriteAsyncInternal(ReadOnlyMemory<byte> source, CancellationToken cancellationToken) { + Debug.Assert(_useAsyncIO); + if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); if (_fileHandle.IsClosed) throw Error.GetFileNotOpen(); - if (_useAsyncIO) + if (!CanWrite) // match Windows behavior; this gets thrown synchronously { - if (!CanWrite) // match Windows behavior; this gets thrown synchronously - { - throw Error.GetWriteNotSupported(); - } + throw Error.GetWriteNotSupported(); + } - // Serialize operations using the semaphore. - Task waitTask = _asyncState.WaitAsync(); + // Serialize operations using the semaphore. + Task waitTask = _asyncState.WaitAsync(); - // If we got ownership immediately, and if there's enough space in our buffer - // to buffer the entire write request, then do so and we're done. - if (waitTask.Status == TaskStatus.RanToCompletion) + // If we got ownership immediately, and if there's enough space in our buffer + // to buffer the entire write request, then do so and we're done. + if (waitTask.Status == TaskStatus.RanToCompletion) + { + int spaceRemaining = _bufferLength - _writePos; + if (spaceRemaining >= source.Length) { - int spaceRemaining = _bufferLength - _writePos; - if (spaceRemaining >= count) + try { - try - { - PrepareForWriting(); - - Buffer.BlockCopy(buffer, offset, GetBuffer(), _writePos, count); - _writePos += count; - - return Task.CompletedTask; - } - catch (Exception exc) - { - return Task.FromException(exc); - } - finally - { - _asyncState.Release(); - } - } - } + PrepareForWriting(); - // Otherwise, issue the whole request asynchronously. - _asyncState.Update(buffer, offset, count); - return waitTask.ContinueWith((t, s) => - { - // The options available on Unix for writing asynchronously to an arbitrary file - // handle typically amount to just using another thread to do the synchronous write, - // which is exactly what this implementation does. This does mean there are subtle - // differences in certain FileStream behaviors between Windows and Unix when multiple - // asynchronous operations are issued against the stream to execute concurrently; on - // Unix the operations will be serialized due to the usage of a semaphore, but the - // position /length information won't be updated until after the write has completed, - // whereas on Windows it may happen before the write has completed. - - Debug.Assert(t.Status == TaskStatus.RanToCompletion); - var thisRef = (FileStream)s; - try + source.Span.CopyTo(new Span<byte>(GetBuffer(), _writePos, source.Length)); + _writePos += source.Length; + + return Task.CompletedTask; + } + catch (Exception exc) { - byte[] b = thisRef._asyncState._buffer; - thisRef._asyncState._buffer = null; // remove reference to user's buffer - thisRef.WriteCore(b, thisRef._asyncState._offset, thisRef._asyncState._count); + return Task.FromException(exc); } - finally { thisRef._asyncState.Release(); } - }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default); - } - else - { - return base.WriteAsync(buffer, offset, count, cancellationToken); + finally + { + _asyncState.Release(); + } + } } - } - /// <summary> - /// Writes a byte to the current position in the stream and advances the position - /// within the stream by one byte. - /// </summary> - /// <param name="value">The byte to write to the stream.</param> - public override void WriteByte(byte value) // avoids an array allocation in the base implementation - { - if (_useAsyncIO) + // Otherwise, issue the whole request asynchronously. + _asyncState.ReadOnlyMemory = source; + return waitTask.ContinueWith((t, s) => { - _asyncState.Wait(); - try { WriteByteCore(value); } - finally { _asyncState.Release(); } - } - else - { - WriteByteCore(value); - } + // The options available on Unix for writing asynchronously to an arbitrary file + // handle typically amount to just using another thread to do the synchronous write, + // which is exactly what this implementation does. This does mean there are subtle + // differences in certain FileStream behaviors between Windows and Unix when multiple + // asynchronous operations are issued against the stream to execute concurrently; on + // Unix the operations will be serialized due to the usage of a semaphore, but the + // position/length information won't be updated until after the write has completed, + // whereas on Windows it may happen before the write has completed. + + Debug.Assert(t.Status == TaskStatus.RanToCompletion); + var thisRef = (FileStream)s; + try + { + ReadOnlyMemory<byte> readOnlyMemory = thisRef._asyncState.ReadOnlyMemory; + thisRef._asyncState.ReadOnlyMemory = default(ReadOnlyMemory<byte>); + thisRef.WriteSpan(readOnlyMemory.Span); + } + finally { thisRef._asyncState.Release(); } + }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default); } /// <summary>Sets the current position of this stream to the given value.</summary> @@ -907,25 +802,11 @@ namespace System.IO /// <summary>State used when the stream is in async mode.</summary> private sealed class AsyncState : SemaphoreSlim { - /// <summary>The caller's buffer currently being used by the active async operation.</summary> - internal byte[] _buffer; - /// <summary>The caller's offset currently being used by the active async operation.</summary> - internal int _offset; - /// <summary>The caller's count currently being used by the active async operation.</summary> - internal int _count; - /// <summary>The last task successfully, synchronously returned task from ReadAsync.</summary> - internal Task<int> _lastSuccessfulReadTask; + internal ReadOnlyMemory<byte> ReadOnlyMemory; + internal Memory<byte> Memory; /// <summary>Initialize the AsyncState.</summary> internal AsyncState() : base(initialCount: 1, maxCount: 1) { } - - /// <summary>Sets the active buffer, offset, and count.</summary> - internal void Update(byte[] buffer, int offset, int count) - { - _buffer = buffer; - _offset = offset; - _count = count; - } } } } diff --git a/src/mscorlib/shared/System/IO/FileStream.Win32.cs b/src/mscorlib/shared/System/IO/FileStream.Win32.cs index 61cd007895..4b75ad6dad 100644 --- a/src/mscorlib/shared/System/IO/FileStream.Win32.cs +++ b/src/mscorlib/shared/System/IO/FileStream.Win32.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.Win32.SafeHandles; -using System.Runtime.InteropServices; namespace System.IO { @@ -11,6 +10,11 @@ namespace System.IO { private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options) { + return CreateFileOpenHandle(mode, share, options); + } + + private unsafe SafeFileHandle CreateFileOpenHandle(FileMode mode, FileShare share, FileOptions options) + { Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share); int fAccess = @@ -30,44 +34,13 @@ namespace System.IO // For mitigating local elevation of privilege attack through named pipes // make sure we always call CreateFile with SECURITY_ANONYMOUS so that the // named pipe server can't impersonate a high privileged client security context + // (note that this is the effective default on CreateFile2) flagsAndAttributes |= (Interop.Kernel32.SecurityOptions.SECURITY_SQOS_PRESENT | Interop.Kernel32.SecurityOptions.SECURITY_ANONYMOUS); - // Don't pop up a dialog for reading from an empty floppy drive - uint oldMode; - bool success = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out oldMode); - try - { - SafeFileHandle fileHandle = Interop.Kernel32.CreateFile(_path, fAccess, share, ref secAttrs, mode, flagsAndAttributes, IntPtr.Zero); - fileHandle.IsAsync = _useAsyncIO; - - if (fileHandle.IsInvalid) - { - // Return a meaningful exception with the full path. - - // NT5 oddity - when trying to open "C:\" as a Win32FileStream, - // we usually get ERROR_PATH_NOT_FOUND from the OS. We should - // probably be consistent w/ every other directory. - int errorCode = Marshal.GetLastWin32Error(); - - if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && _path.Length == PathInternal.GetRootLength(_path)) - errorCode = Interop.Errors.ERROR_ACCESS_DENIED; - - throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); - } - - int fileType = Interop.Kernel32.GetFileType(fileHandle); - if (fileType != Interop.Kernel32.FileTypes.FILE_TYPE_DISK) - { - fileHandle.Dispose(); - throw new NotSupportedException(SR.NotSupported_FileStreamOnNonFiles); - } - - return fileHandle; - } - finally + using (DisableMediaInsertionPrompt.Create()) { - if (success) - Interop.Kernel32.SetThreadErrorMode(oldMode, out oldMode); + return ValidateFileHandle( + Interop.Kernel32.CreateFile(_path, fAccess, share, ref secAttrs, mode, flagsAndAttributes, IntPtr.Zero)); } } } diff --git a/src/mscorlib/shared/System/IO/FileStream.WinRT.cs b/src/mscorlib/shared/System/IO/FileStream.WinRT.cs index b9a9f8a783..304088eea7 100644 --- a/src/mscorlib/shared/System/IO/FileStream.WinRT.cs +++ b/src/mscorlib/shared/System/IO/FileStream.WinRT.cs @@ -13,7 +13,7 @@ namespace System.IO { Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share); - int fAccess = + int access = ((_access & FileAccess.Read) == FileAccess.Read ? GENERIC_READ : 0) | ((_access & FileAccess.Write) == FileAccess.Write ? GENERIC_WRITE : 0); @@ -30,31 +30,15 @@ namespace System.IO parameters.dwFileFlags = (uint)options; parameters.lpSecurityAttributes = &secAttrs; - SafeFileHandle fileHandle = Interop.Kernel32.CreateFile2( - lpFileName: _path, - dwDesiredAccess: fAccess, - dwShareMode: share, - dwCreationDisposition: mode, - pCreateExParams: ¶meters); - - fileHandle.IsAsync = _useAsyncIO; - - if (fileHandle.IsInvalid) + using (DisableMediaInsertionPrompt.Create()) { - // Return a meaningful exception with the full path. - - // NT5 oddity - when trying to open "C:\" as a Win32FileStream, - // we usually get ERROR_PATH_NOT_FOUND from the OS. We should - // probably be consistent w/ every other directory. - int errorCode = Marshal.GetLastWin32Error(); - - if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && _path.Length == PathInternal.GetRootLength(_path)) - errorCode = Interop.Errors.ERROR_ACCESS_DENIED; - - throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); + return ValidateFileHandle(Interop.Kernel32.CreateFile2( + lpFileName: _path, + dwDesiredAccess: access, + dwShareMode: share, + dwCreationDisposition: mode, + pCreateExParams: ref parameters)); } - - return fileHandle; } } } diff --git a/src/mscorlib/shared/System/IO/FileStream.Windows.cs b/src/mscorlib/shared/System/IO/FileStream.Windows.cs index cdf1cb092e..eec11b4b13 100644 --- a/src/mscorlib/shared/System/IO/FileStream.Windows.cs +++ b/src/mscorlib/shared/System/IO/FileStream.Windows.cs @@ -48,7 +48,6 @@ namespace System.IO private static unsafe IOCompletionCallback s_ioCallback = FileStreamCompletionSource.IOCallback; - private Task<int> _lastSynchronouslyCompletedTask = null; // cached task for read ops that complete synchronously private Task _activeBufferOperation = null; // tracks in-progress async ops using the buffer private PreAllocatedOverlapped _preallocatedOverlapped; // optimization for async ops to avoid per-op allocations private FileStreamCompletionSource _currentOverlappedOwner; // async op currently using the preallocated overlapped @@ -313,7 +312,7 @@ namespace System.IO // If the buffer is already flushed, don't spin up the OS write if (_writePos == 0) return Task.CompletedTask; - Task flushTask = WriteInternalCoreAsync(GetBuffer(), 0, _writePos, cancellationToken); + Task flushTask = WriteAsyncInternalCore(new ReadOnlyMemory<byte>(GetBuffer(), 0, _writePos), cancellationToken); _writePos = 0; // Update the active buffer operation @@ -324,6 +323,8 @@ namespace System.IO return flushTask; } + private void FlushWriteBufferForWriteByte() => FlushWriteBuffer(); + // Writes are buffered. Anytime the buffer fills up // (_writePos + delta > _bufferSize) or the buffer switches to reading // and there is left over data (_writePos > 0), this function must be called. @@ -355,7 +356,7 @@ namespace System.IO } else { - WriteCore(GetBuffer(), 0, _writePos); + WriteCore(new ReadOnlySpan<byte>(GetBuffer(), 0, _writePos)); } _writePos = 0; @@ -411,14 +412,9 @@ namespace System.IO // accessing its fields by ref. This avoids a compiler warning. private FileStreamCompletionSource CompareExchangeCurrentOverlappedOwner(FileStreamCompletionSource newSource, FileStreamCompletionSource existingSource) => Interlocked.CompareExchange(ref _currentOverlappedOwner, newSource, existingSource); - public override int Read(byte[] array, int offset, int count) - { - ValidateReadWriteArgs(array, offset, count); - return ReadCore(array, offset, count); - } - - private int ReadCore(byte[] array, int offset, int count) + private int ReadSpan(Span<byte> destination) { + Debug.Assert(!_useAsyncIO, "Must only be used when in synchronous mode"); Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); @@ -430,23 +426,23 @@ namespace System.IO { if (!CanRead) throw Error.GetReadNotSupported(); if (_writePos > 0) FlushWriteBuffer(); - if (!CanSeek || (count >= _bufferLength)) + if (!CanSeek || (destination.Length >= _bufferLength)) { - n = ReadNative(array, offset, count); + n = ReadNative(destination); // Throw away read buffer. _readPos = 0; _readLength = 0; return n; } - n = ReadNative(GetBuffer(), 0, _bufferLength); + n = ReadNative(GetBuffer()); if (n == 0) return 0; isBlocked = n < _bufferLength; _readPos = 0; _readLength = n; } // Now copy min of count or numBytesAvailable (i.e. near EOF) to array. - if (n > count) n = count; - Buffer.BlockCopy(GetBuffer(), _readPos, array, offset, n); + if (n > destination.Length) n = destination.Length; + new ReadOnlySpan<byte>(GetBuffer(), _readPos, n).CopyTo(destination); _readPos += n; // We may have read less than the number of bytes the user asked @@ -466,10 +462,10 @@ namespace System.IO // read some more from the underlying stream. However, if we got // fewer bytes from the underlying stream than we asked for (i.e. we're // probably blocked), don't ask for more bytes. - if (n < count && !isBlocked) + if (n < destination.Length && !isBlocked) { Debug.Assert(_readPos == _readLength, "Read buffer should be empty!"); - int moreBytesRead = ReadNative(array, offset + n, count - n); + int moreBytesRead = ReadNative(destination.Slice(n)); n += moreBytesRead; // We've just made our buffer inconsistent with our position // pointer. We must throw away the read buffer. @@ -482,28 +478,28 @@ namespace System.IO } [Conditional("DEBUG")] - private void AssertCanRead(byte[] buffer, int offset, int count) + private void AssertCanRead() { Debug.Assert(!_fileHandle.IsClosed, "!_fileHandle.IsClosed"); Debug.Assert(CanRead, "CanRead"); - Debug.Assert(buffer != null, "buffer != null"); - Debug.Assert(_writePos == 0, "_writePos == 0"); - Debug.Assert(offset >= 0, "offset is negative"); - Debug.Assert(count >= 0, "count is negative"); } - private unsafe int ReadNative(byte[] buffer, int offset, int count) - { - AssertCanRead(buffer, offset, count); + /// <summary>Reads from the file handle into the buffer, overwriting anything in it.</summary> + private int FillReadBufferForReadByte() => + _useAsyncIO ? + ReadNativeAsync(new Memory<byte>(_buffer), 0, CancellationToken.None).GetAwaiter().GetResult() : + ReadNative(_buffer); - if (_useAsyncIO) - return ReadNativeAsync(buffer, offset, count, 0, CancellationToken.None).GetAwaiter().GetResult(); + private unsafe int ReadNative(Span<byte> buffer) + { + Debug.Assert(!_useAsyncIO, $"{nameof(ReadNative)} doesn't work on asynchronous file streams."); + AssertCanRead(); // Make sure we are reading from the right spot VerifyOSHandlePosition(); int errorCode = 0; - int r = ReadFileNative(_fileHandle, buffer, offset, count, null, out errorCode); + int r = ReadFileNative(_fileHandle, buffer, null, out errorCode); if (r == -1) { @@ -579,7 +575,6 @@ namespace System.IO { if (_readPos > 0) { - //Console.WriteLine("Seek: seeked for 0, adjusting buffer back by: "+_readPos+" _readLen: "+_readLen); Buffer.BlockCopy(GetBuffer(), _readPos, GetBuffer(), 0, _readLength - _readPos); _readLength -= _readPos; _readPos = 0; @@ -592,7 +587,6 @@ namespace System.IO else if (oldPos - _readPos < pos && pos < oldPos + _readLength - _readPos) { int diff = (int)(pos - oldPos); - //Console.WriteLine("Seek: diff was "+diff+", readpos was "+_readPos+" adjusting buffer - shrinking by "+ (_readPos + diff)); Buffer.BlockCopy(GetBuffer(), _readPos + diff, GetBuffer(), 0, _readLength - (_readPos + diff)); _readLength -= (_readPos + diff); _readPos = 0; @@ -646,9 +640,9 @@ namespace System.IO _preallocatedOverlapped = new PreAllocatedOverlapped(s_ioCallback, this, _buffer); } - public override void Write(byte[] array, int offset, int count) + private void WriteSpan(ReadOnlySpan<byte> source) { - ValidateReadWriteArgs(array, offset, count); + Debug.Assert(!_useAsyncIO, "Must only be used when in synchronous mode"); if (_writePos == 0) { @@ -672,65 +666,56 @@ namespace System.IO int numBytes = _bufferLength - _writePos; // space left in buffer if (numBytes > 0) { - if (numBytes > count) - numBytes = count; - Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, numBytes); - _writePos += numBytes; - if (count == numBytes) return; - offset += numBytes; - count -= numBytes; + if (numBytes >= source.Length) + { + source.CopyTo(new Span<byte>(GetBuffer()).Slice(_writePos)); + _writePos += source.Length; + return; + } + else + { + source.Slice(0, numBytes).CopyTo(new Span<byte>(GetBuffer()).Slice(_writePos)); + _writePos += numBytes; + source = source.Slice(numBytes); + } } // Reset our buffer. We essentially want to call FlushWrite // without calling Flush on the underlying Stream. - if (_useAsyncIO) - { - WriteInternalCoreAsync(GetBuffer(), 0, _writePos, CancellationToken.None).GetAwaiter().GetResult(); - } - else - { - WriteCore(GetBuffer(), 0, _writePos); - } + WriteCore(new ReadOnlySpan<byte>(GetBuffer(), 0, _writePos)); _writePos = 0; } + // If the buffer would slow writes down, avoid buffer completely. - if (count >= _bufferLength) + if (source.Length >= _bufferLength) { Debug.Assert(_writePos == 0, "FileStream cannot have buffered data to write here! Your stream will be corrupted."); - WriteCore(array, offset, count); + WriteCore(source); return; } - else if (count == 0) + else if (source.Length == 0) { return; // Don't allocate a buffer then call memcpy for 0 bytes. } // Copy remaining bytes into buffer, to write at a later date. - Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, count); - _writePos = count; + source.CopyTo(new Span<byte>(GetBuffer()).Slice(_writePos)); + _writePos = source.Length; return; } - private unsafe void WriteCore(byte[] buffer, int offset, int count) + private unsafe void WriteCore(ReadOnlySpan<byte> source) { + Debug.Assert(!_useAsyncIO); Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); Debug.Assert(CanWrite, "_parent.CanWrite"); - - Debug.Assert(buffer != null, "buffer != null"); Debug.Assert(_readPos == _readLength, "_readPos == _readLen"); - Debug.Assert(offset >= 0, "offset is negative"); - Debug.Assert(count >= 0, "count is negative"); - if (_useAsyncIO) - { - WriteInternalCoreAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult(); - return; - } // Make sure we are writing to the position that we think we are VerifyOSHandlePosition(); int errorCode = 0; - int r = WriteFileNative(_fileHandle, buffer, offset, count, null, out errorCode); + int r = WriteFileNative(_fileHandle, source, null, out errorCode); if (r == -1) { @@ -742,9 +727,8 @@ namespace System.IO else { // ERROR_INVALID_PARAMETER may be returned for writes - // where the position is too large (i.e. writing at Int64.MaxValue - // on Win9x) OR for synchronous writes to a handle opened - // asynchronously. + // where the position is too large or for synchronous writes + // to a handle opened asynchronously. if (errorCode == ERROR_INVALID_PARAMETER) throw new IOException(SR.IO_FileTooLongOrHandleNotSync); throw Win32Marshal.GetExceptionForWin32Error(errorCode); @@ -755,15 +739,9 @@ namespace System.IO return; } - private Task<int> ReadAsyncInternal(byte[] array, int offset, int numBytes, CancellationToken cancellationToken) + private Task<int> ReadAsyncInternal(Memory<byte> destination, CancellationToken cancellationToken, out int synchronousResult) { - // If async IO is not supported on this platform or - // if this Win32FileStream was not opened with FileOptions.Asynchronous. - if (!_useAsyncIO) - { - return base.ReadAsync(array, offset, numBytes, cancellationToken); - } - + Debug.Assert(_useAsyncIO); if (!CanRead) throw Error.GetReadNotSupported(); Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); @@ -785,18 +763,17 @@ namespace System.IO // pipes. But don't completely ignore buffered data either. if (_readPos < _readLength) { - int n = _readLength - _readPos; - if (n > numBytes) n = numBytes; - Buffer.BlockCopy(GetBuffer(), _readPos, array, offset, n); + int n = Math.Min(_readLength - _readPos, destination.Length); + new Span<byte>(GetBuffer(), _readPos, n).CopyTo(destination.Span); _readPos += n; - - // Return a completed task - return TaskFromResultOrCache(n); + synchronousResult = n; + return null; } else { Debug.Assert(_writePos == 0, "Win32FileStream must not have buffered write data here! Pipes should be unidirectional."); - return ReadNativeAsync(array, offset, numBytes, 0, cancellationToken); + synchronousResult = 0; + return ReadNativeAsync(destination, 0, cancellationToken); } } @@ -817,17 +794,16 @@ namespace System.IO // problem, and any async read less than 64K gets turned into a // synchronous read by NT anyways... -- - if (numBytes < _bufferLength) + if (destination.Length < _bufferLength) { - Task<int> readTask = ReadNativeAsync(GetBuffer(), 0, _bufferLength, 0, cancellationToken); + Task<int> readTask = ReadNativeAsync(new Memory<byte>(GetBuffer()), 0, cancellationToken); _readLength = readTask.GetAwaiter().GetResult(); - int n = _readLength; - if (n > numBytes) n = numBytes; - Buffer.BlockCopy(GetBuffer(), 0, array, offset, n); + int n = Math.Min(_readLength, destination.Length); + new Span<byte>(GetBuffer(), 0, n).CopyTo(destination.Span); _readPos = n; - // Return a completed task (recycling the one above if possible) - return (_readLength == n ? readTask : TaskFromResultOrCache(n)); + synchronousResult = n; + return null; } else { @@ -835,20 +811,21 @@ namespace System.IO // with our read buffer. Throw away the read buffer's contents. _readPos = 0; _readLength = 0; - return ReadNativeAsync(array, offset, numBytes, 0, cancellationToken); + synchronousResult = 0; + return ReadNativeAsync(destination, 0, cancellationToken); } } else { - int n = _readLength - _readPos; - if (n > numBytes) n = numBytes; - Buffer.BlockCopy(GetBuffer(), _readPos, array, offset, n); + int n = Math.Min(_readLength - _readPos, destination.Length); + new Span<byte>(GetBuffer(), _readPos, n).CopyTo(destination.Span); _readPos += n; - if (n >= numBytes) + if (n == destination.Length) { // Return a completed task - return TaskFromResultOrCache(n); + synchronousResult = n; + return null; } else { @@ -859,19 +836,21 @@ namespace System.IO // Throw away read buffer. _readPos = 0; _readLength = 0; - return ReadNativeAsync(array, offset + n, numBytes - n, n, cancellationToken); + synchronousResult = 0; + return ReadNativeAsync(destination.Slice(n), n, cancellationToken); } } } - unsafe private Task<int> ReadNativeAsync(byte[] bytes, int offset, int numBytes, int numBufferedBytesRead, CancellationToken cancellationToken) + unsafe private Task<int> ReadNativeAsync(Memory<byte> destination, int numBufferedBytesRead, CancellationToken cancellationToken) { - AssertCanRead(bytes, offset, numBytes); + AssertCanRead(); Debug.Assert(_useAsyncIO, "ReadNativeAsync doesn't work on synchronous file streams!"); // Create and store async stream class library specific data in the async result - - FileStreamCompletionSource completionSource = new FileStreamCompletionSource(this, numBufferedBytesRead, bytes, cancellationToken); + FileStreamCompletionSource completionSource = destination.TryGetArray(out ArraySegment<byte> memoryArray) ? + new FileStreamCompletionSource(this, numBufferedBytesRead, memoryArray.Array) : + new MemoryFileStreamCompletionSource(this, numBufferedBytesRead, destination); NativeOverlapped* intOverlapped = completionSource.Overlapped; // Calculate position in the file we should be at after the read is done @@ -882,12 +861,16 @@ namespace System.IO // Make sure we are reading from the position that we think we are VerifyOSHandlePosition(); - if (_filePosition + numBytes > len) + if (_filePosition + destination.Length > len) { if (_filePosition <= len) - numBytes = (int)(len - _filePosition); + { + destination = destination.Slice(0, (int)(len - _filePosition)); + } else - numBytes = 0; + { + destination = default(Memory<byte>); + } } // Now set the position to read from in the NativeOverlapped struct @@ -904,12 +887,13 @@ namespace System.IO // the file pointer when writing to a UNC path! // So changed the code below to seek to an absolute // location, not a relative one. ReadFile seems consistent though. - SeekCore(_fileHandle, numBytes, SeekOrigin.Current); + SeekCore(_fileHandle, destination.Length, SeekOrigin.Current); } // queue an async ReadFile operation and pass in a packed overlapped int errorCode = 0; - int r = ReadFileNative(_fileHandle, bytes, offset, numBytes, intOverlapped, out errorCode); + int r = ReadFileNative(_fileHandle, destination.Span, intOverlapped, out errorCode); + // ReadFile, the OS version, will return 0 on failure. But // my ReadFileNative wrapper returns -1. My wrapper will return // the following: @@ -920,7 +904,7 @@ namespace System.IO // read back from this call when using overlapped structures! You must // not pass in a non-null lpNumBytesRead to ReadFile when using // overlapped structures! This is by design NT behavior. - if (r == -1 && numBytes != -1) + if (r == -1) { // For pipes, when they hit EOF, they will come here. if (errorCode == ERROR_BROKEN_PIPE) @@ -951,10 +935,10 @@ namespace System.IO throw Win32Marshal.GetExceptionForWin32Error(errorCode); } } - else + else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING { // Only once the IO is pending do we register for cancellation - completionSource.RegisterForCancellation(); + completionSource.RegisterForCancellation(cancellationToken); } } else @@ -966,33 +950,19 @@ namespace System.IO // synchronously or asynchronously. We absolutely must not // set asyncResult._numBytes here, since will never have correct // results. - //Console.WriteLine("ReadFile returned: "+r+" (0x"+Int32.Format(r, "x")+") The IO completed synchronously, but the user callback was called on a separate thread"); } return completionSource.Task; } - // Reads a byte from the file stream. Returns the byte cast to an int - // or -1 if reading from the end of the stream. - public override int ReadByte() + private Task WriteAsyncInternal(ReadOnlyMemory<byte> source, CancellationToken cancellationToken) { - return ReadByteCore(); - } - - private Task WriteAsyncInternal(byte[] array, int offset, int numBytes, CancellationToken cancellationToken) - { - // If async IO is not supported on this platform or - // if this Win32FileStream was not opened with FileOptions.Asynchronous. - if (!_useAsyncIO) - { - return base.WriteAsync(array, offset, numBytes, cancellationToken); - } - - if (!CanWrite) throw Error.GetWriteNotSupported(); - + Debug.Assert(_useAsyncIO); Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); Debug.Assert(!_isPipe || (_readPos == 0 && _readLength == 0), "Win32FileStream must not have buffered data here! Pipes should be unidirectional."); + if (!CanWrite) throw Error.GetWriteNotSupported(); + bool writeDataStoredInBuffer = false; if (!_isPipe) // avoid async buffering with pipes, as doing so can lead to deadlocks (see comments in ReadInternalAsyncCore) { @@ -1016,10 +986,10 @@ namespace System.IO // - There's no active flush operation, such that we don't have to worry about the existing buffer being in use. // - And the data we're trying to write fits in the buffer, meaning it wasn't already filled by previous writes. // In that case, just store it in the buffer. - if (numBytes < _bufferLength && !HasActiveBufferOperation && numBytes <= remainingBuffer) + if (source.Length < _bufferLength && !HasActiveBufferOperation && source.Length <= remainingBuffer) { - Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, numBytes); - _writePos += numBytes; + source.Span.CopyTo(new Span<byte>(GetBuffer(), _writePos, source.Length)); + _writePos += source.Length; writeDataStoredInBuffer = true; // There is one special-but-common case, common because devs often use @@ -1028,7 +998,7 @@ namespace System.IO // then we're done and can return a completed task now. But if we filled the buffer // completely, we want to do the asynchronous flush/write as part of this operation // rather than waiting until the next write that fills the buffer. - if (numBytes != remainingBuffer) + if (source.Length != remainingBuffer) return Task.CompletedTask; Debug.Assert(_writePos == _bufferLength); @@ -1084,40 +1054,37 @@ namespace System.IO // Finally, issue the write asynchronously, and return a Task that logically // represents the write operation, including any flushing done. - Task writeTask = WriteInternalCoreAsync(array, offset, numBytes, cancellationToken); + Task writeTask = WriteAsyncInternalCore(source, cancellationToken); return (flushTask == null || flushTask.Status == TaskStatus.RanToCompletion) ? writeTask : (writeTask.Status == TaskStatus.RanToCompletion) ? flushTask : Task.WhenAll(flushTask, writeTask); } - private unsafe Task WriteInternalCoreAsync(byte[] bytes, int offset, int numBytes, CancellationToken cancellationToken) + private unsafe Task WriteAsyncInternalCore(ReadOnlyMemory<byte> source, CancellationToken cancellationToken) { Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); Debug.Assert(CanWrite, "_parent.CanWrite"); - Debug.Assert(bytes != null, "bytes != null"); Debug.Assert(_readPos == _readLength, "_readPos == _readLen"); Debug.Assert(_useAsyncIO, "WriteInternalCoreAsync doesn't work on synchronous file streams!"); - Debug.Assert(offset >= 0, "offset is negative"); - Debug.Assert(numBytes >= 0, "numBytes is negative"); // Create and store async stream class library specific data in the async result - FileStreamCompletionSource completionSource = new FileStreamCompletionSource(this, 0, bytes, cancellationToken); + FileStreamCompletionSource completionSource = source.DangerousTryGetArray(out ArraySegment<byte> array) ? + new FileStreamCompletionSource(this, 0, array.Array) : + new MemoryFileStreamCompletionSource(this, 0, source); NativeOverlapped* intOverlapped = completionSource.Overlapped; if (CanSeek) { // Make sure we set the length of the file appropriately. long len = Length; - //Console.WriteLine("WriteInternalCoreAsync - Calculating end pos. pos: "+pos+" len: "+len+" numBytes: "+numBytes); // Make sure we are writing to the position that we think we are VerifyOSHandlePosition(); - if (_filePosition + numBytes > len) + if (_filePosition + source.Length > len) { - //Console.WriteLine("WriteInternalCoreAsync - Setting length to: "+(pos + numBytes)); - SetLengthCore(_filePosition + numBytes); + SetLengthCore(_filePosition + source.Length); } // Now set the position to read from in the NativeOverlapped struct @@ -1128,14 +1095,12 @@ namespace System.IO // When using overlapped IO, the OS is not supposed to // touch the file pointer location at all. We will adjust it // ourselves. This isn't threadsafe. - SeekCore(_fileHandle, numBytes, SeekOrigin.Current); + SeekCore(_fileHandle, source.Length, SeekOrigin.Current); } - //Console.WriteLine("WriteInternalCoreAsync finishing. pos: "+pos+" numBytes: "+numBytes+" _pos: "+_pos+" Position: "+Position); - int errorCode = 0; // queue an async WriteFile operation and pass in a packed overlapped - int r = WriteFileNative(_fileHandle, bytes, offset, numBytes, intOverlapped, out errorCode); + int r = WriteFileNative(_fileHandle, source.Span, intOverlapped, out errorCode); // WriteFile, the OS version, will return 0 on failure. But // my WriteFileNative wrapper returns -1. My wrapper will return @@ -1147,10 +1112,8 @@ namespace System.IO // written back from this call when using overlapped IO! You must // not pass in a non-null lpNumBytesWritten to WriteFile when using // overlapped structures! This is ByDesign NT behavior. - if (r == -1 && numBytes != -1) + if (r == -1) { - //Console.WriteLine("WriteFile returned 0; Write will complete asynchronously (if errorCode==3e5) errorCode: 0x{0:x}", errorCode); - // For pipes, when they are closed on the other side, they will come here. if (errorCode == ERROR_NO_DATA) { @@ -1177,10 +1140,10 @@ namespace System.IO throw Win32Marshal.GetExceptionForWin32Error(errorCode); } } - else // ERROR_IO_PENDING + else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING { // Only once the IO is pending do we register for cancellation - completionSource.RegisterForCancellation(); + completionSource.RegisterForCancellation(cancellationToken); } } else @@ -1192,17 +1155,11 @@ namespace System.IO // synchronously or asynchronously. We absolutely must not // set asyncResult._numBytes here, since will never have correct // results. - //Console.WriteLine("WriteFile returned: "+r+" (0x"+Int32.Format(r, "x")+") The IO completed synchronously, but the user callback was called on another thread."); } return completionSource.Task; } - public override void WriteByte(byte value) - { - WriteByteCore(value); - } - // Windows API definitions, from winbase.h and others private const int FILE_ATTRIBUTE_NORMAL = 0x00000080; @@ -1223,35 +1180,19 @@ namespace System.IO private const int ERROR_IO_PENDING = 997; // __ConsoleStream also uses this code. - private unsafe int ReadFileNative(SafeFileHandle handle, byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int errorCode) + private unsafe int ReadFileNative(SafeFileHandle handle, Span<byte> bytes, NativeOverlapped* overlapped, out int errorCode) { Debug.Assert(handle != null, "handle != null"); - Debug.Assert(offset >= 0, "offset >= 0"); - Debug.Assert(count >= 0, "count >= 0"); - Debug.Assert(bytes != null, "bytes != null"); - // Don't corrupt memory when multiple threads are erroneously writing - // to this stream simultaneously. - if (bytes.Length - offset < count) - throw new IndexOutOfRangeException(SR.IndexOutOfRange_IORaceCondition); - Debug.Assert((_useAsyncIO && overlapped != null) || (!_useAsyncIO && overlapped == null), "Async IO and overlapped parameters inconsistent in call to ReadFileNative."); - // You can't use the fixed statement on an array of length 0. - if (bytes.Length == 0) - { - errorCode = 0; - return 0; - } - - int r = 0; + int r; int numBytesRead = 0; - fixed (byte* p = &bytes[0]) + fixed (byte* p = &bytes.DangerousGetPinnableReference()) { - if (_useAsyncIO) - r = Interop.Kernel32.ReadFile(handle, p + offset, count, IntPtr.Zero, overlapped); - else - r = Interop.Kernel32.ReadFile(handle, p + offset, count, out numBytesRead, IntPtr.Zero); + r = _useAsyncIO ? + Interop.Kernel32.ReadFile(handle, p, bytes.Length, IntPtr.Zero, overlapped) : + Interop.Kernel32.ReadFile(handle, p, bytes.Length, out numBytesRead, IntPtr.Zero); } if (r == 0) @@ -1266,37 +1207,19 @@ namespace System.IO } } - private unsafe int WriteFileNative(SafeFileHandle handle, byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int errorCode) + private unsafe int WriteFileNative(SafeFileHandle handle, ReadOnlySpan<byte> buffer, NativeOverlapped* overlapped, out int errorCode) { Debug.Assert(handle != null, "handle != null"); - Debug.Assert(offset >= 0, "offset >= 0"); - Debug.Assert(count >= 0, "count >= 0"); - Debug.Assert(bytes != null, "bytes != null"); - // Don't corrupt memory when multiple threads are erroneously writing - // to this stream simultaneously. (the OS is reading from - // the array we pass to WriteFile, but if we read beyond the end and - // that memory isn't allocated, we could get an AV.) - if (bytes.Length - offset < count) - throw new IndexOutOfRangeException(SR.IndexOutOfRange_IORaceCondition); - Debug.Assert((_useAsyncIO && overlapped != null) || (!_useAsyncIO && overlapped == null), "Async IO and overlapped parameters inconsistent in call to WriteFileNative."); - // You can't use the fixed statement on an array of length 0. - if (bytes.Length == 0) - { - errorCode = 0; - return 0; - } - int numBytesWritten = 0; - int r = 0; + int r; - fixed (byte* p = &bytes[0]) + fixed (byte* p = &buffer.DangerousGetPinnableReference()) { - if (_useAsyncIO) - r = Interop.Kernel32.WriteFile(handle, p + offset, count, IntPtr.Zero, overlapped); - else - r = Interop.Kernel32.WriteFile(handle, p + offset, count, out numBytesWritten, IntPtr.Zero); + r = _useAsyncIO ? + Interop.Kernel32.WriteFile(handle, p, buffer.Length, IntPtr.Zero, overlapped) : + Interop.Kernel32.WriteFile(handle, p, buffer.Length, out numBytesWritten, IntPtr.Zero); } if (r == 0) @@ -1472,7 +1395,7 @@ namespace System.IO } // Kick off the read. - synchronousSuccess = ReadFileNative(_fileHandle, copyBuffer, 0, copyBuffer.Length, readAwaitable._nativeOverlapped, out errorCode) >= 0; + synchronousSuccess = ReadFileNative(_fileHandle, copyBuffer, readAwaitable._nativeOverlapped, out errorCode) >= 0; } // If the operation did not synchronously succeed, it either failed or initiated the asynchronous operation. @@ -1696,20 +1619,6 @@ namespace System.IO } } - private Task<int> TaskFromResultOrCache(int result) - { - Task<int> completedTask = _lastSynchronouslyCompletedTask; - Debug.Assert(completedTask == null || completedTask.Status == TaskStatus.RanToCompletion, "Cached task should have completed successfully"); - - if ((completedTask == null) || (completedTask.Result != result)) - { - completedTask = Task.FromResult(result); - _lastSynchronouslyCompletedTask = completedTask; - } - - return completedTask; - } - private void LockInternal(long position, long length) { int positionLow = unchecked((int)(position)); @@ -1735,5 +1644,25 @@ namespace System.IO throw Win32Marshal.GetExceptionForLastWin32Error(); } } + private SafeFileHandle ValidateFileHandle(SafeFileHandle fileHandle) + { + if (fileHandle.IsInvalid) + { + // Return a meaningful exception with the full path. + + // NT5 oddity - when trying to open "C:\" as a Win32FileStream, + // we usually get ERROR_PATH_NOT_FOUND from the OS. We should + // probably be consistent w/ every other directory. + int errorCode = Marshal.GetLastWin32Error(); + + if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && _path.Length == PathInternal.GetRootLength(_path)) + errorCode = Interop.Errors.ERROR_ACCESS_DENIED; + + throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); + } + + fileHandle.IsAsync = _useAsyncIO; + return fileHandle; + } } } diff --git a/src/mscorlib/shared/System/IO/FileStream.cs b/src/mscorlib/shared/System/IO/FileStream.cs index 39f7b60d12..65c63bcc53 100644 --- a/src/mscorlib/shared/System/IO/FileStream.cs +++ b/src/mscorlib/shared/System/IO/FileStream.cs @@ -51,6 +51,9 @@ namespace System.IO /// </summary> private readonly bool _useAsyncIO; + /// <summary>cached task for read ops that complete synchronously</summary> + private Task<int> _lastSynchronouslyCompletedTask = null; + /// <summary> /// Currently cached position in the stream. This should always mirror the underlying file's actual position, /// and should only ever be out of sync if another stream with access to this same file manipulates it, at which @@ -292,6 +295,36 @@ namespace System.IO return FlushAsyncInternal(cancellationToken); } + public override int Read(byte[] array, int offset, int count) + { + ValidateReadWriteArgs(array, offset, count); + return _useAsyncIO ? + ReadAsyncTask(array, offset, count, CancellationToken.None).GetAwaiter().GetResult() : + ReadSpan(new Span<byte>(array, offset, count)); + } + + public override int Read(Span<byte> destination) + { + if (GetType() == typeof(FileStream) && !_useAsyncIO) + { + if (_fileHandle.IsClosed) + { + throw Error.GetFileNotOpen(); + } + return ReadSpan(destination); + } + else + { + // This type is derived from FileStream and/or the stream is in async mode. If this is a + // derived type, it may have overridden Read(byte[], int, int) prior to this Read(Span<byte>) + // overload being introduced. In that case, this Read(Span<byte>) overload should use the behavior + // of Read(byte[],int,int) overload. Or if the stream is in async mode, we can't call the + // synchronous ReadSpan, so we similarly call the base Read, which will turn delegate to + // Read(byte[],int,int), which will do the right thing if we're in async mode. + return base.Read(destination); + } + } + public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { if (buffer == null) @@ -304,10 +337,12 @@ namespace System.IO throw new ArgumentException(SR.Argument_InvalidOffLen /*, no good single parameter name to pass*/); // If we have been inherited into a subclass, the following implementation could be incorrect - // since it does not call through to Read() or ReadAsync() which a subclass might have overridden. + // since it does not call through to Read() which a subclass might have overridden. // To be safe we will only use this implementation in cases where we know it is safe to do so, // and delegate to our base class (which will call into Read/ReadAsync) when we are not sure. - if (GetType() != typeof(FileStream)) + // Similarly, if we weren't opened for asynchronous I/O, call to the base implementation so that + // Read is invoked asynchronously. + if (GetType() != typeof(FileStream) || !_useAsyncIO) return base.ReadAsync(buffer, offset, count, cancellationToken); if (cancellationToken.IsCancellationRequested) @@ -316,7 +351,86 @@ namespace System.IO if (IsClosed) throw Error.GetFileNotOpen(); - return ReadAsyncInternal(buffer, offset, count, cancellationToken); + return ReadAsyncTask(buffer, offset, count, cancellationToken); + } + + public override ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default(CancellationToken)) + { + if (!_useAsyncIO || GetType() != typeof(FileStream)) + { + // If we're not using async I/O, delegate to the base, which will queue a call to Read. + // Or if this isn't a concrete FileStream, a derived type may have overridden ReadAsync(byte[],...), + // which was introduced first, so delegate to the base which will delegate to that. + return base.ReadAsync(destination, cancellationToken); + } + + if (cancellationToken.IsCancellationRequested) + { + return new ValueTask<int>(Task.FromCanceled<int>(cancellationToken)); + } + + if (IsClosed) + { + throw Error.GetFileNotOpen(); + } + + Task<int> t = ReadAsyncInternal(destination, cancellationToken, out int synchronousResult); + return t != null ? + new ValueTask<int>(t) : + new ValueTask<int>(synchronousResult); + } + + private Task<int> ReadAsyncTask(byte[] array, int offset, int count, CancellationToken cancellationToken) + { + Task<int> t = ReadAsyncInternal(new Memory<byte>(array, offset, count), cancellationToken, out int synchronousResult); + + if (t == null) + { + t = _lastSynchronouslyCompletedTask; + Debug.Assert(t == null || t.IsCompletedSuccessfully, "Cached task should have completed successfully"); + + if (t == null || t.Result != synchronousResult) + { + _lastSynchronouslyCompletedTask = t = Task.FromResult(synchronousResult); + } + } + + return t; + } + + public override void Write(byte[] array, int offset, int count) + { + ValidateReadWriteArgs(array, offset, count); + if (_useAsyncIO) + { + WriteAsyncInternal(new ReadOnlyMemory<byte>(array, offset, count), CancellationToken.None).GetAwaiter().GetResult(); + } + else + { + WriteSpan(new ReadOnlySpan<byte>(array, offset, count)); + } + } + + public override void Write(ReadOnlySpan<byte> destination) + { + if (GetType() == typeof(FileStream) && !_useAsyncIO) + { + if (_fileHandle.IsClosed) + { + throw Error.GetFileNotOpen(); + } + WriteSpan(destination); + } + else + { + // This type is derived from FileStream and/or the stream is in async mode. If this is a + // derived type, it may have overridden Write(byte[], int, int) prior to this Write(ReadOnlySpan<byte>) + // overload being introduced. In that case, this Write(ReadOnlySpan<byte>) overload should use the behavior + // of Write(byte[],int,int) overload. Or if the stream is in async mode, we can't call the + // synchronous WriteSpan, so we similarly call the base Write, which will turn delegate to + // Write(byte[],int,int), which will do the right thing if we're in async mode. + base.Write(destination); + } } public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) @@ -334,7 +448,7 @@ namespace System.IO // since it does not call through to Write() or WriteAsync() which a subclass might have overridden. // To be safe we will only use this implementation in cases where we know it is safe to do so, // and delegate to our base class (which will call into Write/WriteAsync) when we are not sure. - if (GetType() != typeof(FileStream)) + if (!_useAsyncIO || GetType() != typeof(FileStream)) return base.WriteAsync(buffer, offset, count, cancellationToken); if (cancellationToken.IsCancellationRequested) @@ -343,7 +457,30 @@ namespace System.IO if (IsClosed) throw Error.GetFileNotOpen(); - return WriteAsyncInternal(buffer, offset, count, cancellationToken); + return WriteAsyncInternal(new ReadOnlyMemory<byte>(buffer, offset, count), cancellationToken); + } + + public override Task WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken)) + { + if (!_useAsyncIO || GetType() != typeof(FileStream)) + { + // If we're not using async I/O, delegate to the base, which will queue a call to Write. + // Or if this isn't a concrete FileStream, a derived type may have overridden WriteAsync(byte[],...), + // which was introduced first, so delegate to the base which will delegate to that. + return base.WriteAsync(source, cancellationToken); + } + + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled<int>(cancellationToken); + } + + if (IsClosed) + { + throw Error.GetFileNotOpen(); + } + + return WriteAsyncInternal(source, cancellationToken); } /// <summary> @@ -594,7 +731,11 @@ namespace System.IO _readPos = _readLength = 0; } - private int ReadByteCore() + /// <summary> + /// Reads a byte from the file stream. Returns the byte cast to an int + /// or -1 if reading from the end of the stream. + /// </summary> + public override int ReadByte() { PrepareForReading(); @@ -602,9 +743,7 @@ namespace System.IO if (_readPos == _readLength) { FlushWriteBuffer(); - Debug.Assert(_bufferLength > 0, "_bufferSize > 0"); - - _readLength = ReadNative(buffer, 0, _bufferLength); + _readLength = FillReadBufferForReadByte(); _readPos = 0; if (_readLength == 0) { @@ -615,13 +754,18 @@ namespace System.IO return buffer[_readPos++]; } - private void WriteByteCore(byte value) + /// <summary> + /// Writes a byte to the current position in the stream and advances the position + /// within the stream by one byte. + /// </summary> + /// <param name="value">The byte to write to the stream.</param> + public override void WriteByte(byte value) { PrepareForWriting(); // Flush the write buffer if it's full if (_writePos == _bufferLength) - FlushWriteBuffer(); + FlushWriteBufferForWriteByte(); // We now have space in the buffer. Store the byte. GetBuffer()[_writePos++] = value; @@ -672,7 +816,7 @@ namespace System.IO if (!IsAsync) return base.BeginRead(array, offset, numBytes, callback, state); else - return TaskToApm.Begin(ReadAsyncInternal(array, offset, numBytes, CancellationToken.None), callback, state); + return TaskToApm.Begin(ReadAsyncTask(array, offset, numBytes, CancellationToken.None), callback, state); } public override IAsyncResult BeginWrite(byte[] array, int offset, int numBytes, AsyncCallback callback, object state) @@ -692,7 +836,7 @@ namespace System.IO if (!IsAsync) return base.BeginWrite(array, offset, numBytes, callback, state); else - return TaskToApm.Begin(WriteAsyncInternal(array, offset, numBytes, CancellationToken.None), callback, state); + return TaskToApm.Begin(WriteAsyncInternal(new ReadOnlyMemory<byte>(array, offset, numBytes), CancellationToken.None), callback, state); } public override int EndRead(IAsyncResult asyncResult) diff --git a/src/mscorlib/shared/System/IO/FileStreamCompletionSource.Win32.cs b/src/mscorlib/shared/System/IO/FileStreamCompletionSource.Win32.cs index 7dca13335e..e3871bcd6c 100644 --- a/src/mscorlib/shared/System/IO/FileStreamCompletionSource.Win32.cs +++ b/src/mscorlib/shared/System/IO/FileStreamCompletionSource.Win32.cs @@ -2,11 +2,11 @@ // 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.Security; +using System.Buffers; +using System.Diagnostics; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using System.Runtime.InteropServices; -using System.Diagnostics; namespace System.IO { @@ -15,7 +15,7 @@ namespace System.IO // This is an internal object extending TaskCompletionSource with fields // for all of the relevant data necessary to complete the IO operation. // This is used by IOCallback and all of the async methods. - unsafe private sealed class FileStreamCompletionSource : TaskCompletionSource<int> + private unsafe class FileStreamCompletionSource : TaskCompletionSource<int> { private const long NoResult = 0; private const long ResultSuccess = (long)1 << 32; @@ -28,7 +28,6 @@ namespace System.IO private readonly FileStream _stream; private readonly int _numBufferedBytes; - private readonly CancellationToken _cancellationToken; private CancellationTokenRegistration _cancellationRegistration; #if DEBUG private bool _cancellationHasBeenRegistered; @@ -37,20 +36,19 @@ namespace System.IO private long _result; // Using long since this needs to be used in Interlocked APIs // Using RunContinuationsAsynchronously for compat reasons (old API used Task.Factory.StartNew for continuations) - internal FileStreamCompletionSource(FileStream stream, int numBufferedBytes, byte[] bytes, CancellationToken cancellationToken) + internal FileStreamCompletionSource(FileStream stream, int numBufferedBytes, byte[] bytes) : base(TaskCreationOptions.RunContinuationsAsynchronously) { _numBufferedBytes = numBufferedBytes; _stream = stream; _result = NoResult; - _cancellationToken = cancellationToken; - // Create the native overlapped. We try to use the preallocated overlapped if possible: - // it's possible if the byte buffer is the same one that's associated with the preallocated overlapped - // and if no one else is currently using the preallocated overlapped. This is the fast-path for cases - // where the user-provided buffer is smaller than the FileStream's buffer (such that the FileStream's + // Create the native overlapped. We try to use the preallocated overlapped if possible: it's possible if the byte + // buffer is null (there's nothing to pin) or the same one that's associated with the preallocated overlapped (and + // thus is already pinned) and if no one else is currently using the preallocated overlapped. This is the fast-path + // for cases where the user-provided buffer is smaller than the FileStream's buffer (such that the FileStream's // buffer is used) and where operations on the FileStream are not being performed concurrently. - _overlapped = ReferenceEquals(bytes, _stream._buffer) && _stream.CompareExchangeCurrentOverlappedOwner(this, null) == null ? + _overlapped = (bytes == null || ReferenceEquals(bytes, _stream._buffer)) && _stream.CompareExchangeCurrentOverlappedOwner(this, null) == null ? _stream._fileHandle.ThreadPoolBinding.AllocateNativeOverlapped(_stream._preallocatedOverlapped) : _stream._fileHandle.ThreadPoolBinding.AllocateNativeOverlapped(s_ioCallback, this, bytes); Debug.Assert(_overlapped != null, "AllocateNativeOverlapped returned null"); @@ -67,15 +65,16 @@ namespace System.IO TrySetResult(numBytes + _numBufferedBytes); } - public void RegisterForCancellation() + public void RegisterForCancellation(CancellationToken cancellationToken) { #if DEBUG + Debug.Assert(cancellationToken.CanBeCanceled); Debug.Assert(!_cancellationHasBeenRegistered, "Cannot register for cancellation twice"); _cancellationHasBeenRegistered = true; #endif - // Quick check to make sure that the cancellation token supports cancellation, and that the IO hasn't completed - if ((_cancellationToken.CanBeCanceled) && (_overlapped != null)) + // Quick check to make sure the IO hasn't completed + if (_overlapped != null) { var cancelCallback = s_cancelCallback; if (cancelCallback == null) s_cancelCallback = cancelCallback = Cancel; @@ -84,7 +83,7 @@ namespace System.IO long packedResult = Interlocked.CompareExchange(ref _result, RegisteringCancellation, NoResult); if (packedResult == NoResult) { - _cancellationRegistration = _cancellationToken.Register(cancelCallback, this); + _cancellationRegistration = cancellationToken.Register(cancelCallback, this); // Switch the result, just in case IO completed while we were setting the registration packedResult = Interlocked.Exchange(ref _result, NoResult); @@ -104,7 +103,7 @@ namespace System.IO } } - internal void ReleaseNativeResource() + internal virtual void ReleaseNativeResource() { // Ensure that cancellation has been completed and cleaned up. _cancellationRegistration.Dispose(); @@ -172,6 +171,7 @@ namespace System.IO private void CompleteCallback(ulong packedResult) { // Free up the native resource and cancellation registration + CancellationToken cancellationToken = _cancellationRegistration.Token; // access before disposing registration ReleaseNativeResource(); // Unpack the result and send it to the user @@ -181,7 +181,7 @@ namespace System.IO int errorCode = unchecked((int)(packedResult & uint.MaxValue)); if (errorCode == Interop.Errors.ERROR_OPERATION_ABORTED) { - TrySetCanceled(_cancellationToken.IsCancellationRequested ? _cancellationToken : new CancellationToken(true)); + TrySetCanceled(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); } else { @@ -218,5 +218,28 @@ namespace System.IO } } } + + /// <summary> + /// Extends <see cref="FileStreamCompletionSource"/> with to support disposing of a + /// <see cref="MemoryHandle"/> when the operation has completed. This should only be used + /// when memory doesn't wrap a byte[]. + /// </summary> + private sealed class MemoryFileStreamCompletionSource : FileStreamCompletionSource + { + private MemoryHandle _handle; // mutable struct; do not make this readonly + + internal MemoryFileStreamCompletionSource(FileStream stream, int numBufferedBytes, ReadOnlyMemory<byte> memory) : + base(stream, numBufferedBytes, bytes: null) // this type handles the pinning, so null is passed for bytes + { + Debug.Assert(!memory.DangerousTryGetArray(out ArraySegment<byte> array), "The base should be used directly if we can get the array."); + _handle = memory.Retain(pin: true); + } + + internal override void ReleaseNativeResource() + { + _handle.Dispose(); + base.ReleaseNativeResource(); + } + } } } diff --git a/src/mscorlib/shared/System/IO/IOException.cs b/src/mscorlib/shared/System/IO/IOException.cs new file mode 100644 index 0000000000..1fbd352613 --- /dev/null +++ b/src/mscorlib/shared/System/IO/IOException.cs @@ -0,0 +1,41 @@ +// 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.Runtime.Serialization; + +namespace System.IO +{ + public class IOException : SystemException + { + public IOException() + : base(SR.Arg_IOException) + { + HResult = HResults.COR_E_IO; + } + + public IOException(String message) + : base(message) + { + HResult = HResults.COR_E_IO; + } + + public IOException(String message, int hresult) + : base(message) + { + HResult = hresult; + } + + public IOException(String message, Exception innerException) + : base(message, innerException) + { + HResult = HResults.COR_E_IO; + } + + protected IOException(SerializationInfo info, StreamingContext context) : base(info, context) + { + throw new PlatformNotSupportedException(); + } + } +} diff --git a/src/mscorlib/shared/System/IO/Path.cs b/src/mscorlib/shared/System/IO/Path.cs index 135bb42881..2676f0d2fe 100644 --- a/src/mscorlib/shared/System/IO/Path.cs +++ b/src/mscorlib/shared/System/IO/Path.cs @@ -166,6 +166,30 @@ namespace System.IO return new string(pRandomFileName, 0, RandomFileNameLength); } + /// <summary> + /// Returns true if the path is fixed to a specific drive or UNC path. This method does no + /// validation of the path (URIs will be returned as relative as a result). + /// Returns false if the path specified is relative to the current drive or working directory. + /// </summary> + /// <remarks> + /// Handles paths that use the alternate directory separator. It is a frequent mistake to + /// assume that rooted paths <see cref="Path.IsPathRooted(string)"/> are not relative. This isn't the case. + /// "C:a" is drive relative- meaning that it will be resolved against the current directory + /// for C: (rooted, but relative). "C:\a" is rooted and not relative (the current directory + /// will not be used to modify the path). + /// </remarks> + /// <exception cref="ArgumentNullException"> + /// Thrown if <paramref name="path"/> is null. + /// </exception> + public static bool IsPathFullyQualified(string path) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + return !PathInternal.IsPartiallyQualified(path); + } + // Tests if a path includes a file extension. The result is // true if the characters that follow the last directory // separator ('\\' or '/') or volume separator (':') in the path include diff --git a/src/mscorlib/shared/System/IO/PathInternal.Windows.cs b/src/mscorlib/shared/System/IO/PathInternal.Windows.cs index 01069e757c..2ab3928608 100644 --- a/src/mscorlib/shared/System/IO/PathInternal.Windows.cs +++ b/src/mscorlib/shared/System/IO/PathInternal.Windows.cs @@ -437,11 +437,6 @@ namespace System.IO return IsDirectorySeparator(ch) || VolumeSeparatorChar == ch; } - internal static string TrimEndingDirectorySeparator(string path) => - EndsInDirectorySeparator(path) ? - path.Substring(0, path.Length - 1) : - path; - /// <summary> /// Returns true if the path is effectively empty for the current OS. /// For unix, this is empty or null. For Windows, this is empty, null, or diff --git a/src/mscorlib/shared/System/IO/PathTooLongException.cs b/src/mscorlib/shared/System/IO/PathTooLongException.cs index 64c8e6c7e6..15f282ebf6 100644 --- a/src/mscorlib/shared/System/IO/PathTooLongException.cs +++ b/src/mscorlib/shared/System/IO/PathTooLongException.cs @@ -13,19 +13,19 @@ namespace System.IO public PathTooLongException() : base(SR.IO_PathTooLong) { - HResult = __HResults.COR_E_PATHTOOLONG; + HResult = HResults.COR_E_PATHTOOLONG; } public PathTooLongException(string message) : base(message) { - HResult = __HResults.COR_E_PATHTOOLONG; + HResult = HResults.COR_E_PATHTOOLONG; } public PathTooLongException(string message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_PATHTOOLONG; + HResult = HResults.COR_E_PATHTOOLONG; } protected PathTooLongException(SerializationInfo info, StreamingContext context) diff --git a/src/mscorlib/shared/System/IO/PinnedBufferMemoryStream.cs b/src/mscorlib/shared/System/IO/PinnedBufferMemoryStream.cs index c8e720b7ac..e8f74dd05c 100644 --- a/src/mscorlib/shared/System/IO/PinnedBufferMemoryStream.cs +++ b/src/mscorlib/shared/System/IO/PinnedBufferMemoryStream.cs @@ -46,6 +46,10 @@ namespace System.IO Initialize(ptr, len, len, FileAccess.Read); } + public override int Read(Span<byte> destination) => ReadCore(destination); + + public override void Write(ReadOnlySpan<byte> source) => WriteCore(source); + ~PinnedBufferMemoryStream() { Dispose(false); diff --git a/src/mscorlib/shared/System/IO/UnmanagedMemoryStream.cs b/src/mscorlib/shared/System/IO/UnmanagedMemoryStream.cs index b78f50fe7b..b899951ba7 100644 --- a/src/mscorlib/shared/System/IO/UnmanagedMemoryStream.cs +++ b/src/mscorlib/shared/System/IO/UnmanagedMemoryStream.cs @@ -361,8 +361,27 @@ namespace System.IO throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); if (buffer.Length - offset < count) throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); // Keep this in sync with contract validation in ReadAsync + return ReadCore(new Span<byte>(buffer, offset, count)); + } + + public override int Read(Span<byte> destination) + { + if (GetType() == typeof(UnmanagedMemoryStream)) + { + return ReadCore(destination); + } + else + { + // UnmanagedMemoryStream is not sealed, and a derived type may have overridden Read(byte[], int, int) prior + // to this Read(Span<byte>) overload being introduced. In that case, this Read(Span<byte>) overload + // should use the behavior of Read(byte[],int,int) overload. + return base.Read(destination); + } + } + + internal int ReadCore(Span<byte> destination) + { if (!_isOpen) throw Error.GetStreamIsClosed(); if (!CanRead) throw Error.GetReadNotSupported(); @@ -370,20 +389,22 @@ namespace System.IO // changes our position after we decide we can read some bytes. long pos = Interlocked.Read(ref _position); long len = Interlocked.Read(ref _length); - long n = len - pos; - if (n > count) - n = count; + long n = Math.Min(len - pos, destination.Length); if (n <= 0) + { return 0; + } int nInt = (int)n; // Safe because n <= count, which is an Int32 if (nInt < 0) + { return 0; // _position could be beyond EOF + } Debug.Assert(pos + nInt >= 0, "_position + n >= 0"); // len is less than 2^63 -1. unsafe { - fixed (byte* pBuffer = buffer) + fixed (byte* pBuffer = &destination.DangerousGetPinnableReference()) { if (_buffer != null) { @@ -393,7 +414,7 @@ namespace System.IO try { _buffer.AcquirePointer(ref pointer); - Buffer.Memcpy(pBuffer + offset, pointer + pos + _offset, nInt); + Buffer.Memcpy(pBuffer, pointer + pos + _offset, nInt); } finally { @@ -405,7 +426,7 @@ namespace System.IO } else { - Buffer.Memcpy(pBuffer + offset, _mem + pos, nInt); + Buffer.Memcpy(pBuffer, _mem + pos, nInt); } } } @@ -450,6 +471,28 @@ namespace System.IO } /// <summary> + /// Reads bytes from stream and puts them into the buffer + /// </summary> + /// <param name="destination">Buffer to read the bytes to.</param> + /// <param name="cancellationToken">Token that can be used to cancel this operation.</param> + public override ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default(CancellationToken)) + { + if (cancellationToken.IsCancellationRequested) + { + return new ValueTask<int>(Task.FromCanceled<int>(cancellationToken)); + } + + try + { + return new ValueTask<int>(Read(destination.Span)); + } + catch (Exception ex) + { + return new ValueTask<int>(Task.FromException<int>(ex)); + } + } + + /// <summary> /// Returns the byte at the stream current Position and advances the Position. /// </summary> /// <returns></returns> @@ -583,17 +626,38 @@ namespace System.IO throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); if (buffer.Length - offset < count) throw new ArgumentException(SR.Argument_InvalidOffLen); - Contract.EndContractBlock(); // Keep contract validation in sync with WriteAsync(..) + WriteCore(new Span<byte>(buffer, offset, count)); + } + + public override void Write(ReadOnlySpan<byte> source) + { + if (GetType() == typeof(UnmanagedMemoryStream)) + { + WriteCore(source); + } + else + { + // UnmanagedMemoryStream is not sealed, and a derived type may have overridden Write(byte[], int, int) prior + // to this Write(Span<byte>) overload being introduced. In that case, this Write(Span<byte>) overload + // should use the behavior of Write(byte[],int,int) overload. + base.Write(source); + } + } + + internal unsafe void WriteCore(ReadOnlySpan<byte> source) + { if (!_isOpen) throw Error.GetStreamIsClosed(); if (!CanWrite) throw Error.GetWriteNotSupported(); long pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition long len = Interlocked.Read(ref _length); - long n = pos + count; + long n = pos + source.Length; // Check for overflow if (n < 0) + { throw new IOException(SR.IO_StreamTooLong); + } if (n > _capacity) { @@ -606,10 +670,7 @@ namespace System.IO // zero any memory in the middle. if (pos > len) { - unsafe - { - Buffer.ZeroMemory(_mem + len, pos - len); - } + Buffer.ZeroMemory(_mem + len, pos - len); } // set length after zeroing memory to avoid race condition of accessing unzeroed memory @@ -619,39 +680,37 @@ namespace System.IO } } - unsafe + fixed (byte* pBuffer = &source.DangerousGetPinnableReference()) { - fixed (byte* pBuffer = buffer) + if (_buffer != null) { - if (_buffer != null) + long bytesLeft = _capacity - pos; + if (bytesLeft < source.Length) { - long bytesLeft = _capacity - pos; - if (bytesLeft < count) - { - throw new ArgumentException(SR.Arg_BufferTooSmall); - } + throw new ArgumentException(SR.Arg_BufferTooSmall); + } - byte* pointer = null; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - _buffer.AcquirePointer(ref pointer); - Buffer.Memcpy(pointer + pos + _offset, pBuffer + offset, count); - } - finally - { - if (pointer != null) - { - _buffer.ReleasePointer(); - } - } + byte* pointer = null; + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + _buffer.AcquirePointer(ref pointer); + Buffer.Memcpy(pointer + pos + _offset, pBuffer, source.Length); } - else + finally { - Buffer.Memcpy(_mem + pos, pBuffer + offset, count); + if (pointer != null) + { + _buffer.ReleasePointer(); + } } } + else + { + Buffer.Memcpy(_mem + pos, pBuffer, source.Length); + } } + Interlocked.Exchange(ref _position, n); return; } @@ -692,6 +751,29 @@ namespace System.IO } /// <summary> + /// Writes buffer into the stream. The operation completes synchronously. + /// </summary> + /// <param name="buffer">Buffer that will be written.</param> + /// <param name="cancellationToken">Token that can be used to cancel the operation.</param> + public override Task WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken)) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + try + { + Write(source.Span); + return Task.CompletedTask; + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + + /// <summary> /// Writes a byte to the stream and advances the current Position. /// </summary> /// <param name="value"></param> diff --git a/src/mscorlib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs b/src/mscorlib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs index d547e771d7..2699912ea1 100644 --- a/src/mscorlib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs +++ b/src/mscorlib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs @@ -114,6 +114,11 @@ namespace System.IO return _unmanagedStream.Read(buffer, offset, count); } + public override int Read(Span<byte> destination) + { + return _unmanagedStream.Read(destination); + } + public override int ReadByte() { return _unmanagedStream.ReadByte(); @@ -136,6 +141,11 @@ namespace System.IO _unmanagedStream.Write(buffer, offset, count); } + public override void Write(ReadOnlySpan<byte> source) + { + _unmanagedStream.Write(source); + } + public override void WriteByte(byte value) { _unmanagedStream.WriteByte(value); @@ -200,11 +210,21 @@ namespace System.IO return _unmanagedStream.ReadAsync(buffer, offset, count, cancellationToken); } + public override ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default(CancellationToken)) + { + return _unmanagedStream.ReadAsync(destination, cancellationToken); + } + public override Task WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) { return _unmanagedStream.WriteAsync(buffer, offset, count, cancellationToken); } + + public override Task WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default(CancellationToken)) + { + return _unmanagedStream.WriteAsync(source, cancellationToken); + } } // class UnmanagedMemoryStreamWrapper } // namespace diff --git a/src/mscorlib/shared/System/IO/Win32Marshal.cs b/src/mscorlib/shared/System/IO/Win32Marshal.cs index a24409ed03..14a064a700 100644 --- a/src/mscorlib/shared/System/IO/Win32Marshal.cs +++ b/src/mscorlib/shared/System/IO/Win32Marshal.cs @@ -63,10 +63,16 @@ namespace System.IO return new IOException(SR.Format(SR.IO_AlreadyExists_Name, path), MakeHRFromErrorCode(errorCode)); case Interop.Errors.ERROR_FILENAME_EXCED_RANGE: - return new PathTooLongException(SR.Format(SR.IO_PathTooLong_Path, path)); + if (path.Length == 0) + return new PathTooLongException(SR.IO_PathTooLong); + else + return new PathTooLongException(SR.Format(SR.IO_PathTooLong_Path, path)); + + case Interop.Errors.ERROR_INVALID_DRIVE: + throw new DriveNotFoundException(SR.Format(SR.IO_DriveNotFound_Drive, path)); case Interop.Errors.ERROR_INVALID_PARAMETER: - return new IOException(GetMessage(errorCode), MakeHRFromErrorCode(errorCode)); + return new IOException(Interop.Kernel32.GetMessage(errorCode), MakeHRFromErrorCode(errorCode)); case Interop.Errors.ERROR_SHARING_VIOLATION: if (path.Length == 0) @@ -84,7 +90,7 @@ namespace System.IO return new OperationCanceledException(); default: - return new IOException(GetMessage(errorCode), MakeHRFromErrorCode(errorCode)); + return new IOException(Interop.Kernel32.GetMessage(errorCode), MakeHRFromErrorCode(errorCode)); } } @@ -97,13 +103,5 @@ namespace System.IO return unchecked(((int)0x80070000) | errorCode); } - - /// <summary> - /// Returns a string message for the specified Win32 error code. - /// </summary> - internal static string GetMessage(int errorCode) - { - return Interop.Kernel32.GetMessage(errorCode); - } } } diff --git a/src/mscorlib/shared/System/IndexOutOfRangeException.cs b/src/mscorlib/shared/System/IndexOutOfRangeException.cs index d5b24b35d5..bec63d73d6 100644 --- a/src/mscorlib/shared/System/IndexOutOfRangeException.cs +++ b/src/mscorlib/shared/System/IndexOutOfRangeException.cs @@ -20,19 +20,19 @@ namespace System public IndexOutOfRangeException() : base(SR.Arg_IndexOutOfRangeException) { - HResult = __HResults.COR_E_INDEXOUTOFRANGE; + HResult = HResults.COR_E_INDEXOUTOFRANGE; } public IndexOutOfRangeException(String message) : base(message) { - HResult = __HResults.COR_E_INDEXOUTOFRANGE; + HResult = HResults.COR_E_INDEXOUTOFRANGE; } public IndexOutOfRangeException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_INDEXOUTOFRANGE; + HResult = HResults.COR_E_INDEXOUTOFRANGE; } } } diff --git a/src/mscorlib/shared/System/InsufficientExecutionStackException.cs b/src/mscorlib/shared/System/InsufficientExecutionStackException.cs index 41df3ae970..a14cb67150 100644 --- a/src/mscorlib/shared/System/InsufficientExecutionStackException.cs +++ b/src/mscorlib/shared/System/InsufficientExecutionStackException.cs @@ -11,19 +11,19 @@ namespace System public InsufficientExecutionStackException() : base(SR.Arg_InsufficientExecutionStackException) { - HResult = __HResults.COR_E_INSUFFICIENTEXECUTIONSTACK; + HResult = HResults.COR_E_INSUFFICIENTEXECUTIONSTACK; } public InsufficientExecutionStackException(String message) : base(message) { - HResult = __HResults.COR_E_INSUFFICIENTEXECUTIONSTACK; + HResult = HResults.COR_E_INSUFFICIENTEXECUTIONSTACK; } public InsufficientExecutionStackException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_INSUFFICIENTEXECUTIONSTACK; + HResult = HResults.COR_E_INSUFFICIENTEXECUTIONSTACK; } } } diff --git a/src/mscorlib/shared/System/Int16.cs b/src/mscorlib/shared/System/Int16.cs new file mode 100644 index 0000000000..6aaf4280d3 --- /dev/null +++ b/src/mscorlib/shared/System/Int16.cs @@ -0,0 +1,320 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: This class will encapsulate a short and provide an +** Object representation of it. +** +** +===========================================================*/ + +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Int16 : IComparable, IConvertible, IFormattable, IComparable<Int16>, IEquatable<Int16> + { + private short m_value; // Do not rename (binary serialization) + + public const short MaxValue = (short)0x7FFF; + public const short MinValue = unchecked((short)0x8000); + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns a value less than zero if this object + // null is considered to be less than any instance. + // If object is not of type Int16, this method throws an ArgumentException. + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + + if (value is Int16) + { + return m_value - ((Int16)value).m_value; + } + + throw new ArgumentException(SR.Arg_MustBeInt16); + } + + public int CompareTo(Int16 value) + { + return m_value - value; + } + + public override bool Equals(Object obj) + { + if (!(obj is Int16)) + { + return false; + } + return m_value == ((Int16)obj).m_value; + } + + [NonVersionable] + public bool Equals(Int16 obj) + { + return m_value == obj; + } + + // Returns a HashCode for the Int16 + public override int GetHashCode() + { + return ((int)((ushort)m_value) | (((int)m_value) << 16)); + } + + + public override String ToString() + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatInt32(m_value, null, NumberFormatInfo.CurrentInfo); + } + + public String ToString(IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatInt32(m_value, null, NumberFormatInfo.GetInstance(provider)); + } + + public String ToString(String format) + { + Contract.Ensures(Contract.Result<String>() != null); + return ToString(format, NumberFormatInfo.CurrentInfo); + } + + public String ToString(String format, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return ToString(format, NumberFormatInfo.GetInstance(provider)); + } + + private String ToString(String format, NumberFormatInfo info) + { + Contract.Ensures(Contract.Result<String>() != null); + + if (m_value < 0 && format != null && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) + { + uint temp = (uint)(m_value & 0x0000FFFF); + return Number.FormatUInt32(temp, format, info); + } + return Number.FormatInt32(m_value, format, info); + } + + public static short Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + } + + public static short Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo); + } + + public static short Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + } + + public static short Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider)); + } + + public static short Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Parse(s, style, NumberFormatInfo.GetInstance(provider)); + } + + private static short Parse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info) + { + int i = 0; + try + { + i = Number.ParseInt32(s, style, info); + } + catch (OverflowException e) + { + throw new OverflowException(SR.Overflow_Int16, e); + } + + // We need this check here since we don't allow signs to specified in hex numbers. So we fixup the result + // for negative numbers + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { // We are parsing a hexadecimal number + if ((i < 0) || (i > UInt16.MaxValue)) + { + throw new OverflowException(SR.Overflow_Int16); + } + return (short)i; + } + + if (i < MinValue || i > MaxValue) throw new OverflowException(SR.Overflow_Int16); + return (short)i; + } + + public static bool TryParse(String s, out Int16 result) + { + if (s == null) + { + result = 0; + return false; + } + + return TryParse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out Int16 result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return TryParse(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result); + } + + public static bool TryParse(ReadOnlySpan<char> s, out Int16 result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + private static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out Int16 result) + { + result = 0; + int i; + if (!Number.TryParseInt32(s, style, info, out i)) + { + return false; + } + + // We need this check here since we don't allow signs to specified in hex numbers. So we fixup the result + // for negative numbers + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { // We are parsing a hexadecimal number + if ((i < 0) || i > UInt16.MaxValue) + { + return false; + } + result = (Int16)i; + return true; + } + + if (i < MinValue || i > MaxValue) + { + return false; + } + result = (Int16)i; + return true; + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.Int16; + } + + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return Convert.ToChar(m_value); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return m_value; + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Int16", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/src/mscorlib/shared/System/Int32.cs b/src/mscorlib/shared/System/Int32.cs new file mode 100644 index 0000000000..f5e832a274 --- /dev/null +++ b/src/mscorlib/shared/System/Int32.cs @@ -0,0 +1,285 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: A representation of a 32 bit 2's complement +** integer. +** +** +===========================================================*/ + +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Int32 : IComparable, IConvertible, IFormattable, IComparable<Int32>, IEquatable<Int32> + { + private int m_value; // Do not rename (binary serialization) + + public const int MaxValue = 0x7fffffff; + public const int MinValue = unchecked((int)0x80000000); + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns : + // 0 if the values are equal + // Negative number if _value is less than value + // Positive number if _value is more than value + // null is considered to be less than any instance, hence returns positive number + // If object is not of type Int32, this method throws an ArgumentException. + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + if (value is Int32) + { + // NOTE: Cannot use return (_value - value) as this causes a wrap + // around in cases where _value - value > MaxValue. + int i = (int)value; + if (m_value < i) return -1; + if (m_value > i) return 1; + return 0; + } + throw new ArgumentException(SR.Arg_MustBeInt32); + } + + public int CompareTo(int value) + { + // NOTE: Cannot use return (_value - value) as this causes a wrap + // around in cases where _value - value > MaxValue. + if (m_value < value) return -1; + if (m_value > value) return 1; + return 0; + } + + public override bool Equals(Object obj) + { + if (!(obj is Int32)) + { + return false; + } + return m_value == ((Int32)obj).m_value; + } + + [NonVersionable] + public bool Equals(Int32 obj) + { + return m_value == obj; + } + + // The absolute value of the int contained. + public override int GetHashCode() + { + return m_value; + } + + [Pure] + public override String ToString() + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatInt32(m_value, null, NumberFormatInfo.CurrentInfo); + } + + [Pure] + public String ToString(String format) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatInt32(m_value, format, NumberFormatInfo.CurrentInfo); + } + + [Pure] + public String ToString(IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatInt32(m_value, null, NumberFormatInfo.GetInstance(provider)); + } + + [Pure] + public String ToString(String format, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatInt32(m_value, format, NumberFormatInfo.GetInstance(provider)); + } + + [Pure] + public static int Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt32(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + } + + [Pure] + public static int Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt32(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo); + } + + // Parses an integer from a String in the given style. If + // a NumberFormatInfo isn't specified, the current culture's + // NumberFormatInfo is assumed. + // + [Pure] + public static int Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt32(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + } + + // Parses an integer from a String in the given style. If + // a NumberFormatInfo isn't specified, the current culture's + // NumberFormatInfo is assumed. + // + [Pure] + public static int Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt32(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider)); + } + + public static int Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.ParseInt32(s, style, NumberFormatInfo.GetInstance(provider)); + } + + // Parses an integer from a String. Returns false rather + // than throwing exceptin if input is invalid + // + [Pure] + public static bool TryParse(String s, out Int32 result) + { + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseInt32(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + // Parses an integer from a String in the given style. Returns false rather + // than throwing exceptin if input is invalid + // + [Pure] + public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out Int32 result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseInt32(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result); + } + + public static bool TryParse(ReadOnlySpan<char> s, out int result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseInt32(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + // + // IConvertible implementation + // + + [Pure] + public TypeCode GetTypeCode() + { + return TypeCode.Int32; + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return Convert.ToChar(m_value); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return m_value; + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Int32", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/src/mscorlib/shared/System/Int64.cs b/src/mscorlib/shared/System/Int64.cs new file mode 100644 index 0000000000..785fa39fa5 --- /dev/null +++ b/src/mscorlib/shared/System/Int64.cs @@ -0,0 +1,262 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: This class will encapsulate a long and provide an +** Object representation of it. +** +** +===========================================================*/ + +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Int64 : IComparable, IConvertible, IFormattable, IComparable<Int64>, IEquatable<Int64> + { + private long m_value; // Do not rename (binary serialization) + + public const long MaxValue = 0x7fffffffffffffffL; + public const long MinValue = unchecked((long)0x8000000000000000L); + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns a value less than zero if this object + // null is considered to be less than any instance. + // If object is not of type Int64, this method throws an ArgumentException. + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + if (value is Int64) + { + // Need to use compare because subtraction will wrap + // to positive for very large neg numbers, etc. + long i = (long)value; + if (m_value < i) return -1; + if (m_value > i) return 1; + return 0; + } + throw new ArgumentException(SR.Arg_MustBeInt64); + } + + public int CompareTo(Int64 value) + { + // Need to use compare because subtraction will wrap + // to positive for very large neg numbers, etc. + if (m_value < value) return -1; + if (m_value > value) return 1; + return 0; + } + + public override bool Equals(Object obj) + { + if (!(obj is Int64)) + { + return false; + } + return m_value == ((Int64)obj).m_value; + } + + [NonVersionable] + public bool Equals(Int64 obj) + { + return m_value == obj; + } + + // The value of the lower 32 bits XORed with the uppper 32 bits. + public override int GetHashCode() + { + return (unchecked((int)((long)m_value)) ^ (int)(m_value >> 32)); + } + + public override String ToString() + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatInt64(m_value, null, NumberFormatInfo.CurrentInfo); + } + + public String ToString(IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatInt64(m_value, null, NumberFormatInfo.GetInstance(provider)); + } + + public String ToString(String format) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatInt64(m_value, format, NumberFormatInfo.CurrentInfo); + } + + public String ToString(String format, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatInt64(m_value, format, NumberFormatInfo.GetInstance(provider)); + } + + public static long Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt64(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + } + + public static long Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt64(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo); + } + + public static long Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt64(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + } + + + // Parses a long from a String in the given style. If + // a NumberFormatInfo isn't specified, the current culture's + // NumberFormatInfo is assumed. + // + public static long Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseInt64(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider)); + } + + public static long Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.ParseInt64(s, style, NumberFormatInfo.GetInstance(provider)); + } + + public static Boolean TryParse(String s, out Int64 result) + { + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseInt64(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + public static Boolean TryParse(String s, NumberStyles style, IFormatProvider provider, out Int64 result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseInt64(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result); + } + + public static bool TryParse(ReadOnlySpan<char> s, out long result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseInt64(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.Int64; + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return Convert.ToChar(m_value); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return m_value; + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Int64", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/src/mscorlib/shared/System/InvalidCastException.cs b/src/mscorlib/shared/System/InvalidCastException.cs index cf359ac0b5..00b393c60e 100644 --- a/src/mscorlib/shared/System/InvalidCastException.cs +++ b/src/mscorlib/shared/System/InvalidCastException.cs @@ -17,19 +17,19 @@ namespace System public InvalidCastException() : base(SR.Arg_InvalidCastException) { - HResult = __HResults.COR_E_INVALIDCAST; + HResult = HResults.COR_E_INVALIDCAST; } public InvalidCastException(String message) : base(message) { - HResult = __HResults.COR_E_INVALIDCAST; + HResult = HResults.COR_E_INVALIDCAST; } public InvalidCastException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_INVALIDCAST; + HResult = HResults.COR_E_INVALIDCAST; } public InvalidCastException(String message, int errorCode) diff --git a/src/mscorlib/shared/System/InvalidOperationException.cs b/src/mscorlib/shared/System/InvalidOperationException.cs index ad743e05ff..74a4ffd74a 100644 --- a/src/mscorlib/shared/System/InvalidOperationException.cs +++ b/src/mscorlib/shared/System/InvalidOperationException.cs @@ -21,19 +21,19 @@ namespace System public InvalidOperationException() : base(SR.Arg_InvalidOperationException) { - HResult = __HResults.COR_E_INVALIDOPERATION; + HResult = HResults.COR_E_INVALIDOPERATION; } public InvalidOperationException(String message) : base(message) { - HResult = __HResults.COR_E_INVALIDOPERATION; + HResult = HResults.COR_E_INVALIDOPERATION; } public InvalidOperationException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_INVALIDOPERATION; + HResult = HResults.COR_E_INVALIDOPERATION; } protected InvalidOperationException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/InvalidProgramException.cs b/src/mscorlib/shared/System/InvalidProgramException.cs index 47e7325836..e3521574ea 100644 --- a/src/mscorlib/shared/System/InvalidProgramException.cs +++ b/src/mscorlib/shared/System/InvalidProgramException.cs @@ -20,19 +20,19 @@ namespace System public InvalidProgramException() : base(SR.InvalidProgram_Default) { - HResult = __HResults.COR_E_INVALIDPROGRAM; + HResult = HResults.COR_E_INVALIDPROGRAM; } public InvalidProgramException(String message) : base(message) { - HResult = __HResults.COR_E_INVALIDPROGRAM; + HResult = HResults.COR_E_INVALIDPROGRAM; } public InvalidProgramException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_INVALIDPROGRAM; + HResult = HResults.COR_E_INVALIDPROGRAM; } } } diff --git a/src/mscorlib/shared/System/Lazy.cs b/src/mscorlib/shared/System/Lazy.cs index 5d68714c7e..2ef3cd2e5f 100644 --- a/src/mscorlib/shared/System/Lazy.cs +++ b/src/mscorlib/shared/System/Lazy.cs @@ -261,7 +261,7 @@ namespace System /// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/> /// class that uses <typeparamref name="T"/>'s default constructor and a specified thread-safety mode. /// </summary> - /// <param name="mode">The lazy thread-safety mode mode</param> + /// <param name="mode">The lazy thread-safety mode</param> /// <exception cref="System.ArgumentOutOfRangeException"><paramref name="mode"/> mode contains an invalid valuee</exception> public Lazy(LazyThreadSafetyMode mode) : this(null, mode, useDefaultConstructor:true) diff --git a/src/mscorlib/shared/System/MemberAccessException.cs b/src/mscorlib/shared/System/MemberAccessException.cs index abca952f19..bb26d9e3c4 100644 --- a/src/mscorlib/shared/System/MemberAccessException.cs +++ b/src/mscorlib/shared/System/MemberAccessException.cs @@ -23,7 +23,7 @@ namespace System public MemberAccessException() : base(SR.Arg_AccessException) { - HResult = __HResults.COR_E_MEMBERACCESS; + HResult = HResults.COR_E_MEMBERACCESS; } // Creates a new MemberAccessException with its message string set to @@ -33,13 +33,13 @@ namespace System public MemberAccessException(String message) : base(message) { - HResult = __HResults.COR_E_MEMBERACCESS; + HResult = HResults.COR_E_MEMBERACCESS; } public MemberAccessException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_MEMBERACCESS; + HResult = HResults.COR_E_MEMBERACCESS; } protected MemberAccessException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/Memory.cs b/src/mscorlib/shared/System/Memory.cs new file mode 100644 index 0000000000..270f88e4bc --- /dev/null +++ b/src/mscorlib/shared/System/Memory.cs @@ -0,0 +1,284 @@ +// 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.Buffers; +using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute; +using EditorBrowsableState = System.ComponentModel.EditorBrowsableState; +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System +{ + public struct Memory<T> + { + // The highest order bit of _index is used to discern whether _arrayOrOwnedMemory is an array or an owned memory + // if (_index >> 31) == 1, object _arrayOrOwnedMemory is an OwnedMemory<T> + // else, object _arrayOrOwnedMemory is a T[] + private readonly object _arrayOrOwnedMemory; + private readonly int _index; + private readonly int _length; + + private const int RemoveOwnedFlagBitMask = 0x7FFFFFFF; + + /// <summary> + /// Creates a new memory 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 Memory(T[] array) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + if (default(T) == null && array.GetType() != typeof(T[])) + ThrowHelper.ThrowArrayTypeMismatchException(); + + _arrayOrOwnedMemory = array; + _index = 0; + _length = array.Length; + } + + /// <summary> + /// Creates a new memory 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 memory.</param> + /// <param name="length">The number of items in the memory.</param> + /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null + /// reference (Nothing in Visual Basic).</exception> + /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> or end index is not in the range (<0 or >=Length). + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Memory(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(); + + _arrayOrOwnedMemory = array; + _index = start; + _length = length; + } + + // Constructor for internal use only. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Memory(OwnedMemory<T> owner, int index, int length) + { + if (owner == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.ownedMemory); + if (index < 0 || length < 0) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + _arrayOrOwnedMemory = owner; + _index = index | (1 << 31); // Before using _index, check if _index < 0, then 'and' it with RemoveOwnedFlagBitMask + _length = length; + } + + /// <summary> + /// Defines an implicit conversion of an array to a <see cref="Memory{T}"/> + /// </summary> + public static implicit operator Memory<T>(T[] array) => new Memory<T>(array); + + /// <summary> + /// Defines an implicit conversion of a <see cref="ArraySegment{T}"/> to a <see cref="Memory{T}"/> + /// </summary> + public static implicit operator Memory<T>(ArraySegment<T> arraySegment) => new Memory<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count); + + /// <summary> + /// Defines an implicit conversion of a <see cref="Memory{T}"/> to a <see cref="ReadOnlyMemory{T}"/> + /// </summary> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlyMemory<T>(Memory<T> memory) + { + if (memory._index < 0) + return new ReadOnlyMemory<T>((OwnedMemory<T>)memory._arrayOrOwnedMemory, memory._index & RemoveOwnedFlagBitMask, memory._length); + return new ReadOnlyMemory<T>((T[])memory._arrayOrOwnedMemory, memory._index, memory._length); + } + + /// <summary> + /// Returns an empty <see cref="Memory{T}"/> + /// </summary> + public static Memory<T> Empty { get; } = Array.Empty<T>(); + + /// <summary> + /// The number of items in the memory. + /// </summary> + public int Length => _length; + + /// <summary> + /// Returns true if Length is 0. + /// </summary> + public bool IsEmpty => _length == 0; + + /// <summary> + /// Forms a slice out of the given memory, beginning at 'start'. + /// </summary> + /// <param name="start">The index at which to begin this slice.</param> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> index is not in range (<0 or >=Length). + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Memory<T> Slice(int start) + { + if ((uint)start > (uint)_length) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + if (_index < 0) + return new Memory<T>((OwnedMemory<T>)_arrayOrOwnedMemory, (_index & RemoveOwnedFlagBitMask) + start, _length - start); + return new Memory<T>((T[])_arrayOrOwnedMemory, _index + start, _length - start); + } + + /// <summary> + /// Forms a slice out of the given memory, beginning at 'start', of given length + /// </summary> + /// <param name="start">The index at which to begin this slice.</param> + /// <param name="length">The desired length for the slice (exclusive).</param> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> or end index is not in range (<0 or >=Length). + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Memory<T> Slice(int start, int length) + { + if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + if (_index < 0) + return new Memory<T>((OwnedMemory<T>)_arrayOrOwnedMemory, (_index & RemoveOwnedFlagBitMask) + start, length); + return new Memory<T>((T[])_arrayOrOwnedMemory, _index + start, length); + } + + /// <summary> + /// Returns a span from the memory. + /// </summary> + public Span<T> Span + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + if (_index < 0) + return ((OwnedMemory<T>)_arrayOrOwnedMemory).AsSpan().Slice(_index & RemoveOwnedFlagBitMask, _length); + return new Span<T>((T[])_arrayOrOwnedMemory, _index, _length); + } + } + + public unsafe MemoryHandle Retain(bool pin = false) + { + MemoryHandle memoryHandle; + if (pin) + { + if (_index < 0) + { + memoryHandle = ((OwnedMemory<T>)_arrayOrOwnedMemory).Pin(); + } + else + { + var array = (T[])_arrayOrOwnedMemory; + var handle = GCHandle.Alloc(array, GCHandleType.Pinned); + void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index); + memoryHandle = new MemoryHandle(null, pointer, handle); + } + } + else + { + if (_index < 0) + { + ((OwnedMemory<T>)_arrayOrOwnedMemory).Retain(); + memoryHandle = new MemoryHandle((OwnedMemory<T>)_arrayOrOwnedMemory); + } + else + { + memoryHandle = new MemoryHandle(null); + } + } + return memoryHandle; + } + + /// <summary> + /// Get an array segment from the underlying memory. + /// If unable to get the array segment, return false with a default array segment. + /// </summary> + public bool TryGetArray(out ArraySegment<T> arraySegment) + { + if (_index < 0) + { + if (((OwnedMemory<T>)_arrayOrOwnedMemory).TryGetArray(out var segment)) + { + arraySegment = new ArraySegment<T>(segment.Array, segment.Offset + (_index & RemoveOwnedFlagBitMask), _length); + return true; + } + } + else + { + arraySegment = new ArraySegment<T>((T[])_arrayOrOwnedMemory, _index, _length); + return true; + } + + arraySegment = default(ArraySegment<T>); + return false; + } + + /// <summary> + /// Copies the contents from the memory into a new array. This heap + /// allocates, so should generally be avoided, however it is sometimes + /// necessary to bridge the gap with APIs written in terms of arrays. + /// </summary> + public T[] ToArray() => Span.ToArray(); + + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) + { + if (obj is ReadOnlyMemory<T>) + { + return ((ReadOnlyMemory<T>)obj).Equals(this); + } + else if (obj is Memory<T> memory) + { + return Equals(memory); + } + else + { + return false; + } + } + + /// <summary> + /// Returns true if the memory points to the same array and has the same length. Note that + /// this does *not* check to see if the *contents* are equal. + /// </summary> + public bool Equals(Memory<T> other) + { + return + _arrayOrOwnedMemory == other._arrayOrOwnedMemory && + _index == other._index && + _length == other._length; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() + { + return CombineHashCodes(_arrayOrOwnedMemory.GetHashCode(), (_index & RemoveOwnedFlagBitMask).GetHashCode(), _length.GetHashCode()); + } + + private static int CombineHashCodes(int left, int right) + { + return ((left << 5) + left) ^ right; + } + + private static int CombineHashCodes(int h1, int h2, int h3) + { + return CombineHashCodes(CombineHashCodes(h1, h2), h3); + } + + } +}
\ No newline at end of file diff --git a/src/mscorlib/shared/System/MethodAccessException.cs b/src/mscorlib/shared/System/MethodAccessException.cs index 2c9c998c15..12691386c5 100644 --- a/src/mscorlib/shared/System/MethodAccessException.cs +++ b/src/mscorlib/shared/System/MethodAccessException.cs @@ -18,19 +18,19 @@ namespace System public MethodAccessException() : base(SR.Arg_MethodAccessException) { - HResult = __HResults.COR_E_METHODACCESS; + HResult = HResults.COR_E_METHODACCESS; } public MethodAccessException(String message) : base(message) { - HResult = __HResults.COR_E_METHODACCESS; + HResult = HResults.COR_E_METHODACCESS; } public MethodAccessException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_METHODACCESS; + HResult = HResults.COR_E_METHODACCESS; } protected MethodAccessException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/MissingMethodException.cs b/src/mscorlib/shared/System/MissingMethodException.cs index 967f434302..4f5e8b6562 100644 --- a/src/mscorlib/shared/System/MissingMethodException.cs +++ b/src/mscorlib/shared/System/MissingMethodException.cs @@ -20,19 +20,19 @@ namespace System public MissingMethodException() : base(SR.Arg_MissingMethodException) { - HResult = __HResults.COR_E_MISSINGMETHOD; + HResult = HResults.COR_E_MISSINGMETHOD; } public MissingMethodException(string message) : base(message) { - HResult = __HResults.COR_E_MISSINGMETHOD; + HResult = HResults.COR_E_MISSINGMETHOD; } public MissingMethodException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_MISSINGMETHOD; + HResult = HResults.COR_E_MISSINGMETHOD; } public MissingMethodException(string className, string methodName) diff --git a/src/mscorlib/shared/System/MulticastNotSupportedException.cs b/src/mscorlib/shared/System/MulticastNotSupportedException.cs index 493671e227..56a3ec9c00 100644 --- a/src/mscorlib/shared/System/MulticastNotSupportedException.cs +++ b/src/mscorlib/shared/System/MulticastNotSupportedException.cs @@ -16,19 +16,19 @@ namespace System public MulticastNotSupportedException() : base(SR.Arg_MulticastNotSupportedException) { - HResult = __HResults.COR_E_MULTICASTNOTSUPPORTED; + HResult = HResults.COR_E_MULTICASTNOTSUPPORTED; } public MulticastNotSupportedException(String message) : base(message) { - HResult = __HResults.COR_E_MULTICASTNOTSUPPORTED; + HResult = HResults.COR_E_MULTICASTNOTSUPPORTED; } public MulticastNotSupportedException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_MULTICASTNOTSUPPORTED; + HResult = HResults.COR_E_MULTICASTNOTSUPPORTED; } } } diff --git a/src/mscorlib/shared/System/NonSerializedAttribute.cs b/src/mscorlib/shared/System/NonSerializedAttribute.cs new file mode 100644 index 0000000000..cabd5a2aa2 --- /dev/null +++ b/src/mscorlib/shared/System/NonSerializedAttribute.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System +{ + [AttributeUsage(AttributeTargets.Field, Inherited = false)] + public sealed class NonSerializedAttribute : Attribute + { + public NonSerializedAttribute() + { + } + } +} diff --git a/src/mscorlib/shared/System/NotFiniteNumberException.cs b/src/mscorlib/shared/System/NotFiniteNumberException.cs index 99882b9eb2..ca47a21e5c 100644 --- a/src/mscorlib/shared/System/NotFiniteNumberException.cs +++ b/src/mscorlib/shared/System/NotFiniteNumberException.cs @@ -14,41 +14,41 @@ namespace System : base(SR.Arg_NotFiniteNumberException) { _offendingNumber = 0; - HResult = __HResults.COR_E_NOTFINITENUMBER; + HResult = HResults.COR_E_NOTFINITENUMBER; } public NotFiniteNumberException(double offendingNumber) : base() { _offendingNumber = offendingNumber; - HResult = __HResults.COR_E_NOTFINITENUMBER; + HResult = HResults.COR_E_NOTFINITENUMBER; } public NotFiniteNumberException(String message) : base(message) { _offendingNumber = 0; - HResult = __HResults.COR_E_NOTFINITENUMBER; + HResult = HResults.COR_E_NOTFINITENUMBER; } public NotFiniteNumberException(String message, double offendingNumber) : base(message) { _offendingNumber = offendingNumber; - HResult = __HResults.COR_E_NOTFINITENUMBER; + HResult = HResults.COR_E_NOTFINITENUMBER; } public NotFiniteNumberException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_NOTFINITENUMBER; + HResult = HResults.COR_E_NOTFINITENUMBER; } public NotFiniteNumberException(String message, double offendingNumber, Exception innerException) : base(message, innerException) { _offendingNumber = offendingNumber; - HResult = __HResults.COR_E_NOTFINITENUMBER; + HResult = HResults.COR_E_NOTFINITENUMBER; } protected NotFiniteNumberException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/NotImplementedException.cs b/src/mscorlib/shared/System/NotImplementedException.cs index ae62527fac..98976c19db 100644 --- a/src/mscorlib/shared/System/NotImplementedException.cs +++ b/src/mscorlib/shared/System/NotImplementedException.cs @@ -21,17 +21,17 @@ namespace System public NotImplementedException() : base(SR.Arg_NotImplementedException) { - HResult = __HResults.E_NOTIMPL; + HResult = HResults.E_NOTIMPL; } public NotImplementedException(String message) : base(message) { - HResult = __HResults.E_NOTIMPL; + HResult = HResults.E_NOTIMPL; } public NotImplementedException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.E_NOTIMPL; + HResult = HResults.E_NOTIMPL; } protected NotImplementedException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/NotSupportedException.cs b/src/mscorlib/shared/System/NotSupportedException.cs index 8532e5ad2f..dc43261fbd 100644 --- a/src/mscorlib/shared/System/NotSupportedException.cs +++ b/src/mscorlib/shared/System/NotSupportedException.cs @@ -20,19 +20,19 @@ namespace System public NotSupportedException() : base(SR.Arg_NotSupportedException) { - HResult = __HResults.COR_E_NOTSUPPORTED; + HResult = HResults.COR_E_NOTSUPPORTED; } public NotSupportedException(String message) : base(message) { - HResult = __HResults.COR_E_NOTSUPPORTED; + HResult = HResults.COR_E_NOTSUPPORTED; } public NotSupportedException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_NOTSUPPORTED; + HResult = HResults.COR_E_NOTSUPPORTED; } protected NotSupportedException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/NullReferenceException.cs b/src/mscorlib/shared/System/NullReferenceException.cs index f689345654..eb6d709dbf 100644 --- a/src/mscorlib/shared/System/NullReferenceException.cs +++ b/src/mscorlib/shared/System/NullReferenceException.cs @@ -20,19 +20,19 @@ namespace System public NullReferenceException() : base(SR.Arg_NullReferenceException) { - HResult = __HResults.COR_E_NULLREFERENCE; + HResult = HResults.COR_E_NULLREFERENCE; } public NullReferenceException(String message) : base(message) { - HResult = __HResults.COR_E_NULLREFERENCE; + HResult = HResults.COR_E_NULLREFERENCE; } public NullReferenceException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_NULLREFERENCE; + HResult = HResults.COR_E_NULLREFERENCE; } protected NullReferenceException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/ObjectDisposedException.cs b/src/mscorlib/shared/System/ObjectDisposedException.cs index 6e8e6b2d74..3d7ba5df15 100644 --- a/src/mscorlib/shared/System/ObjectDisposedException.cs +++ b/src/mscorlib/shared/System/ObjectDisposedException.cs @@ -28,14 +28,14 @@ namespace System public ObjectDisposedException(String objectName, String message) : base(message) { - HResult = __HResults.COR_E_OBJECTDISPOSED; + HResult = HResults.COR_E_OBJECTDISPOSED; _objectName = objectName; } public ObjectDisposedException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_OBJECTDISPOSED; + HResult = HResults.COR_E_OBJECTDISPOSED; } protected ObjectDisposedException(SerializationInfo info, StreamingContext context) diff --git a/src/mscorlib/shared/System/OperationCanceledException.cs b/src/mscorlib/shared/System/OperationCanceledException.cs index 2c7654854f..44a0427c58 100644 --- a/src/mscorlib/shared/System/OperationCanceledException.cs +++ b/src/mscorlib/shared/System/OperationCanceledException.cs @@ -31,19 +31,19 @@ namespace System public OperationCanceledException() : base(SR.OperationCanceled) { - HResult = __HResults.COR_E_OPERATIONCANCELED; + HResult = HResults.COR_E_OPERATIONCANCELED; } public OperationCanceledException(String message) : base(message) { - HResult = __HResults.COR_E_OPERATIONCANCELED; + HResult = HResults.COR_E_OPERATIONCANCELED; } public OperationCanceledException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_OPERATIONCANCELED; + HResult = HResults.COR_E_OPERATIONCANCELED; } diff --git a/src/mscorlib/shared/System/OverflowException.cs b/src/mscorlib/shared/System/OverflowException.cs index 4052e41a18..bf6676ca2c 100644 --- a/src/mscorlib/shared/System/OverflowException.cs +++ b/src/mscorlib/shared/System/OverflowException.cs @@ -20,19 +20,19 @@ namespace System public OverflowException() : base(SR.Arg_OverflowException) { - HResult = __HResults.COR_E_OVERFLOW; + HResult = HResults.COR_E_OVERFLOW; } public OverflowException(String message) : base(message) { - HResult = __HResults.COR_E_OVERFLOW; + HResult = HResults.COR_E_OVERFLOW; } public OverflowException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_OVERFLOW; + HResult = HResults.COR_E_OVERFLOW; } protected OverflowException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/PlatformNotSupportedException.cs b/src/mscorlib/shared/System/PlatformNotSupportedException.cs index f679ac9454..4403c1da06 100644 --- a/src/mscorlib/shared/System/PlatformNotSupportedException.cs +++ b/src/mscorlib/shared/System/PlatformNotSupportedException.cs @@ -20,19 +20,19 @@ namespace System public PlatformNotSupportedException() : base(SR.Arg_PlatformNotSupported) { - HResult = __HResults.COR_E_PLATFORMNOTSUPPORTED; + HResult = HResults.COR_E_PLATFORMNOTSUPPORTED; } public PlatformNotSupportedException(String message) : base(message) { - HResult = __HResults.COR_E_PLATFORMNOTSUPPORTED; + HResult = HResults.COR_E_PLATFORMNOTSUPPORTED; } public PlatformNotSupportedException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_PLATFORMNOTSUPPORTED; + HResult = HResults.COR_E_PLATFORMNOTSUPPORTED; } protected PlatformNotSupportedException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/Random.cs b/src/mscorlib/shared/System/Random.cs index 4affed8a1a..20f035e2e8 100644 --- a/src/mscorlib/shared/System/Random.cs +++ b/src/mscorlib/shared/System/Random.cs @@ -2,20 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -/*============================================================ -** -** -** Purpose: A random number generator. -** -** -===========================================================*/ - -using System; -using System.Runtime; -using System.Runtime.CompilerServices; -using System.Globalization; -using System.Diagnostics.Contracts; - namespace System { public class Random @@ -23,11 +9,10 @@ namespace System // // Private Constants // - private const int MBIG = Int32.MaxValue; + private const int MBIG = int.MaxValue; private const int MSEED = 161803398; private const int MZ = 0; - // // Member Variables // @@ -64,7 +49,7 @@ namespace System int mj, mk; //Initialize our Seed array. - int subtraction = (Seed == Int32.MinValue) ? Int32.MaxValue : Math.Abs(Seed); + int subtraction = (Seed == int.MinValue) ? int.MaxValue : Math.Abs(Seed); mj = MSEED - subtraction; _seedArray[55] = mj; mk = 1; @@ -195,8 +180,8 @@ namespace System result = -result; } double d = result; - d += (Int32.MaxValue - 1); // get a number in range [0 .. 2 * Int32MaxValue - 1) - d /= 2 * (uint)Int32.MaxValue - 1; + d += (int.MaxValue - 1); // get a number in range [0 .. 2 * Int32MaxValue - 1) + d /= 2 * (uint)int.MaxValue - 1; return d; } @@ -213,10 +198,9 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(minValue), SR.Format(SR.Argument_MinMaxValue, nameof(minValue), nameof(maxValue))); } - Contract.EndContractBlock(); long range = (long)maxValue - minValue; - if (range <= (long)Int32.MaxValue) + if (range <= int.MaxValue) { return ((int)(Sample() * range) + minValue); } @@ -238,7 +222,6 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(maxValue), SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(maxValue))); } - Contract.EndContractBlock(); return (int)(Sample() * maxValue); } @@ -263,10 +246,17 @@ namespace System public virtual void NextBytes(byte[] buffer) { if (buffer == null) throw new ArgumentNullException(nameof(buffer)); - Contract.EndContractBlock(); for (int i = 0; i < buffer.Length; i++) { - buffer[i] = (byte)(InternalSample() % (Byte.MaxValue + 1)); + buffer[i] = (byte)InternalSample(); + } + } + + public virtual void NextBytes(Span<byte> buffer) + { + for (int i = 0; i < buffer.Length; i++) + { + buffer[i] = (byte)Next(); } } } diff --git a/src/mscorlib/shared/System/RankException.cs b/src/mscorlib/shared/System/RankException.cs index 15759ea75d..f2c5d06548 100644 --- a/src/mscorlib/shared/System/RankException.cs +++ b/src/mscorlib/shared/System/RankException.cs @@ -21,19 +21,19 @@ namespace System public RankException() : base(SR.Arg_RankException) { - HResult = __HResults.COR_E_RANK; + HResult = HResults.COR_E_RANK; } public RankException(String message) : base(message) { - HResult = __HResults.COR_E_RANK; + HResult = HResults.COR_E_RANK; } public RankException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_RANK; + HResult = HResults.COR_E_RANK; } protected RankException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/ReadOnlyMemory.cs b/src/mscorlib/shared/System/ReadOnlyMemory.cs new file mode 100644 index 0000000000..8153d02fa3 --- /dev/null +++ b/src/mscorlib/shared/System/ReadOnlyMemory.cs @@ -0,0 +1,270 @@ +// 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.Buffers; +using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute; +using EditorBrowsableState = System.ComponentModel.EditorBrowsableState; +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System +{ + public struct ReadOnlyMemory<T> + { + // The highest order bit of _index is used to discern whether _arrayOrOwnedMemory is an array or an owned memory + // if (_index >> 31) == 1, object _arrayOrOwnedMemory is an OwnedMemory<T> + // else, object _arrayOrOwnedMemory is a T[] + private readonly object _arrayOrOwnedMemory; + private readonly int _index; + private readonly int _length; + + private const int RemoveOwnedFlagBitMask = 0x7FFFFFFF; + + /// <summary> + /// Creates a new memory 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 ReadOnlyMemory(T[] array) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + + _arrayOrOwnedMemory = array; + _index = 0; + _length = array.Length; + } + + /// <summary> + /// Creates a new memory 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 memory.</param> + /// <param name="length">The number of items in the memory.</param> + /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null + /// reference (Nothing in Visual Basic).</exception> + /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> or end index is not in the range (<0 or >=Length). + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlyMemory(T[] array, int start, int length) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + _arrayOrOwnedMemory = array; + _index = start; + _length = length; + } + + // Constructor for internal use only. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ReadOnlyMemory(OwnedMemory<T> owner, int index, int length) + { + if (owner == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.ownedMemory); + if (index < 0 || length < 0) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + _arrayOrOwnedMemory = owner; + _index = index | (1 << 31); // Before using _index, check if _index < 0, then 'and' it with RemoveOwnedFlagBitMask + _length = length; + } + + /// <summary> + /// Defines an implicit conversion of an array to a <see cref="Memory{T}"/> + /// </summary> + public static implicit operator ReadOnlyMemory<T>(T[] array) => new ReadOnlyMemory<T>(array); + + /// <summary> + /// Defines an implicit conversion of a <see cref="ArraySegment{T}"/> to a <see cref="Memory{T}"/> + /// </summary> + public static implicit operator ReadOnlyMemory<T>(ArraySegment<T> arraySegment) => new ReadOnlyMemory<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count); + + /// <summary> + /// Returns an empty <see cref="Memory{T}"/> + /// </summary> + public static ReadOnlyMemory<T> Empty { get; } = Array.Empty<T>(); + + /// <summary> + /// The number of items in the memory. + /// </summary> + public int Length => _length; + + /// <summary> + /// Returns true if Length is 0. + /// </summary> + public bool IsEmpty => _length == 0; + + /// <summary> + /// Forms a slice out of the given memory, beginning at 'start'. + /// </summary> + /// <param name="start">The index at which to begin this slice.</param> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> index is not in range (<0 or >=Length). + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlyMemory<T> Slice(int start) + { + if ((uint)start > (uint)_length) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + if (_index < 0) + return new ReadOnlyMemory<T>((OwnedMemory<T>)_arrayOrOwnedMemory, (_index & RemoveOwnedFlagBitMask) + start, _length - start); + return new ReadOnlyMemory<T>((T[])_arrayOrOwnedMemory, _index + start, _length - start); + } + + /// <summary> + /// Forms a slice out of the given memory, beginning at 'start', of given length + /// </summary> + /// <param name="start">The index at which to begin this slice.</param> + /// <param name="length">The desired length for the slice (exclusive).</param> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> or end index is not in range (<0 or >=Length). + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlyMemory<T> Slice(int start, int length) + { + if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + if (_index < 0) + return new ReadOnlyMemory<T>((OwnedMemory<T>)_arrayOrOwnedMemory, (_index & RemoveOwnedFlagBitMask) + start, length); + return new ReadOnlyMemory<T>((T[])_arrayOrOwnedMemory, _index + start, length); + } + + /// <summary> + /// Returns a span from the memory. + /// </summary> + public ReadOnlySpan<T> Span + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + if (_index < 0) + return ((OwnedMemory<T>)_arrayOrOwnedMemory).AsSpan().Slice(_index & RemoveOwnedFlagBitMask, _length); + return new ReadOnlySpan<T>((T[])_arrayOrOwnedMemory, _index, _length); + } + } + + public unsafe MemoryHandle Retain(bool pin = false) + { + MemoryHandle memoryHandle; + if (pin) + { + if (_index < 0) + { + memoryHandle = ((OwnedMemory<T>)_arrayOrOwnedMemory).Pin(); + } + else + { + var array = (T[])_arrayOrOwnedMemory; + var handle = GCHandle.Alloc(array, GCHandleType.Pinned); + void* pointer = Unsafe.Add<T>(Unsafe.AsPointer(ref array.GetRawSzArrayData()), _index); + memoryHandle = new MemoryHandle(null, pointer, handle); + } + } + else + { + if (_index < 0) + { + ((OwnedMemory<T>)_arrayOrOwnedMemory).Retain(); + memoryHandle = new MemoryHandle((OwnedMemory<T>)_arrayOrOwnedMemory); + } + else + { + memoryHandle = new MemoryHandle(null); + } + } + return memoryHandle; + } + + /// <summary> + /// Get an array segment from the underlying memory. + /// If unable to get the array segment, return false with a default array segment. + /// </summary> + [EditorBrowsable(EditorBrowsableState.Never)] + public bool DangerousTryGetArray(out ArraySegment<T> arraySegment) + { + if (_index < 0) + { + if (((OwnedMemory<T>)_arrayOrOwnedMemory).TryGetArray(out var segment)) + { + arraySegment = new ArraySegment<T>(segment.Array, segment.Offset + (_index & RemoveOwnedFlagBitMask), _length); + return true; + } + } + else + { + arraySegment = new ArraySegment<T>((T[])_arrayOrOwnedMemory, _index, _length); + return true; + } + + arraySegment = default(ArraySegment<T>); + return false; + } + + /// <summary> + /// Copies the contents from the memory into a new array. This heap + /// allocates, so should generally be avoided, however it is sometimes + /// necessary to bridge the gap with APIs written in terms of arrays. + /// </summary> + public T[] ToArray() => Span.ToArray(); + + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) + { + if (obj is ReadOnlyMemory<T> readOnlyMemory) + { + return Equals(readOnlyMemory); + } + else if (obj is Memory<T> memory) + { + return Equals(memory); + } + else + { + return false; + } + } + + /// <summary> + /// Returns true if the memory points to the same array and has the same length. Note that + /// this does *not* check to see if the *contents* are equal. + /// </summary> + public bool Equals(ReadOnlyMemory<T> other) + { + return + _arrayOrOwnedMemory == other._arrayOrOwnedMemory && + _index == other._index && + _length == other._length; + } + + [EditorBrowsable( EditorBrowsableState.Never)] + public override int GetHashCode() + { + return CombineHashCodes(_arrayOrOwnedMemory.GetHashCode(), (_index & RemoveOwnedFlagBitMask).GetHashCode(), _length.GetHashCode()); + } + + private static int CombineHashCodes(int left, int right) + { + return ((left << 5) + left) ^ right; + } + + private static int CombineHashCodes(int h1, int h2, int h3) + { + return CombineHashCodes(CombineHashCodes(h1, h2), h3); + } + + } +}
\ No newline at end of file diff --git a/src/mscorlib/shared/System/ReadOnlySpan.cs b/src/mscorlib/shared/System/ReadOnlySpan.cs index ae49f59269..bcf1697e88 100644 --- a/src/mscorlib/shared/System/ReadOnlySpan.cs +++ b/src/mscorlib/shared/System/ReadOnlySpan.cs @@ -2,10 +2,10 @@ // 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.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; -using EditorBrowsableState = System.ComponentModel.EditorBrowsableState; -using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute; +using System.Runtime.Versioning; #pragma warning disable 0809 //warning CS0809: Obsolete member 'Span<T>.Equals(object)' overrides non-obsolete member 'object.Equals(object)' @@ -15,7 +15,9 @@ namespace System /// ReadOnlySpan represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed /// or native memory, or to memory allocated on the stack. It is type- and memory-safe. /// </summary> + [IsReadOnly] [IsByRefLike] + [NonVersionable] public struct ReadOnlySpan<T> { /// <summary>A byref or a native ptr.</summary> @@ -44,29 +46,6 @@ namespace System /// <summary> /// Creates a new read-only span over the portion of the target array beginning - /// at 'start' index and covering the remainder of the array. - /// </summary> - /// <param name="array">The target array.</param> - /// <param name="start">The index at which to begin the read-only span.</param> - /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null - /// reference (Nothing in Visual Basic).</exception> - /// <exception cref="System.ArgumentOutOfRangeException"> - /// Thrown when the specified <paramref name="start"/> is not in the range (<0 or >=Length). - /// </exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpan(T[] array, int start) - { - if (array == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - if ((uint)start > (uint)array.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(); - - _pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start)); - _length = array.Length - start; - } - - /// <summary> - /// Creates a new read-only span over the portion of the target array beginning /// at 'start' index and ending at 'end' index (exclusive). /// </summary> /// <param name="array">The target array.</param> @@ -126,6 +105,7 @@ namespace System /// <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)] + [EditorBrowsable(EditorBrowsableState.Never)] public static ReadOnlySpan<T> DangerousCreate(object obj, ref T objectData, int length) => new ReadOnlySpan<T>(ref objectData, length); // Constructor for internal use only. @@ -143,6 +123,7 @@ namespace System /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] + [EditorBrowsable(EditorBrowsableState.Never)] public ref T DangerousGetPinnableReference() { return ref _pointer.Value; @@ -151,12 +132,26 @@ namespace System /// <summary> /// The number of items in the read-only span. /// </summary> - public int Length => _length; + public int Length + { + [NonVersionable] + get + { + return _length; + } + } /// <summary> /// Returns true if Length is 0. /// </summary> - public bool IsEmpty => _length == 0; + public bool IsEmpty + { + [NonVersionable] + get + { + return _length == 0; + } + } /// <summary> /// Returns the specified element of the read-only span. @@ -179,6 +174,7 @@ namespace System [Intrinsic] #endif [MethodImpl(MethodImplOptions.AggressiveInlining)] + [NonVersionable] get { if ((uint)index >= (uint)_length) diff --git a/src/mscorlib/shared/System/Reflection/AmbiguousMatchException.cs b/src/mscorlib/shared/System/Reflection/AmbiguousMatchException.cs index a0075bbc0b..c4aeca704d 100644 --- a/src/mscorlib/shared/System/Reflection/AmbiguousMatchException.cs +++ b/src/mscorlib/shared/System/Reflection/AmbiguousMatchException.cs @@ -11,19 +11,19 @@ namespace System.Reflection public AmbiguousMatchException() : base(SR.RFLCT_Ambiguous) { - HResult = __HResults.COR_E_AMBIGUOUSMATCH; + HResult = HResults.COR_E_AMBIGUOUSMATCH; } public AmbiguousMatchException(string message) : base(message) { - HResult = __HResults.COR_E_AMBIGUOUSMATCH; + HResult = HResults.COR_E_AMBIGUOUSMATCH; } public AmbiguousMatchException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_AMBIGUOUSMATCH; + HResult = HResults.COR_E_AMBIGUOUSMATCH; } } } diff --git a/src/mscorlib/shared/System/Reflection/BindingFlags.cs b/src/mscorlib/shared/System/Reflection/BindingFlags.cs index 26c875d0f9..7ba83e20da 100644 --- a/src/mscorlib/shared/System/Reflection/BindingFlags.cs +++ b/src/mscorlib/shared/System/Reflection/BindingFlags.cs @@ -46,5 +46,6 @@ namespace System.Reflection // These are a couple of misc attributes used IgnoreReturn = 0x01000000, // This is used in COM Interop + DoNotWrapExceptions = 0x02000000, // Disables wrapping exceptions in TargetInvocationException } } diff --git a/src/mscorlib/shared/System/Reflection/CustomAttributeFormatException.cs b/src/mscorlib/shared/System/Reflection/CustomAttributeFormatException.cs index 13766ae8d0..ae67158a53 100644 --- a/src/mscorlib/shared/System/Reflection/CustomAttributeFormatException.cs +++ b/src/mscorlib/shared/System/Reflection/CustomAttributeFormatException.cs @@ -21,7 +21,7 @@ namespace System.Reflection public CustomAttributeFormatException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_CUSTOMATTRIBUTEFORMAT; + HResult = HResults.COR_E_CUSTOMATTRIBUTEFORMAT; } protected CustomAttributeFormatException(SerializationInfo info, StreamingContext context) diff --git a/src/mscorlib/shared/System/Reflection/IReflect.cs b/src/mscorlib/shared/System/Reflection/IReflect.cs index 51141ae47c..a7e84b6168 100644 --- a/src/mscorlib/shared/System/Reflection/IReflect.cs +++ b/src/mscorlib/shared/System/Reflection/IReflect.cs @@ -47,7 +47,7 @@ namespace System.Reflection MemberInfo[] GetMembers(BindingFlags bindingAttr); // Description of the Binding Process. - // We must invoke a method that is accessable and for which the provided + // We must invoke a method that is accessible and for which the provided // parameters have the most specific match. A method may be called if // 1. The number of parameters in the method declaration equals the number of // arguments provided to the invocation @@ -59,7 +59,7 @@ namespace System.Reflection // of methods is filtered by the name, number of arguments and a set of search modifiers // defined in the Binder. // - // After the method is selected, it will be invoked. Accessability is checked + // After the method is selected, it will be invoked. Accessibility is checked // at that point. The search may be control which set of methods are searched based // upon the accessibility attribute associated with the method. // diff --git a/src/mscorlib/shared/System/Reflection/InvalidFilterCriteriaException.cs b/src/mscorlib/shared/System/Reflection/InvalidFilterCriteriaException.cs index 07880a768d..85a447707c 100644 --- a/src/mscorlib/shared/System/Reflection/InvalidFilterCriteriaException.cs +++ b/src/mscorlib/shared/System/Reflection/InvalidFilterCriteriaException.cs @@ -21,7 +21,7 @@ namespace System.Reflection public InvalidFilterCriteriaException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_INVALIDFILTERCRITERIA; + HResult = HResults.COR_E_INVALIDFILTERCRITERIA; } protected InvalidFilterCriteriaException(SerializationInfo info, StreamingContext context) diff --git a/src/mscorlib/shared/System/Reflection/MethodInfo.Internal.cs b/src/mscorlib/shared/System/Reflection/MethodInfo.Internal.cs new file mode 100644 index 0000000000..a36ef900da --- /dev/null +++ b/src/mscorlib/shared/System/Reflection/MethodInfo.Internal.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Reflection +{ + public abstract partial class MethodInfo : MethodBase + { +#if CORECLR + internal +#else + // Not an api but needs to be public so that Reflection.Core can access it. + public +#endif + virtual int GenericParameterCount => GetGenericArguments().Length; + } +} diff --git a/src/mscorlib/shared/System/Reflection/MethodInfo.cs b/src/mscorlib/shared/System/Reflection/MethodInfo.cs index 3f60149e87..95c62ba7f0 100644 --- a/src/mscorlib/shared/System/Reflection/MethodInfo.cs +++ b/src/mscorlib/shared/System/Reflection/MethodInfo.cs @@ -4,7 +4,7 @@ namespace System.Reflection { - public abstract class MethodInfo : MethodBase + public abstract partial class MethodInfo : MethodBase { protected MethodInfo() { } diff --git a/src/mscorlib/shared/System/Reflection/Pointer.cs b/src/mscorlib/shared/System/Reflection/Pointer.cs index 55376c66c0..e1a9990cf0 100644 --- a/src/mscorlib/shared/System/Reflection/Pointer.cs +++ b/src/mscorlib/shared/System/Reflection/Pointer.cs @@ -46,6 +46,6 @@ namespace System.Reflection } internal Type GetPointerType() => _ptrType; - internal object GetPointerValue() => (IntPtr)_ptr; + internal IntPtr GetPointerValue() => (IntPtr)_ptr; } } diff --git a/src/mscorlib/shared/System/Reflection/ReflectionTypeLoadException.cs b/src/mscorlib/shared/System/Reflection/ReflectionTypeLoadException.cs index ca0c6ab0db..0e86d34056 100644 --- a/src/mscorlib/shared/System/Reflection/ReflectionTypeLoadException.cs +++ b/src/mscorlib/shared/System/Reflection/ReflectionTypeLoadException.cs @@ -13,7 +13,7 @@ namespace System.Reflection { Types = classes; LoaderExceptions = exceptions; - HResult = __HResults.COR_E_REFLECTIONTYPELOAD; + HResult = HResults.COR_E_REFLECTIONTYPELOAD; } public ReflectionTypeLoadException(Type[] classes, Exception[] exceptions, string message) @@ -21,7 +21,7 @@ namespace System.Reflection { Types = classes; LoaderExceptions = exceptions; - HResult = __HResults.COR_E_REFLECTIONTYPELOAD; + HResult = HResults.COR_E_REFLECTIONTYPELOAD; } public override void GetObjectData(SerializationInfo info, StreamingContext context) diff --git a/src/mscorlib/shared/System/Reflection/SignatureArrayType.cs b/src/mscorlib/shared/System/Reflection/SignatureArrayType.cs new file mode 100644 index 0000000000..52011b8a9d --- /dev/null +++ b/src/mscorlib/shared/System/Reflection/SignatureArrayType.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +namespace System.Reflection +{ + internal sealed class SignatureArrayType : SignatureHasElementType + { + internal SignatureArrayType(SignatureType elementType, int rank, bool isMultiDim) + : base(elementType) + { + Debug.Assert(rank > 0); + Debug.Assert(rank == 1 || isMultiDim); + + _rank = rank; + _isMultiDim = isMultiDim; + } + + protected sealed override bool IsArrayImpl() => true; + protected sealed override bool IsByRefImpl() => false; + protected sealed override bool IsPointerImpl() => false; + + public sealed override bool IsSZArray => !_isMultiDim; + public sealed override bool IsVariableBoundArray => _isMultiDim; + + public sealed override int GetArrayRank() => _rank; + + protected sealed override string Suffix + { + get + { + if (!_isMultiDim) + return "[]"; + else if (_rank == 1) + return "[*]"; + else + return "[" + new string(',', _rank - 1) + "]"; + } + } + + private readonly int _rank; + private readonly bool _isMultiDim; + } +} diff --git a/src/mscorlib/shared/System/Reflection/SignatureByRefType.cs b/src/mscorlib/shared/System/Reflection/SignatureByRefType.cs new file mode 100644 index 0000000000..eb5f6de42e --- /dev/null +++ b/src/mscorlib/shared/System/Reflection/SignatureByRefType.cs @@ -0,0 +1,27 @@ +// 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; + +namespace System.Reflection +{ + internal sealed class SignatureByRefType : SignatureHasElementType + { + internal SignatureByRefType(SignatureType elementType) + : base(elementType) + { + } + + protected sealed override bool IsArrayImpl() => false; + protected sealed override bool IsByRefImpl() => true; + protected sealed override bool IsPointerImpl() => false; + + public sealed override bool IsSZArray => false; + public sealed override bool IsVariableBoundArray => false; + + public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass); + + protected sealed override string Suffix => "&"; + } +} diff --git a/src/mscorlib/shared/System/Reflection/SignatureConstructedGenericType.cs b/src/mscorlib/shared/System/Reflection/SignatureConstructedGenericType.cs new file mode 100644 index 0000000000..afcdfda352 --- /dev/null +++ b/src/mscorlib/shared/System/Reflection/SignatureConstructedGenericType.cs @@ -0,0 +1,73 @@ +// 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.Text; +using System.Diagnostics; + +namespace System.Reflection +{ + internal sealed class SignatureConstructedGenericType : SignatureType + { + internal SignatureConstructedGenericType(Type genericTypeDefinition, Type[] genericTypeArguments) + { + Debug.Assert(genericTypeDefinition != null && genericTypeArguments != null); + _genericTypeDefinition = genericTypeDefinition; + _genericTypeArguments = (Type[])(genericTypeArguments.Clone()); + } + + public sealed override bool IsTypeDefinition => false; + public sealed override bool IsGenericTypeDefinition => false; + protected sealed override bool HasElementTypeImpl() => false; + protected sealed override bool IsArrayImpl() => false; + protected sealed override bool IsByRefImpl() => false; + public sealed override bool IsByRefLike => _genericTypeDefinition.IsByRefLike; + protected sealed override bool IsPointerImpl() => false; + public sealed override bool IsSZArray => false; + public sealed override bool IsVariableBoundArray => false; + public sealed override bool IsConstructedGenericType => true; + public sealed override bool IsGenericParameter => false; + public sealed override bool IsGenericMethodParameter => false; + public sealed override bool ContainsGenericParameters + { + get + { + for (int i = 0; i < _genericTypeArguments.Length; i++) + { + if (_genericTypeArguments[i].ContainsGenericParameters) + return true; + } + return false; + } + } + + internal sealed override SignatureType ElementType => null; + public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass); + public sealed override Type GetGenericTypeDefinition() => _genericTypeDefinition; + public sealed override Type[] GetGenericArguments() => GenericTypeArguments; + public sealed override Type[] GenericTypeArguments => (Type[])(_genericTypeArguments.Clone()); + public sealed override int GenericParameterPosition => throw new InvalidOperationException(SR.Arg_NotGenericParameter); + + public sealed override string Name => _genericTypeDefinition.Name; + public sealed override string Namespace => _genericTypeDefinition.Namespace; + + public sealed override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append(_genericTypeDefinition.ToString()); + sb.Append('['); + for (int i = 0; i < _genericTypeArguments.Length; i++) + { + if (i != 0) + sb.Append(','); + sb.Append(_genericTypeArguments[i].ToString()); + } + sb.Append(']'); + return sb.ToString(); + } + + private readonly Type _genericTypeDefinition; + private readonly Type[] _genericTypeArguments; + } +} diff --git a/src/mscorlib/shared/System/Reflection/SignatureGenericMethodParameterType.cs b/src/mscorlib/shared/System/Reflection/SignatureGenericMethodParameterType.cs new file mode 100644 index 0000000000..ae73272ab7 --- /dev/null +++ b/src/mscorlib/shared/System/Reflection/SignatureGenericMethodParameterType.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Reflection +{ + internal sealed class SignatureGenericMethodParameterType : SignatureGenericParameterType + { + internal SignatureGenericMethodParameterType(int position) + : base(position) + { + } + + public sealed override bool IsGenericMethodParameter => true; + + public sealed override string Name => "!!" + GenericParameterPosition; + } +} diff --git a/src/mscorlib/shared/System/Reflection/SignatureGenericParameterType.cs b/src/mscorlib/shared/System/Reflection/SignatureGenericParameterType.cs new file mode 100644 index 0000000000..fee7bce353 --- /dev/null +++ b/src/mscorlib/shared/System/Reflection/SignatureGenericParameterType.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; + +namespace System.Reflection +{ + internal abstract class SignatureGenericParameterType : SignatureType + { + protected SignatureGenericParameterType(int position) + { + Debug.Assert(position >= 0); + _position = position; + } + + public sealed override bool IsTypeDefinition => false; + public sealed override bool IsGenericTypeDefinition => false; + protected sealed override bool HasElementTypeImpl() => false; + protected sealed override bool IsArrayImpl() => false; + protected sealed override bool IsByRefImpl() => false; + public sealed override bool IsByRefLike => false; + protected sealed override bool IsPointerImpl() => false; + public sealed override bool IsSZArray => false; + public sealed override bool IsVariableBoundArray => false; + public sealed override bool IsConstructedGenericType => false; + public sealed override bool IsGenericParameter => true; + public abstract override bool IsGenericMethodParameter { get; } + public sealed override bool ContainsGenericParameters => true; + + internal sealed override SignatureType ElementType => null; + public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass); + public sealed override Type GetGenericTypeDefinition() => throw new InvalidOperationException(SR.InvalidOperation_NotGenericType); + public sealed override Type[] GetGenericArguments() => Array.Empty<Type>(); + public sealed override Type[] GenericTypeArguments => Array.Empty<Type>(); + public sealed override int GenericParameterPosition => _position; + + public abstract override string Name { get; } + public sealed override string Namespace => null; + + public sealed override string ToString() => Name; + + private readonly int _position; + } +} diff --git a/src/mscorlib/shared/System/Reflection/SignatureHasElementType.cs b/src/mscorlib/shared/System/Reflection/SignatureHasElementType.cs new file mode 100644 index 0000000000..e1aa6c3584 --- /dev/null +++ b/src/mscorlib/shared/System/Reflection/SignatureHasElementType.cs @@ -0,0 +1,48 @@ +// 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.Diagnostics; + +namespace System.Reflection +{ + internal abstract class SignatureHasElementType : SignatureType + { + protected SignatureHasElementType(SignatureType elementType) + { + Debug.Assert(elementType != null); + _elementType = elementType; + } + + public sealed override bool IsTypeDefinition => false; + public sealed override bool IsGenericTypeDefinition => false; + protected sealed override bool HasElementTypeImpl() => true; + protected abstract override bool IsArrayImpl(); + protected abstract override bool IsByRefImpl(); + public sealed override bool IsByRefLike => false; + protected abstract override bool IsPointerImpl(); + public abstract override bool IsSZArray { get; } + public abstract override bool IsVariableBoundArray { get; } + public sealed override bool IsConstructedGenericType => false; + public sealed override bool IsGenericParameter => false; + public sealed override bool IsGenericMethodParameter => false; + public sealed override bool ContainsGenericParameters => _elementType.ContainsGenericParameters; + + internal sealed override SignatureType ElementType => _elementType; + public abstract override int GetArrayRank(); + public sealed override Type GetGenericTypeDefinition() => throw new InvalidOperationException(SR.InvalidOperation_NotGenericType); + public sealed override Type[] GetGenericArguments() => Array.Empty<Type>(); + public sealed override Type[] GenericTypeArguments => Array.Empty<Type>(); + public sealed override int GenericParameterPosition => throw new InvalidOperationException(SR.Arg_NotGenericParameter); + + public sealed override string Name => _elementType.Name + Suffix; + public sealed override string Namespace => _elementType.Namespace; + + public sealed override string ToString() => _elementType.ToString() + Suffix; + + protected abstract string Suffix { get; } + + private readonly SignatureType _elementType; + } +} diff --git a/src/mscorlib/shared/System/Reflection/SignaturePointerType.cs b/src/mscorlib/shared/System/Reflection/SignaturePointerType.cs new file mode 100644 index 0000000000..a75a208165 --- /dev/null +++ b/src/mscorlib/shared/System/Reflection/SignaturePointerType.cs @@ -0,0 +1,27 @@ +// 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; + +namespace System.Reflection +{ + internal sealed class SignaturePointerType : SignatureHasElementType + { + internal SignaturePointerType(SignatureType elementType) + : base(elementType) + { + } + + protected sealed override bool IsArrayImpl() => false; + protected sealed override bool IsByRefImpl() => false; + protected sealed override bool IsPointerImpl() => true; + + public sealed override bool IsSZArray => false; + public sealed override bool IsVariableBoundArray => false; + + public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass); + + protected sealed override string Suffix => "*"; + } +} diff --git a/src/mscorlib/shared/System/Reflection/SignatureType.cs b/src/mscorlib/shared/System/Reflection/SignatureType.cs new file mode 100644 index 0000000000..c3dfc49174 --- /dev/null +++ b/src/mscorlib/shared/System/Reflection/SignatureType.cs @@ -0,0 +1,139 @@ +// 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.Globalization; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace System.Reflection +{ + // + // Signature Types are highly restricted Type objects that can be passed in the Type[] parameter to Type.GetMethod(). Their primary + // use is to pass types representing generic parameters defined by the method, or types composed from them. Passing in the normal + // generic parameter Type obtained from MethodInfo.GetGenericArguments() is usually impractical (if you had the method, you wouldn't + // be looking for it!) + // + internal abstract class SignatureType : Type + { + public sealed override bool IsSignatureType => true; + + // Type flavor predicates + public abstract override bool IsTypeDefinition { get; } + protected abstract override bool HasElementTypeImpl(); + protected abstract override bool IsArrayImpl(); + public abstract override bool IsSZArray { get; } + public abstract override bool IsVariableBoundArray { get; } + protected abstract override bool IsByRefImpl(); + public abstract override bool IsByRefLike { get; } + protected abstract override bool IsPointerImpl(); + public sealed override bool IsGenericType => IsGenericTypeDefinition || IsConstructedGenericType; + public abstract override bool IsGenericTypeDefinition { get; } + public abstract override bool IsConstructedGenericType { get; } + public abstract override bool IsGenericParameter { get; } + public abstract bool IsGenericMethodParameter { get; } + public abstract override bool ContainsGenericParameters { get; } + public sealed override MemberTypes MemberType => MemberTypes.TypeInfo; + + // Compositors + public sealed override Type MakeArrayType() => new SignatureArrayType(this, rank: 1, isMultiDim: false); + public sealed override Type MakeArrayType(int rank) + { + if (rank <= 0) + throw new IndexOutOfRangeException(); + return new SignatureArrayType(this, rank: rank, isMultiDim: true); + } + public sealed override Type MakeByRefType() => new SignatureByRefType(this); + public sealed override Type MakePointerType() => new SignaturePointerType(this); + public sealed override Type MakeGenericType(params Type[] typeArguments) => throw new NotSupportedException(SR.NotSupported_SignatureType); // There is no SignatureType for type definition types so it would never be legal to call this. + + // Dissectors + public sealed override Type GetElementType() => ElementType; + public abstract override int GetArrayRank(); + public abstract override Type GetGenericTypeDefinition(); + public abstract override Type[] GenericTypeArguments { get; } + public abstract override Type[] GetGenericArguments(); + public abstract override int GenericParameterPosition { get; } + internal abstract SignatureType ElementType { get; } + + // Identity +#if DEBUG + public sealed override bool Equals(object o) => base.Equals(o); + public sealed override bool Equals(Type o) => base.Equals(o); + public sealed override int GetHashCode() => base.GetHashCode(); +#endif + public sealed override Type UnderlyingSystemType => this; // Equals(Type) depends on this. + + // Naming and diagnostics + public abstract override string Name { get; } + public abstract override string Namespace { get; } + public sealed override string FullName => null; + public sealed override string AssemblyQualifiedName => null; + public abstract override string ToString(); + + // Not supported on Signature Types + public sealed override Assembly Assembly => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Module Module => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type ReflectedType => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type BaseType => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type[] GetInterfaces() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsAssignableFrom(Type c) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override int MetadataToken => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type DeclaringType => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MethodBase DeclaringMethod => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type[] GetGenericParameterConstraints() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override GenericParameterAttributes GenericParameterAttributes => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsEnumDefined(object value) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override string GetEnumName(object value) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override string[] GetEnumNames() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type GetEnumUnderlyingType() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Array GetEnumValues() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Guid GUID => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override TypeCode GetTypeCodeImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override TypeAttributes GetAttributeFlagsImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override EventInfo GetEvent(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override EventInfo[] GetEvents(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override FieldInfo GetField(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override FieldInfo[] GetFields(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MemberInfo[] GetMembers(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MethodInfo[] GetMethods(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type GetNestedType(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type[] GetNestedTypes(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override MethodInfo GetMethodImpl(string name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MemberInfo[] FindMembers(MemberTypes memberType, BindingFlags bindingAttr, MemberFilter filter, object filterCriteria) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MemberInfo[] GetMember(string name, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override MemberInfo[] GetDefaultMembers() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override EventInfo[] GetEvents() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override object[] GetCustomAttributes(bool inherit) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override object[] GetCustomAttributes(Type attributeType, bool inherit) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsDefined(Type attributeType, bool inherit) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override IList<CustomAttributeData> GetCustomAttributesData() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type GetInterface(string name, bool ignoreCase) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override bool IsCOMObjectImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override bool IsPrimitiveImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override IEnumerable<CustomAttributeData> CustomAttributes => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override Type[] FindInterfaces(TypeFilter filter, object filterCriteria) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override InterfaceMapping GetInterfaceMap(Type interfaceType) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override bool IsContextfulImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsEnum => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsEquivalentTo(Type other) => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsInstanceOfType(object o) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override bool IsMarshalByRefImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsSecurityCritical => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsSecuritySafeCritical => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsSecurityTransparent => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsSerializable => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override bool IsSubclassOf(Type c) => throw new NotSupportedException(SR.NotSupported_SignatureType); + protected sealed override bool IsValueTypeImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override StructLayoutAttribute StructLayoutAttribute => throw new NotSupportedException(SR.NotSupported_SignatureType); + public sealed override RuntimeTypeHandle TypeHandle => throw new NotSupportedException(SR.NotSupported_SignatureType); + } +} diff --git a/src/mscorlib/shared/System/Reflection/SignatureTypeExtensions.cs b/src/mscorlib/shared/System/Reflection/SignatureTypeExtensions.cs new file mode 100644 index 0000000000..b30da50073 --- /dev/null +++ b/src/mscorlib/shared/System/Reflection/SignatureTypeExtensions.cs @@ -0,0 +1,227 @@ +// 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.Reflection; +using System.Diagnostics; + +namespace System.Reflection +{ +#if CORECLR + internal +#else + public // Needs to be public so that Reflection.Core can see it. +#endif + static class SignatureTypeExtensions + { + /// <summary> + /// This is semantically identical to + /// + /// parameter.ParameterType == pattern.TryResolveAgainstGenericMethod(parameter.Member) + /// + /// but without the allocation overhead of TryResolve. + /// </summary> + public static bool MatchesParameterTypeExactly(this Type pattern, ParameterInfo parameter) + { + if (pattern is SignatureType signatureType) + return signatureType.MatchesExactly(parameter.ParameterType); + else + return pattern == (object)(parameter.ParameterType); + } + + /// <summary> + /// This is semantically identical to + /// + /// actual == pattern.TryResolveAgainstGenericMethod(parameterMember) + /// + /// but without the allocation overhead of TryResolve. + /// </summary> + internal static bool MatchesExactly(this SignatureType pattern, Type actual) + { + if (pattern.IsSZArray) + { + return actual.IsSZArray && pattern.ElementType.MatchesExactly(actual.GetElementType()); + } + else if (pattern.IsVariableBoundArray) + { + return actual.IsVariableBoundArray && pattern.GetArrayRank() == actual.GetArrayRank() && pattern.ElementType.MatchesExactly(actual.GetElementType()); + } + else if (pattern.IsByRef) + { + return actual.IsByRef && pattern.ElementType.MatchesExactly(actual.GetElementType()); + } + else if (pattern.IsPointer) + { + return actual.IsPointer && pattern.ElementType.MatchesExactly(actual.GetElementType()); + } + else if (pattern.IsConstructedGenericType) + { + if (!actual.IsConstructedGenericType) + return false; + if (!(pattern.GetGenericTypeDefinition() == actual.GetGenericTypeDefinition())) + return false; + Type[] patternGenericTypeArguments = pattern.GenericTypeArguments; + Type[] actualGenericTypeArguments = actual.GenericTypeArguments; + int count = patternGenericTypeArguments.Length; + if (count != actualGenericTypeArguments.Length) + return false; + for (int i = 0; i < count; i++) + { + Type patternGenericTypeArgument = patternGenericTypeArguments[i]; + if (patternGenericTypeArgument is SignatureType signatureType) + { + if (!signatureType.MatchesExactly(actualGenericTypeArguments[i])) + return false; + } + else + { + if (patternGenericTypeArgument != actualGenericTypeArguments[i]) + return false; + } + } + return true; + } + else if (pattern.IsGenericMethodParameter) + { + if (!(actual.IsGenericParameter && actual.DeclaringMethod != null)) + return false; + if (pattern.GenericParameterPosition != actual.GenericParameterPosition) + return false; + return true; + } + else + { + return false; + } + } + + /// <summary> + /// Translates a SignatureType into its equivalent resolved Type by recursively substituting all generic parameter references + /// with its corresponding generic parameter definition. This is slow so MatchesExactly or MatchesParameterTypeExactly should be + /// substituted instead whenever possible. This is only used by the DefaultBinder when its fast-path checks have been exhausted and + /// it needs to call non-trivial methods like IsAssignableFrom which SignatureTypes will never support. + /// + /// Because this method is used to eliminate method candidates in a GetMethod() lookup, it is entirely possible that the Type + /// might not be creatable due to conflicting generic constraints. Since this merely implies that this candidate is not + /// the method we're looking for, we return null rather than let the TypeLoadException bubble up. The DefaultBinder will catch + /// the null and continue its search for a better candidate. + /// </summary> + internal static Type TryResolveAgainstGenericMethod(this SignatureType signatureType, MethodInfo genericMethod) + { + return signatureType.TryResolve(genericMethod.GetGenericArguments()); + } + + private static Type TryResolve(this SignatureType signatureType, Type[] genericMethodParameters) + { + if (signatureType.IsSZArray) + { + return signatureType.ElementType.TryResolve(genericMethodParameters)?.TryMakeArrayType(); + } + else if (signatureType.IsVariableBoundArray) + { + return signatureType.ElementType.TryResolve(genericMethodParameters)?.TryMakeArrayType(signatureType.GetArrayRank()); + } + else if (signatureType.IsByRef) + { + return signatureType.ElementType.TryResolve(genericMethodParameters)?.TryMakeByRefType(); + } + else if (signatureType.IsPointer) + { + return signatureType.ElementType.TryResolve(genericMethodParameters)?.TryMakePointerType(); + } + else if (signatureType.IsConstructedGenericType) + { + Type[] genericTypeArguments = signatureType.GenericTypeArguments; + int count = genericTypeArguments.Length; + Type[] newGenericTypeArguments = new Type[count]; + for (int i = 0; i < count; i++) + { + Type genericTypeArgument = genericTypeArguments[i]; + if (genericTypeArgument is SignatureType signatureGenericTypeArgument) + { + newGenericTypeArguments[i] = signatureGenericTypeArgument.TryResolve(genericMethodParameters); + if (newGenericTypeArguments[i] == null) + return null; + } + else + { + newGenericTypeArguments[i] = genericTypeArgument; + } + } + return signatureType.GetGenericTypeDefinition().TryMakeGenericType(newGenericTypeArguments); + } + else if (signatureType.IsGenericMethodParameter) + { + int position = signatureType.GenericParameterPosition; + if (position >= genericMethodParameters.Length) + return null; + return genericMethodParameters[position]; + } + else + { + return null; + } + } + + private static Type TryMakeArrayType(this Type type) + { + try + { + return type.MakeArrayType(); + } + catch + { + return null; + } + } + + private static Type TryMakeArrayType(this Type type, int rank) + { + try + { + return type.MakeArrayType(rank); + } + catch + { + return null; + } + } + + private static Type TryMakeByRefType(this Type type) + { + try + { + return type.MakeByRefType(); + } + catch + { + return null; + } + } + + private static Type TryMakePointerType(this Type type) + { + try + { + return type.MakePointerType(); + } + catch + { + return null; + } + } + + private static Type TryMakeGenericType(this Type type, Type[] instantiation) + { + try + { + return type.MakeGenericType(instantiation); + } + catch + { + return null; + } + } + } +} diff --git a/src/mscorlib/shared/System/Reflection/TargetException.cs b/src/mscorlib/shared/System/Reflection/TargetException.cs index 6e43f56fa4..2309daace1 100644 --- a/src/mscorlib/shared/System/Reflection/TargetException.cs +++ b/src/mscorlib/shared/System/Reflection/TargetException.cs @@ -21,7 +21,7 @@ namespace System.Reflection public TargetException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_TARGET; + HResult = HResults.COR_E_TARGET; } protected TargetException(SerializationInfo info, StreamingContext context) diff --git a/src/mscorlib/shared/System/Reflection/TargetInvocationException.cs b/src/mscorlib/shared/System/Reflection/TargetInvocationException.cs index 8d0bfef40d..7c4f60a03e 100644 --- a/src/mscorlib/shared/System/Reflection/TargetInvocationException.cs +++ b/src/mscorlib/shared/System/Reflection/TargetInvocationException.cs @@ -11,13 +11,13 @@ namespace System.Reflection public TargetInvocationException(Exception inner) : base(SR.Arg_TargetInvocationException, inner) { - HResult = __HResults.COR_E_TARGETINVOCATION; + HResult = HResults.COR_E_TARGETINVOCATION; } public TargetInvocationException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_TARGETINVOCATION; + HResult = HResults.COR_E_TARGETINVOCATION; } } } diff --git a/src/mscorlib/shared/System/Reflection/TargetParameterCountException.cs b/src/mscorlib/shared/System/Reflection/TargetParameterCountException.cs index e200cdb94f..f31758b7ba 100644 --- a/src/mscorlib/shared/System/Reflection/TargetParameterCountException.cs +++ b/src/mscorlib/shared/System/Reflection/TargetParameterCountException.cs @@ -11,19 +11,19 @@ namespace System.Reflection public TargetParameterCountException() : base(SR.Arg_TargetParameterCountException) { - HResult = __HResults.COR_E_TARGETPARAMCOUNT; + HResult = HResults.COR_E_TARGETPARAMCOUNT; } public TargetParameterCountException(string message) : base(message) { - HResult = __HResults.COR_E_TARGETPARAMCOUNT; + HResult = HResults.COR_E_TARGETPARAMCOUNT; } public TargetParameterCountException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_TARGETPARAMCOUNT; + HResult = HResults.COR_E_TARGETPARAMCOUNT; } } } diff --git a/src/mscorlib/shared/System/Reflection/TypeDelegator.cs b/src/mscorlib/shared/System/Reflection/TypeDelegator.cs index bcbff05d62..1dba278893 100644 --- a/src/mscorlib/shared/System/Reflection/TypeDelegator.cs +++ b/src/mscorlib/shared/System/Reflection/TypeDelegator.cs @@ -108,6 +108,7 @@ namespace System.Reflection protected override bool IsPointerImpl() => typeImpl.IsPointer; protected override bool IsValueTypeImpl() => typeImpl.IsValueType; protected override bool IsCOMObjectImpl() => typeImpl.IsCOMObject; + public override bool IsByRefLike => typeImpl.IsByRefLike; public override bool IsConstructedGenericType => typeImpl.IsConstructedGenericType; public override Type GetElementType() => typeImpl.GetElementType(); protected override bool HasElementTypeImpl() => typeImpl.HasElementType; diff --git a/src/mscorlib/shared/System/Resources/MissingManifestResourceException.cs b/src/mscorlib/shared/System/Resources/MissingManifestResourceException.cs index ec814393d0..e11d15d200 100644 --- a/src/mscorlib/shared/System/Resources/MissingManifestResourceException.cs +++ b/src/mscorlib/shared/System/Resources/MissingManifestResourceException.cs @@ -12,19 +12,19 @@ namespace System.Resources public MissingManifestResourceException() : base(SR.Arg_MissingManifestResourceException) { - HResult = System.__HResults.COR_E_MISSINGMANIFESTRESOURCE; + HResult = System.HResults.COR_E_MISSINGMANIFESTRESOURCE; } public MissingManifestResourceException(string message) : base(message) { - HResult = System.__HResults.COR_E_MISSINGMANIFESTRESOURCE; + HResult = System.HResults.COR_E_MISSINGMANIFESTRESOURCE; } public MissingManifestResourceException(string message, Exception inner) : base(message, inner) { - HResult = System.__HResults.COR_E_MISSINGMANIFESTRESOURCE; + HResult = System.HResults.COR_E_MISSINGMANIFESTRESOURCE; } protected MissingManifestResourceException(SerializationInfo info, StreamingContext context) diff --git a/src/mscorlib/shared/System/Resources/MissingSatelliteAssemblyException.cs b/src/mscorlib/shared/System/Resources/MissingSatelliteAssemblyException.cs index d2ddc992ac..615aed6a8c 100644 --- a/src/mscorlib/shared/System/Resources/MissingSatelliteAssemblyException.cs +++ b/src/mscorlib/shared/System/Resources/MissingSatelliteAssemblyException.cs @@ -27,26 +27,26 @@ namespace System.Resources public MissingSatelliteAssemblyException() : base(SR.MissingSatelliteAssembly_Default) { - HResult = System.__HResults.COR_E_MISSINGSATELLITEASSEMBLY; + HResult = System.HResults.COR_E_MISSINGSATELLITEASSEMBLY; } public MissingSatelliteAssemblyException(string message) : base(message) { - HResult = System.__HResults.COR_E_MISSINGSATELLITEASSEMBLY; + HResult = System.HResults.COR_E_MISSINGSATELLITEASSEMBLY; } public MissingSatelliteAssemblyException(string message, String cultureName) : base(message) { - HResult = System.__HResults.COR_E_MISSINGSATELLITEASSEMBLY; + HResult = System.HResults.COR_E_MISSINGSATELLITEASSEMBLY; _cultureName = cultureName; } public MissingSatelliteAssemblyException(string message, Exception inner) : base(message, inner) { - HResult = System.__HResults.COR_E_MISSINGSATELLITEASSEMBLY; + HResult = System.HResults.COR_E_MISSINGSATELLITEASSEMBLY; } protected MissingSatelliteAssemblyException(SerializationInfo info, StreamingContext context) diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs new file mode 100644 index 0000000000..688a3a01ba --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + /// <summary> + /// Indicates the type of the async method builder that should be used by a language compiler to + /// build the attributed type when used as the return type of an async method. + /// </summary> + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Delegate | AttributeTargets.Enum, Inherited = false, AllowMultiple = false)] + public sealed class AsyncMethodBuilderAttribute : Attribute + { + /// <summary>Initializes the <see cref="AsyncMethodBuilderAttribute"/>.</summary> + /// <param name="builderType">The <see cref="Type"/> of the associated builder.</param> + public AsyncMethodBuilderAttribute(Type builderType) => BuilderType = builderType; + + /// <summary>Gets the <see cref="Type"/> of the associated builder.</summary> + public Type BuilderType { get; } + } +} diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs new file mode 100644 index 0000000000..813d9e5cd7 --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs @@ -0,0 +1,111 @@ +// 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.Runtime.InteropServices; +using System.Security; +using System.Threading.Tasks; + +namespace System.Runtime.CompilerServices +{ + /// <summary>Represents a builder for asynchronous methods that returns a <see cref="ValueTask{TResult}"/>.</summary> + /// <typeparam name="TResult">The type of the result.</typeparam> + [StructLayout(LayoutKind.Auto)] + public struct AsyncValueTaskMethodBuilder<TResult> + { + /// <summary>The <see cref="AsyncTaskMethodBuilder{TResult}"/> to which most operations are delegated.</summary> + private AsyncTaskMethodBuilder<TResult> _methodBuilder; // mutable struct; do not make it readonly + /// <summary>The result for this builder, if it's completed before any awaits occur.</summary> + private TResult _result; + /// <summary>true if <see cref="_result"/> contains the synchronous result for the async method; otherwise, false.</summary> + private bool _haveResult; + /// <summary>true if the builder should be used for setting/getting the result; otherwise, false.</summary> + private bool _useBuilder; + + /// <summary>Creates an instance of the <see cref="AsyncValueTaskMethodBuilder{TResult}"/> struct.</summary> + /// <returns>The initialized instance.</returns> + public static AsyncValueTaskMethodBuilder<TResult> Create() => +#if CORECLR + // _methodBuilder should be initialized to AsyncTaskMethodBuilder<TResult>.Create(), but on coreclr + // that Create() is a nop, so we can just return the default here. + default(AsyncValueTaskMethodBuilder<TResult>); +#else + // corert's AsyncTaskMethodBuilder<TResult>.Create() currently does additional debugger-related + // work, so we need to delegate to it. + new AsyncValueTaskMethodBuilder<TResult>() { _methodBuilder = AsyncTaskMethodBuilder<TResult>.Create() }; +#endif + + /// <summary>Begins running the builder with the associated state machine.</summary> + /// <typeparam name="TStateMachine">The type of the state machine.</typeparam> + /// <param name="stateMachine">The state machine instance, passed by reference.</param> + public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine => + _methodBuilder.Start(ref stateMachine); // will provide the right ExecutionContext semantics + + /// <summary>Associates the builder with the specified state machine.</summary> + /// <param name="stateMachine">The state machine instance to associate with the builder.</param> + public void SetStateMachine(IAsyncStateMachine stateMachine) => _methodBuilder.SetStateMachine(stateMachine); + + /// <summary>Marks the task as successfully completed.</summary> + /// <param name="result">The result to use to complete the task.</param> + public void SetResult(TResult result) + { + if (_useBuilder) + { + _methodBuilder.SetResult(result); + } + else + { + _result = result; + _haveResult = true; + } + } + + /// <summary>Marks the task as failed and binds the specified exception to the task.</summary> + /// <param name="exception">The exception to bind to the task.</param> + public void SetException(Exception exception) => _methodBuilder.SetException(exception); + + /// <summary>Gets the task for this builder.</summary> + public ValueTask<TResult> Task + { + get + { + if (_haveResult) + { + return new ValueTask<TResult>(_result); + } + else + { + _useBuilder = true; + return new ValueTask<TResult>(_methodBuilder.Task); + } + } + } + + /// <summary>Schedules the state machine to proceed to the next action when the specified awaiter completes.</summary> + /// <typeparam name="TAwaiter">The type of the awaiter.</typeparam> + /// <typeparam name="TStateMachine">The type of the state machine.</typeparam> + /// <param name="awaiter">the awaiter</param> + /// <param name="stateMachine">The state machine.</param> + public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine + { + _useBuilder = true; + _methodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine); + } + + /// <summary>Schedules the state machine to proceed to the next action when the specified awaiter completes.</summary> + /// <typeparam name="TAwaiter">The type of the awaiter.</typeparam> + /// <typeparam name="TStateMachine">The type of the state machine.</typeparam> + /// <param name="awaiter">the awaiter</param> + /// <param name="stateMachine">The state machine.</param> + [SecuritySafeCritical] + public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine + { + _useBuilder = true; + _methodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); + } + } +} diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs new file mode 100644 index 0000000000..4ec931c4af --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs @@ -0,0 +1,71 @@ +// 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.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace System.Runtime.CompilerServices +{ + /// <summary>Provides an awaitable type that enables configured awaits on a <see cref="ValueTask{TResult}"/>.</summary> + /// <typeparam name="TResult">The type of the result produced.</typeparam> + [StructLayout(LayoutKind.Auto)] + public struct ConfiguredValueTaskAwaitable<TResult> + { + /// <summary>The wrapped <see cref="ValueTask{TResult}"/>.</summary> + private readonly ValueTask<TResult> _value; + /// <summary>true to attempt to marshal the continuation back to the original context captured; otherwise, false.</summary> + private readonly bool _continueOnCapturedContext; + + /// <summary>Initializes the awaitable.</summary> + /// <param name="value">The wrapped <see cref="ValueTask{TResult}"/>.</param> + /// <param name="continueOnCapturedContext"> + /// true to attempt to marshal the continuation back to the original synchronization context captured; otherwise, false. + /// </param> + internal ConfiguredValueTaskAwaitable(ValueTask<TResult> value, bool continueOnCapturedContext) + { + _value = value; + _continueOnCapturedContext = continueOnCapturedContext; + } + + /// <summary>Returns an awaiter for this <see cref="ConfiguredValueTaskAwaitable{TResult}"/> instance.</summary> + public ConfiguredValueTaskAwaiter GetAwaiter() => + new ConfiguredValueTaskAwaiter(_value, _continueOnCapturedContext); + + /// <summary>Provides an awaiter for a <see cref="ConfiguredValueTaskAwaitable{TResult}"/>.</summary> + [StructLayout(LayoutKind.Auto)] + public struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion + { + /// <summary>The value being awaited.</summary> + private ValueTask<TResult> _value; // Methods are called on this; avoid making it readonly so as to avoid unnecessary copies + /// <summary>The value to pass to ConfigureAwait.</summary> + private readonly bool _continueOnCapturedContext; + + /// <summary>Initializes the awaiter.</summary> + /// <param name="value">The value to be awaited.</param> + /// <param name="continueOnCapturedContext">The value to pass to ConfigureAwait.</param> + internal ConfiguredValueTaskAwaiter(ValueTask<TResult> value, bool continueOnCapturedContext) + { + _value = value; + _continueOnCapturedContext = continueOnCapturedContext; + } + + /// <summary>Gets whether the <see cref="ConfiguredValueTaskAwaitable{TResult}"/> has completed.</summary> + public bool IsCompleted => _value.IsCompleted; + + /// <summary>Gets the result of the ValueTask.</summary> + public TResult GetResult() => + _value._task == null ? + _value._result : + _value._task.GetAwaiter().GetResult(); + + /// <summary>Schedules the continuation action for the <see cref="ConfiguredValueTaskAwaitable{TResult}"/>.</summary> + public void OnCompleted(Action continuation) => + _value.AsTask().ConfigureAwait(_continueOnCapturedContext).GetAwaiter().OnCompleted(continuation); + + /// <summary>Schedules the continuation action for the <see cref="ConfiguredValueTaskAwaitable{TResult}"/>.</summary> + public void UnsafeOnCompleted(Action continuation) => + _value.AsTask().ConfigureAwait(_continueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(continuation); + } + } +} diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/CustomConstantAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/CustomConstantAttribute.cs new file mode 100644 index 0000000000..f75693eb40 --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/CompilerServices/CustomConstantAttribute.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)] + public abstract class CustomConstantAttribute : Attribute + { + public abstract Object Value { get; } + } +} diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs new file mode 100644 index 0000000000..813e6803bf --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/CompilerServices/DateTimeConstantAttribute.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)] + public sealed class DateTimeConstantAttribute : CustomConstantAttribute + { + private DateTime _date; + + public DateTimeConstantAttribute(long ticks) + { + _date = new DateTime(ticks); + } + + public override Object Value => _date; + } +} diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/DecimalConstantAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/DecimalConstantAttribute.cs new file mode 100644 index 0000000000..19db84eb43 --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/CompilerServices/DecimalConstantAttribute.cs @@ -0,0 +1,39 @@ +// 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. + +// Note: If you add a new ctor overloads you need to update ParameterInfo.RawDefaultValue + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)] + public sealed class DecimalConstantAttribute : Attribute + { + private Decimal _dec; + + [CLSCompliant(false)] + public DecimalConstantAttribute( + byte scale, + byte sign, + uint hi, + uint mid, + uint low + ) + { + _dec = new Decimal((int)low, (int)mid, (int)hi, (sign != 0), scale); + } + + public DecimalConstantAttribute( + byte scale, + byte sign, + int hi, + int mid, + int low + ) + { + _dec = new Decimal(low, mid, hi, (sign != 0), scale); + } + + public Decimal Value => _dec; + } +} diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/IntrinsicAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/IntrinsicAttribute.cs new file mode 100644 index 0000000000..dd01bacc49 --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/CompilerServices/IntrinsicAttribute.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + // Calls to methods or references to fields marked with this attribute may be replaced at + // some call sites with jit intrinsic expansions. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Field, Inherited = false)] + internal sealed class IntrinsicAttribute : Attribute + { + } +} diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/MethodImplAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/MethodImplAttribute.cs new file mode 100644 index 0000000000..8e8f93c268 --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/CompilerServices/MethodImplAttribute.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.CompilerServices +{ + // Custom attribute to specify additional method properties. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)] + sealed public class MethodImplAttribute : Attribute + { + public MethodCodeType MethodCodeType; + + public MethodImplAttribute(MethodImplOptions methodImplOptions) + { + Value = methodImplOptions; + } + + public MethodImplAttribute(short value) + { + Value = (MethodImplOptions)value; + } + + public MethodImplAttribute() + { + } + + public MethodImplOptions Value { get; } + } +} diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/RuntimeWrappedException.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/RuntimeWrappedException.cs new file mode 100644 index 0000000000..e4af9be678 --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/CompilerServices/RuntimeWrappedException.cs @@ -0,0 +1,33 @@ +// 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.Runtime.Serialization; + +namespace System.Runtime.CompilerServices +{ + /// <summary> + /// Exception used to wrap all non-CLS compliant exceptions. + /// </summary> + public sealed class RuntimeWrappedException : Exception + { + private Object _wrappedException; // EE expects this name + + // Not an api but has to be public as System.Linq.Expression invokes this through Reflection when an expression + // throws an object that doesn't derive from Exception. + public RuntimeWrappedException(Object thrownObject) + : base(SR.RuntimeWrappedException) + { + HResult = HResults.COR_E_RUNTIMEWRAPPED; + _wrappedException = thrownObject; + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + } + + public Object WrappedException => _wrappedException; + } +} diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs new file mode 100644 index 0000000000..c419482521 --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs @@ -0,0 +1,37 @@ +// 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.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace System.Runtime.CompilerServices +{ + /// <summary>Provides an awaiter for a <see cref="ValueTask{TResult}"/>.</summary> + public struct ValueTaskAwaiter<TResult> : ICriticalNotifyCompletion + { + /// <summary>The value being awaited.</summary> + private ValueTask<TResult> _value; // Methods are called on this; avoid making it readonly so as to avoid unnecessary copies + + /// <summary>Initializes the awaiter.</summary> + /// <param name="value">The value to be awaited.</param> + internal ValueTaskAwaiter(ValueTask<TResult> value) => _value = value; + + /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> has completed.</summary> + public bool IsCompleted => _value.IsCompleted; + + /// <summary>Gets the result of the ValueTask.</summary> + public TResult GetResult() => + _value._task == null ? + _value._result : + _value._task.GetAwaiter().GetResult(); + + /// <summary>Schedules the continuation action for this ValueTask.</summary> + public void OnCompleted(Action continuation) => + _value.AsTask().ConfigureAwait(continueOnCapturedContext: true).GetAwaiter().OnCompleted(continuation); + + /// <summary>Schedules the continuation action for this ValueTask.</summary> + public void UnsafeOnCompleted(Action continuation) => + _value.AsTask().ConfigureAwait(continueOnCapturedContext: true).GetAwaiter().UnsafeOnCompleted(continuation); + } +} diff --git a/src/mscorlib/shared/System/Runtime/ExceptionServices/HandleProcessCorruptedStateExceptionsAttribute.cs b/src/mscorlib/shared/System/Runtime/ExceptionServices/HandleProcessCorruptedStateExceptionsAttribute.cs new file mode 100644 index 0000000000..cc1bc81e5a --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/ExceptionServices/HandleProcessCorruptedStateExceptionsAttribute.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.ExceptionServices +{ + // This attribute can be applied to methods to indicate that ProcessCorruptedState + // Exceptions should be delivered to them. + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class HandleProcessCorruptedStateExceptionsAttribute : Attribute + { + public HandleProcessCorruptedStateExceptionsAttribute() + { + } + } +} diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/BestFitMappingAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/BestFitMappingAttribute.cs new file mode 100644 index 0000000000..4ebee1538c --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/InteropServices/BestFitMappingAttribute.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)] + public sealed class BestFitMappingAttribute : Attribute + { + public BestFitMappingAttribute(bool BestFitMapping) + { + this.BestFitMapping = BestFitMapping; + } + + public bool BestFitMapping { get; } + + public bool ThrowOnUnmappableChar; + } +} diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/DefaultCharSetAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/DefaultCharSetAttribute.cs new file mode 100644 index 0000000000..7a486f7017 --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/InteropServices/DefaultCharSetAttribute.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Module, Inherited = false)] + public sealed class DefaultCharSetAttribute : Attribute + { + public DefaultCharSetAttribute(CharSet charSet) + { + CharSet = charSet; + } + + public CharSet CharSet { get; } + } +} diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/DefaultDllImportSearchPathsAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/DefaultDllImportSearchPathsAttribute.cs new file mode 100644 index 0000000000..1ff27fbbd5 --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/InteropServices/DefaultDllImportSearchPathsAttribute.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Method, AllowMultiple = false)] + public sealed class DefaultDllImportSearchPathsAttribute : Attribute + { + public DefaultDllImportSearchPathsAttribute(DllImportSearchPath paths) + { + Paths = paths; + } + + public DllImportSearchPath Paths { get; } + } +} diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/DllImportAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/DllImportAttribute.cs new file mode 100644 index 0000000000..97f870d49c --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/InteropServices/DllImportAttribute.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + public sealed class DllImportAttribute : Attribute + { + public DllImportAttribute(string dllName) + { + Value = dllName; + } + + public string Value { get; } + + public string EntryPoint; + public CharSet CharSet; + public bool SetLastError; + public bool ExactSpelling; + public CallingConvention CallingConvention; + public bool BestFitMapping; + public bool PreserveSig; + public bool ThrowOnUnmappableChar; + } +} diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/DllImportSearchPath.cs b/src/mscorlib/shared/System/Runtime/InteropServices/DllImportSearchPath.cs new file mode 100644 index 0000000000..8dbdb40be9 --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/InteropServices/DllImportSearchPath.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices +{ + [Flags] + public enum DllImportSearchPath + { + UseDllDirectoryForDependencies = 0x100, + ApplicationDirectory = 0x200, + UserDirectories = 0x400, + System32 = 0x800, + SafeDirectories = 0x1000, + AssemblyDirectory = 0x2, + LegacyBehavior = 0x0 + } +} diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/ExternalException.cs b/src/mscorlib/shared/System/Runtime/InteropServices/ExternalException.cs index 81b9a46928..0861d19362 100644 --- a/src/mscorlib/shared/System/Runtime/InteropServices/ExternalException.cs +++ b/src/mscorlib/shared/System/Runtime/InteropServices/ExternalException.cs @@ -26,19 +26,19 @@ namespace System.Runtime.InteropServices public ExternalException() : base(SR.Arg_ExternalException) { - HResult = __HResults.E_FAIL; + HResult = HResults.E_FAIL; } public ExternalException(string message) : base(message) { - HResult = __HResults.E_FAIL; + HResult = HResults.E_FAIL; } public ExternalException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.E_FAIL; + HResult = HResults.E_FAIL; } public ExternalException(string message, int errorCode) diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/FieldOffsetAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/FieldOffsetAttribute.cs new file mode 100644 index 0000000000..27e1097749 --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/InteropServices/FieldOffsetAttribute.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Field, Inherited = false)] + public sealed class FieldOffsetAttribute : Attribute + { + public FieldOffsetAttribute(int offset) + { + Value = offset; + } + + public int Value { get; } + } +} diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/InAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/InAttribute.cs new file mode 100644 index 0000000000..39f5a958bc --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/InteropServices/InAttribute.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + public sealed class InAttribute : Attribute + { + public InAttribute() + { + } + } +} diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/MarshalAsAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/MarshalAsAttribute.cs new file mode 100644 index 0000000000..4a64050ed1 --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/InteropServices/MarshalAsAttribute.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.ReturnValue, Inherited = false)] + public sealed class MarshalAsAttribute : Attribute + { + public MarshalAsAttribute(UnmanagedType unmanagedType) + { + Value = unmanagedType; + } + public MarshalAsAttribute(short unmanagedType) + { + Value = (UnmanagedType)unmanagedType; + } + + public UnmanagedType Value { get; } + + // Fields used with SubType = SafeArray. + public VarEnum SafeArraySubType; + public Type SafeArrayUserDefinedSubType; + + // Field used with iid_is attribute (interface pointers). + public int IidParameterIndex; + + // Fields used with SubType = ByValArray and LPArray. + // Array size = parameter(PI) * PM + C + public UnmanagedType ArraySubType; + public short SizeParamIndex; // param index PI + public int SizeConst; // constant C + + // Fields used with SubType = CustomMarshaler + public string MarshalType; // Name of marshaler class + public Type MarshalTypeRef; // Type of marshaler class + public string MarshalCookie; // cookie to pass to marshaler + } +} diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/OptionalAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/OptionalAttribute.cs new file mode 100644 index 0000000000..5ac75d7b3e --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/InteropServices/OptionalAttribute.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + public sealed class OptionalAttribute : Attribute + { + public OptionalAttribute() + { + } + } +} diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/OutAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/OutAttribute.cs new file mode 100644 index 0000000000..338ceac91e --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/InteropServices/OutAttribute.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + public sealed class OutAttribute : Attribute + { + public OutAttribute() + { + } + } +} diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/PreserveSigAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/PreserveSigAttribute.cs new file mode 100644 index 0000000000..464e1abcbe --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/InteropServices/PreserveSigAttribute.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + public sealed class PreserveSigAttribute : Attribute + { + public PreserveSigAttribute() + { + } + } +} diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/StructLayoutAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/StructLayoutAttribute.cs new file mode 100644 index 0000000000..c4cce9956e --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/InteropServices/StructLayoutAttribute.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)] + public sealed class StructLayoutAttribute : Attribute + { + public StructLayoutAttribute(LayoutKind layoutKind) + { + Value = layoutKind; + } + + public StructLayoutAttribute(short layoutKind) + { + Value = (LayoutKind)layoutKind; + } + + public LayoutKind Value { get; } + + public int Pack; + public int Size; + public CharSet CharSet; + } +} diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/UnmanagedFunctionPointerAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/UnmanagedFunctionPointerAttribute.cs index 2d69c95afe..c4f96903ee 100644 --- a/src/mscorlib/shared/System/Runtime/InteropServices/UnmanagedFunctionPointerAttribute.cs +++ b/src/mscorlib/shared/System/Runtime/InteropServices/UnmanagedFunctionPointerAttribute.cs @@ -7,11 +7,6 @@ namespace System.Runtime.InteropServices [AttributeUsage(AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] public sealed class UnmanagedFunctionPointerAttribute : Attribute { - public bool BestFitMapping; - public bool SetLastError; - public bool ThrowOnUnmappableChar; - public CharSet CharSet; - public UnmanagedFunctionPointerAttribute() { CallingConvention = CallingConvention.Winapi; @@ -23,5 +18,10 @@ namespace System.Runtime.InteropServices } public CallingConvention CallingConvention { get; } + + public bool BestFitMapping; + public bool SetLastError; + public bool ThrowOnUnmappableChar; + public CharSet CharSet; } } diff --git a/src/mscorlib/shared/System/Runtime/Serialization/SerializationException.cs b/src/mscorlib/shared/System/Runtime/Serialization/SerializationException.cs index bfe3313659..3e0af092c0 100644 --- a/src/mscorlib/shared/System/Runtime/Serialization/SerializationException.cs +++ b/src/mscorlib/shared/System/Runtime/Serialization/SerializationException.cs @@ -15,19 +15,19 @@ namespace System.Runtime.Serialization public SerializationException() : base(s_nullMessage) { - HResult = __HResults.COR_E_SERIALIZATION; + HResult = HResults.COR_E_SERIALIZATION; } public SerializationException(String message) : base(message) { - HResult = __HResults.COR_E_SERIALIZATION; + HResult = HResults.COR_E_SERIALIZATION; } public SerializationException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_SERIALIZATION; + HResult = HResults.COR_E_SERIALIZATION; } protected SerializationException(SerializationInfo info, StreamingContext context) diff --git a/src/mscorlib/shared/System/SByte.cs b/src/mscorlib/shared/System/SByte.cs new file mode 100644 index 0000000000..c984767336 --- /dev/null +++ b/src/mscorlib/shared/System/SByte.cs @@ -0,0 +1,328 @@ +// 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.Contracts; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [CLSCompliant(false)] [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct SByte : IComparable, IConvertible, IFormattable, IComparable<SByte>, IEquatable<SByte> + { + private sbyte m_value; // Do not rename (binary serialization) + + // The maximum value that a Byte may represent: 127. + public const sbyte MaxValue = (sbyte)0x7F; + + // The minimum value that a Byte may represent: -128. + public const sbyte MinValue = unchecked((sbyte)0x80); + + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns a value less than zero if this object + // null is considered to be less than any instance. + // If object is not of type SByte, this method throws an ArgumentException. + // + public int CompareTo(Object obj) + { + if (obj == null) + { + return 1; + } + if (!(obj is SByte)) + { + throw new ArgumentException(SR.Arg_MustBeSByte); + } + return m_value - ((SByte)obj).m_value; + } + + public int CompareTo(SByte value) + { + return m_value - value; + } + + // Determines whether two Byte objects are equal. + public override bool Equals(Object obj) + { + if (!(obj is SByte)) + { + return false; + } + return m_value == ((SByte)obj).m_value; + } + + [NonVersionable] + public bool Equals(SByte obj) + { + return m_value == obj; + } + + // Gets a hash code for this instance. + public override int GetHashCode() + { + return ((int)m_value ^ (int)m_value << 8); + } + + + // Provides a string representation of a byte. + public override String ToString() + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatInt32(m_value, null, NumberFormatInfo.CurrentInfo); + } + + public String ToString(IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatInt32(m_value, null, NumberFormatInfo.GetInstance(provider)); + } + + public String ToString(String format) + { + Contract.Ensures(Contract.Result<String>() != null); + return ToString(format, NumberFormatInfo.CurrentInfo); + } + + public String ToString(String format, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return ToString(format, NumberFormatInfo.GetInstance(provider)); + } + + private String ToString(String format, NumberFormatInfo info) + { + Contract.Ensures(Contract.Result<String>() != null); + + if (m_value < 0 && format != null && format.Length > 0 && (format[0] == 'X' || format[0] == 'x')) + { + uint temp = (uint)(m_value & 0x000000FF); + return Number.FormatUInt32(temp, format, info); + } + return Number.FormatInt32(m_value, format, info); + } + + [CLSCompliant(false)] + public static sbyte Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + } + + [CLSCompliant(false)] + public static sbyte Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo); + } + + [CLSCompliant(false)] + public static sbyte Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + } + + // Parses a signed byte from a String in the given style. If + // a NumberFormatInfo isn't specified, the current culture's + // NumberFormatInfo is assumed. + // + [CLSCompliant(false)] + public static sbyte Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static sbyte Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Parse(s, style, NumberFormatInfo.GetInstance(provider)); + } + + private static sbyte Parse(String s, NumberStyles style, NumberFormatInfo info) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse(s.AsReadOnlySpan(), style, info); + } + + private static sbyte Parse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info) + { + int i = 0; + try + { + i = Number.ParseInt32(s, style, info); + } + catch (OverflowException e) + { + throw new OverflowException(SR.Overflow_SByte, e); + } + + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { // We are parsing a hexadecimal number + if ((i < 0) || i > Byte.MaxValue) + { + throw new OverflowException(SR.Overflow_SByte); + } + return (sbyte)i; + } + + if (i < MinValue || i > MaxValue) throw new OverflowException(SR.Overflow_SByte); + return (sbyte)i; + } + + [CLSCompliant(false)] + public static bool TryParse(String s, out SByte result) + { + if (s == null) + { + result = 0; + return false; + } + + return TryParse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + [CLSCompliant(false)] + public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out SByte result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return TryParse(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result); + } + + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan<char> s, out sbyte result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + private static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out SByte result) + { + result = 0; + int i; + if (!Number.TryParseInt32(s, style, info, out i)) + { + return false; + } + + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { // We are parsing a hexadecimal number + if ((i < 0) || i > Byte.MaxValue) + { + return false; + } + result = (sbyte)i; + return true; + } + + if (i < MinValue || i > MaxValue) + { + return false; + } + result = (sbyte)i; + return true; + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.SByte; + } + + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return Convert.ToChar(m_value); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return m_value; + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return m_value; + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "SByte", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/src/mscorlib/shared/System/Security/SecurityException.cs b/src/mscorlib/shared/System/Security/SecurityException.cs index 538f475343..2866a4780c 100644 --- a/src/mscorlib/shared/System/Security/SecurityException.cs +++ b/src/mscorlib/shared/System/Security/SecurityException.cs @@ -12,32 +12,32 @@ namespace System.Security public SecurityException() : base(SR.Arg_SecurityException) { - HResult = __HResults.COR_E_SECURITY; + HResult = HResults.COR_E_SECURITY; } public SecurityException(string message) : base(message) { - HResult = __HResults.COR_E_SECURITY; + HResult = HResults.COR_E_SECURITY; } public SecurityException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_SECURITY; + HResult = HResults.COR_E_SECURITY; } public SecurityException(string message, Type type) : base(message) { - HResult = __HResults.COR_E_SECURITY; + HResult = HResults.COR_E_SECURITY; PermissionType = type; } public SecurityException(string message, Type type, string state) : base(message) { - HResult = __HResults.COR_E_SECURITY; + HResult = HResults.COR_E_SECURITY; PermissionType = type; PermissionState = state; } diff --git a/src/mscorlib/shared/System/Security/VerificationException.cs b/src/mscorlib/shared/System/Security/VerificationException.cs index ea5a75906e..bd095f3df4 100644 --- a/src/mscorlib/shared/System/Security/VerificationException.cs +++ b/src/mscorlib/shared/System/Security/VerificationException.cs @@ -11,19 +11,19 @@ namespace System.Security public VerificationException() : base(SR.Verification_Exception) { - HResult = __HResults.COR_E_VERIFICATION; + HResult = HResults.COR_E_VERIFICATION; } public VerificationException(string message) : base(message) { - HResult = __HResults.COR_E_VERIFICATION; + HResult = HResults.COR_E_VERIFICATION; } public VerificationException(string message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_VERIFICATION; + HResult = HResults.COR_E_VERIFICATION; } protected VerificationException(SerializationInfo info, StreamingContext context) diff --git a/src/mscorlib/shared/System/SerializableAttribute.cs b/src/mscorlib/shared/System/SerializableAttribute.cs new file mode 100644 index 0000000000..c256931373 --- /dev/null +++ b/src/mscorlib/shared/System/SerializableAttribute.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Delegate, Inherited = false)] + public sealed class SerializableAttribute : Attribute + { + public SerializableAttribute() { } + } +} diff --git a/src/mscorlib/shared/System/Single.cs b/src/mscorlib/shared/System/Single.cs new file mode 100644 index 0000000000..0be8cfbc37 --- /dev/null +++ b/src/mscorlib/shared/System/Single.cs @@ -0,0 +1,442 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: A wrapper class for the primitive type float. +** +** +===========================================================*/ + +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct Single : IComparable, IConvertible, IFormattable, IComparable<Single>, IEquatable<Single> + { + private float m_value; // Do not rename (binary serialization) + + // + // Public constants + // + public const float MinValue = (float)-3.40282346638528859e+38; + public const float Epsilon = (float)1.4e-45; + public const float MaxValue = (float)3.40282346638528859e+38; + public const float PositiveInfinity = (float)1.0 / (float)0.0; + public const float NegativeInfinity = (float)-1.0 / (float)0.0; + public const float NaN = (float)0.0 / (float)0.0; + + // We use this explicit definition to avoid the confusion between 0.0 and -0.0. + internal const float NegativeZero = (float)-0.0; + + /// <summary>Determines whether the specified value is finite (zero, subnormal, or normal).</summary> + [Pure] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFinite(float f) + { + var bits = BitConverter.SingleToInt32Bits(f); + return (bits & 0x7FFFFFFF) < 0x7F800000; + } + + /// <summary>Determines whether the specified value is infinite.</summary> + [Pure] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsInfinity(float f) + { + var bits = BitConverter.SingleToInt32Bits(f); + return (bits & 0x7FFFFFFF) == 0x7F800000; + } + + /// <summary>Determines whether the specified value is NaN.</summary> + [Pure] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsNaN(float f) + { + var bits = BitConverter.SingleToInt32Bits(f); + return (bits & 0x7FFFFFFF) > 0x7F800000; + } + + /// <summary>Determines whether the specified value is negative.</summary> + [Pure] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsNegative(float f) + { + var bits = unchecked((uint)BitConverter.SingleToInt32Bits(f)); + return (bits & 0x80000000) == 0x80000000; + } + + /// <summary>Determines whether the specified value is negative infinity.</summary> + [Pure] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsNegativeInfinity(float f) + { + return (f == float.NegativeInfinity); + } + + /// <summary>Determines whether the specified value is normal.</summary> + [Pure] + [NonVersionable] + // This is probably not worth inlining, it has branches and should be rarely called + public unsafe static bool IsNormal(float f) + { + var bits = BitConverter.SingleToInt32Bits(f); + bits &= 0x7FFFFFFF; + return (bits < 0x7F800000) && (bits != 0) && ((bits & 0x7F800000) != 0); + } + + /// <summary>Determines whether the specified value is positive infinity.</summary> + [Pure] + [NonVersionable] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe static bool IsPositiveInfinity(float f) + { + return (f == float.PositiveInfinity); + } + + /// <summary>Determines whether the specified value is subnormal.</summary> + [Pure] + [NonVersionable] + // This is probably not worth inlining, it has branches and should be rarely called + public unsafe static bool IsSubnormal(float f) + { + var bits = BitConverter.SingleToInt32Bits(f); + bits &= 0x7FFFFFFF; + return (bits < 0x7F800000) && (bits != 0) && ((bits & 0x7F800000) == 0); + } + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns a value less than zero if this object + // null is considered to be less than any instance. + // If object is not of type Single, this method throws an ArgumentException. + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + if (value is Single) + { + float f = (float)value; + if (m_value < f) return -1; + if (m_value > f) return 1; + if (m_value == f) return 0; + + // At least one of the values is NaN. + if (IsNaN(m_value)) + return (IsNaN(f) ? 0 : -1); + else // f is NaN. + return 1; + } + throw new ArgumentException(SR.Arg_MustBeSingle); + } + + + public int CompareTo(Single value) + { + if (m_value < value) return -1; + if (m_value > value) return 1; + if (m_value == value) return 0; + + // At least one of the values is NaN. + if (IsNaN(m_value)) + return (IsNaN(value) ? 0 : -1); + else // f is NaN. + return 1; + } + + [NonVersionable] + public static bool operator ==(Single left, Single right) + { + return left == right; + } + + [NonVersionable] + public static bool operator !=(Single left, Single right) + { + return left != right; + } + + [NonVersionable] + public static bool operator <(Single left, Single right) + { + return left < right; + } + + [NonVersionable] + public static bool operator >(Single left, Single right) + { + return left > right; + } + + [NonVersionable] + public static bool operator <=(Single left, Single right) + { + return left <= right; + } + + [NonVersionable] + public static bool operator >=(Single left, Single right) + { + return left >= right; + } + + public override bool Equals(Object obj) + { + if (!(obj is Single)) + { + return false; + } + float temp = ((Single)obj).m_value; + if (temp == m_value) + { + return true; + } + + return IsNaN(temp) && IsNaN(m_value); + } + + public bool Equals(Single obj) + { + if (obj == m_value) + { + return true; + } + + return IsNaN(obj) && IsNaN(m_value); + } + + public unsafe override int GetHashCode() + { + float f = m_value; + if (f == 0) + { + // Ensure that 0 and -0 have the same hash code + return 0; + } + int v = *(int*)(&f); + return v; + } + + public override String ToString() + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatSingle(m_value, null, NumberFormatInfo.CurrentInfo); + } + + public String ToString(IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatSingle(m_value, null, NumberFormatInfo.GetInstance(provider)); + } + + public String ToString(String format) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatSingle(m_value, format, NumberFormatInfo.CurrentInfo); + } + + public String ToString(String format, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatSingle(m_value, format, NumberFormatInfo.GetInstance(provider)); + } + + // Parses a float from a String in the given style. If + // a NumberFormatInfo isn't specified, the current culture's + // NumberFormatInfo is assumed. + // + // This method will not throw an OverflowException, but will return + // PositiveInfinity or NegativeInfinity for a number that is too + // large or too small. + // + public static float Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseSingle(s.AsReadOnlySpan(), NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo); + } + + public static float Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseSingle(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo); + } + + public static float Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseSingle(s.AsReadOnlySpan(), NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.GetInstance(provider)); + } + + public static float Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseSingle(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider)); + } + + public static float Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return Number.ParseSingle(s, style, NumberFormatInfo.GetInstance(provider)); + } + + public static Boolean TryParse(String s, out Single result) + { + if (s == null) + { + result = 0; + return false; + } + + return TryParse(s.AsReadOnlySpan(), NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo, out result); + } + + public static Boolean TryParse(String s, NumberStyles style, IFormatProvider provider, out Single result) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + + if (s == null) + { + result = 0; + return false; + } + + return TryParse(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result); + } + + public static Boolean TryParse(ReadOnlySpan<char> s, out Single result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + private static Boolean TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out Single result) + { + bool success = Number.TryParseSingle(s, style, info, out result); + if (!success) + { + ReadOnlySpan<char> sTrim = s.Trim(); + if (StringSpanHelpers.Equals(sTrim, info.PositiveInfinitySymbol)) + { + result = PositiveInfinity; + } + else if (StringSpanHelpers.Equals(sTrim, info.NegativeInfinitySymbol)) + { + result = NegativeInfinity; + } + else if (StringSpanHelpers.Equals(sTrim, info.NaNSymbol)) + { + result = NaN; + } + else + { + return false; // We really failed + } + } + return true; + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.Single; + } + + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Single", "Char")); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return m_value; + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Single", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/src/mscorlib/shared/System/Span.NonGeneric.cs b/src/mscorlib/shared/System/Span.NonGeneric.cs index 62b6faa8ec..0e8bfaa994 100644 --- a/src/mscorlib/shared/System/Span.NonGeneric.cs +++ b/src/mscorlib/shared/System/Span.NonGeneric.cs @@ -118,7 +118,7 @@ namespace System /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="text"/> is a null /// reference (Nothing in Visual Basic).</exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan<char> AsSpan(this string text) + public static ReadOnlySpan<char> AsReadOnlySpan(this string text) { if (text == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); diff --git a/src/mscorlib/shared/System/Span.cs b/src/mscorlib/shared/System/Span.cs index 88612eb28b..de6787d09d 100644 --- a/src/mscorlib/shared/System/Span.cs +++ b/src/mscorlib/shared/System/Span.cs @@ -2,11 +2,10 @@ // 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.ComponentModel; using System.Diagnostics; -using System.Runtime; using System.Runtime.CompilerServices; -using EditorBrowsableState = System.ComponentModel.EditorBrowsableState; -using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute; +using System.Runtime.Versioning; #pragma warning disable 0809 //warning CS0809: Obsolete member 'Span<T>.Equals(object)' overrides non-obsolete member 'object.Equals(object)' @@ -22,7 +21,9 @@ namespace System /// 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> + [IsReadOnly] [IsByRefLike] + [NonVersionable] public struct Span<T> { /// <summary>A byref or a native ptr.</summary> @@ -54,32 +55,6 @@ namespace System /// <summary> /// Creates a new span over the portion of the target array beginning - /// at 'start' index and covering the remainder of the array. - /// </summary> - /// <param name="array">The target array.</param> - /// <param name="start">The index at which to begin the span.</param> - /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null - /// reference (Nothing in Visual Basic).</exception> - /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception> - /// <exception cref="System.ArgumentOutOfRangeException"> - /// Thrown when the specified <paramref name="start"/> is not in the range (<0 or >=Length). - /// </exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span(T[] array, int start) - { - if (array == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - if (default(T) == null && array.GetType() != typeof(T[])) - ThrowHelper.ThrowArrayTypeMismatchException(); - if ((uint)start > (uint)array.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(); - - _pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start)); - _length = array.Length - start; - } - - /// <summary> - /// Creates a new span over the portion of the target array beginning /// at 'start' index and ending at 'end' index (exclusive). /// </summary> /// <param name="array">The target array.</param> @@ -142,6 +117,7 @@ namespace System /// <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)] + [EditorBrowsable(EditorBrowsableState.Never)] public static Span<T> DangerousCreate(object obj, ref T objectData, int length) => new Span<T>(ref objectData, length); // Constructor for internal use only. @@ -158,6 +134,8 @@ namespace System /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. /// </summary> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [EditorBrowsable(EditorBrowsableState.Never)] public ref T DangerousGetPinnableReference() { return ref _pointer.Value; @@ -166,12 +144,26 @@ namespace System /// <summary> /// The number of items in the span. /// </summary> - public int Length => _length; + public int Length + { + [NonVersionable] + get + { + return _length; + } + } /// <summary> /// Returns true if Length is 0. /// </summary> - public bool IsEmpty => _length == 0; + public bool IsEmpty + { + [NonVersionable] + get + { + return _length == 0; + } + } /// <summary> /// Returns a reference to specified element of the Span. @@ -194,6 +186,7 @@ namespace System [Intrinsic] #endif [MethodImpl(MethodImplOptions.AggressiveInlining)] + [NonVersionable] get { if ((uint)index >= (uint)_length) diff --git a/src/mscorlib/shared/System/StackOverflowException.cs b/src/mscorlib/shared/System/StackOverflowException.cs index fb0e88246c..8b21eb769a 100644 --- a/src/mscorlib/shared/System/StackOverflowException.cs +++ b/src/mscorlib/shared/System/StackOverflowException.cs @@ -20,19 +20,19 @@ namespace System public StackOverflowException() : base(SR.Arg_StackOverflowException) { - HResult = __HResults.COR_E_STACKOVERFLOW; + HResult = HResults.COR_E_STACKOVERFLOW; } public StackOverflowException(String message) : base(message) { - HResult = __HResults.COR_E_STACKOVERFLOW; + HResult = HResults.COR_E_STACKOVERFLOW; } public StackOverflowException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_STACKOVERFLOW; + HResult = HResults.COR_E_STACKOVERFLOW; } } } diff --git a/src/mscorlib/shared/System/StringSpanHelpers.cs b/src/mscorlib/shared/System/StringSpanHelpers.cs new file mode 100644 index 0000000000..bdfd965641 --- /dev/null +++ b/src/mscorlib/shared/System/StringSpanHelpers.cs @@ -0,0 +1,93 @@ +// 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.Globalization; + +namespace System +{ + /// <summary>Helpers for string-like operations on spans of chars.</summary> + internal static class StringSpanHelpers + { + // TODO https://github.com/dotnet/corefx/issues/21395: Provide public, efficient implementations + + public static bool Equals(this ReadOnlySpan<char> left, ReadOnlySpan<char> right, StringComparison comparisonType) => + comparisonType == StringComparison.Ordinal ? Equals(left, right) : + comparisonType == StringComparison.OrdinalIgnoreCase ? EqualsOrdinalIgnoreCase(left, right) : + throw new ArgumentOutOfRangeException(nameof(comparisonType)); + + public static bool Equals(this ReadOnlySpan<char> left, string right) => + Equals(left, right.AsReadOnlySpan()); + + public static bool Equals(this ReadOnlySpan<char> left, ReadOnlySpan<char> right) + { + if (left.Length != right.Length) + { + return false; + } + + for (int i = 0; i < left.Length; i++) + { + if (left[i] != right[i]) + { + return false; + } + } + + return true; + } + + private static bool EqualsOrdinalIgnoreCase(this ReadOnlySpan<char> left, ReadOnlySpan<char> right) + { + if (left.Length != right.Length) + { + return false; + } + + for (int i = 0; i < left.Length; i++) + { + char x = left[i], y = right[i]; + if (x != y && + TextInfo.ToUpperAsciiInvariant(x) != TextInfo.ToUpperAsciiInvariant(y)) + { + return false; + } + } + + return true; + } + + public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> source) + { + int startIndex = 0, endIndex = source.Length - 1; + + while (startIndex <= endIndex && char.IsWhiteSpace(source[startIndex])) + { + startIndex++; + } + + while (endIndex >= startIndex && char.IsWhiteSpace(source[endIndex])) + { + endIndex--; + } + + return source.Slice(startIndex, endIndex - startIndex + 1); + } + + public static int IndexOf(this ReadOnlySpan<char> source, char value) => + IndexOf(source, value, 0); + + public static int IndexOf(this ReadOnlySpan<char> source, char value, int startIndex) + { + for (int i = startIndex; i < source.Length; i++) + { + if (source[i] == value) + { + return i; + } + } + + return -1; + } + } +} diff --git a/src/mscorlib/shared/System/SystemException.cs b/src/mscorlib/shared/System/SystemException.cs index d5bcde7efc..64a4b9ca31 100644 --- a/src/mscorlib/shared/System/SystemException.cs +++ b/src/mscorlib/shared/System/SystemException.cs @@ -11,19 +11,19 @@ namespace System public SystemException() : base(SR.Arg_SystemException) { - HResult = __HResults.COR_E_SYSTEM; + HResult = HResults.COR_E_SYSTEM; } public SystemException(String message) : base(message) { - HResult = __HResults.COR_E_SYSTEM; + HResult = HResults.COR_E_SYSTEM; } public SystemException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_SYSTEM; + HResult = HResults.COR_E_SYSTEM; } protected SystemException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/Text/Decoder.cs b/src/mscorlib/shared/System/Text/Decoder.cs index f109f3db49..82d33d518c 100644 --- a/src/mscorlib/shared/System/Text/Decoder.cs +++ b/src/mscorlib/shared/System/Text/Decoder.cs @@ -134,6 +134,14 @@ namespace System.Text return GetCharCount(arrbyte, 0, count); } + public virtual unsafe int GetCharCount(ReadOnlySpan<byte> bytes, bool flush) + { + fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference()) + { + return GetCharCount(bytesPtr, bytes.Length, flush); + } + } + // Decodes a range of bytes in a byte array into a range of characters // in a character array. The method decodes byteCount bytes from // bytes starting at index byteIndex, storing the resulting @@ -220,6 +228,15 @@ namespace System.Text return charCount; } + public virtual unsafe int GetChars(ReadOnlySpan<byte> bytes, Span<char> chars, bool flush) + { + fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference()) + fixed (char* charsPtr = &chars.DangerousGetPinnableReference()) + { + return GetChars(bytesPtr, bytes.Length, charsPtr, chars.Length, flush); + } + } + // This method is used when the output buffer might not be large enough. // It will decode until it runs out of bytes, and then it will return // true if it the entire input was converted. In either case it @@ -326,5 +343,14 @@ namespace System.Text // Oops, we didn't have anything, we'll have to throw an overflow throw new ArgumentException(SR.Argument_ConversionOverflow); } + + public virtual unsafe void Convert(ReadOnlySpan<byte> bytes, Span<char> chars, bool flush, out int bytesUsed, out int charsUsed, out bool completed) + { + fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference()) + fixed (char* charsPtr = &chars.DangerousGetPinnableReference()) + { + Convert(bytesPtr, bytes.Length, charsPtr, chars.Length, flush, out bytesUsed, out charsUsed, out completed); + } + } } } diff --git a/src/mscorlib/shared/System/Text/DecoderExceptionFallback.cs b/src/mscorlib/shared/System/Text/DecoderExceptionFallback.cs index b465aa68ef..26d604a919 100644 --- a/src/mscorlib/shared/System/Text/DecoderExceptionFallback.cs +++ b/src/mscorlib/shared/System/Text/DecoderExceptionFallback.cs @@ -106,19 +106,19 @@ namespace System.Text public DecoderFallbackException() : base(SR.Arg_ArgumentException) { - HResult = __HResults.COR_E_ARGUMENT; + HResult = HResults.COR_E_ARGUMENT; } public DecoderFallbackException(String message) : base(message) { - HResult = __HResults.COR_E_ARGUMENT; + HResult = HResults.COR_E_ARGUMENT; } public DecoderFallbackException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_ARGUMENT; + HResult = HResults.COR_E_ARGUMENT; } public DecoderFallbackException(String message, byte[] bytesUnknown, int index) diff --git a/src/mscorlib/shared/System/Text/Encoder.cs b/src/mscorlib/shared/System/Text/Encoder.cs index b0602f3cce..79a633f1ff 100644 --- a/src/mscorlib/shared/System/Text/Encoder.cs +++ b/src/mscorlib/shared/System/Text/Encoder.cs @@ -132,6 +132,14 @@ namespace System.Text return GetByteCount(arrChar, 0, count, flush); } + public virtual unsafe int GetByteCount(ReadOnlySpan<char> chars, bool flush) + { + fixed (char* charsPtr = &chars.DangerousGetPinnableReference()) + { + return GetByteCount(charsPtr, chars.Length, flush); + } + } + // Encodes a range of characters in a character array into a range of bytes // in a byte array. The method encodes charCount characters from // chars starting at index charIndex, storing the resulting @@ -214,6 +222,15 @@ namespace System.Text return byteCount; } + public virtual unsafe int GetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, bool flush) + { + fixed (char* charsPtr = &chars.DangerousGetPinnableReference()) + fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference()) + { + return GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length, flush); + } + } + // This method is used to avoid running out of output buffer space. // It will encode until it runs out of chars, and then it will return // true if it the entire input was converted. In either case it @@ -320,6 +337,15 @@ namespace System.Text // Oops, we didn't have anything, we'll have to throw an overflow throw new ArgumentException(SR.Argument_ConversionOverflow); } + + public virtual unsafe void Convert(ReadOnlySpan<char> chars, Span<byte> bytes, bool flush, out int charsUsed, out int bytesUsed, out bool completed) + { + fixed (char* charsPtr = &chars.DangerousGetPinnableReference()) + fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference()) + { + Convert(charsPtr, chars.Length, bytesPtr, bytes.Length, flush, out charsUsed, out bytesUsed, out completed); + } + } } } diff --git a/src/mscorlib/shared/System/Text/EncoderExceptionFallback.cs b/src/mscorlib/shared/System/Text/EncoderExceptionFallback.cs index 98eff25b94..71426e5f31 100644 --- a/src/mscorlib/shared/System/Text/EncoderExceptionFallback.cs +++ b/src/mscorlib/shared/System/Text/EncoderExceptionFallback.cs @@ -108,19 +108,19 @@ namespace System.Text public EncoderFallbackException() : base(SR.Arg_ArgumentException) { - HResult = __HResults.COR_E_ARGUMENT; + HResult = HResults.COR_E_ARGUMENT; } public EncoderFallbackException(String message) : base(message) { - HResult = __HResults.COR_E_ARGUMENT; + HResult = HResults.COR_E_ARGUMENT; } public EncoderFallbackException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_ARGUMENT; + HResult = HResults.COR_E_ARGUMENT; } internal EncoderFallbackException( diff --git a/src/mscorlib/shared/System/Text/Encoding.cs b/src/mscorlib/shared/System/Text/Encoding.cs index 4f23d2a4a7..bf8cb03db2 100644 --- a/src/mscorlib/shared/System/Text/Encoding.cs +++ b/src/mscorlib/shared/System/Text/Encoding.cs @@ -382,6 +382,8 @@ namespace System.Text return Array.Empty<byte>(); } + public virtual ReadOnlySpan<byte> Preamble => GetPreamble(); + private void GetDataItem() { if (_dataItem == null) @@ -733,6 +735,14 @@ namespace System.Text return GetByteCount(arrChar, 0, count); } + public virtual unsafe int GetByteCount(ReadOnlySpan<char> chars) + { + fixed (char* charsPtr = &chars.DangerousGetPinnableReference()) + { + return GetByteCount(charsPtr, chars.Length); + } + } + // For NLS Encodings, workhorse takes an encoder (may be null) // Always validate parameters before calling internal version, which will only assert. internal virtual unsafe int GetByteCount(char* chars, int count, EncoderNLS encoder) @@ -916,6 +926,15 @@ namespace System.Text return byteCount; } + public virtual unsafe int GetBytes(ReadOnlySpan<char> chars, Span<byte> bytes) + { + fixed (char* charsPtr = &chars.DangerousGetPinnableReference()) + fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference()) + { + return GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length); + } + } + // Returns the number of characters produced by decoding the given byte // array. // @@ -962,6 +981,14 @@ namespace System.Text return GetCharCount(arrbyte, 0, count); } + public virtual unsafe int GetCharCount(ReadOnlySpan<byte> bytes) + { + fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference()) + { + return GetCharCount(bytesPtr, bytes.Length); + } + } + // This is our internal workhorse // Always validate parameters before calling internal version, which will only assert. internal virtual unsafe int GetCharCount(byte* bytes, int count, DecoderNLS decoder) @@ -1070,6 +1097,14 @@ namespace System.Text return charCount; } + public virtual unsafe int GetChars(ReadOnlySpan<byte> bytes, Span<char> chars) + { + fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference()) + fixed (char* charsPtr = &chars.DangerousGetPinnableReference()) + { + return GetChars(bytesPtr, bytes.Length, charsPtr, chars.Length); + } + } // This is our internal workhorse // Always validate parameters before calling internal version, which will only assert. @@ -1093,6 +1128,15 @@ namespace System.Text return String.CreateStringFromEncoding(bytes, byteCount, this); } + public unsafe string GetString(ReadOnlySpan<byte> bytes) + { + fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference()) + { + return GetString(bytesPtr, bytes.Length); + } + } + + // Returns the code page identifier of this encoding. The returned value is // an integer between 0 and 65535 if the encoding has a code page // identifier, or -1 if the encoding does not represent a code page. diff --git a/src/mscorlib/shared/System/Text/Normalization.cs b/src/mscorlib/shared/System/Text/Normalization.cs index dc8bc2af71..976756251a 100644 --- a/src/mscorlib/shared/System/Text/Normalization.cs +++ b/src/mscorlib/shared/System/Text/Normalization.cs @@ -4,7 +4,6 @@ namespace System.Text { - // This is the enumeration for Normalization Forms public enum NormalizationForm { FormC = 1, @@ -12,18 +11,4 @@ namespace System.Text FormKC = 5, FormKD = 6 } - - internal enum ExtendedNormalizationForms - { - FormC = 1, - FormD = 2, - FormKC = 5, - FormKD = 6, - FormIdna = 0xd, - FormCDisallowUnassigned = 0x101, - FormDDisallowUnassigned = 0x102, - FormKCDisallowUnassigned = 0x105, - FormKDDisallowUnassigned = 0x106, - FormIdnaDisallowUnassigned = 0x10d - } } diff --git a/src/mscorlib/shared/System/Text/StringBuilder.cs b/src/mscorlib/shared/System/Text/StringBuilder.cs index 34533a92ca..a73e6757d4 100644 --- a/src/mscorlib/shared/System/Text/StringBuilder.cs +++ b/src/mscorlib/shared/System/Text/StringBuilder.cs @@ -822,11 +822,6 @@ namespace System.Text throw new ArgumentNullException(nameof(destination)); } - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count), SR.Arg_NegativeArgCount); - } - if (destinationIndex < 0) { throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.Format(SR.ArgumentOutOfRange_MustBeNonNegNum, nameof(destinationIndex))); @@ -836,6 +831,17 @@ namespace System.Text { throw new ArgumentException(SR.ArgumentOutOfRange_OffsetOut); } + Contract.EndContractBlock(); + + CopyTo(sourceIndex, new Span<char>(destination).Slice(destinationIndex), count); + } + + public void CopyTo(int sourceIndex, Span<char> destination, int count) + { + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), SR.Arg_NegativeArgCount); + } if ((uint)sourceIndex > (uint)Length) { @@ -852,7 +858,7 @@ namespace System.Text StringBuilder chunk = this; int sourceEndIndex = sourceIndex + count; - int curDestIndex = destinationIndex + count; + int curDestIndex = count; while (count > 0) { int chunkEndIndex = sourceEndIndex - chunk.m_ChunkOffset; @@ -1033,6 +1039,21 @@ namespace System.Text return this; } + public StringBuilder Append(ReadOnlySpan<char> value) + { + if (value.Length > 0) + { + unsafe + { + fixed (char* valueChars = &value.DangerousGetPinnableReference()) + { + Append(valueChars, value.Length); + } + } + } + return this; + } + #region AppendJoin public unsafe StringBuilder AppendJoin(string separator, params object[] values) @@ -1263,6 +1284,27 @@ namespace System.Text public StringBuilder Insert(int index, Object value) => (value == null) ? this : Insert(index, value.ToString(), 1); + public StringBuilder Insert(int index, ReadOnlySpan<char> value) + { + Contract.Ensures(Contract.Result<StringBuilder>() != null); + Contract.EndContractBlock(); + + if ((uint)index > (uint)Length) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + } + + if (value.Length > 0) + { + unsafe + { + fixed (char* sourcePtr = &value.DangerousGetPinnableReference()) + Insert(index, sourcePtr, value.Length); + } + } + return this; + } + public StringBuilder AppendFormat(String format, Object arg0) => AppendFormatHelper(null, format, new ParamsArray(arg0)); public StringBuilder AppendFormat(String format, Object arg0, Object arg1) => AppendFormatHelper(null, format, new ParamsArray(arg0, arg1)); @@ -1884,7 +1926,7 @@ namespace System.Text Debug.Assert(gapEnd <= sourceChunk.m_ChunkLength, "gap too big"); if (delta != 0) // can skip the sliding of gaps if source an target string are the same size. { - // Copy the gap data between the current replacement and the the next replacement + // Copy the gap data between the current replacement and the next replacement fixed (char* sourcePtr = &sourceChunk.m_ChunkChars[gapStart]) ReplaceInPlaceAtChunk(ref targetChunk, ref targetIndexInChunk, sourcePtr, gapEnd - gapStart); } @@ -1944,7 +1986,7 @@ namespace System.Text /// </summary> /// <param name="chunk"> /// The chunk in which to start replacing characters. - /// Receieves the chunk in which character replacement ends. + /// Receives the chunk in which character replacement ends. /// </param> /// <param name="indexInChunk"> /// The index in <paramref name="chunk"/> to start replacing characters at. @@ -2001,22 +2043,23 @@ namespace System.Text } } - private static void ThreadSafeCopy(char[] source, int sourceIndex, char[] destination, int destinationIndex, int count) + private static unsafe void ThreadSafeCopy(char[] source, int sourceIndex, Span<char> destination, int destinationIndex, int count) { if (count > 0) { - if ((uint)sourceIndex <= (uint)source.Length && (sourceIndex + count) <= source.Length) + if ((uint)sourceIndex > (uint)source.Length || count > source.Length - sourceIndex) { - unsafe - { - fixed (char* sourcePtr = &source[sourceIndex]) - ThreadSafeCopy(sourcePtr, destination, destinationIndex, count); - } + throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_Index); } - else + + if ((uint)destinationIndex > (uint)destination.Length || count > destination.Length - destinationIndex) { - throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_Index); + throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.ArgumentOutOfRange_Index); } + + fixed (char* sourcePtr = &source[sourceIndex]) + fixed (char* destinationPtr = &destination.DangerousGetPinnableReference()) + string.wstrcpy(destinationPtr + destinationIndex, sourcePtr, count); } } diff --git a/src/mscorlib/shared/System/Text/UTF32Encoding.cs b/src/mscorlib/shared/System/Text/UTF32Encoding.cs index 10161d193e..260518e21c 100644 --- a/src/mscorlib/shared/System/Text/UTF32Encoding.cs +++ b/src/mscorlib/shared/System/Text/UTF32Encoding.cs @@ -39,6 +39,9 @@ namespace System.Text internal static readonly UTF32Encoding s_default = new UTF32Encoding(bigEndian: false, byteOrderMark: true); internal static readonly UTF32Encoding s_bigEndianDefault = new UTF32Encoding(bigEndian: true, byteOrderMark: true); + private static readonly byte[] s_bigEndianPreamble = new byte[4] { 0x00, 0x00, 0xFE, 0xFF }; + private static readonly byte[] s_littleEndianPreamble = new byte[4] { 0xFF, 0xFE, 0x00, 0x00 }; + private bool _emitUTF32ByteOrderMark = false; private bool _isThrowException = false; private bool _bigEndian = false; @@ -1177,6 +1180,10 @@ namespace System.Text return Array.Empty<byte>(); } + public override ReadOnlySpan<byte> Preamble => + GetType() != typeof(UTF32Encoding) ? GetPreamble() : // in case a derived UTF32Encoding overrode GetPreamble + _emitUTF32ByteOrderMark ? (_bigEndian ? s_bigEndianPreamble : s_littleEndianPreamble) : + Array.Empty<byte>(); public override bool Equals(Object value) { diff --git a/src/mscorlib/shared/System/Text/UTF8Encoding.cs b/src/mscorlib/shared/System/Text/UTF8Encoding.cs index 02b18935e4..974bf75650 100644 --- a/src/mscorlib/shared/System/Text/UTF8Encoding.cs +++ b/src/mscorlib/shared/System/Text/UTF8Encoding.cs @@ -54,15 +54,19 @@ namespace System.Text internal sealed class UTF8EncodingSealed : UTF8Encoding { public UTF8EncodingSealed(bool encoderShouldEmitUTF8Identifier) : base(encoderShouldEmitUTF8Identifier) { } + + public override ReadOnlySpan<byte> Preamble => _emitUTF8Identifier ? s_preamble : Array.Empty<byte>(); } // Used by Encoding.UTF8 for lazy initialization // The initialization code will not be run until a static member of the class is referenced internal static readonly UTF8EncodingSealed s_default = new UTF8EncodingSealed(encoderShouldEmitUTF8Identifier: true); + internal static readonly byte[] s_preamble = new byte[3] { 0xEF, 0xBB, 0xBF }; + // Yes, the idea of emitting U+FEFF as a UTF-8 identifier has made it into // the standard. - private bool _emitUTF8Identifier = false; + internal readonly bool _emitUTF8Identifier = false; private bool _isThrowException = false; @@ -2497,6 +2501,10 @@ namespace System.Text return Array.Empty<byte>(); } + public override ReadOnlySpan<byte> Preamble => + GetType() != typeof(UTF8Encoding) ? GetPreamble() : // in case a derived UTF8Encoding overrode GetPreamble + _emitUTF8Identifier ? s_preamble : + Array.Empty<byte>(); public override bool Equals(Object value) { diff --git a/src/mscorlib/shared/System/Text/UnicodeEncoding.cs b/src/mscorlib/shared/System/Text/UnicodeEncoding.cs index 8e44317ce2..78355299c1 100644 --- a/src/mscorlib/shared/System/Text/UnicodeEncoding.cs +++ b/src/mscorlib/shared/System/Text/UnicodeEncoding.cs @@ -20,6 +20,9 @@ namespace System.Text internal static readonly UnicodeEncoding s_bigEndianDefault = new UnicodeEncoding(bigEndian: true, byteOrderMark: true); internal static readonly UnicodeEncoding s_littleEndianDefault = new UnicodeEncoding(bigEndian: false, byteOrderMark: true); + private static readonly byte[] s_bigEndianPreamble = new byte[2] { 0xfe, 0xff }; + private static readonly byte[] s_littleEndianPreamble = new byte[2] { 0xff, 0xfe }; + internal bool isThrowException = false; internal bool bigEndian = false; @@ -1898,6 +1901,10 @@ namespace System.Text return Array.Empty<Byte>(); } + public override ReadOnlySpan<byte> Preamble => + GetType() != typeof(UnicodeEncoding) ? GetPreamble() : // in case a derived UnicodeEncoding overrode GetPreamble + byteOrderMark ? (bigEndian ? s_bigEndianPreamble : s_littleEndianPreamble) : + Array.Empty<byte>(); public override int GetMaxByteCount(int charCount) { diff --git a/src/mscorlib/shared/System/Threading/AbandonedMutexException.cs b/src/mscorlib/shared/System/Threading/AbandonedMutexException.cs index 15bc5a7341..c43148d22a 100644 --- a/src/mscorlib/shared/System/Threading/AbandonedMutexException.cs +++ b/src/mscorlib/shared/System/Threading/AbandonedMutexException.cs @@ -22,39 +22,39 @@ namespace System.Threading public AbandonedMutexException() : base(SR.Threading_AbandonedMutexException) { - HResult = __HResults.COR_E_ABANDONEDMUTEX; + HResult = HResults.COR_E_ABANDONEDMUTEX; } public AbandonedMutexException(String message) : base(message) { - HResult = __HResults.COR_E_ABANDONEDMUTEX; + HResult = HResults.COR_E_ABANDONEDMUTEX; } public AbandonedMutexException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_ABANDONEDMUTEX; + HResult = HResults.COR_E_ABANDONEDMUTEX; } public AbandonedMutexException(int location, WaitHandle handle) : base(SR.Threading_AbandonedMutexException) { - HResult = __HResults.COR_E_ABANDONEDMUTEX; + HResult = HResults.COR_E_ABANDONEDMUTEX; SetupException(location, handle); } public AbandonedMutexException(String message, int location, WaitHandle handle) : base(message) { - HResult = __HResults.COR_E_ABANDONEDMUTEX; + HResult = HResults.COR_E_ABANDONEDMUTEX; SetupException(location, handle); } public AbandonedMutexException(String message, Exception inner, int location, WaitHandle handle) : base(message, inner) { - HResult = __HResults.COR_E_ABANDONEDMUTEX; + HResult = HResults.COR_E_ABANDONEDMUTEX; SetupException(location, handle); } diff --git a/src/mscorlib/shared/System/Threading/DeferredDisposableLifetime.cs b/src/mscorlib/shared/System/Threading/DeferredDisposableLifetime.cs index 89380fee60..e2b1eb983b 100644 --- a/src/mscorlib/shared/System/Threading/DeferredDisposableLifetime.cs +++ b/src/mscorlib/shared/System/Threading/DeferredDisposableLifetime.cs @@ -18,7 +18,7 @@ namespace System.Threading /// Indicates whether the object has been disposed. /// </param> /// <remarks> - /// If the refount reaches zero before the object is disposed, this method will be called with + /// If the refcount reaches zero before the object is disposed, this method will be called with /// <paramref name="disposed"/> set to false. If the object is then disposed, this method will be /// called again, with <paramref name="disposed"/> set to true. If the refcount reaches zero /// after the object has already been disposed, this will be called a single time, with diff --git a/src/mscorlib/shared/System/Threading/LazyInitializer.cs b/src/mscorlib/shared/System/Threading/LazyInitializer.cs index b03d1db302..f422ab9172 100644 --- a/src/mscorlib/shared/System/Threading/LazyInitializer.cs +++ b/src/mscorlib/shared/System/Threading/LazyInitializer.cs @@ -25,7 +25,7 @@ namespace System.Threading /// Initializes a target reference type with the type's default constructor if the target has not /// already been initialized. /// </summary> - /// <typeparam name="T">The refence type of the reference to be initialized.</typeparam> + /// <typeparam name="T">The reference type of the reference to be initialized.</typeparam> /// <param name="target">A reference of type <typeparamref name="T"/> to initialize if it has not /// already been initialized.</param> /// <returns>The initialized reference of type <typeparamref name="T"/>.</returns> diff --git a/src/mscorlib/shared/System/Threading/ReaderWriterLockSlim.cs b/src/mscorlib/shared/System/Threading/ReaderWriterLockSlim.cs index 98517ad85f..1b2147e6e1 100644 --- a/src/mscorlib/shared/System/Threading/ReaderWriterLockSlim.cs +++ b/src/mscorlib/shared/System/Threading/ReaderWriterLockSlim.cs @@ -4,7 +4,6 @@ using Internal.Runtime.Augments; using System.Diagnostics; // for TraceInformation -using System.Threading; using System.Runtime.CompilerServices; namespace System.Threading @@ -54,8 +53,10 @@ namespace System.Threading /// </summary> public class ReaderWriterLockSlim : IDisposable { + private static readonly int ProcessorCount = Environment.ProcessorCount; + //Specifying if locked can be reacquired recursively. - private bool _fIsReentrant; + private readonly bool _fIsReentrant; // Lock specification for myLock: This lock protects exactly the local fields associated with this // instance of ReaderWriterLockSlim. It does NOT protect the memory associated with @@ -74,8 +75,7 @@ namespace System.Threading private uint _numWriteUpgradeWaiters; // maximum number of threads that can be doing a WaitOne on the upgradeEvent (at most 1). private uint _numUpgradeWaiters; - //Variable used for quick check when there are no waiters. - private bool _fNoWaiters; + private WaiterStates _waiterStates; private int _upgradeLockOwnerId; private int _writeLockOwnerId; @@ -146,10 +146,37 @@ namespace System.Threading _fIsReentrant = true; } InitializeThreadCounts(); - _fNoWaiters = true; + _waiterStates = WaiterStates.NoWaiters; _lockID = Interlocked.Increment(ref s_nextLockID); } + private bool HasNoWaiters + { + get + { +#if DEBUG + Debug.Assert(MyLockHeld); +#endif + + return (_waiterStates & WaiterStates.NoWaiters) != WaiterStates.None; + } + set + { +#if DEBUG + Debug.Assert(MyLockHeld); +#endif + + if (value) + { + _waiterStates |= WaiterStates.NoWaiters; + } + else + { + _waiterStates &= ~WaiterStates.NoWaiters; + } + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsRWEntryEmpty(ReaderWriterCount rwc) { @@ -352,8 +379,7 @@ namespace System.Threading } bool retVal = true; - - int spincount = 0; + int spinCount = 0; for (; ;) { @@ -368,13 +394,17 @@ namespace System.Threading break; } - if (spincount < MaxSpinCount) + if (timeout.IsExpired) { ExitMyLock(); - if (timeout.IsExpired) - return false; - spincount++; - SpinWait(spincount); + return false; + } + + if (spinCount < MaxSpinCount && ShouldSpinForEnterAnyRead()) + { + ExitMyLock(); + spinCount++; + SpinWait(spinCount); EnterMyLock(); //The per-thread structure may have been recycled as the lock is acquired (due to message pumping), load again. if (IsRwHashEntryChanged(lrwc)) @@ -391,7 +421,13 @@ namespace System.Threading continue; // since we left the lock, start over. } - retVal = WaitOnEvent(_readEvent, ref _numReadWaiters, timeout, isWriteWaiter: false); + retVal = + WaitOnEvent( + _readEvent, + ref _numReadWaiters, + timeout, + isWriteWaiter: false, + waiterSignaledState: WaiterStates.None); if (!retVal) { return false; @@ -480,8 +516,8 @@ namespace System.Threading } } - int spincount = 0; bool retVal = true; + int spinCount = 0; for (; ;) { @@ -528,13 +564,17 @@ namespace System.Threading } } - if (spincount < MaxSpinCount) + if (timeout.IsExpired) { ExitMyLock(); - if (timeout.IsExpired) - return false; - spincount++; - SpinWait(spincount); + return false; + } + + if (spinCount < MaxSpinCount && ShouldSpinForEnterAnyWrite(upgradingToWrite)) + { + ExitMyLock(); + spinCount++; + SpinWait(spinCount); EnterMyLock(); continue; } @@ -549,7 +589,13 @@ namespace System.Threading Debug.Assert(_numWriteUpgradeWaiters == 0, "There can be at most one thread with the upgrade lock held."); - retVal = WaitOnEvent(_waitUpgradeEvent, ref _numWriteUpgradeWaiters, timeout, isWriteWaiter: true); + retVal = + WaitOnEvent( + _waitUpgradeEvent, + ref _numWriteUpgradeWaiters, + timeout, + isWriteWaiter: true, + waiterSignaledState: WaiterStates.None); //The lock is not held in case of failure. if (!retVal) @@ -564,7 +610,13 @@ namespace System.Threading continue; // since we left the lock, start over. } - retVal = WaitOnEvent(_writeEvent, ref _numWriteWaiters, timeout, isWriteWaiter: true); + retVal = + WaitOnEvent( + _writeEvent, + ref _numWriteWaiters, + timeout, + isWriteWaiter: true, + waiterSignaledState: WaiterStates.WriteWaiterSignaled); //The lock is not held in case of failure. if (!retVal) return false; @@ -671,8 +723,7 @@ namespace System.Threading } bool retVal = true; - - int spincount = 0; + int spinCount = 0; for (; ;) { @@ -686,13 +737,17 @@ namespace System.Threading break; } - if (spincount < MaxSpinCount) + if (timeout.IsExpired) { ExitMyLock(); - if (timeout.IsExpired) - return false; - spincount++; - SpinWait(spincount); + return false; + } + + if (spinCount < MaxSpinCount && ShouldSpinForEnterAnyRead()) + { + ExitMyLock(); + spinCount++; + SpinWait(spinCount); EnterMyLock(); continue; } @@ -705,7 +760,13 @@ namespace System.Threading } //Only one thread with the upgrade lock held can proceed. - retVal = WaitOnEvent(_upgradeEvent, ref _numUpgradeWaiters, timeout, isWriteWaiter: false); + retVal = + WaitOnEvent( + _upgradeEvent, + ref _numUpgradeWaiters, + timeout, + isWriteWaiter: false, + waiterSignaledState: WaiterStates.UpgradeableReadWaiterSignaled); if (!retVal) return false; } @@ -890,14 +951,20 @@ namespace System.Threading EventWaitHandle waitEvent, ref uint numWaiters, TimeoutTracker timeout, - bool isWriteWaiter) + bool isWriteWaiter, + WaiterStates waiterSignaledState) { #if DEBUG Debug.Assert(MyLockHeld); #endif + Debug.Assert( + waiterSignaledState == WaiterStates.None || + waiterSignaledState == WaiterStates.WriteWaiterSignaled || + waiterSignaledState == WaiterStates.UpgradeableReadWaiterSignaled); + waitEvent.Reset(); numWaiters++; - _fNoWaiters = false; + HasNoWaiters = false; //Setting these bits will prevent new readers from getting in. if (_numWriteWaiters == 1) @@ -917,8 +984,19 @@ namespace System.Threading EnterMyLock(); --numWaiters; + if (waitSuccessful && waiterSignaledState != WaiterStates.None) + { + // Indicate that a signaled waiter of this type has woken. Since non-read waiters are signaled to wake one + // at a time, we avoid waking up more than one waiter of that type upon successive enter/exit loops until + // the signaled thread actually wakes up. For example, if there are multiple write waiters and one thread is + // repeatedly entering and exiting a write lock, every exit would otherwise signal a different write waiter + // to wake up unnecessarily when only one woken waiter may actually succeed in entering the write lock. + Debug.Assert((_waiterStates & waiterSignaledState) != WaiterStates.None); + _waiterStates &= ~waiterSignaledState; + } + if (_numWriteWaiters == 0 && _numWriteUpgradeWaiters == 0 && _numUpgradeWaiters == 0 && _numReadWaiters == 0) - _fNoWaiters = true; + HasNoWaiters = true; if (_numWriteWaiters == 0) ClearWritersWaiting(); @@ -948,7 +1026,7 @@ namespace System.Threading #if DEBUG Debug.Assert(MyLockHeld); #endif - if (_fNoWaiters) + if (HasNoWaiters) { ExitMyLock(); return; @@ -984,8 +1062,20 @@ namespace System.Threading } else if (readercount == 0 && _numWriteWaiters > 0) { + // Check if a waiter of the same type has already been signaled but hasn't woken yet. If so, avoid signaling + // and waking another waiter unnecessarily. + WaiterStates signaled = _waiterStates & WaiterStates.WriteWaiterSignaled; + if (signaled == WaiterStates.None) + { + _waiterStates |= WaiterStates.WriteWaiterSignaled; + } + ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) - _writeEvent.Set(); // release one writer. + + if (signaled == WaiterStates.None) + { + _writeEvent.Set(); // release one writer. + } } else { @@ -999,7 +1089,7 @@ namespace System.Threading Debug.Assert(MyLockHeld); #endif - if (_numWriteWaiters != 0 || _numWriteUpgradeWaiters != 0 || _fNoWaiters) + if (_numWriteWaiters != 0 || _numWriteUpgradeWaiters != 0 || HasNoWaiters) { ExitMyLock(); return; @@ -1009,6 +1099,19 @@ namespace System.Threading bool setReadEvent = _numReadWaiters != 0; bool setUpgradeEvent = _numUpgradeWaiters != 0 && _upgradeLockOwnerId == -1; + if (setUpgradeEvent) + { + // Check if a waiter of the same type has already been signaled but hasn't woken yet. If so, avoid signaling + // and waking another waiter unnecessarily. + if ((_waiterStates & WaiterStates.UpgradeableReadWaiterSignaled) == WaiterStates.None) + { + _waiterStates |= WaiterStates.UpgradeableReadWaiterSignaled; + } + else + { + setUpgradeEvent = false; + } + } ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) @@ -1059,33 +1162,62 @@ namespace System.Threading return _owners & READER_MASK; } + private bool ShouldSpinForEnterAnyRead() + { + // If there is a write waiter or write upgrade waiter, the waiter would block a reader from acquiring the RW lock + // because the waiter takes precedence. In that case, the reader is not likely to make progress by spinning. + // Although another thread holding a write lock would prevent this thread from acquiring a read lock, it is by + // itself not a good enough reason to skip spinning. + return HasNoWaiters || (_numWriteWaiters == 0 && _numWriteUpgradeWaiters == 0); + } + + private bool ShouldSpinForEnterAnyWrite(bool isUpgradeToWrite) + { + // If there is a write upgrade waiter, the waiter would block a writer from acquiring the RW lock because the waiter + // holds a read lock. In that case, the writer is not likely to make progress by spinning. Regarding upgrading to a + // write lock, there is no type of waiter that would block the upgrade from happening. Although another thread + // holding a read or write lock would prevent this thread from acquiring the write lock, it is by itself not a good + // enough reason to skip spinning. + return isUpgradeToWrite || _numWriteUpgradeWaiters == 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool TryEnterMyLock() + { + return Interlocked.CompareExchange(ref _myLock, 1, 0) == 0; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void EnterMyLock() { - if (Interlocked.CompareExchange(ref _myLock, 1, 0) != 0) + if (!TryEnterMyLock()) + { EnterMyLockSpin(); + } } private void EnterMyLockSpin() { - int pc = Environment.ProcessorCount; - for (int i = 0; ; i++) + int processorCount = ProcessorCount; + for (int spinIndex = 0; ; spinIndex++) { - if (i < LockSpinCount && pc > 1) + if (spinIndex < LockSpinCount && processorCount > 1) { - RuntimeThread.SpinWait(LockSpinCycles * (i + 1)); // Wait a few dozen instructions to let another processor release lock. + RuntimeThread.SpinWait(LockSpinCycles * (spinIndex + 1)); // Wait a few dozen instructions to let another processor release lock. } - else if (i < (LockSpinCount + LockSleep0Count)) + else if (spinIndex < (LockSpinCount + LockSleep0Count)) { - RuntimeThread.Sleep(0); // Give up my quantum. + RuntimeThread.Sleep(0); // Give up my quantum. } else { - RuntimeThread.Sleep(1); // Give up my quantum. + RuntimeThread.Sleep(1); // Give up my quantum. } - if (_myLock == 0 && Interlocked.CompareExchange(ref _myLock, 1, 0) == 0) + if (_myLock == 0 && TryEnterMyLock()) + { return; + } } } @@ -1099,12 +1231,12 @@ namespace System.Threading private bool MyLockHeld { get { return _myLock != 0; } } #endif - private static void SpinWait(int SpinCount) + private static void SpinWait(int spinCount) { //Exponential back-off - if ((SpinCount < 5) && (Environment.ProcessorCount > 1)) + if ((spinCount < 5) && (ProcessorCount > 1)) { - RuntimeThread.SpinWait(LockSpinCycles * SpinCount); + RuntimeThread.SpinWait(LockSpinCycles * spinCount); } else { @@ -1307,5 +1439,19 @@ namespace System.Threading return (int)_numWriteWaiters; } } + + [Flags] + private enum WaiterStates : byte + { + None = 0x0, + + // Used for quick check when there are no waiters + NoWaiters = 0x1, + + // Used to avoid signaling more than one waiter to wake up when only one can make progress, see WaitOnEvent + WriteWaiterSignaled = 0x2, + UpgradeableReadWaiterSignaled = 0x4 + // Write upgrade waiters are excluded because there can only be one at any given time + } } } diff --git a/src/mscorlib/shared/System/Threading/SpinWait.cs b/src/mscorlib/shared/System/Threading/SpinWait.cs index d25d54f26f..414ad1852f 100644 --- a/src/mscorlib/shared/System/Threading/SpinWait.cs +++ b/src/mscorlib/shared/System/Threading/SpinWait.cs @@ -69,9 +69,26 @@ namespace System.Threading // numbers may seem fairly arbitrary, but were derived with at least some // thought in the design document. I fully expect they will need to change // over time as we gain more experience with performance. - internal const int YIELD_THRESHOLD = 10; // When to switch over to a true yield. - internal const int SLEEP_0_EVERY_HOW_MANY_TIMES = 5; // After how many yields should we Sleep(0)? - internal const int SLEEP_1_EVERY_HOW_MANY_TIMES = 20; // After how many yields should we Sleep(1)? + internal const int YieldThreshold = 10; // When to switch over to a true yield. + private const int Sleep0EveryHowManyYields = 5; // After how many yields should we Sleep(0)? + internal const int DefaultSleep1Threshold = 20; // After how many yields should we Sleep(1) frequently? + + /// <summary> + /// A suggested number of spin iterations before doing a proper wait, such as waiting on an event that becomes signaled + /// when the resource becomes available. + /// </summary> + /// <remarks> + /// These numbers were arrived at by experimenting with different numbers in various cases that currently use it. It's + /// only a suggested value and typically works well when the proper wait is something like an event. + /// + /// Spinning less can lead to early waiting and more context switching, spinning more can decrease latency but may use + /// up some CPU time unnecessarily. Depends on the situation too, for instance SemaphoreSlim uses more iterations + /// because the waiting there is currently a lot more expensive (involves more spinning, taking a lock, etc.). It also + /// depends on the likelihood of the spin being successful and how long the wait would be but those are not accounted + /// for here. + /// </remarks> + internal static readonly int SpinCountforSpinBeforeWait = PlatformHelper.IsSingleProcessor ? 1 : 35; + internal const int Sleep1ThresholdForSpinBeforeWait = 40; // should be greater than SpinCountforSpinBeforeWait // The number of times we've spun already. private int _count; @@ -81,7 +98,12 @@ namespace System.Threading /// </summary> public int Count { - get { return _count; } + get => _count; + internal set + { + Debug.Assert(value >= 0); + _count = value; + } } /// <summary> @@ -94,10 +116,7 @@ namespace System.Threading /// On a single-CPU machine, <see cref="SpinOnce"/> always yields the processor. On machines with /// multiple CPUs, <see cref="SpinOnce"/> may yield after an unspecified number of calls. /// </remarks> - public bool NextSpinWillYield - { - get { return _count > YIELD_THRESHOLD || PlatformHelper.IsSingleProcessor; } - } + public bool NextSpinWillYield => _count >= YieldThreshold || PlatformHelper.IsSingleProcessor; /// <summary> /// Performs a single spin. @@ -108,7 +127,27 @@ namespace System.Threading /// </remarks> public void SpinOnce() { - if (NextSpinWillYield) + SpinOnce(DefaultSleep1Threshold); + } + + internal void SpinOnce(int sleep1Threshold) + { + Debug.Assert(sleep1Threshold >= YieldThreshold || PlatformHelper.IsSingleProcessor); // so that NextSpinWillYield behaves as requested + + // (_count - YieldThreshold) % 2 == 0: The purpose of this check is to interleave Thread.Yield/Sleep(0) with + // Thread.SpinWait. Otherwise, the following issues occur: + // - When there are no threads to switch to, Yield and Sleep(0) become no-op and it turns the spin loop into a + // busy-spin that may quickly reach the max spin count and cause the thread to enter a wait state, or may + // just busy-spin for longer than desired before a Sleep(1). Completing the spin loop too early can cause + // excessive context switcing if a wait follows, and entering the Sleep(1) stage too early can cause + // excessive delays. + // - If there are multiple threads doing Yield and Sleep(0) (typically from the same spin loop due to + // contention), they may switch between one another, delaying work that can make progress. + if (( + _count >= YieldThreshold && + (_count >= sleep1Threshold || (_count - YieldThreshold) % 2 == 0) + ) || + PlatformHelper.IsSingleProcessor) { // // We must yield. @@ -125,19 +164,21 @@ namespace System.Threading // configured to use the (default) coarse-grained system timer. // - int yieldsSoFar = (_count >= YIELD_THRESHOLD ? _count - YIELD_THRESHOLD : _count); - - if ((yieldsSoFar % SLEEP_1_EVERY_HOW_MANY_TIMES) == (SLEEP_1_EVERY_HOW_MANY_TIMES - 1)) + if (_count >= sleep1Threshold) { RuntimeThread.Sleep(1); } - else if ((yieldsSoFar % SLEEP_0_EVERY_HOW_MANY_TIMES) == (SLEEP_0_EVERY_HOW_MANY_TIMES - 1)) - { - RuntimeThread.Sleep(0); - } else { - RuntimeThread.Yield(); + int yieldsSoFar = _count >= YieldThreshold ? (_count - YieldThreshold) / 2 : _count; + if ((yieldsSoFar % Sleep0EveryHowManyYields) == (Sleep0EveryHowManyYields - 1)) + { + RuntimeThread.Sleep(0); + } + else + { + RuntimeThread.Yield(); + } } } else @@ -153,11 +194,24 @@ namespace System.Threading // number of spins we are willing to tolerate to reduce delay to the caller, // since we expect most callers will eventually block anyway. // - RuntimeThread.SpinWait(4 << _count); + // Also, cap the maximum spin count to a value such that many thousands of CPU cycles would not be wasted doing + // the equivalent of YieldProcessor(), as that that point SwitchToThread/Sleep(0) are more likely to be able to + // allow other useful work to run. Long YieldProcessor() loops can help to reduce contention, but Sleep(1) is + // usually better for that. + // + // RuntimeThread.OptimalMaxSpinWaitsPerSpinIteration: + // - See Thread::InitializeYieldProcessorNormalized(), which describes and calculates this value. + // + int n = RuntimeThread.OptimalMaxSpinWaitsPerSpinIteration; + if (_count <= 30 && (1 << _count) < n) + { + n = 1 << _count; + } + RuntimeThread.SpinWait(n); } // Finally, increment our spin counter. - _count = (_count == int.MaxValue ? YIELD_THRESHOLD : _count + 1); + _count = (_count == int.MaxValue ? YieldThreshold : _count + 1); } /// <summary> @@ -299,9 +353,7 @@ namespace System.Threading /// <summary> /// Gets whether the current machine has only a single processor. /// </summary> - internal static bool IsSingleProcessor - { - get { return ProcessorCount == 1; } - } + /// <remarks>This typically does not change on a machine, so it's checked only once.</remarks> + internal static readonly bool IsSingleProcessor = ProcessorCount == 1; } } diff --git a/src/mscorlib/shared/System/Threading/SynchronizationLockException.cs b/src/mscorlib/shared/System/Threading/SynchronizationLockException.cs index c64fc9ced8..5ab186ec9a 100644 --- a/src/mscorlib/shared/System/Threading/SynchronizationLockException.cs +++ b/src/mscorlib/shared/System/Threading/SynchronizationLockException.cs @@ -21,19 +21,19 @@ namespace System.Threading public SynchronizationLockException() : base(SR.Arg_SynchronizationLockException) { - HResult = __HResults.COR_E_SYNCHRONIZATIONLOCK; + HResult = HResults.COR_E_SYNCHRONIZATIONLOCK; } public SynchronizationLockException(String message) : base(message) { - HResult = __HResults.COR_E_SYNCHRONIZATIONLOCK; + HResult = HResults.COR_E_SYNCHRONIZATIONLOCK; } public SynchronizationLockException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_SYNCHRONIZATIONLOCK; + HResult = HResults.COR_E_SYNCHRONIZATIONLOCK; } protected SynchronizationLockException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/Threading/Tasks/ValueTask.cs b/src/mscorlib/shared/System/Threading/Tasks/ValueTask.cs new file mode 100644 index 0000000000..384e4a8ab3 --- /dev/null +++ b/src/mscorlib/shared/System/Threading/Tasks/ValueTask.cs @@ -0,0 +1,169 @@ +// 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.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Threading.Tasks +{ + /// <summary> + /// Provides a value type that wraps a <see cref="Task{TResult}"/> and a <typeparamref name="TResult"/>, + /// only one of which is used. + /// </summary> + /// <typeparam name="TResult">The type of the result.</typeparam> + /// <remarks> + /// <para> + /// Methods may return an instance of this value type when it's likely that the result of their + /// operations will be available synchronously and when the method is expected to be invoked so + /// frequently that the cost of allocating a new <see cref="Task{TResult}"/> for each call will + /// be prohibitive. + /// </para> + /// <para> + /// There are tradeoffs to using a <see cref="ValueTask{TResult}"/> instead of a <see cref="Task{TResult}"/>. + /// For example, while a <see cref="ValueTask{TResult}"/> can help avoid an allocation in the case where the + /// successful result is available synchronously, it also contains two fields whereas a <see cref="Task{TResult}"/> + /// as a reference type is a single field. This means that a method call ends up returning two fields worth of + /// data instead of one, which is more data to copy. It also means that if a method that returns one of these + /// is awaited within an async method, the state machine for that async method will be larger due to needing + /// to store the struct that's two fields instead of a single reference. + /// </para> + /// <para> + /// Further, for uses other than consuming the result of an asynchronous operation via await, + /// <see cref="ValueTask{TResult}"/> can lead to a more convoluted programming model, which can in turn actually + /// lead to more allocations. For example, consider a method that could return either a <see cref="Task{TResult}"/> + /// with a cached task as a common result or a <see cref="ValueTask{TResult}"/>. If the consumer of the result + /// wants to use it as a <see cref="Task{TResult}"/>, such as to use with in methods like Task.WhenAll and Task.WhenAny, + /// the <see cref="ValueTask{TResult}"/> would first need to be converted into a <see cref="Task{TResult}"/> using + /// <see cref="ValueTask{TResult}.AsTask"/>, which leads to an allocation that would have been avoided if a cached + /// <see cref="Task{TResult}"/> had been used in the first place. + /// </para> + /// <para> + /// As such, the default choice for any asynchronous method should be to return a <see cref="Task"/> or + /// <see cref="Task{TResult}"/>. Only if performance analysis proves it worthwhile should a <see cref="ValueTask{TResult}"/> + /// be used instead of <see cref="Task{TResult}"/>. There is no non-generic version of <see cref="ValueTask{TResult}"/> + /// as the Task.CompletedTask property may be used to hand back a successfully completed singleton in the case where + /// a <see cref="Task"/>-returning method completes synchronously and successfully. + /// </para> + /// </remarks> + [AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder<>))] + [StructLayout(LayoutKind.Auto)] + public struct ValueTask<TResult> : IEquatable<ValueTask<TResult>> + { + /// <summary>The task to be used if the operation completed asynchronously or if it completed synchronously but non-successfully.</summary> + internal readonly Task<TResult> _task; + /// <summary>The result to be used if the operation completed successfully synchronously.</summary> + internal readonly TResult _result; + + /// <summary>Initialize the <see cref="ValueTask{TResult}"/> with the result of the successful operation.</summary> + /// <param name="result">The result.</param> + public ValueTask(TResult result) + { + _task = null; + _result = result; + } + + /// <summary> + /// Initialize the <see cref="ValueTask{TResult}"/> with a <see cref="Task{TResult}"/> that represents the operation. + /// </summary> + /// <param name="task">The task.</param> + public ValueTask(Task<TResult> task) + { + if (task == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.task); + } + + _task = task; + _result = default(TResult); + } + + /// <summary>Returns the hash code for this instance.</summary> + public override int GetHashCode() => + _task != null ? _task.GetHashCode() : + _result != null ? _result.GetHashCode() : + 0; + + /// <summary>Returns a value indicating whether this value is equal to a specified <see cref="object"/>.</summary> + public override bool Equals(object obj) => + obj is ValueTask<TResult> && + Equals((ValueTask<TResult>)obj); + + /// <summary>Returns a value indicating whether this value is equal to a specified <see cref="ValueTask{TResult}"/> value.</summary> + public bool Equals(ValueTask<TResult> other) => + _task != null || other._task != null ? + _task == other._task : + EqualityComparer<TResult>.Default.Equals(_result, other._result); + + /// <summary>Returns a value indicating whether two <see cref="ValueTask{TResult}"/> values are equal.</summary> + public static bool operator==(ValueTask<TResult> left, ValueTask<TResult> right) => + left.Equals(right); + + /// <summary>Returns a value indicating whether two <see cref="ValueTask{TResult}"/> values are not equal.</summary> + public static bool operator!=(ValueTask<TResult> left, ValueTask<TResult> right) => + !left.Equals(right); + + /// <summary> + /// Gets a <see cref="Task{TResult}"/> object to represent this ValueTask. It will + /// either return the wrapped task object if one exists, or it'll manufacture a new + /// task object to represent the result. + /// </summary> + public Task<TResult> AsTask() => + // Return the task if we were constructed from one, otherwise manufacture one. We don't + // cache the generated task into _task as it would end up changing both equality comparison + // and the hash code we generate in GetHashCode. + _task ?? AsyncTaskMethodBuilder<TResult>.GetTaskForResult(_result); + + /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a completed operation.</summary> + public bool IsCompleted => _task == null || _task.IsCompleted; + + /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a successfully completed operation.</summary> + public bool IsCompletedSuccessfully => _task == null || _task.IsCompletedSuccessfully; + + /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a failed operation.</summary> + public bool IsFaulted => _task != null && _task.IsFaulted; + + /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a canceled operation.</summary> + public bool IsCanceled => _task != null && _task.IsCanceled; + + /// <summary>Gets the result.</summary> + public TResult Result => _task == null ? _result : _task.GetAwaiter().GetResult(); + + /// <summary>Gets an awaiter for this value.</summary> + public ValueTaskAwaiter<TResult> GetAwaiter() => new ValueTaskAwaiter<TResult>(this); + + /// <summary>Configures an awaiter for this value.</summary> + /// <param name="continueOnCapturedContext"> + /// true to attempt to marshal the continuation back to the captured context; otherwise, false. + /// </param> + public ConfiguredValueTaskAwaitable<TResult> ConfigureAwait(bool continueOnCapturedContext) => + new ConfiguredValueTaskAwaitable<TResult>(this, continueOnCapturedContext); + + /// <summary>Gets a string-representation of this <see cref="ValueTask{TResult}"/>.</summary> + public override string ToString() + { + if (_task != null) + { + return _task.IsCompletedSuccessfully && _task.Result != null ? + _task.Result.ToString() : + string.Empty; + } + else + { + return _result != null ? + _result.ToString() : + string.Empty; + } + } + + // TODO https://github.com/dotnet/corefx/issues/22171: + // Remove CreateAsyncMethodBuilder once the C# compiler relies on the AsyncBuilder attribute. + + /// <summary>Creates a method builder for use with an async method.</summary> + /// <returns>The created builder.</returns> + [EditorBrowsable(EditorBrowsableState.Never)] // intended only for compiler consumption + public static AsyncValueTaskMethodBuilder<TResult> CreateAsyncMethodBuilder() => AsyncValueTaskMethodBuilder<TResult>.Create(); + } +} diff --git a/src/mscorlib/shared/System/Threading/ThreadAbortException.cs b/src/mscorlib/shared/System/Threading/ThreadAbortException.cs index ebbc29a702..01fdf1317b 100644 --- a/src/mscorlib/shared/System/Threading/ThreadAbortException.cs +++ b/src/mscorlib/shared/System/Threading/ThreadAbortException.cs @@ -22,7 +22,7 @@ namespace System.Threading { internal ThreadAbortException() { - HResult = __HResults.COR_E_THREADABORTED; + HResult = HResults.COR_E_THREADABORTED; } public object ExceptionState => null; diff --git a/src/mscorlib/shared/System/Threading/ThreadStartException.cs b/src/mscorlib/shared/System/Threading/ThreadStartException.cs index 7a87943ed1..35930d1d2d 100644 --- a/src/mscorlib/shared/System/Threading/ThreadStartException.cs +++ b/src/mscorlib/shared/System/Threading/ThreadStartException.cs @@ -11,13 +11,13 @@ namespace System.Threading internal ThreadStartException() : base(SR.Arg_ThreadStartException) { - HResult = __HResults.COR_E_THREADSTART; + HResult = HResults.COR_E_THREADSTART; } internal ThreadStartException(Exception reason) : base(SR.Arg_ThreadStartException, reason) { - HResult = __HResults.COR_E_THREADSTART; + HResult = HResults.COR_E_THREADSTART; } } } diff --git a/src/mscorlib/shared/System/Threading/ThreadStateException.cs b/src/mscorlib/shared/System/Threading/ThreadStateException.cs index 9477cb1ae4..d8f97a4f3b 100644 --- a/src/mscorlib/shared/System/Threading/ThreadStateException.cs +++ b/src/mscorlib/shared/System/Threading/ThreadStateException.cs @@ -21,19 +21,19 @@ namespace System.Threading public ThreadStateException() : base(SR.Arg_ThreadStateException) { - HResult = __HResults.COR_E_THREADSTATE; + HResult = HResults.COR_E_THREADSTATE; } public ThreadStateException(String message) : base(message) { - HResult = __HResults.COR_E_THREADSTATE; + HResult = HResults.COR_E_THREADSTATE; } public ThreadStateException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_THREADSTATE; + HResult = HResults.COR_E_THREADSTATE; } protected ThreadStateException(SerializationInfo info, StreamingContext context) diff --git a/src/mscorlib/shared/System/Threading/TimeoutHelper.cs b/src/mscorlib/shared/System/Threading/TimeoutHelper.cs index c66c9add92..c96a4d00d6 100644 --- a/src/mscorlib/shared/System/Threading/TimeoutHelper.cs +++ b/src/mscorlib/shared/System/Threading/TimeoutHelper.cs @@ -13,7 +13,7 @@ namespace System.Threading internal static class TimeoutHelper { /// <summary> - /// Returns the Environment.TickCount as a start time in milliseconds as a uint, TickCount tools over from postive to negative every ~ 25 days + /// Returns the Environment.TickCount as a start time in milliseconds as a uint, TickCount tools over from positive to negative every ~ 25 days /// then ~25 days to back to positive again, uint is sued to ignore the sign and double the range to 50 days /// </summary> /// <returns></returns> @@ -26,7 +26,7 @@ namespace System.Threading /// Helper function to measure and update the elapsed time /// </summary> /// <param name="startTime"> The first time (in milliseconds) observed when the wait started</param> - /// <param name="originalWaitMillisecondsTimeout">The orginal wait timeoutout in milliseconds</param> + /// <param name="originalWaitMillisecondsTimeout">The original wait timeout in milliseconds</param> /// <returns>The new wait time in milliseconds, -1 if the time expired</returns> public static int UpdateTimeOut(uint startTime, int originalWaitMillisecondsTimeout) { diff --git a/src/mscorlib/shared/System/Threading/WaitHandleCannotBeOpenedException.cs b/src/mscorlib/shared/System/Threading/WaitHandleCannotBeOpenedException.cs index 770e70d7ab..47e127191d 100644 --- a/src/mscorlib/shared/System/Threading/WaitHandleCannotBeOpenedException.cs +++ b/src/mscorlib/shared/System/Threading/WaitHandleCannotBeOpenedException.cs @@ -10,17 +10,17 @@ namespace System.Threading { public WaitHandleCannotBeOpenedException() : base(SR.Threading_WaitHandleCannotBeOpenedException) { - HResult = __HResults.COR_E_WAITHANDLECANNOTBEOPENED; + HResult = HResults.COR_E_WAITHANDLECANNOTBEOPENED; } public WaitHandleCannotBeOpenedException(String message) : base(message) { - HResult = __HResults.COR_E_WAITHANDLECANNOTBEOPENED; + HResult = HResults.COR_E_WAITHANDLECANNOTBEOPENED; } public WaitHandleCannotBeOpenedException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_WAITHANDLECANNOTBEOPENED; + HResult = HResults.COR_E_WAITHANDLECANNOTBEOPENED; } protected WaitHandleCannotBeOpenedException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/TimeSpan.cs b/src/mscorlib/shared/System/TimeSpan.cs index 39be059362..263a0a5824 100644 --- a/src/mscorlib/shared/System/TimeSpan.cs +++ b/src/mscorlib/shared/System/TimeSpan.cs @@ -316,55 +316,119 @@ namespace System } public static TimeSpan Parse(String s) { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); /* Constructs a TimeSpan from a string. Leading and trailing white space characters are allowed. */ - return TimeSpanParse.Parse(s, null); + return TimeSpanParse.Parse(s.AsReadOnlySpan(), null); } public static TimeSpan Parse(String input, IFormatProvider formatProvider) { + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + return TimeSpanParse.Parse(input.AsReadOnlySpan(), formatProvider); + } + public static TimeSpan Parse(ReadOnlySpan<char> input, IFormatProvider formatProvider = null) + { return TimeSpanParse.Parse(input, formatProvider); } public static TimeSpan ParseExact(String input, String format, IFormatProvider formatProvider) { - return TimeSpanParse.ParseExact(input, format, formatProvider, TimeSpanStyles.None); + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + return TimeSpanParse.ParseExact(input.AsReadOnlySpan(), format, formatProvider, TimeSpanStyles.None); } public static TimeSpan ParseExact(String input, String[] formats, IFormatProvider formatProvider) { - return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None); + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + return TimeSpanParse.ParseExactMultiple(input.AsReadOnlySpan(), formats, formatProvider, TimeSpanStyles.None); } public static TimeSpan ParseExact(String input, String format, IFormatProvider formatProvider, TimeSpanStyles styles) { ValidateStyles(styles, nameof(styles)); + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + return TimeSpanParse.ParseExact(input.AsReadOnlySpan(), format, formatProvider, styles); + } + public static TimeSpan ParseExact(ReadOnlySpan<char> input, string format, IFormatProvider formatProvider, TimeSpanStyles styles = TimeSpanStyles.None) + { + ValidateStyles(styles, nameof(styles)); return TimeSpanParse.ParseExact(input, format, formatProvider, styles); } public static TimeSpan ParseExact(String input, String[] formats, IFormatProvider formatProvider, TimeSpanStyles styles) { ValidateStyles(styles, nameof(styles)); + if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); + return TimeSpanParse.ParseExactMultiple(input.AsReadOnlySpan(), formats, formatProvider, styles); + } + public static TimeSpan ParseExact(ReadOnlySpan<char> input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles = TimeSpanStyles.None) + { + ValidateStyles(styles, nameof(styles)); return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, styles); } public static Boolean TryParse(String s, out TimeSpan result) { - return TimeSpanParse.TryParse(s, null, out result); + if (s == null) + { + result = default(TimeSpan); + return false; + } + return TimeSpanParse.TryParse(s.AsReadOnlySpan(), null, out result); } public static Boolean TryParse(String input, IFormatProvider formatProvider, out TimeSpan result) { + if (input == null) + { + result = default(TimeSpan); + return false; + } + return TimeSpanParse.TryParse(input.AsReadOnlySpan(), formatProvider, out result); + } + public static bool TryParse(ReadOnlySpan<char> input, out TimeSpan result, IFormatProvider formatProvider = null) + { return TimeSpanParse.TryParse(input, formatProvider, out result); } public static Boolean TryParseExact(String input, String format, IFormatProvider formatProvider, out TimeSpan result) { - return TimeSpanParse.TryParseExact(input, format, formatProvider, TimeSpanStyles.None, out result); + if (input == null) + { + result = default(TimeSpan); + return false; + } + return TimeSpanParse.TryParseExact(input.AsReadOnlySpan(), format, formatProvider, TimeSpanStyles.None, out result); } public static Boolean TryParseExact(String input, String[] formats, IFormatProvider formatProvider, out TimeSpan result) { - return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None, out result); + if (input == null) + { + result = default(TimeSpan); + return false; + } + return TimeSpanParse.TryParseExactMultiple(input.AsReadOnlySpan(), formats, formatProvider, TimeSpanStyles.None, out result); } public static Boolean TryParseExact(String input, String format, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) { ValidateStyles(styles, nameof(styles)); + if (input == null) + { + result = default(TimeSpan); + return false; + } + return TimeSpanParse.TryParseExact(input.AsReadOnlySpan(), format, formatProvider, styles, out result); + } + public static bool TryParseExact(ReadOnlySpan<char> input, string format, IFormatProvider formatProvider, out TimeSpan result, TimeSpanStyles styles = TimeSpanStyles.None) + { + ValidateStyles(styles, nameof(styles)); return TimeSpanParse.TryParseExact(input, format, formatProvider, styles, out result); } public static Boolean TryParseExact(String input, String[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result) { ValidateStyles(styles, nameof(styles)); + if (input == null) + { + result = default(TimeSpan); + return false; + } + return TimeSpanParse.TryParseExactMultiple(input.AsReadOnlySpan(), formats, formatProvider, styles, out result); + } + public static bool TryParseExact(ReadOnlySpan<char> input, string[] formats, IFormatProvider formatProvider, out TimeSpan result, TimeSpanStyles styles = TimeSpanStyles.None) + { + ValidateStyles(styles, nameof(styles)); return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, styles, out result); } public override String ToString() @@ -379,6 +443,10 @@ namespace System { return TimeSpanFormat.Format(this, format, formatProvider); } + public bool TryFormat(Span<char> destination, out int charsWritten, string format = null, IFormatProvider formatProvider = null) + { + return TimeSpanFormat.TryFormat(this, destination, out charsWritten, format, formatProvider); + } #endregion public static TimeSpan operator -(TimeSpan t) diff --git a/src/mscorlib/shared/System/TimeoutException.cs b/src/mscorlib/shared/System/TimeoutException.cs index 4ba95bc47b..b50ae2f902 100644 --- a/src/mscorlib/shared/System/TimeoutException.cs +++ b/src/mscorlib/shared/System/TimeoutException.cs @@ -20,19 +20,19 @@ namespace System public TimeoutException() : base(SR.Arg_TimeoutException) { - HResult = __HResults.COR_E_TIMEOUT; + HResult = HResults.COR_E_TIMEOUT; } public TimeoutException(String message) : base(message) { - HResult = __HResults.COR_E_TIMEOUT; + HResult = HResults.COR_E_TIMEOUT; } public TimeoutException(String message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_TIMEOUT; + HResult = HResults.COR_E_TIMEOUT; } protected TimeoutException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/Type.cs b/src/mscorlib/shared/System/Type.cs index 2ba58918a0..ab3d55bc66 100644 --- a/src/mscorlib/shared/System/Type.cs +++ b/src/mscorlib/shared/System/Type.cs @@ -47,6 +47,8 @@ namespace System public virtual bool IsSZArray { get { throw NotImplemented.ByDesign; } } public virtual bool IsVariableBoundArray => IsArray && !IsSZArray; + public virtual bool IsByRefLike => throw new NotSupportedException(SR.NotSupported_SubclassOverride); + public bool HasElementType => HasElementTypeImpl(); protected abstract bool HasElementTypeImpl(); public abstract Type GetElementType(); @@ -106,6 +108,8 @@ namespace System public bool IsValueType => IsValueTypeImpl(); protected virtual bool IsValueTypeImpl() => IsSubclassOf(typeof(ValueType)); + public virtual bool IsSignatureType => false; + public virtual bool IsSecurityCritical { get { throw NotImplemented.ByDesign; } } public virtual bool IsSecuritySafeCritical { get { throw NotImplemented.ByDesign; } } public virtual bool IsSecurityTransparent { get { throw NotImplemented.ByDesign; } } @@ -177,6 +181,27 @@ namespace System protected abstract MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers); + public MethodInfo GetMethod(string name, int genericParameterCount, Type[] types) => GetMethod(name, genericParameterCount, types, null); + public MethodInfo GetMethod(string name, int genericParameterCount, Type[] types, ParameterModifier[] modifiers) => GetMethod(name, genericParameterCount, Type.DefaultLookup, null, types, modifiers); + public MethodInfo GetMethod(string name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) => GetMethod(name, genericParameterCount, bindingAttr, binder, CallingConventions.Any, types, modifiers); + public MethodInfo GetMethod(string name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + if (name == null) + throw new ArgumentNullException(nameof(name)); + if (genericParameterCount < 0) + throw new ArgumentException(SR.ArgumentOutOfRange_NeedNonNegNum, nameof(genericParameterCount)); + if (types == null) + throw new ArgumentNullException(nameof(types)); + for (int i = 0; i < types.Length; i++) + { + if (types[i] == null) + throw new ArgumentNullException(nameof(types)); + } + return GetMethodImpl(name, genericParameterCount, bindingAttr, binder, callConvention, types, modifiers); + } + + protected virtual MethodInfo GetMethodImpl(string name, int genericParameterCount, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) => throw new NotSupportedException(); + public MethodInfo[] GetMethods() => GetMethods(Type.DefaultLookup); public abstract MethodInfo[] GetMethods(BindingFlags bindingAttr); @@ -317,6 +342,13 @@ namespace System public virtual Type MakeGenericType(params Type[] typeArguments) { throw new NotSupportedException(SR.NotSupported_SubclassOverride); } public virtual Type MakePointerType() { throw new NotSupportedException(); } + public static Type MakeGenericMethodParameter(int position) + { + if (position < 0) + throw new ArgumentException(SR.ArgumentOutOfRange_MustBeNonNegNum, nameof(position)); + return new SignatureGenericMethodParameterType(position); + } + public override string ToString() => "Type: " + Name; // Why do we add the "Type: " prefix? public override bool Equals(object o) => o == null ? false : Equals(o as Type); diff --git a/src/mscorlib/shared/System/TypeAccessException.cs b/src/mscorlib/shared/System/TypeAccessException.cs index 302dcb1ac1..ff081582b6 100644 --- a/src/mscorlib/shared/System/TypeAccessException.cs +++ b/src/mscorlib/shared/System/TypeAccessException.cs @@ -13,19 +13,19 @@ namespace System public TypeAccessException() : base(SR.Arg_TypeAccessException) { - HResult = __HResults.COR_E_TYPEACCESS; + HResult = HResults.COR_E_TYPEACCESS; } public TypeAccessException(string message) : base(message) { - HResult = __HResults.COR_E_TYPEACCESS; + HResult = HResults.COR_E_TYPEACCESS; } public TypeAccessException(string message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_TYPEACCESS; + HResult = HResults.COR_E_TYPEACCESS; } protected TypeAccessException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/TypeInitializationException.cs b/src/mscorlib/shared/System/TypeInitializationException.cs index 8d0b8a9f79..03a1bad3a7 100644 --- a/src/mscorlib/shared/System/TypeInitializationException.cs +++ b/src/mscorlib/shared/System/TypeInitializationException.cs @@ -28,7 +28,7 @@ namespace System private TypeInitializationException() : base(SR.TypeInitialization_Default) { - HResult = __HResults.COR_E_TYPEINITIALIZATION; + HResult = HResults.COR_E_TYPEINITIALIZATION; } @@ -41,14 +41,14 @@ namespace System // for Interop only, though it's not particularly useful. internal TypeInitializationException(String message) : base(message) { - HResult = __HResults.COR_E_TYPEINITIALIZATION; + HResult = HResults.COR_E_TYPEINITIALIZATION; } internal TypeInitializationException(String fullTypeName, String message, Exception innerException) : base(message, innerException) { _typeName = fullTypeName; - HResult = __HResults.COR_E_TYPEINITIALIZATION; + HResult = HResults.COR_E_TYPEINITIALIZATION; } public override void GetObjectData(SerializationInfo info, StreamingContext context) diff --git a/src/mscorlib/shared/System/TypeUnloadedException.cs b/src/mscorlib/shared/System/TypeUnloadedException.cs index c7ed71c9cb..97039bb8f1 100644 --- a/src/mscorlib/shared/System/TypeUnloadedException.cs +++ b/src/mscorlib/shared/System/TypeUnloadedException.cs @@ -11,19 +11,19 @@ namespace System public TypeUnloadedException() : base(SR.Arg_TypeUnloadedException) { - HResult = __HResults.COR_E_TYPEUNLOADED; + HResult = HResults.COR_E_TYPEUNLOADED; } public TypeUnloadedException(string message) : base(message) { - HResult = __HResults.COR_E_TYPEUNLOADED; + HResult = HResults.COR_E_TYPEUNLOADED; } public TypeUnloadedException(string message, Exception innerException) : base(message, innerException) { - HResult = __HResults.COR_E_TYPEUNLOADED; + HResult = HResults.COR_E_TYPEUNLOADED; } protected TypeUnloadedException(SerializationInfo info, StreamingContext context) diff --git a/src/mscorlib/shared/System/UInt16.cs b/src/mscorlib/shared/System/UInt16.cs new file mode 100644 index 0000000000..4cd290bc45 --- /dev/null +++ b/src/mscorlib/shared/System/UInt16.cs @@ -0,0 +1,292 @@ +// 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. + +/*============================================================ +** +** +** Purpose: This class will encapsulate a short and provide an +** Object representation of it. +** +** +===========================================================*/ + +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [CLSCompliant(false)] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct UInt16 : IComparable, IConvertible, IFormattable, IComparable<UInt16>, IEquatable<UInt16> + { + private ushort m_value; // Do not rename (binary serialization) + + public const ushort MaxValue = (ushort)0xFFFF; + public const ushort MinValue = 0; + + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns a value less than zero if this object + // null is considered to be less than any instance. + // If object is not of type UInt16, this method throws an ArgumentException. + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + if (value is UInt16) + { + return ((int)m_value - (int)(((UInt16)value).m_value)); + } + throw new ArgumentException(SR.Arg_MustBeUInt16); + } + + public int CompareTo(UInt16 value) + { + return ((int)m_value - (int)value); + } + + public override bool Equals(Object obj) + { + if (!(obj is UInt16)) + { + return false; + } + return m_value == ((UInt16)obj).m_value; + } + + [NonVersionable] + public bool Equals(UInt16 obj) + { + return m_value == obj; + } + + // Returns a HashCode for the UInt16 + public override int GetHashCode() + { + return (int)m_value; + } + + // Converts the current value to a String in base-10 with no extra padding. + public override String ToString() + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatUInt32(m_value, null, NumberFormatInfo.CurrentInfo); + } + + public String ToString(IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatUInt32(m_value, null, NumberFormatInfo.GetInstance(provider)); + } + + + public String ToString(String format) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatUInt32(m_value, format, NumberFormatInfo.CurrentInfo); + } + + public String ToString(String format, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatUInt32(m_value, format, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static ushort Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + } + + [CLSCompliant(false)] + public static ushort Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo); + } + + + [CLSCompliant(false)] + public static ushort Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static ushort Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Parse(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static ushort Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Parse(s, style, NumberFormatInfo.GetInstance(provider)); + } + + private static ushort Parse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info) + { + uint i = 0; + try + { + i = Number.ParseUInt32(s, style, info); + } + catch (OverflowException e) + { + throw new OverflowException(SR.Overflow_UInt16, e); + } + + if (i > MaxValue) throw new OverflowException(SR.Overflow_UInt16); + return (ushort)i; + } + + [CLSCompliant(false)] + public static bool TryParse(String s, out UInt16 result) + { + if (s == null) + { + result = 0; + return false; + } + + return TryParse(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + [CLSCompliant(false)] + public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out UInt16 result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return TryParse(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result); + } + + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan<char> s, out ushort result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + private static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out UInt16 result) + { + result = 0; + UInt32 i; + if (!Number.TryParseUInt32(s, style, info, out i)) + { + return false; + } + if (i > MaxValue) + { + return false; + } + result = (UInt16)i; + return true; + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.UInt16; + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return Convert.ToChar(m_value); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return m_value; + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "UInt16", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/src/mscorlib/shared/System/UInt32.cs b/src/mscorlib/shared/System/UInt32.cs new file mode 100644 index 0000000000..49835613b2 --- /dev/null +++ b/src/mscorlib/shared/System/UInt32.cs @@ -0,0 +1,269 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: This class will encapsulate an uint and +** provide an Object representation of it. +** +** +===========================================================*/ + +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [CLSCompliant(false)] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct UInt32 : IComparable, IConvertible, IFormattable, IComparable<UInt32>, IEquatable<UInt32> + { + private uint m_value; // Do not rename (binary serialization) + + public const uint MaxValue = (uint)0xffffffff; + public const uint MinValue = 0U; + + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns a value less than zero if this object + // null is considered to be less than any instance. + // If object is not of type UInt32, this method throws an ArgumentException. + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + if (value is UInt32) + { + // Need to use compare because subtraction will wrap + // to positive for very large neg numbers, etc. + uint i = (uint)value; + if (m_value < i) return -1; + if (m_value > i) return 1; + return 0; + } + throw new ArgumentException(SR.Arg_MustBeUInt32); + } + + public int CompareTo(UInt32 value) + { + // Need to use compare because subtraction will wrap + // to positive for very large neg numbers, etc. + if (m_value < value) return -1; + if (m_value > value) return 1; + return 0; + } + + public override bool Equals(Object obj) + { + if (!(obj is UInt32)) + { + return false; + } + return m_value == ((UInt32)obj).m_value; + } + + [NonVersionable] + public bool Equals(UInt32 obj) + { + return m_value == obj; + } + + // The absolute value of the int contained. + public override int GetHashCode() + { + return ((int)m_value); + } + + // The base 10 representation of the number with no extra padding. + public override String ToString() + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatUInt32(m_value, null, NumberFormatInfo.CurrentInfo); + } + + public String ToString(IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatUInt32(m_value, null, NumberFormatInfo.GetInstance(provider)); + } + + public String ToString(String format) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatUInt32(m_value, format, NumberFormatInfo.CurrentInfo); + } + + public String ToString(String format, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatUInt32(m_value, format, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static uint Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt32(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + } + + [CLSCompliant(false)] + public static uint Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt32(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo); + } + + + [CLSCompliant(false)] + public static uint Parse(String s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt32(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static uint Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt32(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static uint Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.ParseUInt32(s, style, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static bool TryParse(String s, out UInt32 result) + { + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseUInt32(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + [CLSCompliant(false)] + public static bool TryParse(String s, NumberStyles style, IFormatProvider provider, out UInt32 result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseUInt32(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result); + } + + [CLSCompliant(false)] + public static bool TryParse(ReadOnlySpan<char> s, out UInt32 result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseUInt32(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.UInt32; + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return Convert.ToChar(m_value); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return m_value; + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "UInt32", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/src/mscorlib/shared/System/UInt64.cs b/src/mscorlib/shared/System/UInt64.cs new file mode 100644 index 0000000000..424d48b8b1 --- /dev/null +++ b/src/mscorlib/shared/System/UInt64.cs @@ -0,0 +1,265 @@ +// 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. + +/*============================================================ +** +** +** Purpose: This class will encapsulate an unsigned long and +** provide an Object representation of it. +** +** +===========================================================*/ + +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace System +{ + [Serializable] + [CLSCompliant(false)] + [StructLayout(LayoutKind.Sequential)] + [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public struct UInt64 : IComparable, IConvertible, IFormattable, IComparable<UInt64>, IEquatable<UInt64> + { + private ulong m_value; // Do not rename (binary serialization) + + public const ulong MaxValue = (ulong)0xffffffffffffffffL; + public const ulong MinValue = 0x0; + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns a value less than zero if this object + // null is considered to be less than any instance. + // If object is not of type UInt64, this method throws an ArgumentException. + // + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + if (value is UInt64) + { + // Need to use compare because subtraction will wrap + // to positive for very large neg numbers, etc. + ulong i = (ulong)value; + if (m_value < i) return -1; + if (m_value > i) return 1; + return 0; + } + throw new ArgumentException(SR.Arg_MustBeUInt64); + } + + public int CompareTo(UInt64 value) + { + // Need to use compare because subtraction will wrap + // to positive for very large neg numbers, etc. + if (m_value < value) return -1; + if (m_value > value) return 1; + return 0; + } + + public override bool Equals(Object obj) + { + if (!(obj is UInt64)) + { + return false; + } + return m_value == ((UInt64)obj).m_value; + } + + [NonVersionable] + public bool Equals(UInt64 obj) + { + return m_value == obj; + } + + // The value of the lower 32 bits XORed with the uppper 32 bits. + public override int GetHashCode() + { + return ((int)m_value) ^ (int)(m_value >> 32); + } + + public override String ToString() + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatUInt64(m_value, null, NumberFormatInfo.CurrentInfo); + } + + public String ToString(IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatUInt64(m_value, null, NumberFormatInfo.GetInstance(provider)); + } + + public String ToString(String format) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatUInt64(m_value, format, NumberFormatInfo.CurrentInfo); + } + + public String ToString(String format, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return Number.FormatUInt64(m_value, format, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static ulong Parse(String s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt64(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + } + + [CLSCompliant(false)] + public static ulong Parse(String s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt64(s.AsReadOnlySpan(), style, NumberFormatInfo.CurrentInfo); + } + + [CLSCompliant(false)] + public static ulong Parse(string s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt64(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static ulong Parse(String s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseUInt64(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static ulong Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.ParseUInt64(s, style, NumberFormatInfo.GetInstance(provider)); + } + + [CLSCompliant(false)] + public static Boolean TryParse(String s, out UInt64 result) + { + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseUInt64(s.AsReadOnlySpan(), NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + } + + [CLSCompliant(false)] + public static Boolean TryParse(String s, NumberStyles style, IFormatProvider provider, out UInt64 result) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseUInt64(s.AsReadOnlySpan(), style, NumberFormatInfo.GetInstance(provider), out result); + } + + [CLSCompliant(false)] + public static Boolean TryParse(ReadOnlySpan<char> s, out UInt64 result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseUInt64(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.UInt64; + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return Convert.ToChar(m_value); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(m_value); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return m_value; + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "UInt64", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/src/mscorlib/shared/System/UnauthorizedAccessException.cs b/src/mscorlib/shared/System/UnauthorizedAccessException.cs index 667d576292..679a1a762d 100644 --- a/src/mscorlib/shared/System/UnauthorizedAccessException.cs +++ b/src/mscorlib/shared/System/UnauthorizedAccessException.cs @@ -24,19 +24,19 @@ namespace System public UnauthorizedAccessException() : base(SR.Arg_UnauthorizedAccessException) { - HResult = __HResults.COR_E_UNAUTHORIZEDACCESS; + HResult = HResults.COR_E_UNAUTHORIZEDACCESS; } public UnauthorizedAccessException(String message) : base(message) { - HResult = __HResults.COR_E_UNAUTHORIZEDACCESS; + HResult = HResults.COR_E_UNAUTHORIZEDACCESS; } public UnauthorizedAccessException(String message, Exception inner) : base(message, inner) { - HResult = __HResults.COR_E_UNAUTHORIZEDACCESS; + HResult = HResults.COR_E_UNAUTHORIZEDACCESS; } protected UnauthorizedAccessException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/mscorlib/shared/System/UnitySerializationHolder.cs b/src/mscorlib/shared/System/UnitySerializationHolder.cs new file mode 100644 index 0000000000..bbfebff8a6 --- /dev/null +++ b/src/mscorlib/shared/System/UnitySerializationHolder.cs @@ -0,0 +1,66 @@ +// 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.Runtime.Serialization; + +namespace System +{ + /// <summary> + /// Holds Null class for which we guarantee that there is only ever one instance of. + /// This only exists for compatibility with .NET Framework. + /// </summary> + [Serializable] +#if CORECLR + internal +#else + public // On CoreRT this must be public. +#endif + sealed class UnitySerializationHolder : ISerializable, IObjectReference + { + internal const int NullUnity = 0x0002; + private readonly int _unityType; + private readonly string _data; + + /// <summary> + /// A helper method that returns the SerializationInfo that a class utilizing + /// UnitySerializationHelper should return from a call to GetObjectData. It contains + /// the unityType (defined above) and any optional data (used only for the reflection types). + /// </summary> + internal static void GetUnitySerializationInfo(SerializationInfo info, int unityType) + { + info.SetType(typeof(UnitySerializationHolder)); + info.AddValue("Data", null, typeof(string)); + info.AddValue("UnityType", unityType); + info.AddValue("AssemblyName", string.Empty); + } + + public UnitySerializationHolder(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + // We are ignoring any other serialization input as we are only concerned about DBNull. + // We also store data and use it for erorr logging. + _unityType = info.GetInt32("UnityType"); + _data = info.GetString("Data"); + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) => + throw new NotSupportedException(SR.NotSupported_UnitySerHolder); + + public object GetRealObject(StreamingContext context) + { + // We are only support deserializing DBNull and throwing for everything else. + if (_unityType != NullUnity) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidUnity, _data ?? "UnityType")); + } + + // We are always returning the same DBNull instance. + return DBNull.Value; + } + } +} diff --git a/src/mscorlib/shared/System/Version.cs b/src/mscorlib/shared/System/Version.cs index a2140ab137..9c66d9b904 100644 --- a/src/mscorlib/shared/System/Version.cs +++ b/src/mscorlib/shared/System/Version.cs @@ -197,82 +197,118 @@ namespace System return accumulator; } - public override String ToString() + public override string ToString() => + ToString(DefaultFormatFieldCount); + + public string ToString(int fieldCount) => + fieldCount == 0 ? string.Empty : + fieldCount == 1 ? _Major.ToString() : + StringBuilderCache.GetStringAndRelease(ToCachedStringBuilder(fieldCount)); + + public bool TryFormat(Span<char> destination, out int charsWritten) => + TryFormat(destination, DefaultFormatFieldCount, out charsWritten); + + public bool TryFormat(Span<char> destination, int fieldCount, out int charsWritten) { - if (_Build == -1) return (ToString(2)); - if (_Revision == -1) return (ToString(3)); - return (ToString(4)); + if (fieldCount == 0) + { + charsWritten = 0; + return true; + } + + // TODO https://github.com/dotnet/corefx/issues/22403: fieldCount==1 can just use int.TryFormat + + StringBuilder sb = ToCachedStringBuilder(fieldCount); + if (sb.Length <= destination.Length) + { + sb.CopyTo(0, destination, sb.Length); + StringBuilderCache.Release(sb); + charsWritten = sb.Length; + return true; + } + + StringBuilderCache.Release(sb); + charsWritten = 0; + return false; } - public String ToString(int fieldCount) + private int DefaultFormatFieldCount => + _Build == -1 ? 2 : + _Revision == -1 ? 3 : + 4; + + private StringBuilder ToCachedStringBuilder(int fieldCount) { - StringBuilder sb; - switch (fieldCount) + if (fieldCount == 1) { - case 0: - return (String.Empty); - case 1: - return (_Major.ToString()); - case 2: - sb = StringBuilderCache.Acquire(); - AppendPositiveNumber(_Major, sb); - sb.Append('.'); - AppendPositiveNumber(_Minor, sb); - return StringBuilderCache.GetStringAndRelease(sb); - default: - if (_Build == -1) - throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "2"), nameof(fieldCount)); + StringBuilder sb = StringBuilderCache.Acquire(); + AppendNonNegativeNumber(_Major, sb); + return sb; + } + else if (fieldCount == 2) + { + StringBuilder sb = StringBuilderCache.Acquire(); + AppendNonNegativeNumber(_Major, sb); + sb.Append('.'); + AppendNonNegativeNumber(_Minor, sb); + return sb; + } + else + { + if (_Build == -1) + { + throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "2"), nameof(fieldCount)); + } - if (fieldCount == 3) - { - sb = StringBuilderCache.Acquire(); - AppendPositiveNumber(_Major, sb); - sb.Append('.'); - AppendPositiveNumber(_Minor, sb); - sb.Append('.'); - AppendPositiveNumber(_Build, sb); - return StringBuilderCache.GetStringAndRelease(sb); - } + if (fieldCount == 3) + { + StringBuilder sb = StringBuilderCache.Acquire(); + AppendNonNegativeNumber(_Major, sb); + sb.Append('.'); + AppendNonNegativeNumber(_Minor, sb); + sb.Append('.'); + AppendNonNegativeNumber(_Build, sb); + return sb; + } - if (_Revision == -1) - throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "3"), nameof(fieldCount)); + if (_Revision == -1) + { + throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "3"), nameof(fieldCount)); + } - if (fieldCount == 4) - { - sb = StringBuilderCache.Acquire(); - AppendPositiveNumber(_Major, sb); - sb.Append('.'); - AppendPositiveNumber(_Minor, sb); - sb.Append('.'); - AppendPositiveNumber(_Build, sb); - sb.Append('.'); - AppendPositiveNumber(_Revision, sb); - return StringBuilderCache.GetStringAndRelease(sb); - } + if (fieldCount == 4) + { + StringBuilder sb = StringBuilderCache.Acquire(); + AppendNonNegativeNumber(_Major, sb); + sb.Append('.'); + AppendNonNegativeNumber(_Minor, sb); + sb.Append('.'); + AppendNonNegativeNumber(_Build, sb); + sb.Append('.'); + AppendNonNegativeNumber(_Revision, sb); + return sb; + } - throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "4"), nameof(fieldCount)); + throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "4"), nameof(fieldCount)); } } + // TODO https://github.com/dotnet/corefx/issues/22616: + // Use StringBuilder.Append(int) once it's been updated to use spans internally. // - // AppendPositiveNumber is an optimization to append a number to a StringBuilder object without + // AppendNonNegativeNumber is an optimization to append a number to a StringBuilder object without // doing any boxing and not even creating intermediate string. // Note: as we always have positive numbers then it is safe to convert the number to string // regardless of the current culture as we'll not have any punctuation marks in the number - // - private const int ZERO_CHAR_VALUE = (int)'0'; - private static void AppendPositiveNumber(int num, StringBuilder sb) + private static void AppendNonNegativeNumber(int num, StringBuilder sb) { Debug.Assert(num >= 0, "AppendPositiveNumber expect positive numbers"); int index = sb.Length; - int reminder; - do { - reminder = num % 10; - num = num / 10; - sb.Insert(index, (char)(ZERO_CHAR_VALUE + reminder)); + num = Math.DivRem(num, 10, out int remainder); + sb.Insert(index, (char)('0' + remainder)); } while (num > 0); } @@ -282,104 +318,108 @@ namespace System { throw new ArgumentNullException(nameof(input)); } - Contract.EndContractBlock(); - VersionResult r = new VersionResult(); - r.Init(nameof(input), true); - if (!TryParseVersion(input, ref r)) - { - throw r.GetVersionParseException(); - } - return r.m_parsedVersion; + return ParseVersion(input.AsReadOnlySpan(), throwOnFailure: true); } - public static bool TryParse(string input, out Version result) - { - VersionResult r = new VersionResult(); - r.Init(nameof(input), false); - bool b = TryParseVersion(input, ref r); - result = r.m_parsedVersion; - return b; - } + public static Version Parse(ReadOnlySpan<char> input) => + ParseVersion(input, throwOnFailure: true); - private static bool TryParseVersion(string version, ref VersionResult result) + public static bool TryParse(string input, out Version result) { - int major, minor, build, revision; - - if ((Object)version == null) + if (input == null) { - result.SetFailure(ParseFailureKind.ArgumentNullException); + result = null; return false; } - String[] parsedComponents = version.Split('.'); - int parsedComponentsLength = parsedComponents.Length; - if ((parsedComponentsLength < 2) || (parsedComponentsLength > 4)) + return (result = ParseVersion(input.AsReadOnlySpan(), throwOnFailure: false)) != null; + } + + public static bool TryParse(ReadOnlySpan<char> input, out Version result) => + (result = ParseVersion(input, throwOnFailure: false)) != null; + + private static Version ParseVersion(ReadOnlySpan<char> input, bool throwOnFailure) + { + // Find the separator between major and minor. It must exist. + int majorEnd = input.IndexOf('.'); + if (majorEnd < 0) { - result.SetFailure(ParseFailureKind.ArgumentException); - return false; + if (throwOnFailure) throw new ArgumentException(SR.Arg_VersionString, nameof(input)); + return null; } - if (!TryParseComponent(parsedComponents[0], nameof(version), ref result, out major)) + // Find the ends of the optional minor and build portions. + // We musn't have any separators after build. + int buildEnd = -1; + int minorEnd = input.IndexOf('.', majorEnd + 1); + if (minorEnd != -1) { - return false; + buildEnd = input.IndexOf('.', minorEnd + 1); + if (buildEnd != -1) + { + if (input.IndexOf('.', buildEnd + 1) != -1) + { + if (throwOnFailure) throw new ArgumentException(SR.Arg_VersionString, nameof(input)); + return null; + } + } } - if (!TryParseComponent(parsedComponents[1], nameof(version), ref result, out minor)) + int major, minor, build, revision; + + // Parse the major version + if (!TryParseComponent(input.Slice(0, majorEnd), nameof(input), throwOnFailure, out major)) { - return false; + return null; } - parsedComponentsLength -= 2; - - if (parsedComponentsLength > 0) + if (minorEnd != -1) { - if (!TryParseComponent(parsedComponents[2], "build", ref result, out build)) + // If there's more than a major and minor, parse the minor, too. + if (!TryParseComponent(input.Slice(majorEnd + 1, minorEnd - majorEnd - 1), nameof(input), throwOnFailure, out minor)) { - return false; + return null; } - parsedComponentsLength--; - - if (parsedComponentsLength > 0) + if (buildEnd != -1) { - if (!TryParseComponent(parsedComponents[3], "revision", ref result, out revision)) - { - return false; - } - else - { - result.m_parsedVersion = new Version(major, minor, build, revision); - } + // major.minor.build.revision + return + TryParseComponent(input.Slice(minorEnd + 1, buildEnd - minorEnd - 1), nameof(build), throwOnFailure, out build) && + TryParseComponent(input.Slice(buildEnd + 1), nameof(revision), throwOnFailure, out revision) ? + new Version(major, minor, build, revision) : + null; } else { - result.m_parsedVersion = new Version(major, minor, build); + // major.minor.build + return TryParseComponent(input.Slice(minorEnd + 1), nameof(build), throwOnFailure, out build) ? + new Version(major, minor, build) : + null; } } else { - result.m_parsedVersion = new Version(major, minor); + // major.minor + return TryParseComponent(input.Slice(majorEnd + 1), nameof(input), throwOnFailure, out minor) ? + new Version(major, minor) : + null; } - - return true; } - private static bool TryParseComponent(string component, string componentName, ref VersionResult result, out int parsedComponent) + private static bool TryParseComponent(ReadOnlySpan<char> component, string componentName, bool throwOnFailure, out int parsedComponent) { - if (!Int32.TryParse(component, NumberStyles.Integer, CultureInfo.InvariantCulture, out parsedComponent)) + if (throwOnFailure) { - result.SetFailure(ParseFailureKind.FormatException, component); - return false; - } - - if (parsedComponent < 0) - { - result.SetFailure(ParseFailureKind.ArgumentOutOfRangeException, componentName); - return false; + if ((parsedComponent = int.Parse(component, NumberStyles.Integer, CultureInfo.InvariantCulture)) < 0) + { + throw new ArgumentOutOfRangeException(componentName, SR.ArgumentOutOfRange_Version); + } + return true; } - return true; + return int.TryParse(component, out parsedComponent, NumberStyles.Integer, CultureInfo.InvariantCulture) && parsedComponent >= 0; } public static bool operator ==(Version v1, Version v2) @@ -422,75 +462,5 @@ namespace System { return (v2 <= v1); } - - internal enum ParseFailureKind - { - ArgumentNullException, - ArgumentException, - ArgumentOutOfRangeException, - FormatException - } - - internal struct VersionResult - { - internal Version m_parsedVersion; - internal ParseFailureKind m_failure; - internal string m_exceptionArgument; - internal string m_argumentName; - internal bool m_canThrow; - - internal void Init(string argumentName, bool canThrow) - { - m_canThrow = canThrow; - m_argumentName = argumentName; - } - - internal void SetFailure(ParseFailureKind failure) - { - SetFailure(failure, String.Empty); - } - - internal void SetFailure(ParseFailureKind failure, string argument) - { - m_failure = failure; - m_exceptionArgument = argument; - if (m_canThrow) - { - throw GetVersionParseException(); - } - } - - internal Exception GetVersionParseException() - { - switch (m_failure) - { - case ParseFailureKind.ArgumentNullException: - return new ArgumentNullException(m_argumentName); - case ParseFailureKind.ArgumentException: - return new ArgumentException(SR.Arg_VersionString); - case ParseFailureKind.ArgumentOutOfRangeException: - return new ArgumentOutOfRangeException(m_exceptionArgument, SR.ArgumentOutOfRange_Version); - case ParseFailureKind.FormatException: - // Regenerate the FormatException as would be thrown by Int32.Parse() - try - { - Int32.Parse(m_exceptionArgument, CultureInfo.InvariantCulture); - } - catch (FormatException e) - { - return e; - } - catch (OverflowException e) - { - return e; - } - Debug.Assert(false, "Int32.Parse() did not throw exception but TryParse failed: " + m_exceptionArgument); - return new FormatException(SR.Format_InvalidString); - default: - Debug.Assert(false, "Unmatched case in Version.GetVersionParseException() for value: " + m_failure); - return new ArgumentException(SR.Arg_VersionString); - } - } - } } } |