diff options
author | Tanner Gooding <tagoo@outlook.com> | 2020-04-24 00:27:23 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-24 00:27:23 +0300 |
commit | 0ce44af8241fafb5c6080b111cc00391308db681 (patch) | |
tree | 4cd4e2cfb44fa5ba30281afbce6c9f4e7602e087 | |
parent | 88c069b819bdfb87afeea4f09c78577e56ea06fa (diff) |
[release/5.0-preview4] Add new IntPtr/UIntPtr API surface (#35248)
* Add new IntPtr/UIntPtr API surface (#307)
* Add new IntPtr surface
* Add new UIntPtr API surface
* Add sequential layout to match [U]Int32
* Add interfaces and sequential layout
* Add interfaces
* Add namespaces
* Add namespaces
* Update UIntPtr.cs
* Update IntPtr.cs
* Change style
* make non versionable, elide copy
* fix style, elide copy, make non versionable
* Fix syntax error
* Fix style issues
* Fix style issues
* Update IntPtr.cs
* Update UIntPtr.cs
* Update ref assembly
* Allow comparison of intptr/uintptr in Array
* Fixed ELEMENT_TYPE cases, added tests based on Int32/UInt32 tests
* Fixes
* Update Array.cs
* Update Array.cs
* Update Array.cs
* Update ArrayTests.cs
* Update ArrayTests.cs
* Update UIntPtrTests.cs
* Update ArrayTests.cs
* fix instance methods??
* fixes
* Fixwa
* fix tests
* Add non versionables
* fix compare methods
* Fix comparison error
* fix boundary
* fix compares
* fix maxvals
* remove xunit buggy data
* silly var name error
* Update src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
Co-Authored-By: Tanner Gooding <tagoo@outlook.com>
* Fix ToString
Co-authored-by: Tanner Gooding <tagoo@outlook.com>
* Remove the explicit IEquatable implementation from the IntPtr/UIntPtr reference API
Co-authored-by: John <johnkellyoxford@gmail.com>
8 files changed, 1238 insertions, 35 deletions
diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 74ea5902d7e..4fabff5d4eb 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -523,6 +523,9 @@ <data name="Arg_MustBeInt64" xml:space="preserve"> <value>Object must be of type Int64.</value> </data> + <data name="Arg_MustBeIntPtr" xml:space="preserve"> + <value>Object must be of type IntPtr.</value> + </data> <data name="Arg_MustBeInterface" xml:space="preserve"> <value>Type passed must be an interface.</value> </data> @@ -562,6 +565,9 @@ <data name="Arg_MustBeUInt64" xml:space="preserve"> <value>Object must be of type UInt64.</value> </data> + <data name="Arg_MustBeUIntPtr" xml:space="preserve"> + <value>Object must be of type UIntPtr.</value> + </data> <data name="Arg_MustBeVersion" xml:space="preserve"> <value>Object must be of type Version.</value> </data> diff --git a/src/libraries/System.Private.CoreLib/src/System/Array.cs b/src/libraries/System.Private.CoreLib/src/System/Array.cs index 5e22383eb1d..2411ba2546a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Array.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Array.cs @@ -510,9 +510,7 @@ namespace System if (comparer == Comparer.Default) { CorElementType et = array.GetCorElementTypeOfElementType(); - if (et.IsPrimitiveType() - // IntPtr/UIntPtr does not implement IComparable - && (et != CorElementType.ELEMENT_TYPE_I) && (et != CorElementType.ELEMENT_TYPE_U)) + if (et.IsPrimitiveType()) { if (value == null) return ~index; @@ -538,15 +536,27 @@ namespace System result = GenericBinarySearch<ushort>(array, adjustedIndex, length, value); break; case CorElementType.ELEMENT_TYPE_I4: +#if TARGET_32BIT + case CorElementType.ELEMENT_TYPE_I: +#endif result = GenericBinarySearch<int>(array, adjustedIndex, length, value); break; case CorElementType.ELEMENT_TYPE_U4: +#if TARGET_32BIT + case CorElementType.ELEMENT_TYPE_U: +#endif result = GenericBinarySearch<uint>(array, adjustedIndex, length, value); break; case CorElementType.ELEMENT_TYPE_I8: +#if TARGET_64BIT + case CorElementType.ELEMENT_TYPE_I: +#endif result = GenericBinarySearch<long>(array, adjustedIndex, length, value); break; case CorElementType.ELEMENT_TYPE_U8: +#if TARGET_64BIT + case CorElementType.ELEMENT_TYPE_U: +#endif result = GenericBinarySearch<ulong>(array, adjustedIndex, length, value); break; case CorElementType.ELEMENT_TYPE_R4: @@ -1674,15 +1684,27 @@ namespace System GenericSort<ushort>(keys, items, adjustedIndex, length); return; case CorElementType.ELEMENT_TYPE_I4: +#if TARGET_32BIT + case CorElementType.ELEMENT_TYPE_I: +#endif GenericSort<int>(keys, items, adjustedIndex, length); return; case CorElementType.ELEMENT_TYPE_U4: +#if TARGET_32BIT + case CorElementType.ELEMENT_TYPE_U: +#endif GenericSort<uint>(keys, items, adjustedIndex, length); return; case CorElementType.ELEMENT_TYPE_I8: +#if TARGET_64BIT + case CorElementType.ELEMENT_TYPE_I: +#endif GenericSort<long>(keys, items, adjustedIndex, length); return; case CorElementType.ELEMENT_TYPE_U8: +#if TARGET_64BIT + case CorElementType.ELEMENT_TYPE_U: +#endif GenericSort<ulong>(keys, items, adjustedIndex, length); return; case CorElementType.ELEMENT_TYPE_R4: @@ -1691,10 +1713,6 @@ namespace System case CorElementType.ELEMENT_TYPE_R8: GenericSort<double>(keys, items, adjustedIndex, length); return; - case CorElementType.ELEMENT_TYPE_I: - case CorElementType.ELEMENT_TYPE_U: - // IntPtr/UIntPtr does not implement IComparable - break; } static void GenericSort<T>(Array keys, Array? items, int adjustedIndex, int length) where T: struct diff --git a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs index e14dbc752f6..d82c18f1fa7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs @@ -4,8 +4,10 @@ using System.Globalization; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; +using Internal.Runtime.CompilerServices; #pragma warning disable SA1121 // explicitly using type aliases instead of built-in types #if TARGET_64BIT @@ -17,8 +19,9 @@ using nint = System.Int32; namespace System { [Serializable] + [StructLayout(LayoutKind.Sequential)] [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - public readonly struct IntPtr : IEquatable<IntPtr>, ISerializable + public readonly struct IntPtr : IEquatable<IntPtr>, IComparable, IComparable<IntPtr>, IFormattable, ISerializable { // WARNING: We allow diagnostic tools to directly inspect this member (_value). // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details. @@ -74,9 +77,6 @@ namespace System obj is IntPtr other && _value == other._value; - unsafe bool IEquatable<IntPtr>.Equals(IntPtr other) => - _value == other._value; - public override unsafe int GetHashCode() { #if TARGET_64BIT @@ -169,10 +169,61 @@ namespace System [NonVersionable] public unsafe void* ToPointer() => _value; - public override unsafe string ToString() => - ((nint)_value).ToString(CultureInfo.InvariantCulture); + public static IntPtr MaxValue + { + [NonVersionable] + get => (IntPtr)nint.MaxValue; + } + + public static IntPtr MinValue + { + [NonVersionable] + get => (IntPtr)nint.MinValue; + } + + // Don't just delegate to nint.CompareTo as it needs to throw when not IntPtr + public unsafe int CompareTo(object? value) + { + if (value is null) + { + return 1; + } + if (value is IntPtr o) + { + nint i = (nint)o; + if ((nint)_value < i) return -1; + if ((nint)_value > i) return 1; + return 0; + } + + throw new ArgumentException(SR.Arg_MustBeIntPtr); + } + + public unsafe int CompareTo(IntPtr value) => ((nint)_value).CompareTo((nint)value); + + [NonVersionable] + public unsafe bool Equals(IntPtr other) => (nint)_value == (nint)other; + + public unsafe override string ToString() => ((nint)_value).ToString(); + public unsafe string ToString(string? format) => ((nint)_value).ToString(format); + public unsafe string ToString(IFormatProvider? provider) => ((nint)_value).ToString(provider); + public unsafe string ToString(string? format, IFormatProvider? provider) => ((nint)_value).ToString(format, provider); + + public static IntPtr Parse(string s) => (IntPtr)nint.Parse(s); + public static IntPtr Parse(string s, NumberStyles style) => (IntPtr)nint.Parse(s, style); + public static IntPtr Parse(string s, IFormatProvider? provider) => (IntPtr)nint.Parse(s, provider); + public static IntPtr Parse(string s, NumberStyles style, IFormatProvider? provider) => (IntPtr)nint.Parse(s, style, provider); - public unsafe string ToString(string format) => - ((nint)_value).ToString(format, CultureInfo.InvariantCulture); + public static bool TryParse(string? s, out IntPtr result) + { + Unsafe.SkipInit(out result); + return nint.TryParse(s, out Unsafe.As<IntPtr, nint>(ref result)); + } + + public static bool TryParse(string? s, NumberStyles style, IFormatProvider? provider, out IntPtr result) + { + Unsafe.SkipInit(out result); + return nint.TryParse(s, style, provider, out Unsafe.As<IntPtr, nint>(ref result)); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs index 217e1566cb3..99eb0cf7590 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs @@ -4,8 +4,10 @@ using System.Globalization; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; +using Internal.Runtime.CompilerServices; #pragma warning disable SA1121 // explicitly using type aliases instead of built-in types #if TARGET_64BIT @@ -18,8 +20,9 @@ namespace System { [Serializable] [CLSCompliant(false)] + [StructLayout(LayoutKind.Sequential)] [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - public readonly struct UIntPtr : IEquatable<UIntPtr>, ISerializable + public readonly struct UIntPtr : IEquatable<UIntPtr>, IComparable, IComparable<UIntPtr>, IFormattable, ISerializable { private readonly unsafe void* _value; // Do not rename (binary serialization) @@ -75,9 +78,6 @@ namespace System return false; } - unsafe bool IEquatable<UIntPtr>.Equals(UIntPtr other) => - _value == other._value; - public override unsafe int GetHashCode() { #if TARGET_64BIT @@ -162,7 +162,60 @@ namespace System [NonVersionable] public unsafe void* ToPointer() => _value; - public override unsafe string ToString() => - ((nuint)_value).ToString(CultureInfo.InvariantCulture); + public static UIntPtr MaxValue + { + [NonVersionable] + get => (UIntPtr)nuint.MaxValue; + } + + public static UIntPtr MinValue + { + [NonVersionable] + get => (UIntPtr)nuint.MinValue; + } + + public unsafe int CompareTo(object? value) + { + if (value is null) + { + return 1; + } + if (value is UIntPtr o) + { + nuint i = (nuint)o; + if ((nuint)_value < i) return -1; + if ((nuint)_value > i) return 1; + return 0; + } + + throw new ArgumentException(SR.Arg_MustBeUIntPtr); + } + + public unsafe int CompareTo(UIntPtr value) => ((nuint)_value).CompareTo((nuint)value); + + [NonVersionable] + public unsafe bool Equals(UIntPtr other) => (nuint)_value == (nuint)other; + + public unsafe override string ToString() => ((nuint)_value).ToString(); + public unsafe string ToString(string? format) => ((nuint)_value).ToString(format); + public unsafe string ToString(IFormatProvider? provider) => ((nuint)_value).ToString(provider); + public unsafe string ToString(string? format, IFormatProvider? provider) => ((nuint)_value).ToString(format, provider); + + public static UIntPtr Parse(string s) => (UIntPtr)nuint.Parse(s); + public static UIntPtr Parse(string s, NumberStyles style) => (UIntPtr)nuint.Parse(s, style); + public static UIntPtr Parse(string s, IFormatProvider? provider) => (UIntPtr)nuint.Parse(s, provider); + public static UIntPtr Parse(string s, NumberStyles style, IFormatProvider? provider) => (UIntPtr)nuint.Parse(s, style, provider); + + public static bool TryParse(string? s, out UIntPtr result) + { + Unsafe.SkipInit(out result); + return nuint.TryParse(s, out Unsafe.As<UIntPtr, nuint>(ref result)); + } + + public static bool TryParse(string? s, NumberStyles style, IFormatProvider? provider, out UIntPtr result) + { + Unsafe.SkipInit(out result); + return nuint.TryParse(s, style, provider, out Unsafe.As<UIntPtr, nuint>(ref result)); + } } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 92c33cee8bb..e34f9431ec0 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -2388,7 +2388,7 @@ namespace System public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Int64 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.Int64 result) { throw null; } } - public readonly partial struct IntPtr : System.IEquatable<System.IntPtr>, System.Runtime.Serialization.ISerializable + public readonly partial struct IntPtr : System.IComparable, System.IComparable<System.IntPtr>, System.IEquatable<System.IntPtr>, System.IFormattable, System.Runtime.Serialization.ISerializable { private readonly int _dummyPrimitive; public static readonly System.IntPtr Zero; @@ -2396,8 +2396,13 @@ namespace System public IntPtr(long value) { throw null; } [System.CLSCompliantAttribute(false)] public unsafe IntPtr(void* value) { throw null; } + public static System.IntPtr MaxValue { get { throw null; } } + public static System.IntPtr MinValue { get { throw null; } } public static int Size { get { throw null; } } public static System.IntPtr Add(System.IntPtr pointer, int offset) { throw null; } + public int CompareTo(System.IntPtr value) { throw null; } + public int CompareTo(object? value) { throw null; } + public bool Equals(System.IntPtr other) { throw null; } public override bool Equals(object? obj) { throw null; } public override int GetHashCode() { throw null; } public static System.IntPtr operator +(System.IntPtr pointer, int offset) { throw null; } @@ -2412,15 +2417,22 @@ namespace System public unsafe static explicit operator System.IntPtr (void* value) { throw null; } public static bool operator !=(System.IntPtr value1, System.IntPtr value2) { throw null; } public static System.IntPtr operator -(System.IntPtr pointer, int offset) { throw null; } + public static System.IntPtr Parse(string s) { throw null; } + public static System.IntPtr Parse(string s, System.Globalization.NumberStyles style) { throw null; } + public static System.IntPtr Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + public static System.IntPtr Parse(string s, System.IFormatProvider? provider) { throw null; } public static System.IntPtr Subtract(System.IntPtr pointer, int offset) { throw null; } - bool System.IEquatable<System.IntPtr>.Equals(System.IntPtr other) { throw null; } void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public int ToInt32() { throw null; } public long ToInt64() { throw null; } [System.CLSCompliantAttribute(false)] public unsafe void* ToPointer() { throw null; } public override string ToString() { throw null; } - public string ToString(string format) { throw null; } + public string ToString(System.IFormatProvider? provider) { throw null; } + public string ToString(string? format) { throw null; } + public string ToString(string? format, System.IFormatProvider? provider) { throw null; } + public static bool TryParse(string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.IntPtr result) { throw null; } + public static bool TryParse(string? s, out System.IntPtr result) { throw null; } } public partial class InvalidCastException : System.SystemException { @@ -4342,16 +4354,21 @@ namespace System public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.UInt64 result) { throw null; } } [System.CLSCompliantAttribute(false)] - public readonly partial struct UIntPtr : System.IEquatable<System.UIntPtr>, System.Runtime.Serialization.ISerializable + public readonly partial struct UIntPtr : System.IComparable, System.IComparable<System.UIntPtr>, System.IEquatable<System.UIntPtr>, System.IFormattable, System.Runtime.Serialization.ISerializable { private readonly int _dummyPrimitive; public static readonly System.UIntPtr Zero; public UIntPtr(uint value) { throw null; } public UIntPtr(ulong value) { throw null; } public unsafe UIntPtr(void* value) { throw null; } + public static System.UIntPtr MaxValue { get { throw null; } } + public static System.UIntPtr MinValue { get { throw null; } } public static int Size { get { throw null; } } public static System.UIntPtr Add(System.UIntPtr pointer, int offset) { throw null; } + public int CompareTo(object? value) { throw null; } + public int CompareTo(System.UIntPtr value) { throw null; } public override bool Equals(object? obj) { throw null; } + public bool Equals(System.UIntPtr other) { throw null; } public override int GetHashCode() { throw null; } public static System.UIntPtr operator +(System.UIntPtr pointer, int offset) { throw null; } public static bool operator ==(System.UIntPtr value1, System.UIntPtr value2) { throw null; } @@ -4363,13 +4380,21 @@ namespace System public unsafe static explicit operator System.UIntPtr (void* value) { throw null; } public static bool operator !=(System.UIntPtr value1, System.UIntPtr value2) { throw null; } public static System.UIntPtr operator -(System.UIntPtr pointer, int offset) { throw null; } + public static System.UIntPtr Parse(string s) { throw null; } + public static System.UIntPtr Parse(string s, System.Globalization.NumberStyles style) { throw null; } + public static System.UIntPtr Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } + public static System.UIntPtr Parse(string s, System.IFormatProvider? provider) { throw null; } public static System.UIntPtr Subtract(System.UIntPtr pointer, int offset) { throw null; } - bool System.IEquatable<System.UIntPtr>.Equals(System.UIntPtr other) { throw null; } void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public unsafe void* ToPointer() { throw null; } public override string ToString() { throw null; } + public string ToString(System.IFormatProvider? provider) { throw null; } + public string ToString(string? format) { throw null; } + public string ToString(string? format, System.IFormatProvider? provider) { throw null; } public uint ToUInt32() { throw null; } public ulong ToUInt64() { throw null; } + public static bool TryParse(string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.UIntPtr result) { throw null; } + public static bool TryParse(string? s, out System.UIntPtr result) { throw null; } } public partial class UnauthorizedAccessException : System.SystemException { @@ -5402,10 +5427,10 @@ namespace System.Diagnostics public static void Close() { } [System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] [System.Diagnostics.ConditionalAttribute("DEBUG")] - public static void Fail(string? message) => throw null; + public static void Fail(string? message) => throw null; [System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] [System.Diagnostics.ConditionalAttribute("DEBUG")] - public static void Fail(string? message, string? detailMessage) => throw null; + public static void Fail(string? message, string? detailMessage) => throw null; [System.Diagnostics.ConditionalAttribute("DEBUG")] public static void Flush() { } [System.Diagnostics.ConditionalAttribute("DEBUG")] @@ -9196,7 +9221,7 @@ namespace System.Runtime.ExceptionServices [System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] public void Throw() => throw null; [System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] - public static void Throw(System.Exception source) => throw null; + public static void Throw(System.Exception source) => throw null; } public partial class FirstChanceExceptionEventArgs : System.EventArgs { diff --git a/src/libraries/System.Runtime/tests/System/ArrayTests.cs b/src/libraries/System.Runtime/tests/System/ArrayTests.cs index b3e350af78a..d18f0e5a2a9 100644 --- a/src/libraries/System.Runtime/tests/System/ArrayTests.cs +++ b/src/libraries/System.Runtime/tests/System/ArrayTests.cs @@ -264,6 +264,39 @@ namespace System.Tests yield return new object[] { new ulong[0], 0, 0, (ulong)0, null, -1 }; + // // [ActiveIssue("https://github.com/xunit/xunit/issues/1771")] + // // IntPtr + + // IntPtr[] intPtrArray = new IntPtr[] { IntPtr.MinValue, (IntPtr)0, (IntPtr)0, IntPtr.MaxValue }; + + // yield return new object[] { intPtrArray, 0, 4, IntPtr.MinValue, null, 0 }; + // yield return new object[] { intPtrArray, 0, 4, (IntPtr)0, null, 1 }; + // yield return new object[] { intPtrArray, 0, 4, IntPtr.MaxValue, null, 3 }; + // yield return new object[] { intPtrArray, 0, 4, (IntPtr)1, null, -4 }; + + // yield return new object[] { intPtrArray, 0, 1, IntPtr.MinValue, null, 0 }; + // yield return new object[] { intPtrArray, 1, 3, IntPtr.MaxValue, null, 3 }; + // yield return new object[] { intPtrArray, 1, 3, IntPtr.MinValue, null, -2 }; + // yield return new object[] { intPtrArray, 1, 0, (IntPtr)0, null, -2 }; + + // yield return new object[] { new IntPtr[0], 0, 0, (IntPtr)0, null, -1 }; + + // // UIntPtr + + // UIntPtr[] uintPtrArray = new UIntPtr[] { UIntPtr.MinValue, (UIntPtr)5, (UIntPtr)5, UIntPtr.MaxValue }; + + // yield return new object[] { uintPtrArray, 0, 4, UIntPtr.MinValue, null, 0 }; + // yield return new object[] { uintPtrArray, 0, 4, (UIntPtr)5, null, 1 }; + // yield return new object[] { uintPtrArray, 0, 4, UIntPtr.MaxValue, null, 3 }; + // yield return new object[] { uintPtrArray, 0, 4, (UIntPtr)1, null, -2 }; + + // yield return new object[] { uintPtrArray, 0, 1, UIntPtr.MinValue, null, 0 }; + // yield return new object[] { uintPtrArray, 1, 3, UIntPtr.MaxValue, null, 3 }; + // yield return new object[] { uintPtrArray, 1, 3, UIntPtr.MinValue, null, -2 }; + // yield return new object[] { uintPtrArray, 1, 0, (UIntPtr)5, null, -2 }; + + // yield return new object[] { new UIntPtr[0], 0, 0, (UIntPtr)0, null, -1 }; + // Char char[] charArray = new char[] { char.MinValue, (char)5, (char)5, char.MaxValue }; @@ -440,10 +473,6 @@ namespace System.Tests // Type does not implement IComparable yield return new object[] { new object[] { new object() }, new object() }; - // IntPtr and UIntPtr are not supported - yield return new object[] { new IntPtr[] { IntPtr.Zero }, IntPtr.Zero }; - yield return new object[] { new UIntPtr[] { UIntPtr.Zero }, UIntPtr.Zero }; - // Conversion between primitives is not allowed yield return new object[] { new sbyte[] { 0 }, 0 }; yield return new object[] { new char[] { '\0' }, (ushort)0 }; @@ -3326,6 +3355,24 @@ namespace System.Tests yield return new object[] { new ulong[1], 0, 1, null, new ulong[1] }; yield return new object[] { new ulong[0], 0, 0, null, new ulong[0] }; + // IntPtr + yield return new object[] { new IntPtr[] { (IntPtr)3, (IntPtr)5, (IntPtr)6, (IntPtr)6 }, 0, 4, null, new IntPtr[] { (IntPtr)3, (IntPtr)5, (IntPtr)6, (IntPtr)6 } }; + yield return new object[] { new IntPtr[] { (IntPtr)5, (IntPtr)6, (IntPtr)3, (IntPtr)6 }, 0, 4, null, new IntPtr[] { (IntPtr)3, (IntPtr)5, (IntPtr)6, (IntPtr)6 } }; + yield return new object[] { new IntPtr[] { (IntPtr)5, (IntPtr)6, (IntPtr)3, (IntPtr)6 }, 0, 4, null, new IntPtr[] { (IntPtr)3, (IntPtr)5, (IntPtr)6, (IntPtr)6 } }; + yield return new object[] { new IntPtr[] { (IntPtr)5, (IntPtr)6, (IntPtr)3, (IntPtr)6 }, 1, 2, null, new IntPtr[] { (IntPtr)5, (IntPtr)3, (IntPtr)6, (IntPtr)6 } }; + yield return new object[] { new IntPtr[] { (IntPtr)5, (IntPtr)6, (IntPtr)3, (IntPtr)6 }, 0, 0, null, new IntPtr[] { (IntPtr)5, (IntPtr)6, (IntPtr)3, (IntPtr)6 } }; + yield return new object[] { new IntPtr[1], 0, 1, null, new IntPtr[1] }; + yield return new object[] { new IntPtr[0], 0, 0, null, new IntPtr[0] }; + + // UIntPtr + yield return new object[] { new UIntPtr[] { (UIntPtr)3, (UIntPtr)5, (UIntPtr)6, (UIntPtr)6 }, 0, 4, null, new UIntPtr[] { (UIntPtr)3, (UIntPtr)5, (UIntPtr)6, (UIntPtr)6 } }; + yield return new object[] { new UIntPtr[] { (UIntPtr)5, (UIntPtr)6, (UIntPtr)3, (UIntPtr)6 }, 0, 4, null, new UIntPtr[] { (UIntPtr)3, (UIntPtr)5, (UIntPtr)6, (UIntPtr)6 } }; + yield return new object[] { new UIntPtr[] { (UIntPtr)5, (UIntPtr)6, (UIntPtr)3, (UIntPtr)6 }, 0, 4, null, new UIntPtr[] { (UIntPtr)3, (UIntPtr)5, (UIntPtr)6, (UIntPtr)6 } }; + yield return new object[] { new UIntPtr[] { (UIntPtr)5, (UIntPtr)6, (UIntPtr)3, (UIntPtr)6 }, 1, 2, null, new UIntPtr[] { (UIntPtr)5, (UIntPtr)3, (UIntPtr)6, (UIntPtr)6 } }; + yield return new object[] { new UIntPtr[] { (UIntPtr)5, (UIntPtr)6, (UIntPtr)3, (UIntPtr)6 }, 0, 0, null, new UIntPtr[] { (UIntPtr)5, (UIntPtr)6, (UIntPtr)3, (UIntPtr)6 } }; + yield return new object[] { new UIntPtr[1], 0, 1, null, new UIntPtr[1] }; + yield return new object[] { new UIntPtr[0], 0, 0, null, new UIntPtr[0] }; + // Int64 yield return new object[] { new long[] { 3, 5, 6, 6 }, 0, 4, null, new long[] { 3, 5, 6, 6 } }; yield return new object[] { new long[] { 5, 6, 3, 6 }, 0, 4, null, new long[] { 3, 5, 6, 6 } }; @@ -3493,8 +3540,6 @@ namespace System.Tests public static IEnumerable<object[]> Sort_NotComparable_TestData() { yield return new object[] { new object[] { "1", 2, new object() } }; - yield return new object[] { new IntPtr[2] }; - yield return new object[] { new UIntPtr[2] }; } [Theory] diff --git a/src/libraries/System.Runtime/tests/System/IntPtrTests.cs b/src/libraries/System.Runtime/tests/System/IntPtrTests.cs index 90bdf0c8e89..2744e8dd886 100644 --- a/src/libraries/System.Runtime/tests/System/IntPtrTests.cs +++ b/src/libraries/System.Runtime/tests/System/IntPtrTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using Xunit; namespace System.Tests @@ -177,5 +178,718 @@ namespace System.Tests Assert.False(ptr == new IntPtr(expected + 1)); Assert.True(ptr != new IntPtr(expected + 1)); } + + + public static IntPtr RealMax => Is64Bit ? (IntPtr)long.MaxValue : (IntPtr)int.MaxValue; + public static IntPtr RealMin => Is64Bit ? (IntPtr)long.MinValue : (IntPtr)int.MinValue; + + [Fact] + public static void Ctor_Empty() + { + var i = new IntPtr(); + Assert.Equal(default, i); + } + + [Fact] + public static void Ctor_Value() + { + IntPtr i = (IntPtr)41; + Assert.Equal((IntPtr)041, i); + } + + [Fact] + public static void MaxValue() + { + Assert.Equal(RealMax, IntPtr.MaxValue); + } + + [Fact] + public static void MinValue() + { + Assert.Equal(RealMin, IntPtr.MinValue); + } + + [Theory] + [InlineData(234, 234, 0)] + [InlineData(234, int.MinValue, 1)] + [InlineData(-234, int.MinValue, 1)] + [InlineData(int.MinValue, int.MinValue, 0)] + [InlineData(234, -123, 1)] + [InlineData(234, 0, 1)] + [InlineData(234, 123, 1)] + [InlineData(234, 456, -1)] + [InlineData(234, int.MaxValue, -1)] + [InlineData(-234, int.MaxValue, -1)] + [InlineData(int.MaxValue, int.MaxValue, 0)] + [InlineData(-234, -234, 0)] + [InlineData(-234, 234, -1)] + [InlineData(-234, -432, 1)] + [InlineData(234, null, 1)] + public static void CompareTo_Other_ReturnsExpected(int l, object value, int expected) + { + var i = (IntPtr)l; + if (value is int intValue) + { + var intPtrValue = (IntPtr)intValue; + Assert.Equal(expected, Math.Sign(i.CompareTo(intPtrValue))); + Assert.Equal(-expected, Math.Sign(intPtrValue.CompareTo(i))); + + Assert.Equal(expected, Math.Sign(i.CompareTo((object)intPtrValue))); + } + else + { + Assert.Equal(expected, Math.Sign(i.CompareTo(value))); + } + } + + [Theory] + [InlineData("a")] + [InlineData((long)234)] + public static void CompareTo_ObjectNotIntPtr_ThrowsArgumentException(object value) + { + AssertExtensions.Throws<ArgumentException>(null, () => ((IntPtr)123).CompareTo(value)); + } + + public static IEnumerable<object[]> ToString_TestData() + { + foreach (NumberFormatInfo defaultFormat in new[] { null, NumberFormatInfo.CurrentInfo }) + { + foreach (string defaultSpecifier in new[] { "G", "G\0", "\0N222", "\0", "" }) + { + yield return new object[] { IntPtr.MinValue, defaultSpecifier, defaultFormat, Is64Bit ? "-9223372036854775808" : "-2147483648" }; + yield return new object[] { (IntPtr)(-4567), defaultSpecifier, defaultFormat, "-4567" }; + yield return new object[] { (IntPtr)0, defaultSpecifier, defaultFormat, "0" }; + yield return new object[] { (IntPtr)4567, defaultSpecifier, defaultFormat, "4567" }; + yield return new object[] { IntPtr.MaxValue, defaultSpecifier, defaultFormat, Is64Bit ? "9223372036854775807" : "2147483647" }; + } + + yield return new object[] { (IntPtr)4567, "D", defaultFormat, "4567" }; + yield return new object[] { (IntPtr)4567, "D99", defaultFormat, "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004567" }; + yield return new object[] { (IntPtr)4567, "D99\09", defaultFormat, "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004567" }; + yield return new object[] { (IntPtr)(-4567), "D99\09", defaultFormat, "-000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004567" }; + + yield return new object[] { (IntPtr)0x2468, "x", defaultFormat, "2468" }; + yield return new object[] { (IntPtr)(-0x2468), "x", defaultFormat, Is64Bit ? "ffffffffffffdb98" : "ffffdb98" }; + yield return new object[] { (IntPtr)2468, "N", defaultFormat, string.Format("{0:N}", 2468.00) }; + } + + var customFormat = new NumberFormatInfo() + { + NegativeSign = "#", + NumberDecimalSeparator = "~", + NumberGroupSeparator = "*", + PositiveSign = "&", + NumberDecimalDigits = 2, + PercentSymbol = "@", + PercentGroupSeparator = ",", + PercentDecimalSeparator = ".", + PercentDecimalDigits = 5 + }; + yield return new object[] { (IntPtr)(-2468), "N", customFormat, "#2*468~00" }; + yield return new object[] { (IntPtr)2468, "N", customFormat, "2*468~00" }; + yield return new object[] { (IntPtr)123, "E", customFormat, "1~230000E&002" }; + yield return new object[] { (IntPtr)123, "F", customFormat, "123~00" }; + yield return new object[] { (IntPtr)123, "P", customFormat, "12,300.00000 @" }; + } + + [Theory] + [MemberData(nameof(ToString_TestData))] + public static void ToStringTest(IntPtr i, string format, IFormatProvider provider, string expected) + { + // Format is case insensitive + string upperFormat = format.ToUpperInvariant(); + string lowerFormat = format.ToLowerInvariant(); + + string upperExpected = expected.ToUpperInvariant(); + string lowerExpected = expected.ToLowerInvariant(); + + bool isDefaultProvider = (provider == null || provider == NumberFormatInfo.CurrentInfo); + if (string.IsNullOrEmpty(format) || format.ToUpperInvariant() == "G") + { + if (isDefaultProvider) + { + Assert.Equal(upperExpected, i.ToString()); + Assert.Equal(upperExpected, i.ToString((IFormatProvider)null)); + } + Assert.Equal(upperExpected, i.ToString(provider)); + } + if (isDefaultProvider) + { + Assert.Equal(upperExpected, i.ToString(upperFormat)); + Assert.Equal(lowerExpected, i.ToString(lowerFormat)); + Assert.Equal(upperExpected, i.ToString(upperFormat, null)); + Assert.Equal(lowerExpected, i.ToString(lowerFormat, null)); + } + Assert.Equal(upperExpected, i.ToString(upperFormat, provider)); + Assert.Equal(lowerExpected, i.ToString(lowerFormat, provider)); + } + + [Fact] + public static void ToString_InvalidFormat_ThrowsFormatException() + { + IntPtr i = (IntPtr)123; + Assert.Throws<FormatException>(() => i.ToString("r")); // Invalid format + Assert.Throws<FormatException>(() => i.ToString("r", null)); // Invalid format + Assert.Throws<FormatException>(() => i.ToString("R")); // Invalid format + Assert.Throws<FormatException>(() => i.ToString("R", null)); // Invalid format + Assert.Throws<FormatException>(() => i.ToString("Y")); // Invalid format + Assert.Throws<FormatException>(() => i.ToString("Y", null)); // Invalid format + } + + public static IEnumerable<object[]> Parse_Valid_TestData() + { + NumberFormatInfo samePositiveNegativeFormat = new NumberFormatInfo() + { + PositiveSign = "|", + NegativeSign = "|" + }; + + NumberFormatInfo emptyPositiveFormat = new NumberFormatInfo() { PositiveSign = "" }; + NumberFormatInfo emptyNegativeFormat = new NumberFormatInfo() { NegativeSign = "" }; + + // None + yield return new object[] { "0", NumberStyles.None, null, (IntPtr)0 }; + yield return new object[] { "0000000000000000000000000000000000000000000000000000000000", NumberStyles.None, null, (IntPtr)0 }; + yield return new object[] { "0000000000000000000000000000000000000000000000000000000001", NumberStyles.None, null, (IntPtr)1 }; + yield return new object[] { "2147483647", NumberStyles.None, null, (IntPtr)2147483647 }; + yield return new object[] { "02147483647", NumberStyles.None, null, (IntPtr)2147483647 }; + yield return new object[] { "00000000000000000000000000000000000000000000000002147483647", NumberStyles.None, null, (IntPtr)2147483647 }; + yield return new object[] { "123\0\0", NumberStyles.None, null, (IntPtr)123 }; + + // All lengths decimal + foreach (bool neg in new[] { false, true }) + { + string s = neg ? "-" : ""; + var result = 0; + for (var i = 1; i <= 10; i++) + { + result = result * 10 + (i % 10); + s += (i % 10).ToString(); + yield return new object[] { s, NumberStyles.Integer, null, (IntPtr)(neg ? result * -1 : result) }; + } + } + + // All lengths hexadecimal + { + string s = ""; + var result = 0; + for (var i = 1; i <= 8; i++) + { + result = (result * 16) + (i % 16); + s += (i % 16).ToString("X"); + yield return new object[] { s, NumberStyles.HexNumber, null, (IntPtr)result }; + } + } + + // HexNumber + yield return new object[] { "123", NumberStyles.HexNumber, null, (IntPtr)0x123 }; + yield return new object[] { "abc", NumberStyles.HexNumber, null, (IntPtr)0xabc }; + yield return new object[] { "ABC", NumberStyles.HexNumber, null, (IntPtr)0xabc }; + yield return new object[] { "12", NumberStyles.HexNumber, null, (IntPtr)0x12 }; + + + if (Is64Bit) + { + yield return new object[] { "8000000000000000", NumberStyles.HexNumber, null, IntPtr.MinValue }; + yield return new object[] { "7FFFFFFFFFFFFFFF", NumberStyles.HexNumber, null, IntPtr.MaxValue }; + } + else + { + yield return new object[] { "80000000", NumberStyles.HexNumber, null, IntPtr.MinValue }; + yield return new object[] { "7FFFFFFF", NumberStyles.HexNumber, null, IntPtr.MaxValue }; + } + + // Currency + NumberFormatInfo currencyFormat = new NumberFormatInfo() + { + CurrencySymbol = "$", + CurrencyGroupSeparator = "|", + NumberGroupSeparator = "/" + }; + yield return new object[] { "$1|000", NumberStyles.Currency, currencyFormat, (IntPtr)1000 }; + yield return new object[] { "$1000", NumberStyles.Currency, currencyFormat, (IntPtr)1000 }; + yield return new object[] { "$ 1000", NumberStyles.Currency, currencyFormat, (IntPtr)1000 }; + yield return new object[] { "1000", NumberStyles.Currency, currencyFormat, (IntPtr)1000 }; + yield return new object[] { "$(1000)", NumberStyles.Currency, currencyFormat, (IntPtr)(-1000) }; + yield return new object[] { "($1000)", NumberStyles.Currency, currencyFormat, (IntPtr)(-1000) }; + yield return new object[] { "$-1000", NumberStyles.Currency, currencyFormat, (IntPtr)(-1000) }; + yield return new object[] { "-$1000", NumberStyles.Currency, currencyFormat, (IntPtr)(-1000) }; + + NumberFormatInfo emptyCurrencyFormat = new NumberFormatInfo() { CurrencySymbol = "" }; + yield return new object[] { "100", NumberStyles.Currency, emptyCurrencyFormat, (IntPtr)100 }; + + // If CurrencySymbol and Negative are the same, NegativeSign is preferred + NumberFormatInfo sameCurrencyNegativeSignFormat = new NumberFormatInfo() + { + NegativeSign = "|", + CurrencySymbol = "|" + }; + yield return new object[] { "|1000", NumberStyles.AllowCurrencySymbol | NumberStyles.AllowLeadingSign, sameCurrencyNegativeSignFormat, (IntPtr)(-1000) }; + + // Any + yield return new object[] { "123", NumberStyles.Any, null, (IntPtr)123 }; + + // AllowLeadingSign + yield return new object[] { "-2147483648", NumberStyles.AllowLeadingSign, null, (IntPtr)(-2147483648) }; + yield return new object[] { "-123", NumberStyles.AllowLeadingSign, null, (IntPtr)(-123) }; + yield return new object[] { "+0", NumberStyles.AllowLeadingSign, null, (IntPtr)0 }; + yield return new object[] { "-0", NumberStyles.AllowLeadingSign, null, (IntPtr)0 }; + yield return new object[] { "+123", NumberStyles.AllowLeadingSign, null, (IntPtr)123 }; + + // If PositiveSign and NegativeSign are the same, PositiveSign is preferred + yield return new object[] { "|123", NumberStyles.AllowLeadingSign, samePositiveNegativeFormat, (IntPtr)123 }; + + // Empty PositiveSign or NegativeSign + yield return new object[] { "100", NumberStyles.AllowLeadingSign, emptyPositiveFormat, (IntPtr)100 }; + yield return new object[] { "100", NumberStyles.AllowLeadingSign, emptyNegativeFormat, (IntPtr)100 }; + + // AllowTrailingSign + yield return new object[] { "123", NumberStyles.AllowTrailingSign, null, (IntPtr)123 }; + yield return new object[] { "123+", NumberStyles.AllowTrailingSign, null, (IntPtr)123 }; + yield return new object[] { "123-", NumberStyles.AllowTrailingSign, null, (IntPtr)(-123) }; + + // If PositiveSign and NegativeSign are the same, PositiveSign is preferred + yield return new object[] { "123|", NumberStyles.AllowTrailingSign, samePositiveNegativeFormat, (IntPtr)123 }; + + // Empty PositiveSign or NegativeSign + yield return new object[] { "100", NumberStyles.AllowTrailingSign, emptyPositiveFormat, (IntPtr)100 }; + yield return new object[] { "100", NumberStyles.AllowTrailingSign, emptyNegativeFormat, (IntPtr)100 }; + + // AllowLeadingWhite and AllowTrailingWhite + yield return new object[] { " 123", NumberStyles.AllowLeadingWhite, null, (IntPtr)123 }; + yield return new object[] { " 123 ", NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, null, (IntPtr)123 }; + yield return new object[] { "123 ", NumberStyles.AllowTrailingWhite, null, (IntPtr)123 }; + yield return new object[] { "123 \0\0", NumberStyles.AllowTrailingWhite, null, (IntPtr)123 }; + yield return new object[] { " 2147483647 ", NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, null, (IntPtr)2147483647 }; + yield return new object[] { " -2147483648 ", NumberStyles.Integer, null, (IntPtr)(-2147483648) }; + foreach (char c in new[] { (char)0x9, (char)0xA, (char)0xB, (char)0xC, (char)0xD }) + { + string cs = c.ToString(); + yield return new object[] { cs + cs + "123" + cs + cs, NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, null, (IntPtr)123 }; + } + yield return new object[] { " 0 ", NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, null, (IntPtr)0 }; + yield return new object[] { " 000000000 ", NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, null, (IntPtr)0 }; + + // AllowThousands + NumberFormatInfo thousandsFormat = new NumberFormatInfo() { NumberGroupSeparator = "|" }; + yield return new object[] { "1000", NumberStyles.AllowThousands, thousandsFormat, (IntPtr)1000 }; + yield return new object[] { "1|0|0|0", NumberStyles.AllowThousands, thousandsFormat, (IntPtr)1000 }; + yield return new object[] { "1|||", NumberStyles.AllowThousands, thousandsFormat, (IntPtr)1 }; + + NumberFormatInfo IntegerNumberSeparatorFormat = new NumberFormatInfo() { NumberGroupSeparator = "1" }; + yield return new object[] { "1111", NumberStyles.AllowThousands, IntegerNumberSeparatorFormat, (IntPtr)1111 }; + + // AllowExponent + yield return new object[] { "1E2", NumberStyles.AllowExponent, null, (IntPtr)100 }; + yield return new object[] { "1E+2", NumberStyles.AllowExponent, null, (IntPtr)100 }; + yield return new object[] { "1e2", NumberStyles.AllowExponent, null, (IntPtr)100 }; + yield return new object[] { "1E0", NumberStyles.AllowExponent, null, (IntPtr)1 }; + yield return new object[] { "(1E2)", NumberStyles.AllowExponent | NumberStyles.AllowParentheses, null, (IntPtr)(-100) }; + yield return new object[] { "-1E2", NumberStyles.AllowExponent | NumberStyles.AllowLeadingSign, null, (IntPtr)(-100) }; + + NumberFormatInfo negativeFormat = new NumberFormatInfo() { PositiveSign = "|" }; + yield return new object[] { "1E|2", NumberStyles.AllowExponent, negativeFormat, (IntPtr)100 }; + + // AllowParentheses + yield return new object[] { "123", NumberStyles.AllowParentheses, null, (IntPtr)123 }; + yield return new object[] { "(123)", NumberStyles.AllowParentheses, null, (IntPtr)(-123) }; + + // AllowDecimalPoint + NumberFormatInfo decimalFormat = new NumberFormatInfo() { NumberDecimalSeparator = "|" }; + yield return new object[] { "67|", NumberStyles.AllowDecimalPoint, decimalFormat, (IntPtr)67 }; + + // NumberFormatInfo has a custom property with length > (IntPtr)1 + NumberFormatInfo IntegerCurrencyFormat = new NumberFormatInfo() { CurrencySymbol = "123" }; + yield return new object[] { "123123", NumberStyles.AllowCurrencySymbol, IntegerCurrencyFormat, (IntPtr)123 }; + + yield return new object[] { "123123", NumberStyles.AllowLeadingSign, new NumberFormatInfo() { PositiveSign = "1" }, (IntPtr)23123 }; + yield return new object[] { "123123", NumberStyles.AllowLeadingSign, new NumberFormatInfo() { NegativeSign = "1" }, (IntPtr)(-23123) }; + yield return new object[] { "123123", NumberStyles.AllowLeadingSign, new NumberFormatInfo() { PositiveSign = "123" }, (IntPtr)123 }; + yield return new object[] { "123123", NumberStyles.AllowLeadingSign, new NumberFormatInfo() { NegativeSign = "123" }, (IntPtr)(-123) }; + yield return new object[] { "123123", NumberStyles.AllowLeadingSign, new NumberFormatInfo() { PositiveSign = "12312" }, (IntPtr)3 }; + yield return new object[] { "123123", NumberStyles.AllowLeadingSign, new NumberFormatInfo() { NegativeSign = "12312" }, (IntPtr)(-3) }; + } + + [Theory] + [MemberData(nameof(Parse_Valid_TestData))] + public static void Parse_Valid(string value, NumberStyles style, IFormatProvider provider, IntPtr expected) + { + IntPtr result; + + // Default style and provider + if (style == NumberStyles.Integer && provider == null) + { + Assert.True(IntPtr.TryParse(value, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, IntPtr.Parse(value)); + } + + // Default provider + if (provider == null) + { + Assert.Equal(expected, IntPtr.Parse(value, style)); + + // Substitute default NumberFormatInfo + Assert.True(IntPtr.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, IntPtr.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, IntPtr.Parse(value, provider)); + } + + // Full overloads + Assert.True(IntPtr.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, IntPtr.Parse(value, style, provider)); + } + + public static IEnumerable<object[]> Parse_Invalid_TestData() + { + // String is null, empty or entirely whitespace + yield return new object[] { null, NumberStyles.Integer, null, typeof(ArgumentNullException) }; + yield return new object[] { null, NumberStyles.Any, null, typeof(ArgumentNullException) }; + + // String contains is null, empty or enitrely whitespace. + foreach (NumberStyles style in new[] { NumberStyles.Integer, NumberStyles.HexNumber, NumberStyles.Any }) + { + yield return new object[] { null, style, null, typeof(ArgumentNullException) }; + yield return new object[] { "", style, null, typeof(FormatException) }; + yield return new object[] { " \t \n \r ", style, null, typeof(FormatException) }; + yield return new object[] { " \0\0", style, null, typeof(FormatException) }; + } + + // Leading or trailing chars for which char.IsWhiteSpace is true but that's not valid for leading/trailing whitespace + foreach (string c in new[] { "\x0085", "\x00A0", "\x1680", "\x2000", "\x2001", "\x2002", "\x2003", "\x2004", "\x2005", "\x2006", "\x2007", "\x2008", "\x2009", "\x200A", "\x2028", "\x2029", "\x202F", "\x205F", "\x3000" }) + { + yield return new object[] { c + "123", NumberStyles.Integer, null, typeof(FormatException) }; + yield return new object[] { "123" + c, NumberStyles.Integer, null, typeof(FormatException) }; + } + + // String contains garbage + foreach (NumberStyles style in new[] { NumberStyles.Integer, NumberStyles.HexNumber, NumberStyles.Any }) + { + yield return new object[] { "Garbage", style, null, typeof(FormatException) }; + yield return new object[] { "g", style, null, typeof(FormatException) }; + yield return new object[] { "g1", style, null, typeof(FormatException) }; + yield return new object[] { "1g", style, null, typeof(FormatException) }; + yield return new object[] { "123g", style, null, typeof(FormatException) }; + yield return new object[] { "g123", style, null, typeof(FormatException) }; + yield return new object[] { "214748364g", style, null, typeof(FormatException) }; + } + + // String has leading zeros + yield return new object[] { "\0\0123", NumberStyles.Integer, null, typeof(FormatException) }; + yield return new object[] { "\0\0123", NumberStyles.Any, null, typeof(FormatException) }; + + // String has IntPtrernal zeros + yield return new object[] { "1\023", NumberStyles.Integer, null, typeof(FormatException) }; + yield return new object[] { "1\023", NumberStyles.Any, null, typeof(FormatException) }; + + // String has trailing zeros but with whitespace after + yield return new object[] { "123\0\0 ", NumberStyles.Integer, null, typeof(FormatException) }; + + // Integer doesn't allow hex, exponents, paretheses, currency, thousands, decimal + yield return new object[] { "abc", NumberStyles.Integer, null, typeof(FormatException) }; + yield return new object[] { "1E23", NumberStyles.Integer, null, typeof(FormatException) }; + yield return new object[] { "(123)", NumberStyles.Integer, null, typeof(FormatException) }; + yield return new object[] { 1000.ToString("C0"), NumberStyles.Integer, null, typeof(FormatException) }; + yield return new object[] { 1000.ToString("N0"), NumberStyles.Integer, null, typeof(FormatException) }; + yield return new object[] { 678.90.ToString("F2"), NumberStyles.Integer, null, typeof(FormatException) }; + + // HexNumber + yield return new object[] { "0xabc", NumberStyles.HexNumber, null, typeof(FormatException) }; + yield return new object[] { "&habc", NumberStyles.HexNumber, null, typeof(FormatException) }; + yield return new object[] { "G1", NumberStyles.HexNumber, null, typeof(FormatException) }; + yield return new object[] { "g1", NumberStyles.HexNumber, null, typeof(FormatException) }; + yield return new object[] { "+abc", NumberStyles.HexNumber, null, typeof(FormatException) }; + yield return new object[] { "-abc", NumberStyles.HexNumber, null, typeof(FormatException) }; + + // None doesn't allow hex or leading or trailing whitespace + yield return new object[] { "abc", NumberStyles.None, null, typeof(FormatException) }; + yield return new object[] { "123 ", NumberStyles.None, null, typeof(FormatException) }; + yield return new object[] { " 123", NumberStyles.None, null, typeof(FormatException) }; + yield return new object[] { " 123 ", NumberStyles.None, null, typeof(FormatException) }; + + // AllowLeadingSign + yield return new object[] { "+", NumberStyles.AllowLeadingSign, null, typeof(FormatException) }; + yield return new object[] { "-", NumberStyles.AllowLeadingSign, null, typeof(FormatException) }; + yield return new object[] { "+-123", NumberStyles.AllowLeadingSign, null, typeof(FormatException) }; + yield return new object[] { "-+123", NumberStyles.AllowLeadingSign, null, typeof(FormatException) }; + yield return new object[] { "- 123", NumberStyles.AllowLeadingSign, null, typeof(FormatException) }; + yield return new object[] { "+ 123", NumberStyles.AllowLeadingSign, null, typeof(FormatException) }; + + // AllowTrailingSign + yield return new object[] { "123-+", NumberStyles.AllowTrailingSign, null, typeof(FormatException) }; + yield return new object[] { "123+-", NumberStyles.AllowTrailingSign, null, typeof(FormatException) }; + yield return new object[] { "123 -", NumberStyles.AllowTrailingSign, null, typeof(FormatException) }; + yield return new object[] { "123 +", NumberStyles.AllowTrailingSign, null, typeof(FormatException) }; + + // Parentheses has priority over CurrencySymbol and PositiveSign + NumberFormatInfo currencyNegativeParenthesesFormat = new NumberFormatInfo() + { + CurrencySymbol = "(", + PositiveSign = "))" + }; + yield return new object[] { "(100))", NumberStyles.AllowParentheses | NumberStyles.AllowCurrencySymbol | NumberStyles.AllowTrailingSign, currencyNegativeParenthesesFormat, typeof(FormatException) }; + + // AllowTrailingSign and AllowLeadingSign + yield return new object[] { "+123+", NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingSign, null, typeof(FormatException) }; + yield return new object[] { "+123-", NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingSign, null, typeof(FormatException) }; + yield return new object[] { "-123+", NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingSign, null, typeof(FormatException) }; + yield return new object[] { "-123-", NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingSign, null, typeof(FormatException) }; + + // AllowLeadingSign and AllowParentheses + yield return new object[] { "-(1000)", NumberStyles.AllowLeadingSign | NumberStyles.AllowParentheses, null, typeof(FormatException) }; + yield return new object[] { "(-1000)", NumberStyles.AllowLeadingSign | NumberStyles.AllowParentheses, null, typeof(FormatException) }; + + // AllowLeadingWhite + yield return new object[] { "1 ", NumberStyles.AllowLeadingWhite, null, typeof(FormatException) }; + yield return new object[] { " 1 ", NumberStyles.AllowLeadingWhite, null, typeof(FormatException) }; + + // AllowTrailingWhite + yield return new object[] { " 1 ", NumberStyles.AllowTrailingWhite, null, typeof(FormatException) }; + yield return new object[] { " 1", NumberStyles.AllowTrailingWhite, null, typeof(FormatException) }; + + // AllowThousands + NumberFormatInfo thousandsFormat = new NumberFormatInfo() { NumberGroupSeparator = "|" }; + yield return new object[] { "|||1", NumberStyles.AllowThousands, null, typeof(FormatException) }; + + // AllowExponent + yield return new object[] { "65E", NumberStyles.AllowExponent, null, typeof(FormatException) }; + yield return new object[] { "65E19", NumberStyles.AllowExponent, null, typeof(OverflowException) }; + yield return new object[] { "65E+19", NumberStyles.AllowExponent, null, typeof(OverflowException) }; + yield return new object[] { "65E-1", NumberStyles.AllowExponent, null, typeof(OverflowException) }; + + // AllowDecimalPoint + NumberFormatInfo decimalFormat = new NumberFormatInfo() { NumberDecimalSeparator = "." }; + yield return new object[] { "67.9", NumberStyles.AllowDecimalPoint, decimalFormat, typeof(OverflowException) }; + + // Parsing Integers doesn't allow NaN, PositiveInfinity or NegativeInfinity + NumberFormatInfo doubleFormat = new NumberFormatInfo() + { + NaNSymbol = "NaN", + PositiveInfinitySymbol = "Infinity", + NegativeInfinitySymbol = "-Infinity" + }; + yield return new object[] { "NaN", NumberStyles.Any, doubleFormat, typeof(FormatException) }; + yield return new object[] { "Infinity", NumberStyles.Any, doubleFormat, typeof(FormatException) }; + yield return new object[] { "-Infinity", NumberStyles.Any, doubleFormat, typeof(FormatException) }; + + // Only has a leading sign + yield return new object[] { "+", NumberStyles.AllowLeadingSign, null, typeof(FormatException) }; + yield return new object[] { "-", NumberStyles.AllowLeadingSign, null, typeof(FormatException) }; + yield return new object[] { " +", NumberStyles.Integer, null, typeof(FormatException) }; + yield return new object[] { " -", NumberStyles.Integer, null, typeof(FormatException) }; + yield return new object[] { "+ ", NumberStyles.Integer, null, typeof(FormatException) }; + yield return new object[] { "- ", NumberStyles.Integer, null, typeof(FormatException) }; + + // NumberFormatInfo has a custom property with length > (IntPtr)1 + NumberFormatInfo IntegerCurrencyFormat = new NumberFormatInfo() { CurrencySymbol = "123" }; + yield return new object[] { "123", NumberStyles.AllowCurrencySymbol, IntegerCurrencyFormat, typeof(FormatException) }; + yield return new object[] { "123", NumberStyles.AllowLeadingSign, new NumberFormatInfo() { PositiveSign = "123" }, typeof(FormatException) }; + yield return new object[] { "123", NumberStyles.AllowLeadingSign, new NumberFormatInfo() { NegativeSign = "123" }, typeof(FormatException) }; + + // Decimals not in range of IntPtr32 + foreach (string s in new[] + { + + "9223372036854775808", // long.MaxValue + 1 + "9223372036854775810", // 10s digit incremented above long.MaxValue + "10000000000000000000", // extra digit after long.MaxValue + + "18446744073709551616", // ulong.MaxValue + 1 + "18446744073709551620", // 10s digit incremented above ulong.MaxValue + "100000000000000000000", // extra digit after ulong.MaxValue + + "-9223372036854775809", // long.MinValue - 1 + "-9223372036854775810", // 10s digit decremented below long.MinValue + "-10000000000000000000", // extra digit after long.MinValue + + "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000", // really big + "-100000000000000000000000000000000000000000000000000000000000000000000000000000000000000" // really small + }) + { + foreach (NumberStyles styles in new[] { NumberStyles.Any, NumberStyles.Integer }) + { + yield return new object[] { s, styles, null, typeof(OverflowException) }; + yield return new object[] { s + " ", styles, null, typeof(OverflowException) }; + yield return new object[] { s + " " + "\0\0\0", styles, null, typeof(OverflowException) }; + + yield return new object[] { s + "g", styles, null, typeof(FormatException) }; + yield return new object[] { s + "\0g", styles, null, typeof(FormatException) }; + yield return new object[] { s + " g", styles, null, typeof(FormatException) }; + } + } + + // Hexadecimals not in range of IntPtr32 + foreach (string s in new[] + { + "10000000000000000", // ulong.MaxValue + (IntPtr)1 + "FFFFFFFFFFFFFFFF0", // extra digit after ulong.MaxValue + + "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000" // really big + }) + { + yield return new object[] { s, NumberStyles.HexNumber, null, typeof(OverflowException) }; + yield return new object[] { s + " ", NumberStyles.HexNumber, null, typeof(OverflowException) }; + yield return new object[] { s + " " + "\0\0", NumberStyles.HexNumber, null, typeof(OverflowException) }; + + yield return new object[] { s + "g", NumberStyles.HexNumber, null, typeof(FormatException) }; + yield return new object[] { s + "\0g", NumberStyles.HexNumber, null, typeof(FormatException) }; + yield return new object[] { s + " g", NumberStyles.HexNumber, null, typeof(FormatException) }; + } + + yield return new object[] { "9223372036854775809-", NumberStyles.AllowTrailingSign, null, typeof(OverflowException) }; + yield return new object[] { "(9223372036854775809)", NumberStyles.AllowParentheses, null, typeof(OverflowException) }; + yield return new object[] { "2E19", NumberStyles.AllowExponent, null, typeof(OverflowException) }; + } + + [Theory] + [MemberData(nameof(Parse_Invalid_TestData))] + public static void Parse_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (2 == 2E10) { } + IntPtr result; + + // Default style and provider + if (style == NumberStyles.Integer && provider == null) + { + Assert.False(IntPtr.TryParse(value, out result)); + Assert.Equal(default, result); + Assert.Throws(exceptionType, () => IntPtr.Parse(value)); + } + + // Default provider + if (provider == null) + { + Assert.Throws(exceptionType, () => IntPtr.Parse(value, style)); + + // Substitute default NumberFormatInfo + Assert.False(IntPtr.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default, result); + Assert.Throws(exceptionType, () => IntPtr.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => IntPtr.Parse(value, provider)); + } + + // Full overloads + Assert.False(IntPtr.TryParse(value, style, provider, out result)); + Assert.Equal(default, result); + Assert.Throws(exceptionType, () => IntPtr.Parse(value, style, provider)); + } + + [Theory] + [InlineData(NumberStyles.HexNumber | NumberStyles.AllowParentheses, null)] + [InlineData(unchecked((NumberStyles)0xFFFFFC00), "style")] + public static void TryParse_InvalidNumberStyle_ThrowsArgumentException(NumberStyles style, string paramName) + { + IntPtr result = (IntPtr)0; + AssertExtensions.Throws<ArgumentException>(paramName, () => IntPtr.TryParse("1", style, null, out result)); + Assert.Equal(default(IntPtr), result); + + AssertExtensions.Throws<ArgumentException>(paramName, () => IntPtr.Parse("1", style)); + AssertExtensions.Throws<ArgumentException>(paramName, () => IntPtr.Parse("1", style, null)); + } + + [Theory] + [InlineData("N")] + [InlineData("F")] + public static void ToString_N_F_EmptyNumberGroup_Success(string specifier) + { + var nfi = (NumberFormatInfo)NumberFormatInfo.InvariantInfo.Clone(); + nfi.NumberGroupSizes = new int[0]; + nfi.NumberGroupSeparator = ","; + Assert.Equal("1234", ((IntPtr)1234).ToString($"{specifier}0", nfi)); + } + + [Fact] + public static void ToString_P_EmptyPercentGroup_Success() + { + var nfi = (NumberFormatInfo)NumberFormatInfo.InvariantInfo.Clone(); + nfi.PercentGroupSizes = new int[0]; + nfi.PercentSymbol = "%"; + Assert.Equal("123400 %", ((IntPtr)1234).ToString("P0", nfi)); + } + + [Fact] + public static void ToString_C_EmptyPercentGroup_Success() + { + var nfi = (NumberFormatInfo)NumberFormatInfo.InvariantInfo.Clone(); + nfi.CurrencyGroupSizes = new int[0]; + nfi.CurrencySymbol = "$"; + Assert.Equal("$1234", ((IntPtr)1234).ToString("C0", nfi)); + } + + public static IEnumerable<object[]> Parse_ValidWithOffsetCount_TestData() + { + foreach (object[] inputs in Parse_Valid_TestData()) + { + yield return new object[] { inputs[0], (IntPtr)0, ((string)inputs[0]).Length, inputs[1], inputs[2], inputs[3] }; + } + + NumberFormatInfo samePositiveNegativeFormat = new NumberFormatInfo() + { + PositiveSign = "|", + NegativeSign = "|" + }; + + NumberFormatInfo emptyPositiveFormat = new NumberFormatInfo() { PositiveSign = "" }; + NumberFormatInfo emptyNegativeFormat = new NumberFormatInfo() { NegativeSign = "" }; + + // None + yield return new object[] { "2147483647", (IntPtr)1, (IntPtr)9, NumberStyles.None, null, (IntPtr)147483647 }; + yield return new object[] { "2147483647", (IntPtr)1, (IntPtr)1, NumberStyles.None, null, (IntPtr)1 }; + yield return new object[] { "123\0\0", (IntPtr)2, (IntPtr)2, NumberStyles.None, null, (IntPtr)3 }; + + // Hex + yield return new object[] { "abc", (IntPtr)0, (IntPtr)1, NumberStyles.HexNumber, null, (IntPtr)0xa }; + yield return new object[] { "ABC", (IntPtr)1, (IntPtr)1, NumberStyles.HexNumber, null, (IntPtr)0xB }; + yield return new object[] { "FFFFFFFF", (IntPtr)6, (IntPtr)2, NumberStyles.HexNumber, null, (IntPtr)0xFF }; + yield return new object[] { "FFFFFFFF", (IntPtr)0, (IntPtr)1, NumberStyles.HexNumber, null, (IntPtr)0xF }; + + // Currency + yield return new object[] { "-$1000", (IntPtr)1, (IntPtr)5, NumberStyles.Currency, new NumberFormatInfo() + { + CurrencySymbol = "$", + CurrencyGroupSeparator = "|", + NumberGroupSeparator = "/" + }, (IntPtr)1000 }; + + NumberFormatInfo emptyCurrencyFormat = new NumberFormatInfo() { CurrencySymbol = "" }; + yield return new object[] { "100", (IntPtr)1, (IntPtr)2, NumberStyles.Currency, emptyCurrencyFormat, (IntPtr)0 }; + yield return new object[] { "100", (IntPtr)0, (IntPtr)1, NumberStyles.Currency, emptyCurrencyFormat, (IntPtr)1 }; + + // If CurrencySymbol and Negative are the same, NegativeSign is preferred + NumberFormatInfo sameCurrencyNegativeSignFormat = new NumberFormatInfo() + { + NegativeSign = "|", + CurrencySymbol = "|" + }; + yield return new object[] { "1000", (IntPtr)1, (IntPtr)3, NumberStyles.AllowCurrencySymbol | NumberStyles.AllowLeadingSign, sameCurrencyNegativeSignFormat, (IntPtr)0 }; + yield return new object[] { "|1000", (IntPtr)0, (IntPtr)2, NumberStyles.AllowCurrencySymbol | NumberStyles.AllowLeadingSign, sameCurrencyNegativeSignFormat, -1 }; + + // Any + yield return new object[] { "123", (IntPtr)0, (IntPtr)2, NumberStyles.Any, null, (IntPtr)12 }; + + // AllowLeadingSign + yield return new object[] { "-2147483648", (IntPtr)0, (IntPtr)10, NumberStyles.AllowLeadingSign, null, -214748364 }; + + // AllowTrailingSign + yield return new object[] { "123-", (IntPtr)0, (IntPtr)3, NumberStyles.AllowTrailingSign, null, (IntPtr)123 }; + + // AllowExponent + yield return new object[] { "1E2", (IntPtr)0, (IntPtr)1, NumberStyles.AllowExponent, null, (IntPtr)1 }; + yield return new object[] { "1E+2", (IntPtr)3, (IntPtr)1, NumberStyles.AllowExponent, null, (IntPtr)2 }; + yield return new object[] { "(1E2)", (IntPtr)1, (IntPtr)3, NumberStyles.AllowExponent | NumberStyles.AllowParentheses, null, (IntPtr)1E2 }; + yield return new object[] { "-1E2", (IntPtr)1, (IntPtr)3, NumberStyles.AllowExponent | NumberStyles.AllowLeadingSign, null, (IntPtr)1E2 }; + } } } diff --git a/src/libraries/System.Runtime/tests/System/UIntPtrTests.cs b/src/libraries/System.Runtime/tests/System/UIntPtrTests.cs index 5881af78988..07b3532f5dd 100644 --- a/src/libraries/System.Runtime/tests/System/UIntPtrTests.cs +++ b/src/libraries/System.Runtime/tests/System/UIntPtrTests.cs @@ -4,6 +4,8 @@ using System; using System.Collections.Generic; +using System.Globalization; +using System.Runtime.CompilerServices; using Xunit; namespace System.Tests @@ -172,5 +174,294 @@ namespace System.Tests Assert.False(ptr == new UIntPtr(expected + 1)); Assert.True(ptr != new UIntPtr(expected + 1)); } + + [Fact] + public static void Ctor_Empty() + { + var i = new UIntPtr(); + Assert.Equal((UIntPtr)0, i); + } + + [Fact] + public static void Ctor_Value() + { + UIntPtr i = (UIntPtr)41; + Assert.Equal((UIntPtr)41, i); + } + + [Fact] + public static void MaxValue() + { + Assert.Equal(UIntPtr.Size == 4 ? (UIntPtr)uint.MaxValue : (UIntPtr)ulong.MaxValue, UIntPtr.MaxValue); + } + + [Fact] + public static void MinValue() + { + Assert.Equal((UIntPtr)0, UIntPtr.MinValue); + } + + [Theory] + [InlineData(234u, 234u, 0)] + [InlineData(234u, uint.MinValue, 1)] + [InlineData(234u, 123u, 1)] + [InlineData(234u, 456u, -1)] + [InlineData(234u, uint.MaxValue, -1)] + [InlineData(234u, null, 1)] + public static void CompareTo_Other_ReturnsExpected(uint i0, object value, int expected) + { + var i = (UIntPtr)i0; + if (value is uint uintValue) + { + var uintPtrValue = (UIntPtr)uintValue; + Assert.Equal(expected, Math.Sign(i.CompareTo(uintPtrValue))); + + Assert.Equal(expected, Math.Sign(i.CompareTo((object)uintPtrValue))); + } + else + { + Assert.Equal(expected, Math.Sign(i.CompareTo(value))); + } + } + + [Theory] + [InlineData("a")] + [InlineData(234)] + public static void CompareTo_ObjectNotUIntPtr_ThrowsArgumentException(object value) + { + AssertExtensions.Throws<ArgumentException>(null, () => ((UIntPtr)123).CompareTo(value)); + } + + public static IEnumerable<object[]> ToString_TestData() + { + foreach (NumberFormatInfo defaultFormat in new[] { null, NumberFormatInfo.CurrentInfo }) + { + foreach (string defaultSpecifier in new[] { "G", "G\0", "\0N222", "\0", "" }) + { + yield return new object[] { (UIntPtr)0, defaultSpecifier, defaultFormat, "0" }; + yield return new object[] { (UIntPtr)4567, defaultSpecifier, defaultFormat, "4567" }; + yield return new object[] { UIntPtr.MaxValue, defaultSpecifier, defaultFormat, Is64Bit ? "18446744073709551615" : "4294967295" }; + } + + yield return new object[] { (UIntPtr)4567, "D", defaultFormat, "4567" }; + yield return new object[] { (UIntPtr)4567, "D18", defaultFormat, "000000000000004567" }; + + yield return new object[] { (UIntPtr)0x2468, "x", defaultFormat, "2468" }; + yield return new object[] { (UIntPtr)2468, "N", defaultFormat, string.Format("{0:N}", 2468.00) }; + } + + var customFormat = new NumberFormatInfo() + { + NegativeSign = "#", + NumberDecimalSeparator = "~", + NumberGroupSeparator = "*", + PositiveSign = "&", + NumberDecimalDigits = 2, + PercentSymbol = "@", + PercentGroupSeparator = ",", + PercentDecimalSeparator = ".", + PercentDecimalDigits = 5 + }; + yield return new object[] { (UIntPtr)2468, "N", customFormat, "2*468~00" }; + yield return new object[] { (UIntPtr)123, "E", customFormat, "1~230000E&002" }; + yield return new object[] { (UIntPtr)123, "F", customFormat, "123~00" }; + yield return new object[] { (UIntPtr)123, "P", customFormat, "12,300.00000 @" }; + } + + [Theory] + [MemberData(nameof(ToString_TestData))] + public static void ToStringTest(UIntPtr i, string format, IFormatProvider provider, string expected) + { + // Format is case insensitive + string upperFormat = format.ToUpperInvariant(); + string lowerFormat = format.ToLowerInvariant(); + + string upperExpected = expected.ToUpperInvariant(); + string lowerExpected = expected.ToLowerInvariant(); + + bool isDefaultProvider = (provider == null || provider == NumberFormatInfo.CurrentInfo); + if (string.IsNullOrEmpty(format) || format.ToUpperInvariant() == "G") + { + if (isDefaultProvider) + { + Assert.Equal(upperExpected, i.ToString()); + Assert.Equal(upperExpected, i.ToString((IFormatProvider)null)); + } + Assert.Equal(upperExpected, i.ToString(provider)); + } + if (isDefaultProvider) + { + Assert.Equal(upperExpected, i.ToString(upperFormat)); + Assert.Equal(lowerExpected, i.ToString(lowerFormat)); + Assert.Equal(upperExpected, i.ToString(upperFormat, null)); + Assert.Equal(lowerExpected, i.ToString(lowerFormat, null)); + } + Assert.Equal(upperExpected, i.ToString(upperFormat, provider)); + Assert.Equal(lowerExpected, i.ToString(lowerFormat, provider)); + } + + [Fact] + public static void ToString_InvalidFormat_ThrowsFormatException() + { + UIntPtr i = (UIntPtr)123; + Assert.Throws<FormatException>(() => i.ToString("r")); // Invalid format + Assert.Throws<FormatException>(() => i.ToString("r", null)); // Invalid format + Assert.Throws<FormatException>(() => i.ToString("R")); // Invalid format + Assert.Throws<FormatException>(() => i.ToString("R", null)); // Invalid format + Assert.Throws<FormatException>(() => i.ToString("Y")); // Invalid format + Assert.Throws<FormatException>(() => i.ToString("Y", null)); // Invalid format + } + + public static IEnumerable<object[]> Parse_Valid_TestData() + { + // Reuse all IntPtr test data that's relevant + foreach (object[] objs in IntPtrTests.Parse_Valid_TestData()) + { + if ((long)(IntPtr)objs[3] < 0) continue; + var intPtr = (IntPtr)objs[3]; + yield return new object[] { objs[0], objs[1], objs[2], Unsafe.As<IntPtr, UIntPtr>(ref intPtr) }; + } + + // All lengths decimal + { + string s = ""; + uint result = 0; + for (int i = 1; i <= 10; i++) + { + result = (uint)(result * 10 + (i % 10)); + s += (i % 10).ToString(); + yield return new object[] { s, NumberStyles.Integer, null, (UIntPtr)result }; + } + } + + // All lengths hexadecimal + { + string s = ""; + uint result = 0; + for (uint i = 1; i <= 8; i++) + { + result = ((result * 16) + (i % 16)); + s += (i % 16).ToString("X"); + yield return new object[] { s, NumberStyles.HexNumber, null, result }; + } + } + + // And test boundary conditions for IntPtr + yield return new object[] { Is64Bit ? "18446744073709551615" : "4294967295", NumberStyles.Integer, null, UIntPtr.MaxValue }; + yield return new object[] { Is64Bit ? "+18446744073709551615" : "+4294967295", NumberStyles.Integer, null, UIntPtr.MaxValue }; + yield return new object[] { Is64Bit ? " +18446744073709551615 " : " +4294967295 ", NumberStyles.Integer, null, UIntPtr.MaxValue }; + yield return new object[] { Is64Bit ? "FFFFFFFFFFFFFFFF" : "FFFFFFFF", NumberStyles.HexNumber, null, UIntPtr.MaxValue }; + yield return new object[] { Is64Bit ? " FFFFFFFFFFFFFFFF " : " FFFFFFFF ", NumberStyles.HexNumber, null, UIntPtr.MaxValue }; + } + + [Theory] + [MemberData(nameof(Parse_Valid_TestData))] + public static void Parse_Valid(string value, NumberStyles style, IFormatProvider provider, UIntPtr expected) + { + UIntPtr result; + + // Default style and provider + if (style == NumberStyles.Integer && provider == null) + { + Assert.True(UIntPtr.TryParse(value, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, UIntPtr.Parse(value)); + } + + // Default provider + if (provider == null) + { + Assert.Equal(expected, UIntPtr.Parse(value, style)); + + // Substitute default NumberFormatInfo + Assert.True(UIntPtr.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, UIntPtr.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Equal(expected, UIntPtr.Parse(value, provider)); + } + + // Full overloads + Assert.True(UIntPtr.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + Assert.Equal(expected, UIntPtr.Parse(value, style, provider)); + } + + public static IEnumerable<object[]> Parse_Invalid_TestData() + { + // > max value + yield return new object[] { "18446744073709551616", NumberStyles.Integer, null, typeof(OverflowException) }; + yield return new object[] { "10000000000000000", NumberStyles.HexNumber, null, typeof(OverflowException) }; + } + + [Theory] + [MemberData(nameof(Parse_Invalid_TestData))] + public static void Parse_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + UIntPtr result; + + // Default style and provider + if (style == NumberStyles.Integer && provider == null) + { + Assert.False(UIntPtr.TryParse(value, out result)); + Assert.Equal(default, result); + Assert.Throws(exceptionType, () => UIntPtr.Parse(value)); + } + + // Default provider + if (provider == null) + { + Assert.Throws(exceptionType, () => UIntPtr.Parse(value, style)); + + // Substitute default NumberFormatInfo + Assert.False(UIntPtr.TryParse(value, style, new NumberFormatInfo(), out result)); + Assert.Equal(default, result); + Assert.Throws(exceptionType, () => UIntPtr.Parse(value, style, new NumberFormatInfo())); + } + + // Default style + if (style == NumberStyles.Integer) + { + Assert.Throws(exceptionType, () => UIntPtr.Parse(value, provider)); + } + + // Full overloads + Assert.False(UIntPtr.TryParse(value, style, provider, out result)); + Assert.Equal(default, result); + Assert.Throws(exceptionType, () => UIntPtr.Parse(value, style, provider)); + } + + [Theory] + [InlineData(NumberStyles.HexNumber | NumberStyles.AllowParentheses, null)] + [InlineData(unchecked((NumberStyles)0xFFFFFC00), "style")] + public static void TryParse_InvalidNumberStyle_ThrowsArgumentException(NumberStyles style, string paramName) + { + UIntPtr result = (UIntPtr)0; + AssertExtensions.Throws<ArgumentException>(paramName, () => UIntPtr.TryParse("1", style, null, out result)); + Assert.Equal(default(UIntPtr), result); + + AssertExtensions.Throws<ArgumentException>(paramName, () => UIntPtr.Parse("1", style)); + AssertExtensions.Throws<ArgumentException>(paramName, () => UIntPtr.Parse("1", style, null)); + } + + public static IEnumerable<object[]> Parse_ValidWithOffsetCount_TestData() + { + foreach (object[] inputs in Parse_Valid_TestData()) + { + yield return new object[] { inputs[0], 0, ((string)inputs[0]).Length, inputs[1], inputs[2], inputs[3] }; + } + + yield return new object[] { "123", 0, 2, NumberStyles.Integer, null, (UIntPtr)12 }; + yield return new object[] { "123", 1, 2, NumberStyles.Integer, null, (UIntPtr)23 }; + yield return new object[] { "4294967295", 0, 1, NumberStyles.Integer, null, 4 }; + yield return new object[] { "4294967295", 9, 1, NumberStyles.Integer, null, 5 }; + yield return new object[] { "12", 0, 1, NumberStyles.HexNumber, null, (UIntPtr)0x1 }; + yield return new object[] { "12", 1, 1, NumberStyles.HexNumber, null, (UIntPtr)0x2 }; + yield return new object[] { "$1,000", 1, 3, NumberStyles.Currency, new NumberFormatInfo() { CurrencySymbol = "$" }, (UIntPtr)10 }; + } } } |