diff --git a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs index 42ce73f6b455b26e3e4f48719146803346bb7b7f..455c941d946688fb98e09121be712f06396c5419 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs @@ -207,13 +207,14 @@ public unsafe int CompareTo(object? value) public unsafe string ToString(IFormatProvider? provider) => ((nint_t)_value).ToString(provider); public unsafe string ToString(string? format, IFormatProvider? provider) => ((nint_t)_value).ToString(format, provider); - unsafe bool ISpanFormattable.TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) => + public unsafe bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider? provider = null) => ((nint_t)_value).TryFormat(destination, out charsWritten, format, provider); public static IntPtr Parse(string s) => (IntPtr)nint_t.Parse(s); public static IntPtr Parse(string s, NumberStyles style) => (IntPtr)nint_t.Parse(s, style); public static IntPtr Parse(string s, IFormatProvider? provider) => (IntPtr)nint_t.Parse(s, provider); public static IntPtr Parse(string s, NumberStyles style, IFormatProvider? provider) => (IntPtr)nint_t.Parse(s, style, provider); + public static IntPtr Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) => (IntPtr)nint_t.Parse(s, style, provider); public static bool TryParse(string? s, out IntPtr result) { @@ -226,5 +227,17 @@ public static bool TryParse(string? s, NumberStyles style, IFormatProvider? prov Unsafe.SkipInit(out result); return nint_t.TryParse(s, style, provider, out Unsafe.As(ref result)); } + + public static bool TryParse(ReadOnlySpan s, out IntPtr result) + { + Unsafe.SkipInit(out result); + return nint_t.TryParse(s, out Unsafe.As(ref result)); + } + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out IntPtr result) + { + Unsafe.SkipInit(out result); + return nint_t.TryParse(s, style, provider, out Unsafe.As(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 28d95bfaf97ddb94310114e90d76a388611f29df..26a093e9aefe61db8d2f422ad4fb854c78f4dd50 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs @@ -199,13 +199,14 @@ public unsafe int CompareTo(object? value) public unsafe string ToString(IFormatProvider? provider) => ((nuint_t)_value).ToString(provider); public unsafe string ToString(string? format, IFormatProvider? provider) => ((nuint_t)_value).ToString(format, provider); - unsafe bool ISpanFormattable.TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) => + public unsafe bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider? provider = null) => ((nuint_t)_value).TryFormat(destination, out charsWritten, format, provider); public static UIntPtr Parse(string s) => (UIntPtr)nuint_t.Parse(s); public static UIntPtr Parse(string s, NumberStyles style) => (UIntPtr)nuint_t.Parse(s, style); public static UIntPtr Parse(string s, IFormatProvider? provider) => (UIntPtr)nuint_t.Parse(s, provider); public static UIntPtr Parse(string s, NumberStyles style, IFormatProvider? provider) => (UIntPtr)nuint_t.Parse(s, style, provider); + public static UIntPtr Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) => (UIntPtr)nuint_t.Parse(s, style, provider); public static bool TryParse(string? s, out UIntPtr result) { @@ -218,5 +219,17 @@ public static bool TryParse(string? s, NumberStyles style, IFormatProvider? prov Unsafe.SkipInit(out result); return nuint_t.TryParse(s, style, provider, out Unsafe.As(ref result)); } + + public static bool TryParse(ReadOnlySpan s, out UIntPtr result) + { + Unsafe.SkipInit(out result); + return nuint_t.TryParse(s, out Unsafe.As(ref result)); + } + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out UIntPtr result) + { + Unsafe.SkipInit(out result); + return nuint_t.TryParse(s, style, provider, out Unsafe.As(ref result)); + } } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 40627b6b69dcfbbd77fc662348aba33291fc46b1..183a202d943aed4ad4997de9c3b3a68735930a59 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -2534,10 +2534,12 @@ public sealed partial class InsufficientMemoryException : System.OutOfMemoryExce 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 bool TryFormat(System.Span destination, out int charsWritten, System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { 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 Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.Integer, System.IFormatProvider? provider = null) { throw null; } public static System.IntPtr Subtract(System.IntPtr pointer, int offset) { throw null; } void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public int ToInt32() { throw null; } @@ -2550,6 +2552,8 @@ public sealed partial class InsufficientMemoryException : System.OutOfMemoryExce 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 static bool TryParse(System.ReadOnlySpan s, out System.IntPtr result) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.IntPtr result) { throw null; } } public partial class InvalidCastException : System.SystemException { @@ -4635,10 +4639,12 @@ public partial class TypeUnloadedException : System.SystemException 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 bool TryFormat(System.Span destination, out int charsWritten, System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { 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 Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.Integer, System.IFormatProvider? provider = null) { throw null; } public static System.UIntPtr Subtract(System.UIntPtr pointer, int offset) { throw null; } void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public unsafe void* ToPointer() { throw null; } @@ -4650,6 +4656,8 @@ public partial class TypeUnloadedException : System.SystemException 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 static bool TryParse(System.ReadOnlySpan s, out System.UIntPtr result) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.UIntPtr result) { throw null; } } public partial class UnauthorizedAccessException : System.SystemException { diff --git a/src/libraries/System.Runtime/tests/System/IntPtrTests.cs b/src/libraries/System.Runtime/tests/System/IntPtrTests.cs index 847cac351017286aac6c8861177b93cc7f538755..50e387d3c94b87c7636e92f019eb3ad6274d9018 100644 --- a/src/libraries/System.Runtime/tests/System/IntPtrTests.cs +++ b/src/libraries/System.Runtime/tests/System/IntPtrTests.cs @@ -831,7 +831,7 @@ public static IEnumerable 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] }; + yield return new object[] { inputs[0], 0, ((string)inputs[0]).Length, inputs[1], inputs[2], inputs[3] }; } NumberFormatInfo samePositiveNegativeFormat = new NumberFormatInfo() @@ -844,18 +844,18 @@ public static IEnumerable Parse_ValidWithOffsetCount_TestData() 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 }; + yield return new object[] { "2147483647", 1, 9, NumberStyles.None, null, (IntPtr)147483647 }; + yield return new object[] { "2147483647", 1, 1, NumberStyles.None, null, (IntPtr)1 }; + yield return new object[] { "123\0\0", 2, 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 }; + yield return new object[] { "abc", 0, 1, NumberStyles.HexNumber, null, (IntPtr)0xa }; + yield return new object[] { "ABC", 1, 1, NumberStyles.HexNumber, null, (IntPtr)0xB }; + yield return new object[] { "FFFFFFFF", 6, 2, NumberStyles.HexNumber, null, (IntPtr)0xFF }; + yield return new object[] { "FFFFFFFF", 0, 1, NumberStyles.HexNumber, null, (IntPtr)0xF }; // Currency - yield return new object[] { "-$1000", (IntPtr)1, (IntPtr)5, NumberStyles.Currency, new NumberFormatInfo() + yield return new object[] { "-$1000", 1, 5, NumberStyles.Currency, new NumberFormatInfo() { CurrencySymbol = "$", CurrencyGroupSeparator = "|", @@ -863,8 +863,8 @@ public static IEnumerable Parse_ValidWithOffsetCount_TestData() }, (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 }; + yield return new object[] { "100", 1, 2, NumberStyles.Currency, emptyCurrencyFormat, (IntPtr)0 }; + yield return new object[] { "100", 0, 1, NumberStyles.Currency, emptyCurrencyFormat, (IntPtr)1 }; // If CurrencySymbol and Negative are the same, NegativeSign is preferred NumberFormatInfo sameCurrencyNegativeSignFormat = new NumberFormatInfo() @@ -872,23 +872,107 @@ public static IEnumerable Parse_ValidWithOffsetCount_TestData() 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 }; + yield return new object[] { "1000", 1, 3, NumberStyles.AllowCurrencySymbol | NumberStyles.AllowLeadingSign, sameCurrencyNegativeSignFormat, (IntPtr)0 }; + yield return new object[] { "|1000", 0, 2, NumberStyles.AllowCurrencySymbol | NumberStyles.AllowLeadingSign, sameCurrencyNegativeSignFormat, (IntPtr)(-1) }; // Any - yield return new object[] { "123", (IntPtr)0, (IntPtr)2, NumberStyles.Any, null, (IntPtr)12 }; + yield return new object[] { "123", 0, 2, NumberStyles.Any, null, (IntPtr)12 }; // AllowLeadingSign - yield return new object[] { "-2147483648", (IntPtr)0, (IntPtr)10, NumberStyles.AllowLeadingSign, null, -214748364 }; + yield return new object[] { "-2147483648", 0, 10, NumberStyles.AllowLeadingSign, null, (IntPtr)(-214748364) }; // AllowTrailingSign - yield return new object[] { "123-", (IntPtr)0, (IntPtr)3, NumberStyles.AllowTrailingSign, null, (IntPtr)123 }; + yield return new object[] { "123-", 0, 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 }; + yield return new object[] { "1E2", 0, 1, NumberStyles.AllowExponent, null, (IntPtr)1 }; + yield return new object[] { "1E+2", 3, 1, NumberStyles.AllowExponent, null, (IntPtr)2 }; + yield return new object[] { "(1E2)", 1, 3, NumberStyles.AllowExponent | NumberStyles.AllowParentheses, null, (IntPtr)1E2 }; + yield return new object[] { "-1E2", 1, 3, NumberStyles.AllowExponent | NumberStyles.AllowLeadingSign, null, (IntPtr)1E2 }; + } + + [Theory] + [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))] + public static void Parse_Span_Valid(string value, int offset, int count, NumberStyles style, IFormatProvider provider, IntPtr expected) + { + IntPtr result; + + // Default style and provider + if (style == NumberStyles.Integer && provider == null) + { + Assert.True(IntPtr.TryParse(value.AsSpan(offset, count), out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, IntPtr.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(IntPtr.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(Parse_Invalid_TestData))] + public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value != null) + { + IntPtr result; + + // Default style and provider + if (style == NumberStyles.Integer && provider == null) + { + Assert.False(IntPtr.TryParse(value.AsSpan(), out result)); + Assert.Equal(default, result); + } + + Assert.Throws(exceptionType, () => int.Parse(value.AsSpan(), style, provider)); + + Assert.False(IntPtr.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default, result); + } + } + + [Theory] + [MemberData(nameof(ToString_TestData))] + public static void TryFormat(IntPtr i, string format, IFormatProvider provider, string expected) + { + char[] actual; + int charsWritten; + + // Just right + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual)); + + // Longer than needed + actual = new char[expected.Length + 1]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual, 0, charsWritten)); + + // Too short + if (expected.Length > 0) + { + actual = new char[expected.Length - 1]; + Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(0, charsWritten); + } + + if (format != null) + { + // Upper format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToUpperInvariant(), new string(actual)); + + // Lower format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToLowerInvariant(), new string(actual)); + } } } } diff --git a/src/libraries/System.Runtime/tests/System/UIntPtrTests.cs b/src/libraries/System.Runtime/tests/System/UIntPtrTests.cs index 38779ceb65cc9a342b850b69cf0746ca0d36a804..3904c3472f79ab842b63573b57fc4165da7bd90c 100644 --- a/src/libraries/System.Runtime/tests/System/UIntPtrTests.cs +++ b/src/libraries/System.Runtime/tests/System/UIntPtrTests.cs @@ -456,11 +456,95 @@ public static IEnumerable Parse_ValidWithOffsetCount_TestData() 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[] { "4294967295", 0, 1, NumberStyles.Integer, null, (UIntPtr)4 }; + yield return new object[] { "4294967295", 9, 1, NumberStyles.Integer, null, (UIntPtr)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 }; } + + [Theory] + [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))] + public static void Parse_Span_Valid(string value, int offset, int count, NumberStyles style, IFormatProvider provider, UIntPtr expected) + { + UIntPtr result; + + // Default style and provider + if (style == NumberStyles.Integer && provider == null) + { + Assert.True(UIntPtr.TryParse(value.AsSpan(offset, count), out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, UIntPtr.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(UIntPtr.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(Parse_Invalid_TestData))] + public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value != null) + { + UIntPtr result; + + // Default style and provider + if (style == NumberStyles.Integer && provider == null) + { + Assert.False(UIntPtr.TryParse(value.AsSpan(), out result)); + Assert.Equal(default, result); + } + + Assert.Throws(exceptionType, () => UIntPtr.Parse(value.AsSpan(), style, provider)); + + Assert.False(UIntPtr.TryParse(value.AsSpan(), style, provider, out result)); + Assert.Equal(default, result); + } + } + + [Theory] + [MemberData(nameof(ToString_TestData))] + public static void TryFormat(UIntPtr i, string format, IFormatProvider provider, string expected) + { + char[] actual; + int charsWritten; + + // Just right + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual)); + + // Longer than needed + actual = new char[expected.Length + 1]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual, 0, charsWritten)); + + // Too short + if (expected.Length > 0) + { + actual = new char[expected.Length - 1]; + Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(0, charsWritten); + } + + if (format != null) + { + // Upper format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToUpperInvariant(), new string(actual)); + + // Lower format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToLowerInvariant(), new string(actual)); + } + } } }