-
Notifications
You must be signed in to change notification settings - Fork 5k
Convert.Try{From/To}HexString #86556
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3a0c31e
cc13dca
eecaed0
b8cb6dd
772208f
7333ca3
f23df24
70c9321
f80a8d3
9fd5c99
d063d4f
986c8fa
c644e59
794eff8
8a858be
6e48f51
071e5f4
80431ab
38d018f
948616f
af59a7f
dbfe893
30afc43
010c836
f532064
9afd0cb
e7439d4
f247ed0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
using System.Text; | ||
using System.Buffers; | ||
using System.Text; | ||
using Xunit; | ||
|
||
namespace System.Tests | ||
|
@@ -34,93 +35,124 @@ 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); | ||
|
||
Span<byte> 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] | ||
public static void InvalidInputString_Null() | ||
{ | ||
AssertExtensions.Throws<ArgumentNullException>("s", () => Convert.FromHexString(null)); | ||
AssertExtensions.Throws<ArgumentNullException>("source", () => Convert.FromHexString(null, default, out _, out _)); | ||
} | ||
|
||
[Fact] | ||
public static void InvalidInputString_HalfByte() | ||
[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")] | ||
public static void InvalidInputString_FormatException_Or_FalseResult(string invalidInput) | ||
{ | ||
Assert.Throws<FormatException>(() => Convert.FromHexString("ABC")); | ||
} | ||
Assert.Throws<FormatException>(() => Convert.FromHexString(invalidInput)); | ||
|
||
[Fact] | ||
public static void InvalidInputString_BadFirstCharacter() | ||
{ | ||
Assert.Throws<FormatException>(() => Convert.FromHexString("x0")); | ||
Span<byte> buffer = stackalloc byte[invalidInput.Length / 2]; | ||
Assert.Equal(OperationStatus.InvalidData, Convert.FromHexString(invalidInput.AsSpan(), buffer, out _, out _)); | ||
} | ||
|
||
[Fact] | ||
public static void InvalidInputString_BadSecondCharacter() | ||
public static void ZeroLength() | ||
{ | ||
Assert.Throws<FormatException>(() => Convert.FromHexString("0x")); | ||
} | ||
Assert.Same(Array.Empty<byte>(), Convert.FromHexString(string.Empty)); | ||
|
||
[Fact] | ||
public static void InvalidInputString_NonAsciiCharacter() | ||
{ | ||
Assert.Throws<FormatException>(() => Convert.FromHexString("0\u0308")); | ||
} | ||
OperationStatus convertResult = Convert.FromHexString(string.Empty, Span<byte>.Empty, out int consumed, out int written); | ||
|
||
[Fact] | ||
public static void InvalidInputString_ZeroWidthSpace() | ||
{ | ||
Assert.Throws<FormatException>(() => Convert.FromHexString("\u200B 000102FDFEFF")); | ||
Assert.Equal(OperationStatus.Done, convertResult); | ||
Assert.Equal(0, written); | ||
Assert.Equal(0, consumed); | ||
} | ||
|
||
[Fact] | ||
public static void InvalidInputString_LeadingWhiteSpace() | ||
public static void ToHexFromHexRoundtrip() | ||
{ | ||
Assert.Throws<FormatException>(() => Convert.FromHexString(" 000102FDFEFF")); | ||
} | ||
const int loopCount = 50; | ||
Span<char> buffer = stackalloc char[loopCount * 2]; | ||
for (int i = 1; i < loopCount; i++) | ||
{ | ||
byte[] data = Security.Cryptography.RandomNumberGenerator.GetBytes(i); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We usually use a seeded Random so the tests are reproducible. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with Dan, but since the existing tests in this type were already using |
||
string hex = Convert.ToHexString(data); | ||
|
||
[Fact] | ||
public static void InvalidInputString_TrailingWhiteSpace() | ||
{ | ||
Assert.Throws<FormatException>(() => Convert.FromHexString("000102FDFEFF ")); | ||
} | ||
Span<char> currentBuffer = buffer.Slice(0, i * 2); | ||
bool tryHex = Convert.TryToHexString(data, currentBuffer, out int written); | ||
Assert.True(tryHex); | ||
AssertExtensions.SequenceEqual(hex.AsSpan(), currentBuffer); | ||
Assert.Equal(hex.Length, written); | ||
|
||
[Fact] | ||
public static void InvalidInputString_WhiteSpace() | ||
{ | ||
Assert.Throws<FormatException>(() => Convert.FromHexString("00 01 02FD FE FF")); | ||
} | ||
TestSequence(data, hex); | ||
TestSequence(data, hex.ToLowerInvariant()); | ||
TestSequence(data, hex.ToUpperInvariant()); | ||
|
||
[Fact] | ||
public static void InvalidInputString_Dash() | ||
{ | ||
Assert.Throws<FormatException>(() => Convert.FromHexString("01-02-FD-FE-FF")); | ||
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(); | ||
|
||
TestSequence(data, mixedCase1); | ||
TestSequence(data, mixedCase2); | ||
|
||
Assert.Throws<FormatException>(() => Convert.FromHexString(hex + " ")); | ||
Assert.Throws<FormatException>(() => Convert.FromHexString("\uAAAA" + hex)); | ||
} | ||
} | ||
|
||
[Fact] | ||
public static void ZeroLength() | ||
public static void TooShortDestination() | ||
{ | ||
Assert.Same(Array.Empty<byte>(), Convert.FromHexString(string.Empty)); | ||
const int destinationSize = 10; | ||
Span<byte> 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 ToHexFromHexRoundtrip() | ||
public static void NeedMoreData_OrFormatException() | ||
{ | ||
for (int i = 1; i < 50; i++) | ||
{ | ||
byte[] data = System.Security.Cryptography.RandomNumberGenerator.GetBytes(i); | ||
string hex = Convert.ToHexString(data); | ||
Assert.Equal(data, Convert.FromHexString(hex.ToLowerInvariant())); | ||
Assert.Equal(data, Convert.FromHexString(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)); | ||
Assert.Throws<FormatException>(() => Convert.FromHexString(hex + " ")); | ||
Assert.Throws<FormatException>(() => Convert.FromHexString("\uAAAA" + hex)); | ||
} | ||
const int destinationSize = 10; | ||
byte[] data = Security.Cryptography.RandomNumberGenerator.GetBytes(destinationSize); | ||
Span<byte> 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.Throws<FormatException>(() => Convert.FromHexString(hex.Substring(0, 1))); | ||
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.Throws<FormatException>(() => 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); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
subjective nit: using a variable called
quotient
to store both "quotient" and "bytesWritten" is confusing to me, we could simply assignbytesWritten
todestination.Length
here