From 3a0c31edc988e284615f017bed4fa9641cad3080 Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Fri, 19 May 2023 00:10:15 +0300 Subject: [PATCH 01/26] Add implementation --- .../src/System/Convert.cs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index c351d4dbf303be..af293c3265e54a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -2960,6 +2960,32 @@ public static byte[] FromHexString(ReadOnlySpan chars) return result; } + public static bool TryFromHexString(string source, Span destination, out int bytesWritten) + { + ArgumentNullException.ThrowIfNull(source); + + return TryFromHexString(source.AsSpan(), destination, out bytesWritten); + } + + public static bool TryFromHexString(ReadOnlySpan source, Span destination, out int bytesWritten) + { + bytesWritten = 0; + var length = source.Length; + if (length == 0 || length % 2 != 0) + return false; + + var twicedLength = length * 2; + + if (destination.Length < twicedLength) + return false; + + if (!HexConverter.TryDecodeFromUtf16(source, destination)) + return false; + + bytesWritten = twicedLength; + return true; + } + /// /// Converts an array of 8-bit unsigned integers to its equivalent string representation that is encoded with uppercase hex characters. /// @@ -3011,5 +3037,23 @@ public static string ToHexString(ReadOnlySpan bytes) return HexConverter.ToString(bytes, HexConverter.Casing.Upper); } + + public static bool TryToHexString(ReadOnlySpan source, Span destination, out int charsWritten) + { + charsWritten = 0; + var length = source.Length; + + if (length == 0 || length > int.MaxValue / 2) + return false; + + int twicedLength = length * 2; + + if (destination.Length < twicedLength) + return false; + + HexConverter.EncodeToUtf16(source, destination); + charsWritten = twicedLength; + return true; + } } // class Convert } // namespace From cc13dca47cd139297e5b55f908794d0907487610 Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Sat, 20 May 2023 19:56:49 +0300 Subject: [PATCH 02/26] Add tests --- .../tests/System/Convert.FromHexString.cs | 79 +++++++------------ 1 file changed, 28 insertions(+), 51 deletions(-) diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs b/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs index 6ed2c2e778007b..37c7c5d5b8f6c4 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs @@ -34,73 +34,50 @@ public static void CompleteValueRange() private static void TestSequence(byte[] expected, string actual) { - Assert.Equal(expected, Convert.FromHexString(actual)); - } + byte[] fromResult = Convert.FromHexString(actual); + Assert.Equal(expected, fromResult); - [Fact] - public static void InvalidInputString_Null() - { - AssertExtensions.Throws("s", () => Convert.FromHexString(null)); - } + Span tryResult = stackalloc byte[actual.Length / 2]; + Assert.True(Convert.TryFromHexString(actual, tryResult, out int written)); + Assert.Equal(fromResult.Length, written); + AssertExtensions.SequenceEqual(expected, tryResult); - [Fact] - public static void InvalidInputString_HalfByte() - { - Assert.Throws(() => Convert.FromHexString("ABC")); } [Fact] - public static void InvalidInputString_BadFirstCharacter() - { - Assert.Throws(() => Convert.FromHexString("x0")); - } - - [Fact] - public static void InvalidInputString_BadSecondCharacter() - { - Assert.Throws(() => Convert.FromHexString("0x")); - } - - [Fact] - public static void InvalidInputString_NonAsciiCharacter() - { - Assert.Throws(() => Convert.FromHexString("0\u0308")); - } - - [Fact] - public static void InvalidInputString_ZeroWidthSpace() - { - Assert.Throws(() => Convert.FromHexString("\u200B 000102FDFEFF")); - } - - [Fact] - public static void InvalidInputString_LeadingWhiteSpace() - { - Assert.Throws(() => Convert.FromHexString(" 000102FDFEFF")); - } - - [Fact] - public static void InvalidInputString_TrailingWhiteSpace() + public static void InvalidInputString_Null() { - Assert.Throws(() => Convert.FromHexString("000102FDFEFF ")); + AssertExtensions.Throws("s", () => Convert.FromHexString(null)); + Assert.False(Convert.TryFromHexString(null, default, out _)); } - [Fact] - public static void InvalidInputString_WhiteSpace() + [Theory] + [InlineData("01-02-FD-FE-FF")] + [InlineData("00 01 02FD FE FF")] + [InlineData("000102FDFEFF ")] + [InlineData(" 000102FDFEFF")] + [InlineData("\u200B 000102FDFEFF")] + [InlineData("0\u0308")] + [InlineData("0x")] + [InlineData("x0")] + [InlineData("ABC")] // HalfByte + public static void InvalidInputString_FormatException_Or_FalseResult(string invalidInput) { - Assert.Throws(() => Convert.FromHexString("00 01 02FD FE FF")); - } + Assert.Throws(() => Convert.FromHexString(invalidInput)); - [Fact] - public static void InvalidInputString_Dash() - { - Assert.Throws(() => Convert.FromHexString("01-02-FD-FE-FF")); + Span buffer = stackalloc byte[invalidInput.Length / 2]; + Assert.False(Convert.TryFromHexString(invalidInput.AsSpan(), buffer, out _)); } [Fact] public static void ZeroLength() { Assert.Same(Array.Empty(), Convert.FromHexString(string.Empty)); + + bool tryResult = Convert.TryFromHexString(string.Empty, Span.Empty, out int written); + + Assert.True(tryResult); + Assert.Equal(0, written); } [Fact] From eecaed0fd7a4c1fba7074b0f4a56f3c7ee280bea Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Sat, 20 May 2023 19:59:06 +0300 Subject: [PATCH 03/26] Update conditions --- src/libraries/System.Private.CoreLib/src/System/Convert.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index af293c3265e54a..3810193dece218 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -2971,9 +2971,13 @@ public static bool TryFromHexString(ReadOnlySpan source, Span destin { bytesWritten = 0; var length = source.Length; - if (length == 0 || length % 2 != 0) + + if (length % 2 != 0) return false; + if (length == 0) + return true; + var twicedLength = length * 2; if (destination.Length < twicedLength) From b8cb6dd43fe2a002f252cfd995d0c45028a5e6e2 Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Sat, 20 May 2023 20:31:49 +0300 Subject: [PATCH 04/26] Add goto --- .../System.Private.CoreLib/src/System/Convert.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index 3810193dece218..40630c1628ffff 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -2969,25 +2969,31 @@ public static bool TryFromHexString(string source, Span destination, out i public static bool TryFromHexString(ReadOnlySpan source, Span destination, out int bytesWritten) { - bytesWritten = 0; var length = source.Length; if (length % 2 != 0) - return false; + goto FalseResult; if (length == 0) + { + bytesWritten = 0; return true; + } var twicedLength = length * 2; if (destination.Length < twicedLength) - return false; + goto FalseResult; if (!HexConverter.TryDecodeFromUtf16(source, destination)) - return false; + goto FalseResult; bytesWritten = twicedLength; return true; + + FalseResult: + bytesWritten = 0; + return false; } /// From 772208ff2965a464c50cf256f632e3b7db671544 Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Sat, 20 May 2023 21:16:33 +0300 Subject: [PATCH 05/26] Some fixes --- .../src/System/Convert.cs | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index 40630c1628ffff..6196c02740e7e5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -2980,15 +2980,15 @@ public static bool TryFromHexString(ReadOnlySpan source, Span destin return true; } - var twicedLength = length * 2; + var halfLength = length / 2; - if (destination.Length < twicedLength) + if (destination.Length < halfLength) goto FalseResult; if (!HexConverter.TryDecodeFromUtf16(source, destination)) goto FalseResult; - bytesWritten = twicedLength; + bytesWritten = halfLength; return true; FalseResult: @@ -3053,17 +3053,27 @@ public static bool TryToHexString(ReadOnlySpan source, Span destinat charsWritten = 0; var length = source.Length; - if (length == 0 || length > int.MaxValue / 2) - return false; + if (length > int.MaxValue / 2) + goto FalseResult; + + if (length == 0) + { + charsWritten = 0; + return true; + } int twicedLength = length * 2; if (destination.Length < twicedLength) - return false; + goto FalseResult; HexConverter.EncodeToUtf16(source, destination); charsWritten = twicedLength; return true; + + FalseResult: + bytesWritten = 0; + return false; } } // class Convert } // namespace From 7333ca3a4b1bdabca280db77caf1390eb81f6e13 Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Sat, 20 May 2023 21:26:03 +0300 Subject: [PATCH 06/26] Some fixes --- src/libraries/System.Private.CoreLib/src/System/Convert.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index 6196c02740e7e5..1782457dde9c59 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -3050,7 +3050,6 @@ public static string ToHexString(ReadOnlySpan bytes) public static bool TryToHexString(ReadOnlySpan source, Span destination, out int charsWritten) { - charsWritten = 0; var length = source.Length; if (length > int.MaxValue / 2) @@ -3072,7 +3071,7 @@ public static bool TryToHexString(ReadOnlySpan source, Span destinat return true; FalseResult: - bytesWritten = 0; + charsWritten = 0; return false; } } // class Convert From f23df241c9a50c576fdb001c24dadbf0b514b5bf Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Sun, 21 May 2023 00:06:06 +0300 Subject: [PATCH 07/26] Add comments --- .../src/System/Convert.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index 1782457dde9c59..3d7525896b21a7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -2960,6 +2960,18 @@ public static byte[] FromHexString(ReadOnlySpan chars) return result; } + /// + /// Converts the string, which encodes binary data as hex characters, to an equivalent 8-bit unsigned integer span. + /// + /// The string to convert. + /// + /// The span in which to write the converted 8-bit unsigned integers. When this method returns false, + /// either the span remains unmodified or contains an incomplete conversion of , + /// up to the last valid character. + /// + /// When this method returns, contains the number of bytes that were written in . + /// true if the conversion was successful; otherwise, false. + /// Passed string is null. public static bool TryFromHexString(string source, Span destination, out int bytesWritten) { ArgumentNullException.ThrowIfNull(source); @@ -2967,6 +2979,17 @@ public static bool TryFromHexString(string source, Span destination, out i return TryFromHexString(source.AsSpan(), destination, out bytesWritten); } + /// + /// Converts the span of chars, which encodes binary data as hex characters, to an equivalent 8-bit unsigned integer span. + /// + /// The span to convert. + /// + /// The span in which to write the converted 8-bit unsigned integers. When this method returns false, + /// either the span remains unmodified or contains an incomplete conversion of , + /// up to the last valid character. + /// + /// When this method returns, contains the number of bytes that were written in . + /// true if the conversion was successful; otherwise, false. public static bool TryFromHexString(ReadOnlySpan source, Span destination, out int bytesWritten) { var length = source.Length; @@ -3048,6 +3071,14 @@ public static string ToHexString(ReadOnlySpan bytes) return HexConverter.ToString(bytes, HexConverter.Casing.Upper); } + + /// + /// Converts a span of 8-bit unsigned integers to its equivalent span representation that is encoded with uppercase hex characters. + /// + /// A span of 8-bit unsigned integers. + /// The span representation in hex of the elements in . + /// When this method returns, contains the number of chars that were written in . + /// true if the conversion was successful; otherwise, false. public static bool TryToHexString(ReadOnlySpan source, Span destination, out int charsWritten) { var length = source.Length; From 70c9321336ad6ab252fec85faf493577f817502b Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Sun, 21 May 2023 21:10:21 +0300 Subject: [PATCH 08/26] Few more tests --- .../tests/System/Convert.FromHexString.cs | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs b/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs index 37c7c5d5b8f6c4..3eed1f1ecc0958 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs @@ -83,18 +83,31 @@ public static void ZeroLength() [Fact] public static void ToHexFromHexRoundtrip() { - for (int i = 1; i < 50; i++) + const int LoopCount = 50; + Span buffer = stackalloc char[LoopCount * 2]; + for (int i = 1; i < LoopCount; i++) { - byte[] data = System.Security.Cryptography.RandomNumberGenerator.GetBytes(i); + byte[] data = Security.Cryptography.RandomNumberGenerator.GetBytes(i); string hex = Convert.ToHexString(data); - Assert.Equal(data, Convert.FromHexString(hex.ToLowerInvariant())); - Assert.Equal(data, Convert.FromHexString(hex.ToUpperInvariant())); + + Span currentBuffer = buffer.Slice(0, i); + bool tryHex = Convert.TryToHexString(data, currentBuffer, out int written); + Assert.True(tryHex); + AssertExtensions.SequenceEqual(hex.AsSpan(), currentBuffer); + Assert.Equal(hex.Length, written); + + TestSequence(data, hex); + TestSequence(data, hex.ToLowerInvariant()); + TestSequence(data, hex.ToUpperInvariant()); + string mixedCase1 = hex.Substring(0, hex.Length / 2).ToUpperInvariant() + hex.Substring(hex.Length / 2).ToLowerInvariant(); string mixedCase2 = hex.Substring(0, hex.Length / 2).ToLowerInvariant() + hex.Substring(hex.Length / 2).ToUpperInvariant(); - Assert.Equal(data, Convert.FromHexString(mixedCase1)); - Assert.Equal(data, Convert.FromHexString(mixedCase2)); + + TestSequence(data, mixedCase1); + TestSequence(data, mixedCase2); + Assert.Throws(() => Convert.FromHexString(hex + " ")); Assert.Throws(() => Convert.FromHexString("\uAAAA" + hex)); } From f80a8d372c2b106040502acab354853997c0cc0d Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Sun, 21 May 2023 21:10:35 +0300 Subject: [PATCH 09/26] Update ref file --- src/libraries/System.Runtime/ref/System.Runtime.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 5362c12fcb659e..66940894091e98 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -1119,7 +1119,9 @@ public static partial class Convert public static byte[] FromBase64CharArray(char[] inArray, int offset, int length) { throw null; } public static byte[] FromBase64String(string s) { throw null; } public static byte[] FromHexString(System.ReadOnlySpan chars) { throw null; } + public static bool TryFromHexString(System.ReadOnlySpan source, Span destination, out int bytesWritten) { throw null; } public static byte[] FromHexString(string s) { throw null; } + public static bool TryFromHexString(string source, Span destination, out int bytesWritten) { throw null; } public static System.TypeCode GetTypeCode(object? value) { throw null; } public static bool IsDBNull([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? value) { throw null; } public static int ToBase64CharArray(byte[] inArray, int offsetIn, int length, char[] outArray, int offsetOut) { throw null; } @@ -1265,6 +1267,7 @@ public static partial class Convert public static string ToHexString(byte[] inArray) { throw null; } public static string ToHexString(byte[] inArray, int offset, int length) { throw null; } public static string ToHexString(System.ReadOnlySpan bytes) { throw null; } + public static bool TryToHexString(System.ReadOnlySpan source, System.Span destination, out int charsWritten) { throw null; } public static short ToInt16(bool value) { throw null; } public static short ToInt16(byte value) { throw null; } public static short ToInt16(char value) { throw null; } From 9fd5c9965459f05bd1c3675a5d050ed1f22d9f89 Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Sun, 21 May 2023 21:11:08 +0300 Subject: [PATCH 10/26] twiced -> twice --- src/libraries/System.Private.CoreLib/src/System/Convert.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index 3d7525896b21a7..469627da6d5507 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -3092,13 +3092,13 @@ public static bool TryToHexString(ReadOnlySpan source, Span destinat return true; } - int twicedLength = length * 2; + int twiceLength = length * 2; - if (destination.Length < twicedLength) + if (destination.Length < twiceLength) goto FalseResult; HexConverter.EncodeToUtf16(source, destination); - charsWritten = twicedLength; + charsWritten = twiceLength; return true; FalseResult: From d063d4fddf98f88ef05bc2b515ab846d6d275c17 Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Sun, 21 May 2023 22:13:46 +0300 Subject: [PATCH 11/26] Fix null test --- .../tests/System/Convert.FromHexString.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs b/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs index 3eed1f1ecc0958..316954717578c9 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs @@ -48,7 +48,7 @@ private static void TestSequence(byte[] expected, string actual) public static void InvalidInputString_Null() { AssertExtensions.Throws("s", () => Convert.FromHexString(null)); - Assert.False(Convert.TryFromHexString(null, default, out _)); + AssertExtensions.Throws("source", () => Convert.TryFromHexString(null, default, out _)); } [Theory] From 986c8fae407574c3251ebb3edf01b3e1f02703ad Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Sun, 21 May 2023 22:14:07 +0300 Subject: [PATCH 12/26] try fix roundtrip test --- .../tests/System/Convert.FromHexString.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs b/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs index 316954717578c9..c1e48051be1916 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs @@ -90,7 +90,7 @@ public static void ToHexFromHexRoundtrip() byte[] data = Security.Cryptography.RandomNumberGenerator.GetBytes(i); string hex = Convert.ToHexString(data); - Span currentBuffer = buffer.Slice(0, i); + Span currentBuffer = buffer.Slice(0, i * 2); bool tryHex = Convert.TryToHexString(data, currentBuffer, out int written); Assert.True(tryHex); AssertExtensions.SequenceEqual(hex.AsSpan(), currentBuffer); From c644e59e37aac2acfcb809f4d75c3e7647e4c87d Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Sun, 21 May 2023 22:17:04 +0300 Subject: [PATCH 13/26] fix tabs & naming --- .../src/System/Convert.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index 469627da6d5507..5e3b5fe28d487e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -3003,20 +3003,20 @@ public static bool TryFromHexString(ReadOnlySpan source, Span destin return true; } - var halfLength = length / 2; + var requiredByteCount = length / 2; - if (destination.Length < halfLength) + if (destination.Length < requiredByteCount) goto FalseResult; if (!HexConverter.TryDecodeFromUtf16(source, destination)) goto FalseResult; - bytesWritten = halfLength; + bytesWritten = requiredByteCount; return true; - FalseResult: - bytesWritten = 0; - return false; + FalseResult: + bytesWritten = 0; + return false; } /// @@ -3092,18 +3092,18 @@ public static bool TryToHexString(ReadOnlySpan source, Span destinat return true; } - int twiceLength = length * 2; + int requiredCharCount = length * 2; - if (destination.Length < twiceLength) + if (destination.Length < requiredCharCount) goto FalseResult; HexConverter.EncodeToUtf16(source, destination); - charsWritten = twiceLength; + charsWritten = requiredCharCount; return true; - FalseResult: - charsWritten = 0; - return false; + FalseResult: + charsWritten = 0; + return false; } } // class Convert } // namespace From 794eff8af5905a5961028e81f0638494451edd4e Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Sun, 21 May 2023 23:48:05 +0300 Subject: [PATCH 14/26] var -> int --- src/libraries/System.Private.CoreLib/src/System/Convert.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index 5e3b5fe28d487e..f8ff3f723f1c06 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -2992,7 +2992,7 @@ public static bool TryFromHexString(string source, Span destination, out i /// true if the conversion was successful; otherwise, false. public static bool TryFromHexString(ReadOnlySpan source, Span destination, out int bytesWritten) { - var length = source.Length; + int length = source.Length; if (length % 2 != 0) goto FalseResult; @@ -3003,7 +3003,7 @@ public static bool TryFromHexString(ReadOnlySpan source, Span destin return true; } - var requiredByteCount = length / 2; + int requiredByteCount = length / 2; if (destination.Length < requiredByteCount) goto FalseResult; @@ -3081,7 +3081,7 @@ public static string ToHexString(ReadOnlySpan bytes) /// true if the conversion was successful; otherwise, false. public static bool TryToHexString(ReadOnlySpan source, Span destination, out int charsWritten) { - var length = source.Length; + int length = source.Length; if (length > int.MaxValue / 2) goto FalseResult; From 6e48f510dfd159deea93827df1d283aeb81e855a Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Mon, 31 Jul 2023 01:14:34 +0300 Subject: [PATCH 15/26] Update implementation --- .../src/System/Convert.cs | 51 ++++++++++++------- .../System.Runtime/ref/System.Runtime.cs | 4 +- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index 39ad7eca6b7ee2..dd59d42ec00221 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -2965,13 +2965,14 @@ public static byte[] FromHexString(ReadOnlySpan chars) /// up to the last valid character. /// /// When this method returns, contains the number of bytes that were written in . + /// When this method returns, contains the number of bytes that were written in . /// true if the conversion was successful; otherwise, false. /// Passed string is null. - public static bool TryFromHexString(string source, Span destination, out int bytesWritten) + public static OperationStatus FromHexString(string source, Span destination, out int charsConsumed, out int bytesWritten) { ArgumentNullException.ThrowIfNull(source); - return TryFromHexString(source.AsSpan(), destination, out bytesWritten); + return FromHexString(source.AsSpan(), destination, out charsConsumed, out bytesWritten); } /// @@ -2984,34 +2985,46 @@ public static bool TryFromHexString(string source, Span destination, out i /// up to the last valid character. /// /// When this method returns, contains the number of bytes that were written in . + /// When this method returns, contains the number of bytes that were written in . /// true if the conversion was successful; otherwise, false. - public static bool TryFromHexString(ReadOnlySpan source, Span destination, out int bytesWritten) + public static OperationStatus FromHexString(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten) { - int length = source.Length; - - if (length % 2 != 0) - goto FalseResult; + (int quotient, int remainder) = Math.DivRem(source.Length, 2); - if (length == 0) + if (quotient == 0) { + charsConsumed = 0; bytesWritten = 0; - return true; + + if (remainder == 1) + return OperationStatus.NeedMoreData; + else + return OperationStatus.Done; } - int requiredByteCount = length / 2; + var successResult = OperationStatus.Done; - if (destination.Length < requiredByteCount) - goto FalseResult; + if (destination.Length < quotient) + { + source = source.Slice(destination.Length * 2); + successResult = OperationStatus.DestinationTooSmall; + } + else if (remainder == 1) + { + source = source.Slice(source.Length - 1); + successResult = OperationStatus.NeedMoreData; + } if (!HexConverter.TryDecodeFromUtf16(source, destination)) - goto FalseResult; - - bytesWritten = requiredByteCount; - return true; + { + bytesWritten = 0; + charsConsumed = 0; + return OperationStatus.InvalidData; + } - FalseResult: - bytesWritten = 0; - return false; + bytesWritten = quotient; + charsConsumed = source.Length; + return successResult; } /// diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index e226287b7d321c..7e29d36916c441 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -1119,9 +1119,9 @@ public static partial class Convert public static byte[] FromBase64CharArray(char[] inArray, int offset, int length) { throw null; } public static byte[] FromBase64String(string s) { throw null; } public static byte[] FromHexString(System.ReadOnlySpan chars) { throw null; } - public static bool TryFromHexString(System.ReadOnlySpan source, Span destination, out int bytesWritten) { throw null; } + public static System.Buffers.OperationStatus FromHexString(System.ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten) { throw null; } public static byte[] FromHexString(string s) { throw null; } - public static bool TryFromHexString(string source, Span destination, out int bytesWritten) { throw null; } + public static System.Buffers.OperationStatus FromHexString(string source, Span destination, out int charsConsumed, out int bytesWritten) { throw null; } public static System.TypeCode GetTypeCode(object? value) { throw null; } public static bool IsDBNull([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? value) { throw null; } public static int ToBase64CharArray(byte[] inArray, int offsetIn, int length, char[] outArray, int offsetOut) { throw null; } From 071e5f46ee79dca557ea9da7aec99a78549d19da Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Mon, 31 Jul 2023 01:28:27 +0300 Subject: [PATCH 16/26] Fix tests --- .../tests/System/Convert.FromHexString.cs | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs b/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs index c1e48051be1916..00a11b383fa6bd 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs @@ -1,4 +1,5 @@ -using System.Text; +using System.Buffers; +using System.Text; using Xunit; namespace System.Tests @@ -38,8 +39,9 @@ private static void TestSequence(byte[] expected, string actual) Assert.Equal(expected, fromResult); Span tryResult = stackalloc byte[actual.Length / 2]; - Assert.True(Convert.TryFromHexString(actual, tryResult, out int written)); + Assert.Equal(OperationStatus.Done, Convert.FromHexString(actual, tryResult, out int consumed, out int written)); Assert.Equal(fromResult.Length, written); + Assert.Equal(actual.Length, consumed); AssertExtensions.SequenceEqual(expected, tryResult); } @@ -48,7 +50,7 @@ private static void TestSequence(byte[] expected, string actual) public static void InvalidInputString_Null() { AssertExtensions.Throws("s", () => Convert.FromHexString(null)); - AssertExtensions.Throws("source", () => Convert.TryFromHexString(null, default, out _)); + AssertExtensions.Throws("source", () => Convert.FromHexString(null, default, out _, out _)); } [Theory] @@ -66,7 +68,7 @@ public static void InvalidInputString_FormatException_Or_FalseResult(string inva Assert.Throws(() => Convert.FromHexString(invalidInput)); Span buffer = stackalloc byte[invalidInput.Length / 2]; - Assert.False(Convert.TryFromHexString(invalidInput.AsSpan(), buffer, out _)); + Assert.Equal(OperationStatus.InvalidData, Convert.FromHexString(invalidInput.AsSpan(), buffer, out _, out _)); } [Fact] @@ -74,18 +76,19 @@ public static void ZeroLength() { Assert.Same(Array.Empty(), Convert.FromHexString(string.Empty)); - bool tryResult = Convert.TryFromHexString(string.Empty, Span.Empty, out int written); + OperationStatus convertResult = Convert.FromHexString(string.Empty, Span.Empty, out int consumed, out int written); - Assert.True(tryResult); + Assert.Equal(OperationStatus.Done, convertResult); Assert.Equal(0, written); + Assert.Equal(0, consumed); } [Fact] public static void ToHexFromHexRoundtrip() { - const int LoopCount = 50; - Span buffer = stackalloc char[LoopCount * 2]; - for (int i = 1; i < LoopCount; i++) + const int loopCount = 50; + Span buffer = stackalloc char[loopCount * 2]; + for (int i = 1; i < loopCount; i++) { byte[] data = Security.Cryptography.RandomNumberGenerator.GetBytes(i); string hex = Convert.ToHexString(data); From 80431ab0f6a51bdb4d5698549f81e593d658f225 Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Mon, 31 Jul 2023 01:56:02 +0300 Subject: [PATCH 17/26] fix slicing --- src/libraries/System.Private.CoreLib/src/System/Convert.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index dd59d42ec00221..94e5f60e7f1e8b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -3006,12 +3006,12 @@ public static OperationStatus FromHexString(ReadOnlySpan source, Span Date: Mon, 31 Jul 2023 01:56:08 +0300 Subject: [PATCH 18/26] More tests --- .../tests/System/Convert.FromHexString.cs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs b/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs index 00a11b383fa6bd..a1dfdfa48fc57f 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs @@ -115,5 +115,44 @@ public static void ToHexFromHexRoundtrip() Assert.Throws(() => Convert.FromHexString("\uAAAA" + hex)); } } + + [Fact] + public static void TooShortDestination() + { + const int destinationSize = 10; + Span destination = stackalloc byte[destinationSize]; + byte[] data = Security.Cryptography.RandomNumberGenerator.GetBytes(destinationSize * 2 + 1); + string hex = Convert.ToHexString(data); + + OperationStatus result = Convert.FromHexString(hex, destination, out int charsConsumed, out int bytesWritten); + + Assert.Equal(OperationStatus.DestinationTooSmall, result); + Assert.Equal(destinationSize * 2, charsConsumed); + Assert.Equal(destinationSize, bytesWritten); + } + + [Fact] + public static void NeedMoreData() + { + const int destinationSize = 10; + byte[] data = Security.Cryptography.RandomNumberGenerator.GetBytes(destinationSize); + Span destination = stackalloc byte[destinationSize]; + var hex = Convert.ToHexString(data); + + var spanHex = hex.AsSpan(0, 1); + var singeResult = Convert.FromHexString(spanHex, destination, out int consumed, out int written); + + Assert.Equal(OperationStatus.NeedMoreData, singeResult); + Assert.Equal(0, consumed); + Assert.Equal(0, written); + + spanHex = hex.AsSpan(0, hex.Length - 1); + + var oneOffResult = Convert.FromHexString(spanHex, destination, out consumed, out written); + + Assert.Equal(OperationStatus.NeedMoreData, oneOffResult); + Assert.Equal(spanHex.Length - 1, consumed); + Assert.Equal((spanHex.Length - 1) / 2, written); + } } } From 948616fd83807433554c65e08ed53ebe18e07a38 Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Mon, 31 Jul 2023 02:07:32 +0300 Subject: [PATCH 19/26] update xml comments --- .../System.Private.CoreLib/src/System/Convert.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index 94e5f60e7f1e8b..355ecbc9dc1bfe 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -2965,8 +2965,13 @@ public static byte[] FromHexString(ReadOnlySpan chars) /// up to the last valid character. /// /// When this method returns, contains the number of bytes that were written in . - /// When this method returns, contains the number of bytes that were written in . + /// When this method returns, contains the number of bytes that were consumed in . /// true if the conversion was successful; otherwise, false. + /// The return value can be as follows: + /// - : was successfully and completely converted into . + /// - : There is not enough space in to decompress . + /// - : The converting action is partially done. At least one (or any odd) more byte is required to complete the converting task. This method should be called again with more input to convert. + /// - : The data in is invalid and could not be converted. /// Passed string is null. public static OperationStatus FromHexString(string source, Span destination, out int charsConsumed, out int bytesWritten) { @@ -2985,8 +2990,13 @@ public static OperationStatus FromHexString(string source, Span destinatio /// up to the last valid character. /// /// When this method returns, contains the number of bytes that were written in . - /// When this method returns, contains the number of bytes that were written in . - /// true if the conversion was successful; otherwise, false. + /// When this method returns, contains the number of chars that were consumed from . + /// One of the enumeration values that indicates the status of the decompression operation. + /// The return value can be as follows: + /// - : was successfully and completely converted into . + /// - : There is not enough space in to decompress . + /// - : The converting action is partially done. At least one (or any odd) more byte is required to complete the converting task. This method should be called again with more input to convert. + /// - : The data in is invalid and could not be converted. public static OperationStatus FromHexString(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten) { (int quotient, int remainder) = Math.DivRem(source.Length, 2); From af59a7fda7d5d730d78105f0b9e60196a6bdc0d8 Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Mon, 31 Jul 2023 02:35:41 +0300 Subject: [PATCH 20/26] Fill values on invalid data result --- .../Common/src/System/HexConverter.cs | 21 ++++++++++++++++--- .../src/System/Convert.cs | 5 ++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/libraries/Common/src/System/HexConverter.cs b/src/libraries/Common/src/System/HexConverter.cs index ccce1cb691f104..053be5c30bfb71 100644 --- a/src/libraries/Common/src/System/HexConverter.cs +++ b/src/libraries/Common/src/System/HexConverter.cs @@ -233,16 +233,28 @@ public static bool TryDecodeFromUtf16(ReadOnlySpan chars, Span bytes if (BitConverter.IsLittleEndian && (Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && chars.Length >= Vector128.Count * 2) { - return TryDecodeFromUtf16_Vector128(chars, bytes); + return TryDecodeFromUtf16_Vector128(chars, bytes, out _); } #endif return TryDecodeFromUtf16(chars, bytes, out _); } + internal static bool TryDecodeFromUtf16Vectorized(ReadOnlySpan chars, Span bytes, out int charsProcessed) + { +#if SYSTEM_PRIVATE_CORELIB + if (BitConverter.IsLittleEndian && (Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && + chars.Length >= Vector128.Count * 2) + { + return TryDecodeFromUtf16_Vector128(chars, bytes, out charsProcessed); + } +#endif + return TryDecodeFromUtf16(chars, bytes, out charsProcessed); + } + #if SYSTEM_PRIVATE_CORELIB [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] [CompExactlyDependsOn(typeof(Ssse3))] - public static bool TryDecodeFromUtf16_Vector128(ReadOnlySpan chars, Span bytes) + public static bool TryDecodeFromUtf16_Vector128(ReadOnlySpan chars, Span bytes, out int charsProcessed) { Debug.Assert(Ssse3.IsSupported || AdvSimd.Arm64.IsSupported); Debug.Assert(chars.Length <= bytes.Length * 2); @@ -309,6 +321,7 @@ public static bool TryDecodeFromUtf16_Vector128(ReadOnlySpan chars, Span.Count * 2; if (offset == (nuint)chars.Length) { + charsProcessed = chars.Length; return true; } // Overlap with the current chunk for trailing elements @@ -320,7 +333,9 @@ public static bool TryDecodeFromUtf16_Vector128(ReadOnlySpan chars, Span source, Span Date: Mon, 31 Jul 2023 20:00:48 +0300 Subject: [PATCH 21/26] Fix tests? --- src/libraries/System.Private.CoreLib/src/System/Convert.cs | 1 + .../tests/System/Convert.FromHexString.cs | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index 688fd4005b8fb1..4ac156285cb3df 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -3017,6 +3017,7 @@ public static OperationStatus FromHexString(ReadOnlySpan source, Span(() => Convert.FromHexString(invalidInput)); @@ -132,7 +131,7 @@ public static void TooShortDestination() } [Fact] - public static void NeedMoreData() + public static void NeedMoreData_OrFormatException() { const int destinationSize = 10; byte[] data = Security.Cryptography.RandomNumberGenerator.GetBytes(destinationSize); @@ -142,6 +141,7 @@ public static void NeedMoreData() var spanHex = hex.AsSpan(0, 1); var singeResult = Convert.FromHexString(spanHex, destination, out int consumed, out int written); + Assert.Throws(() => Convert.FromHexString(hex.Substring(0, 1))); Assert.Equal(OperationStatus.NeedMoreData, singeResult); Assert.Equal(0, consumed); Assert.Equal(0, written); @@ -150,6 +150,7 @@ public static void NeedMoreData() var oneOffResult = Convert.FromHexString(spanHex, destination, out consumed, out written); + Assert.Throws(() => Convert.FromHexString(hex.Substring(0, hex.Length - 1))); Assert.Equal(OperationStatus.NeedMoreData, oneOffResult); Assert.Equal(spanHex.Length - 1, consumed); Assert.Equal((spanHex.Length - 1) / 2, written); From 30afc43afdfb62b03eea04ff151a055f2d0bb772 Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Mon, 31 Jul 2023 23:31:55 +0300 Subject: [PATCH 22/26] Try update loop --- src/libraries/Common/src/System/HexConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Common/src/System/HexConverter.cs b/src/libraries/Common/src/System/HexConverter.cs index 053be5c30bfb71..72ccb0a39d5a4e 100644 --- a/src/libraries/Common/src/System/HexConverter.cs +++ b/src/libraries/Common/src/System/HexConverter.cs @@ -348,7 +348,7 @@ public static bool TryDecodeFromUtf16(ReadOnlySpan chars, Span bytes int j = 0; int byteLo = 0; int byteHi = 0; - while (j < bytes.Length) + while (j < chars.Length / 2) { byteLo = FromChar(chars[i + 1]); byteHi = FromChar(chars[i]); From f532064a915d510dd4e618d47f0b00bc5c1e1d38 Mon Sep 17 00:00:00 2001 From: hrrrrustic Date: Sun, 24 Sep 2023 19:38:56 +0300 Subject: [PATCH 23/26] try fix target buffer size error --- src/libraries/Common/src/System/HexConverter.cs | 2 +- src/libraries/System.Private.CoreLib/src/System/Convert.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/Common/src/System/HexConverter.cs b/src/libraries/Common/src/System/HexConverter.cs index 72ccb0a39d5a4e..053be5c30bfb71 100644 --- a/src/libraries/Common/src/System/HexConverter.cs +++ b/src/libraries/Common/src/System/HexConverter.cs @@ -348,7 +348,7 @@ public static bool TryDecodeFromUtf16(ReadOnlySpan chars, Span bytes int j = 0; int byteLo = 0; int byteHi = 0; - while (j < chars.Length / 2) + while (j < bytes.Length) { byteLo = FromChar(chars[i + 1]); byteHi = FromChar(chars[i]); diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index 4ac156285cb3df..c25fe01d9a9e38 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -3023,6 +3023,7 @@ public static OperationStatus FromHexString(ReadOnlySpan source, Span Date: Mon, 25 Sep 2023 00:07:09 +0300 Subject: [PATCH 24/26] Remove Vectorized copy-paste --- src/libraries/Common/src/System/HexConverter.cs | 14 +------------- .../System.Private.CoreLib/src/System/Convert.cs | 4 ++-- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/libraries/Common/src/System/HexConverter.cs b/src/libraries/Common/src/System/HexConverter.cs index 053be5c30bfb71..74e217d83c8676 100644 --- a/src/libraries/Common/src/System/HexConverter.cs +++ b/src/libraries/Common/src/System/HexConverter.cs @@ -227,19 +227,7 @@ public static char ToCharLower(int value) return (char)value; } - public static bool TryDecodeFromUtf16(ReadOnlySpan chars, Span bytes) - { -#if SYSTEM_PRIVATE_CORELIB - if (BitConverter.IsLittleEndian && (Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && - chars.Length >= Vector128.Count * 2) - { - return TryDecodeFromUtf16_Vector128(chars, bytes, out _); - } -#endif - return TryDecodeFromUtf16(chars, bytes, out _); - } - - internal static bool TryDecodeFromUtf16Vectorized(ReadOnlySpan chars, Span bytes, out int charsProcessed) + public static bool TryDecodeFromUtf16(ReadOnlySpan chars, Span bytes, out int charsProcessed) { #if SYSTEM_PRIVATE_CORELIB if (BitConverter.IsLittleEndian && (Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index c25fe01d9a9e38..9dd7bbcca6d9d6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -2949,7 +2949,7 @@ public static byte[] FromHexString(ReadOnlySpan chars) byte[] result = GC.AllocateUninitializedArray(chars.Length >> 1); - if (!HexConverter.TryDecodeFromUtf16(chars, result)) + if (!HexConverter.TryDecodeFromUtf16(chars, result, out _)) throw new FormatException(SR.Format_BadHexChar); return result; @@ -3027,7 +3027,7 @@ public static OperationStatus FromHexString(ReadOnlySpan source, Span Date: Mon, 25 Sep 2023 01:03:20 +0300 Subject: [PATCH 25/26] fix naming --- src/libraries/Common/src/System/HexConverter.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/Common/src/System/HexConverter.cs b/src/libraries/Common/src/System/HexConverter.cs index 74e217d83c8676..bf0e6dcd108310 100644 --- a/src/libraries/Common/src/System/HexConverter.cs +++ b/src/libraries/Common/src/System/HexConverter.cs @@ -236,7 +236,7 @@ public static bool TryDecodeFromUtf16(ReadOnlySpan chars, Span bytes return TryDecodeFromUtf16_Vector128(chars, bytes, out charsProcessed); } #endif - return TryDecodeFromUtf16(chars, bytes, out charsProcessed); + return TryDecodeFromUtf16_Scalar(chars, bytes, out charsProcessed); } #if SYSTEM_PRIVATE_CORELIB @@ -321,13 +321,13 @@ public static bool TryDecodeFromUtf16_Vector128(ReadOnlySpan chars, Span chars, Span bytes, out int charsProcessed) + private static bool TryDecodeFromUtf16_Scalar(ReadOnlySpan chars, Span bytes, out int charsProcessed) { Debug.Assert(chars.Length % 2 == 0, "Un-even number of characters provided"); Debug.Assert(chars.Length / 2 == bytes.Length, "Target buffer not right-sized for provided characters"); From f247ed0dd29a1f57a37ba88f7a5da312f849cffd Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 31 Oct 2023 09:14:20 +0100 Subject: [PATCH 26/26] Apply suggestions from code review --- .../src/System/Convert.cs | 62 ++++++------------- .../tests/System/Convert.FromHexString.cs | 3 +- 2 files changed, 21 insertions(+), 44 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index 9dd7bbcca6d9d6..ebf4ee8caaebfe 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -2960,18 +2960,13 @@ public static byte[] FromHexString(ReadOnlySpan chars) /// /// The string to convert. /// - /// The span in which to write the converted 8-bit unsigned integers. When this method returns false, + /// The span in which to write the converted 8-bit unsigned integers. When this method returns value different than , /// either the span remains unmodified or contains an incomplete conversion of , /// up to the last valid character. /// - /// When this method returns, contains the number of bytes that were written in . - /// When this method returns, contains the number of bytes that were consumed in . - /// true if the conversion was successful; otherwise, false. - /// The return value can be as follows: - /// - : was successfully and completely converted into . - /// - : There is not enough space in to decompress . - /// - : The converting action is partially done. At least one (or any odd) more byte is required to complete the converting task. This method should be called again with more input to convert. - /// - : The data in is invalid and could not be converted. + /// When this method returns, contains the number of bytes that were written to . + /// When this method returns, contains the number of characters that were consumed from . + /// An describing the result of the operation. /// Passed string is null. public static OperationStatus FromHexString(string source, Span destination, out int charsConsumed, out int bytesWritten) { @@ -2985,18 +2980,13 @@ public static OperationStatus FromHexString(string source, Span destinatio /// /// The span to convert. /// - /// The span in which to write the converted 8-bit unsigned integers. When this method returns false, + /// The span in which to write the converted 8-bit unsigned integers. When this method returns value different than , /// either the span remains unmodified or contains an incomplete conversion of , /// up to the last valid character. /// - /// When this method returns, contains the number of bytes that were written in . - /// When this method returns, contains the number of chars that were consumed from . - /// One of the enumeration values that indicates the status of the decompression operation. - /// The return value can be as follows: - /// - : was successfully and completely converted into . - /// - : There is not enough space in to decompress . - /// - : The converting action is partially done. At least one (or any odd) more byte is required to complete the converting task. This method should be called again with more input to convert. - /// - : The data in is invalid and could not be converted. + /// When this method returns, contains the number of bytes that were written to . + /// When this method returns, contains the number of characters that were consumed from . + /// An describing the result of the operation. public static OperationStatus FromHexString(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten) { (int quotient, int remainder) = Math.DivRem(source.Length, 2); @@ -3006,25 +2996,22 @@ public static OperationStatus FromHexString(ReadOnlySpan source, Span source, Span @@ -3100,29 +3087,20 @@ public static string ToHexString(ReadOnlySpan bytes) /// true if the conversion was successful; otherwise, false. public static bool TryToHexString(ReadOnlySpan source, Span destination, out int charsWritten) { - int length = source.Length; - - if (length > int.MaxValue / 2) - goto FalseResult; - - if (length == 0) + if (source.Length == 0) { charsWritten = 0; return true; } - - int requiredCharCount = length * 2; - - if (destination.Length < requiredCharCount) - goto FalseResult; + else if (source.Length > int.MaxValue / 2 || destination.Length > source.Length * 2) + { + charsWritten = 0; + return false; + } HexConverter.EncodeToUtf16(source, destination); - charsWritten = requiredCharCount; + charsWritten = source.Length * 2; return true; - - FalseResult: - charsWritten = 0; - return false; } } // class Convert } // namespace diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs b/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs index 74f44b50ec2169..ad032d931f2bbd 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Convert.FromHexString.cs @@ -38,12 +38,11 @@ private static void TestSequence(byte[] expected, string actual) byte[] fromResult = Convert.FromHexString(actual); Assert.Equal(expected, fromResult); - Span tryResult = stackalloc byte[actual.Length / 2]; + Span tryResult = new byte[actual.Length / 2]; Assert.Equal(OperationStatus.Done, Convert.FromHexString(actual, tryResult, out int consumed, out int written)); Assert.Equal(fromResult.Length, written); Assert.Equal(actual.Length, consumed); AssertExtensions.SequenceEqual(expected, tryResult); - } [Fact]