diff options
author | Jiyoung Yun <jy910.yun@samsung.com> | 2017-04-13 14:17:19 +0900 |
---|---|---|
committer | Jiyoung Yun <jy910.yun@samsung.com> | 2017-04-13 14:17:19 +0900 |
commit | a56e30c8d33048216567753d9d3fefc2152af8ac (patch) | |
tree | 7e5d979695fc4a431740982eb1cfecc2898b23a5 /src/mscorlib/shared/System | |
parent | 4b11dc566a5bbfa1378d6266525c281b028abcc8 (diff) | |
download | coreclr-a56e30c8d33048216567753d9d3fefc2152af8ac.tar.gz coreclr-a56e30c8d33048216567753d9d3fefc2152af8ac.tar.bz2 coreclr-a56e30c8d33048216567753d9d3fefc2152af8ac.zip |
Imported Upstream version 2.0.0.11353upstream/2.0.0.11353
Diffstat (limited to 'src/mscorlib/shared/System')
441 files changed, 86190 insertions, 0 deletions
diff --git a/src/mscorlib/shared/System/Action.cs b/src/mscorlib/shared/System/Action.cs new file mode 100644 index 0000000000..b82c14d9dc --- /dev/null +++ b/src/mscorlib/shared/System/Action.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. + +namespace System +{ + public delegate void Action<in T>(T obj); + + public delegate void Action(); + public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2); + public delegate void Action<in T1, in T2, in T3>(T1 arg1, T2 arg2, T3 arg3); + public delegate void Action<in T1, in T2, in T3, in T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + public delegate TResult Func<out TResult>(); + public delegate TResult Func<in T, out TResult>(T arg); + public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2); + public delegate TResult Func<in T1, in T2, in T3, out TResult>(T1 arg1, T2 arg2, T3 arg3); + public delegate TResult Func<in T1, in T2, in T3, in T4, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + public delegate void Action<in T1, in T2, in T3, in T4, in T5>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); + public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6); + public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7); + public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8); + + public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); + public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6); + public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7); + public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8); + + public delegate int Comparison<in T>(T x, T y); + + public delegate TOutput Converter<in TInput, out TOutput>(TInput input); + + public delegate bool Predicate<in T>(T obj); +} diff --git a/src/mscorlib/shared/System/ApplicationException.cs b/src/mscorlib/shared/System/ApplicationException.cs new file mode 100644 index 0000000000..900feb57f9 --- /dev/null +++ b/src/mscorlib/shared/System/ApplicationException.cs @@ -0,0 +1,56 @@ +// 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 base class for all "less serious" exceptions that must be +** declared or caught. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + // The ApplicationException is the base class for nonfatal, + // application errors that occur. These exceptions are generated + // (i.e., thrown) by an application, not the Runtime. Applications that need + // to create their own exceptions do so by extending this class. + // ApplicationException extends but adds no new functionality to + // RecoverableException. + // + [Serializable] + public class ApplicationException : Exception + { + // Creates a new ApplicationException with its message string set to + // the empty string, its HRESULT set to COR_E_APPLICATION, + // and its ExceptionInfo reference set to null. + public ApplicationException() + : base(SR.Arg_ApplicationException) + { + HResult = __HResults.COR_E_APPLICATION; + } + + // Creates a new ApplicationException with its message string set to + // message, its HRESULT set to COR_E_APPLICATION, + // and its ExceptionInfo reference set to null. + // + public ApplicationException(String message) + : base(message) + { + HResult = __HResults.COR_E_APPLICATION; + } + + public ApplicationException(String message, Exception innerException) + : base(message, innerException) + { + 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 new file mode 100644 index 0000000000..96afbe10d9 --- /dev/null +++ b/src/mscorlib/shared/System/ArgumentException.cs @@ -0,0 +1,97 @@ +// 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 for invalid arguments to a method. +** +** +=============================================================================*/ + +using System.Globalization; +using System.Runtime.Serialization; + +namespace System +{ + // The ArgumentException is thrown when an argument does not meet + // the contract of the method. Ideally it should give a meaningful error + // message describing what was wrong and which parameter is incorrect. + // + [Serializable] + public class ArgumentException : SystemException + { + private String _paramName; + + // Creates a new ArgumentException with its message + // string set to the empty string. + public ArgumentException() + : base(SR.Arg_ArgumentException) + { + HResult = __HResults.COR_E_ARGUMENT; + } + + // Creates a new ArgumentException with its message + // string set to message. + // + public ArgumentException(String message) + : base(message) + { + HResult = __HResults.COR_E_ARGUMENT; + } + + public ArgumentException(String message, Exception innerException) + : base(message, innerException) + { + HResult = __HResults.COR_E_ARGUMENT; + } + + public ArgumentException(String message, String paramName, Exception innerException) + : base(message, innerException) + { + _paramName = paramName; + HResult = __HResults.COR_E_ARGUMENT; + } + + public ArgumentException(String message, String paramName) + : base(message) + { + _paramName = paramName; + HResult = __HResults.COR_E_ARGUMENT; + } + + protected ArgumentException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + _paramName = info.GetString("ParamName"); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("ParamName", _paramName, typeof(String)); + } + + public override String Message + { + get + { + String s = base.Message; + if (!String.IsNullOrEmpty(_paramName)) + { + String resourceString = SR.Format(SR.Arg_ParamName_Name, _paramName); + return s + Environment.NewLine + resourceString; + } + else + return s; + } + } + + public virtual String ParamName + { + get { return _paramName; } + } + } +} diff --git a/src/mscorlib/shared/System/ArgumentNullException.cs b/src/mscorlib/shared/System/ArgumentNullException.cs new file mode 100644 index 0000000000..3a86223ccf --- /dev/null +++ b/src/mscorlib/shared/System/ArgumentNullException.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. + +/*============================================================================= +** +** +** +** Purpose: Exception class for null arguments to a method. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + // The ArgumentException is thrown when an argument + // is null when it shouldn't be. + // + [Serializable] + public class ArgumentNullException : ArgumentException + { + // Creates a new ArgumentNullException with its message + // string set to a default message explaining an argument was null. + public ArgumentNullException() + : base(SR.ArgumentNull_Generic) + { + // Use E_POINTER - COM used that for null pointers. Description is "invalid pointer" + HResult = __HResults.E_POINTER; + } + + public ArgumentNullException(String paramName) + : base(SR.ArgumentNull_Generic, paramName) + { + HResult = __HResults.E_POINTER; + } + + public ArgumentNullException(String message, Exception innerException) + : base(message, innerException) + { + HResult = __HResults.E_POINTER; + } + + public ArgumentNullException(String paramName, String message) + : base(message, paramName) + { + HResult = __HResults.E_POINTER; + } + + protected ArgumentNullException(SerializationInfo info, StreamingContext context) : base(info, context) { } + } +} diff --git a/src/mscorlib/shared/System/ArithmeticException.cs b/src/mscorlib/shared/System/ArithmeticException.cs new file mode 100644 index 0000000000..081ba454f5 --- /dev/null +++ b/src/mscorlib/shared/System/ArithmeticException.cs @@ -0,0 +1,51 @@ +// 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 for bad arithmetic conditions! +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + // The ArithmeticException is thrown when overflow or underflow + // occurs. + // + [Serializable] + public class ArithmeticException : SystemException + { + // Creates a new ArithmeticException with its message string set to + // the empty string, its HRESULT set to COR_E_ARITHMETIC, + // and its ExceptionInfo reference set to null. + public ArithmeticException() + : base(SR.Arg_ArithmeticException) + { + HResult = __HResults.COR_E_ARITHMETIC; + } + + // Creates a new ArithmeticException with its message string set to + // message, its HRESULT set to COR_E_ARITHMETIC, + // and its ExceptionInfo reference set to null. + // + public ArithmeticException(String message) + : base(message) + { + HResult = __HResults.COR_E_ARITHMETIC; + } + + public ArithmeticException(String message, Exception innerException) + : base(message, innerException) + { + 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 new file mode 100644 index 0000000000..3e941fdf8e --- /dev/null +++ b/src/mscorlib/shared/System/ArrayTypeMismatchException.cs @@ -0,0 +1,51 @@ +// 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 arrays are of different primitive types. +** +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + // The ArrayMismatchException is thrown when an attempt to store + // an object of the wrong type within an array occurs. + // + [Serializable] + public class ArrayTypeMismatchException : SystemException + { + // Creates a new ArrayMismatchException with its message string set to + // the empty string, its HRESULT set to COR_E_ARRAYTYPEMISMATCH, + // and its ExceptionInfo reference set to null. + public ArrayTypeMismatchException() + : base(SR.Arg_ArrayTypeMismatchException) + { + HResult = __HResults.COR_E_ARRAYTYPEMISMATCH; + } + + // Creates a new ArrayMismatchException with its message string set to + // message, its HRESULT set to COR_E_ARRAYTYPEMISMATCH, + // and its ExceptionInfo reference set to null. + // + public ArrayTypeMismatchException(String message) + : base(message) + { + HResult = __HResults.COR_E_ARRAYTYPEMISMATCH; + } + + public ArrayTypeMismatchException(String message, Exception innerException) + : base(message, innerException) + { + HResult = __HResults.COR_E_ARRAYTYPEMISMATCH; + } + + protected ArrayTypeMismatchException(SerializationInfo info, StreamingContext context) : base(info, context) { } + } +} diff --git a/src/mscorlib/shared/System/AssemblyLoadEventArgs.cs b/src/mscorlib/shared/System/AssemblyLoadEventArgs.cs new file mode 100644 index 0000000000..d7e5249693 --- /dev/null +++ b/src/mscorlib/shared/System/AssemblyLoadEventArgs.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 System.Reflection; + +namespace System +{ + public class AssemblyLoadEventArgs : EventArgs + { + public AssemblyLoadEventArgs(Assembly loadedAssembly) + { + LoadedAssembly = loadedAssembly; + } + + public Assembly LoadedAssembly { get; } + } +} diff --git a/src/mscorlib/shared/System/AssemblyLoadEventHandler.cs b/src/mscorlib/shared/System/AssemblyLoadEventHandler.cs new file mode 100644 index 0000000000..752379fdc7 --- /dev/null +++ b/src/mscorlib/shared/System/AssemblyLoadEventHandler.cs @@ -0,0 +1,8 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System +{ + public delegate void AssemblyLoadEventHandler(object sender, AssemblyLoadEventArgs args); +} diff --git a/src/mscorlib/shared/System/AsyncCallback.cs b/src/mscorlib/shared/System/AsyncCallback.cs new file mode 100644 index 0000000000..5c49535cff --- /dev/null +++ b/src/mscorlib/shared/System/AsyncCallback.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. + +/*============================================================ +** +** Interface: AsyncCallbackDelegate +** +** Purpose: Type of callback for async operations +** +===========================================================*/ + +namespace System +{ + [Serializable] + public delegate void AsyncCallback(IAsyncResult ar); +} diff --git a/src/mscorlib/shared/System/AttributeTargets.cs b/src/mscorlib/shared/System/AttributeTargets.cs new file mode 100644 index 0000000000..fdfa4ab730 --- /dev/null +++ b/src/mscorlib/shared/System/AttributeTargets.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 +{ + // Enum used to indicate all the elements of the + // VOS it is valid to attach this element to. + [Flags] + [Serializable] + public enum AttributeTargets + { + Assembly = 0x0001, + Module = 0x0002, + Class = 0x0004, + Struct = 0x0008, + Enum = 0x0010, + Constructor = 0x0020, + Method = 0x0040, + Property = 0x0080, + Field = 0x0100, + Event = 0x0200, + Interface = 0x0400, + Parameter = 0x0800, + Delegate = 0x1000, + ReturnValue = 0x2000, + GenericParameter = 0x4000, + + All = Assembly | Module | Class | Struct | Enum | Constructor | + Method | Property | Field | Event | Interface | Parameter | + Delegate | ReturnValue | GenericParameter + } +} diff --git a/src/mscorlib/shared/System/AttributeUsageAttribute.cs b/src/mscorlib/shared/System/AttributeUsageAttribute.cs new file mode 100644 index 0000000000..6f9aeb20f3 --- /dev/null +++ b/src/mscorlib/shared/System/AttributeUsageAttribute.cs @@ -0,0 +1,58 @@ +// 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 class denotes how to specify the usage of an attribute +** +** +===========================================================*/ + +using System.Reflection; + +namespace System +{ + /* By default, attributes are inherited and multiple attributes are not allowed */ + [Serializable] + [AttributeUsage(AttributeTargets.Class, Inherited = true)] + public sealed class AttributeUsageAttribute : Attribute + { + private AttributeTargets _attributeTarget = AttributeTargets.All; // Defaults to all + private bool _allowMultiple = false; // Defaults to false + private bool _inherited = true; // Defaults to true + + internal static AttributeUsageAttribute Default = new AttributeUsageAttribute(AttributeTargets.All); + + //Constructors + public AttributeUsageAttribute(AttributeTargets validOn) + { + _attributeTarget = validOn; + } + internal AttributeUsageAttribute(AttributeTargets validOn, bool allowMultiple, bool inherited) + { + _attributeTarget = validOn; + _allowMultiple = allowMultiple; + _inherited = inherited; + } + + public AttributeTargets ValidOn + { + get { return _attributeTarget; } + } + + public bool AllowMultiple + { + get { return _allowMultiple; } + set { _allowMultiple = value; } + } + + public bool Inherited + { + get { return _inherited; } + set { _inherited = value; } + } + } +} diff --git a/src/mscorlib/shared/System/Buffers/ArrayPool.cs b/src/mscorlib/shared/System/Buffers/ArrayPool.cs new file mode 100644 index 0000000000..77a07f7fa5 --- /dev/null +++ b/src/mscorlib/shared/System/Buffers/ArrayPool.cs @@ -0,0 +1,100 @@ +// 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.Buffers +{ + /// <summary> + /// Provides a resource pool that enables reusing instances of type <see cref="T:T[]"/>. + /// </summary> + /// <remarks> + /// <para> + /// Renting and returning buffers with an <see cref="ArrayPool{T}"/> can increase performance + /// in situations where arrays are created and destroyed frequently, resulting in significant + /// memory pressure on the garbage collector. + /// </para> + /// <para> + /// This class is thread-safe. All members may be used by multiple threads concurrently. + /// </para> + /// </remarks> + public abstract class ArrayPool<T> + { + /// <summary> + /// Retrieves a shared <see cref="ArrayPool{T}"/> instance. + /// </summary> + /// <remarks> + /// The shared pool provides a default implementation of <see cref="ArrayPool{T}"/> + /// that's intended for general applicability. It maintains arrays of multiple sizes, and + /// may hand back a larger array than was actually requested, but will never hand back a smaller + /// array than was requested. Renting a buffer from it with <see cref="Rent"/> will result in an + /// existing buffer being taken from the pool if an appropriate buffer is available or in a new + /// buffer being allocated if one is not available. + /// byte[] and char[] are the most commonly pooled array types. For these we use a special pool type + /// optimized for very fast access speeds, at the expense of more memory consumption. + /// The shared pool instance is created lazily on first access. + /// </remarks> + public static ArrayPool<T> Shared { get; } = + typeof(T) == typeof(byte) || typeof(T) == typeof(char) ? new TlsOverPerCoreLockedStacksArrayPool<T>() : + Create(); + + /// <summary> + /// Creates a new <see cref="ArrayPool{T}"/> instance using default configuration options. + /// </summary> + /// <returns>A new <see cref="ArrayPool{T}"/> instance.</returns> + public static ArrayPool<T> Create() => new ConfigurableArrayPool<T>(); + + /// <summary> + /// Creates a new <see cref="ArrayPool{T}"/> instance using custom configuration options. + /// </summary> + /// <param name="maxArrayLength">The maximum length of array instances that may be stored in the pool.</param> + /// <param name="maxArraysPerBucket"> + /// The maximum number of array instances that may be stored in each bucket in the pool. The pool + /// groups arrays of similar lengths into buckets for faster access. + /// </param> + /// <returns>A new <see cref="ArrayPool{T}"/> instance with the specified configuration options.</returns> + /// <remarks> + /// The created pool will group arrays into buckets, with no more than <paramref name="maxArraysPerBucket"/> + /// in each bucket and with those arrays not exceeding <paramref name="maxArrayLength"/> in length. + /// </remarks> + public static ArrayPool<T> Create(int maxArrayLength, int maxArraysPerBucket) => + new ConfigurableArrayPool<T>(maxArrayLength, maxArraysPerBucket); + + /// <summary> + /// Retrieves a buffer that is at least the requested length. + /// </summary> + /// <param name="minimumLength">The minimum length of the array needed.</param> + /// <returns> + /// An <see cref="T:T[]"/> that is at least <paramref name="minimumLength"/> in length. + /// </returns> + /// <remarks> + /// This buffer is loaned to the caller and should be returned to the same pool via + /// <see cref="Return"/> so that it may be reused in subsequent usage of <see cref="Rent"/>. + /// It is not a fatal error to not return a rented buffer, but failure to do so may lead to + /// decreased application performance, as the pool may need to create a new buffer to replace + /// the one lost. + /// </remarks> + public abstract T[] Rent(int minimumLength); + + /// <summary> + /// Returns to the pool an array that was previously obtained via <see cref="Rent"/> on the same + /// <see cref="ArrayPool{T}"/> instance. + /// </summary> + /// <param name="array"> + /// The buffer previously obtained from <see cref="Rent"/> to return to the pool. + /// </param> + /// <param name="clearArray"> + /// If <c>true</c> and if the pool will store the buffer to enable subsequent reuse, <see cref="Return"/> + /// will clear <paramref name="array"/> of its contents so that a subsequent consumer via <see cref="Rent"/> + /// will not see the previous consumer's content. If <c>false</c> or if the pool will release the buffer, + /// the array's contents are left unchanged. + /// </param> + /// <remarks> + /// Once a buffer has been returned to the pool, the caller gives up all ownership of the buffer + /// and must not use it. The reference returned from a given call to <see cref="Rent"/> must only be + /// returned via <see cref="Return"/> once. The default <see cref="ArrayPool{T}"/> + /// may hold onto the returned buffer in order to rent it again, or it may release the returned buffer + /// if it's determined that the pool already has enough buffers stored. + /// </remarks> + public abstract void Return(T[] array, bool clearArray = false); + } +} diff --git a/src/mscorlib/shared/System/Buffers/ConfigurableArrayPool.cs b/src/mscorlib/shared/System/Buffers/ConfigurableArrayPool.cs new file mode 100644 index 0000000000..f7b6034d20 --- /dev/null +++ b/src/mscorlib/shared/System/Buffers/ConfigurableArrayPool.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. + +using System.Diagnostics; +using System.Threading; + +namespace System.Buffers +{ + internal sealed partial class ConfigurableArrayPool<T> : ArrayPool<T> + { + /// <summary>The default maximum length of each array in the pool (2^20).</summary> + private const int DefaultMaxArrayLength = 1024 * 1024; + /// <summary>The default maximum number of arrays per bucket that are available for rent.</summary> + private const int DefaultMaxNumberOfArraysPerBucket = 50; + + private readonly Bucket[] _buckets; + + internal ConfigurableArrayPool() : this(DefaultMaxArrayLength, DefaultMaxNumberOfArraysPerBucket) + { + } + + internal ConfigurableArrayPool(int maxArrayLength, int maxArraysPerBucket) + { + if (maxArrayLength <= 0) + { + throw new ArgumentOutOfRangeException(nameof(maxArrayLength)); + } + if (maxArraysPerBucket <= 0) + { + throw new ArgumentOutOfRangeException(nameof(maxArraysPerBucket)); + } + + // Our bucketing algorithm has a min length of 2^4 and a max length of 2^30. + // Constrain the actual max used to those values. + const int MinimumArrayLength = 0x10, MaximumArrayLength = 0x40000000; + if (maxArrayLength > MaximumArrayLength) + { + maxArrayLength = MaximumArrayLength; + } + else if (maxArrayLength < MinimumArrayLength) + { + maxArrayLength = MinimumArrayLength; + } + + // Create the buckets. + int poolId = Id; + int maxBuckets = Utilities.SelectBucketIndex(maxArrayLength); + var buckets = new Bucket[maxBuckets + 1]; + for (int i = 0; i < buckets.Length; i++) + { + buckets[i] = new Bucket(Utilities.GetMaxSizeForBucket(i), maxArraysPerBucket, poolId); + } + _buckets = buckets; + } + + /// <summary>Gets an ID for the pool to use with events.</summary> + private int Id => GetHashCode(); + + public override T[] Rent(int minimumLength) + { + // Arrays can't be smaller than zero. We allow requesting zero-length arrays (even though + // pooling such an array isn't valuable) as it's a valid length array, and we want the pool + // to be usable in general instead of using `new`, even for computed lengths. + if (minimumLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(minimumLength)); + } + else if (minimumLength == 0) + { + // No need for events with the empty array. Our pool is effectively infinite + // and we'll never allocate for rents and never store for returns. + return Array.Empty<T>(); + } + + var log = ArrayPoolEventSource.Log; + T[] buffer = null; + + int index = Utilities.SelectBucketIndex(minimumLength); + if (index < _buckets.Length) + { + // Search for an array starting at the 'index' bucket. If the bucket is empty, bump up to the + // next higher bucket and try that one, but only try at most a few buckets. + const int MaxBucketsToTry = 2; + int i = index; + do + { + // Attempt to rent from the bucket. If we get a buffer from it, return it. + buffer = _buckets[i].Rent(); + if (buffer != null) + { + if (log.IsEnabled()) + { + log.BufferRented(buffer.GetHashCode(), buffer.Length, Id, _buckets[i].Id); + } + return buffer; + } + } + while (++i < _buckets.Length && i != index + MaxBucketsToTry); + + // The pool was exhausted for this buffer size. Allocate a new buffer with a size corresponding + // to the appropriate bucket. + buffer = new T[_buckets[index]._bufferLength]; + } + else + { + // The request was for a size too large for the pool. Allocate an array of exactly the requested length. + // When it's returned to the pool, we'll simply throw it away. + buffer = new T[minimumLength]; + } + + if (log.IsEnabled()) + { + int bufferId = buffer.GetHashCode(), bucketId = -1; // no bucket for an on-demand allocated buffer + log.BufferRented(bufferId, buffer.Length, Id, bucketId); + log.BufferAllocated(bufferId, buffer.Length, Id, bucketId, index >= _buckets.Length ? + ArrayPoolEventSource.BufferAllocatedReason.OverMaximumSize : ArrayPoolEventSource.BufferAllocatedReason.PoolExhausted); + } + + return buffer; + } + + public override void Return(T[] array, bool clearArray = false) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + else if (array.Length == 0) + { + // Ignore empty arrays. When a zero-length array is rented, we return a singleton + // rather than actually taking a buffer out of the lowest bucket. + return; + } + + // Determine with what bucket this array length is associated + int bucket = Utilities.SelectBucketIndex(array.Length); + + // If we can tell that the buffer was allocated, drop it. Otherwise, check if we have space in the pool + if (bucket < _buckets.Length) + { + // Clear the array if the user requests + if (clearArray) + { + Array.Clear(array, 0, array.Length); + } + + // Return the buffer to its bucket. In the future, we might consider having Return return false + // instead of dropping a bucket, in which case we could try to return to a lower-sized bucket, + // just as how in Rent we allow renting from a higher-sized bucket. + _buckets[bucket].Return(array); + } + + // Log that the buffer was returned + var log = ArrayPoolEventSource.Log; + if (log.IsEnabled()) + { + log.BufferReturned(array.GetHashCode(), array.Length, Id); + } + } + + /// <summary>Provides a thread-safe bucket containing buffers that can be Rent'd and Return'd.</summary> + private sealed class Bucket + { + internal readonly int _bufferLength; + private readonly T[][] _buffers; + private readonly int _poolId; + + private SpinLock _lock; // do not make this readonly; it's a mutable struct + private int _index; + + /// <summary> + /// Creates the pool with numberOfBuffers arrays where each buffer is of bufferLength length. + /// </summary> + internal Bucket(int bufferLength, int numberOfBuffers, int poolId) + { + _lock = new SpinLock(Debugger.IsAttached); // only enable thread tracking if debugger is attached; it adds non-trivial overheads to Enter/Exit + _buffers = new T[numberOfBuffers][]; + _bufferLength = bufferLength; + _poolId = poolId; + } + + /// <summary>Gets an ID for the bucket to use with events.</summary> + internal int Id => GetHashCode(); + + /// <summary>Takes an array from the bucket. If the bucket is empty, returns null.</summary> + internal T[] Rent() + { + T[][] buffers = _buffers; + T[] buffer = null; + + // While holding the lock, grab whatever is at the next available index and + // update the index. We do as little work as possible while holding the spin + // lock to minimize contention with other threads. The try/finally is + // necessary to properly handle thread aborts on platforms which have them. + bool lockTaken = false, allocateBuffer = false; + try + { + _lock.Enter(ref lockTaken); + + if (_index < buffers.Length) + { + buffer = buffers[_index]; + buffers[_index++] = null; + allocateBuffer = buffer == null; + } + } + finally + { + if (lockTaken) _lock.Exit(false); + } + + // While we were holding the lock, we grabbed whatever was at the next available index, if + // there was one. If we tried and if we got back null, that means we hadn't yet allocated + // for that slot, in which case we should do so now. + if (allocateBuffer) + { + buffer = new T[_bufferLength]; + + var log = ArrayPoolEventSource.Log; + if (log.IsEnabled()) + { + log.BufferAllocated(buffer.GetHashCode(), _bufferLength, _poolId, Id, + ArrayPoolEventSource.BufferAllocatedReason.Pooled); + } + } + + return buffer; + } + + /// <summary> + /// Attempts to return the buffer to the bucket. If successful, the buffer will be stored + /// in the bucket and true will be returned; otherwise, the buffer won't be stored, and false + /// will be returned. + /// </summary> + internal void Return(T[] array) + { + // Check to see if the buffer is the correct size for this bucket + if (array.Length != _bufferLength) + { + throw new ArgumentException(SR.ArgumentException_BufferNotFromPool, nameof(array)); + } + + // While holding the spin lock, if there's room available in the bucket, + // put the buffer into the next available slot. Otherwise, we just drop it. + // The try/finally is necessary to properly handle thread aborts on platforms + // which have them. + bool lockTaken = false; + try + { + _lock.Enter(ref lockTaken); + + if (_index != 0) + { + _buffers[--_index] = array; + } + } + finally + { + if (lockTaken) _lock.Exit(false); + } + } + } + } +} diff --git a/src/mscorlib/shared/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs b/src/mscorlib/shared/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs new file mode 100644 index 0000000000..64c5cebe85 --- /dev/null +++ b/src/mscorlib/shared/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.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. + +using Microsoft.Win32; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace System.Buffers +{ + /// <summary> + /// Provides an ArrayPool implementation meant to be used as the singleton returned from ArrayPool.Shared. + /// </summary> + /// <remarks> + /// The implementation uses a tiered caching scheme, with a small per-thread cache for each array size, followed + /// by a cache per array size shared by all threads, split into per-core stacks meant to be used by threads + /// running on that core. Locks are used to protect each per-core stack, because a thread can migrate after + /// checking its processor number, because multiple threads could interleave on the same core, and because + /// a thread is allowed to check other core's buckets if its core's bucket is empty/full. + /// </remarks> + internal sealed partial class TlsOverPerCoreLockedStacksArrayPool<T> : ArrayPool<T> + { + // TODO: #7747: "Investigate optimizing ArrayPool heuristics" + // - Explore caching in TLS more than one array per size per thread, and moving stale buffers to the global queue. + // - Explore dumping stale buffers from the global queue, similar to PinnableBufferCache (maybe merging them). + // - Explore changing the size of each per-core bucket, potentially dynamically or based on other factors like array size. + // - Explore changing number of buckets and what sizes of arrays are cached. + // - Investigate whether false sharing is causing any issues, in particular on LockedStack's count and the contents of its array. + // ... + + /// <summary>The number of buckets (array sizes) in the pool, one for each array length, starting from length 16.</summary> + private const int NumBuckets = 17; // Utilities.SelectBucketIndex(2*1024*1024) + /// <summary>Maximum number of per-core stacks to use per array size.</summary> + private const int MaxPerCorePerArraySizeStacks = 64; // selected to avoid needing to worry about processor groups + /// <summary>The maximum number of buffers to store in a bucket's global queue.</summary> + private const int MaxBuffersPerArraySizePerCore = 8; + + /// <summary>The length of arrays stored in the corresponding indices in <see cref="_buckets"/> and <see cref="t_tlsBuckets"/>.</summary> + private readonly int[] _bucketArraySizes; + /// <summary> + /// An array of per-core array stacks. The slots are lazily initialized to avoid creating + /// lots of overhead for unused array sizes. + /// </summary> + private readonly PerCoreLockedStacks[] _buckets = new PerCoreLockedStacks[NumBuckets]; + /// <summary>A per-thread array of arrays, to cache one array per array size per thread.</summary> + [ThreadStatic] + private static T[][] t_tlsBuckets; + + /// <summary>Initialize the pool.</summary> + public TlsOverPerCoreLockedStacksArrayPool() + { + var sizes = new int[NumBuckets]; + for (int i = 0; i < sizes.Length; i++) + { + sizes[i] = Utilities.GetMaxSizeForBucket(i); + } + _bucketArraySizes = sizes; + } + + /// <summary>Allocate a new PerCoreLockedStacks and try to store it into the <see cref="_buckets"/> array.</summary> + private PerCoreLockedStacks CreatePerCoreLockedStacks(int bucketIndex) + { + var inst = new PerCoreLockedStacks(); + return Interlocked.CompareExchange(ref _buckets[bucketIndex], inst, null) ?? inst; + } + + /// <summary>Gets an ID for the pool to use with events.</summary> + private int Id => GetHashCode(); + + public override T[] Rent(int minimumLength) + { + // Arrays can't be smaller than zero. We allow requesting zero-length arrays (even though + // pooling such an array isn't valuable) as it's a valid length array, and we want the pool + // to be usable in general instead of using `new`, even for computed lengths. + if (minimumLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(minimumLength)); + } + else if (minimumLength == 0) + { + // No need to log the empty array. Our pool is effectively infinite + // and we'll never allocate for rents and never store for returns. + return Array.Empty<T>(); + } + + ArrayPoolEventSource log = ArrayPoolEventSource.Log; + T[] buffer; + + // Get the bucket number for the array length + int bucketIndex = Utilities.SelectBucketIndex(minimumLength); + + // If the array could come from a bucket... + if (bucketIndex < _buckets.Length) + { + // First try to get it from TLS if possible. + T[][] tlsBuckets = t_tlsBuckets; + if (tlsBuckets != null) + { + buffer = tlsBuckets[bucketIndex]; + if (buffer != null) + { + tlsBuckets[bucketIndex] = null; + if (log.IsEnabled()) + { + log.BufferRented(buffer.GetHashCode(), buffer.Length, Id, bucketIndex); + } + return buffer; + } + } + + // We couldn't get a buffer from TLS, so try the global stack. + PerCoreLockedStacks b = _buckets[bucketIndex]; + if (b != null) + { + buffer = b.TryPop(); + if (buffer != null) + { + if (log.IsEnabled()) + { + log.BufferRented(buffer.GetHashCode(), buffer.Length, Id, bucketIndex); + } + return buffer; + } + } + + // No buffer available. Allocate a new buffer with a size corresponding to the appropriate bucket. + buffer = new T[_bucketArraySizes[bucketIndex]]; + } + else + { + // The request was for a size too large for the pool. Allocate an array of exactly the requested length. + // When it's returned to the pool, we'll simply throw it away. + buffer = new T[minimumLength]; + } + + if (log.IsEnabled()) + { + int bufferId = buffer.GetHashCode(), bucketId = -1; // no bucket for an on-demand allocated buffer + log.BufferRented(bufferId, buffer.Length, Id, bucketId); + log.BufferAllocated(bufferId, buffer.Length, Id, bucketId, bucketIndex >= _buckets.Length ? + ArrayPoolEventSource.BufferAllocatedReason.OverMaximumSize : + ArrayPoolEventSource.BufferAllocatedReason.PoolExhausted); + } + + return buffer; + } + + public override void Return(T[] array, bool clearArray = false) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + + // Determine with what bucket this array length is associated + int bucketIndex = Utilities.SelectBucketIndex(array.Length); + + // If we can tell that the buffer was allocated (or empty), drop it. Otherwise, check if we have space in the pool. + if (bucketIndex < _buckets.Length) + { + // Clear the array if the user requests. + if (clearArray) + { + Array.Clear(array, 0, array.Length); + } + + // Check to see if the buffer is the correct size for this bucket + if (array.Length != _bucketArraySizes[bucketIndex]) + { + throw new ArgumentException(SR.ArgumentException_BufferNotFromPool, nameof(array)); + } + + // Write through the TLS bucket. If there weren't any buckets, create them + // and store this array into it. If there were, store this into it, and + // if there was a previous one there, push that to the global stack. This + // helps to keep LIFO access such that the most recently pushed stack will + // be in TLS and the first to be popped next. + T[][] tlsBuckets = t_tlsBuckets; + if (tlsBuckets == null) + { + t_tlsBuckets = tlsBuckets = new T[NumBuckets][]; + tlsBuckets[bucketIndex] = array; + } + else + { + T[] prev = tlsBuckets[bucketIndex]; + tlsBuckets[bucketIndex] = array; + if (prev != null) + { + PerCoreLockedStacks bucket = _buckets[bucketIndex] ?? CreatePerCoreLockedStacks(bucketIndex); + bucket.TryPush(prev); + } + } + } + + // Log that the buffer was returned + ArrayPoolEventSource log = ArrayPoolEventSource.Log; + if (log.IsEnabled()) + { + log.BufferReturned(array.GetHashCode(), array.Length, Id); + } + } + + /// <summary> + /// Stores a set of stacks of arrays, with one stack per core. + /// </summary> + private sealed class PerCoreLockedStacks + { + /// <summary>The stacks.</summary> + private readonly LockedStack[] _perCoreStacks; + + /// <summary>Initializes the stacks.</summary> + public PerCoreLockedStacks() + { + // Create the stacks. We create as many as there are processors, limited by our max. + var stacks = new LockedStack[Math.Min(Environment.ProcessorCount, MaxPerCorePerArraySizeStacks)]; + for (int i = 0; i < stacks.Length; i++) + { + stacks[i] = new LockedStack(); + } + _perCoreStacks = stacks; + } + + /// <summary>Try to push the array into the stacks. If each is full when it's tested, the array will be dropped.</summary> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void TryPush(T[] array) + { + // Try to push on to the associated stack first. If that fails, + // round-robin through the other stacks. + LockedStack[] stacks = _perCoreStacks; + int index = Environment.CurrentExecutionId % stacks.Length; + for (int i = 0; i < stacks.Length; i++) + { + if (stacks[index].TryPush(array)) return; + if (++index == stacks.Length) index = 0; + } + } + + /// <summary>Try to get an array from the stacks. If each is empty when it's tested, null will be returned.</summary> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T[] TryPop() + { + // Try to pop from the associated stack first. If that fails, + // round-robin through the other stacks. + T[] arr; + LockedStack[] stacks = _perCoreStacks; + int index = Environment.CurrentExecutionId % stacks.Length; + for (int i = 0; i < stacks.Length; i++) + { + if ((arr = stacks[index].TryPop()) != null) return arr; + if (++index == stacks.Length) index = 0; + } + return null; + } + } + + /// <summary>Provides a simple stack of arrays, protected by a lock.</summary> + private sealed class LockedStack + { + private readonly T[][] _arrays = new T[MaxBuffersPerArraySizePerCore][]; + private int _count; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryPush(T[] array) + { + bool enqueued = false; + Monitor.Enter(this); + if (_count < MaxBuffersPerArraySizePerCore) + { + _arrays[_count++] = array; + enqueued = true; + } + Monitor.Exit(this); + return enqueued; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T[] TryPop() + { + T[] arr = null; + Monitor.Enter(this); + if (_count > 0) + { + arr = _arrays[--_count]; + _arrays[_count] = null; + } + Monitor.Exit(this); + return arr; + } + } + } +} diff --git a/src/mscorlib/shared/System/Buffers/Utilities.cs b/src/mscorlib/shared/System/Buffers/Utilities.cs new file mode 100644 index 0000000000..4f115fe9dd --- /dev/null +++ b/src/mscorlib/shared/System/Buffers/Utilities.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.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System.Buffers +{ + internal static class Utilities + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static int SelectBucketIndex(int bufferSize) + { + uint bitsRemaining = ((uint)bufferSize - 1) >> 4; + + int poolIndex = 0; + if (bitsRemaining > 0xFFFF) { bitsRemaining >>= 16; poolIndex = 16; } + if (bitsRemaining > 0xFF) { bitsRemaining >>= 8; poolIndex += 8; } + if (bitsRemaining > 0xF) { bitsRemaining >>= 4; poolIndex += 4; } + if (bitsRemaining > 0x3) { bitsRemaining >>= 2; poolIndex += 2; } + if (bitsRemaining > 0x1) { bitsRemaining >>= 1; poolIndex += 1; } + + return poolIndex + (int)bitsRemaining; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static int GetMaxSizeForBucket(int binIndex) + { + int maxSize = 16 << binIndex; + Debug.Assert(maxSize >= 0); + return maxSize; + } + } +} diff --git a/src/mscorlib/shared/System/CLSCompliantAttribute.cs b/src/mscorlib/shared/System/CLSCompliantAttribute.cs new file mode 100644 index 0000000000..e03600d132 --- /dev/null +++ b/src/mscorlib/shared/System/CLSCompliantAttribute.cs @@ -0,0 +1,34 @@ +// 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: Container for assemblies. +** +** +=============================================================================*/ + +namespace System +{ + [Serializable] + [AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = false)] + public sealed class CLSCompliantAttribute : Attribute + { + private bool _compliant; + + public CLSCompliantAttribute(bool isCompliant) + { + _compliant = isCompliant; + } + public bool IsCompliant + { + get + { + return _compliant; + } + } + } +} diff --git a/src/mscorlib/shared/System/Char.cs b/src/mscorlib/shared/System/Char.cs new file mode 100644 index 0000000000..2b3133a669 --- /dev/null +++ b/src/mscorlib/shared/System/Char.cs @@ -0,0 +1,1122 @@ +// 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 is the value class representing a Unicode character +** Char methods until we create this functionality. +** +** +===========================================================*/ + +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Runtime.InteropServices; + +namespace System +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct Char : IComparable, IComparable<Char>, IEquatable<Char>, IConvertible + { + // + // Member Variables + // + internal char m_value; + + // + // Public Constants + // + // The maximum character value. + public const char MaxValue = (char)0xFFFF; + // The minimum character value. + public const char MinValue = (char)0x00; + + // Unicode category values from Unicode U+0000 ~ U+00FF. Store them in byte[] array to save space. + private static readonly byte[] s_categoryForLatin1 = { + (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0000 - 0007 + (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0008 - 000F + (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0010 - 0017 + (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0018 - 001F + (byte)UnicodeCategory.SpaceSeparator, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.CurrencySymbol, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, // 0020 - 0027 + (byte)UnicodeCategory.OpenPunctuation, (byte)UnicodeCategory.ClosePunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.DashPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, // 0028 - 002F + (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, // 0030 - 0037 + (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.OtherPunctuation, // 0038 - 003F + (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, // 0040 - 0047 + (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, // 0048 - 004F + (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, // 0050 - 0057 + (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.OpenPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.ClosePunctuation, (byte)UnicodeCategory.ModifierSymbol, (byte)UnicodeCategory.ConnectorPunctuation, // 0058 - 005F + (byte)UnicodeCategory.ModifierSymbol, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 0060 - 0067 + (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 0068 - 006F + (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 0070 - 0077 + (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.OpenPunctuation, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.ClosePunctuation, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.Control, // 0078 - 007F + (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0080 - 0087 + (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0088 - 008F + (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0090 - 0097 + (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0098 - 009F + (byte)UnicodeCategory.SpaceSeparator, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.CurrencySymbol, (byte)UnicodeCategory.CurrencySymbol, (byte)UnicodeCategory.CurrencySymbol, (byte)UnicodeCategory.CurrencySymbol, (byte)UnicodeCategory.OtherSymbol, (byte)UnicodeCategory.OtherSymbol, // 00A0 - 00A7 + (byte)UnicodeCategory.ModifierSymbol, (byte)UnicodeCategory.OtherSymbol, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.InitialQuotePunctuation, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.DashPunctuation, (byte)UnicodeCategory.OtherSymbol, (byte)UnicodeCategory.ModifierSymbol, // 00A8 - 00AF + (byte)UnicodeCategory.OtherSymbol, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.ModifierSymbol, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.OtherSymbol, (byte)UnicodeCategory.OtherPunctuation, // 00B0 - 00B7 + (byte)UnicodeCategory.ModifierSymbol, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.FinalQuotePunctuation, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.OtherPunctuation, // 00B8 - 00BF + (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, // 00C0 - 00C7 + (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, // 00C8 - 00CF + (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.MathSymbol, // 00D0 - 00D7 + (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 00D8 - 00DF + (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 00E0 - 00E7 + (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 00E8 - 00EF + (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.MathSymbol, // 00F0 - 00F7 + (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 00F8 - 00FF + }; + + // Return true for all characters below or equal U+00ff, which is ASCII + Latin-1 Supplement. + private static bool IsLatin1(char ch) + { + return (ch <= '\x00ff'); + } + + // Return true for all characters below or equal U+007f, which is ASCII. + private static bool IsAscii(char ch) + { + return (ch <= '\x007f'); + } + + // Return the Unicode category for Unicode character <= 0x00ff. + private static UnicodeCategory GetLatin1UnicodeCategory(char ch) + { + Debug.Assert(IsLatin1(ch), "Char.GetLatin1UnicodeCategory(): ch should be <= 007f"); + return (UnicodeCategory)(s_categoryForLatin1[(int)ch]); + } + + // + // Private Constants + // + + // + // Overriden Instance Methods + // + + // Calculate a hashcode for a 2 byte Unicode character. + public override int GetHashCode() + { + return (int)m_value | ((int)m_value << 16); + } + + // Used for comparing two boxed Char objects. + // + public override bool Equals(Object obj) + { + if (!(obj is Char)) + { + return false; + } + return (m_value == ((Char)obj).m_value); + } + + [System.Runtime.Versioning.NonVersionable] + public bool Equals(Char obj) + { + return m_value == obj; + } + + // 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 Char, this method throws an ArgumentException. + // + [Pure] + public int CompareTo(Object value) + { + if (value == null) + { + return 1; + } + if (!(value is Char)) + { + throw new ArgumentException(SR.Arg_MustBeChar); + } + + return (m_value - ((Char)value).m_value); + } + + [Pure] + public int CompareTo(Char value) + { + return (m_value - value); + } + + // Overrides System.Object.ToString. + [Pure] + public override String ToString() + { + Contract.Ensures(Contract.Result<String>() != null); + return Char.ToString(m_value); + } + + [Pure] + public String ToString(IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return Char.ToString(m_value); + } + + // + // Formatting Methods + // + + /*===================================ToString=================================== + **This static methods takes a character and returns the String representation of it. + ==============================================================================*/ + // Provides a string representation of a character. + [Pure] + public static string ToString(char c) => string.CreateFromChar(c); + + public static char Parse(String s) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + Contract.EndContractBlock(); + + if (s.Length != 1) + { + throw new FormatException(SR.Format_NeedSingleChar); + } + return s[0]; + } + + public static bool TryParse(String s, out Char result) + { + result = '\0'; + if (s == null) + { + return false; + } + if (s.Length != 1) + { + return false; + } + result = s[0]; + return true; + } + + // + // Static Methods + // + /*=================================ISDIGIT====================================== + **A wrapper for Char. Returns a boolean indicating whether ** + **character c is considered to be a digit. ** + ==============================================================================*/ + // Determines whether a character is a digit. + [Pure] + public static bool IsDigit(char c) + { + if (IsLatin1(c)) + { + return (c >= '0' && c <= '9'); + } + return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.DecimalDigitNumber); + } + + + /*=================================CheckLetter===================================== + ** Check if the specified UnicodeCategory belongs to the letter categories. + ==============================================================================*/ + internal static bool CheckLetter(UnicodeCategory uc) + { + switch (uc) + { + case (UnicodeCategory.UppercaseLetter): + case (UnicodeCategory.LowercaseLetter): + case (UnicodeCategory.TitlecaseLetter): + case (UnicodeCategory.ModifierLetter): + case (UnicodeCategory.OtherLetter): + return (true); + } + return (false); + } + + /*=================================ISLETTER===================================== + **A wrapper for Char. Returns a boolean indicating whether ** + **character c is considered to be a letter. ** + ==============================================================================*/ + // Determines whether a character is a letter. + [Pure] + public static bool IsLetter(char c) + { + if (IsLatin1(c)) + { + if (IsAscii(c)) + { + c |= (char)0x20; + return ((c >= 'a' && c <= 'z')); + } + return (CheckLetter(GetLatin1UnicodeCategory(c))); + } + return (CheckLetter(CharUnicodeInfo.GetUnicodeCategory(c))); + } + + private static bool IsWhiteSpaceLatin1(char c) + { + // There are characters which belong to UnicodeCategory.Control but are considered as white spaces. + // We use code point comparisons for these characters here as a temporary fix. + + // U+0009 = <control> HORIZONTAL TAB + // U+000a = <control> LINE FEED + // U+000b = <control> VERTICAL TAB + // U+000c = <contorl> FORM FEED + // U+000d = <control> CARRIAGE RETURN + // U+0085 = <control> NEXT LINE + // U+00a0 = NO-BREAK SPACE + if ((c == ' ') || (c >= '\x0009' && c <= '\x000d') || c == '\x00a0' || c == '\x0085') + { + return (true); + } + return (false); + } + + /*===============================ISWHITESPACE=================================== + **A wrapper for Char. Returns a boolean indicating whether ** + **character c is considered to be a whitespace character. ** + ==============================================================================*/ + // Determines whether a character is whitespace. + [Pure] + public static bool IsWhiteSpace(char c) + { + if (IsLatin1(c)) + { + return (IsWhiteSpaceLatin1(c)); + } + return CharUnicodeInfo.IsWhiteSpace(c); + } + + + /*===================================IsUpper==================================== + **Arguments: c -- the characater to be checked. + **Returns: True if c is an uppercase character. + ==============================================================================*/ + // Determines whether a character is upper-case. + [Pure] + public static bool IsUpper(char c) + { + if (IsLatin1(c)) + { + if (IsAscii(c)) + { + return (c >= 'A' && c <= 'Z'); + } + return (GetLatin1UnicodeCategory(c) == UnicodeCategory.UppercaseLetter); + } + return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.UppercaseLetter); + } + + /*===================================IsLower==================================== + **Arguments: c -- the characater to be checked. + **Returns: True if c is an lowercase character. + ==============================================================================*/ + // Determines whether a character is lower-case. + [Pure] + public static bool IsLower(char c) + { + if (IsLatin1(c)) + { + if (IsAscii(c)) + { + return (c >= 'a' && c <= 'z'); + } + return (GetLatin1UnicodeCategory(c) == UnicodeCategory.LowercaseLetter); + } + return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.LowercaseLetter); + } + + internal static bool CheckPunctuation(UnicodeCategory uc) + { + switch (uc) + { + case UnicodeCategory.ConnectorPunctuation: + case UnicodeCategory.DashPunctuation: + case UnicodeCategory.OpenPunctuation: + case UnicodeCategory.ClosePunctuation: + case UnicodeCategory.InitialQuotePunctuation: + case UnicodeCategory.FinalQuotePunctuation: + case UnicodeCategory.OtherPunctuation: + return (true); + } + return (false); + } + + + /*================================IsPunctuation================================= + **Arguments: c -- the characater to be checked. + **Returns: True if c is an punctuation mark + ==============================================================================*/ + // Determines whether a character is a punctuation mark. + [Pure] + public static bool IsPunctuation(char c) + { + if (IsLatin1(c)) + { + return (CheckPunctuation(GetLatin1UnicodeCategory(c))); + } + return (CheckPunctuation(CharUnicodeInfo.GetUnicodeCategory(c))); + } + + /*=================================CheckLetterOrDigit===================================== + ** Check if the specified UnicodeCategory belongs to the letter or digit categories. + ==============================================================================*/ + internal static bool CheckLetterOrDigit(UnicodeCategory uc) + { + switch (uc) + { + case UnicodeCategory.UppercaseLetter: + case UnicodeCategory.LowercaseLetter: + case UnicodeCategory.TitlecaseLetter: + case UnicodeCategory.ModifierLetter: + case UnicodeCategory.OtherLetter: + case UnicodeCategory.DecimalDigitNumber: + return (true); + } + return (false); + } + + // Determines whether a character is a letter or a digit. + [Pure] + public static bool IsLetterOrDigit(char c) + { + if (IsLatin1(c)) + { + return (CheckLetterOrDigit(GetLatin1UnicodeCategory(c))); + } + return (CheckLetterOrDigit(CharUnicodeInfo.GetUnicodeCategory(c))); + } + + /*===================================ToUpper==================================== + ** + ==============================================================================*/ + // Converts a character to upper-case for the specified culture. + // <;<;Not fully implemented>;>; + public static char ToUpper(char c, CultureInfo culture) + { + if (culture == null) + throw new ArgumentNullException(nameof(culture)); + Contract.EndContractBlock(); + return culture.TextInfo.ToUpper(c); + } + + /*=================================TOUPPER====================================== + **A wrapper for Char.toUpperCase. Converts character c to its ** + **uppercase equivalent. If c is already an uppercase character or is not an ** + **alphabetic, nothing happens. ** + ==============================================================================*/ + // Converts a character to upper-case for the default culture. + // + public static char ToUpper(char c) + { + return ToUpper(c, CultureInfo.CurrentCulture); + } + + + // Converts a character to upper-case for invariant culture. + public static char ToUpperInvariant(char c) + { + return ToUpper(c, CultureInfo.InvariantCulture); + } + + + /*===================================ToLower==================================== + ** + ==============================================================================*/ + // Converts a character to lower-case for the specified culture. + // <;<;Not fully implemented>;>; + public static char ToLower(char c, CultureInfo culture) + { + if (culture == null) + throw new ArgumentNullException(nameof(culture)); + Contract.EndContractBlock(); + return culture.TextInfo.ToLower(c); + } + + /*=================================TOLOWER====================================== + **A wrapper for Char.toLowerCase. Converts character c to its ** + **lowercase equivalent. If c is already a lowercase character or is not an ** + **alphabetic, nothing happens. ** + ==============================================================================*/ + // Converts a character to lower-case for the default culture. + public static char ToLower(char c) + { + return ToLower(c, CultureInfo.CurrentCulture); + } + + + // Converts a character to lower-case for invariant culture. + public static char ToLowerInvariant(char c) + { + return ToLower(c, CultureInfo.InvariantCulture); + } + + + // + // IConvertible implementation + // + [Pure] + public TypeCode GetTypeCode() + { + return TypeCode.Char; + } + + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "Boolean")); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return 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 Convert.ToUInt64(m_value); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "Single")); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "Double")); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "Decimal")); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "DateTime")); + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + public static bool IsControl(char c) + { + if (IsLatin1(c)) + { + return (GetLatin1UnicodeCategory(c) == UnicodeCategory.Control); + } + return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.Control); + } + + public static bool IsControl(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + Contract.EndContractBlock(); + char c = s[index]; + if (IsLatin1(c)) + { + return (GetLatin1UnicodeCategory(c) == UnicodeCategory.Control); + } + return (CharUnicodeInfo.GetUnicodeCategory(s, index) == UnicodeCategory.Control); + } + + + public static bool IsDigit(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + Contract.EndContractBlock(); + char c = s[index]; + if (IsLatin1(c)) + { + return (c >= '0' && c <= '9'); + } + return (CharUnicodeInfo.GetUnicodeCategory(s, index) == UnicodeCategory.DecimalDigitNumber); + } + + public static bool IsLetter(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + Contract.EndContractBlock(); + char c = s[index]; + if (IsLatin1(c)) + { + if (IsAscii(c)) + { + c |= (char)0x20; + return ((c >= 'a' && c <= 'z')); + } + return (CheckLetter(GetLatin1UnicodeCategory(c))); + } + return (CheckLetter(CharUnicodeInfo.GetUnicodeCategory(s, index))); + } + + public static bool IsLetterOrDigit(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + Contract.EndContractBlock(); + char c = s[index]; + if (IsLatin1(c)) + { + return CheckLetterOrDigit(GetLatin1UnicodeCategory(c)); + } + return CheckLetterOrDigit(CharUnicodeInfo.GetUnicodeCategory(s, index)); + } + + public static bool IsLower(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + Contract.EndContractBlock(); + char c = s[index]; + if (IsLatin1(c)) + { + if (IsAscii(c)) + { + return (c >= 'a' && c <= 'z'); + } + return (GetLatin1UnicodeCategory(c) == UnicodeCategory.LowercaseLetter); + } + + return (CharUnicodeInfo.GetUnicodeCategory(s, index) == UnicodeCategory.LowercaseLetter); + } + + /*=================================CheckNumber===================================== + ** Check if the specified UnicodeCategory belongs to the number categories. + ==============================================================================*/ + + internal static bool CheckNumber(UnicodeCategory uc) + { + switch (uc) + { + case (UnicodeCategory.DecimalDigitNumber): + case (UnicodeCategory.LetterNumber): + case (UnicodeCategory.OtherNumber): + return (true); + } + return (false); + } + + public static bool IsNumber(char c) + { + if (IsLatin1(c)) + { + if (IsAscii(c)) + { + return (c >= '0' && c <= '9'); + } + return (CheckNumber(GetLatin1UnicodeCategory(c))); + } + return (CheckNumber(CharUnicodeInfo.GetUnicodeCategory(c))); + } + + public static bool IsNumber(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + Contract.EndContractBlock(); + char c = s[index]; + if (IsLatin1(c)) + { + if (IsAscii(c)) + { + return (c >= '0' && c <= '9'); + } + return (CheckNumber(GetLatin1UnicodeCategory(c))); + } + return (CheckNumber(CharUnicodeInfo.GetUnicodeCategory(s, index))); + } + + //////////////////////////////////////////////////////////////////////// + // + // IsPunctuation + // + // Determines if the given character is a punctuation character. + // + //////////////////////////////////////////////////////////////////////// + + public static bool IsPunctuation(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + Contract.EndContractBlock(); + char c = s[index]; + if (IsLatin1(c)) + { + return (CheckPunctuation(GetLatin1UnicodeCategory(c))); + } + return (CheckPunctuation(CharUnicodeInfo.GetUnicodeCategory(s, index))); + } + + + /*================================= CheckSeparator ============================ + ** Check if the specified UnicodeCategory belongs to the seprator categories. + ==============================================================================*/ + + internal static bool CheckSeparator(UnicodeCategory uc) + { + switch (uc) + { + case UnicodeCategory.SpaceSeparator: + case UnicodeCategory.LineSeparator: + case UnicodeCategory.ParagraphSeparator: + return (true); + } + return (false); + } + + private static bool IsSeparatorLatin1(char c) + { + // U+00a0 = NO-BREAK SPACE + // There is no LineSeparator or ParagraphSeparator in Latin 1 range. + return (c == '\x0020' || c == '\x00a0'); + } + + public static bool IsSeparator(char c) + { + if (IsLatin1(c)) + { + return (IsSeparatorLatin1(c)); + } + return (CheckSeparator(CharUnicodeInfo.GetUnicodeCategory(c))); + } + + public static bool IsSeparator(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + Contract.EndContractBlock(); + char c = s[index]; + if (IsLatin1(c)) + { + return (IsSeparatorLatin1(c)); + } + return (CheckSeparator(CharUnicodeInfo.GetUnicodeCategory(s, index))); + } + + [Pure] + public static bool IsSurrogate(char c) + { + return (c >= HIGH_SURROGATE_START && c <= LOW_SURROGATE_END); + } + + [Pure] + public static bool IsSurrogate(String s, int index) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + Contract.EndContractBlock(); + return (IsSurrogate(s[index])); + } + + /*================================= CheckSymbol ============================ + ** Check if the specified UnicodeCategory belongs to the symbol categories. + ==============================================================================*/ + + internal static bool CheckSymbol(UnicodeCategory uc) + { + switch (uc) + { + case (UnicodeCategory.MathSymbol): + case (UnicodeCategory.CurrencySymbol): + case (UnicodeCategory.ModifierSymbol): + case (UnicodeCategory.OtherSymbol): + return (true); + } + return (false); + } + + public static bool IsSymbol(char c) + { + if (IsLatin1(c)) + { + return (CheckSymbol(GetLatin1UnicodeCategory(c))); + } + return (CheckSymbol(CharUnicodeInfo.GetUnicodeCategory(c))); + } + + public static bool IsSymbol(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + Contract.EndContractBlock(); + char c = s[index]; + if (IsLatin1(c)) + { + return (CheckSymbol(GetLatin1UnicodeCategory(c))); + } + return (CheckSymbol(CharUnicodeInfo.GetUnicodeCategory(s, index))); + } + + + public static bool IsUpper(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + Contract.EndContractBlock(); + char c = s[index]; + if (IsLatin1(c)) + { + if (IsAscii(c)) + { + return (c >= 'A' && c <= 'Z'); + } + return (GetLatin1UnicodeCategory(c) == UnicodeCategory.UppercaseLetter); + } + + return (CharUnicodeInfo.GetUnicodeCategory(s, index) == UnicodeCategory.UppercaseLetter); + } + + public static bool IsWhiteSpace(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + Contract.EndContractBlock(); + + if (IsLatin1(s[index])) + { + return IsWhiteSpaceLatin1(s[index]); + } + + return CharUnicodeInfo.IsWhiteSpace(s, index); + } + + public static UnicodeCategory GetUnicodeCategory(char c) + { + if (IsLatin1(c)) + { + return (GetLatin1UnicodeCategory(c)); + } + return CharUnicodeInfo.InternalGetUnicodeCategory(c); + } + + public static UnicodeCategory GetUnicodeCategory(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + Contract.EndContractBlock(); + if (IsLatin1(s[index])) + { + return (GetLatin1UnicodeCategory(s[index])); + } + return CharUnicodeInfo.InternalGetUnicodeCategory(s, index); + } + + public static double GetNumericValue(char c) + { + return CharUnicodeInfo.GetNumericValue(c); + } + + public static double GetNumericValue(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + Contract.EndContractBlock(); + return CharUnicodeInfo.GetNumericValue(s, index); + } + + + /*================================= IsHighSurrogate ============================ + ** Check if a char is a high surrogate. + ==============================================================================*/ + [Pure] + public static bool IsHighSurrogate(char c) + { + return ((c >= CharUnicodeInfo.HIGH_SURROGATE_START) && (c <= CharUnicodeInfo.HIGH_SURROGATE_END)); + } + + [Pure] + public static bool IsHighSurrogate(String s, int index) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + if (index < 0 || index >= s.Length) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + Contract.EndContractBlock(); + return (IsHighSurrogate(s[index])); + } + + /*================================= IsLowSurrogate ============================ + ** Check if a char is a low surrogate. + ==============================================================================*/ + [Pure] + public static bool IsLowSurrogate(char c) + { + return ((c >= CharUnicodeInfo.LOW_SURROGATE_START) && (c <= CharUnicodeInfo.LOW_SURROGATE_END)); + } + + [Pure] + public static bool IsLowSurrogate(String s, int index) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + if (index < 0 || index >= s.Length) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + Contract.EndContractBlock(); + return (IsLowSurrogate(s[index])); + } + + /*================================= IsSurrogatePair ============================ + ** Check if the string specified by the index starts with a surrogate pair. + ==============================================================================*/ + [Pure] + public static bool IsSurrogatePair(String s, int index) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + if (index < 0 || index >= s.Length) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + Contract.EndContractBlock(); + if (index + 1 < s.Length) + { + return (IsSurrogatePair(s[index], s[index + 1])); + } + return (false); + } + + [Pure] + public static bool IsSurrogatePair(char highSurrogate, char lowSurrogate) + { + return ((highSurrogate >= CharUnicodeInfo.HIGH_SURROGATE_START && highSurrogate <= CharUnicodeInfo.HIGH_SURROGATE_END) && + (lowSurrogate >= CharUnicodeInfo.LOW_SURROGATE_START && lowSurrogate <= CharUnicodeInfo.LOW_SURROGATE_END)); + } + + internal const int UNICODE_PLANE00_END = 0x00ffff; + // The starting codepoint for Unicode plane 1. Plane 1 contains 0x010000 ~ 0x01ffff. + internal const int UNICODE_PLANE01_START = 0x10000; + // The end codepoint for Unicode plane 16. This is the maximum code point value allowed for Unicode. + // Plane 16 contains 0x100000 ~ 0x10ffff. + internal const int UNICODE_PLANE16_END = 0x10ffff; + + internal const int HIGH_SURROGATE_START = 0x00d800; + internal const int LOW_SURROGATE_END = 0x00dfff; + + + + /*================================= ConvertFromUtf32 ============================ + ** Convert an UTF32 value into a surrogate pair. + ==============================================================================*/ + + public static String ConvertFromUtf32(int utf32) + { + // For UTF32 values from U+00D800 ~ U+00DFFF, we should throw. They + // are considered as irregular code unit sequence, but they are not illegal. + if ((utf32 < 0 || utf32 > UNICODE_PLANE16_END) || (utf32 >= HIGH_SURROGATE_START && utf32 <= LOW_SURROGATE_END)) + { + throw new ArgumentOutOfRangeException(nameof(utf32), SR.ArgumentOutOfRange_InvalidUTF32); + } + Contract.EndContractBlock(); + + if (utf32 < UNICODE_PLANE01_START) + { + // This is a BMP character. + return (Char.ToString((char)utf32)); + } + + unsafe + { + // This is a supplementary character. Convert it to a surrogate pair in UTF-16. + utf32 -= UNICODE_PLANE01_START; + uint surrogate = 0; // allocate 2 chars worth of stack space + char* address = (char*)&surrogate; + address[0] = (char)((utf32 / 0x400) + (int)CharUnicodeInfo.HIGH_SURROGATE_START); + address[1] = (char)((utf32 % 0x400) + (int)CharUnicodeInfo.LOW_SURROGATE_START); + return new string(address, 0, 2); + } + } + + + /*=============================ConvertToUtf32=================================== + ** Convert a surrogate pair to UTF32 value + ==============================================================================*/ + + public static int ConvertToUtf32(char highSurrogate, char lowSurrogate) + { + if (!IsHighSurrogate(highSurrogate)) + { + throw new ArgumentOutOfRangeException(nameof(highSurrogate), SR.ArgumentOutOfRange_InvalidHighSurrogate); + } + if (!IsLowSurrogate(lowSurrogate)) + { + throw new ArgumentOutOfRangeException(nameof(lowSurrogate), SR.ArgumentOutOfRange_InvalidLowSurrogate); + } + Contract.EndContractBlock(); + return (((highSurrogate - CharUnicodeInfo.HIGH_SURROGATE_START) * 0x400) + (lowSurrogate - CharUnicodeInfo.LOW_SURROGATE_START) + UNICODE_PLANE01_START); + } + + /*=============================ConvertToUtf32=================================== + ** Convert a character or a surrogate pair starting at index of the specified string + ** to UTF32 value. + ** The char pointed by index should be a surrogate pair or a BMP character. + ** This method throws if a high-surrogate is not followed by a low surrogate. + ** This method throws if a low surrogate is seen without preceding a high-surrogate. + ==============================================================================*/ + + public static int ConvertToUtf32(String s, int index) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + + if (index < 0 || index >= s.Length) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); + } + Contract.EndContractBlock(); + // Check if the character at index is a high surrogate. + int temp1 = (int)s[index] - CharUnicodeInfo.HIGH_SURROGATE_START; + if (temp1 >= 0 && temp1 <= 0x7ff) + { + // Found a surrogate char. + if (temp1 <= 0x3ff) + { + // Found a high surrogate. + if (index < s.Length - 1) + { + int temp2 = (int)s[index + 1] - CharUnicodeInfo.LOW_SURROGATE_START; + if (temp2 >= 0 && temp2 <= 0x3ff) + { + // Found a low surrogate. + return ((temp1 * 0x400) + temp2 + UNICODE_PLANE01_START); + } + else + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidHighSurrogate, index), nameof(s)); + } + } + else + { + // Found a high surrogate at the end of the string. + throw new ArgumentException(SR.Format(SR.Argument_InvalidHighSurrogate, index), nameof(s)); + } + } + else + { + // Find a low surrogate at the character pointed by index. + throw new ArgumentException(SR.Format(SR.Argument_InvalidLowSurrogate, index), nameof(s)); + } + } + // Not a high-surrogate or low-surrogate. Genereate the UTF32 value for the BMP characters. + return ((int)s[index]); + } + } +} diff --git a/src/mscorlib/shared/System/CharEnumerator.cs b/src/mscorlib/shared/System/CharEnumerator.cs new file mode 100644 index 0000000000..ea9915a7c4 --- /dev/null +++ b/src/mscorlib/shared/System/CharEnumerator.cs @@ -0,0 +1,80 @@ +// 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: Enumerates the characters on a string. skips range +** checks. +** +** +============================================================*/ + +using System.Collections; +using System.Collections.Generic; + +namespace System +{ + public sealed class CharEnumerator : IEnumerator, IEnumerator<char>, IDisposable, ICloneable + { + private String _str; + private int _index; + private char _currentElement; + + internal CharEnumerator(String str) + { + _str = str; + _index = -1; + } + + public object Clone() + { + return MemberwiseClone(); + } + + public bool MoveNext() + { + if (_index < (_str.Length - 1)) + { + _index++; + _currentElement = _str[_index]; + return true; + } + else + _index = _str.Length; + return false; + } + + public void Dispose() + { + if (_str != null) + _index = _str.Length; + _str = null; + } + + Object IEnumerator.Current + { + get { return Current; } + } + + public char Current + { + get + { + if (_index == -1) + throw new InvalidOperationException(SR.InvalidOperation_EnumNotStarted); + if (_index >= _str.Length) + throw new InvalidOperationException(SR.InvalidOperation_EnumEnded); + return _currentElement; + } + } + + public void Reset() + { + _currentElement = (char)0; + _index = -1; + } + } +} diff --git a/src/mscorlib/shared/System/Collections/DictionaryEntry.cs b/src/mscorlib/shared/System/Collections/DictionaryEntry.cs new file mode 100644 index 0000000000..290306d006 --- /dev/null +++ b/src/mscorlib/shared/System/Collections/DictionaryEntry.cs @@ -0,0 +1,58 @@ +// 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.ComponentModel; + +namespace System.Collections +{ + // A DictionaryEntry holds a key and a value from a dictionary. + // It is returned by IDictionaryEnumerator::GetEntry(). + [Serializable] + public struct DictionaryEntry + { + private Object _key; + private Object _value; + + // Constructs a new DictionaryEnumerator by setting the Key + // and Value fields appropriately. + public DictionaryEntry(Object key, Object value) + { + _key = key; + _value = value; + } + + public Object Key + { + get + { + return _key; + } + + set + { + _key = value; + } + } + + public Object Value + { + get + { + return _value; + } + + set + { + _value = value; + } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void Deconstruct(out object key, out object value) + { + key = Key; + value = Value; + } + } +} diff --git a/src/mscorlib/shared/System/Collections/Generic/ICollection.cs b/src/mscorlib/shared/System/Collections/Generic/ICollection.cs new file mode 100644 index 0000000000..52852aa1fb --- /dev/null +++ b/src/mscorlib/shared/System/Collections/Generic/ICollection.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; +using System.Runtime.CompilerServices; +using System.Diagnostics.Contracts; + +namespace System.Collections.Generic +{ + // Base interface for all collections, defining enumerators, size, and + // synchronization methods. + public interface ICollection<T> : IEnumerable<T> + { + // Number of items in the collections. + int Count { get; } + + bool IsReadOnly { get; } + + void Add(T item); + + void Clear(); + + bool Contains(T item); + + // CopyTo copies a collection into an Array, starting at a particular + // index into the array. + // + void CopyTo(T[] array, int arrayIndex); + + //void CopyTo(int sourceIndex, T[] destinationArray, int destinationIndex, int count); + + bool Remove(T item); + } +} diff --git a/src/mscorlib/shared/System/Collections/Generic/IComparer.cs b/src/mscorlib/shared/System/Collections/Generic/IComparer.cs new file mode 100644 index 0000000000..713d499cc8 --- /dev/null +++ b/src/mscorlib/shared/System/Collections/Generic/IComparer.cs @@ -0,0 +1,20 @@ +// 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.Collections.Generic +{ + // The generic IComparer interface implements a method that compares + // two objects. It is used in conjunction with the Sort and + // BinarySearch methods on the Array, List, and SortedList classes. + public interface IComparer<in T> + { + // Compares two objects. An implementation of this method must return a + // value less than zero if x is less than y, zero if x is equal to y, or a + // value greater than zero if x is greater than y. + // + int Compare(T x, T y); + } +} diff --git a/src/mscorlib/shared/System/Collections/Generic/IDictionary.cs b/src/mscorlib/shared/System/Collections/Generic/IDictionary.cs new file mode 100644 index 0000000000..a73a2f55bd --- /dev/null +++ b/src/mscorlib/shared/System/Collections/Generic/IDictionary.cs @@ -0,0 +1,51 @@ +// 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.Contracts; + +namespace System.Collections.Generic +{ + // An IDictionary is a possibly unordered set of key-value pairs. + // Keys can be any non-null object. Values can be any object. + // You can look up a value in an IDictionary via the default indexed + // property, Items. + public interface IDictionary<TKey, TValue> : ICollection<KeyValuePair<TKey, TValue>> + { + // Interfaces are not serializable + // The Item property provides methods to read and edit entries + // in the Dictionary. + TValue this[TKey key] + { + get; + set; + } + + // Returns a collections of the keys in this dictionary. + ICollection<TKey> Keys + { + get; + } + + // Returns a collections of the values in this dictionary. + ICollection<TValue> Values + { + get; + } + + // Returns whether this dictionary contains a particular key. + // + bool ContainsKey(TKey key); + + // Adds a key-value pair to the dictionary. + // + void Add(TKey key, TValue value); + + // Removes a particular key from the dictionary. + // + bool Remove(TKey key); + + bool TryGetValue(TKey key, out TValue value); + } +} diff --git a/src/mscorlib/shared/System/Collections/Generic/IEnumerable.cs b/src/mscorlib/shared/System/Collections/Generic/IEnumerable.cs new file mode 100644 index 0000000000..84264d5cf0 --- /dev/null +++ b/src/mscorlib/shared/System/Collections/Generic/IEnumerable.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. + +using System; +using System.Collections; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Diagnostics.Contracts; + +namespace System.Collections.Generic +{ + // Implement this interface if you need to support foreach semantics. + public interface IEnumerable<out T> : IEnumerable + { + // Returns an IEnumerator for this enumerable Object. The enumerator provides + // a simple way to access all the contents of a collection. + /// <include file='doc\IEnumerable.uex' path='docs/doc[@for="IEnumerable.GetEnumerator"]/*' /> + new IEnumerator<T> GetEnumerator(); + } +} diff --git a/src/mscorlib/shared/System/Collections/Generic/IEnumerator.cs b/src/mscorlib/shared/System/Collections/Generic/IEnumerator.cs new file mode 100644 index 0000000000..6360576974 --- /dev/null +++ b/src/mscorlib/shared/System/Collections/Generic/IEnumerator.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. + +using System; +using System.Runtime.InteropServices; + +namespace System.Collections.Generic +{ + // Base interface for all generic enumerators, providing a simple approach + // to iterating over a collection. + public interface IEnumerator<out T> : IDisposable, IEnumerator + { + // Returns the current element of the enumeration. The returned value is + // undefined before the first call to MoveNext and following a + // call to MoveNext that returned false. Multiple calls to + // GetCurrent with no intervening calls to MoveNext + // will return the same object. + // + /// <include file='doc\IEnumerator.uex' path='docs/doc[@for="IEnumerator.Current"]/*' /> + new T Current + { + get; + } + } +} diff --git a/src/mscorlib/shared/System/Collections/Generic/IEqualityComparer.cs b/src/mscorlib/shared/System/Collections/Generic/IEqualityComparer.cs new file mode 100644 index 0000000000..543bdb5fce --- /dev/null +++ b/src/mscorlib/shared/System/Collections/Generic/IEqualityComparer.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 System; + +namespace System.Collections.Generic +{ + // The generic IEqualityComparer interface implements methods to if check two objects are equal + // and generate Hashcode for an object. + // It is use in Dictionary class. + public interface IEqualityComparer<in T> + { + bool Equals(T x, T y); + int GetHashCode(T obj); + } +} + diff --git a/src/mscorlib/shared/System/Collections/Generic/IList.cs b/src/mscorlib/shared/System/Collections/Generic/IList.cs new file mode 100644 index 0000000000..43d6659da9 --- /dev/null +++ b/src/mscorlib/shared/System/Collections/Generic/IList.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; +using System.Collections; +using System.Runtime.CompilerServices; +using System.Diagnostics.Contracts; + +namespace System.Collections.Generic +{ + // An IList is an ordered collection of objects. The exact ordering + // is up to the implementation of the list, ranging from a sorted + // order to insertion order. + public interface IList<T> : ICollection<T> + { + // The Item property provides methods to read and edit entries in the List. + T this[int index] + { + get; + set; + } + + // Returns the index of a particular item, if it is in the list. + // Returns -1 if the item isn't in the list. + int IndexOf(T item); + + // Inserts value into the list at position index. + // index must be non-negative and less than or equal to the + // number of elements in the list. If index equals the number + // of items in the list, then value is appended to the end. + void Insert(int index, T item); + + // Removes the item at position index. + void RemoveAt(int index); + } +} diff --git a/src/mscorlib/shared/System/Collections/Generic/IReadOnlyCollection.cs b/src/mscorlib/shared/System/Collections/Generic/IReadOnlyCollection.cs new file mode 100644 index 0000000000..09ee89f035 --- /dev/null +++ b/src/mscorlib/shared/System/Collections/Generic/IReadOnlyCollection.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; +using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; + +namespace System.Collections.Generic +{ + // Provides a read-only, covariant view of a generic list. + public interface IReadOnlyCollection<out T> : IEnumerable<T> + { + int Count { get; } + } +} diff --git a/src/mscorlib/shared/System/Collections/Generic/IReadOnlyDictionary.cs b/src/mscorlib/shared/System/Collections/Generic/IReadOnlyDictionary.cs new file mode 100644 index 0000000000..169e2958bb --- /dev/null +++ b/src/mscorlib/shared/System/Collections/Generic/IReadOnlyDictionary.cs @@ -0,0 +1,20 @@ +// 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.Contracts; + +namespace System.Collections.Generic +{ + // Provides a read-only view of a generic dictionary. + public interface IReadOnlyDictionary<TKey, TValue> : IReadOnlyCollection<KeyValuePair<TKey, TValue>> + { + bool ContainsKey(TKey key); + bool TryGetValue(TKey key, out TValue value); + + TValue this[TKey key] { get; } + IEnumerable<TKey> Keys { get; } + IEnumerable<TValue> Values { get; } + } +} diff --git a/src/mscorlib/shared/System/Collections/Generic/IReadOnlyList.cs b/src/mscorlib/shared/System/Collections/Generic/IReadOnlyList.cs new file mode 100644 index 0000000000..00b5be65ff --- /dev/null +++ b/src/mscorlib/shared/System/Collections/Generic/IReadOnlyList.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; +using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; + +namespace System.Collections.Generic +{ + // Provides a read-only, covariant view of a generic list. + public interface IReadOnlyList<out T> : IReadOnlyCollection<T> + { + T this[int index] { get; } + } +} diff --git a/src/mscorlib/shared/System/Collections/Generic/KeyNotFoundException.cs b/src/mscorlib/shared/System/Collections/Generic/KeyNotFoundException.cs new file mode 100644 index 0000000000..1fca7732ae --- /dev/null +++ b/src/mscorlib/shared/System/Collections/Generic/KeyNotFoundException.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.Collections.Generic +{ + [Serializable] + public class KeyNotFoundException : SystemException + { + public KeyNotFoundException() + : base(SR.Arg_KeyNotFound) + { + HResult = __HResults.COR_E_KEYNOTFOUND; + } + + public KeyNotFoundException(String message) + : base(message) + { + HResult = __HResults.COR_E_KEYNOTFOUND; + } + + public KeyNotFoundException(String message, Exception innerException) + : base(message, innerException) + { + HResult = __HResults.COR_E_KEYNOTFOUND; + } + + protected KeyNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { } + } +} diff --git a/src/mscorlib/shared/System/Collections/Generic/KeyValuePair.cs b/src/mscorlib/shared/System/Collections/Generic/KeyValuePair.cs new file mode 100644 index 0000000000..fc51af25f8 --- /dev/null +++ b/src/mscorlib/shared/System/Collections/Generic/KeyValuePair.cs @@ -0,0 +1,82 @@ +// 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.ComponentModel; +using System.Text; + +namespace System.Collections.Generic +{ + // Provides the Create factory method for KeyValuePair<TKey, TValue>. + public static class KeyValuePair + { + // Creates a new KeyValuePair<TKey, TValue> from the given values. + public static KeyValuePair<TKey, TValue> Create<TKey, TValue>(TKey key, TValue value) + { + return new KeyValuePair<TKey, TValue>(key, value); + } + + /// <summary> + /// Used by KeyValuePair.ToString to reduce generic code + /// </summary> + internal static string PairToString(object key, object value) + { + StringBuilder s = StringBuilderCache.Acquire(); + s.Append('['); + + if (key != null) + { + s.Append(key); + } + + s.Append(", "); + + if (value != null) + { + s.Append(value); + } + + s.Append(']'); + + return StringBuilderCache.GetStringAndRelease(s); + } + } + + // A KeyValuePair holds a key and a value from a dictionary. + // It is used by the IEnumerable<T> implementation for both IDictionary<TKey, TValue> + // and IReadOnlyDictionary<TKey, TValue>. + [Serializable] + public struct KeyValuePair<TKey, TValue> + { + private TKey key; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. + private TValue value; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload. + + public KeyValuePair(TKey key, TValue value) + { + this.key = key; + this.value = value; + } + + public TKey Key + { + get { return key; } + } + + public TValue Value + { + get { return value; } + } + + public override string ToString() + { + return KeyValuePair.PairToString(Key, Value); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void Deconstruct(out TKey key, out TValue value) + { + key = Key; + value = Value; + } + } +} diff --git a/src/mscorlib/shared/System/Collections/ICollection.cs b/src/mscorlib/shared/System/Collections/ICollection.cs new file mode 100644 index 0000000000..80ea092363 --- /dev/null +++ b/src/mscorlib/shared/System/Collections/ICollection.cs @@ -0,0 +1,70 @@ +// 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.Contracts; + +namespace System.Collections +{ + // Base interface for all collections, defining enumerators, size, and + // synchronization methods. + public interface ICollection : IEnumerable + { + // Interfaces are not serialable + // CopyTo copies a collection into an Array, starting at a particular + // index into the array. + // + void CopyTo(Array array, int index); + + // Number of items in the collections. + int Count + { get; } + + + // SyncRoot will return an Object to use for synchronization + // (thread safety). You can use this object in your code to take a + // lock on the collection, even if this collection is a wrapper around + // another collection. The intent is to tunnel through to a real + // 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, + // the expected usage for SyncRoot would look like this: + // + // ICollection col = ... + // lock (col.SyncRoot) { + // // Some operation on the collection, which is now thread safe. + // // This may include multiple operations. + // } + // + // + // The system-provided collections have a static method called + // Synchronized which will create a thread-safe wrapper around the + // collection. All access to the collection that you want to be + // thread-safe should go through that wrapper collection. However, if + // you need to do multiple calls on that collection (such as retrieving + // two items, or checking the count then doing something), you should + // NOT use our thread-safe wrapper since it only takes a lock for the + // duration of a single method call. Instead, use Monitor.Enter/Exit + // or your language's equivalent to the C# lock keyword as mentioned + // above. + // + // For collections with no publically available underlying store, the + // expected implementation is to simply return the this pointer. Note + // that the this pointer may not be sufficient for collections that + // wrap other collections; those should return the underlying + // collection's SyncRoot property. + Object SyncRoot + { get; } + + // Is this collection synchronized (i.e., thread-safe)? If you want a + // thread-safe collection, you can use SyncRoot as an object to + // synchronize your collection with. If you're using one of the + // collections in System.Collections, you could call the static + // Synchronized method to get a thread-safe wrapper around the + // underlying collection. + bool IsSynchronized + { get; } + } +} diff --git a/src/mscorlib/shared/System/Collections/IComparer.cs b/src/mscorlib/shared/System/Collections/IComparer.cs new file mode 100644 index 0000000000..cef91c3ffa --- /dev/null +++ b/src/mscorlib/shared/System/Collections/IComparer.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Collections +{ + // The IComparer interface implements a method that compares two objects. It is + // used in conjunction with the Sort and BinarySearch methods on + // the Array and List classes. + // + // Interfaces are not serializable + public interface IComparer + { + // Compares two objects. An implementation of this method must return a + // value less than zero if x is less than y, zero if x is equal to y, or a + // value greater than zero if x is greater than y. + // + int Compare(Object x, Object y); + } +} diff --git a/src/mscorlib/shared/System/Collections/IDictionary.cs b/src/mscorlib/shared/System/Collections/IDictionary.cs new file mode 100644 index 0000000000..8bc7fcf125 --- /dev/null +++ b/src/mscorlib/shared/System/Collections/IDictionary.cs @@ -0,0 +1,61 @@ +// 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.Contracts; + +namespace System.Collections +{ + // An IDictionary is a possibly unordered set of key-value pairs. + // Keys can be any non-null object. Values can be any object. + // You can look up a value in an IDictionary via the default indexed + // property, Items. + public interface IDictionary : ICollection + { + // Interfaces are not serializable + // The Item property provides methods to read and edit entries + // in the Dictionary. + Object this[Object key] + { + get; + set; + } + + // Returns a collections of the keys in this dictionary. + ICollection Keys + { + get; + } + + // Returns a collections of the values in this dictionary. + ICollection Values + { + get; + } + + // Returns whether this dictionary contains a particular key. + // + bool Contains(Object key); + + // Adds a key-value pair to the dictionary. + // + void Add(Object key, Object value); + + // Removes all pairs from the dictionary. + void Clear(); + + bool IsReadOnly + { get; } + + bool IsFixedSize + { get; } + + // Returns an IDictionaryEnumerator for this dictionary. + new IDictionaryEnumerator GetEnumerator(); + + // Removes a particular key from the dictionary. + // + void Remove(Object key); + } +} diff --git a/src/mscorlib/shared/System/Collections/IDictionaryEnumerator.cs b/src/mscorlib/shared/System/Collections/IDictionaryEnumerator.cs new file mode 100644 index 0000000000..451cf68976 --- /dev/null +++ b/src/mscorlib/shared/System/Collections/IDictionaryEnumerator.cs @@ -0,0 +1,68 @@ +// 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.Collections +{ + // This interface represents an enumerator that allows sequential access to the + // elements of a dictionary. Upon creation, an enumerator is conceptually + // positioned before the first element of the enumeration. The first call to the + // MoveNext method brings the first element of the enumeration into view, + // and each successive call to MoveNext brings the next element into + // view until MoveNext returns false, indicating that there are no more + // elements to enumerate. Following each call to MoveNext, the + // Key and Value methods are used to obtain the key and + // value of the element currently in view. The values returned by calls to + // Key and Value are undefined before the first call to + // MoveNext and following a call to MoveNext that returned false. + // Enumerators are typically used in while loops of the form + // + // IDictionaryEnumerator e = ...; + // while (e.MoveNext()) { + // Object key = e.Key; + // Object value = e.Value; + // ... + // } + // + // The IDictionaryEnumerator interface extends the IEnumerator + // inerface and can thus be used as a regular enumerator. The Current + // method of an IDictionaryEnumerator returns a DictionaryEntry containing + // the current key and value pair. However, the GetEntry method will + // return the same DictionaryEntry and avoids boxing the DictionaryEntry (boxing + // is somewhat expensive). + // + public interface IDictionaryEnumerator : IEnumerator + { + // Returns the key of the current element of the enumeration. The returned + // value is undefined before the first call to GetNext and following + // a call to GetNext that returned false. Multiple calls to + // GetKey with no intervening calls to GetNext will return + // the same object. + // + Object Key + { + get; + } + + // Returns the value of the current element of the enumeration. The + // returned value is undefined before the first call to GetNext and + // following a call to GetNext that returned false. Multiple calls + // to GetValue with no intervening calls to GetNext will + // return the same object. + // + Object Value + { + get; + } + + // GetBlock will copy dictionary values into the given Array. It will either + // fill up the array, or if there aren't enough elements, it will + // copy as much as possible into the Array. The number of elements + // copied is returned. + // + DictionaryEntry Entry + { + get; + } + } +} diff --git a/src/mscorlib/shared/System/Collections/IEnumerable.cs b/src/mscorlib/shared/System/Collections/IEnumerable.cs new file mode 100644 index 0000000000..91aec62423 --- /dev/null +++ b/src/mscorlib/shared/System/Collections/IEnumerable.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 System; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; + +namespace System.Collections +{ + public interface IEnumerable + { + // Returns an IEnumerator for this enumerable Object. The enumerator provides + // a simple way to access all the contents of a collection. + [Pure] + IEnumerator GetEnumerator(); + } +} diff --git a/src/mscorlib/shared/System/Collections/IEnumerator.cs b/src/mscorlib/shared/System/Collections/IEnumerator.cs new file mode 100644 index 0000000000..2c2c2e4a97 --- /dev/null +++ b/src/mscorlib/shared/System/Collections/IEnumerator.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.InteropServices; + +namespace System.Collections +{ + // Base interface for all enumerators, providing a simple approach + // to iterating over a collection. + public interface IEnumerator + { + // Advances the enumerator to the next element of the enumeration and + // returns a boolean indicating whether an element is available. Upon + // creation, an enumerator is conceptually positioned before the first + // element of the enumeration, and the first call to MoveNext + // brings the first element of the enumeration into view. + // + bool MoveNext(); + + // Returns the current element of the enumeration. The returned value is + // undefined before the first call to MoveNext and following a + // call to MoveNext that returned false. Multiple calls to + // GetCurrent with no intervening calls to MoveNext + // will return the same object. + // + Object Current + { + get; + } + + // Resets the enumerator to the beginning of the enumeration, starting over. + // The preferred behavior for Reset is to return the exact same enumeration. + // This means if you modify the underlying collection then call Reset, your + // IEnumerator will be invalid, just as it would have been if you had called + // MoveNext or Current. + // + void Reset(); + } +} diff --git a/src/mscorlib/shared/System/Collections/IEqualityComparer.cs b/src/mscorlib/shared/System/Collections/IEqualityComparer.cs new file mode 100644 index 0000000000..35513f577d --- /dev/null +++ b/src/mscorlib/shared/System/Collections/IEqualityComparer.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; + +namespace System.Collections +{ + // An IEqualityComparer is a mechanism to consume custom performant comparison infrastructure + // that can be consumed by some of the common collections. + public interface IEqualityComparer + { + bool Equals(Object x, Object y); + int GetHashCode(Object obj); + } +} diff --git a/src/mscorlib/shared/System/Collections/IList.cs b/src/mscorlib/shared/System/Collections/IList.cs new file mode 100644 index 0000000000..bb2e221cc1 --- /dev/null +++ b/src/mscorlib/shared/System/Collections/IList.cs @@ -0,0 +1,60 @@ +// 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.Contracts; + +namespace System.Collections +{ + // An IList is an ordered collection of objects. The exact ordering + // is up to the implementation of the list, ranging from a sorted + // order to insertion order. + public interface IList : ICollection + { + // The Item property provides methods to read and edit entries in the List. + Object this[int index] + { + get; + set; + } + + // Adds an item to the list. The exact position in the list is + // implementation-dependent, so while ArrayList may always insert + // in the last available location, a SortedList most likely would not. + // The return value is the position the new element was inserted in. + int Add(Object value); + + // Returns whether the list contains a particular item. + bool Contains(Object value); + + // Removes all items from the list. + void Clear(); + + bool IsReadOnly + { get; } + + + bool IsFixedSize + { + get; + } + + + // Returns the index of a particular item, if it is in the list. + // Returns -1 if the item isn't in the list. + int IndexOf(Object value); + + // Inserts value into the list at position index. + // index must be non-negative and less than or equal to the + // number of elements in the list. If index equals the number + // of items in the list, then value is appended to the end. + void Insert(int index, Object value); + + // Removes an item from the list. + void Remove(Object value); + + // Removes the item at position index. + void RemoveAt(int index); + } +} diff --git a/src/mscorlib/shared/System/Collections/IStructuralComparable.cs b/src/mscorlib/shared/System/Collections/IStructuralComparable.cs new file mode 100644 index 0000000000..a5e4834b9b --- /dev/null +++ b/src/mscorlib/shared/System/Collections/IStructuralComparable.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. + +using System; + +namespace System.Collections +{ + public interface IStructuralComparable + { + Int32 CompareTo(Object other, IComparer comparer); + } +} diff --git a/src/mscorlib/shared/System/Collections/IStructuralEquatable.cs b/src/mscorlib/shared/System/Collections/IStructuralEquatable.cs new file mode 100644 index 0000000000..4e61d5e75f --- /dev/null +++ b/src/mscorlib/shared/System/Collections/IStructuralEquatable.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.Collections +{ + public interface IStructuralEquatable + { + Boolean Equals(Object other, IEqualityComparer comparer); + int GetHashCode(IEqualityComparer comparer); + } +} diff --git a/src/mscorlib/shared/System/ComponentModel/DefaultValueAttribute.cs b/src/mscorlib/shared/System/ComponentModel/DefaultValueAttribute.cs new file mode 100644 index 0000000000..3cdc907297 --- /dev/null +++ b/src/mscorlib/shared/System/ComponentModel/DefaultValueAttribute.cs @@ -0,0 +1,228 @@ +// 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.ComponentModel; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; + +namespace System.ComponentModel +{ + /// <devdoc> + /// <para>Specifies the default value for a property.</para> + /// </devdoc> + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes")] + [AttributeUsage(AttributeTargets.All)] + public class DefaultValueAttribute : Attribute + { + /// <devdoc> + /// This is the default value. + /// </devdoc> + private object _value; + + /// <devdoc> + /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class, converting the + /// specified value to the + /// specified type, and using the U.S. English culture as the + /// translation + /// context.</para> + /// </devdoc> + public DefaultValueAttribute(Type type, string value) + { + // The try/catch here is because attributes should never throw exceptions. We would fail to + // load an otherwise normal class. + try + { + if (type.IsSubclassOf(typeof(Enum))) + { + _value = Enum.Parse(type, value, true); + } + else if (type == typeof(TimeSpan)) + { + _value = TimeSpan.Parse(value); + } + else + { + _value = Convert.ChangeType(value, type, CultureInfo.InvariantCulture); + } + } + catch + { + } + } + + /// <devdoc> + /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a Unicode + /// character.</para> + /// </devdoc> + public DefaultValueAttribute(char value) + { + _value = value; + } + /// <devdoc> + /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using an 8-bit unsigned + /// integer.</para> + /// </devdoc> + public DefaultValueAttribute(byte value) + { + _value = value; + } + /// <devdoc> + /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a 16-bit signed + /// integer.</para> + /// </devdoc> + public DefaultValueAttribute(short value) + { + _value = value; + } + /// <devdoc> + /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a 32-bit signed + /// integer.</para> + /// </devdoc> + public DefaultValueAttribute(int value) + { + _value = value; + } + /// <devdoc> + /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a 64-bit signed + /// integer.</para> + /// </devdoc> + public DefaultValueAttribute(long value) + { + _value = value; + } + /// <devdoc> + /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a + /// single-precision floating point + /// number.</para> + /// </devdoc> + public DefaultValueAttribute(float value) + { + _value = value; + } + /// <devdoc> + /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a + /// double-precision floating point + /// number.</para> + /// </devdoc> + public DefaultValueAttribute(double value) + { + _value = value; + } + /// <devdoc> + /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a <see cref='System.Boolean'/> + /// value.</para> + /// </devdoc> + public DefaultValueAttribute(bool value) + { + _value = value; + } + /// <devdoc> + /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a <see cref='System.String'/>.</para> + /// </devdoc> + public DefaultValueAttribute(string value) + { + _value = value; + } + + /// <devdoc> + /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> + /// class.</para> + /// </devdoc> + public DefaultValueAttribute(object value) + { + _value = value; + } + + /// <devdoc> + /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a <see cref='System.SByte'/> + /// value.</para> + /// </devdoc> + [CLSCompliant(false)] + public DefaultValueAttribute(sbyte value) + { + _value = value; + } + + /// <devdoc> + /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a <see cref='System.UInt16'/> + /// value.</para> + /// </devdoc> + [CLSCompliant(false)] + public DefaultValueAttribute(ushort value) + { + _value = value; + } + + /// <devdoc> + /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a <see cref='System.UInt32'/> + /// value.</para> + /// </devdoc> + [CLSCompliant(false)] + public DefaultValueAttribute(uint value) + { + _value = value; + } + + /// <devdoc> + /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a <see cref='System.UInt64'/> + /// value.</para> + /// </devdoc> + [CLSCompliant(false)] + public DefaultValueAttribute(ulong value) + { + _value = value; + } + + /// <devdoc> + /// <para> + /// Gets the default value of the property this + /// attribute is + /// bound to. + /// </para> + /// </devdoc> + public virtual object Value + { + get + { + return _value; + } + } + + public override bool Equals(object obj) + { + if (obj == this) + { + return true; + } + + DefaultValueAttribute other = obj as DefaultValueAttribute; + + if (other != null) + { + if (Value != null) + { + return Value.Equals(other.Value); + } + else + { + return (other.Value == null); + } + } + return false; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + protected void SetValue(object value) + { + _value = value; + } + } +} diff --git a/src/mscorlib/shared/System/ComponentModel/EditorBrowsableAttribute.cs b/src/mscorlib/shared/System/ComponentModel/EditorBrowsableAttribute.cs new file mode 100644 index 0000000000..9b4d6e626e --- /dev/null +++ b/src/mscorlib/shared/System/ComponentModel/EditorBrowsableAttribute.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. + +namespace System.ComponentModel +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Delegate | AttributeTargets.Interface)] + public sealed class EditorBrowsableAttribute : Attribute + { + private EditorBrowsableState browsableState; + + public EditorBrowsableAttribute(EditorBrowsableState state) + { + browsableState = state; + } + + public EditorBrowsableAttribute() : this(EditorBrowsableState.Always) { } + + public EditorBrowsableState State + { + get { return browsableState; } + } + + public override bool Equals(object obj) + { + if (obj == this) + { + return true; + } + + EditorBrowsableAttribute other = obj as EditorBrowsableAttribute; + + return (other != null) && other.browsableState == browsableState; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + + public enum EditorBrowsableState + { + Always, + Never, + Advanced + } +} diff --git a/src/mscorlib/shared/System/Configuration/Assemblies/AssemblyHashAlgorithm.cs b/src/mscorlib/shared/System/Configuration/Assemblies/AssemblyHashAlgorithm.cs new file mode 100644 index 0000000000..aca8da5932 --- /dev/null +++ b/src/mscorlib/shared/System/Configuration/Assemblies/AssemblyHashAlgorithm.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.Configuration.Assemblies +{ + public enum AssemblyHashAlgorithm + { + None = 0, + MD5 = 0x8003, + SHA1 = 0x8004, + SHA256 = 0x800c, + SHA384 = 0x800d, + SHA512 = 0x800e, + } +} diff --git a/src/mscorlib/shared/System/Configuration/Assemblies/AssemblyVersionCompatibility.cs b/src/mscorlib/shared/System/Configuration/Assemblies/AssemblyVersionCompatibility.cs new file mode 100644 index 0000000000..ef7b3eb45f --- /dev/null +++ b/src/mscorlib/shared/System/Configuration/Assemblies/AssemblyVersionCompatibility.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.Configuration.Assemblies +{ + public enum AssemblyVersionCompatibility + { + SameMachine = 1, + SameProcess = 2, + SameDomain = 3, + } +} diff --git a/src/mscorlib/shared/System/Convert.cs b/src/mscorlib/shared/System/Convert.cs new file mode 100644 index 0000000000..576f78f1f1 --- /dev/null +++ b/src/mscorlib/shared/System/Convert.cs @@ -0,0 +1,3031 @@ +// 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.Globalization; +using System.Threading; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Security; +using System.Diagnostics; +using System.Diagnostics.Contracts; + +namespace System +{ + [Flags] + public enum Base64FormattingOptions + { + None = 0, + InsertLineBreaks = 1 + } + + // Returns the type code of this object. An implementation of this method + // must not return TypeCode.Empty (which represents a null reference) or + // TypeCode.Object (which represents an object that doesn't implement the + // IConvertible interface). An implementation of this method should return + // TypeCode.DBNull if the value of this object is a database null. For + // example, a nullable integer type should return TypeCode.DBNull if the + // value of the object is the database null. Otherwise, an implementation + // of this method should return the TypeCode that best describes the + // internal representation of the object. + // The Value class provides conversion and querying methods for values. The + // Value class contains static members only, and it is not possible to create + // instances of the class. + // + // The statically typed conversion methods provided by the Value class are all + // of the form: + // + // public static XXX ToXXX(YYY value) + // + // where XXX is the target type and YYY is the source type. The matrix below + // shows the set of supported conversions. The set of conversions is symmetric + // such that for every ToXXX(YYY) there is also a ToYYY(XXX). + // + // From: To: Bol Chr SBy Byt I16 U16 I32 U32 I64 U64 Sgl Dbl Dec Dat Str + // ---------------------------------------------------------------------- + // Boolean x x x x x x x x x x x x x + // Char x x x x x x x x x x + // SByte x x x x x x x x x x x x x x + // Byte x x x x x x x x x x x x x x + // Int16 x x x x x x x x x x x x x x + // UInt16 x x x x x x x x x x x x x x + // Int32 x x x x x x x x x x x x x x + // UInt32 x x x x x x x x x x x x x x + // Int64 x x x x x x x x x x x x x x + // UInt64 x x x x x x x x x x x x x x + // Single x x x x x x x x x x x x x + // Double x x x x x x x x x x x x x + // Decimal x x x x x x x x x x x x x + // DateTime x x + // String x x x x x x x x x x x x x x x + // ---------------------------------------------------------------------- + // + // For dynamic conversions, the Value class provides a set of methods of the + // form: + // + // public static XXX ToXXX(object value) + // + // where XXX is the target type (Boolean, Char, SByte, Byte, Int16, UInt16, + // Int32, UInt32, Int64, UInt64, Single, Double, Decimal, DateTime, + // or String). The implementations of these methods all take the form: + // + // public static XXX toXXX(object value) { + // return value == null? XXX.Default: ((IConvertible)value).ToXXX(); + // } + // + // The code first checks if the given value is a null reference (which is the + // same as Value.Empty), in which case it returns the default value for type + // XXX. Otherwise, a cast to IConvertible is performed, and the appropriate ToXXX() + // method is invoked on the object. An InvalidCastException is thrown if the + // cast to IConvertible fails, and that exception is simply allowed to propagate out + // 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 + // that Value.DBNull is NOT the same as a null object reference, which is + // represented by Value.Empty. + // + // The Equals() method of DBNull always returns false, even when the + // argument is itself DBNull. + // + // When passed Value.DBNull, the Value.GetTypeCode() method returns + // TypeCode.DBNull. + // + // When passed Value.DBNull, the Value.ToXXX() methods all throw an + // InvalidCastException. + + public static class Convert + { + //A typeof operation is fairly expensive (does a system call), so we'll cache these here + //statically. These are exactly lined up with the TypeCode, eg. ConvertType[TypeCode.Int16] + //will give you the type of an Int16. + internal static readonly Type[] ConvertTypes = { + typeof(System.Empty), + typeof(Object), + typeof(System.DBNull), + typeof(Boolean), + typeof(Char), + typeof(SByte), + typeof(Byte), + typeof(Int16), + typeof(UInt16), + typeof(Int32), + typeof(UInt32), + typeof(Int64), + typeof(UInt64), + typeof(Single), + typeof(Double), + typeof(Decimal), + typeof(DateTime), + typeof(Object), //TypeCode is discontinuous so we need a placeholder. + typeof(String) + }; + + // Need to special case Enum because typecode will be underlying type, e.g. Int32 + private static readonly Type EnumType = typeof(Enum); + + internal static readonly char[] base64Table = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O', + 'P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d', + 'e','f','g','h','i','j','k','l','m','n','o','p','q','r','s', + 't','u','v','w','x','y','z','0','1','2','3','4','5','6','7', + '8','9','+','/','=' }; + + private const Int32 base64LineBreakPosition = 76; + +#if _DEBUG + private static bool TriggerAsserts = DoAsserts(); + private static bool DoAsserts() + { + Debug.Assert(ConvertTypes != null, "[Convert.cctor]ConvertTypes!=null"); + Debug.Assert(ConvertTypes.Length == ((int)TypeCode.String + 1), "[Convert.cctor]ConvertTypes.Length == ((int)TypeCode.String + 1)"); + Debug.Assert(ConvertTypes[(int)TypeCode.Empty] == typeof(System.Empty), + "[Convert.cctor]ConvertTypes[(int)TypeCode.Empty]==typeof(System.Empty)"); + Debug.Assert(ConvertTypes[(int)TypeCode.String] == typeof(String), + "[Convert.cctor]ConvertTypes[(int)TypeCode.String]==typeof(System.String)"); + Debug.Assert(ConvertTypes[(int)TypeCode.Int32] == typeof(int), + "[Convert.cctor]ConvertTypes[(int)TypeCode.Int32]==typeof(int)"); + return true; + } +#endif + + public static readonly Object DBNull = System.DBNull.Value; + + // Returns the type code for the given object. If the argument is null, + // the result is TypeCode.Empty. If the argument is not a value (i.e. if + // the object does not implement IConvertible), the result is TypeCode.Object. + // Otherwise, the result is the type code of the object, as determined by + // the object's implementation of IConvertible. + [Pure] + public static TypeCode GetTypeCode(object value) + { + if (value == null) return TypeCode.Empty; + IConvertible temp = value as IConvertible; + if (temp != null) + { + return temp.GetTypeCode(); + } + return TypeCode.Object; + } + + // Returns true if the given object is a database null. This operation + // corresponds to "value.GetTypeCode() == TypeCode.DBNull". + [Pure] + public static bool IsDBNull(object value) + { + if (value == System.DBNull.Value) return true; + IConvertible convertible = value as IConvertible; + return convertible != null ? convertible.GetTypeCode() == TypeCode.DBNull : false; + } + + // Converts the given object to the given type. In general, this method is + // equivalent to calling the Value.ToXXX(value) method for the given + // typeCode and boxing the result. + // + // The method first checks if the given object implements IConvertible. If not, + // the only permitted conversion is from a null to TypeCode.Empty, the + // result of which is null. + // + // If the object does implement IConvertible, a check is made to see if the + // object already has the given type code, in which case the object is + // simply returned. Otherwise, the appropriate ToXXX() is invoked on the + // object's implementation of IConvertible. + public static Object ChangeType(Object value, TypeCode typeCode) + { + return ChangeType(value, typeCode, CultureInfo.CurrentCulture); + } + + public static Object ChangeType(Object value, TypeCode typeCode, IFormatProvider provider) + { + if (value == null && (typeCode == TypeCode.Empty || typeCode == TypeCode.String || typeCode == TypeCode.Object)) + { + return null; + } + + IConvertible v = value as IConvertible; + if (v == null) + { + throw new InvalidCastException(SR.InvalidCast_IConvertible); + } + + // This line is invalid for things like Enums that return a TypeCode + // of Int32, but the object can't actually be cast to an Int32. + // if (v.GetTypeCode() == typeCode) return value; + switch (typeCode) + { + case TypeCode.Boolean: + return v.ToBoolean(provider); + case TypeCode.Char: + return v.ToChar(provider); + case TypeCode.SByte: + return v.ToSByte(provider); + case TypeCode.Byte: + return v.ToByte(provider); + case TypeCode.Int16: + return v.ToInt16(provider); + case TypeCode.UInt16: + return v.ToUInt16(provider); + case TypeCode.Int32: + return v.ToInt32(provider); + case TypeCode.UInt32: + return v.ToUInt32(provider); + case TypeCode.Int64: + return v.ToInt64(provider); + case TypeCode.UInt64: + return v.ToUInt64(provider); + case TypeCode.Single: + return v.ToSingle(provider); + case TypeCode.Double: + return v.ToDouble(provider); + case TypeCode.Decimal: + return v.ToDecimal(provider); + case TypeCode.DateTime: + return v.ToDateTime(provider); + case TypeCode.String: + return v.ToString(provider); + case TypeCode.Object: + return value; + case TypeCode.DBNull: + throw new InvalidCastException(SR.InvalidCast_DBNull); + case TypeCode.Empty: + throw new InvalidCastException(SR.InvalidCast_Empty); + default: + throw new ArgumentException(SR.Arg_UnknownTypeCode); + } + } + + internal static Object DefaultToType(IConvertible value, Type targetType, IFormatProvider provider) + { + Debug.Assert(value != null, "[Convert.DefaultToType]value!=null"); + if (targetType == null) + { + throw new ArgumentNullException(nameof(targetType)); + } + Contract.EndContractBlock(); + + if (ReferenceEquals(value.GetType(), targetType)) + { + return value; + } + + if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Boolean])) + return value.ToBoolean(provider); + if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Char])) + return value.ToChar(provider); + if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.SByte])) + return value.ToSByte(provider); + if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Byte])) + return value.ToByte(provider); + if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Int16])) + return value.ToInt16(provider); + if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.UInt16])) + return value.ToUInt16(provider); + if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Int32])) + return value.ToInt32(provider); + if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.UInt32])) + return value.ToUInt32(provider); + if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Int64])) + return value.ToInt64(provider); + if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.UInt64])) + return value.ToUInt64(provider); + if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Single])) + return value.ToSingle(provider); + if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Double])) + return value.ToDouble(provider); + if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Decimal])) + return value.ToDecimal(provider); + if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.DateTime])) + return value.ToDateTime(provider); + if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.String])) + return value.ToString(provider); + if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Object])) + return (Object)value; + // Need to special case Enum because typecode will be underlying type, e.g. Int32 + if (ReferenceEquals(targetType, EnumType)) + return (Enum)value; + if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.DBNull])) + throw new InvalidCastException(SR.InvalidCast_DBNull); + if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Empty])) + throw new InvalidCastException(SR.InvalidCast_Empty); + + throw new InvalidCastException(string.Format(SR.InvalidCast_FromTo, value.GetType().FullName, targetType.FullName)); + } + + public static Object ChangeType(Object value, Type conversionType) + { + return ChangeType(value, conversionType, CultureInfo.CurrentCulture); + } + + public static Object ChangeType(Object value, Type conversionType, IFormatProvider provider) + { + if (ReferenceEquals(conversionType, null)) + { + throw new ArgumentNullException(nameof(conversionType)); + } + Contract.EndContractBlock(); + + if (value == null) + { + if (conversionType.IsValueType) + { + throw new InvalidCastException(SR.InvalidCast_CannotCastNullToValueType); + } + return null; + } + + IConvertible ic = value as IConvertible; + if (ic == null) + { + if (value.GetType() == conversionType) + { + return value; + } + throw new InvalidCastException(SR.InvalidCast_IConvertible); + } + + if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.Boolean])) + return ic.ToBoolean(provider); + if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.Char])) + return ic.ToChar(provider); + if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.SByte])) + return ic.ToSByte(provider); + if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.Byte])) + return ic.ToByte(provider); + if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.Int16])) + return ic.ToInt16(provider); + if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.UInt16])) + return ic.ToUInt16(provider); + if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.Int32])) + return ic.ToInt32(provider); + if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.UInt32])) + return ic.ToUInt32(provider); + if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.Int64])) + return ic.ToInt64(provider); + if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.UInt64])) + return ic.ToUInt64(provider); + if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.Single])) + return ic.ToSingle(provider); + if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.Double])) + return ic.ToDouble(provider); + if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.Decimal])) + return ic.ToDecimal(provider); + if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.DateTime])) + return ic.ToDateTime(provider); + if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.String])) + return ic.ToString(provider); + if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.Object])) + return (Object)value; + + return ic.ToType(conversionType, provider); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowCharOverflowException() { throw new OverflowException(SR.Overflow_Char); } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowByteOverflowException() { throw new OverflowException(SR.Overflow_Byte); } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowSByteOverflowException() { throw new OverflowException(SR.Overflow_SByte); } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowInt16OverflowException() { throw new OverflowException(SR.Overflow_Int16); } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowUInt16OverflowException() { throw new OverflowException(SR.Overflow_UInt16); } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowInt32OverflowException() { throw new OverflowException(SR.Overflow_Int32); } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowUInt32OverflowException() { throw new OverflowException(SR.Overflow_UInt32); } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowInt64OverflowException() { throw new OverflowException(SR.Overflow_Int64); } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowUInt64OverflowException() { throw new OverflowException(SR.Overflow_UInt64); } + + // Conversions to Boolean + public static bool ToBoolean(Object value) + { + return value == null ? false : ((IConvertible)value).ToBoolean(null); + } + + public static bool ToBoolean(Object value, IFormatProvider provider) + { + return value == null ? false : ((IConvertible)value).ToBoolean(provider); + } + + + public static bool ToBoolean(bool value) + { + return value; + } + + [CLSCompliant(false)] + public static bool ToBoolean(sbyte value) + { + return value != 0; + } + + // To be consistent with IConvertible in the base data types else we get different semantics + // with widening operations. Without this operator this widen succeeds,with this API the widening throws. + public static bool ToBoolean(char value) + { + return ((IConvertible)value).ToBoolean(null); + } + + public static bool ToBoolean(byte value) + { + return value != 0; + } + + + public static bool ToBoolean(short value) + { + return value != 0; + } + + [CLSCompliant(false)] + public static bool ToBoolean(ushort value) + { + return value != 0; + } + + public static bool ToBoolean(int value) + { + return value != 0; + } + + [CLSCompliant(false)] + public static bool ToBoolean(uint value) + { + return value != 0; + } + + public static bool ToBoolean(long value) + { + return value != 0; + } + + [CLSCompliant(false)] + public static bool ToBoolean(ulong value) + { + return value != 0; + } + + public static bool ToBoolean(String value) + { + if (value == null) + return false; + return Boolean.Parse(value); + } + + public static bool ToBoolean(String value, IFormatProvider provider) + { + if (value == null) + return false; + return Boolean.Parse(value); + } + + public static bool ToBoolean(float value) + { + return value != 0; + } + + public static bool ToBoolean(double value) + { + return value != 0; + } + + public static bool ToBoolean(decimal value) + { + return value != 0; + } + + public static bool ToBoolean(DateTime value) + { + return ((IConvertible)value).ToBoolean(null); + } + + // Disallowed conversions to Boolean + // public static bool ToBoolean(TimeSpan value) + + // Conversions to Char + + + public static char ToChar(object value) + { + return value == null ? (char)0 : ((IConvertible)value).ToChar(null); + } + + public static char ToChar(object value, IFormatProvider provider) + { + return value == null ? (char)0 : ((IConvertible)value).ToChar(provider); + } + + public static char ToChar(bool value) + { + return ((IConvertible)value).ToChar(null); + } + + public static char ToChar(char value) + { + return value; + } + + [CLSCompliant(false)] + public static char ToChar(sbyte value) + { + if (value < 0) ThrowCharOverflowException(); + Contract.EndContractBlock(); + return (char)value; + } + + public static char ToChar(byte value) + { + return (char)value; + } + + public static char ToChar(short value) + { + if (value < 0) ThrowCharOverflowException(); + Contract.EndContractBlock(); + return (char)value; + } + + [CLSCompliant(false)] + public static char ToChar(ushort value) + { + return (char)value; + } + + public static char ToChar(int value) + { + if (value < 0 || value > Char.MaxValue) ThrowCharOverflowException(); + Contract.EndContractBlock(); + return (char)value; + } + + [CLSCompliant(false)] + public static char ToChar(uint value) + { + if (value > Char.MaxValue) ThrowCharOverflowException(); + Contract.EndContractBlock(); + return (char)value; + } + + public static char ToChar(long value) + { + if (value < 0 || value > Char.MaxValue) ThrowCharOverflowException(); + Contract.EndContractBlock(); + return (char)value; + } + + [CLSCompliant(false)] + public static char ToChar(ulong value) + { + if (value > Char.MaxValue) ThrowCharOverflowException(); + Contract.EndContractBlock(); + return (char)value; + } + + // + // @VariantSwitch + // Remove FormatExceptions; + // + public static char ToChar(String value) + { + return ToChar(value, null); + } + + public static char ToChar(String value, IFormatProvider provider) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + Contract.EndContractBlock(); + + if (value.Length != 1) + throw new FormatException(SR.Format_NeedSingleChar); + + return value[0]; + } + + // To be consistent with IConvertible in the base data types else we get different semantics + // with widening operations. Without this operator this widen succeeds,with this API the widening throws. + public static char ToChar(float value) + { + return ((IConvertible)value).ToChar(null); + } + + // To be consistent with IConvertible in the base data types else we get different semantics + // with widening operations. Without this operator this widen succeeds,with this API the widening throws. + public static char ToChar(double value) + { + return ((IConvertible)value).ToChar(null); + } + + // To be consistent with IConvertible in the base data types else we get different semantics + // with widening operations. Without this operator this widen succeeds,with this API the widening throws. + public static char ToChar(decimal value) + { + return ((IConvertible)value).ToChar(null); + } + + public static char ToChar(DateTime value) + { + return ((IConvertible)value).ToChar(null); + } + + + // Disallowed conversions to Char + // public static char ToChar(TimeSpan value) + + // Conversions to SByte + + [CLSCompliant(false)] + public static sbyte ToSByte(object value) + { + return value == null ? (sbyte)0 : ((IConvertible)value).ToSByte(null); + } + + [CLSCompliant(false)] + public static sbyte ToSByte(object value, IFormatProvider provider) + { + return value == null ? (sbyte)0 : ((IConvertible)value).ToSByte(provider); + } + + [CLSCompliant(false)] + public static sbyte ToSByte(bool value) + { + return value ? (sbyte)Boolean.True : (sbyte)Boolean.False; + } + + [CLSCompliant(false)] + public static sbyte ToSByte(sbyte value) + { + return value; + } + + [CLSCompliant(false)] + public static sbyte ToSByte(char value) + { + if (value > SByte.MaxValue) ThrowSByteOverflowException(); + Contract.EndContractBlock(); + return (sbyte)value; + } + + [CLSCompliant(false)] + public static sbyte ToSByte(byte value) + { + if (value > SByte.MaxValue) ThrowSByteOverflowException(); + Contract.EndContractBlock(); + return (sbyte)value; + } + + [CLSCompliant(false)] + public static sbyte ToSByte(short value) + { + if (value < SByte.MinValue || value > SByte.MaxValue) ThrowSByteOverflowException(); + Contract.EndContractBlock(); + return (sbyte)value; + } + + [CLSCompliant(false)] + public static sbyte ToSByte(ushort value) + { + if (value > SByte.MaxValue) ThrowSByteOverflowException(); + Contract.EndContractBlock(); + return (sbyte)value; + } + + [CLSCompliant(false)] + public static sbyte ToSByte(int value) + { + if (value < SByte.MinValue || value > SByte.MaxValue) ThrowSByteOverflowException(); + Contract.EndContractBlock(); + return (sbyte)value; + } + + [CLSCompliant(false)] + public static sbyte ToSByte(uint value) + { + if (value > SByte.MaxValue) ThrowSByteOverflowException(); + Contract.EndContractBlock(); + return (sbyte)value; + } + + [CLSCompliant(false)] + public static sbyte ToSByte(long value) + { + if (value < SByte.MinValue || value > SByte.MaxValue) ThrowSByteOverflowException(); + Contract.EndContractBlock(); + return (sbyte)value; + } + + [CLSCompliant(false)] + public static sbyte ToSByte(ulong value) + { + if (value > (ulong)SByte.MaxValue) ThrowSByteOverflowException(); + Contract.EndContractBlock(); + return (sbyte)value; + } + + [CLSCompliant(false)] + public static sbyte ToSByte(float value) + { + return ToSByte((double)value); + } + + [CLSCompliant(false)] + public static sbyte ToSByte(double value) + { + return ToSByte(ToInt32(value)); + } + + [CLSCompliant(false)] + public static sbyte ToSByte(decimal value) + { + return Decimal.ToSByte(Decimal.Round(value, 0)); + } + + [CLSCompliant(false)] + public static sbyte ToSByte(String value) + { + if (value == null) + return 0; + return SByte.Parse(value, CultureInfo.CurrentCulture); + } + + [CLSCompliant(false)] + public static sbyte ToSByte(String value, IFormatProvider provider) + { + return SByte.Parse(value, NumberStyles.Integer, provider); + } + + [CLSCompliant(false)] + public static sbyte ToSByte(DateTime value) + { + return ((IConvertible)value).ToSByte(null); + } + + // Disallowed conversions to SByte + // public static sbyte ToSByte(TimeSpan value) + + // Conversions to Byte + + public static byte ToByte(object value) + { + return value == null ? (byte)0 : ((IConvertible)value).ToByte(null); + } + + public static byte ToByte(object value, IFormatProvider provider) + { + return value == null ? (byte)0 : ((IConvertible)value).ToByte(provider); + } + + public static byte ToByte(bool value) + { + return value ? (byte)Boolean.True : (byte)Boolean.False; + } + + public static byte ToByte(byte value) + { + return value; + } + + public static byte ToByte(char value) + { + if (value > Byte.MaxValue) ThrowByteOverflowException(); + Contract.EndContractBlock(); + return (byte)value; + } + + [CLSCompliant(false)] + public static byte ToByte(sbyte value) + { + if (value < Byte.MinValue) ThrowByteOverflowException(); + Contract.EndContractBlock(); + return (byte)value; + } + + public static byte ToByte(short value) + { + if (value < Byte.MinValue || value > Byte.MaxValue) ThrowByteOverflowException(); + Contract.EndContractBlock(); + return (byte)value; + } + + [CLSCompliant(false)] + public static byte ToByte(ushort value) + { + if (value > Byte.MaxValue) ThrowByteOverflowException(); + Contract.EndContractBlock(); + return (byte)value; + } + + public static byte ToByte(int value) + { + if (value < Byte.MinValue || value > Byte.MaxValue) ThrowByteOverflowException(); + Contract.EndContractBlock(); + return (byte)value; + } + + [CLSCompliant(false)] + public static byte ToByte(uint value) + { + if (value > Byte.MaxValue) ThrowByteOverflowException(); + Contract.EndContractBlock(); + return (byte)value; + } + + public static byte ToByte(long value) + { + if (value < Byte.MinValue || value > Byte.MaxValue) ThrowByteOverflowException(); + Contract.EndContractBlock(); + return (byte)value; + } + + [CLSCompliant(false)] + public static byte ToByte(ulong value) + { + if (value > Byte.MaxValue) ThrowByteOverflowException(); + Contract.EndContractBlock(); + return (byte)value; + } + + public static byte ToByte(float value) + { + return ToByte((double)value); + } + + public static byte ToByte(double value) + { + return ToByte(ToInt32(value)); + } + + public static byte ToByte(decimal value) + { + return Decimal.ToByte(Decimal.Round(value, 0)); + } + + public static byte ToByte(String value) + { + if (value == null) + return 0; + return Byte.Parse(value, CultureInfo.CurrentCulture); + } + + public static byte ToByte(String value, IFormatProvider provider) + { + if (value == null) + return 0; + return Byte.Parse(value, NumberStyles.Integer, provider); + } + + public static byte ToByte(DateTime value) + { + return ((IConvertible)value).ToByte(null); + } + + + // Disallowed conversions to Byte + // public static byte ToByte(TimeSpan value) + + // Conversions to Int16 + + public static short ToInt16(object value) + { + return value == null ? (short)0 : ((IConvertible)value).ToInt16(null); + } + + public static short ToInt16(object value, IFormatProvider provider) + { + return value == null ? (short)0 : ((IConvertible)value).ToInt16(provider); + } + + public static short ToInt16(bool value) + { + return value ? (short)Boolean.True : (short)Boolean.False; + } + + public static short ToInt16(char value) + { + if (value > Int16.MaxValue) ThrowInt16OverflowException(); + Contract.EndContractBlock(); + return (short)value; + } + + [CLSCompliant(false)] + public static short ToInt16(sbyte value) + { + return value; + } + + public static short ToInt16(byte value) + { + return value; + } + + [CLSCompliant(false)] + public static short ToInt16(ushort value) + { + if (value > Int16.MaxValue) ThrowInt16OverflowException(); + Contract.EndContractBlock(); + return (short)value; + } + + public static short ToInt16(int value) + { + if (value < Int16.MinValue || value > Int16.MaxValue) ThrowInt16OverflowException(); + Contract.EndContractBlock(); + return (short)value; + } + + [CLSCompliant(false)] + public static short ToInt16(uint value) + { + if (value > Int16.MaxValue) ThrowInt16OverflowException(); + Contract.EndContractBlock(); + return (short)value; + } + + public static short ToInt16(short value) + { + return value; + } + + public static short ToInt16(long value) + { + if (value < Int16.MinValue || value > Int16.MaxValue) ThrowInt16OverflowException(); + Contract.EndContractBlock(); + return (short)value; + } + + [CLSCompliant(false)] + public static short ToInt16(ulong value) + { + if (value > (ulong)Int16.MaxValue) ThrowInt16OverflowException(); + Contract.EndContractBlock(); + return (short)value; + } + + public static short ToInt16(float value) + { + return ToInt16((double)value); + } + + public static short ToInt16(double value) + { + return ToInt16(ToInt32(value)); + } + + public static short ToInt16(decimal value) + { + return Decimal.ToInt16(Decimal.Round(value, 0)); + } + + public static short ToInt16(String value) + { + if (value == null) + return 0; + return Int16.Parse(value, CultureInfo.CurrentCulture); + } + + public static short ToInt16(String value, IFormatProvider provider) + { + if (value == null) + return 0; + return Int16.Parse(value, NumberStyles.Integer, provider); + } + + public static short ToInt16(DateTime value) + { + return ((IConvertible)value).ToInt16(null); + } + + + // Disallowed conversions to Int16 + // public static short ToInt16(TimeSpan value) + + // Conversions to UInt16 + + [CLSCompliant(false)] + public static ushort ToUInt16(object value) + { + return value == null ? (ushort)0 : ((IConvertible)value).ToUInt16(null); + } + + [CLSCompliant(false)] + public static ushort ToUInt16(object value, IFormatProvider provider) + { + return value == null ? (ushort)0 : ((IConvertible)value).ToUInt16(provider); + } + + + [CLSCompliant(false)] + public static ushort ToUInt16(bool value) + { + return value ? (ushort)Boolean.True : (ushort)Boolean.False; + } + + [CLSCompliant(false)] + public static ushort ToUInt16(char value) + { + return value; + } + + [CLSCompliant(false)] + public static ushort ToUInt16(sbyte value) + { + if (value < 0) ThrowUInt16OverflowException(); + Contract.EndContractBlock(); + return (ushort)value; + } + + [CLSCompliant(false)] + public static ushort ToUInt16(byte value) + { + return value; + } + + [CLSCompliant(false)] + public static ushort ToUInt16(short value) + { + if (value < 0) ThrowUInt16OverflowException(); + Contract.EndContractBlock(); + return (ushort)value; + } + + [CLSCompliant(false)] + public static ushort ToUInt16(int value) + { + if (value < 0 || value > UInt16.MaxValue) ThrowUInt16OverflowException(); + Contract.EndContractBlock(); + return (ushort)value; + } + + [CLSCompliant(false)] + public static ushort ToUInt16(ushort value) + { + return value; + } + + [CLSCompliant(false)] + public static ushort ToUInt16(uint value) + { + if (value > UInt16.MaxValue) ThrowUInt16OverflowException(); + Contract.EndContractBlock(); + return (ushort)value; + } + + + [CLSCompliant(false)] + public static ushort ToUInt16(long value) + { + if (value < 0 || value > UInt16.MaxValue) ThrowUInt16OverflowException(); + Contract.EndContractBlock(); + return (ushort)value; + } + + [CLSCompliant(false)] + public static ushort ToUInt16(ulong value) + { + if (value > UInt16.MaxValue) ThrowUInt16OverflowException(); + Contract.EndContractBlock(); + return (ushort)value; + } + + [CLSCompliant(false)] + public static ushort ToUInt16(float value) + { + return ToUInt16((double)value); + } + + [CLSCompliant(false)] + public static ushort ToUInt16(double value) + { + return ToUInt16(ToInt32(value)); + } + + [CLSCompliant(false)] + public static ushort ToUInt16(decimal value) + { + return Decimal.ToUInt16(Decimal.Round(value, 0)); + } + + [CLSCompliant(false)] + public static ushort ToUInt16(String value) + { + if (value == null) + return 0; + return UInt16.Parse(value, CultureInfo.CurrentCulture); + } + + [CLSCompliant(false)] + public static ushort ToUInt16(String value, IFormatProvider provider) + { + if (value == null) + return 0; + return UInt16.Parse(value, NumberStyles.Integer, provider); + } + + [CLSCompliant(false)] + public static ushort ToUInt16(DateTime value) + { + return ((IConvertible)value).ToUInt16(null); + } + + // Disallowed conversions to UInt16 + // public static ushort ToUInt16(TimeSpan value) + + // Conversions to Int32 + + public static int ToInt32(object value) + { + return value == null ? 0 : ((IConvertible)value).ToInt32(null); + } + + public static int ToInt32(object value, IFormatProvider provider) + { + return value == null ? 0 : ((IConvertible)value).ToInt32(provider); + } + + + public static int ToInt32(bool value) + { + return value ? Boolean.True : Boolean.False; + } + + public static int ToInt32(char value) + { + return value; + } + + [CLSCompliant(false)] + public static int ToInt32(sbyte value) + { + return value; + } + + public static int ToInt32(byte value) + { + return value; + } + + public static int ToInt32(short value) + { + return value; + } + + [CLSCompliant(false)] + public static int ToInt32(ushort value) + { + return value; + } + + [CLSCompliant(false)] + public static int ToInt32(uint value) + { + if (value > Int32.MaxValue) ThrowInt32OverflowException(); + Contract.EndContractBlock(); + return (int)value; + } + + public static int ToInt32(int value) + { + return value; + } + + public static int ToInt32(long value) + { + if (value < Int32.MinValue || value > Int32.MaxValue) ThrowInt32OverflowException(); + Contract.EndContractBlock(); + return (int)value; + } + + [CLSCompliant(false)] + public static int ToInt32(ulong value) + { + if (value > Int32.MaxValue) ThrowInt32OverflowException(); + Contract.EndContractBlock(); + return (int)value; + } + + public static int ToInt32(float value) + { + return ToInt32((double)value); + } + + public static int ToInt32(double value) + { + if (value >= 0) + { + if (value < 2147483647.5) + { + int result = (int)value; + double dif = value - result; + if (dif > 0.5 || dif == 0.5 && (result & 1) != 0) result++; + return result; + } + } + else + { + if (value >= -2147483648.5) + { + int result = (int)value; + double dif = value - result; + if (dif < -0.5 || dif == -0.5 && (result & 1) != 0) result--; + return result; + } + } + throw new OverflowException(SR.Overflow_Int32); + } + + public static int ToInt32(decimal value) + { + return Decimal.ToInt32(Decimal.Round(value, 0)); + } + + public static int ToInt32(String value) + { + if (value == null) + return 0; + return Int32.Parse(value, CultureInfo.CurrentCulture); + } + + public static int ToInt32(String value, IFormatProvider provider) + { + if (value == null) + return 0; + return Int32.Parse(value, NumberStyles.Integer, provider); + } + + public static int ToInt32(DateTime value) + { + return ((IConvertible)value).ToInt32(null); + } + + + // Disallowed conversions to Int32 + // public static int ToInt32(TimeSpan value) + + // Conversions to UInt32 + + [CLSCompliant(false)] + public static uint ToUInt32(object value) + { + return value == null ? 0 : ((IConvertible)value).ToUInt32(null); + } + + [CLSCompliant(false)] + public static uint ToUInt32(object value, IFormatProvider provider) + { + return value == null ? 0 : ((IConvertible)value).ToUInt32(provider); + } + + + [CLSCompliant(false)] + public static uint ToUInt32(bool value) + { + return value ? (uint)Boolean.True : (uint)Boolean.False; + } + + [CLSCompliant(false)] + public static uint ToUInt32(char value) + { + return value; + } + + [CLSCompliant(false)] + public static uint ToUInt32(sbyte value) + { + if (value < 0) ThrowUInt32OverflowException(); + Contract.EndContractBlock(); + return (uint)value; + } + + [CLSCompliant(false)] + public static uint ToUInt32(byte value) + { + return value; + } + + [CLSCompliant(false)] + public static uint ToUInt32(short value) + { + if (value < 0) ThrowUInt32OverflowException(); + Contract.EndContractBlock(); + return (uint)value; + } + + [CLSCompliant(false)] + public static uint ToUInt32(ushort value) + { + return value; + } + + [CLSCompliant(false)] + public static uint ToUInt32(int value) + { + if (value < 0) ThrowUInt32OverflowException(); + Contract.EndContractBlock(); + return (uint)value; + } + + [CLSCompliant(false)] + public static uint ToUInt32(uint value) + { + return value; + } + + [CLSCompliant(false)] + public static uint ToUInt32(long value) + { + if (value < 0 || value > UInt32.MaxValue) ThrowUInt32OverflowException(); + Contract.EndContractBlock(); + return (uint)value; + } + + [CLSCompliant(false)] + public static uint ToUInt32(ulong value) + { + if (value > UInt32.MaxValue) ThrowUInt32OverflowException(); + Contract.EndContractBlock(); + return (uint)value; + } + + [CLSCompliant(false)] + public static uint ToUInt32(float value) + { + return ToUInt32((double)value); + } + + [CLSCompliant(false)] + public static uint ToUInt32(double value) + { + if (value >= -0.5 && value < 4294967295.5) + { + uint result = (uint)value; + double dif = value - result; + if (dif > 0.5 || dif == 0.5 && (result & 1) != 0) result++; + return result; + } + throw new OverflowException(SR.Overflow_UInt32); + } + + [CLSCompliant(false)] + public static uint ToUInt32(decimal value) + { + return Decimal.ToUInt32(Decimal.Round(value, 0)); + } + + [CLSCompliant(false)] + public static uint ToUInt32(String value) + { + if (value == null) + return 0; + return UInt32.Parse(value, CultureInfo.CurrentCulture); + } + + [CLSCompliant(false)] + public static uint ToUInt32(String value, IFormatProvider provider) + { + if (value == null) + return 0; + return UInt32.Parse(value, NumberStyles.Integer, provider); + } + + [CLSCompliant(false)] + public static uint ToUInt32(DateTime value) + { + return ((IConvertible)value).ToUInt32(null); + } + + // Disallowed conversions to UInt32 + // public static uint ToUInt32(TimeSpan value) + + // Conversions to Int64 + + public static long ToInt64(object value) + { + return value == null ? 0 : ((IConvertible)value).ToInt64(null); + } + + public static long ToInt64(object value, IFormatProvider provider) + { + return value == null ? 0 : ((IConvertible)value).ToInt64(provider); + } + + + public static long ToInt64(bool value) + { + return value ? Boolean.True : Boolean.False; + } + + public static long ToInt64(char value) + { + return value; + } + + [CLSCompliant(false)] + public static long ToInt64(sbyte value) + { + return value; + } + + public static long ToInt64(byte value) + { + return value; + } + + public static long ToInt64(short value) + { + return value; + } + + [CLSCompliant(false)] + public static long ToInt64(ushort value) + { + return value; + } + + public static long ToInt64(int value) + { + return value; + } + + [CLSCompliant(false)] + public static long ToInt64(uint value) + { + return value; + } + + [CLSCompliant(false)] + public static long ToInt64(ulong value) + { + if (value > Int64.MaxValue) ThrowInt64OverflowException(); + Contract.EndContractBlock(); + return (long)value; + } + + public static long ToInt64(long value) + { + return value; + } + + + public static long ToInt64(float value) + { + return ToInt64((double)value); + } + + public static long ToInt64(double value) + { + return checked((long)Math.Round(value)); + } + + public static long ToInt64(decimal value) + { + return Decimal.ToInt64(Decimal.Round(value, 0)); + } + + public static long ToInt64(string value) + { + if (value == null) + return 0; + return Int64.Parse(value, CultureInfo.CurrentCulture); + } + + public static long ToInt64(String value, IFormatProvider provider) + { + if (value == null) + return 0; + return Int64.Parse(value, NumberStyles.Integer, provider); + } + + public static long ToInt64(DateTime value) + { + return ((IConvertible)value).ToInt64(null); + } + + // Disallowed conversions to Int64 + // public static long ToInt64(TimeSpan value) + + // Conversions to UInt64 + + [CLSCompliant(false)] + public static ulong ToUInt64(object value) + { + return value == null ? 0 : ((IConvertible)value).ToUInt64(null); + } + + [CLSCompliant(false)] + public static ulong ToUInt64(object value, IFormatProvider provider) + { + return value == null ? 0 : ((IConvertible)value).ToUInt64(provider); + } + + [CLSCompliant(false)] + public static ulong ToUInt64(bool value) + { + return value ? (ulong)Boolean.True : (ulong)Boolean.False; + } + + [CLSCompliant(false)] + public static ulong ToUInt64(char value) + { + return value; + } + + + [CLSCompliant(false)] + public static ulong ToUInt64(sbyte value) + { + if (value < 0) ThrowUInt64OverflowException(); + Contract.EndContractBlock(); + return (ulong)value; + } + + [CLSCompliant(false)] + public static ulong ToUInt64(byte value) + { + return value; + } + + [CLSCompliant(false)] + public static ulong ToUInt64(short value) + { + if (value < 0) ThrowUInt64OverflowException(); + Contract.EndContractBlock(); + return (ulong)value; + } + + [CLSCompliant(false)] + public static ulong ToUInt64(ushort value) + { + return value; + } + + [CLSCompliant(false)] + public static ulong ToUInt64(int value) + { + if (value < 0) ThrowUInt64OverflowException(); + Contract.EndContractBlock(); + return (ulong)value; + } + + [CLSCompliant(false)] + public static ulong ToUInt64(uint value) + { + return value; + } + + [CLSCompliant(false)] + public static ulong ToUInt64(long value) + { + if (value < 0) ThrowUInt64OverflowException(); + Contract.EndContractBlock(); + return (ulong)value; + } + + [CLSCompliant(false)] + public static ulong ToUInt64(UInt64 value) + { + return value; + } + + [CLSCompliant(false)] + public static ulong ToUInt64(float value) + { + return ToUInt64((double)value); + } + + [CLSCompliant(false)] + public static ulong ToUInt64(double value) + { + return checked((ulong)Math.Round(value)); + } + + [CLSCompliant(false)] + public static ulong ToUInt64(decimal value) + { + return Decimal.ToUInt64(Decimal.Round(value, 0)); + } + + [CLSCompliant(false)] + public static ulong ToUInt64(String value) + { + if (value == null) + return 0; + return UInt64.Parse(value, CultureInfo.CurrentCulture); + } + + [CLSCompliant(false)] + public static ulong ToUInt64(String value, IFormatProvider provider) + { + if (value == null) + return 0; + return UInt64.Parse(value, NumberStyles.Integer, provider); + } + + [CLSCompliant(false)] + public static ulong ToUInt64(DateTime value) + { + return ((IConvertible)value).ToUInt64(null); + } + + // Disallowed conversions to UInt64 + // public static ulong ToUInt64(TimeSpan value) + + // Conversions to Single + + public static float ToSingle(object value) + { + return value == null ? 0 : ((IConvertible)value).ToSingle(null); + } + + public static float ToSingle(object value, IFormatProvider provider) + { + return value == null ? 0 : ((IConvertible)value).ToSingle(provider); + } + + [CLSCompliant(false)] + public static float ToSingle(sbyte value) + { + return value; + } + + public static float ToSingle(byte value) + { + return value; + } + + public static float ToSingle(char value) + { + return ((IConvertible)value).ToSingle(null); + } + + public static float ToSingle(short value) + { + return value; + } + + [CLSCompliant(false)] + public static float ToSingle(ushort value) + { + return value; + } + + public static float ToSingle(int value) + { + return value; + } + + [CLSCompliant(false)] + public static float ToSingle(uint value) + { + return value; + } + + public static float ToSingle(long value) + { + return value; + } + + [CLSCompliant(false)] + public static float ToSingle(ulong value) + { + return value; + } + + public static float ToSingle(float value) + { + return value; + } + + public static float ToSingle(double value) + { + return (float)value; + } + + public static float ToSingle(decimal value) + { + return (float)value; + } + + public static float ToSingle(String value) + { + if (value == null) + return 0; + return Single.Parse(value, CultureInfo.CurrentCulture); + } + + public static float ToSingle(String value, IFormatProvider provider) + { + if (value == null) + return 0; + return Single.Parse(value, NumberStyles.Float | NumberStyles.AllowThousands, provider); + } + + + public static float ToSingle(bool value) + { + return value ? Boolean.True : Boolean.False; + } + + public static float ToSingle(DateTime value) + { + return ((IConvertible)value).ToSingle(null); + } + + // Disallowed conversions to Single + // public static float ToSingle(TimeSpan value) + + // Conversions to Double + + public static double ToDouble(object value) + { + return value == null ? 0 : ((IConvertible)value).ToDouble(null); + } + + public static double ToDouble(object value, IFormatProvider provider) + { + return value == null ? 0 : ((IConvertible)value).ToDouble(provider); + } + + + [CLSCompliant(false)] + public static double ToDouble(sbyte value) + { + return value; + } + + public static double ToDouble(byte value) + { + return value; + } + + public static double ToDouble(short value) + { + return value; + } + + public static double ToDouble(char value) + { + return ((IConvertible)value).ToDouble(null); + } + + [CLSCompliant(false)] + public static double ToDouble(ushort value) + { + return value; + } + + public static double ToDouble(int value) + { + return value; + } + + [CLSCompliant(false)] + public static double ToDouble(uint value) + { + return value; + } + + public static double ToDouble(long value) + { + return value; + } + + [CLSCompliant(false)] + public static double ToDouble(ulong value) + { + return value; + } + + public static double ToDouble(float value) + { + return value; + } + + public static double ToDouble(double value) + { + return value; + } + + public static double ToDouble(decimal value) + { + return (double)value; + } + + public static double ToDouble(String value) + { + if (value == null) + return 0; + return Double.Parse(value, CultureInfo.CurrentCulture); + } + + public static double ToDouble(String value, IFormatProvider provider) + { + if (value == null) + return 0; + return Double.Parse(value, NumberStyles.Float | NumberStyles.AllowThousands, provider); + } + + public static double ToDouble(bool value) + { + return value ? Boolean.True : Boolean.False; + } + + public static double ToDouble(DateTime value) + { + return ((IConvertible)value).ToDouble(null); + } + + // Disallowed conversions to Double + // public static double ToDouble(TimeSpan value) + + // Conversions to Decimal + + public static decimal ToDecimal(object value) + { + return value == null ? 0 : ((IConvertible)value).ToDecimal(null); + } + + public static decimal ToDecimal(object value, IFormatProvider provider) + { + return value == null ? 0 : ((IConvertible)value).ToDecimal(provider); + } + + [CLSCompliant(false)] + public static decimal ToDecimal(sbyte value) + { + return value; + } + + public static decimal ToDecimal(byte value) + { + return value; + } + + public static decimal ToDecimal(char value) + { + return ((IConvertible)value).ToDecimal(null); + } + + public static decimal ToDecimal(short value) + { + return value; + } + + [CLSCompliant(false)] + public static decimal ToDecimal(ushort value) + { + return value; + } + + public static decimal ToDecimal(int value) + { + return value; + } + + [CLSCompliant(false)] + public static decimal ToDecimal(uint value) + { + return value; + } + + public static decimal ToDecimal(long value) + { + return value; + } + + [CLSCompliant(false)] + public static decimal ToDecimal(ulong value) + { + return value; + } + + public static decimal ToDecimal(float value) + { + return (decimal)value; + } + + public static decimal ToDecimal(double value) + { + return (decimal)value; + } + + public static decimal ToDecimal(String value) + { + if (value == null) + return 0m; + return Decimal.Parse(value, CultureInfo.CurrentCulture); + } + + public static Decimal ToDecimal(String value, IFormatProvider provider) + { + if (value == null) + return 0m; + return Decimal.Parse(value, NumberStyles.Number, provider); + } + + public static decimal ToDecimal(decimal value) + { + return value; + } + + public static decimal ToDecimal(bool value) + { + return value ? Boolean.True : Boolean.False; + } + + public static decimal ToDecimal(DateTime value) + { + return ((IConvertible)value).ToDecimal(null); + } + + // Disallowed conversions to Decimal + // public static decimal ToDecimal(TimeSpan value) + + // Conversions to DateTime + + public static DateTime ToDateTime(DateTime value) + { + return value; + } + + public static DateTime ToDateTime(object value) + { + return value == null ? DateTime.MinValue : ((IConvertible)value).ToDateTime(null); + } + + public static DateTime ToDateTime(object value, IFormatProvider provider) + { + return value == null ? DateTime.MinValue : ((IConvertible)value).ToDateTime(provider); + } + + public static DateTime ToDateTime(String value) + { + if (value == null) + return new DateTime(0); + return DateTime.Parse(value, CultureInfo.CurrentCulture); + } + + public static DateTime ToDateTime(String value, IFormatProvider provider) + { + if (value == null) + return new DateTime(0); + return DateTime.Parse(value, provider); + } + + [CLSCompliant(false)] + public static DateTime ToDateTime(sbyte value) + { + return ((IConvertible)value).ToDateTime(null); + } + + public static DateTime ToDateTime(byte value) + { + return ((IConvertible)value).ToDateTime(null); + } + + public static DateTime ToDateTime(short value) + { + return ((IConvertible)value).ToDateTime(null); + } + + [CLSCompliant(false)] + public static DateTime ToDateTime(ushort value) + { + return ((IConvertible)value).ToDateTime(null); + } + + public static DateTime ToDateTime(int value) + { + return ((IConvertible)value).ToDateTime(null); + } + + [CLSCompliant(false)] + public static DateTime ToDateTime(uint value) + { + return ((IConvertible)value).ToDateTime(null); + } + + public static DateTime ToDateTime(long value) + { + return ((IConvertible)value).ToDateTime(null); + } + + [CLSCompliant(false)] + public static DateTime ToDateTime(ulong value) + { + return ((IConvertible)value).ToDateTime(null); + } + + public static DateTime ToDateTime(bool value) + { + return ((IConvertible)value).ToDateTime(null); + } + + public static DateTime ToDateTime(char value) + { + return ((IConvertible)value).ToDateTime(null); + } + + public static DateTime ToDateTime(float value) + { + return ((IConvertible)value).ToDateTime(null); + } + + public static DateTime ToDateTime(double value) + { + return ((IConvertible)value).ToDateTime(null); + } + + public static DateTime ToDateTime(decimal value) + { + return ((IConvertible)value).ToDateTime(null); + } + + // Disallowed conversions to DateTime + // public static DateTime ToDateTime(TimeSpan value) + + // Conversions to String + + public static string ToString(Object value) + { + return ToString(value, null); + } + + public static string ToString(Object value, IFormatProvider provider) + { + IConvertible ic = value as IConvertible; + if (ic != null) + return ic.ToString(provider); + IFormattable formattable = value as IFormattable; + if (formattable != null) + return formattable.ToString(null, provider); + return value == null ? String.Empty : value.ToString(); + } + + public static string ToString(bool value) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(); + } + + public static string ToString(bool value, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(); + } + + public static string ToString(char value) + { + Contract.Ensures(Contract.Result<string>() != null); + return Char.ToString(value); + } + + public static string ToString(char value, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(); + } + + [CLSCompliant(false)] + public static string ToString(sbyte value) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(CultureInfo.CurrentCulture); + } + + [CLSCompliant(false)] + public static string ToString(sbyte value, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(provider); + } + + public static string ToString(byte value) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(CultureInfo.CurrentCulture); + } + + public static string ToString(byte value, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(provider); + } + + public static string ToString(short value) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(CultureInfo.CurrentCulture); + } + + public static string ToString(short value, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(provider); + } + + [CLSCompliant(false)] + public static string ToString(ushort value) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(CultureInfo.CurrentCulture); + } + + [CLSCompliant(false)] + public static string ToString(ushort value, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(provider); + } + + public static string ToString(int value) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(CultureInfo.CurrentCulture); + } + + public static string ToString(int value, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(provider); + } + + [CLSCompliant(false)] + public static string ToString(uint value) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(CultureInfo.CurrentCulture); + } + + [CLSCompliant(false)] + public static string ToString(uint value, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(provider); + } + + public static string ToString(long value) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(CultureInfo.CurrentCulture); + } + + public static string ToString(long value, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(provider); + } + + [CLSCompliant(false)] + public static string ToString(ulong value) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(CultureInfo.CurrentCulture); + } + + [CLSCompliant(false)] + public static string ToString(ulong value, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(provider); + } + + public static string ToString(float value) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(CultureInfo.CurrentCulture); + } + + public static string ToString(float value, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(provider); + } + + public static string ToString(double value) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(CultureInfo.CurrentCulture); + } + + public static string ToString(double value, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(provider); + } + + public static string ToString(decimal value) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(CultureInfo.CurrentCulture); + } + + public static string ToString(Decimal value, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(provider); + } + + public static string ToString(DateTime value) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(); + } + + public static string ToString(DateTime value, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<string>() != null); + return value.ToString(provider); + } + + public static String ToString(String value) + { + Contract.Ensures(Contract.Result<string>() == value); // We were always skipping the null check here. + return value; + } + + public static String ToString(String value, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<string>() == value); // We were always skipping the null check here. + return value; // avoid the null check + } + + + // + // Conversions which understand Base XXX numbers. + // + // Parses value in base base. base can only + // be 2, 8, 10, or 16. If base is 16, the number may be preceded + // by 0x; any other leading or trailing characters cause an error. + // + public static byte ToByte(String value, int fromBase) + { + if (fromBase != 2 && fromBase != 8 && fromBase != 10 && fromBase != 16) + { + throw new ArgumentException(SR.Arg_InvalidBase); + } + Contract.EndContractBlock(); + int r = ParseNumbers.StringToInt(value, fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsUnsigned); + if (r < Byte.MinValue || r > Byte.MaxValue) + ThrowByteOverflowException(); + return (byte)r; + } + + // Parses value in base fromBase. fromBase can only + // be 2, 8, 10, or 16. If fromBase is 16, the number may be preceded + // by 0x; any other leading or trailing characters cause an error. + // + [CLSCompliant(false)] + public static sbyte ToSByte(String value, int fromBase) + { + if (fromBase != 2 && fromBase != 8 && fromBase != 10 && fromBase != 16) + { + throw new ArgumentException(SR.Arg_InvalidBase); + } + Contract.EndContractBlock(); + int r = ParseNumbers.StringToInt(value, fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsI1); + if (fromBase != 10 && r <= Byte.MaxValue) + return (sbyte)r; + + if (r < SByte.MinValue || r > SByte.MaxValue) + ThrowSByteOverflowException(); + return (sbyte)r; + } + + // Parses value in base fromBase. fromBase can only + // be 2, 8, 10, or 16. If fromBase is 16, the number may be preceded + // by 0x; any other leading or trailing characters cause an error. + // + public static short ToInt16(String value, int fromBase) + { + if (fromBase != 2 && fromBase != 8 && fromBase != 10 && fromBase != 16) + { + throw new ArgumentException(SR.Arg_InvalidBase); + } + Contract.EndContractBlock(); + int r = ParseNumbers.StringToInt(value, fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsI2); + if (fromBase != 10 && r <= UInt16.MaxValue) + return (short)r; + + if (r < Int16.MinValue || r > Int16.MaxValue) + ThrowInt16OverflowException(); + return (short)r; + } + + // Parses value in base fromBase. fromBase can only + // be 2, 8, 10, or 16. If fromBase is 16, the number may be preceded + // by 0x; any other leading or trailing characters cause an error. + // + [CLSCompliant(false)] + public static ushort ToUInt16(String value, int fromBase) + { + if (fromBase != 2 && fromBase != 8 && fromBase != 10 && fromBase != 16) + { + throw new ArgumentException(SR.Arg_InvalidBase); + } + Contract.EndContractBlock(); + int r = ParseNumbers.StringToInt(value, fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsUnsigned); + if (r < UInt16.MinValue || r > UInt16.MaxValue) + ThrowUInt16OverflowException(); + return (ushort)r; + } + + // Parses value in base fromBase. fromBase can only + // be 2, 8, 10, or 16. If fromBase is 16, the number may be preceded + // by 0x; any other leading or trailing characters cause an error. + // + public static int ToInt32(String value, int fromBase) + { + if (fromBase != 2 && fromBase != 8 && fromBase != 10 && fromBase != 16) + { + throw new ArgumentException(SR.Arg_InvalidBase); + } + Contract.EndContractBlock(); + return ParseNumbers.StringToInt(value, fromBase, ParseNumbers.IsTight); + } + + // Parses value in base fromBase. fromBase can only + // be 2, 8, 10, or 16. If fromBase is 16, the number may be preceded + // by 0x; any other leading or trailing characters cause an error. + // + [CLSCompliant(false)] + public static uint ToUInt32(String value, int fromBase) + { + if (fromBase != 2 && fromBase != 8 && fromBase != 10 && fromBase != 16) + { + throw new ArgumentException(SR.Arg_InvalidBase); + } + Contract.EndContractBlock(); + return (uint)ParseNumbers.StringToInt(value, fromBase, ParseNumbers.TreatAsUnsigned | ParseNumbers.IsTight); + } + + // Parses value in base fromBase. fromBase can only + // be 2, 8, 10, or 16. If fromBase is 16, the number may be preceded + // by 0x; any other leading or trailing characters cause an error. + // + public static long ToInt64(String value, int fromBase) + { + if (fromBase != 2 && fromBase != 8 && fromBase != 10 && fromBase != 16) + { + throw new ArgumentException(SR.Arg_InvalidBase); + } + Contract.EndContractBlock(); + return ParseNumbers.StringToLong(value, fromBase, ParseNumbers.IsTight); + } + + // Parses value in base fromBase. fromBase can only + // be 2, 8, 10, or 16. If fromBase is 16, the number may be preceded + // by 0x; any other leading or trailing characters cause an error. + // + [CLSCompliant(false)] + public static ulong ToUInt64(String value, int fromBase) + { + if (fromBase != 2 && fromBase != 8 && fromBase != 10 && fromBase != 16) + { + throw new ArgumentException(SR.Arg_InvalidBase); + } + Contract.EndContractBlock(); + return (ulong)ParseNumbers.StringToLong(value, fromBase, ParseNumbers.TreatAsUnsigned | ParseNumbers.IsTight); + } + + // Convert the byte value to a string in base fromBase + public static String ToString(byte value, int toBase) + { + if (toBase != 2 && toBase != 8 && toBase != 10 && toBase != 16) + { + throw new ArgumentException(SR.Arg_InvalidBase); + } + Contract.EndContractBlock(); + return ParseNumbers.IntToString((int)value, toBase, -1, ' ', ParseNumbers.PrintAsI1); + } + + // Convert the Int16 value to a string in base fromBase + public static String ToString(short value, int toBase) + { + if (toBase != 2 && toBase != 8 && toBase != 10 && toBase != 16) + { + throw new ArgumentException(SR.Arg_InvalidBase); + } + Contract.EndContractBlock(); + return ParseNumbers.IntToString((int)value, toBase, -1, ' ', ParseNumbers.PrintAsI2); + } + + // Convert the Int32 value to a string in base toBase + public static String ToString(int value, int toBase) + { + if (toBase != 2 && toBase != 8 && toBase != 10 && toBase != 16) + { + throw new ArgumentException(SR.Arg_InvalidBase); + } + Contract.EndContractBlock(); + return ParseNumbers.IntToString(value, toBase, -1, ' ', 0); + } + + // Convert the Int64 value to a string in base toBase + public static String ToString(long value, int toBase) + { + if (toBase != 2 && toBase != 8 && toBase != 10 && toBase != 16) + { + throw new ArgumentException(SR.Arg_InvalidBase); + } + Contract.EndContractBlock(); + return ParseNumbers.LongToString(value, toBase, -1, ' ', 0); + } + + public static String ToBase64String(byte[] inArray) + { + if (inArray == null) + { + throw new ArgumentNullException(nameof(inArray)); + } + Contract.Ensures(Contract.Result<string>() != null); + Contract.EndContractBlock(); + return ToBase64String(inArray, 0, inArray.Length, Base64FormattingOptions.None); + } + + public static String ToBase64String(byte[] inArray, Base64FormattingOptions options) + { + if (inArray == null) + { + throw new ArgumentNullException(nameof(inArray)); + } + Contract.Ensures(Contract.Result<string>() != null); + Contract.EndContractBlock(); + return ToBase64String(inArray, 0, inArray.Length, options); + } + + public static String ToBase64String(byte[] inArray, int offset, int length) + { + return ToBase64String(inArray, offset, length, Base64FormattingOptions.None); + } + + public static unsafe String ToBase64String(byte[] inArray, int offset, int length, Base64FormattingOptions options) + { + //Do data verfication + if (inArray == null) + throw new ArgumentNullException(nameof(inArray)); + if (length < 0) + throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Index); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_GenericPositive); + if (options < Base64FormattingOptions.None || options > Base64FormattingOptions.InsertLineBreaks) + throw new ArgumentException(string.Format(SR.Arg_EnumIllegalVal, (int)options)); + Contract.Ensures(Contract.Result<string>() != null); + Contract.EndContractBlock(); + + int inArrayLength; + int stringLength; + + inArrayLength = inArray.Length; + if (offset > (inArrayLength - length)) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_OffsetLength); + + if (inArrayLength == 0) + return String.Empty; + + bool insertLineBreaks = (options == Base64FormattingOptions.InsertLineBreaks); + //Create the new string. This is the maximally required length. + stringLength = ToBase64_CalculateAndValidateOutputLength(length, insertLineBreaks); + + string returnString = string.FastAllocateString(stringLength); + fixed (char* outChars = returnString) + { + fixed (byte* inData = &inArray[0]) + { + int j = ConvertToBase64Array(outChars, inData, offset, length, insertLineBreaks); + Debug.Assert(returnString.Length == j, "returnString.Length == j"); + return returnString; + } + } + } + + public static int ToBase64CharArray(byte[] inArray, int offsetIn, int length, char[] outArray, int offsetOut) + { + Contract.Ensures(Contract.Result<int>() >= 0); + Contract.Ensures(Contract.Result<int>() <= outArray.Length); + Contract.EndContractBlock(); + + return ToBase64CharArray(inArray, offsetIn, length, outArray, offsetOut, Base64FormattingOptions.None); + } + + public static unsafe int ToBase64CharArray(byte[] inArray, int offsetIn, int length, char[] outArray, int offsetOut, Base64FormattingOptions options) + { + //Do data verfication + if (inArray == null) + throw new ArgumentNullException(nameof(inArray)); + if (outArray == null) + throw new ArgumentNullException(nameof(outArray)); + if (length < 0) + throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Index); + if (offsetIn < 0) + throw new ArgumentOutOfRangeException(nameof(offsetIn), SR.ArgumentOutOfRange_GenericPositive); + if (offsetOut < 0) + throw new ArgumentOutOfRangeException(nameof(offsetOut), SR.ArgumentOutOfRange_GenericPositive); + + if (options < Base64FormattingOptions.None || options > Base64FormattingOptions.InsertLineBreaks) + { + throw new ArgumentException(string.Format(SR.Arg_EnumIllegalVal, (int)options)); + } + Contract.Ensures(Contract.Result<int>() >= 0); + Contract.Ensures(Contract.Result<int>() <= outArray.Length); + Contract.EndContractBlock(); + + + int retVal; + + int inArrayLength; + int outArrayLength; + int numElementsToCopy; + + inArrayLength = inArray.Length; + + if (offsetIn > (int)(inArrayLength - length)) + throw new ArgumentOutOfRangeException(nameof(offsetIn), SR.ArgumentOutOfRange_OffsetLength); + + if (inArrayLength == 0) + return 0; + + bool insertLineBreaks = (options == Base64FormattingOptions.InsertLineBreaks); + //This is the maximally required length that must be available in the char array + outArrayLength = outArray.Length; + + // Length of the char buffer required + numElementsToCopy = ToBase64_CalculateAndValidateOutputLength(length, insertLineBreaks); + + if (offsetOut > (int)(outArrayLength - numElementsToCopy)) + throw new ArgumentOutOfRangeException(nameof(offsetOut), SR.ArgumentOutOfRange_OffsetOut); + + fixed (char* outChars = &outArray[offsetOut]) + { + fixed (byte* inData = &inArray[0]) + { + retVal = ConvertToBase64Array(outChars, inData, offsetIn, length, insertLineBreaks); + } + } + + return retVal; + } + + private static unsafe int ConvertToBase64Array(char* outChars, byte* inData, int offset, int length, bool insertLineBreaks) + { + int lengthmod3 = length % 3; + int calcLength = offset + (length - lengthmod3); + int j = 0; + int charcount = 0; + //Convert three bytes at a time to base64 notation. This will consume 4 chars. + int i; + + // get a pointer to the base64Table to avoid unnecessary range checking + fixed (char* base64 = &base64Table[0]) + { + for (i = offset; i < calcLength; i += 3) + { + if (insertLineBreaks) + { + if (charcount == base64LineBreakPosition) + { + outChars[j++] = '\r'; + outChars[j++] = '\n'; + charcount = 0; + } + charcount += 4; + } + outChars[j] = base64[(inData[i] & 0xfc) >> 2]; + outChars[j + 1] = base64[((inData[i] & 0x03) << 4) | ((inData[i + 1] & 0xf0) >> 4)]; + outChars[j + 2] = base64[((inData[i + 1] & 0x0f) << 2) | ((inData[i + 2] & 0xc0) >> 6)]; + outChars[j + 3] = base64[(inData[i + 2] & 0x3f)]; + j += 4; + } + + //Where we left off before + i = calcLength; + + if (insertLineBreaks && (lengthmod3 != 0) && (charcount == base64LineBreakPosition)) + { + outChars[j++] = '\r'; + outChars[j++] = '\n'; + } + + switch (lengthmod3) + { + case 2: //One character padding needed + outChars[j] = base64[(inData[i] & 0xfc) >> 2]; + outChars[j + 1] = base64[((inData[i] & 0x03) << 4) | ((inData[i + 1] & 0xf0) >> 4)]; + outChars[j + 2] = base64[(inData[i + 1] & 0x0f) << 2]; + outChars[j + 3] = base64[64]; //Pad + j += 4; + break; + case 1: // Two character padding needed + outChars[j] = base64[(inData[i] & 0xfc) >> 2]; + outChars[j + 1] = base64[(inData[i] & 0x03) << 4]; + outChars[j + 2] = base64[64]; //Pad + outChars[j + 3] = base64[64]; //Pad + j += 4; + break; + } + } + + return j; + } + + private static int ToBase64_CalculateAndValidateOutputLength(int inputLength, bool insertLineBreaks) + { + long outlen = ((long)inputLength) / 3 * 4; // the base length - we want integer division here. + outlen += ((inputLength % 3) != 0) ? 4 : 0; // at most 4 more chars for the remainder + + if (outlen == 0) + return 0; + + if (insertLineBreaks) + { + long newLines = outlen / base64LineBreakPosition; + if ((outlen % base64LineBreakPosition) == 0) + { + --newLines; + } + outlen += newLines * 2; // the number of line break chars we'll add, "\r\n" + } + + // If we overflow an int then we cannot allocate enough + // memory to output the value so throw + if (outlen > int.MaxValue) + throw new OutOfMemoryException(); + + return (int)outlen; + } + + + /// <summary> + /// 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> + public static Byte[] FromBase64String(String s) + { + // "s" is an unfortunate parameter name, but we need to keep it for backward compat. + + if (s == null) + throw new ArgumentNullException(nameof(s)); + + Contract.EndContractBlock(); + + unsafe + { + fixed (Char* sPtr = s) + { + return FromBase64CharPtr(sPtr, s.Length); + } + } + } + + + /// <summary> + /// Converts the specified range of a Char array, which encodes binary data as Base64 digits, to the equivalent byte array. + /// </summary> + /// <param name="inArray">Chars representing Base64 encoding characters</param> + /// <param name="offset">A position within the input array.</param> + /// <param name="length">Number of element to convert.</param> + /// <returns>The array of bytes represented by the specified Base64 encoding characters.</returns> + public static Byte[] FromBase64CharArray(Char[] inArray, Int32 offset, Int32 length) + { + if (inArray == null) + throw new ArgumentNullException(nameof(inArray)); + + if (length < 0) + throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Index); + + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_GenericPositive); + + if (offset > inArray.Length - length) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_OffsetLength); + + Contract.EndContractBlock(); + + if (inArray.Length == 0) + { + return Array.Empty<byte>(); + } + + unsafe + { + fixed (Char* inArrayPtr = &inArray[0]) + { + return FromBase64CharPtr(inArrayPtr + offset, length); + } + } + } + + + + /// <summary> + /// Convert Base64 encoding characters to bytes: + /// - Compute result length exactly by actually walking the input; + /// - Allocate new result array based on computation; + /// - Decode input into the new array; + /// </summary> + /// <param name="inputPtr">Pointer to the first input char</param> + /// <param name="inputLength">Number of input chars</param> + /// <returns></returns> + private static unsafe Byte[] FromBase64CharPtr(Char* inputPtr, Int32 inputLength) + { + // The validity of parameters much be checked by callers, thus we are Critical here. + + Debug.Assert(0 <= inputLength); + + // We need to get rid of any trailing white spaces. + // Otherwise we would be rejecting input such as "abc= ": + while (inputLength > 0) + { + Int32 lastChar = inputPtr[inputLength - 1]; + if (lastChar != (Int32)' ' && lastChar != (Int32)'\n' && lastChar != (Int32)'\r' && lastChar != (Int32)'\t') + break; + inputLength--; + } + + // Compute the output length: + Int32 resultLength = FromBase64_ComputeResultLength(inputPtr, inputLength); + + Debug.Assert(0 <= resultLength); + + // resultLength can be zero. We will still enter FromBase64_Decode and process the input. + // It may either simply write no bytes (e.g. input = " ") or throw (e.g. input = "ab"). + + // Create result byte blob: + Byte[] decodedBytes = new Byte[resultLength]; + + // Convert Base64 chars into bytes: + Int32 actualResultLength; + fixed (Byte* decodedBytesPtr = decodedBytes) + actualResultLength = FromBase64_Decode(inputPtr, inputLength, decodedBytesPtr, resultLength); + + // Note that actualResultLength can differ from resultLength if the caller is modifying the array + // as it is being converted. Silently ignore the failure. + // Consider throwing exception in an non in-place release. + + // We are done: + return decodedBytes; + } + + + /// <summary> + /// Decode characters representing a Base64 encoding into bytes: + /// Walk the input. Every time 4 chars are read, convert them to the 3 corresponding output bytes. + /// This method is a bit lengthy on purpose. We are trying to avoid jumps to helpers in the loop + /// to aid performance. + /// </summary> + /// <param name="inputPtr">Pointer to first input char</param> + /// <param name="inputLength">Number of input chars</param> + /// <param name="destPtr">Pointer to location for the first result byte</param> + /// <param name="destLength">Max length of the preallocated result buffer</param> + /// <returns>If the result buffer was not large enough to write all result bytes, return -1; + /// Otherwise return the number of result bytes actually produced.</returns> + private static unsafe Int32 FromBase64_Decode(Char* startInputPtr, Int32 inputLength, Byte* startDestPtr, Int32 destLength) + { + // You may find this method weird to look at. It's written for performance, not aesthetics. + // You will find unrolled loops label jumps and bit manipulations. + + const UInt32 intA = (UInt32)'A'; + const UInt32 inta = (UInt32)'a'; + const UInt32 int0 = (UInt32)'0'; + const UInt32 intEq = (UInt32)'='; + const UInt32 intPlus = (UInt32)'+'; + const UInt32 intSlash = (UInt32)'/'; + const UInt32 intSpace = (UInt32)' '; + const UInt32 intTab = (UInt32)'\t'; + const UInt32 intNLn = (UInt32)'\n'; + const UInt32 intCRt = (UInt32)'\r'; + const UInt32 intAtoZ = (UInt32)('Z' - 'A'); // = ('z' - 'a') + const UInt32 int0to9 = (UInt32)('9' - '0'); + + Char* inputPtr = startInputPtr; + Byte* destPtr = startDestPtr; + + // Pointers to the end of input and output: + Char* endInputPtr = inputPtr + inputLength; + Byte* endDestPtr = destPtr + destLength; + + // Current char code/value: + UInt32 currCode; + + // This 4-byte integer will contain the 4 codes of the current 4-char group. + // Eeach char codes for 6 bits = 24 bits. + // The remaining byte will be FF, we use it as a marker when 4 chars have been processed. + UInt32 currBlockCodes = 0x000000FFu; + + unchecked + { + while (true) + { + // break when done: + if (inputPtr >= endInputPtr) + goto _AllInputConsumed; + + // Get current char: + currCode = (UInt32)(*inputPtr); + inputPtr++; + + // Determine current char code: + + if (currCode - intA <= intAtoZ) + currCode -= intA; + + else if (currCode - inta <= intAtoZ) + currCode -= (inta - 26u); + + else if (currCode - int0 <= int0to9) + currCode -= (int0 - 52u); + + else + { + // Use the slower switch for less common cases: + switch (currCode) + { + // Significant chars: + case intPlus: + currCode = 62u; + break; + + case intSlash: + currCode = 63u; + break; + + // Legal no-value chars (we ignore these): + case intCRt: + case intNLn: + case intSpace: + case intTab: + continue; + + // The equality char is only legal at the end of the input. + // Jump after the loop to make it easier for the JIT register predictor to do a good job for the loop itself: + case intEq: + goto _EqualityCharEncountered; + + // Other chars are illegal: + default: + throw new FormatException(SR.Format_BadBase64Char); + } + } + + // Ok, we got the code. Save it: + currBlockCodes = (currBlockCodes << 6) | currCode; + + // Last bit in currBlockCodes will be on after in shifted right 4 times: + if ((currBlockCodes & 0x80000000u) != 0u) + { + if ((Int32)(endDestPtr - destPtr) < 3) + return -1; + + *(destPtr) = (Byte)(currBlockCodes >> 16); + *(destPtr + 1) = (Byte)(currBlockCodes >> 8); + *(destPtr + 2) = (Byte)(currBlockCodes); + destPtr += 3; + + currBlockCodes = 0x000000FFu; + } + } + } // unchecked while + + // 'd be nice to have an assert that we never get here, but CS0162: Unreachable code detected. + // Debug.Assert(false, "We only leave the above loop by jumping; should never get here."); + + // We jump here out of the loop if we hit an '=': + _EqualityCharEncountered: + + Debug.Assert(currCode == intEq); + + // Recall that inputPtr is now one position past where '=' was read. + // '=' can only be at the last input pos: + if (inputPtr == endInputPtr) + { + // Code is zero for trailing '=': + currBlockCodes <<= 6; + + // The '=' did not complete a 4-group. The input must be bad: + if ((currBlockCodes & 0x80000000u) == 0u) + throw new FormatException(SR.Format_BadBase64CharArrayLength); + + if ((int)(endDestPtr - destPtr) < 2) // Autch! We underestimated the output length! + return -1; + + // We are good, store bytes form this past group. We had a single "=", so we take two bytes: + *(destPtr++) = (Byte)(currBlockCodes >> 16); + *(destPtr++) = (Byte)(currBlockCodes >> 8); + + currBlockCodes = 0x000000FFu; + } + else + { // '=' can also be at the pre-last position iff the last is also a '=' excluding the white spaces: + // We need to get rid of any intermediate white spaces. + // Otherwise we would be rejecting input such as "abc= =": + while (inputPtr < (endInputPtr - 1)) + { + Int32 lastChar = *(inputPtr); + if (lastChar != (Int32)' ' && lastChar != (Int32)'\n' && lastChar != (Int32)'\r' && lastChar != (Int32)'\t') + break; + inputPtr++; + } + + if (inputPtr == (endInputPtr - 1) && *(inputPtr) == '=') + { + // Code is zero for each of the two '=': + currBlockCodes <<= 12; + + // The '=' did not complete a 4-group. The input must be bad: + if ((currBlockCodes & 0x80000000u) == 0u) + throw new FormatException(SR.Format_BadBase64CharArrayLength); + + if ((Int32)(endDestPtr - destPtr) < 1) // Autch! We underestimated the output length! + return -1; + + // We are good, store bytes form this past group. We had a "==", so we take only one byte: + *(destPtr++) = (Byte)(currBlockCodes >> 16); + + currBlockCodes = 0x000000FFu; + } + else // '=' is not ok at places other than the end: + throw new FormatException(SR.Format_BadBase64Char); + } + + // We get here either from above or by jumping out of the loop: + _AllInputConsumed: + + // The last block of chars has less than 4 items + if (currBlockCodes != 0x000000FFu) + throw new FormatException(SR.Format_BadBase64CharArrayLength); + + // Return how many bytes were actually recovered: + return (Int32)(destPtr - startDestPtr); + } // Int32 FromBase64_Decode(...) + + + /// <summary> + /// Compute the number of bytes encoded in the specified Base 64 char array: + /// Walk the entire input counting white spaces and padding chars, then compute result length + /// based on 3 bytes per 4 chars. + /// </summary> + private static unsafe Int32 FromBase64_ComputeResultLength(Char* inputPtr, Int32 inputLength) + { + const UInt32 intEq = (UInt32)'='; + const UInt32 intSpace = (UInt32)' '; + + Debug.Assert(0 <= inputLength); + + Char* inputEndPtr = inputPtr + inputLength; + Int32 usefulInputLength = inputLength; + Int32 padding = 0; + + while (inputPtr < inputEndPtr) + { + UInt32 c = (UInt32)(*inputPtr); + inputPtr++; + + // We want to be as fast as possible and filter out spaces with as few comparisons as possible. + // We end up accepting a number of illegal chars as legal white-space chars. + // This is ok: as soon as we hit them during actual decode we will recognise them as illegal and throw. + if (c <= intSpace) + usefulInputLength--; + + else if (c == intEq) + { + usefulInputLength--; + padding++; + } + } + + Debug.Assert(0 <= usefulInputLength); + + // For legal input, we can assume that 0 <= padding < 3. But it may be more for illegal input. + // We will notice it at decode when we see a '=' at the wrong place. + Debug.Assert(0 <= padding); + + // Perf: reuse the variable that stored the number of '=' to store the number of bytes encoded by the + // last group that contains the '=': + if (padding != 0) + { + if (padding == 1) + padding = 2; + else if (padding == 2) + padding = 1; + else + throw new FormatException(SR.Format_BadBase64Char); + } + + // Done: + return (usefulInputLength / 4) * 3 + padding; + } + } // class Convert +} // namespace + diff --git a/src/mscorlib/shared/System/CurrentSystemTimeZone.cs b/src/mscorlib/shared/System/CurrentSystemTimeZone.cs new file mode 100644 index 0000000000..2d848397a9 --- /dev/null +++ b/src/mscorlib/shared/System/CurrentSystemTimeZone.cs @@ -0,0 +1,199 @@ +// 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 represents the current system timezone. It is +** the only meaningful implementation of the TimeZone class +** available in this version. +** +** The only TimeZone that we support in version 1 is the +** CurrentTimeZone as determined by the system timezone. +** +** +============================================================*/ + +using System; +using System.Diagnostics.Contracts; +using System.Text; +using System.Collections; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; + +namespace System +{ + [Obsolete("System.CurrentSystemTimeZone has been deprecated. Please investigate the use of System.TimeZoneInfo.Local instead.")] + [Serializable] + internal partial class CurrentSystemTimeZone : TimeZone + { + // Standard offset in ticks to the Universal time if + // no daylight saving is in used. + // E.g. the offset for PST (Pacific Standard time) should be -8 * 60 * 60 * 1000 * 10000. + // (1 millisecond = 10000 ticks) + private long m_ticksOffset; + private String m_standardName; + private String m_daylightName; + + internal CurrentSystemTimeZone() + { + TimeZoneInfo local = TimeZoneInfo.Local; + + m_ticksOffset = local.BaseUtcOffset.Ticks; + m_standardName = local.StandardName; + m_daylightName = local.DaylightName; + } + + public override String StandardName + { + get + { + return m_standardName; + } + } + + public override String DaylightName + { + get + { + return m_daylightName; + } + } + + internal long GetUtcOffsetFromUniversalTime(DateTime time, ref Boolean isAmbiguousLocalDst) + { + // Get the daylight changes for the year of the specified time. + TimeSpan offset = new TimeSpan(m_ticksOffset); + DaylightTime daylightTime = GetDaylightChanges(time.Year); + isAmbiguousLocalDst = false; + + if (daylightTime == null || daylightTime.Delta.Ticks == 0) + { + return offset.Ticks; + } + + // The start and end times represent the range of universal times that are in DST for that year. + // Within that there is an ambiguous hour, usually right at the end, but at the beginning in + // the unusual case of a negative daylight savings delta. + DateTime startTime = daylightTime.Start - offset; + DateTime endTime = daylightTime.End - offset - daylightTime.Delta; + DateTime ambiguousStart; + DateTime ambiguousEnd; + + if (daylightTime.Delta.Ticks > 0) + { + ambiguousStart = endTime - daylightTime.Delta; + ambiguousEnd = endTime; + } + else + { + ambiguousStart = startTime; + ambiguousEnd = startTime - daylightTime.Delta; + } + + Boolean isDst = false; + if (startTime > endTime) + { + // In southern hemisphere, the daylight saving time starts later in the year, and ends in the beginning of next year. + // Note, the summer in the southern hemisphere begins late in the year. + isDst = (time < endTime || time >= startTime); + } + else + { + // In northern hemisphere, the daylight saving time starts in the middle of the year. + isDst = (time >= startTime && time < endTime); + } + + if (isDst) + { + offset += daylightTime.Delta; + + // See if the resulting local time becomes ambiguous. This must be captured here or the + // DateTime will not be able to round-trip back to UTC accurately. + if (time >= ambiguousStart && time < ambiguousEnd) + { + isAmbiguousLocalDst = true; + } + } + return offset.Ticks; + } + + public override DateTime ToLocalTime(DateTime time) + { + if (time.Kind == DateTimeKind.Local) + { + return time; + } + Boolean isAmbiguousLocalDst = false; + Int64 offset = GetUtcOffsetFromUniversalTime(time, ref isAmbiguousLocalDst); + long tick = time.Ticks + offset; + if (tick > DateTime.MaxTicks) + { + return new DateTime(DateTime.MaxTicks, DateTimeKind.Local); + } + if (tick < DateTime.MinTicks) + { + return new DateTime(DateTime.MinTicks, DateTimeKind.Local); + } + return new DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst); + } + + public override DaylightTime GetDaylightChanges(int year) + { + if (year < 1 || year > 9999) + { + throw new ArgumentOutOfRangeException(nameof(year), SR.Format(SR.ArgumentOutOfRange_Range, 1, 9999)); + } + + return GetCachedDaylightChanges(year); + } + + private static DaylightTime CreateDaylightChanges(int year) + { + DaylightTime currentDaylightChanges = null; + + if (TimeZoneInfo.Local.SupportsDaylightSavingTime) + { + DateTime start; + DateTime end; + TimeSpan delta; + + foreach (var rule in TimeZoneInfo.Local.GetAdjustmentRules()) + { + if (rule.DateStart.Year <= year && rule.DateEnd.Year >= year && rule.DaylightDelta != TimeSpan.Zero) + { + start = TimeZoneInfo.TransitionTimeToDateTime(year, rule.DaylightTransitionStart); + end = TimeZoneInfo.TransitionTimeToDateTime(year, rule.DaylightTransitionEnd); + delta = rule.DaylightDelta; + + currentDaylightChanges = new DaylightTime(start, end, delta); + break; + } + } + } + + if (currentDaylightChanges == null) + { + currentDaylightChanges = new DaylightTime(DateTime.MinValue, DateTime.MinValue, TimeSpan.Zero); + } + + return currentDaylightChanges; + } + + public override TimeSpan GetUtcOffset(DateTime time) + { + if (time.Kind == DateTimeKind.Utc) + { + return TimeSpan.Zero; + } + else + { + return new TimeSpan(TimeZone.CalculateUtcOffset(time, GetDaylightChanges(time.Year)).Ticks + m_ticksOffset); + } + } + } // class CurrentSystemTimeZone +} diff --git a/src/mscorlib/shared/System/DBNull.cs b/src/mscorlib/shared/System/DBNull.cs new file mode 100644 index 0000000000..486eb72f2a --- /dev/null +++ b/src/mscorlib/shared/System/DBNull.cs @@ -0,0 +1,119 @@ +// 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 +{ + [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) + { + UnitySerializationHolder.GetUnitySerializationInfo(info, UnitySerializationHolder.NullUnity, null, null); + } + + public override string ToString() + { + return string.Empty; + } + + public string ToString(IFormatProvider provider) + { + return string.Empty; + } + + public TypeCode GetTypeCode() + { + return TypeCode.DBNull; + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + decimal IConvertible.ToDecimal(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.InvalidCast_FromDBNull); + } + + object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} + diff --git a/src/mscorlib/shared/System/DataMisalignedException.cs b/src/mscorlib/shared/System/DataMisalignedException.cs new file mode 100644 index 0000000000..b1991a048e --- /dev/null +++ b/src/mscorlib/shared/System/DataMisalignedException.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. + +/*============================================================================= +** +** +** Purpose: The exception class for a misaligned access exception +** +=============================================================================*/ + +using System.Runtime.Serialization; + +namespace System +{ + [Serializable] + public sealed class DataMisalignedException : SystemException + { + public DataMisalignedException() + : base(SR.Arg_DataMisalignedException) + { + HResult = __HResults.COR_E_DATAMISALIGNED; + } + + public DataMisalignedException(String message) + : base(message) + { + HResult = __HResults.COR_E_DATAMISALIGNED; + } + + public DataMisalignedException(String message, Exception innerException) + : base(message, innerException) + { + HResult = __HResults.COR_E_DATAMISALIGNED; + } + + internal DataMisalignedException(SerializationInfo info, StreamingContext context) : base(info, context) { } + } +} diff --git a/src/mscorlib/shared/System/DateTime.cs b/src/mscorlib/shared/System/DateTime.cs new file mode 100644 index 0000000000..ddb72da77d --- /dev/null +++ b/src/mscorlib/shared/System/DateTime.cs @@ -0,0 +1,1516 @@ +// 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; +using System.Diagnostics.Contracts; +using System.Threading; +using System.Globalization; +using System.Runtime; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; +using System.Runtime.Versioning; +using System.Security; +using CultureInfo = System.Globalization.CultureInfo; +using Calendar = System.Globalization.Calendar; + +namespace System +{ + + // This value type represents a date and time. Every DateTime + // object has a private field (Ticks) of type Int64 that stores the + // date and time as the number of 100 nanosecond intervals since + // 12:00 AM January 1, year 1 A.D. in the proleptic Gregorian Calendar. + // + // Starting from V2.0, DateTime also stored some context about its time + // zone in the form of a 3-state value representing Unspecified, Utc or + // Local. This is stored in the two top bits of the 64-bit numeric value + // with the remainder of the bits storing the tick count. This information + // is only used during time zone conversions and is not part of the + // identity of the DateTime. Thus, operations like Compare and Equals + // ignore this state. This is to stay compatible with earlier behavior + // and performance characteristics and to avoid forcing people into dealing + // with the effects of daylight savings. Note, that this has little effect + // on how the DateTime works except in a context where its specific time + // zone is needed, such as during conversions and some parsing and formatting + // cases. + // + // There is also 4th state stored that is a special type of Local value that + // is used to avoid data loss when round-tripping between local and UTC time. + // See below for more information on this 4th state, although it is + // effectively hidden from most users, who just see the 3-state DateTimeKind + // enumeration. + // + // For compatibility, DateTime does not serialize the Kind data when used in + // binary serialization. + // + // For a description of various calendar issues, look at + // + // Calendar Studies web site, at + // http://serendipity.nofadz.com/hermetic/cal_stud.htm. + // + // + [StructLayout(LayoutKind.Auto)] + [Serializable] + public partial struct DateTime : IComparable, IFormattable, IConvertible, IComparable<DateTime>, IEquatable<DateTime>, ISerializable + { + // Number of 100ns ticks per time unit + private const long TicksPerMillisecond = 10000; + private const long TicksPerSecond = TicksPerMillisecond * 1000; + private const long TicksPerMinute = TicksPerSecond * 60; + private const long TicksPerHour = TicksPerMinute * 60; + private const long TicksPerDay = TicksPerHour * 24; + + // Number of milliseconds per time unit + private const int MillisPerSecond = 1000; + private const int MillisPerMinute = MillisPerSecond * 60; + private const int MillisPerHour = MillisPerMinute * 60; + private const int MillisPerDay = MillisPerHour * 24; + + // Number of days in a non-leap year + private const int DaysPerYear = 365; + // Number of days in 4 years + private const int DaysPer4Years = DaysPerYear * 4 + 1; // 1461 + // Number of days in 100 years + private const int DaysPer100Years = DaysPer4Years * 25 - 1; // 36524 + // Number of days in 400 years + private const int DaysPer400Years = DaysPer100Years * 4 + 1; // 146097 + + // Number of days from 1/1/0001 to 12/31/1600 + private const int DaysTo1601 = DaysPer400Years * 4; // 584388 + // Number of days from 1/1/0001 to 12/30/1899 + private const int DaysTo1899 = DaysPer400Years * 4 + DaysPer100Years * 3 - 367; + // Number of days from 1/1/0001 to 12/31/1969 + internal const int DaysTo1970 = DaysPer400Years * 4 + DaysPer100Years * 3 + DaysPer4Years * 17 + DaysPerYear; // 719,162 + // Number of days from 1/1/0001 to 12/31/9999 + private const int DaysTo10000 = DaysPer400Years * 25 - 366; // 3652059 + + internal const long MinTicks = 0; + internal const long MaxTicks = DaysTo10000 * TicksPerDay - 1; + private const long MaxMillis = (long)DaysTo10000 * MillisPerDay; + + private const long TicksTo1970 = DaysTo1970 * TicksPerDay; + private const long FileTimeOffset = DaysTo1601 * TicksPerDay; + private const long DoubleDateOffset = DaysTo1899 * TicksPerDay; + // The minimum OA date is 0100/01/01 (Note it's year 100). + // The maximum OA date is 9999/12/31 + private const long OADateMinAsTicks = (DaysPer100Years - DaysPerYear) * TicksPerDay; + // All OA dates must be greater than (not >=) OADateMinAsDouble + private const double OADateMinAsDouble = -657435.0; + // All OA dates must be less than (not <=) OADateMaxAsDouble + private const double OADateMaxAsDouble = 2958466.0; + + private const int DatePartYear = 0; + private const int DatePartDayOfYear = 1; + private const int DatePartMonth = 2; + private const int DatePartDay = 3; + + private static readonly int[] s_daysToMonth365 = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; + private static readonly int[] s_daysToMonth366 = { + 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}; + + public static readonly DateTime MinValue = new DateTime(MinTicks, DateTimeKind.Unspecified); + public static readonly DateTime MaxValue = new DateTime(MaxTicks, DateTimeKind.Unspecified); + + private const UInt64 TicksMask = 0x3FFFFFFFFFFFFFFF; + private const UInt64 FlagsMask = 0xC000000000000000; + private const UInt64 LocalMask = 0x8000000000000000; + private const Int64 TicksCeiling = 0x4000000000000000; + private const UInt64 KindUnspecified = 0x0000000000000000; + private const UInt64 KindUtc = 0x4000000000000000; + private const UInt64 KindLocal = 0x8000000000000000; + private const UInt64 KindLocalAmbiguousDst = 0xC000000000000000; + private const Int32 KindShift = 62; + + private const String TicksField = "ticks"; + private const String DateDataField = "_dateData"; + + // The data is stored as an unsigned 64-bit integeter + // 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 + // value for the rare case where the date time is local, but is in an overlapped daylight + // savings time hour and it is in daylight savings time. This allows distinction of these + // otherwise ambiguous local times and prevents data loss when round tripping from Local to + // UTC time. + private UInt64 _dateData; + + // Constructs a DateTime from a tick count. The ticks + // argument specifies the date as the number of 100-nanosecond intervals + // that have elapsed since 1/1/0001 12:00am. + // + public DateTime(long ticks) + { + if (ticks < MinTicks || ticks > MaxTicks) + throw new ArgumentOutOfRangeException(nameof(ticks), SR.ArgumentOutOfRange_DateTimeBadTicks); + Contract.EndContractBlock(); + _dateData = (UInt64)ticks; + } + + private DateTime(UInt64 dateData) + { + this._dateData = dateData; + } + + public DateTime(long ticks, DateTimeKind kind) + { + if (ticks < MinTicks || ticks > MaxTicks) + { + throw new ArgumentOutOfRangeException(nameof(ticks), SR.ArgumentOutOfRange_DateTimeBadTicks); + } + if (kind < DateTimeKind.Unspecified || kind > DateTimeKind.Local) + { + throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind)); + } + Contract.EndContractBlock(); + _dateData = ((UInt64)ticks | ((UInt64)kind << KindShift)); + } + + internal DateTime(long ticks, DateTimeKind kind, Boolean isAmbiguousDst) + { + if (ticks < MinTicks || ticks > MaxTicks) + { + throw new ArgumentOutOfRangeException(nameof(ticks), SR.ArgumentOutOfRange_DateTimeBadTicks); + } + Debug.Assert(kind == DateTimeKind.Local, "Internal Constructor is for local times only"); + Contract.EndContractBlock(); + _dateData = ((UInt64)ticks | (isAmbiguousDst ? KindLocalAmbiguousDst : KindLocal)); + } + + // Constructs a DateTime from a given year, month, and day. The + // time-of-day of the resulting DateTime is always midnight. + // + public DateTime(int year, int month, int day) + { + _dateData = (UInt64)DateToTicks(year, month, day); + } + + // Constructs a DateTime from a given year, month, and day for + // the specified calendar. The + // time-of-day of the resulting DateTime is always midnight. + // + public DateTime(int year, int month, int day, Calendar calendar) + : this(year, month, day, 0, 0, 0, calendar) + { + } + + // Constructs a DateTime from a given year, month, day, hour, + // minute, and second. + // + public DateTime(int year, int month, int day, int hour, int minute, int second) + { + _dateData = (UInt64)(DateToTicks(year, month, day) + TimeToTicks(hour, minute, second)); + } + + public DateTime(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind) + { + if (kind < DateTimeKind.Unspecified || kind > DateTimeKind.Local) + { + throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind)); + } + Contract.EndContractBlock(); + Int64 ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); + _dateData = ((UInt64)ticks | ((UInt64)kind << KindShift)); + } + + // Constructs a DateTime from a given year, month, day, hour, + // minute, and second for the specified calendar. + // + public DateTime(int year, int month, int day, int hour, int minute, int second, Calendar calendar) + { + if (calendar == null) + throw new ArgumentNullException(nameof(calendar)); + Contract.EndContractBlock(); + _dateData = (UInt64)calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks; + } + + // Constructs a DateTime from a given year, month, day, hour, + // minute, and second. + // + public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond) + { + if (millisecond < 0 || millisecond >= MillisPerSecond) + { + throw new ArgumentOutOfRangeException(nameof(millisecond), SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1)); + } + Contract.EndContractBlock(); + Int64 ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); + ticks += millisecond * TicksPerMillisecond; + if (ticks < MinTicks || ticks > MaxTicks) + throw new ArgumentException(SR.Arg_DateTimeRange); + _dateData = (UInt64)ticks; + } + + public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind) + { + if (millisecond < 0 || millisecond >= MillisPerSecond) + { + throw new ArgumentOutOfRangeException(nameof(millisecond), SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1)); + } + if (kind < DateTimeKind.Unspecified || kind > DateTimeKind.Local) + { + throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind)); + } + Contract.EndContractBlock(); + Int64 ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); + ticks += millisecond * TicksPerMillisecond; + if (ticks < MinTicks || ticks > MaxTicks) + throw new ArgumentException(SR.Arg_DateTimeRange); + _dateData = ((UInt64)ticks | ((UInt64)kind << KindShift)); + } + + // Constructs a DateTime from a given year, month, day, hour, + // minute, and second for the specified calendar. + // + public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar) + { + if (calendar == null) + throw new ArgumentNullException(nameof(calendar)); + if (millisecond < 0 || millisecond >= MillisPerSecond) + { + throw new ArgumentOutOfRangeException(nameof(millisecond), SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1)); + } + Contract.EndContractBlock(); + Int64 ticks = calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks; + ticks += millisecond * TicksPerMillisecond; + if (ticks < MinTicks || ticks > MaxTicks) + throw new ArgumentException(SR.Arg_DateTimeRange); + _dateData = (UInt64)ticks; + } + + public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, DateTimeKind kind) + { + if (calendar == null) + throw new ArgumentNullException(nameof(calendar)); + if (millisecond < 0 || millisecond >= MillisPerSecond) + { + throw new ArgumentOutOfRangeException(nameof(millisecond), SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1)); + } + if (kind < DateTimeKind.Unspecified || kind > DateTimeKind.Local) + { + throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind)); + } + Contract.EndContractBlock(); + Int64 ticks = calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks; + ticks += millisecond * TicksPerMillisecond; + if (ticks < MinTicks || ticks > MaxTicks) + throw new ArgumentException(SR.Arg_DateTimeRange); + _dateData = ((UInt64)ticks | ((UInt64)kind << KindShift)); + } + + private DateTime(SerializationInfo info, StreamingContext context) + { + if (info == null) + throw new ArgumentNullException(nameof(info)); + Contract.EndContractBlock(); + + Boolean foundTicks = false; + Boolean foundDateData = false; + Int64 serializedTicks = 0; + UInt64 serializedDateData = 0; + + + // Get the data + SerializationInfoEnumerator enumerator = info.GetEnumerator(); + while (enumerator.MoveNext()) + { + switch (enumerator.Name) + { + case TicksField: + serializedTicks = Convert.ToInt64(enumerator.Value, CultureInfo.InvariantCulture); + foundTicks = true; + break; + case DateDataField: + serializedDateData = Convert.ToUInt64(enumerator.Value, CultureInfo.InvariantCulture); + foundDateData = true; + break; + default: + // Ignore other fields for forward compatibility. + break; + } + } + if (foundDateData) + { + _dateData = serializedDateData; + } + else if (foundTicks) + { + _dateData = (UInt64)serializedTicks; + } + else + { + throw new SerializationException(SR.Serialization_MissingDateTimeData); + } + Int64 ticks = InternalTicks; + if (ticks < MinTicks || ticks > MaxTicks) + { + throw new SerializationException(SR.Serialization_DateTimeTicksOutOfRange); + } + } + + + + internal Int64 InternalTicks + { + get + { + return (Int64)(_dateData & TicksMask); + } + } + + private UInt64 InternalKind + { + get + { + return (_dateData & FlagsMask); + } + } + + // Returns the DateTime resulting from adding the given + // TimeSpan to this DateTime. + // + public DateTime Add(TimeSpan value) + { + return AddTicks(value._ticks); + } + + // Returns the DateTime resulting from adding a fractional number of + // time units to this DateTime. + private DateTime Add(double value, int scale) + { + long millis = (long)(value * scale + (value >= 0 ? 0.5 : -0.5)); + if (millis <= -MaxMillis || millis >= MaxMillis) + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_AddValue); + return AddTicks(millis * TicksPerMillisecond); + } + + // Returns the DateTime resulting from adding a fractional number of + // days to this DateTime. The result is computed by rounding the + // fractional number of days given by value to the nearest + // millisecond, and adding that interval to this DateTime. The + // value argument is permitted to be negative. + // + public DateTime AddDays(double value) + { + return Add(value, MillisPerDay); + } + + // Returns the DateTime resulting from adding a fractional number of + // hours to this DateTime. The result is computed by rounding the + // fractional number of hours given by value to the nearest + // millisecond, and adding that interval to this DateTime. The + // value argument is permitted to be negative. + // + public DateTime AddHours(double value) + { + return Add(value, MillisPerHour); + } + + // Returns the DateTime resulting from the given number of + // milliseconds to this DateTime. The result is computed by rounding + // the number of milliseconds given by value to the nearest integer, + // and adding that interval to this DateTime. The value + // argument is permitted to be negative. + // + public DateTime AddMilliseconds(double value) + { + return Add(value, 1); + } + + // Returns the DateTime resulting from adding a fractional number of + // minutes to this DateTime. The result is computed by rounding the + // fractional number of minutes given by value to the nearest + // millisecond, and adding that interval to this DateTime. The + // value argument is permitted to be negative. + // + public DateTime AddMinutes(double value) + { + return Add(value, MillisPerMinute); + } + + // Returns the DateTime resulting from adding the given number of + // months to this DateTime. The result is computed by incrementing + // (or decrementing) the year and month parts of this DateTime by + // months months, and, if required, adjusting the day part of the + // resulting date downwards to the last day of the resulting month in the + // resulting year. The time-of-day part of the result is the same as the + // time-of-day part of this DateTime. + // + // In more precise terms, considering this DateTime to be of the + // form y / m / d + t, where y is the + // year, m is the month, d is the day, and t is the + // time-of-day, the result is y1 / m1 / d1 + t, + // where y1 and m1 are computed by adding months months + // to y and m, and d1 is the largest value less than + // or equal to d that denotes a valid day in month m1 of year + // y1. + // + public DateTime AddMonths(int months) + { + if (months < -120000 || months > 120000) throw new ArgumentOutOfRangeException(nameof(months), SR.ArgumentOutOfRange_DateTimeBadMonths); + Contract.EndContractBlock(); + int y = GetDatePart(DatePartYear); + int m = GetDatePart(DatePartMonth); + int d = GetDatePart(DatePartDay); + int i = m - 1 + months; + if (i >= 0) + { + m = i % 12 + 1; + y = y + i / 12; + } + else + { + m = 12 + (i + 1) % 12; + y = y + (i - 11) / 12; + } + if (y < 1 || y > 9999) + { + throw new ArgumentOutOfRangeException(nameof(months), SR.ArgumentOutOfRange_DateArithmetic); + } + int days = DaysInMonth(y, m); + if (d > days) d = days; + return new DateTime((UInt64)(DateToTicks(y, m, d) + InternalTicks % TicksPerDay) | InternalKind); + } + + // Returns the DateTime resulting from adding a fractional number of + // seconds to this DateTime. The result is computed by rounding the + // fractional number of seconds given by value to the nearest + // millisecond, and adding that interval to this DateTime. The + // value argument is permitted to be negative. + // + public DateTime AddSeconds(double value) + { + return Add(value, MillisPerSecond); + } + + // Returns the DateTime resulting from adding the given number of + // 100-nanosecond ticks to this DateTime. The value argument + // is permitted to be negative. + // + public DateTime AddTicks(long value) + { + long ticks = InternalTicks; + if (value > MaxTicks - ticks || value < MinTicks - ticks) + { + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_DateArithmetic); + } + return new DateTime((UInt64)(ticks + value) | InternalKind); + } + + // Returns the DateTime resulting from adding the given number of + // years to this DateTime. The result is computed by incrementing + // (or decrementing) the year part of this DateTime by value + // years. If the month and day of this DateTime is 2/29, and if the + // resulting year is not a leap year, the month and day of the resulting + // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day + // parts of the result are the same as those of this DateTime. + // + public DateTime AddYears(int value) + { + if (value < -10000 || value > 10000) + { + // DateTimeOffset.AddYears(int years) is implemented on top of DateTime.AddYears(int value). Use the more appropriate + // parameter name out of the two for the exception. + throw new ArgumentOutOfRangeException("years", SR.ArgumentOutOfRange_DateTimeBadYears); + } + Contract.EndContractBlock(); + return AddMonths(value * 12); + } + + // Compares two DateTime values, returning an integer that indicates + // their relationship. + // + public static int Compare(DateTime t1, DateTime t2) + { + Int64 ticks1 = t1.InternalTicks; + Int64 ticks2 = t2.InternalTicks; + if (ticks1 > ticks2) return 1; + if (ticks1 < ticks2) return -1; + return 0; + } + + // Compares this DateTime to a given object. This method provides an + // implementation of the IComparable interface. The object + // argument must be another DateTime, or otherwise an exception + // occurs. Null is considered less than any instance. + // + // Returns a value less than zero if this object + public int CompareTo(Object value) + { + if (value == null) return 1; + if (!(value is DateTime)) + { + throw new ArgumentException(SR.Arg_MustBeDateTime); + } + + return Compare(this, (DateTime)value); + } + + public int CompareTo(DateTime value) + { + return Compare(this, value); + } + + // Returns the tick count corresponding to the given year, month, and day. + // Will check the if the parameters are valid. + private static long DateToTicks(int year, int month, int day) + { + if (year >= 1 && year <= 9999 && month >= 1 && month <= 12) + { + int[] days = IsLeapYear(year) ? s_daysToMonth366 : s_daysToMonth365; + if (day >= 1 && day <= days[month] - days[month - 1]) + { + int y = year - 1; + int n = y * 365 + y / 4 - y / 100 + y / 400 + days[month - 1] + day - 1; + return n * TicksPerDay; + } + } + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay); + } + + // Return the tick count corresponding to the given hour, minute, second. + // Will check the if the parameters are valid. + private static long TimeToTicks(int hour, int minute, int second) + { + //TimeSpan.TimeToTicks is a family access function which does no error checking, so + //we need to put some error checking out here. + if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60) + { + return (TimeSpan.TimeToTicks(hour, minute, second)); + } + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond); + } + + // Returns the number of days in the month given by the year and + // month arguments. + // + public static int DaysInMonth(int year, int month) + { + if (month < 1 || month > 12) throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month); + Contract.EndContractBlock(); + // IsLeapYear checks the year argument + int[] days = IsLeapYear(year) ? s_daysToMonth366 : s_daysToMonth365; + return days[month] - days[month - 1]; + } + + // Converts an OLE Date to a tick count. + // This function is duplicated in COMDateTime.cpp + internal static long DoubleDateToTicks(double value) + { + // The check done this way will take care of NaN + if (!(value < OADateMaxAsDouble) || !(value > OADateMinAsDouble)) + throw new ArgumentException(SR.Arg_OleAutDateInvalid); + + // Conversion to long will not cause an overflow here, as at this point the "value" is in between OADateMinAsDouble and OADateMaxAsDouble + long millis = (long)(value * MillisPerDay + (value >= 0 ? 0.5 : -0.5)); + // The interesting thing here is when you have a value like 12.5 it all positive 12 days and 12 hours from 01/01/1899 + // However if you a value of -12.25 it is minus 12 days but still positive 6 hours, almost as though you meant -11.75 all negative + // This line below fixes up the millis in the negative case + if (millis < 0) + { + millis -= (millis % MillisPerDay) * 2; + } + + millis += DoubleDateOffset / TicksPerMillisecond; + + if (millis < 0 || millis >= MaxMillis) throw new ArgumentException(SR.Arg_OleAutDateScale); + return millis * TicksPerMillisecond; + } + + // Checks if this DateTime is equal to a given object. Returns + // true if the given object is a boxed DateTime and its value + // is equal to the value of this DateTime. Returns false + // otherwise. + // + public override bool Equals(Object value) + { + if (value is DateTime) + { + return InternalTicks == ((DateTime)value).InternalTicks; + } + return false; + } + + public bool Equals(DateTime value) + { + return InternalTicks == value.InternalTicks; + } + + // Compares two DateTime values for equality. Returns true if + // the two DateTime values are equal, or false if they are + // not equal. + // + public static bool Equals(DateTime t1, DateTime t2) + { + return t1.InternalTicks == t2.InternalTicks; + } + + public static DateTime FromBinary(Int64 dateData) + { + if ((dateData & (unchecked((Int64)LocalMask))) != 0) + { + // Local times need to be adjusted as you move from one time zone to another, + // just as they are when serializing in text. As such the format for local times + // changes to store the ticks of the UTC time, but with flags that look like a + // local date. + Int64 ticks = dateData & (unchecked((Int64)TicksMask)); + // Negative ticks are stored in the top part of the range and should be converted back into a negative number + if (ticks > TicksCeiling - TicksPerDay) + { + ticks = ticks - TicksCeiling; + } + // Convert the ticks back to local. If the UTC ticks are out of range, we need to default to + // the UTC offset from MinValue and MaxValue to be consistent with Parse. + Boolean isAmbiguousLocalDst = false; + Int64 offsetTicks; + if (ticks < MinTicks) + { + offsetTicks = TimeZoneInfo.GetLocalUtcOffset(DateTime.MinValue, TimeZoneInfoOptions.NoThrowOnInvalidTime).Ticks; + } + else if (ticks > MaxTicks) + { + offsetTicks = TimeZoneInfo.GetLocalUtcOffset(DateTime.MaxValue, TimeZoneInfoOptions.NoThrowOnInvalidTime).Ticks; + } + else + { + // Because the ticks conversion between UTC and local is lossy, we need to capture whether the + // time is in a repeated hour so that it can be passed to the DateTime constructor. + DateTime utcDt = new DateTime(ticks, DateTimeKind.Utc); + Boolean isDaylightSavings = false; + offsetTicks = TimeZoneInfo.GetUtcOffsetFromUtc(utcDt, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst).Ticks; + } + ticks += offsetTicks; + // Another behaviour of parsing is to cause small times to wrap around, so that they can be used + // to compare times of day + if (ticks < 0) + { + ticks += TicksPerDay; + } + if (ticks < MinTicks || ticks > MaxTicks) + { + throw new ArgumentException(SR.Argument_DateTimeBadBinaryData, nameof(dateData)); + } + return new DateTime(ticks, DateTimeKind.Local, isAmbiguousLocalDst); + } + else + { + return DateTime.FromBinaryRaw(dateData); + } + } + + // A version of ToBinary that uses the real representation and does not adjust local times. This is needed for + // scenarios where the serialized data must maintain compatibility + internal static DateTime FromBinaryRaw(Int64 dateData) + { + Int64 ticks = dateData & (Int64)TicksMask; + if (ticks < MinTicks || ticks > MaxTicks) + throw new ArgumentException(SR.Argument_DateTimeBadBinaryData, nameof(dateData)); + return new DateTime((UInt64)dateData); + } + + // Creates a DateTime from a Windows filetime. A Windows filetime is + // a long representing the date and time as the number of + // 100-nanosecond intervals that have elapsed since 1/1/1601 12:00am. + // + public static DateTime FromFileTime(long fileTime) + { + return FromFileTimeUtc(fileTime).ToLocalTime(); + } + + public static DateTime FromFileTimeUtc(long fileTime) + { + if (fileTime < 0 || fileTime > MaxTicks - FileTimeOffset) + { + throw new ArgumentOutOfRangeException(nameof(fileTime), SR.ArgumentOutOfRange_FileTimeInvalid); + } + Contract.EndContractBlock(); + + // This is the ticks in Universal time for this fileTime. + long universalTicks = fileTime + FileTimeOffset; + return new DateTime(universalTicks, DateTimeKind.Utc); + } + + // Creates a DateTime from an OLE Automation Date. + // + public static DateTime FromOADate(double d) + { + return new DateTime(DoubleDateToTicks(d), DateTimeKind.Unspecified); + } + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + Contract.EndContractBlock(); + + // Serialize both the old and the new format + info.AddValue(TicksField, InternalTicks); + info.AddValue(DateDataField, _dateData); + } + + public Boolean IsDaylightSavingTime() + { + if (Kind == DateTimeKind.Utc) + { + return false; + } + return TimeZoneInfo.Local.IsDaylightSavingTime(this, TimeZoneInfoOptions.NoThrowOnInvalidTime); + } + + public static DateTime SpecifyKind(DateTime value, DateTimeKind kind) + { + return new DateTime(value.InternalTicks, kind); + } + + public Int64 ToBinary() + { + if (Kind == DateTimeKind.Local) + { + // Local times need to be adjusted as you move from one time zone to another, + // just as they are when serializing in text. As such the format for local times + // changes to store the ticks of the UTC time, but with flags that look like a + // local date. + + // To match serialization in text we need to be able to handle cases where + // the UTC value would be out of range. Unused parts of the ticks range are + // used for this, so that values just past max value are stored just past the + // end of the maximum range, and values just below minimum value are stored + // at the end of the ticks area, just below 2^62. + TimeSpan offset = TimeZoneInfo.GetLocalUtcOffset(this, TimeZoneInfoOptions.NoThrowOnInvalidTime); + Int64 ticks = Ticks; + Int64 storedTicks = ticks - offset.Ticks; + if (storedTicks < 0) + { + storedTicks = TicksCeiling + storedTicks; + } + return storedTicks | (unchecked((Int64)LocalMask)); + } + else + { + return (Int64)_dateData; + } + } + + // Returns the date part of this DateTime. The resulting value + // corresponds to this DateTime with the time-of-day part set to + // zero (midnight). + // + public DateTime Date + { + get + { + Int64 ticks = InternalTicks; + return new DateTime((UInt64)(ticks - ticks % TicksPerDay) | InternalKind); + } + } + + // Returns a given date part of this DateTime. This method is used + // to compute the year, day-of-year, month, or day part. + private int GetDatePart(int part) + { + Int64 ticks = InternalTicks; + // n = number of days since 1/1/0001 + int n = (int)(ticks / TicksPerDay); + // y400 = number of whole 400-year periods since 1/1/0001 + int y400 = n / DaysPer400Years; + // n = day number within 400-year period + n -= y400 * DaysPer400Years; + // y100 = number of whole 100-year periods within 400-year period + int y100 = n / DaysPer100Years; + // Last 100-year period has an extra day, so decrement result if 4 + if (y100 == 4) y100 = 3; + // n = day number within 100-year period + n -= y100 * DaysPer100Years; + // y4 = number of whole 4-year periods within 100-year period + int y4 = n / DaysPer4Years; + // n = day number within 4-year period + n -= y4 * DaysPer4Years; + // y1 = number of whole years within 4-year period + int y1 = n / DaysPerYear; + // Last year has an extra day, so decrement result if 4 + if (y1 == 4) y1 = 3; + // If year was requested, compute and return it + if (part == DatePartYear) + { + return y400 * 400 + y100 * 100 + y4 * 4 + y1 + 1; + } + // n = day number within year + n -= y1 * DaysPerYear; + // If day-of-year was requested, return it + if (part == DatePartDayOfYear) return n + 1; + // Leap year calculation looks different from IsLeapYear since y1, y4, + // and y100 are relative to year 1, not year 0 + bool leapYear = y1 == 3 && (y4 != 24 || y100 == 3); + int[] days = leapYear ? s_daysToMonth366 : s_daysToMonth365; + // All months have less than 32 days, so n >> 5 is a good conservative + // estimate for the month + int m = (n >> 5) + 1; + // m = 1-based month number + while (n >= days[m]) m++; + // If month was requested, return it + if (part == DatePartMonth) return m; + // Return 1-based day-of-month + return n - days[m - 1] + 1; + } + + // Returns the day-of-month part of this DateTime. The returned + // value is an integer between 1 and 31. + // + public int Day + { + get + { + Contract.Ensures(Contract.Result<int>() >= 1); + Contract.Ensures(Contract.Result<int>() <= 31); + return GetDatePart(DatePartDay); + } + } + + // Returns the day-of-week part of this DateTime. The returned value + // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates + // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates + // Thursday, 5 indicates Friday, and 6 indicates Saturday. + // + public DayOfWeek DayOfWeek + { + get + { + Contract.Ensures(Contract.Result<DayOfWeek>() >= DayOfWeek.Sunday); + Contract.Ensures(Contract.Result<DayOfWeek>() <= DayOfWeek.Saturday); + return (DayOfWeek)((InternalTicks / TicksPerDay + 1) % 7); + } + } + + // Returns the day-of-year part of this DateTime. The returned value + // is an integer between 1 and 366. + // + public int DayOfYear + { + get + { + Contract.Ensures(Contract.Result<int>() >= 1); + Contract.Ensures(Contract.Result<int>() <= 366); // leap year + return GetDatePart(DatePartDayOfYear); + } + } + + // Returns the hash code for this DateTime. + // + public override int GetHashCode() + { + Int64 ticks = InternalTicks; + return unchecked((int)ticks) ^ (int)(ticks >> 32); + } + + // Returns the hour part of this DateTime. The returned value is an + // integer between 0 and 23. + // + public int Hour + { + get + { + Contract.Ensures(Contract.Result<int>() >= 0); + Contract.Ensures(Contract.Result<int>() < 24); + return (int)((InternalTicks / TicksPerHour) % 24); + } + } + + internal Boolean IsAmbiguousDaylightSavingTime() + { + return (InternalKind == KindLocalAmbiguousDst); + } + + [Pure] + public DateTimeKind Kind + { + get + { + switch (InternalKind) + { + case KindUnspecified: + return DateTimeKind.Unspecified; + case KindUtc: + return DateTimeKind.Utc; + default: + return DateTimeKind.Local; + } + } + } + + // Returns the millisecond part of this DateTime. The returned value + // is an integer between 0 and 999. + // + public int Millisecond + { + get + { + Contract.Ensures(Contract.Result<int>() >= 0); + Contract.Ensures(Contract.Result<int>() < 1000); + return (int)((InternalTicks / TicksPerMillisecond) % 1000); + } + } + + // Returns the minute part of this DateTime. The returned value is + // an integer between 0 and 59. + // + public int Minute + { + get + { + Contract.Ensures(Contract.Result<int>() >= 0); + Contract.Ensures(Contract.Result<int>() < 60); + return (int)((InternalTicks / TicksPerMinute) % 60); + } + } + + // Returns the month part of this DateTime. The returned value is an + // integer between 1 and 12. + // + public int Month + { + get + { + Contract.Ensures(Contract.Result<int>() >= 1); + return GetDatePart(DatePartMonth); + } + } + + // 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. + // + public static DateTime Now + { + get + { + Contract.Ensures(Contract.Result<DateTime>().Kind == DateTimeKind.Local); + + DateTime utc = UtcNow; + Boolean isAmbiguousLocalDst = false; + Int64 offset = TimeZoneInfo.GetDateTimeNowUtcOffsetFromUtc(utc, out isAmbiguousLocalDst).Ticks; + long tick = utc.Ticks + offset; + if (tick > DateTime.MaxTicks) + { + return new DateTime(DateTime.MaxTicks, DateTimeKind.Local); + } + if (tick < DateTime.MinTicks) + { + return new DateTime(DateTime.MinTicks, DateTimeKind.Local); + } + return new DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst); + } + } + + // Returns the second part of this DateTime. The returned value is + // an integer between 0 and 59. + // + public int Second + { + get + { + Contract.Ensures(Contract.Result<int>() >= 0); + Contract.Ensures(Contract.Result<int>() < 60); + return (int)((InternalTicks / TicksPerSecond) % 60); + } + } + + // Returns the tick count for this DateTime. The returned value is + // the number of 100-nanosecond intervals that have elapsed since 1/1/0001 + // 12:00am. + // + public long Ticks + { + get + { + return InternalTicks; + } + } + + // Returns the time-of-day part of this DateTime. The returned value + // is a TimeSpan that indicates the time elapsed since midnight. + // + public TimeSpan TimeOfDay + { + get + { + return new TimeSpan(InternalTicks % TicksPerDay); + } + } + + // Returns a DateTime representing the current date. The date part + // of the returned value is the current date, and the time-of-day part of + // the returned value is zero (midnight). + // + public static DateTime Today + { + get + { + return DateTime.Now.Date; + } + } + + // Returns the year part of this DateTime. The returned value is an + // integer between 1 and 9999. + // + public int Year + { + get + { + Contract.Ensures(Contract.Result<int>() >= 1 && Contract.Result<int>() <= 9999); + return GetDatePart(DatePartYear); + } + } + + // Checks whether a given year is a leap year. This method returns true if + // year is a leap year, or false if not. + // + public static bool IsLeapYear(int year) + { + if (year < 1 || year > 9999) + { + throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_Year); + } + Contract.EndContractBlock(); + return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); + } + + // Constructs a DateTime from a string. The string must specify a + // date and optionally a time in a culture-specific or universal format. + // Leading and trailing whitespace characters are allowed. + // + public static DateTime Parse(String s) + { + return (DateTimeParse.Parse(s, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None)); + } + + // Constructs a DateTime from a string. The string must specify a + // date and optionally a time in a culture-specific or universal format. + // Leading and trailing whitespace characters are allowed. + // + public static DateTime Parse(String s, IFormatProvider provider) + { + return (DateTimeParse.Parse(s, DateTimeFormatInfo.GetInstance(provider), DateTimeStyles.None)); + } + + public static DateTime Parse(String s, IFormatProvider provider, DateTimeStyles styles) + { + DateTimeFormatInfo.ValidateStyles(styles, nameof(styles)); + return (DateTimeParse.Parse(s, DateTimeFormatInfo.GetInstance(provider), styles)); + } + + // Constructs a DateTime from a string. The string must specify a + // date and optionally a time in a culture-specific or universal format. + // Leading and trailing whitespace characters are allowed. + // + public static DateTime ParseExact(String s, String format, IFormatProvider provider) + { + return (DateTimeParse.ParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), DateTimeStyles.None)); + } + + // Constructs a DateTime from a string. The string must specify a + // date and optionally a time in a culture-specific or universal format. + // Leading and trailing whitespace characters are allowed. + // + public static DateTime ParseExact(String s, String format, IFormatProvider provider, DateTimeStyles style) + { + DateTimeFormatInfo.ValidateStyles(style, nameof(style)); + return (DateTimeParse.ParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style)); + } + + public static DateTime ParseExact(String s, String[] formats, IFormatProvider provider, DateTimeStyles style) + { + DateTimeFormatInfo.ValidateStyles(style, nameof(style)); + return DateTimeParse.ParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style); + } + + public TimeSpan Subtract(DateTime value) + { + return new TimeSpan(InternalTicks - value.InternalTicks); + } + + public DateTime Subtract(TimeSpan value) + { + long ticks = InternalTicks; + long valueTicks = value._ticks; + if (ticks - MinTicks < valueTicks || ticks - MaxTicks > valueTicks) + { + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_DateArithmetic); + } + return new DateTime((UInt64)(ticks - valueTicks) | InternalKind); + } + + // This function is duplicated in COMDateTime.cpp + private static double TicksToOADate(long value) + { + if (value == 0) + return 0.0; // Returns OleAut's zero'ed date value. + if (value < TicksPerDay) // This is a fix for VB. They want the default day to be 1/1/0001 rathar then 12/30/1899. + value += DoubleDateOffset; // We could have moved this fix down but we would like to keep the bounds check. + if (value < OADateMinAsTicks) + throw new OverflowException(SR.Arg_OleAutDateInvalid); + // Currently, our max date == OA's max date (12/31/9999), so we don't + // need an overflow check in that direction. + long millis = (value - DoubleDateOffset) / TicksPerMillisecond; + if (millis < 0) + { + long frac = millis % MillisPerDay; + if (frac != 0) millis -= (MillisPerDay + frac) * 2; + } + return (double)millis / MillisPerDay; + } + + // Converts the DateTime instance into an OLE Automation compatible + // double date. + public double ToOADate() + { + return TicksToOADate(InternalTicks); + } + + public long ToFileTime() + { + // Treats the input as local if it is not specified + return ToUniversalTime().ToFileTimeUtc(); + } + + public long ToFileTimeUtc() + { + // Treats the input as universal if it is not specified + long ticks = ((InternalKind & LocalMask) != 0) ? ToUniversalTime().InternalTicks : this.InternalTicks; + ticks -= FileTimeOffset; + if (ticks < 0) + { + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_FileTimeInvalid); + } + return ticks; + } + + public DateTime ToLocalTime() + { + return ToLocalTime(false); + } + + internal DateTime ToLocalTime(bool throwOnOverflow) + { + if (Kind == DateTimeKind.Local) + { + return this; + } + Boolean isDaylightSavings = false; + Boolean isAmbiguousLocalDst = false; + Int64 offset = TimeZoneInfo.GetUtcOffsetFromUtc(this, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst).Ticks; + long tick = Ticks + offset; + if (tick > DateTime.MaxTicks) + { + if (throwOnOverflow) + throw new ArgumentException(SR.Arg_ArgumentOutOfRangeException); + else + return new DateTime(DateTime.MaxTicks, DateTimeKind.Local); + } + if (tick < DateTime.MinTicks) + { + if (throwOnOverflow) + throw new ArgumentException(SR.Arg_ArgumentOutOfRangeException); + else + return new DateTime(DateTime.MinTicks, DateTimeKind.Local); + } + return new DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst); + } + + public String ToLongDateString() + { + Contract.Ensures(Contract.Result<String>() != null); + return DateTimeFormat.Format(this, "D", DateTimeFormatInfo.CurrentInfo); + } + + public String ToLongTimeString() + { + Contract.Ensures(Contract.Result<String>() != null); + return DateTimeFormat.Format(this, "T", DateTimeFormatInfo.CurrentInfo); + } + + public String ToShortDateString() + { + Contract.Ensures(Contract.Result<String>() != null); + return DateTimeFormat.Format(this, "d", DateTimeFormatInfo.CurrentInfo); + } + + public String ToShortTimeString() + { + Contract.Ensures(Contract.Result<String>() != null); + return DateTimeFormat.Format(this, "t", DateTimeFormatInfo.CurrentInfo); + } + + public override String ToString() + { + Contract.Ensures(Contract.Result<String>() != null); + return DateTimeFormat.Format(this, null, DateTimeFormatInfo.CurrentInfo); + } + + public String ToString(String format) + { + Contract.Ensures(Contract.Result<String>() != null); + return DateTimeFormat.Format(this, format, DateTimeFormatInfo.CurrentInfo); + } + + public String ToString(IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return DateTimeFormat.Format(this, null, DateTimeFormatInfo.GetInstance(provider)); + } + + public String ToString(String format, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String>() != null); + return DateTimeFormat.Format(this, format, DateTimeFormatInfo.GetInstance(provider)); + } + + public DateTime ToUniversalTime() + { + return TimeZoneInfo.ConvertTimeToUtc(this, TimeZoneInfoOptions.NoThrowOnInvalidTime); + } + + public static Boolean TryParse(String s, out DateTime result) + { + return DateTimeParse.TryParse(s, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, out result); + } + + public static Boolean TryParse(String s, IFormatProvider provider, DateTimeStyles styles, out DateTime result) + { + DateTimeFormatInfo.ValidateStyles(styles, nameof(styles)); + return DateTimeParse.TryParse(s, DateTimeFormatInfo.GetInstance(provider), styles, out result); + } + + public static Boolean TryParseExact(String s, String format, IFormatProvider provider, DateTimeStyles style, out DateTime result) + { + DateTimeFormatInfo.ValidateStyles(style, nameof(style)); + return DateTimeParse.TryParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style, out result); + } + + public static Boolean TryParseExact(String s, String[] formats, IFormatProvider provider, DateTimeStyles style, out DateTime result) + { + DateTimeFormatInfo.ValidateStyles(style, nameof(style)); + return DateTimeParse.TryParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style, out result); + } + + public static DateTime operator +(DateTime d, TimeSpan t) + { + long ticks = d.InternalTicks; + long valueTicks = t._ticks; + if (valueTicks > MaxTicks - ticks || valueTicks < MinTicks - ticks) + { + throw new ArgumentOutOfRangeException(nameof(t), SR.ArgumentOutOfRange_DateArithmetic); + } + return new DateTime((UInt64)(ticks + valueTicks) | d.InternalKind); + } + + public static DateTime operator -(DateTime d, TimeSpan t) + { + long ticks = d.InternalTicks; + long valueTicks = t._ticks; + if (ticks - MinTicks < valueTicks || ticks - MaxTicks > valueTicks) + { + throw new ArgumentOutOfRangeException(nameof(t), SR.ArgumentOutOfRange_DateArithmetic); + } + return new DateTime((UInt64)(ticks - valueTicks) | d.InternalKind); + } + + public static TimeSpan operator -(DateTime d1, DateTime d2) + { + return new TimeSpan(d1.InternalTicks - d2.InternalTicks); + } + + public static bool operator ==(DateTime d1, DateTime d2) + { + return d1.InternalTicks == d2.InternalTicks; + } + + public static bool operator !=(DateTime d1, DateTime d2) + { + return d1.InternalTicks != d2.InternalTicks; + } + + public static bool operator <(DateTime t1, DateTime t2) + { + return t1.InternalTicks < t2.InternalTicks; + } + + public static bool operator <=(DateTime t1, DateTime t2) + { + return t1.InternalTicks <= t2.InternalTicks; + } + + public static bool operator >(DateTime t1, DateTime t2) + { + return t1.InternalTicks > t2.InternalTicks; + } + + public static bool operator >=(DateTime t1, DateTime t2) + { + return t1.InternalTicks >= t2.InternalTicks; + } + + + // Returns a string array containing all of the known date and time options for the + // current culture. The strings returned are properly formatted date and + // time strings for the current instance of DateTime. + public String[] GetDateTimeFormats() + { + Contract.Ensures(Contract.Result<String[]>() != null); + return (GetDateTimeFormats(CultureInfo.CurrentCulture)); + } + + // Returns a string array containing all of the known date and time options for the + // using the information provided by IFormatProvider. The strings returned are properly formatted date and + // time strings for the current instance of DateTime. + public String[] GetDateTimeFormats(IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String[]>() != null); + return (DateTimeFormat.GetAllDateTimes(this, DateTimeFormatInfo.GetInstance(provider))); + } + + + // Returns a string array containing all of the date and time options for the + // given format format and current culture. The strings returned are properly formatted date and + // time strings for the current instance of DateTime. + public String[] GetDateTimeFormats(char format) + { + Contract.Ensures(Contract.Result<String[]>() != null); + return (GetDateTimeFormats(format, CultureInfo.CurrentCulture)); + } + + // Returns a string array containing all of the date and time options for the + // given format format and given culture. The strings returned are properly formatted date and + // time strings for the current instance of DateTime. + public String[] GetDateTimeFormats(char format, IFormatProvider provider) + { + Contract.Ensures(Contract.Result<String[]>() != null); + return (DateTimeFormat.GetAllDateTimes(this, format, DateTimeFormatInfo.GetInstance(provider))); + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.DateTime; + } + + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Boolean")); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Char")); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "SByte")); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Byte")); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Int16")); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "UInt16")); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Int32")); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "UInt32")); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Int64")); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "UInt64")); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Single")); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Double")); + } + + Decimal IConvertible.ToDecimal(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Decimal")); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + return this; + } + + Object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + + // Tries to construct a DateTime from a given year, month, day, hour, + // minute, second and millisecond. + // + internal static Boolean TryCreate(int year, int month, int day, int hour, int minute, int second, int millisecond, out DateTime result) + { + result = DateTime.MinValue; + if (year < 1 || year > 9999 || month < 1 || month > 12) + { + return false; + } + int[] days = IsLeapYear(year) ? s_daysToMonth366 : s_daysToMonth365; + if (day < 1 || day > days[month] - days[month - 1]) + { + return false; + } + if (hour < 0 || hour >= 24 || minute < 0 || minute >= 60 || second < 0 || second >= 60) + { + return false; + } + if (millisecond < 0 || millisecond >= MillisPerSecond) + { + return false; + } + long ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); + + ticks += millisecond * TicksPerMillisecond; + if (ticks < MinTicks || ticks > MaxTicks) + { + return false; + } + result = new DateTime(ticks, DateTimeKind.Unspecified); + return true; + } + } +} diff --git a/src/mscorlib/shared/System/DateTimeKind.cs b/src/mscorlib/shared/System/DateTimeKind.cs new file mode 100644 index 0000000000..6b5e690df0 --- /dev/null +++ b/src/mscorlib/shared/System/DateTimeKind.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 +{ + // This enum is used to indentify DateTime instances in cases when they are known to be in local time, + // UTC time or if this information has not been specified or is not applicable. + + [Serializable] + public enum DateTimeKind + { + Unspecified = 0, + Utc = 1, + Local = 2, + } +} diff --git a/src/mscorlib/shared/System/DateTimeOffset.cs b/src/mscorlib/shared/System/DateTimeOffset.cs new file mode 100644 index 0000000000..d5ccbd9195 --- /dev/null +++ b/src/mscorlib/shared/System/DateTimeOffset.cs @@ -0,0 +1,921 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace System +{ + // DateTimeOffset is a value type that consists of a DateTime and a time zone offset, + // ie. how far away the time is from GMT. The DateTime is stored whole, and the offset + // is stored as an Int16 internally to save space, but presented as a TimeSpan. + // + // The range is constrained so that both the represented clock time and the represented + // UTC time fit within the boundaries of MaxValue. This gives it the same range as DateTime + // for actual UTC times, and a slightly constrained range on one end when an offset is + // present. + // + // This class should be substitutable for date time in most cases; so most operations + // effectively work on the clock time. However, the underlying UTC time is what counts + // for the purposes of identity, sorting and subtracting two instances. + // + // + // There are theoretically two date times stored, the UTC and the relative local representation + // or the 'clock' time. It actually does not matter which is stored in m_dateTime, so it is desirable + // for most methods to go through the helpers UtcDateTime and ClockDateTime both to abstract this + // out and for internal readability. + + [StructLayout(LayoutKind.Auto)] + [Serializable] + public struct DateTimeOffset : IComparable, IFormattable, IComparable<DateTimeOffset>, IEquatable<DateTimeOffset>, ISerializable, IDeserializationCallback + { + // Constants + internal const Int64 MaxOffset = TimeSpan.TicksPerHour * 14; + internal const Int64 MinOffset = -MaxOffset; + + private const long UnixEpochTicks = TimeSpan.TicksPerDay * DateTime.DaysTo1970; // 621,355,968,000,000,000 + private const long UnixEpochSeconds = UnixEpochTicks / TimeSpan.TicksPerSecond; // 62,135,596,800 + private const long UnixEpochMilliseconds = UnixEpochTicks / TimeSpan.TicksPerMillisecond; // 62,135,596,800,000 + + internal const long UnixMinSeconds = DateTime.MinTicks / TimeSpan.TicksPerSecond - UnixEpochSeconds; + internal const long UnixMaxSeconds = DateTime.MaxTicks / TimeSpan.TicksPerSecond - UnixEpochSeconds; + + // Static Fields + public static readonly DateTimeOffset MinValue = new DateTimeOffset(DateTime.MinTicks, TimeSpan.Zero); + public static readonly DateTimeOffset MaxValue = new DateTimeOffset(DateTime.MaxTicks, TimeSpan.Zero); + + // Instance Fields + private DateTime _dateTime; + private Int16 _offsetMinutes; + + // Constructors + + // Constructs a DateTimeOffset from a tick count and offset + public DateTimeOffset(long ticks, TimeSpan offset) + { + _offsetMinutes = ValidateOffset(offset); + // Let the DateTime constructor do the range checks + DateTime dateTime = new DateTime(ticks); + _dateTime = ValidateDate(dateTime, offset); + } + + // Constructs a DateTimeOffset from a DateTime. For Local and Unspecified kinds, + // extracts the local offset. For UTC, creates a UTC instance with a zero offset. + public DateTimeOffset(DateTime dateTime) + { + TimeSpan offset; + if (dateTime.Kind != DateTimeKind.Utc) + { + // Local and Unspecified are both treated as Local + offset = TimeZoneInfo.GetLocalUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime); + } + else + { + offset = new TimeSpan(0); + } + _offsetMinutes = ValidateOffset(offset); + _dateTime = ValidateDate(dateTime, offset); + } + + // Constructs a DateTimeOffset from a DateTime. And an offset. Always makes the clock time + // consistent with the DateTime. For Utc ensures the offset is zero. For local, ensures that + // the offset corresponds to the local. + public DateTimeOffset(DateTime dateTime, TimeSpan offset) + { + if (dateTime.Kind == DateTimeKind.Local) + { + if (offset != TimeZoneInfo.GetLocalUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime)) + { + throw new ArgumentException(SR.Argument_OffsetLocalMismatch, nameof(offset)); + } + } + else if (dateTime.Kind == DateTimeKind.Utc) + { + if (offset != TimeSpan.Zero) + { + throw new ArgumentException(SR.Argument_OffsetUtcMismatch, nameof(offset)); + } + } + _offsetMinutes = ValidateOffset(offset); + _dateTime = ValidateDate(dateTime, offset); + } + + // Constructs a DateTimeOffset from a given year, month, day, hour, + // minute, second and offset. + public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, TimeSpan offset) + { + _offsetMinutes = ValidateOffset(offset); + _dateTime = ValidateDate(new DateTime(year, month, day, hour, minute, second), offset); + } + + // Constructs a DateTimeOffset from a given year, month, day, hour, + // minute, second, millsecond and offset + public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, TimeSpan offset) + { + _offsetMinutes = ValidateOffset(offset); + _dateTime = ValidateDate(new DateTime(year, month, day, hour, minute, second, millisecond), offset); + } + + // Constructs a DateTimeOffset from a given year, month, day, hour, + // minute, second, millsecond, Calendar and offset. + public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, TimeSpan offset) + { + _offsetMinutes = ValidateOffset(offset); + _dateTime = ValidateDate(new DateTime(year, month, day, hour, minute, second, millisecond, calendar), offset); + } + + // 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. + // + public static DateTimeOffset Now + { + get + { + return new DateTimeOffset(DateTime.Now); + } + } + + public static DateTimeOffset UtcNow + { + get + { + return new DateTimeOffset(DateTime.UtcNow); + } + } + + public DateTime DateTime + { + get + { + return ClockDateTime; + } + } + + public DateTime UtcDateTime + { + get + { + return DateTime.SpecifyKind(_dateTime, DateTimeKind.Utc); + } + } + + public DateTime LocalDateTime + { + get + { + return UtcDateTime.ToLocalTime(); + } + } + + // Adjust to a given offset with the same UTC time. Can throw ArgumentException + // + public DateTimeOffset ToOffset(TimeSpan offset) + { + return new DateTimeOffset((_dateTime + offset).Ticks, offset); + } + + + // Instance Properties + + // The clock or visible time represented. This is just a wrapper around the internal date because this is + // the chosen storage mechanism. Going through this helper is good for readability and maintainability. + // This should be used for display but not identity. + private DateTime ClockDateTime + { + get + { + return new DateTime((_dateTime + Offset).Ticks, DateTimeKind.Unspecified); + } + } + + // Returns the date part of this DateTimeOffset. The resulting value + // corresponds to this DateTimeOffset with the time-of-day part set to + // zero (midnight). + // + public DateTime Date + { + get + { + return ClockDateTime.Date; + } + } + + // Returns the day-of-month part of this DateTimeOffset. The returned + // value is an integer between 1 and 31. + // + public int Day + { + get + { + return ClockDateTime.Day; + } + } + + // Returns the day-of-week part of this DateTimeOffset. The returned value + // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates + // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates + // Thursday, 5 indicates Friday, and 6 indicates Saturday. + // + public DayOfWeek DayOfWeek + { + get + { + return ClockDateTime.DayOfWeek; + } + } + + // Returns the day-of-year part of this DateTimeOffset. The returned value + // is an integer between 1 and 366. + // + public int DayOfYear + { + get + { + return ClockDateTime.DayOfYear; + } + } + + // Returns the hour part of this DateTimeOffset. The returned value is an + // integer between 0 and 23. + // + public int Hour + { + get + { + return ClockDateTime.Hour; + } + } + + + // Returns the millisecond part of this DateTimeOffset. The returned value + // is an integer between 0 and 999. + // + public int Millisecond + { + get + { + return ClockDateTime.Millisecond; + } + } + + // Returns the minute part of this DateTimeOffset. The returned value is + // an integer between 0 and 59. + // + public int Minute + { + get + { + return ClockDateTime.Minute; + } + } + + // Returns the month part of this DateTimeOffset. The returned value is an + // integer between 1 and 12. + // + public int Month + { + get + { + return ClockDateTime.Month; + } + } + + public TimeSpan Offset + { + get + { + return new TimeSpan(0, _offsetMinutes, 0); + } + } + + // Returns the second part of this DateTimeOffset. The returned value is + // an integer between 0 and 59. + // + public int Second + { + get + { + return ClockDateTime.Second; + } + } + + // Returns the tick count for this DateTimeOffset. The returned value is + // the number of 100-nanosecond intervals that have elapsed since 1/1/0001 + // 12:00am. + // + public long Ticks + { + get + { + return ClockDateTime.Ticks; + } + } + + public long UtcTicks + { + get + { + return UtcDateTime.Ticks; + } + } + + // Returns the time-of-day part of this DateTimeOffset. The returned value + // is a TimeSpan that indicates the time elapsed since midnight. + // + public TimeSpan TimeOfDay + { + get + { + return ClockDateTime.TimeOfDay; + } + } + + // Returns the year part of this DateTimeOffset. The returned value is an + // integer between 1 and 9999. + // + public int Year + { + get + { + return ClockDateTime.Year; + } + } + + // Returns the DateTimeOffset resulting from adding the given + // TimeSpan to this DateTimeOffset. + // + public DateTimeOffset Add(TimeSpan timeSpan) + { + return new DateTimeOffset(ClockDateTime.Add(timeSpan), Offset); + } + + // Returns the DateTimeOffset resulting from adding a fractional number of + // days to this DateTimeOffset. The result is computed by rounding the + // fractional number of days given by value to the nearest + // millisecond, and adding that interval to this DateTimeOffset. The + // value argument is permitted to be negative. + // + public DateTimeOffset AddDays(double days) + { + return new DateTimeOffset(ClockDateTime.AddDays(days), Offset); + } + + // Returns the DateTimeOffset resulting from adding a fractional number of + // hours to this DateTimeOffset. The result is computed by rounding the + // fractional number of hours given by value to the nearest + // millisecond, and adding that interval to this DateTimeOffset. The + // value argument is permitted to be negative. + // + public DateTimeOffset AddHours(double hours) + { + return new DateTimeOffset(ClockDateTime.AddHours(hours), Offset); + } + + // Returns the DateTimeOffset resulting from the given number of + // milliseconds to this DateTimeOffset. The result is computed by rounding + // the number of milliseconds given by value to the nearest integer, + // and adding that interval to this DateTimeOffset. The value + // argument is permitted to be negative. + // + public DateTimeOffset AddMilliseconds(double milliseconds) + { + return new DateTimeOffset(ClockDateTime.AddMilliseconds(milliseconds), Offset); + } + + // Returns the DateTimeOffset resulting from adding a fractional number of + // minutes to this DateTimeOffset. The result is computed by rounding the + // fractional number of minutes given by value to the nearest + // millisecond, and adding that interval to this DateTimeOffset. The + // value argument is permitted to be negative. + // + public DateTimeOffset AddMinutes(double minutes) + { + return new DateTimeOffset(ClockDateTime.AddMinutes(minutes), Offset); + } + + public DateTimeOffset AddMonths(int months) + { + return new DateTimeOffset(ClockDateTime.AddMonths(months), Offset); + } + + // Returns the DateTimeOffset resulting from adding a fractional number of + // seconds to this DateTimeOffset. The result is computed by rounding the + // fractional number of seconds given by value to the nearest + // millisecond, and adding that interval to this DateTimeOffset. The + // value argument is permitted to be negative. + // + public DateTimeOffset AddSeconds(double seconds) + { + return new DateTimeOffset(ClockDateTime.AddSeconds(seconds), Offset); + } + + // Returns the DateTimeOffset resulting from adding the given number of + // 100-nanosecond ticks to this DateTimeOffset. The value argument + // is permitted to be negative. + // + public DateTimeOffset AddTicks(long ticks) + { + return new DateTimeOffset(ClockDateTime.AddTicks(ticks), Offset); + } + + // Returns the DateTimeOffset resulting from adding the given number of + // years to this DateTimeOffset. The result is computed by incrementing + // (or decrementing) the year part of this DateTimeOffset by value + // years. If the month and day of this DateTimeOffset is 2/29, and if the + // resulting year is not a leap year, the month and day of the resulting + // DateTimeOffset becomes 2/28. Otherwise, the month, day, and time-of-day + // parts of the result are the same as those of this DateTimeOffset. + // + public DateTimeOffset AddYears(int years) + { + return new DateTimeOffset(ClockDateTime.AddYears(years), Offset); + } + + // Compares two DateTimeOffset values, returning an integer that indicates + // their relationship. + // + public static int Compare(DateTimeOffset first, DateTimeOffset second) + { + return DateTime.Compare(first.UtcDateTime, second.UtcDateTime); + } + + // Compares this DateTimeOffset to a given object. This method provides an + // implementation of the IComparable interface. The object + // argument must be another DateTimeOffset, or otherwise an exception + // occurs. Null is considered less than any instance. + // + int IComparable.CompareTo(Object obj) + { + if (obj == null) return 1; + if (!(obj is DateTimeOffset)) + { + throw new ArgumentException(SR.Arg_MustBeDateTimeOffset); + } + + DateTime objUtc = ((DateTimeOffset)obj).UtcDateTime; + DateTime utc = UtcDateTime; + if (utc > objUtc) return 1; + if (utc < objUtc) return -1; + return 0; + } + + public int CompareTo(DateTimeOffset other) + { + DateTime otherUtc = other.UtcDateTime; + DateTime utc = UtcDateTime; + if (utc > otherUtc) return 1; + if (utc < otherUtc) return -1; + return 0; + } + + + // Checks if this DateTimeOffset is equal to a given object. Returns + // true if the given object is a boxed DateTimeOffset and its value + // is equal to the value of this DateTimeOffset. Returns false + // otherwise. + // + public override bool Equals(Object obj) + { + if (obj is DateTimeOffset) + { + return UtcDateTime.Equals(((DateTimeOffset)obj).UtcDateTime); + } + return false; + } + + public bool Equals(DateTimeOffset other) + { + return UtcDateTime.Equals(other.UtcDateTime); + } + + public bool EqualsExact(DateTimeOffset other) + { + // + // returns true when the ClockDateTime, Kind, and Offset match + // + // currently the Kind should always be Unspecified, but there is always the possibility that a future version + // of DateTimeOffset overloads the Kind field + // + return (ClockDateTime == other.ClockDateTime && Offset == other.Offset && ClockDateTime.Kind == other.ClockDateTime.Kind); + } + + // Compares two DateTimeOffset values for equality. Returns true if + // the two DateTimeOffset values are equal, or false if they are + // not equal. + // + public static bool Equals(DateTimeOffset first, DateTimeOffset second) + { + return DateTime.Equals(first.UtcDateTime, second.UtcDateTime); + } + + // Creates a DateTimeOffset from a Windows filetime. A Windows filetime is + // a long representing the date and time as the number of + // 100-nanosecond intervals that have elapsed since 1/1/1601 12:00am. + // + public static DateTimeOffset FromFileTime(long fileTime) + { + return new DateTimeOffset(DateTime.FromFileTime(fileTime)); + } + + public static DateTimeOffset FromUnixTimeSeconds(long seconds) + { + if (seconds < UnixMinSeconds || seconds > UnixMaxSeconds) + { + throw new ArgumentOutOfRangeException(nameof(seconds), + SR.Format(SR.ArgumentOutOfRange_Range, UnixMinSeconds, UnixMaxSeconds)); + } + + long ticks = seconds * TimeSpan.TicksPerSecond + UnixEpochTicks; + return new DateTimeOffset(ticks, TimeSpan.Zero); + } + + public static DateTimeOffset FromUnixTimeMilliseconds(long milliseconds) + { + const long MinMilliseconds = DateTime.MinTicks / TimeSpan.TicksPerMillisecond - UnixEpochMilliseconds; + const long MaxMilliseconds = DateTime.MaxTicks / TimeSpan.TicksPerMillisecond - UnixEpochMilliseconds; + + if (milliseconds < MinMilliseconds || milliseconds > MaxMilliseconds) + { + throw new ArgumentOutOfRangeException(nameof(milliseconds), + SR.Format(SR.ArgumentOutOfRange_Range, MinMilliseconds, MaxMilliseconds)); + } + + long ticks = milliseconds * TimeSpan.TicksPerMillisecond + UnixEpochTicks; + return new DateTimeOffset(ticks, TimeSpan.Zero); + } + + // ----- SECTION: private serialization instance methods ----------------* + + void IDeserializationCallback.OnDeserialization(Object sender) + { + try + { + _offsetMinutes = ValidateOffset(Offset); + _dateTime = ValidateDate(ClockDateTime, Offset); + } + catch (ArgumentException e) + { + throw new SerializationException(SR.Serialization_InvalidData, e); + } + } + + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + info.AddValue("DateTime", _dateTime); + info.AddValue("OffsetMinutes", _offsetMinutes); + } + + + private DateTimeOffset(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + _dateTime = (DateTime)info.GetValue("DateTime", typeof(DateTime)); + _offsetMinutes = (Int16)info.GetValue("OffsetMinutes", typeof(Int16)); + } + + // Returns the hash code for this DateTimeOffset. + // + public override int GetHashCode() + { + return UtcDateTime.GetHashCode(); + } + + // Constructs a DateTimeOffset from a string. The string must specify a + // date and optionally a time in a culture-specific or universal format. + // Leading and trailing whitespace characters are allowed. + // + public static DateTimeOffset Parse(String input) + { + TimeSpan offset; + DateTime dateResult = DateTimeParse.Parse(input, + DateTimeFormatInfo.CurrentInfo, + DateTimeStyles.None, + out offset); + return new DateTimeOffset(dateResult.Ticks, offset); + } + + // Constructs a DateTimeOffset from a string. The string must specify a + // date and optionally a time in a culture-specific or universal format. + // Leading and trailing whitespace characters are allowed. + // + public static DateTimeOffset Parse(String input, IFormatProvider formatProvider) + { + return Parse(input, formatProvider, DateTimeStyles.None); + } + + public static DateTimeOffset Parse(String input, IFormatProvider formatProvider, DateTimeStyles styles) + { + styles = ValidateStyles(styles, nameof(styles)); + TimeSpan offset; + DateTime dateResult = DateTimeParse.Parse(input, + DateTimeFormatInfo.GetInstance(formatProvider), + styles, + out offset); + return new DateTimeOffset(dateResult.Ticks, offset); + } + + // Constructs a DateTimeOffset from a string. The string must specify a + // date and optionally a time in a culture-specific or universal format. + // Leading and trailing whitespace characters are allowed. + // + public static DateTimeOffset ParseExact(String input, String format, IFormatProvider formatProvider) + { + return ParseExact(input, format, formatProvider, DateTimeStyles.None); + } + + // Constructs a DateTimeOffset from a string. The string must specify a + // date and optionally a time in a culture-specific or universal format. + // Leading and trailing whitespace characters are allowed. + // + public static DateTimeOffset ParseExact(String input, String format, IFormatProvider formatProvider, DateTimeStyles styles) + { + styles = ValidateStyles(styles, nameof(styles)); + TimeSpan offset; + DateTime dateResult = DateTimeParse.ParseExact(input, + format, + DateTimeFormatInfo.GetInstance(formatProvider), + styles, + out offset); + return new DateTimeOffset(dateResult.Ticks, offset); + } + + public static DateTimeOffset ParseExact(String input, String[] formats, IFormatProvider formatProvider, DateTimeStyles styles) + { + styles = ValidateStyles(styles, nameof(styles)); + TimeSpan offset; + DateTime dateResult = DateTimeParse.ParseExactMultiple(input, + formats, + DateTimeFormatInfo.GetInstance(formatProvider), + styles, + out offset); + return new DateTimeOffset(dateResult.Ticks, offset); + } + + public TimeSpan Subtract(DateTimeOffset value) + { + return UtcDateTime.Subtract(value.UtcDateTime); + } + + public DateTimeOffset Subtract(TimeSpan value) + { + return new DateTimeOffset(ClockDateTime.Subtract(value), Offset); + } + + + public long ToFileTime() + { + return UtcDateTime.ToFileTime(); + } + + public long ToUnixTimeSeconds() + { + // Truncate sub-second precision before offsetting by the Unix Epoch to avoid + // the last digit being off by one for dates that result in negative Unix times. + // + // For example, consider the DateTimeOffset 12/31/1969 12:59:59.001 +0 + // ticks = 621355967990010000 + // ticksFromEpoch = ticks - UnixEpochTicks = -9990000 + // secondsFromEpoch = ticksFromEpoch / TimeSpan.TicksPerSecond = 0 + // + // Notice that secondsFromEpoch is rounded *up* by the truncation induced by integer division, + // whereas we actually always want to round *down* when converting to Unix time. This happens + // automatically for positive Unix time values. Now the example becomes: + // seconds = ticks / TimeSpan.TicksPerSecond = 62135596799 + // secondsFromEpoch = seconds - UnixEpochSeconds = -1 + // + // In other words, we want to consistently round toward the time 1/1/0001 00:00:00, + // rather than toward the Unix Epoch (1/1/1970 00:00:00). + long seconds = UtcDateTime.Ticks / TimeSpan.TicksPerSecond; + return seconds - UnixEpochSeconds; + } + + public long ToUnixTimeMilliseconds() + { + // Truncate sub-millisecond precision before offsetting by the Unix Epoch to avoid + // the last digit being off by one for dates that result in negative Unix times + long milliseconds = UtcDateTime.Ticks / TimeSpan.TicksPerMillisecond; + return milliseconds - UnixEpochMilliseconds; + } + + public DateTimeOffset ToLocalTime() + { + return ToLocalTime(false); + } + + internal DateTimeOffset ToLocalTime(bool throwOnOverflow) + { + return new DateTimeOffset(UtcDateTime.ToLocalTime(throwOnOverflow)); + } + + public override String ToString() + { + return DateTimeFormat.Format(ClockDateTime, null, DateTimeFormatInfo.CurrentInfo, Offset); + } + + public String ToString(String format) + { + return DateTimeFormat.Format(ClockDateTime, format, DateTimeFormatInfo.CurrentInfo, Offset); + } + + public String ToString(IFormatProvider formatProvider) + { + return DateTimeFormat.Format(ClockDateTime, null, DateTimeFormatInfo.GetInstance(formatProvider), Offset); + } + + public String ToString(String format, IFormatProvider formatProvider) + { + return DateTimeFormat.Format(ClockDateTime, format, DateTimeFormatInfo.GetInstance(formatProvider), Offset); + } + + public DateTimeOffset ToUniversalTime() + { + return new DateTimeOffset(UtcDateTime); + } + + public static Boolean TryParse(String input, out DateTimeOffset result) + { + TimeSpan offset; + DateTime dateResult; + Boolean parsed = DateTimeParse.TryParse(input, + DateTimeFormatInfo.CurrentInfo, + DateTimeStyles.None, + out dateResult, + out offset); + result = new DateTimeOffset(dateResult.Ticks, offset); + return parsed; + } + + public static Boolean TryParse(String input, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result) + { + styles = ValidateStyles(styles, nameof(styles)); + TimeSpan offset; + DateTime dateResult; + Boolean parsed = DateTimeParse.TryParse(input, + DateTimeFormatInfo.GetInstance(formatProvider), + styles, + out dateResult, + out offset); + result = new DateTimeOffset(dateResult.Ticks, offset); + return parsed; + } + + public static Boolean TryParseExact(String input, String format, IFormatProvider formatProvider, DateTimeStyles styles, + out DateTimeOffset result) + { + styles = ValidateStyles(styles, nameof(styles)); + TimeSpan offset; + DateTime dateResult; + Boolean parsed = DateTimeParse.TryParseExact(input, + format, + DateTimeFormatInfo.GetInstance(formatProvider), + styles, + out dateResult, + out offset); + result = new DateTimeOffset(dateResult.Ticks, offset); + return parsed; + } + + public static Boolean TryParseExact(String input, String[] formats, IFormatProvider formatProvider, DateTimeStyles styles, + out DateTimeOffset result) + { + styles = ValidateStyles(styles, nameof(styles)); + TimeSpan offset; + DateTime dateResult; + Boolean parsed = DateTimeParse.TryParseExactMultiple(input, + formats, + DateTimeFormatInfo.GetInstance(formatProvider), + styles, + out dateResult, + out offset); + result = new DateTimeOffset(dateResult.Ticks, offset); + return parsed; + } + + // Ensures the TimeSpan is valid to go in a DateTimeOffset. + private static Int16 ValidateOffset(TimeSpan offset) + { + Int64 ticks = offset.Ticks; + if (ticks % TimeSpan.TicksPerMinute != 0) + { + throw new ArgumentException(SR.Argument_OffsetPrecision, nameof(offset)); + } + if (ticks < MinOffset || ticks > MaxOffset) + { + throw new ArgumentOutOfRangeException(nameof(offset), SR.Argument_OffsetOutOfRange); + } + return (Int16)(offset.Ticks / TimeSpan.TicksPerMinute); + } + + // Ensures that the time and offset are in range. + private static DateTime ValidateDate(DateTime dateTime, TimeSpan offset) + { + // The key validation is that both the UTC and clock times fit. The clock time is validated + // by the DateTime constructor. + Debug.Assert(offset.Ticks >= MinOffset && offset.Ticks <= MaxOffset, "Offset not validated."); + + // This operation cannot overflow because offset should have already been validated to be within + // 14 hours and the DateTime instance is more than that distance from the boundaries of Int64. + Int64 utcTicks = dateTime.Ticks - offset.Ticks; + if (utcTicks < DateTime.MinTicks || utcTicks > DateTime.MaxTicks) + { + throw new ArgumentOutOfRangeException(nameof(offset), SR.Argument_UTCOutOfRange); + } + // make sure the Kind is set to Unspecified + // + return new DateTime(utcTicks, DateTimeKind.Unspecified); + } + + private static DateTimeStyles ValidateStyles(DateTimeStyles style, String parameterName) + { + if ((style & DateTimeFormatInfo.InvalidDateTimeStyles) != 0) + { + throw new ArgumentException(SR.Argument_InvalidDateTimeStyles, parameterName); + } + if (((style & (DateTimeStyles.AssumeLocal)) != 0) && ((style & (DateTimeStyles.AssumeUniversal)) != 0)) + { + throw new ArgumentException(SR.Argument_ConflictingDateTimeStyles, parameterName); + } + if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) + { + throw new ArgumentException(SR.Argument_DateTimeOffsetInvalidDateTimeStyles, parameterName); + } + + // RoundtripKind does not make sense for DateTimeOffset; ignore this flag for backward compatibility with DateTime + style &= ~DateTimeStyles.RoundtripKind; + + // AssumeLocal is also ignored as that is what we do by default with DateTimeOffset.Parse + style &= ~DateTimeStyles.AssumeLocal; + + return style; + } + + // Operators + + public static implicit operator DateTimeOffset(DateTime dateTime) + { + return new DateTimeOffset(dateTime); + } + + public static DateTimeOffset operator +(DateTimeOffset dateTimeOffset, TimeSpan timeSpan) + { + return new DateTimeOffset(dateTimeOffset.ClockDateTime + timeSpan, dateTimeOffset.Offset); + } + + + public static DateTimeOffset operator -(DateTimeOffset dateTimeOffset, TimeSpan timeSpan) + { + return new DateTimeOffset(dateTimeOffset.ClockDateTime - timeSpan, dateTimeOffset.Offset); + } + + public static TimeSpan operator -(DateTimeOffset left, DateTimeOffset right) + { + return left.UtcDateTime - right.UtcDateTime; + } + + public static bool operator ==(DateTimeOffset left, DateTimeOffset right) + { + return left.UtcDateTime == right.UtcDateTime; + } + + public static bool operator !=(DateTimeOffset left, DateTimeOffset right) + { + return left.UtcDateTime != right.UtcDateTime; + } + + public static bool operator <(DateTimeOffset left, DateTimeOffset right) + { + return left.UtcDateTime < right.UtcDateTime; + } + + public static bool operator <=(DateTimeOffset left, DateTimeOffset right) + { + return left.UtcDateTime <= right.UtcDateTime; + } + + public static bool operator >(DateTimeOffset left, DateTimeOffset right) + { + return left.UtcDateTime > right.UtcDateTime; + } + + public static bool operator >=(DateTimeOffset left, DateTimeOffset right) + { + return left.UtcDateTime >= right.UtcDateTime; + } + } +} diff --git a/src/mscorlib/shared/System/DayOfWeek.cs b/src/mscorlib/shared/System/DayOfWeek.cs new file mode 100644 index 0000000000..5d84257158 --- /dev/null +++ b/src/mscorlib/shared/System/DayOfWeek.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. + +/*============================================================ +** +** +** +** Purpose: Enum for the day of the week. +** +** +============================================================*/ + +namespace System +{ + [Serializable] + public enum DayOfWeek + { + Sunday = 0, + Monday = 1, + Tuesday = 2, + Wednesday = 3, + Thursday = 4, + Friday = 5, + Saturday = 6, + } +} diff --git a/src/mscorlib/shared/System/DefaultBinder.cs b/src/mscorlib/shared/System/DefaultBinder.cs new file mode 100644 index 0000000000..3b46d5f4d3 --- /dev/null +++ b/src/mscorlib/shared/System/DefaultBinder.cs @@ -0,0 +1,1201 @@ +// 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.Reflection; +using System.Diagnostics; +using CultureInfo = System.Globalization.CultureInfo; + +namespace System +{ + //Marked serializable even though it has no state. + [Serializable] +#if CORECLR + internal +#else + public sealed +#endif + partial class DefaultBinder : Binder + { + // This method is passed a set of methods and must choose the best + // fit. The methods all have the same number of arguments and the object + // array args. On exit, this method will choice the best fit method + // and coerce the args to match that method. By match, we mean all primitive + // arguments are exact matchs and all object arguments are exact or subclasses + // of the target. If the target OR is an interface, the object must implement + // that interface. There are a couple of exceptions + // thrown when a method cannot be returned. If no method matchs the args and + // ArgumentException is thrown. If multiple methods match the args then + // an AmbiguousMatchException is thrown. + // + // The most specific match will be selected. + // + public sealed override MethodBase BindToMethod( + BindingFlags bindingAttr, MethodBase[] match, ref object[] args, + ParameterModifier[] modifiers, CultureInfo cultureInfo, string[] names, out object state) + { + if (match == null || match.Length == 0) + throw new ArgumentException(SR.Arg_EmptyArray, nameof(match)); + + MethodBase[] candidates = (MethodBase[])match.Clone(); + + int i; + int j; + + state = null; + +#region Map named parameters to candidate parameter postions + // 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 + // named parameters (names) may change the order. If names + // is not provided, then we assume the default mapping (0,1,...) + int[][] paramOrder = new int[candidates.Length][]; + + for (i = 0; i < candidates.Length; i++) + { + ParameterInfo[] par = candidates[i].GetParametersNoCopy(); + + // args.Length + 1 takes into account the possibility of a last paramArray that can be omitted + paramOrder[i] = new int[(par.Length > args.Length) ? par.Length : args.Length]; + + if (names == null) + { + // Default mapping + for (j = 0; j < args.Length; j++) + paramOrder[i][j] = j; + } + else + { + // Named parameters, reorder the mapping. If CreateParamOrder fails, it means that the method + // doesn't have a name that matchs one of the named parameters so we don't consider it any further. + if (!CreateParamOrder(paramOrder[i], par, names)) + candidates[i] = null; + } + } +#endregion + + Type[] paramArrayTypes = new Type[candidates.Length]; + + Type[] argTypes = new Type[args.Length]; + +#region Cache the type of the provided arguments + // object that contain a null are treated as if they were typeless (but match either object + // references or value classes). We mark this condition by placing a null in the argTypes array. + for (i = 0; i < args.Length; i++) + { + if (args[i] != null) + { + argTypes[i] = args[i].GetType(); + } + } +#endregion + + + // Find the method that matches... + int CurIdx = 0; + bool defaultValueBinding = ((bindingAttr & BindingFlags.OptionalParamBinding) != 0); + + Type paramArrayType = null; + +#region Filter methods by parameter count and type + for (i = 0; i < candidates.Length; i++) + { + paramArrayType = null; + + // If we have named parameters then we may have a hole in the candidates array. + if (candidates[i] == null) + continue; + + // Validate the parameters. + ParameterInfo[] par = candidates[i].GetParametersNoCopy(); + +#region Match method by parameter count + if (par.Length == 0) + { +#region No formal parameters + if (args.Length != 0) + { + if ((candidates[i].CallingConvention & CallingConventions.VarArgs) == 0) + continue; + } + + // This is a valid routine so we move it up the candidates list. + paramOrder[CurIdx] = paramOrder[i]; + candidates[CurIdx++] = candidates[i]; + + continue; +#endregion + } + else if (par.Length > args.Length) + { +#region Shortage of provided parameters + // If the number of parameters is greater than the number of args then + // we are in the situation were we may be using default values. + for (j = args.Length; j < par.Length - 1; j++) + { + if (par[j].DefaultValue == System.DBNull.Value) + break; + } + + if (j != par.Length - 1) + continue; + + if (par[j].DefaultValue == System.DBNull.Value) + { + if (!par[j].ParameterType.IsArray) + continue; + + if (!par[j].IsDefined(typeof(ParamArrayAttribute), true)) + continue; + + paramArrayType = par[j].ParameterType.GetElementType(); + } +#endregion + } + else if (par.Length < args.Length) + { +#region Excess provided parameters + // test for the ParamArray case + int lastArgPos = par.Length - 1; + + if (!par[lastArgPos].ParameterType.IsArray) + continue; + + if (!par[lastArgPos].IsDefined(typeof(ParamArrayAttribute), true)) + continue; + + if (paramOrder[i][lastArgPos] != lastArgPos) + continue; + + paramArrayType = par[lastArgPos].ParameterType.GetElementType(); +#endregion + } + else + { +#region Test for paramArray, save paramArray type + int lastArgPos = par.Length - 1; + + if (par[lastArgPos].ParameterType.IsArray + && par[lastArgPos].IsDefined(typeof(ParamArrayAttribute), true) + && paramOrder[i][lastArgPos] == lastArgPos) + { + if (!par[lastArgPos].ParameterType.IsAssignableFrom(argTypes[lastArgPos])) + paramArrayType = par[lastArgPos].ParameterType.GetElementType(); + } +#endregion + } +#endregion + + Type pCls = null; + int argsToCheck = (paramArrayType != null) ? par.Length - 1 : args.Length; + +#region Match method by parameter type + for (j = 0; j < argsToCheck; j++) + { +#region Classic argument coersion checks + // get the formal type + pCls = par[j].ParameterType; + + if (pCls.IsByRef) + pCls = pCls.GetElementType(); + + // the type is the same + if (pCls == argTypes[paramOrder[i][j]]) + continue; + + // a default value is available + if (defaultValueBinding && args[paramOrder[i][j]] == Type.Missing) + continue; + + // the argument was null, so it matches with everything + if (args[paramOrder[i][j]] == null) + continue; + + // the type is Object, so it will match everything + if (pCls == typeof(object)) + continue; + + // now do a "classic" type check + if (pCls.IsPrimitive) + { + if (argTypes[paramOrder[i][j]] == null || !CanChangePrimitiveObjectToType(args[paramOrder[i][j]], pCls)) + { + break; + } + } + else + { + if (argTypes[paramOrder[i][j]] == null) + continue; + + if (!pCls.IsAssignableFrom(argTypes[paramOrder[i][j]])) + { + if (argTypes[paramOrder[i][j]].IsCOMObject) + { + if (pCls.IsInstanceOfType(args[paramOrder[i][j]])) + continue; + } + break; + } + } +#endregion + } + + if (paramArrayType != null && j == par.Length - 1) + { +#region Check that excess arguments can be placed in the param array + for (; j < args.Length; j++) + { + if (paramArrayType.IsPrimitive) + { + if (argTypes[j] == null || !CanChangePrimitiveObjectToType(args[j], paramArrayType)) + break; + } + else + { + if (argTypes[j] == null) + continue; + + if (!paramArrayType.IsAssignableFrom(argTypes[j])) + { + if (argTypes[j].IsCOMObject) + { + if (paramArrayType.IsInstanceOfType(args[j])) + continue; + } + + break; + } + } + } +#endregion + } +#endregion + + if (j == args.Length) + { +#region This is a valid routine so we move it up the candidates list + paramOrder[CurIdx] = paramOrder[i]; + paramArrayTypes[CurIdx] = paramArrayType; + candidates[CurIdx++] = candidates[i]; +#endregion + } + } +#endregion + + // If we didn't find a method + if (CurIdx == 0) + throw new MissingMethodException(SR.MissingMember); + + if (CurIdx == 1) + { +#region Found only one method + if (names != null) + { + state = new BinderState((int[])paramOrder[0].Clone(), args.Length, paramArrayTypes[0] != null); + ReorderParams(paramOrder[0], args); + } + + // If the parameters and the args are not the same length or there is a paramArray + // then we need to create a argument array. + ParameterInfo[] parms = candidates[0].GetParametersNoCopy(); + + if (parms.Length == args.Length) + { + if (paramArrayTypes[0] != null) + { + object[] objs = new object[parms.Length]; + int lastPos = parms.Length - 1; + Array.Copy(args, 0, objs, 0, lastPos); + objs[lastPos] = Array.CreateInstance(paramArrayTypes[0], 1); + ((Array)objs[lastPos]).SetValue(args[lastPos], 0); + args = objs; + } + } + else if (parms.Length > args.Length) + { + object[] objs = new object[parms.Length]; + + for (i = 0; i < args.Length; i++) + objs[i] = args[i]; + + for (; i < parms.Length - 1; i++) + objs[i] = parms[i].DefaultValue; + + if (paramArrayTypes[0] != null) + objs[i] = Array.CreateInstance(paramArrayTypes[0], 0); // create an empty array for the + + else + objs[i] = parms[i].DefaultValue; + + args = objs; + } + else + { + if ((candidates[0].CallingConvention & CallingConventions.VarArgs) == 0) + { + object[] objs = new object[parms.Length]; + int paramArrayPos = parms.Length - 1; + Array.Copy(args, 0, objs, 0, paramArrayPos); + objs[paramArrayPos] = Array.CreateInstance(paramArrayTypes[0], args.Length - paramArrayPos); + Array.Copy(args, paramArrayPos, (System.Array)objs[paramArrayPos], 0, args.Length - paramArrayPos); + args = objs; + } + } +#endregion + + return candidates[0]; + } + + int currentMin = 0; + bool ambig = false; + for (i = 1; i < CurIdx; i++) + { +#region Walk all of the methods looking the most specific method to invoke + int newMin = FindMostSpecificMethod(candidates[currentMin], paramOrder[currentMin], paramArrayTypes[currentMin], + candidates[i], paramOrder[i], paramArrayTypes[i], argTypes, args); + + if (newMin == 0) + { + ambig = true; + } + else if (newMin == 2) + { + currentMin = i; + ambig = false; + } +#endregion + } + + if (ambig) + throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException); + + // Reorder (if needed) + if (names != null) + { + state = new BinderState((int[])paramOrder[currentMin].Clone(), args.Length, paramArrayTypes[currentMin] != null); + ReorderParams(paramOrder[currentMin], args); + } + + // If the parameters and the args are not the same length or there is a paramArray + // then we need to create a argument array. + ParameterInfo[] parameters = candidates[currentMin].GetParametersNoCopy(); + if (parameters.Length == args.Length) + { + if (paramArrayTypes[currentMin] != null) + { + object[] objs = new object[parameters.Length]; + int lastPos = parameters.Length - 1; + Array.Copy(args, 0, objs, 0, lastPos); + objs[lastPos] = Array.CreateInstance(paramArrayTypes[currentMin], 1); + ((Array)objs[lastPos]).SetValue(args[lastPos], 0); + args = objs; + } + } + else if (parameters.Length > args.Length) + { + object[] objs = new object[parameters.Length]; + + for (i = 0; i < args.Length; i++) + objs[i] = args[i]; + + for (; i < parameters.Length - 1; i++) + objs[i] = parameters[i].DefaultValue; + + if (paramArrayTypes[currentMin] != null) + { + objs[i] = Array.CreateInstance(paramArrayTypes[currentMin], 0); + } + else + { + objs[i] = parameters[i].DefaultValue; + } + + args = objs; + } + else + { + if ((candidates[currentMin].CallingConvention & CallingConventions.VarArgs) == 0) + { + object[] objs = new object[parameters.Length]; + int paramArrayPos = parameters.Length - 1; + Array.Copy(args, 0, objs, 0, paramArrayPos); + objs[paramArrayPos] = Array.CreateInstance(paramArrayTypes[currentMin], args.Length - paramArrayPos); + Array.Copy(args, paramArrayPos, (System.Array)objs[paramArrayPos], 0, args.Length - paramArrayPos); + args = objs; + } + } + + return candidates[currentMin]; + } + + + // Given a set of fields that match the base criteria, select a field. + // if value is null then we have no way to select a field + public sealed override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo cultureInfo) + { + if (match == null) + { + throw new ArgumentNullException(nameof(match)); + } + + int i; + // Find the method that match... + int CurIdx = 0; + + Type valueType = null; + + FieldInfo[] candidates = (FieldInfo[])match.Clone(); + + // If we are a FieldSet, then use the value's type to disambiguate + if ((bindingAttr & BindingFlags.SetField) != 0) + { + valueType = value.GetType(); + + for (i = 0; i < candidates.Length; i++) + { + Type pCls = candidates[i].FieldType; + if (pCls == valueType) + { + candidates[CurIdx++] = candidates[i]; + continue; + } + if (value == Empty.Value) + { + // the object passed in was null which would match any non primitive non value type + if (pCls.IsClass) + { + candidates[CurIdx++] = candidates[i]; + continue; + } + } + if (pCls == typeof(object)) + { + candidates[CurIdx++] = candidates[i]; + continue; + } + if (pCls.IsPrimitive) + { + if (CanChangePrimitiveObjectToType(value, pCls)) + { + candidates[CurIdx++] = candidates[i]; + continue; + } + } + else + { + if (pCls.IsAssignableFrom(valueType)) + { + candidates[CurIdx++] = candidates[i]; + continue; + } + } + } + if (CurIdx == 0) + throw new MissingFieldException(SR.MissingField); + if (CurIdx == 1) + return candidates[0]; + } + + // Walk all of the methods looking the most specific method to invoke + int currentMin = 0; + bool ambig = false; + for (i = 1; i < CurIdx; i++) + { + int newMin = FindMostSpecificField(candidates[currentMin], candidates[i]); + if (newMin == 0) + ambig = true; + else + { + if (newMin == 2) + { + currentMin = i; + ambig = false; + } + } + } + if (ambig) + throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException); + return candidates[currentMin]; + } + + // Given a set of methods that match the base criteria, select a method based + // upon an array of types. This method should return null if no method matchs + // the criteria. + public sealed override MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers) + { + int i; + int j; + + Type[] realTypes = new Type[types.Length]; + for (i = 0; i < types.Length; i++) + { + realTypes[i] = types[i].UnderlyingSystemType; + if (!(realTypes[i].IsRuntimeImplemented())) + throw new ArgumentException(SR.Arg_MustBeType, nameof(types)); + } + types = realTypes; + + // We don't automatically jump out on exact match. + if (match == null || match.Length == 0) + throw new ArgumentException(SR.Arg_EmptyArray, nameof(match)); + + MethodBase[] candidates = (MethodBase[])match.Clone(); + + // Find all the methods that can be described by the types parameter. + // Remove all of them that cannot. + int CurIdx = 0; + for (i = 0; i < candidates.Length; i++) + { + ParameterInfo[] par = candidates[i].GetParametersNoCopy(); + if (par.Length != types.Length) + continue; + for (j = 0; j < types.Length; j++) + { + Type pCls = par[j].ParameterType; + if (pCls == types[j]) + continue; + if (pCls == typeof(object)) + continue; + if (pCls.IsPrimitive) + { + if (!(types[j].UnderlyingSystemType.IsRuntimeImplemented()) || + !CanChangePrimitive(types[j].UnderlyingSystemType, pCls.UnderlyingSystemType)) + break; + } + else + { + if (!pCls.IsAssignableFrom(types[j])) + break; + } + } + if (j == types.Length) + candidates[CurIdx++] = candidates[i]; + } + if (CurIdx == 0) + return null; + if (CurIdx == 1) + return candidates[0]; + + // Walk all of the methods looking the most specific method to invoke + int currentMin = 0; + bool ambig = false; + int[] paramOrder = new int[types.Length]; + for (i = 0; i < types.Length; i++) + paramOrder[i] = i; + for (i = 1; i < CurIdx; i++) + { + int newMin = FindMostSpecificMethod(candidates[currentMin], paramOrder, null, candidates[i], paramOrder, null, types, null); + if (newMin == 0) + ambig = true; + else + { + if (newMin == 2) + { + currentMin = i; + ambig = false; + currentMin = i; + } + } + } + if (ambig) + throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException); + return candidates[currentMin]; + } + + // Given a set of properties that match the base criteria, select one. + public sealed override PropertyInfo SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, + Type[] indexes, ParameterModifier[] modifiers) + { + // Allow a null indexes array. But if it is not null, every element must be non-null as well. + if (indexes != null) + { + foreach (Type index in indexes) + { + if (index == null) + throw new ArgumentNullException(nameof(indexes)); + } + } + + if (match == null || match.Length == 0) + throw new ArgumentException(SR.Arg_EmptyArray, nameof(match)); + + PropertyInfo[] candidates = (PropertyInfo[])match.Clone(); + + int i, j = 0; + + // Find all the properties that can be described by type indexes parameter + int CurIdx = 0; + int indexesLength = (indexes != null) ? indexes.Length : 0; + for (i = 0; i < candidates.Length; i++) + { + if (indexes != null) + { + ParameterInfo[] par = candidates[i].GetIndexParameters(); + if (par.Length != indexesLength) + continue; + + for (j = 0; j < indexesLength; j++) + { + Type pCls = par[j].ParameterType; + + // If the classes exactly match continue + if (pCls == indexes[j]) + continue; + if (pCls == typeof(object)) + continue; + + if (pCls.IsPrimitive) + { + if (!(indexes[j].UnderlyingSystemType.IsRuntimeImplemented()) || + !CanChangePrimitive(indexes[j].UnderlyingSystemType, pCls.UnderlyingSystemType)) + break; + } + else + { + if (!pCls.IsAssignableFrom(indexes[j])) + break; + } + } + } + + if (j == indexesLength) + { + if (returnType != null) + { + if (candidates[i].PropertyType.IsPrimitive) + { + if (!(returnType.UnderlyingSystemType.IsRuntimeImplemented()) || + !CanChangePrimitive(returnType.UnderlyingSystemType, candidates[i].PropertyType.UnderlyingSystemType)) + continue; + } + else + { + if (!candidates[i].PropertyType.IsAssignableFrom(returnType)) + continue; + } + } + candidates[CurIdx++] = candidates[i]; + } + } + if (CurIdx == 0) + return null; + if (CurIdx == 1) + return candidates[0]; + + // Walk all of the properties looking the most specific method to invoke + int currentMin = 0; + bool ambig = false; + int[] paramOrder = new int[indexesLength]; + for (i = 0; i < indexesLength; i++) + paramOrder[i] = i; + for (i = 1; i < CurIdx; i++) + { + int newMin = FindMostSpecificType(candidates[currentMin].PropertyType, candidates[i].PropertyType, returnType); + if (newMin == 0 && indexes != null) + newMin = FindMostSpecific(candidates[currentMin].GetIndexParameters(), + paramOrder, + null, + candidates[i].GetIndexParameters(), + paramOrder, + null, + indexes, + null); + if (newMin == 0) + { + newMin = FindMostSpecificProperty(candidates[currentMin], candidates[i]); + if (newMin == 0) + ambig = true; + } + if (newMin == 2) + { + ambig = false; + currentMin = i; + } + } + + if (ambig) + throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException); + return candidates[currentMin]; + } + + // ChangeType + // The default binder doesn't support any change type functionality. + // This is because the default is built into the low level invoke code. + public override object ChangeType(object value, Type type, CultureInfo cultureInfo) + { + throw new NotSupportedException(SR.NotSupported_ChangeType); + } + + public sealed override void ReorderArgumentArray(ref object[] args, object state) + { + BinderState binderState = (BinderState)state; + ReorderParams(binderState._argsMap, args); + if (binderState._isParamArray) + { + int paramArrayPos = args.Length - 1; + if (args.Length == binderState._originalSize) + args[paramArrayPos] = ((object[])args[paramArrayPos])[0]; + else + { + // must be args.Length < state.originalSize + object[] newArgs = new object[args.Length]; + Array.Copy(args, 0, newArgs, 0, paramArrayPos); + for (int i = paramArrayPos, j = 0; i < newArgs.Length; i++, j++) + { + newArgs[i] = ((object[])args[paramArrayPos])[j]; + } + args = newArgs; + } + } + else + { + if (args.Length > binderState._originalSize) + { + object[] newArgs = new object[binderState._originalSize]; + Array.Copy(args, 0, newArgs, 0, binderState._originalSize); + args = newArgs; + } + } + } + + // Return any exact bindings that may exist. (This method is not defined on the + // Binder and is used by RuntimeType.) + public static MethodBase ExactBinding(MethodBase[] match, Type[] types, ParameterModifier[] modifiers) + { + if (match == null) + throw new ArgumentNullException(nameof(match)); + + MethodBase[] aExactMatches = new MethodBase[match.Length]; + int cExactMatches = 0; + + for (int i = 0; i < match.Length; i++) + { + ParameterInfo[] par = match[i].GetParametersNoCopy(); + if (par.Length == 0) + { + continue; + } + int j; + for (j = 0; j < types.Length; j++) + { + Type pCls = par[j].ParameterType; + + // If the classes exactly match continue + if (!pCls.Equals(types[j])) + break; + } + if (j < types.Length) + continue; + + // Add the exact match to the array of exact matches. + aExactMatches[cExactMatches] = match[i]; + cExactMatches++; + } + + if (cExactMatches == 0) + return null; + + if (cExactMatches == 1) + return aExactMatches[0]; + + return FindMostDerivedNewSlotMeth(aExactMatches, cExactMatches); + } + + // Return any exact bindings that may exist. (This method is not defined on the + // Binder and is used by RuntimeType.) + public static PropertyInfo ExactPropertyBinding(PropertyInfo[] match, Type returnType, Type[] types, ParameterModifier[] modifiers) + { + if (match == null) + throw new ArgumentNullException(nameof(match)); + + PropertyInfo bestMatch = null; + int typesLength = (types != null) ? types.Length : 0; + for (int i = 0; i < match.Length; i++) + { + ParameterInfo[] par = match[i].GetIndexParameters(); + int j; + for (j = 0; j < typesLength; j++) + { + Type pCls = par[j].ParameterType; + + // If the classes exactly match continue + if (pCls != types[j]) + break; + } + if (j < typesLength) + continue; + if (returnType != null && returnType != match[i].PropertyType) + continue; + + if (bestMatch != null) + throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException); + + bestMatch = match[i]; + } + return bestMatch; + } + + private static int FindMostSpecific(ParameterInfo[] p1, int[] paramOrder1, Type paramArrayType1, + ParameterInfo[] p2, int[] paramOrder2, Type paramArrayType2, + Type[] types, object[] args) + { + // A method using params is always less specific than one not using params + if (paramArrayType1 != null && paramArrayType2 == null) return 2; + if (paramArrayType2 != null && paramArrayType1 == null) return 1; + + // now either p1 and p2 both use params or neither does. + + bool p1Less = false; + bool p2Less = false; + + for (int i = 0; i < types.Length; i++) + { + if (args != null && args[i] == Type.Missing) + continue; + + Type c1, c2; + + // If a param array is present, then either + // the user re-ordered the parameters in which case + // the argument to the param array is either an array + // in which case the params is conceptually ignored and so paramArrayType1 == null + // or the argument to the param array is a single element + // in which case paramOrder[i] == p1.Length - 1 for that element + // or the user did not re-order the parameters in which case + // the paramOrder array could contain indexes larger than p.Length - 1 (see VSW 577286) + // so any index >= p.Length - 1 is being put in the param array + + if (paramArrayType1 != null && paramOrder1[i] >= p1.Length - 1) + c1 = paramArrayType1; + else + c1 = p1[paramOrder1[i]].ParameterType; + + if (paramArrayType2 != null && paramOrder2[i] >= p2.Length - 1) + c2 = paramArrayType2; + else + c2 = p2[paramOrder2[i]].ParameterType; + + if (c1 == c2) continue; + + switch (FindMostSpecificType(c1, c2, types[i])) + { + case 0: return 0; + case 1: p1Less = true; break; + case 2: p2Less = true; break; + } + } + + // Two way p1Less and p2Less can be equal. All the arguments are the + // same they both equal false, otherwise there were things that both + // were the most specific type on.... + if (p1Less == p2Less) + { + // if we cannot tell which is a better match based on parameter types (p1Less == p2Less), + // let's see which one has the most matches without using the params array (the longer one wins). + if (!p1Less && args != null) + { + if (p1.Length > p2.Length) + { + return 1; + } + else if (p2.Length > p1.Length) + { + return 2; + } + } + + return 0; + } + else + { + return (p1Less == true) ? 1 : 2; + } + } + + private static int FindMostSpecificType(Type c1, Type c2, Type t) + { + // If the two types are exact move on... + if (c1 == c2) + return 0; + + if (c1 == t) + return 1; + + if (c2 == t) + return 2; + + bool c1FromC2; + bool c2FromC1; + + if (c1.IsByRef || c2.IsByRef) + { + if (c1.IsByRef && c2.IsByRef) + { + c1 = c1.GetElementType(); + c2 = c2.GetElementType(); + } + else if (c1.IsByRef) + { + if (c1.GetElementType() == c2) + return 2; + + c1 = c1.GetElementType(); + } + else + { + if (c2.GetElementType() == c1) + return 1; + + c2 = c2.GetElementType(); + } + } + + + if (c1.IsPrimitive && c2.IsPrimitive) + { + c1FromC2 = CanChangePrimitive(c2, c1); + c2FromC1 = CanChangePrimitive(c1, c2); + } + else + { + c1FromC2 = c1.IsAssignableFrom(c2); + c2FromC1 = c2.IsAssignableFrom(c1); + } + + if (c1FromC2 == c2FromC1) + return 0; + + if (c1FromC2) + { + return 2; + } + else + { + return 1; + } + } + + private static int FindMostSpecificMethod(MethodBase m1, int[] paramOrder1, Type paramArrayType1, + MethodBase m2, int[] paramOrder2, Type paramArrayType2, + Type[] types, object[] args) + { + // Find the most specific method based on the parameters. + int res = FindMostSpecific(m1.GetParametersNoCopy(), paramOrder1, paramArrayType1, + m2.GetParametersNoCopy(), paramOrder2, paramArrayType2, types, args); + + // If the match was not ambigous then return the result. + if (res != 0) + return res; + + // Check to see if the methods have the exact same name and signature. + if (CompareMethodSig(m1, m2)) + { + // Determine the depth of the declaring types for both methods. + int hierarchyDepth1 = GetHierarchyDepth(m1.DeclaringType); + int hierarchyDepth2 = GetHierarchyDepth(m2.DeclaringType); + + // The most derived method is the most specific one. + if (hierarchyDepth1 == hierarchyDepth2) + { + return 0; + } + else if (hierarchyDepth1 < hierarchyDepth2) + { + return 2; + } + else + { + return 1; + } + } + + // The match is ambigous. + return 0; + } + + private static int FindMostSpecificField(FieldInfo cur1, FieldInfo cur2) + { + // Check to see if the fields have the same name. + if (cur1.Name == cur2.Name) + { + int hierarchyDepth1 = GetHierarchyDepth(cur1.DeclaringType); + int hierarchyDepth2 = GetHierarchyDepth(cur2.DeclaringType); + + if (hierarchyDepth1 == hierarchyDepth2) + { + Debug.Assert(cur1.IsStatic != cur2.IsStatic, "hierarchyDepth1 == hierarchyDepth2"); + return 0; + } + else if (hierarchyDepth1 < hierarchyDepth2) + return 2; + else + return 1; + } + + // The match is ambigous. + return 0; + } + + private static int FindMostSpecificProperty(PropertyInfo cur1, PropertyInfo cur2) + { + // Check to see if the fields have the same name. + if (cur1.Name == cur2.Name) + { + int hierarchyDepth1 = GetHierarchyDepth(cur1.DeclaringType); + int hierarchyDepth2 = GetHierarchyDepth(cur2.DeclaringType); + + if (hierarchyDepth1 == hierarchyDepth2) + { + return 0; + } + else if (hierarchyDepth1 < hierarchyDepth2) + return 2; + else + return 1; + } + + // The match is ambigous. + return 0; + } + + public static bool CompareMethodSig(MethodBase m1, MethodBase m2) + { + ParameterInfo[] params1 = m1.GetParametersNoCopy(); + ParameterInfo[] params2 = m2.GetParametersNoCopy(); + + if (params1.Length != params2.Length) + return false; + + int numParams = params1.Length; + for (int i = 0; i < numParams; i++) + { + if (params1[i].ParameterType != params2[i].ParameterType) + return false; + } + + return true; + } + + private static int GetHierarchyDepth(Type t) + { + int depth = 0; + + Type currentType = t; + do + { + depth++; + currentType = currentType.BaseType; + } while (currentType != null); + + return depth; + } + + internal static MethodBase FindMostDerivedNewSlotMeth(MethodBase[] match, int cMatches) + { + int deepestHierarchy = 0; + MethodBase methWithDeepestHierarchy = null; + + for (int i = 0; i < cMatches; i++) + { + // Calculate the depth of the hierarchy of the declaring type of the + // current method. + int currentHierarchyDepth = GetHierarchyDepth(match[i].DeclaringType); + + // The two methods have the same name, signature, and hierarchy depth. + // This can only happen if at least one is vararg or generic. + if (currentHierarchyDepth == deepestHierarchy) + { + throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException); + } + + // Check to see if this method is on the most derived class. + if (currentHierarchyDepth > deepestHierarchy) + { + deepestHierarchy = currentHierarchyDepth; + methWithDeepestHierarchy = match[i]; + } + } + + return methWithDeepestHierarchy; + } + + // This method will sort the vars array into the mapping order stored + // in the paramOrder array. + private static void ReorderParams(int[] paramOrder, object[] vars) + { + object[] varsCopy = new object[vars.Length]; + for (int i = 0; i < vars.Length; i++) + varsCopy[i] = vars[i]; + + for (int i = 0; i < vars.Length; i++) + vars[i] = varsCopy[paramOrder[i]]; + } + + // This method will create the mapping between the Parameters and the underlying + // data based upon the names array. The names array is stored in the same order + // as the values and maps to the parameters of the method. We store the mapping + // from the parameters to the names in the paramOrder array. All parameters that + // don't have matching names are then stored in the array in order. + private static bool CreateParamOrder(int[] paramOrder, ParameterInfo[] pars, string[] names) + { + bool[] used = new bool[pars.Length]; + + // Mark which parameters have not been found in the names list + for (int i = 0; i < pars.Length; i++) + paramOrder[i] = -1; + // Find the parameters with names. + for (int i = 0; i < names.Length; i++) + { + int j; + for (j = 0; j < pars.Length; j++) + { + if (names[i].Equals(pars[j].Name)) + { + paramOrder[j] = i; + used[i] = true; + break; + } + } + // This is an error condition. The name was not found. This + // method must not match what we sent. + if (j == pars.Length) + return false; + } + + // Now we fill in the holes with the parameters that are unused. + int pos = 0; + for (int i = 0; i < pars.Length; i++) + { + if (paramOrder[i] == -1) + { + for (; pos < pars.Length; pos++) + { + if (!used[pos]) + { + paramOrder[i] = pos; + pos++; + break; + } + } + } + } + return true; + } + + internal class BinderState + { + internal readonly int[] _argsMap; + internal readonly int _originalSize; + internal readonly bool _isParamArray; + + internal BinderState(int[] argsMap, int originalSize, bool isParamArray) + { + _argsMap = argsMap; + _originalSize = originalSize; + _isParamArray = isParamArray; + } + } + } +} diff --git a/src/mscorlib/shared/System/Diagnostics/CodeAnalysis/SuppressMessageAttribute.cs b/src/mscorlib/shared/System/Diagnostics/CodeAnalysis/SuppressMessageAttribute.cs new file mode 100644 index 0000000000..893d7b8bc9 --- /dev/null +++ b/src/mscorlib/shared/System/Diagnostics/CodeAnalysis/SuppressMessageAttribute.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. + +/*============================================================ +** +** +** +** An attribute to suppress violation messages/warnings +** by static code analysis tools. +** +** +===========================================================*/ + +namespace System.Diagnostics.CodeAnalysis +{ + [AttributeUsage( + AttributeTargets.All, + Inherited = false, + AllowMultiple = true + ) + ] + [Conditional("CODE_ANALYSIS")] + public sealed class SuppressMessageAttribute : Attribute + { + public SuppressMessageAttribute(string category, string checkId) + { + Category = category; + CheckId = checkId; + } + + public string Category { get; } + public string CheckId { get; } + public string Scope { get; set; } + public string Target { get; set; } + public string MessageId { get; set; } + public string Justification { get; set; } + } +} diff --git a/src/mscorlib/shared/System/Diagnostics/ConditionalAttribute.cs b/src/mscorlib/shared/System/Diagnostics/ConditionalAttribute.cs new file mode 100644 index 0000000000..d5bca6e208 --- /dev/null +++ b/src/mscorlib/shared/System/Diagnostics/ConditionalAttribute.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.Diagnostics +{ + [Serializable] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)] + public sealed class ConditionalAttribute : Attribute + { + public ConditionalAttribute(string conditionString) + { + ConditionString = conditionString; + } + + public string ConditionString { get; } + } +} diff --git a/src/mscorlib/shared/System/Diagnostics/Debug.cs b/src/mscorlib/shared/System/Diagnostics/Debug.cs new file mode 100644 index 0000000000..59f3c378da --- /dev/null +++ b/src/mscorlib/shared/System/Diagnostics/Debug.cs @@ -0,0 +1,323 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// Do not remove this, it is needed to retain calls to these conditional methods in release builds +#define DEBUG + +namespace System.Diagnostics +{ + /// <summary> + /// Provides a set of properties and methods for debugging code. + /// </summary> + public static partial class Debug + { + private static readonly object s_lock = new object(); + + public static bool AutoFlush { get { return true; } set { } } + + [ThreadStatic] + private static int s_indentLevel; + public static int IndentLevel + { + get + { + return s_indentLevel; + } + set + { + s_indentLevel = value < 0 ? 0 : value; + } + } + + private static int s_indentSize = 4; + public static int IndentSize + { + get + { + return s_indentSize; + } + set + { + s_indentSize = value < 0 ? 0 : value; + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Close() { } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Flush() { } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Indent() + { + IndentLevel++; + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Unindent() + { + IndentLevel--; + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Print(string message) + { + Write(message); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Print(string format, params object[] args) + { + Write(string.Format(null, format, args)); + } + + private static readonly object s_ForLock = new Object(); + + [System.Diagnostics.Conditional("DEBUG")] + public static void Assert(bool condition) + { + Assert(condition, string.Empty, string.Empty); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Assert(bool condition, string message) + { + Assert(condition, message, string.Empty); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Assert(bool condition, string message, string detailMessage) + { + if (!condition) + { + string stackTrace; + + try + { + stackTrace = Internal.Runtime.Augments.EnvironmentAugments.StackTrace; + } + catch + { + stackTrace = ""; + } + + WriteLine(FormatAssert(stackTrace, message, detailMessage)); + s_ShowAssertDialog(stackTrace, message, detailMessage); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Fail(string message) + { + Assert(false, message, string.Empty); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Fail(string message, string detailMessage) + { + Assert(false, message, detailMessage); + } + + private static string FormatAssert(string stackTrace, string message, string detailMessage) + { + string newLine = GetIndentString() + Environment.NewLine; + return SR.DebugAssertBanner + newLine + + SR.DebugAssertShortMessage + newLine + + message + newLine + + SR.DebugAssertLongMessage + newLine + + detailMessage + newLine + + stackTrace; + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Assert(bool condition, string message, string detailMessageFormat, params object[] args) + { + Assert(condition, message, string.Format(detailMessageFormat, args)); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteLine(string message) + { + Write(message + Environment.NewLine); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Write(string message) + { + lock (s_lock) + { + if (message == null) + { + s_WriteCore(string.Empty); + return; + } + if (s_needIndent) + { + message = GetIndentString() + message; + s_needIndent = false; + } + s_WriteCore(message); + if (message.EndsWith(Environment.NewLine)) + { + s_needIndent = true; + } + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteLine(object value) + { + WriteLine(value?.ToString()); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteLine(object value, string category) + { + WriteLine(value?.ToString(), category); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteLine(string format, params object[] args) + { + WriteLine(string.Format(null, format, args)); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteLine(string message, string category) + { + if (category == null) + { + WriteLine(message); + } + else + { + WriteLine(category + ":" + message); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Write(object value) + { + Write(value?.ToString()); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Write(string message, string category) + { + if (category == null) + { + Write(message); + } + else + { + Write(category + ":" + message); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void Write(object value, string category) + { + Write(value?.ToString(), category); + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteIf(bool condition, string message) + { + if (condition) + { + Write(message); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteIf(bool condition, object value) + { + if (condition) + { + Write(value); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteIf(bool condition, string message, string category) + { + if (condition) + { + Write(message, category); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteIf(bool condition, object value, string category) + { + if (condition) + { + Write(value, category); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteLineIf(bool condition, object value) + { + if (condition) + { + WriteLine(value); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteLineIf(bool condition, object value, string category) + { + if (condition) + { + WriteLine(value, category); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteLineIf(bool condition, string message) + { + if (condition) + { + WriteLine(message); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + public static void WriteLineIf(bool condition, string message, string category) + { + if (condition) + { + WriteLine(message, category); + } + } + + private static bool s_needIndent; + + private static string s_indentString; + + private static string GetIndentString() + { + int indentCount = IndentSize * IndentLevel; + if (s_indentString?.Length == indentCount) + { + return s_indentString; + } + return s_indentString = new string(' ', indentCount); + } + + private sealed class DebugAssertException : Exception + { + internal DebugAssertException(string message, string detailMessage, string stackTrace) : + base(message + Environment.NewLine + detailMessage + Environment.NewLine + stackTrace) + { + } + } + + // internal and not readonly so that the tests can swap this out. + internal static Action<string, string, string> s_ShowAssertDialog = ShowAssertDialog; + internal static Action<string> s_WriteCore = WriteCore; + } +} diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/ActivityTracker.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/ActivityTracker.cs new file mode 100644 index 0000000000..e32abd2895 --- /dev/null +++ b/src/mscorlib/shared/System/Diagnostics/Tracing/ActivityTracker.cs @@ -0,0 +1,665 @@ +// 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; +using System.Threading; +#if !ES_BUILD_AGAINST_DOTNET_V35 +using Contract = System.Diagnostics.Contracts.Contract; +#else +using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract; +#endif + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +using System.Threading.Tasks; +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// Tracks activities. This is meant to be a singleton (accessed by the ActivityTracer.Instance static property) + /// + /// Logically this is simply holds the m_current variable that holds the async local that holds the current ActivityInfo + /// An ActivityInfo is represents a activity (which knows its creator and thus knows its path). + /// + /// Most of the magic is in the async local (it gets copied to new tasks) + /// + /// On every start event call OnStart + /// + /// Guid activityID; + /// Guid relatedActivityID; + /// if (OnStart(activityName, out activityID, out relatedActivityID, ForceStop, options)) + /// // Log Start event with activityID and relatedActivityID + /// + /// On every stop event call OnStop + /// + /// Guid activityID; + /// if (OnStop(activityName, ref activityID ForceStop)) + /// // Stop event with activityID + /// + /// On any normal event log the event with activityTracker.CurrentActivityId + /// </summary> + internal class ActivityTracker + { + + /// <summary> + /// Called on work item begins. The activity name = providerName + activityName without 'Start' suffix. + /// It updates CurrentActivityId to track. + /// + /// It returns true if the Start should be logged, otherwise (if it is illegal recursion) it return false. + /// + /// The start event should use as its activity ID the CurrentActivityId AFTER calling this routine and its + /// RelatedActivityID the CurrentActivityId BEFORE calling this routine (the creator). + /// + /// If activity tracing is not on, then activityId and relatedActivityId are not set + /// </summary> + public void OnStart(string providerName, string activityName, int task, ref Guid activityId, ref Guid relatedActivityId, EventActivityOptions options) + { + if (m_current == null) // We are not enabled + { + // We used to rely on the TPL provider turning us on, but that has the disadvantage that you don't get Start-Stop tracking + // until you use Tasks for the first time (which you may never do). Thus we change it to pull rather tan push for whether + // we are enabled. + if (m_checkedForEnable) + return; + m_checkedForEnable = true; + if (TplEtwProvider.Log.IsEnabled(EventLevel.Informational, TplEtwProvider.Keywords.TasksFlowActivityIds)) + Enable(); + if (m_current == null) + return; + } + + + Debug.Assert((options & EventActivityOptions.Disable) == 0); + + var currentActivity = m_current.Value; + var fullActivityName = NormalizeActivityName(providerName, activityName, task); + + var etwLog = TplEtwProvider.Log; + if (etwLog.Debug) + { + etwLog.DebugFacilityMessage("OnStartEnter", fullActivityName); + etwLog.DebugFacilityMessage("OnStartEnterActivityState", ActivityInfo.LiveActivities(currentActivity)); + } + + if (currentActivity != null) + { + // Stop activity tracking if we reached the maximum allowed depth + if (currentActivity.m_level >= MAX_ACTIVITY_DEPTH) + { + activityId = Guid.Empty; + relatedActivityId = Guid.Empty; + if (etwLog.Debug) + etwLog.DebugFacilityMessage("OnStartRET", "Fail"); + return; + } + // Check for recursion, and force-stop any activities if the activity already started. + if ((options & EventActivityOptions.Recursive) == 0) + { + ActivityInfo existingActivity = FindActiveActivity(fullActivityName, currentActivity); + if (existingActivity != null) + { + OnStop(providerName, activityName, task, ref activityId); + currentActivity = m_current.Value; + } + } + } + + // Get a unique ID for this activity. + long id; + if (currentActivity == null) + id = Interlocked.Increment(ref m_nextId); + else + id = Interlocked.Increment(ref currentActivity.m_lastChildID); + + // The previous ID is my 'causer' and becomes my related activity ID + relatedActivityId = EventSource.CurrentThreadActivityId; + + // Add to the list of started but not stopped activities. + ActivityInfo newActivity = new ActivityInfo(fullActivityName, id, currentActivity, relatedActivityId, options); + m_current.Value = newActivity; + + // Remember the current ID so we can log it + activityId = newActivity.ActivityId; + + if (etwLog.Debug) + { + etwLog.DebugFacilityMessage("OnStartRetActivityState", ActivityInfo.LiveActivities(newActivity)); + etwLog.DebugFacilityMessage1("OnStartRet", activityId.ToString(), relatedActivityId.ToString()); + } + } + + /// <summary> + /// Called when a work item stops. The activity name = providerName + activityName without 'Stop' suffix. + /// It updates m_current variable to track this fact. The Stop event associated with stop should log the ActivityID associated with the event. + /// + /// If activity tracing is not on, then activityId and relatedActivityId are not set + /// </summary> + public void OnStop(string providerName, string activityName, int task, ref Guid activityId) + { + if (m_current == null) // We are not enabled + return; + + var fullActivityName = NormalizeActivityName(providerName, activityName, task); + + var etwLog = TplEtwProvider.Log; + if (etwLog.Debug) + { + etwLog.DebugFacilityMessage("OnStopEnter", fullActivityName); + etwLog.DebugFacilityMessage("OnStopEnterActivityState", ActivityInfo.LiveActivities(m_current.Value)); + } + + for (; ; ) // This is a retry loop. + { + ActivityInfo currentActivity = m_current.Value; + ActivityInfo newCurrentActivity = null; // if we have seen any live activities (orphans), at he first one we have seen. + + // Search to find the activity to stop in one pass. This insures that we don't let one mistake + // (stopping something that was not started) cause all active starts to be stopped + // By first finding the target start to stop we are more robust. + ActivityInfo activityToStop = FindActiveActivity(fullActivityName, currentActivity); + + // ignore stops where we can't find a start because we may have popped them previously. + if (activityToStop == null) + { + activityId = Guid.Empty; + // TODO add some logging about this. Basically could not find matching start. + if (etwLog.Debug) + etwLog.DebugFacilityMessage("OnStopRET", "Fail"); + return; + } + + activityId = activityToStop.ActivityId; + + // See if there are any orphans that need to be stopped. + ActivityInfo orphan = currentActivity; + while (orphan != activityToStop && orphan != null) + { + if (orphan.m_stopped != 0) // Skip dead activities. + { + orphan = orphan.m_creator; + continue; + } + if (orphan.CanBeOrphan()) + { + // We can't pop anything after we see a valid orphan, remember this for later when we update m_current. + if (newCurrentActivity == null) + newCurrentActivity = orphan; + } + else + { + orphan.m_stopped = 1; + Debug.Assert(orphan.m_stopped != 0); + } + orphan = orphan.m_creator; + } + + // try to Stop the activity atomically. Other threads may be trying to do this as well. + if (Interlocked.CompareExchange(ref activityToStop.m_stopped, 1, 0) == 0) + { + // I succeeded stopping this activity. Now we update our m_current pointer + + // If I haven't yet determined the new current activity, it is my creator. + if (newCurrentActivity == null) + newCurrentActivity = activityToStop.m_creator; + + m_current.Value = newCurrentActivity; + + if (etwLog.Debug) + { + etwLog.DebugFacilityMessage("OnStopRetActivityState", ActivityInfo.LiveActivities(newCurrentActivity)); + etwLog.DebugFacilityMessage("OnStopRet", activityId.ToString()); + } + return; + } + // We failed to stop it. We must have hit a race to stop it. Just start over and try again. + } + } + + /// <summary> + /// Turns on activity tracking. It is sticky, once on it stays on (race issues otherwise) + /// </summary> + public void Enable() + { + if (m_current == null) + { + // Catch the not Implemented + try + { + m_current = new AsyncLocal<ActivityInfo>(ActivityChanging); + } + catch (NotImplementedException) { +#if (!ES_BUILD_PCL && ! ES_BUILD_PN) + // send message to debugger without delay + System.Diagnostics.Debugger.Log(0, null, "Activity Enabled() called but AsyncLocals Not Supported (pre V4.6). Ignoring Enable"); +#endif + } + } + } + + /// <summary> + /// An activity tracker is a singleton, this is how you get the one and only instance. + /// </summary> + public static ActivityTracker Instance { get { return s_activityTrackerInstance; } } + + + #region private + + /// <summary> + /// The current activity ID. Use this to log normal events. + /// </summary> + private Guid CurrentActivityId { get { return m_current.Value.ActivityId; } } + + /// <summary> + /// Searched for a active (nonstopped) activity with the given name. Returns null if not found. + /// </summary> + private ActivityInfo FindActiveActivity(string name, ActivityInfo startLocation) + { + var activity = startLocation; + while (activity != null) + { + if (name == activity.m_name && activity.m_stopped == 0) + return activity; + activity = activity.m_creator; + } + return null; + } + + /// <summary> + /// Strip out "Start" or "End" suffix from activity name and add providerName prefix. + /// If 'task' it does not end in Start or Stop and Task is non-zero use that as the name of the activity + /// </summary> + private string NormalizeActivityName(string providerName, string activityName, int task) + { + if (activityName.EndsWith(EventSource.s_ActivityStartSuffix, StringComparison.Ordinal)) + activityName = activityName.Substring(0, activityName.Length - EventSource.s_ActivityStartSuffix.Length); + else if (activityName.EndsWith(EventSource.s_ActivityStopSuffix, StringComparison.Ordinal)) + activityName = activityName.Substring(0, activityName.Length - EventSource.s_ActivityStopSuffix.Length); + else if (task != 0) + activityName = "task" + task.ToString(); + + // We use provider name to distinguish between activities from different providers. + return providerName + activityName; + } + + // ******************************************************************************* + /// <summary> + /// An ActivityInfo represents a particular activity. It is almost read-only. The only + /// fields that change after creation are + /// m_lastChildID - used to generate unique IDs for the children activities and for the most part can be ignored. + /// m_stopped - indicates that this activity is dead + /// This read-only-ness is important because an activity's m_creator chain forms the + /// 'Path of creation' for the activity (which is also its unique ID) but is also used as + /// the 'list of live parents' which indicate of those ancestors, which are alive (if they + /// are not marked dead they are alive). + /// </summary> + private class ActivityInfo + { + public ActivityInfo(string name, long uniqueId, ActivityInfo creator, Guid activityIDToRestore, EventActivityOptions options) + { + m_name = name; + m_eventOptions = options; + m_creator = creator; + m_uniqueId = uniqueId; + m_level = creator != null ? creator.m_level + 1 : 0; + m_activityIdToRestore = activityIDToRestore; + + // Create a nice GUID that encodes the chain of activities that started this one. + CreateActivityPathGuid(out m_guid, out m_activityPathGuidOffset); + } + + public Guid ActivityId + { + get + { + return m_guid; + } + } + + public static string Path(ActivityInfo activityInfo) + { + if (activityInfo == null) + return (""); + return Path(activityInfo.m_creator) + "/" + activityInfo.m_uniqueId.ToString(); + } + + public override string ToString() + { + return m_name + "(" + Path(this) + (m_stopped != 0 ? ",DEAD)" : ")"); + } + + public static string LiveActivities(ActivityInfo list) + { + if (list == null) + return ""; + return list.ToString() + ";" + LiveActivities(list.m_creator); + } + + public bool CanBeOrphan() + { + if ((m_eventOptions & EventActivityOptions.Detachable) != 0) + return true; + return false; + } + + #region private + + #region CreateActivityPathGuid + /// <summary> + /// Logically every activity Path (see Path()) that describes the activities that caused this + /// (rooted in an activity that predates activity tracking. + /// + /// We wish to encode this path in the Guid to the extent that we can. Many of the paths have + /// many small numbers in them and we take advantage of this in the encoding to output as long + /// a path in the GUID as possible. + /// + /// Because of the possibility of GUID collision, we only use 96 of the 128 bits of the GUID + /// for encoding the path. The last 32 bits are a simple checksum (and random number) that + /// identifies this as using the convention defined here. + /// + /// It returns both the GUID which has the path as well as the offset that points just beyond + /// the end of the activity (so it can be appended to). Note that if the end is in a nibble + /// (it uses nibbles instead of bytes as the unit of encoding, then it will point at the unfinished + /// byte (since the top nibble can't be zero you can determine if this is true by seeing if + /// this byte is nonZero. This offset is needed to efficiently create the ID for child activities. + /// </summary> + private unsafe void CreateActivityPathGuid(out Guid idRet, out int activityPathGuidOffset) + { + fixed (Guid* outPtr = &idRet) + { + int activityPathGuidOffsetStart = 0; + if (m_creator != null) + { + activityPathGuidOffsetStart = m_creator.m_activityPathGuidOffset; + idRet = m_creator.m_guid; + } + else + { + // TODO FIXME - differentiate between AD inside PCL + int appDomainID = 0; +#if (!ES_BUILD_STANDALONE && !ES_BUILD_PN) + appDomainID = System.Threading.Thread.GetDomainID(); +#endif + // We start with the appdomain number to make this unique among appdomains. + activityPathGuidOffsetStart = AddIdToGuid(outPtr, activityPathGuidOffsetStart, (uint)appDomainID); + } + + activityPathGuidOffset = AddIdToGuid(outPtr, activityPathGuidOffsetStart, (uint)m_uniqueId); + + + // If the path does not fit, Make a GUID by incrementing rather than as a path, keeping as much of the path as possible + if (12 < activityPathGuidOffset) + CreateOverflowGuid(outPtr); + } + } + + /// <summary> + /// If we can't fit the activity Path into the GUID we come here. What we do is simply + /// generate a 4 byte number (s_nextOverflowId). Then look for an ancestor that has + /// sufficient space for this ID. By doing this, we preserve the fact that this activity + /// is a child (of unknown depth) from that ancestor. + /// </summary> + private unsafe void CreateOverflowGuid(Guid* outPtr) + { + // Search backwards for an ancestor that has sufficient space to put the ID. + for (ActivityInfo ancestor = m_creator; ancestor != null; ancestor = ancestor.m_creator) + { + if (ancestor.m_activityPathGuidOffset <= 10) // we need at least 2 bytes. + { + uint id = unchecked((uint)Interlocked.Increment(ref ancestor.m_lastChildID)); // Get a unique ID + // Try to put the ID into the GUID + *outPtr = ancestor.m_guid; + int endId = AddIdToGuid(outPtr, ancestor.m_activityPathGuidOffset, id, true); + + // Does it fit? + if (endId <= 12) + break; + } + } + } + + /// <summary> + /// The encoding for a list of numbers used to make Activity GUIDs. Basically + /// we operate on nibbles (which are nice because they show up as hex digits). The + /// list is ended with a end nibble (0) and depending on the nibble value (Below) + /// the value is either encoded into nibble itself or it can spill over into the + /// bytes that follow. + /// </summary> + enum NumberListCodes : byte + { + End = 0x0, // ends the list. No valid value has this prefix. + LastImmediateValue = 0xA, + + PrefixCode = 0xB, // all the 'long' encodings go here. If the next nibble is MultiByte1-4 + // than this is a 'overflow' id. Unlike the hierarchical IDs these are + // allocated densely but don't tell you anything about nesting. we use + // these when we run out of space in the GUID to store the path. + + MultiByte1 = 0xC, // 1 byte follows. If this Nibble is in the high bits, it the high bits of the number are stored in the low nibble. + // commented out because the code does not explicitly reference the names (but they are logically defined). + // MultiByte2 = 0xD, // 2 bytes follow (we don't bother with the nibble optimization) + // MultiByte3 = 0xE, // 3 bytes follow (we don't bother with the nibble optimization) + // MultiByte4 = 0xF, // 4 bytes follow (we don't bother with the nibble optimization) + } + + /// Add the activity id 'id' to the output Guid 'outPtr' starting at the offset 'whereToAddId' + /// Thus if this number is 6 that is where 'id' will be added. This will return 13 (12 + /// is the maximum number of bytes that fit in a GUID) if the path did not fit. + /// If 'overflow' is true, then the number is encoded as an 'overflow number (which has a + /// special (longer prefix) that indicates that this ID is allocated differently + private static unsafe int AddIdToGuid(Guid* outPtr, int whereToAddId, uint id, bool overflow = false) + { + byte* ptr = (byte*)outPtr; + byte* endPtr = ptr + 12; + ptr += whereToAddId; + if (endPtr <= ptr) + return 13; // 12 means we might exactly fit, 13 means we definately did not fit + + if (0 < id && id <= (uint)NumberListCodes.LastImmediateValue && !overflow) + WriteNibble(ref ptr, endPtr, id); + else + { + uint len = 4; + if (id <= 0xFF) + len = 1; + else if (id <= 0xFFFF) + len = 2; + else if (id <= 0xFFFFFF) + len = 3; + + if (overflow) + { + if (endPtr <= ptr + 2) // I need at least 2 bytes + return 13; + + // Write out the prefix code nibble and the length nibble + WriteNibble(ref ptr, endPtr, (uint)NumberListCodes.PrefixCode); + } + // The rest is the same for overflow and non-overflow case + WriteNibble(ref ptr, endPtr, (uint)NumberListCodes.MultiByte1 + (len - 1)); + + // Do we have an odd nibble? If so flush it or use it for the 12 byte case. + if (ptr < endPtr && *ptr != 0) + { + // If the value < 4096 we can use the nibble we are otherwise just outputting as padding. + if (id < 4096) + { + // Indicate this is a 1 byte multicode with 4 high order bits in the lower nibble. + *ptr = (byte)(((uint)NumberListCodes.MultiByte1 << 4) + (id >> 8)); + id &= 0xFF; // Now we only want the low order bits. + } + ptr++; + } + + // Write out the bytes. + while (0 < len) + { + if (endPtr <= ptr) + { + ptr++; // Indicate that we have overflowed + break; + } + *ptr++ = (byte)id; + id = (id >> 8); + --len; + } + } + + // Compute the checksum + uint* sumPtr = (uint*)outPtr; + // We set the last DWORD the sum of the first 3 DWORDS in the GUID. This + sumPtr[3] = sumPtr[0] + sumPtr[1] + sumPtr[2] + 0x599D99AD; // This last number is a random number (it identifies us as us) + + return (int)(ptr - ((byte*)outPtr)); + } + + /// <summary> + /// Write a single Nible 'value' (must be 0-15) to the byte buffer represented by *ptr. + /// Will not go past 'endPtr'. Also it assumes that we never write 0 so we can detect + /// whether a nibble has already been written to ptr because it will be nonzero. + /// Thus if it is non-zero it adds to the current byte, otherwise it advances and writes + /// the new byte (in the high bits) of the next byte. + /// </summary> + private static unsafe void WriteNibble(ref byte* ptr, byte* endPtr, uint value) + { + Debug.Assert(0 <= value && value < 16); + Debug.Assert(ptr < endPtr); + + if (*ptr != 0) + *ptr++ |= (byte)value; + else + *ptr = (byte)(value << 4); + } + + #endregion // CreateGuidForActivityPath + + readonly internal string m_name; // The name used in the 'start' and 'stop' APIs to help match up + readonly long m_uniqueId; // a small number that makes this activity unique among its siblings + internal readonly Guid m_guid; // Activity Guid, it is basically an encoding of the Path() (see CreateActivityPathGuid) + internal readonly int m_activityPathGuidOffset; // Keeps track of where in m_guid the causality path stops (used to generated child GUIDs) + internal readonly int m_level; // current depth of the Path() of the activity (used to keep recursion under control) + readonly internal EventActivityOptions m_eventOptions; // Options passed to start. + internal long m_lastChildID; // used to create a unique ID for my children activities + internal int m_stopped; // This work item has stopped + readonly internal ActivityInfo m_creator; // My parent (creator). Forms the Path() for the activity. + readonly internal Guid m_activityIdToRestore; // The Guid to restore after a stop. + #endregion + } + + // This callback is used to initialize the m_current AsyncLocal Variable. + // Its job is to keep the ETW Activity ID (part of thread local storage) in sync + // with m_current.ActivityID + void ActivityChanging(AsyncLocalValueChangedArgs<ActivityInfo> args) + { + ActivityInfo cur = args.CurrentValue; + ActivityInfo prev = args.PreviousValue; + + // Are we popping off a value? (we have a prev, and it creator is cur) + // Then check if we should use the GUID at the time of the start event + if (prev != null && prev.m_creator == cur) + { + // If the saved activity ID is not the same as the creator activity + // that takes precedence (it means someone explicitly did a SetActivityID) + // Set it to that and get out + if (cur == null || prev.m_activityIdToRestore != cur.ActivityId) + { + EventSource.SetCurrentThreadActivityId(prev.m_activityIdToRestore); + return; + } + } + + // OK we did not have an explicit SetActivityID set. Then we should be + // setting the activity to current ActivityInfo. However that activity + // might be dead, in which case we should skip it, so we never set + // the ID to dead things. + while (cur != null) + { + // We found a live activity (typically the first time), set it to that. + if (cur.m_stopped == 0) + { + EventSource.SetCurrentThreadActivityId(cur.ActivityId); + return; + } + cur = cur.m_creator; + } + // we can get here if there is no information on our activity stack (everything is dead) + // currently we do nothing, as that seems better than setting to Guid.Emtpy. + } + + /// <summary> + /// Async local variables have the property that the are automatically copied whenever a task is created and used + /// while that task is running. Thus m_current 'flows' to any task that is caused by the current thread that + /// last set it. + /// + /// This variable points a a linked list that represents all Activities that have started but have not stopped. + /// </summary> + AsyncLocal<ActivityInfo> m_current; + bool m_checkedForEnable; + + // Singleton + private static ActivityTracker s_activityTrackerInstance = new ActivityTracker(); + + // Used to create unique IDs at the top level. Not used for nested Ids (each activity has its own id generator) + static long m_nextId = 0; + private const ushort MAX_ACTIVITY_DEPTH = 100; // Limit maximum depth of activities to be tracked at 100. + // This will avoid leaking memory in case of activities that are never stopped. + + #endregion + } + +#if ES_BUILD_STANDALONE || ES_BUILD_PN + /******************************** SUPPORT *****************************/ + /// <summary> + /// This is supplied by the framework. It is has the semantics that the value is copied to any new Tasks that is created + /// by the current task. Thus all causally related code gets this value. Note that reads and writes to this VARIABLE + /// (not what it points it) to this does not need to be protected by locks because it is inherently thread local (you always + /// only get your thread local copy which means that you never have races. + /// </summary> + /// +#if ES_BUILD_STANDALONE + [EventSource(Name = "Microsoft.Tasks.Nuget")] +#else + [EventSource(Name = "System.Diagnostics.Tracing.TplEtwProvider")] +#endif + internal class TplEtwProvider : EventSource + { + public class Keywords + { + public const EventKeywords TasksFlowActivityIds = (EventKeywords)0x80; + public const EventKeywords Debug = (EventKeywords)0x20000; + } + + public static TplEtwProvider Log = new TplEtwProvider(); + public bool Debug { get { return IsEnabled(EventLevel.Verbose, Keywords.Debug); } } + + public void DebugFacilityMessage(string Facility, string Message) { WriteEvent(1, Facility, Message); } + public void DebugFacilityMessage1(string Facility, string Message, string Arg) { WriteEvent(2, Facility, Message, Arg); } + public void SetActivityId(Guid Id) { WriteEvent(3, Id); } + } +#endif + +#if ES_BUILD_AGAINST_DOTNET_V35 || ES_BUILD_PCL || NO_ASYNC_LOCAL + // In these cases we don't have any Async local support. Do nothing. + internal sealed class AsyncLocalValueChangedArgs<T> + { + public T PreviousValue { get { return default(T); } } + public T CurrentValue { get { return default(T); } } + + } + + internal sealed class AsyncLocal<T> + { + public AsyncLocal(Action<AsyncLocalValueChangedArgs<T>> valueChangedHandler) { + throw new NotImplementedException("AsyncLocal only available on V4.6 and above"); + } + public T Value + { + get { return default(T); } + set { } + } + } +#endif + +} diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/EventActivityOptions.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/EventActivityOptions.cs new file mode 100644 index 0000000000..782afbf869 --- /dev/null +++ b/src/mscorlib/shared/System/Diagnostics/Tracing/EventActivityOptions.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. + +using System; + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// EventActivityOptions flags allow to specify different activity related characteristics. + /// </summary> + [Flags] + public enum EventActivityOptions + { + /// <summary> + /// No special options are added to the event. + /// </summary> + None = 0, + + /// <summary> + /// Disable Implicit Activity Tracking + /// </summary> + Disable = 0x2, + + /// <summary> + /// Allow activity event to call itself (directly or indirectly) + /// </summary> + Recursive = 0x4, + + /// <summary> + /// Allows event activity to live beyond its parent. + /// </summary> + Detachable = 0x8 + } +} diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/EventCounter.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/EventCounter.cs new file mode 100644 index 0000000000..b1f946464e --- /dev/null +++ b/src/mscorlib/shared/System/Diagnostics/Tracing/EventCounter.cs @@ -0,0 +1,436 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; +#if ES_BUILD_PCL + using System.Threading.Tasks; +#endif + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + /// <summary> + /// Provides the ability to collect statistics through EventSource + /// </summary> + public class EventCounter + { + /// <summary> + /// Initializes a new instance of the <see cref="EventCounter"/> class. + /// </summary> + /// <param name="name">The name.</param> + /// <param name="eventSource">The event source.</param> + public EventCounter(string name, EventSource eventSource) + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + if (eventSource == null) + { + throw new ArgumentNullException(nameof(eventSource)); + } + + InitializeBuffer(); + _name = name; + EventCounterGroup.AddEventCounter(eventSource, this); + } + + /// <summary> + /// Writes the metric. + /// </summary> + /// <param name="value">The value.</param> + public void WriteMetric(float value) + { + Enqueue(value); + } + + #region private implementation + + private readonly string _name; + + #region Buffer Management + + // Values buffering + private const int BufferedSize = 10; + private const float UnusedBufferSlotValue = float.NegativeInfinity; + private const int UnsetIndex = -1; + private volatile float[] _bufferedValues; + private volatile int _bufferedValuesIndex; + + private void InitializeBuffer() + { + _bufferedValues = new float[BufferedSize]; + for (int i = 0; i < _bufferedValues.Length; i++) + { + _bufferedValues[i] = UnusedBufferSlotValue; + } + } + + private void Enqueue(float value) + { + // It is possible that two threads read the same bufferedValuesIndex, but only one will be able to write the slot, so that is okay. + int i = _bufferedValuesIndex; + while (true) + { + float result = Interlocked.CompareExchange(ref _bufferedValues[i], value, UnusedBufferSlotValue); + i++; + if (_bufferedValues.Length <= i) + { + // It is possible that two threads both think the buffer is full, but only one get to actually flush it, the other + // will eventually enter this code path and potentially calling Flushing on a buffer that is not full, and that's okay too. + lock (_bufferedValues) + { + Flush(); + } + i = 0; + } + + if (result == UnusedBufferSlotValue) + { + // CompareExchange succeeded + _bufferedValuesIndex = i; + return; + } + } + } + + private void Flush() + { + for (int i = 0; i < _bufferedValues.Length; i++) + { + var value = Interlocked.Exchange(ref _bufferedValues[i], UnusedBufferSlotValue); + if (value != UnusedBufferSlotValue) + { + OnMetricWritten(value); + } + } + + _bufferedValuesIndex = 0; + } + + #endregion // Buffer Management + + #region Statistics Calculation + + // Statistics + private int _count; + private float _sum; + private float _sumSquared; + private float _min; + private float _max; + + private void OnMetricWritten(float value) + { + _sum += value; + _sumSquared += value * value; + if (_count == 0 || value > _max) + { + _max = value; + } + + if (_count == 0 || value < _min) + { + _min = value; + } + + _count++; + } + + internal EventCounterPayload GetEventCounterPayload() + { + lock (_bufferedValues) + { + Flush(); + EventCounterPayload result = new EventCounterPayload(); + result.Name = _name; + result.Count = _count; + result.Mean = _sum / _count; + result.StandardDerivation = (float)Math.Sqrt(_sumSquared / _count - _sum * _sum / _count / _count); + result.Min = _min; + result.Max = _max; + ResetStatistics(); + return result; + } + } + + private void ResetStatistics() + { + _count = 0; + _sum = 0; + _sumSquared = 0; + _min = 0; + _max = 0; + } + + #endregion // Statistics Calculation + + #endregion // private implementation + } + + #region internal supporting classes + + [EventData] + internal class EventCounterPayload : IEnumerable<KeyValuePair<string, object>> + { + public string Name { get; set; } + + public float Mean { get; set; } + + public float StandardDerivation { get; set; } + + public int Count { get; set; } + + public float Min { get; set; } + + public float Max { get; set; } + + public float IntervalSec { get; internal set; } + + #region Implementation of the IEnumerable interface + + public IEnumerator<KeyValuePair<string, object>> GetEnumerator() + { + return ForEnumeration.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ForEnumeration.GetEnumerator(); + } + + private IEnumerable<KeyValuePair<string, object>> ForEnumeration + { + get + { + yield return new KeyValuePair<string, object>("Name", Name); + yield return new KeyValuePair<string, object>("Mean", Mean); + yield return new KeyValuePair<string, object>("StandardDerivation", StandardDerivation); + yield return new KeyValuePair<string, object>("Count", Count); + yield return new KeyValuePair<string, object>("Min", Min); + yield return new KeyValuePair<string, object>("Max", Max); + } + } + + #endregion // Implementation of the IEnumerable interface + } + + internal class EventCounterGroup : IDisposable + { + private readonly EventSource _eventSource; + private readonly int _eventSourceIndex; + private readonly List<EventCounter> _eventCounters; + + internal EventCounterGroup(EventSource eventSource, int eventSourceIndex) + { + _eventSource = eventSource; + _eventSourceIndex = eventSourceIndex; + _eventCounters = new List<EventCounter>(); + RegisterCommandCallback(); + } + + private void Add(EventCounter eventCounter) + { + _eventCounters.Add(eventCounter); + } + + #region EventSource Command Processing + + private void RegisterCommandCallback() + { + _eventSource.EventCommandExecuted += OnEventSourceCommand; + } + + private void OnEventSourceCommand(object sender, EventCommandEventArgs e) + { + if (e.Command == EventCommand.Enable || e.Command == EventCommand.Update) + { + string valueStr; + float value; + if (e.Arguments.TryGetValue("EventCounterIntervalSec", out valueStr) && float.TryParse(valueStr, out value)) + { + EnableTimer(value); + } + } + } + + #endregion // EventSource Command Processing + + #region Global EventCounterGroup Array management + + private static EventCounterGroup[] s_eventCounterGroups; + + internal static void AddEventCounter(EventSource eventSource, EventCounter eventCounter) + { + int eventSourceIndex = EventListener.EventSourceIndex(eventSource); + + EventCounterGroup.EnsureEventSourceIndexAvailable(eventSourceIndex); + EventCounterGroup eventCounterGroup = GetEventCounterGroup(eventSource); + eventCounterGroup.Add(eventCounter); + } + + private static void EnsureEventSourceIndexAvailable(int eventSourceIndex) + { + if (EventCounterGroup.s_eventCounterGroups == null) + { + EventCounterGroup.s_eventCounterGroups = new EventCounterGroup[eventSourceIndex + 1]; + } + else if (eventSourceIndex >= EventCounterGroup.s_eventCounterGroups.Length) + { + EventCounterGroup[] newEventCounterGroups = new EventCounterGroup[eventSourceIndex + 1]; + Array.Copy(EventCounterGroup.s_eventCounterGroups, 0, newEventCounterGroups, 0, EventCounterGroup.s_eventCounterGroups.Length); + EventCounterGroup.s_eventCounterGroups = newEventCounterGroups; + } + } + + private static EventCounterGroup GetEventCounterGroup(EventSource eventSource) + { + int eventSourceIndex = EventListener.EventSourceIndex(eventSource); + EventCounterGroup result = EventCounterGroup.s_eventCounterGroups[eventSourceIndex]; + if (result == null) + { + result = new EventCounterGroup(eventSource, eventSourceIndex); + EventCounterGroup.s_eventCounterGroups[eventSourceIndex] = result; + } + + return result; + } + + #endregion // Global EventCounterGroup Array management + + #region Timer Processing + + private DateTime _timeStampSinceCollectionStarted; + private int _pollingIntervalInMilliseconds; + private Timer _pollingTimer; + + private void EnableTimer(float pollingIntervalInSeconds) + { + if (pollingIntervalInSeconds == 0) + { + if (_pollingTimer != null) + { + _pollingTimer.Dispose(); + _pollingTimer = null; + } + + _pollingIntervalInMilliseconds = 0; + } + else if (_pollingIntervalInMilliseconds == 0 || pollingIntervalInSeconds < _pollingIntervalInMilliseconds) + { + _pollingIntervalInMilliseconds = (int)(pollingIntervalInSeconds * 1000); + if (_pollingTimer != null) + { + _pollingTimer.Dispose(); + _pollingTimer = null; + } + + _timeStampSinceCollectionStarted = DateTime.Now; + _pollingTimer = new Timer(OnTimer, null, _pollingIntervalInMilliseconds, _pollingIntervalInMilliseconds); + } + } + + private void OnTimer(object state) + { + if (_eventSource.IsEnabled()) + { + DateTime now = DateTime.Now; + TimeSpan elapsed = now - _timeStampSinceCollectionStarted; + lock (_pollingTimer) + { + foreach (var eventCounter in _eventCounters) + { + EventCounterPayload payload = eventCounter.GetEventCounterPayload(); + payload.IntervalSec = (float)elapsed.TotalSeconds; + _eventSource.Write("EventCounters", new EventSourceOptions() { Level = EventLevel.LogAlways }, new { Payload = payload }); + } + + + _timeStampSinceCollectionStarted = now; + } + } + else + { + _pollingTimer.Dispose(); + _pollingTimer = null; + EventCounterGroup.s_eventCounterGroups[_eventSourceIndex] = null; + } + } + + #region PCL timer hack + +#if ES_BUILD_PCL + internal delegate void TimerCallback(object state); + + internal sealed class Timer : CancellationTokenSource, IDisposable + { + private int _period; + private TimerCallback _callback; + private object _state; + + internal Timer(TimerCallback callback, object state, int dueTime, int period) + { + _callback = callback; + _state = state; + _period = period; + Schedule(dueTime); + } + + private void Schedule(int dueTime) + { + Task.Delay(dueTime, Token).ContinueWith(OnTimer, null, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default); + } + + private void OnTimer(Task t, object s) + { + Schedule(_period); + _callback(_state); + } + + public new void Dispose() { base.Cancel(); } + } +#endif + #endregion // PCL timer hack + + #endregion // Timer Processing + + #region Implementation of the IDisposable interface + + private bool _disposed = false; + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + if (disposing) + { + if (_pollingTimer != null) + { + _pollingTimer.Dispose(); + _pollingTimer = null; + } + } + + _disposed = true; + } + + #endregion // Implementation of the IDisposable interface + } + + #endregion // internal supporting classes +} diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/EventDescriptor.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/EventDescriptor.cs new file mode 100644 index 0000000000..8fb471a99f --- /dev/null +++ b/src/mscorlib/shared/System/Diagnostics/Tracing/EventDescriptor.cs @@ -0,0 +1,209 @@ +// 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; + +#if ES_BUILD_STANDALONE +using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment; +#endif + +#if !ES_BUILD_AGAINST_DOTNET_V35 +using Contract = System.Diagnostics.Contracts.Contract; +#else +using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract; +#endif + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + [StructLayout(LayoutKind.Explicit, Size = 16)] +#if !CORECLR && !ES_BUILD_PN + [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)] +#endif // !CORECLR && !ES_BUILD_PN + + /* + EventDescriptor was public in the separate System.Diagnostics.Tracing assembly(pre NS2.0), + now the move to CoreLib marked them as private. + While they are technically private (it's a contract used between the library and the ILC toolchain), + we need them to be rooted and exported from shared library for the system to work. + For now I'm simply marking them as public again.A cleaner solution might be to use.rd.xml to + root them and modify shared library definition to force export them. + */ +#if ES_BUILD_PN + public +#else + internal +#endif + struct EventDescriptor + { + # region private + [FieldOffset(0)] + private int m_traceloggingId; + [FieldOffset(0)] + private ushort m_id; + [FieldOffset(2)] + private byte m_version; + [FieldOffset(3)] + private byte m_channel; + [FieldOffset(4)] + private byte m_level; + [FieldOffset(5)] + private byte m_opcode; + [FieldOffset(6)] + private ushort m_task; + [FieldOffset(8)] + private long m_keywords; + #endregion + + public EventDescriptor( + int traceloggingId, + byte level, + byte opcode, + long keywords + ) + { + this.m_id = 0; + this.m_version = 0; + this.m_channel = 0; + this.m_traceloggingId = traceloggingId; + this.m_level = level; + this.m_opcode = opcode; + this.m_task = 0; + this.m_keywords = keywords; + } + + public EventDescriptor( + int id, + byte version, + byte channel, + byte level, + byte opcode, + int task, + long keywords + ) + { + if (id < 0) + { + throw new ArgumentOutOfRangeException(nameof(id), Resources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + } + + if (id > ushort.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(id), Resources.GetResourceString("ArgumentOutOfRange_NeedValidId", 1, ushort.MaxValue)); + } + + m_traceloggingId = 0; + m_id = (ushort)id; + m_version = version; + m_channel = channel; + m_level = level; + m_opcode = opcode; + m_keywords = keywords; + + if (task < 0) + { + throw new ArgumentOutOfRangeException(nameof(task), Resources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + } + + if (task > ushort.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(task), Resources.GetResourceString("ArgumentOutOfRange_NeedValidId", 1, ushort.MaxValue)); + } + + m_task = (ushort)task; + } + + public int EventId + { + get + { + return m_id; + } + } + public byte Version + { + get + { + return m_version; + } + } + public byte Channel + { + get + { + return m_channel; + } + } + public byte Level + { + get + { + return m_level; + } + } + public byte Opcode + { + get + { + return m_opcode; + } + } + public int Task + { + get + { + return m_task; + } + } + public long Keywords + { + get + { + return m_keywords; + } + } + + public override bool Equals(object obj) + { + if (!(obj is EventDescriptor)) + return false; + + return Equals((EventDescriptor) obj); + } + + public override int GetHashCode() + { + return m_id ^ m_version ^ m_channel ^ m_level ^ m_opcode ^ m_task ^ (int)m_keywords; + } + + public bool Equals(EventDescriptor other) + { + if ((m_id != other.m_id) || + (m_version != other.m_version) || + (m_channel != other.m_channel) || + (m_level != other.m_level) || + (m_opcode != other.m_opcode) || + (m_task != other.m_task) || + (m_keywords != other.m_keywords)) + { + return false; + } + return true; + } + + public static bool operator ==(EventDescriptor event1, EventDescriptor event2) + { + return event1.Equals(event2); + } + + public static bool operator !=(EventDescriptor event1, EventDescriptor event2) + { + return !event1.Equals(event2); + } + } +} diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs new file mode 100644 index 0000000000..57d550dc8c --- /dev/null +++ b/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs @@ -0,0 +1,1207 @@ +// 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; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Security; +#if !CORECLR && !ES_BUILD_PN +using System.Security.Permissions; +#endif // !CORECLR && !ES_BUILD_PN +using System.Threading; +using System; + +#if !ES_BUILD_AGAINST_DOTNET_V35 +using Contract = System.Diagnostics.Contracts.Contract; +#else +using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract; +#endif + +#if ES_BUILD_AGAINST_DOTNET_V35 +using Microsoft.Internal; // for Tuple (can't define alias for open generic types so we "use" the whole namespace) +#endif + +#if ES_BUILD_STANDALONE +namespace Microsoft.Diagnostics.Tracing +#else +namespace System.Diagnostics.Tracing +#endif +{ + // New in CLR4.0 + internal enum ControllerCommand + { + // Strictly Positive numbers are for provider-specific commands, negative number are for 'shared' commands. 256 + // The first 256 negative numbers are reserved for the framework. + Update = 0, // Not used by EventPrividerBase. + SendManifest = -1, + Enable = -2, + Disable = -3, + }; + + /// <summary> + /// Only here because System.Diagnostics.EventProvider needs one more extensibility hook (when it gets a + /// controller callback) + /// </summary> +#if !CORECLR && !ES_BUILD_PN + [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)] +#endif // !CORECLR && !ES_BUILD_PN + internal partial class EventProvider : IDisposable + { + // This is the windows EVENT_DATA_DESCRIPTOR structure. We expose it because this is what + // subclasses of EventProvider use when creating efficient (but unsafe) version of + // EventWrite. We do make it a nested type because we really don't expect anyone to use + // it except subclasses (and then only rarely). + public struct EventData + { + internal unsafe ulong Ptr; + internal uint Size; + internal uint Reserved; + } + + /// <summary> + /// A struct characterizing ETW sessions (identified by the etwSessionId) as + /// activity-tracing-aware or legacy. A session that's activity-tracing-aware + /// has specified one non-zero bit in the reserved range 44-47 in the + /// 'allKeywords' value it passed in for a specific EventProvider. + /// </summary> + public struct SessionInfo + { + internal int sessionIdBit; // the index of the bit used for tracing in the "reserved" field of AllKeywords + internal int etwSessionId; // the machine-wide ETW session ID + + internal SessionInfo(int sessionIdBit_, int etwSessionId_) + { sessionIdBit = sessionIdBit_; etwSessionId = etwSessionId_; } + } + + private static bool m_setInformationMissing; + + UnsafeNativeMethods.ManifestEtw.EtwEnableCallback m_etwCallback; // Trace Callback function + private long m_regHandle; // Trace Registration Handle + private byte m_level; // Tracing Level + private long m_anyKeywordMask; // Trace Enable Flags + 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 + internal bool m_disposed; // when true provider has unregistered + + [ThreadStatic] + private static WriteEventErrorCode s_returnCode; // The last return code + + private const int s_basicTypeAllocationBufferSize = 16; + private const int s_etwMaxNumberArguments = 128; + private const int s_etwAPIMaxRefObjCount = 8; + private const int s_maxEventDataDescriptors = 128; + private const int s_traceEventMaximumSize = 65482; + private const int s_traceEventMaximumStringSize = 32724; + + [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] + public enum WriteEventErrorCode : int + { + //check mapping to runtime codes + NoError = 0, + NoFreeBuffers = 1, + EventTooBig = 2, + NullInput = 3, + TooManyArgs = 4, + Other = 5, + }; + + // Because callbacks happen on registration, and we need the callbacks for those setup + // we can't call Register in the constructor. + // + // Note that EventProvider should ONLY be used by EventSource. In particular because + // it registers a callback from native code you MUST dispose it BEFORE shutdown, otherwise + // you may get native callbacks during shutdown when we have destroyed the delegate. + // EventSource has special logic to do this, no one else should be calling EventProvider. + internal EventProvider() + { + } + + /// <summary> + /// This method registers the controlGuid of this class with ETW. We need to be running on + /// Vista or above. If not a PlatformNotSupported exception will be thrown. If for some + /// reason the ETW Register call failed a NotSupported exception will be thrown. + /// </summary> + // <SecurityKernel Critical="True" Ring="0"> + // <CallsSuppressUnmanagedCode Name="UnsafeNativeMethods.ManifestEtw.EventRegister(System.Guid&,Microsoft.Win32.UnsafeNativeMethods.ManifestEtw+EtwEnableCallback,System.Void*,System.Int64&):System.UInt32" /> + // <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) + { + m_providerId = providerGuid; + uint status; + m_etwCallback = new UnsafeNativeMethods.ManifestEtw.EtwEnableCallback(EtwEnableCallBack); + + status = EventRegister(ref m_providerId, m_etwCallback); + if (status != 0) + { + throw new ArgumentException(Win32Native.GetMessage(unchecked((int)status))); + } + } + + // + // implement Dispose Pattern to early deregister from ETW insted of waiting for + // the finalizer to call deregistration. + // Once the user is done with the provider it needs to call Close() or Dispose() + // If neither are called the finalizer will unregister the provider anyway + // + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + // <SecurityKernel Critical="True" TreatAsSafe="Does not expose critical resource" Ring="1"> + // <ReferencesCritical Name="Method: Deregister():Void" Ring="1" /> + // </SecurityKernel> + protected virtual void Dispose(bool disposing) + { + // + // explicit cleanup is done by calling Dispose with true from + // Dispose() or Close(). The disposing arguement is ignored because there + // are no unmanaged resources. + // The finalizer calls Dispose with false. + // + + // + // check if the object has been allready disposed + // + if (m_disposed) return; + + // Disable the provider. + m_enabled = false; + + // Do most of the work under a lock to avoid shutdown race. + + long registrationHandle = 0; + lock (EventListener.EventListenersLock) + { + // Double check + if (m_disposed) + return; + + registrationHandle = m_regHandle; + m_regHandle = 0; + m_disposed = true; + } + + // We do the Unregistration outside the EventListenerLock because there is a lock + // inside the ETW routines. This lock is taken before ETW issues commands + // Thus the ETW lock gets taken first and then our EventListenersLock gets taken + // in SendCommand(), and also here. If we called EventUnregister after taking + // the EventListenersLock then the take-lock order is reversed and we can have + // deadlocks in race conditions (dispose racing with an ETW command). + // + // We solve by Unregistering after releasing the EventListenerLock. + if (registrationHandle != 0) + EventUnregister(registrationHandle); + + } + + /// <summary> + /// This method deregisters the controlGuid of this class with ETW. + /// + /// </summary> + public virtual void Close() + { + Dispose(); + } + + ~EventProvider() + { + Dispose(false); + } + + // <SecurityKernel Critical="True" Ring="0"> + // <UsesUnsafeCode Name="Parameter filterData of type: Void*" /> + // <UsesUnsafeCode Name="Parameter callbackContext of type: Void*" /> + // </SecurityKernel> + unsafe void EtwEnableCallBack( + [In] ref System.Guid sourceId, + [In] int controlCode, + [In] byte setLevel, + [In] long anyKeyword, + [In] long allKeyword, + [In] UnsafeNativeMethods.ManifestEtw.EVENT_FILTER_DESCRIPTOR* filterData, + [In] void* callbackContext + ) + { + // This is an optional callback API. We will therefore ignore any failures that happen as a + // result of turning on this provider as to not crash the app. + // EventSource has code to validate whether initialization it expected to occur actually occurred + try + { + ControllerCommand command = ControllerCommand.Update; + IDictionary<string, string> args = null; + bool skipFinalOnControllerCommand = false; + if (controlCode == UnsafeNativeMethods.ManifestEtw.EVENT_CONTROL_CODE_ENABLE_PROVIDER) + { + m_enabled = true; + m_level = setLevel; + m_anyKeywordMask = anyKeyword; + m_allKeywordMask = allKeyword; + + // ES_SESSION_INFO is a marker for additional places we #ifdeffed out to remove + // references to EnumerateTraceGuidsEx. This symbol is actually not used because + // today we use FEATURE_ACTIVITYSAMPLING to determine if this code is there or not. + // However we put it in the #if so that we don't lose the fact that this feature + // switch is at least partially independent of FEATURE_ACTIVITYSAMPLING + + List<Tuple<SessionInfo, bool>> sessionsChanged = GetSessions(); + foreach (var session in sessionsChanged) + { + int sessionChanged = session.Item1.sessionIdBit; + int etwSessionId = session.Item1.etwSessionId; + bool bEnabling = session.Item2; + + skipFinalOnControllerCommand = true; + args = null; // reinitialize args for every session... + + // if we get more than one session changed we have no way + // of knowing which one "filterData" belongs to + |