Skip to content
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

Convert.ToHexStringLower: lower variant for Convert.ToHexString #92483

Merged
merged 13 commits into from
Nov 20, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -1782,7 +1782,11 @@ public static ActivityTraceId CreateFromBytes(ReadOnlySpan<byte> idData)
if (idData.Length != 16)
throw new ArgumentOutOfRangeException(nameof(idData));

#if NET9_0_OR_GREATER
return new ActivityTraceId(Convert.ToHexStringLower(idData));
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
#else
return new ActivityTraceId(HexConverter.ToString(idData, HexConverter.Casing.Lower));
#endif
}
public static ActivityTraceId CreateFromUtf8String(ReadOnlySpan<byte> idData) => new ActivityTraceId(idData);

Expand Down Expand Up @@ -1861,7 +1865,11 @@ private ActivityTraceId(ReadOnlySpan<byte> idData)
span[1] = BinaryPrimitives.ReverseEndianness(span[1]);
}

#if NET9_0_OR_GREATER
_hexString = Convert.ToHexStringLower(MemoryMarshal.AsBytes(span));
#else
_hexString = HexConverter.ToString(MemoryMarshal.AsBytes(span), HexConverter.Casing.Lower);
#endif
}

/// <summary>
Expand Down Expand Up @@ -1956,14 +1964,22 @@ public static unsafe ActivitySpanId CreateRandom()
{
ulong id;
ActivityTraceId.SetToRandomBytes(new Span<byte>(&id, sizeof(ulong)));
#if NET9_0_OR_GREATER
return new ActivitySpanId(Convert.ToHexStringLower(new ReadOnlySpan<byte>(&id, sizeof(ulong))));
#else
return new ActivitySpanId(HexConverter.ToString(new ReadOnlySpan<byte>(&id, sizeof(ulong)), HexConverter.Casing.Lower));
#endif
}
public static ActivitySpanId CreateFromBytes(ReadOnlySpan<byte> idData)
{
if (idData.Length != 8)
throw new ArgumentOutOfRangeException(nameof(idData));

#if NET9_0_OR_GREATER
return new ActivitySpanId(Convert.ToHexStringLower(idData));
#else
return new ActivitySpanId(HexConverter.ToString(idData, HexConverter.Casing.Lower));
#endif
}
public static ActivitySpanId CreateFromUtf8String(ReadOnlySpan<byte> idData) => new ActivitySpanId(idData);

Expand Down Expand Up @@ -2031,7 +2047,11 @@ private unsafe ActivitySpanId(ReadOnlySpan<byte> idData)
id = BinaryPrimitives.ReverseEndianness(id);
}

#if NET9_0_OR_GREATER
_hexString = Convert.ToHexStringLower(new ReadOnlySpan<byte>(&id, sizeof(ulong)));
#else
_hexString = HexConverter.ToString(new ReadOnlySpan<byte>(&id, sizeof(ulong)), HexConverter.Casing.Lower);
#endif
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ private static string ComputeHash(string data, string algorithm)
#pragma warning restore CA5351
}

return HexConverter.ToString(hashBuffer.Slice(0, written), HexConverter.Casing.Lower);
return Convert.ToHexStringLower(hashBuffer.Slice(0, written));
}

internal sealed class DigestResponse
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,27 +63,27 @@ public unsafe void WriteSecret()
string clientRandom = string.Empty;
if (_tlsSecrets->IsSet.ClientRandom != 0)
{
clientRandom = HexConverter.ToString(new ReadOnlySpan<byte>(_tlsSecrets->ClientRandom, 32));
clientRandom = Convert.ToHexString(new ReadOnlySpan<byte>(_tlsSecrets->ClientRandom, 32));
}
if (_tlsSecrets->IsSet.ClientHandshakeTrafficSecret != 0)
{
s_fileStream.Write(Encoding.ASCII.GetBytes($"CLIENT_HANDSHAKE_TRAFFIC_SECRET {clientRandom} {HexConverter.ToString(new ReadOnlySpan<byte>(_tlsSecrets->ClientHandshakeTrafficSecret, _tlsSecrets->SecretLength))}\n"));
s_fileStream.Write(Encoding.ASCII.GetBytes($"CLIENT_HANDSHAKE_TRAFFIC_SECRET {clientRandom} {Convert.ToHexString(new ReadOnlySpan<byte>(_tlsSecrets->ClientHandshakeTrafficSecret, _tlsSecrets->SecretLength))}\n"));
}
if (_tlsSecrets->IsSet.ServerHandshakeTrafficSecret != 0)
{
s_fileStream.Write(Encoding.ASCII.GetBytes($"SERVER_HANDSHAKE_TRAFFIC_SECRET {clientRandom} {HexConverter.ToString(new ReadOnlySpan<byte>(_tlsSecrets->ServerHandshakeTrafficSecret, _tlsSecrets->SecretLength))}\n"));
s_fileStream.Write(Encoding.ASCII.GetBytes($"SERVER_HANDSHAKE_TRAFFIC_SECRET {clientRandom} {Convert.ToHexString(new ReadOnlySpan<byte>(_tlsSecrets->ServerHandshakeTrafficSecret, _tlsSecrets->SecretLength))}\n"));
}
if (_tlsSecrets->IsSet.ClientTrafficSecret0 != 0)
{
s_fileStream.Write(Encoding.ASCII.GetBytes($"CLIENT_TRAFFIC_SECRET_0 {clientRandom} {HexConverter.ToString(new ReadOnlySpan<byte>(_tlsSecrets->ClientTrafficSecret0, _tlsSecrets->SecretLength))}\n"));
s_fileStream.Write(Encoding.ASCII.GetBytes($"CLIENT_TRAFFIC_SECRET_0 {clientRandom} {Convert.ToHexString(new ReadOnlySpan<byte>(_tlsSecrets->ClientTrafficSecret0, _tlsSecrets->SecretLength))}\n"));
}
if (_tlsSecrets->IsSet.ServerTrafficSecret0 != 0)
{
s_fileStream.Write(Encoding.ASCII.GetBytes($"SERVER_TRAFFIC_SECRET_0 {clientRandom} {HexConverter.ToString(new ReadOnlySpan<byte>(_tlsSecrets->ServerTrafficSecret0, _tlsSecrets->SecretLength))}\n"));
s_fileStream.Write(Encoding.ASCII.GetBytes($"SERVER_TRAFFIC_SECRET_0 {clientRandom} {Convert.ToHexString(new ReadOnlySpan<byte>(_tlsSecrets->ServerTrafficSecret0, _tlsSecrets->SecretLength))}\n"));
}
if (_tlsSecrets->IsSet.ClientEarlyTrafficSecret != 0)
{
s_fileStream.Write(Encoding.ASCII.GetBytes($"CLIENT_EARLY_TRAFFIC_SECRET {clientRandom} {HexConverter.ToString(new ReadOnlySpan<byte>(_tlsSecrets->ClientEarlyTrafficSecret, _tlsSecrets->SecretLength))}\n"));
s_fileStream.Write(Encoding.ASCII.GetBytes($"CLIENT_EARLY_TRAFFIC_SECRET {clientRandom} {Convert.ToHexString(new ReadOnlySpan<byte>(_tlsSecrets->ClientEarlyTrafficSecret, _tlsSecrets->SecretLength))}\n"));
}
s_fileStream.Flush();
}
Expand Down
82 changes: 79 additions & 3 deletions src/libraries/System.Private.CoreLib/src/System/Convert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3077,7 +3077,6 @@ public static string ToHexString(ReadOnlySpan<byte> bytes)
return HexConverter.ToString(bytes, HexConverter.Casing.Upper);
}


/// <summary>
/// Converts a span of 8-bit unsigned integers to its equivalent span representation that is encoded with uppercase hex characters.
/// </summary>
Expand All @@ -3092,14 +3091,91 @@ public static bool TryToHexString(ReadOnlySpan<byte> source, Span<char> destinat
charsWritten = 0;
return true;
}
else if (source.Length > int.MaxValue / 2 || destination.Length > source.Length * 2)
else if (source.Length > int.MaxValue / 2 || destination.Length > source.Length * 2)
{
charsWritten = 0;
return false;
}

HexConverter.EncodeToUtf16(source, destination);
charsWritten = source.Length * 2;
charsWritten = source.Length * 2;
return true;
}

/// <summary>
/// Converts an array of 8-bit unsigned integers to its equivalent string representation that is encoded with lowercase hex characters.
/// </summary>
/// <param name="inArray">An array of 8-bit unsigned integers.</param>
/// <returns>The string representation in hex of the elements in <paramref name="inArray"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="inArray"/> is <code>null</code>.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="inArray"/> is too large to be encoded.</exception>
public static string ToHexStringLower(byte[] inArray)
{
ArgumentNullException.ThrowIfNull(inArray);

return ToHexStringLower(new ReadOnlySpan<byte>(inArray));
}

/// <summary>
/// Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is encoded with lowercase hex characters.
/// Parameters specify the subset as an offset in the input array and the number of elements in the array to convert.
/// </summary>
/// <param name="inArray">An array of 8-bit unsigned integers.</param>
/// <param name="offset">An offset in <paramref name="inArray"/>.</param>
/// <param name="length">The number of elements of <paramref name="inArray"/> to convert.</param>
/// <returns>The string representation in hex of <paramref name="length"/> elements of <paramref name="inArray"/>, starting at position <paramref name="offset"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="inArray"/> is <code>null</code>.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> or <paramref name="length"/> is negative.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> plus <paramref name="length"/> is greater than the length of <paramref name="inArray"/>.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="inArray"/> is too large to be encoded.</exception>
public static string ToHexStringLower(byte[] inArray, int offset, int length)
{
ArgumentNullException.ThrowIfNull(inArray);

ArgumentOutOfRangeException.ThrowIfNegative(length);
ArgumentOutOfRangeException.ThrowIfNegative(offset);
ArgumentOutOfRangeException.ThrowIfGreaterThan(offset, inArray.Length - length);

return ToHexStringLower(new ReadOnlySpan<byte>(inArray, offset, length));
}

/// <summary>
/// Converts a span of 8-bit unsigned integers to its equivalent string representation that is encoded with lowercase hex characters.
/// </summary>
/// <param name="bytes">A span of 8-bit unsigned integers.</param>
/// <returns>The string representation in hex of the elements in <paramref name="bytes"/>.</returns>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="bytes"/> is too large to be encoded.</exception>
public static string ToHexStringLower(ReadOnlySpan<byte> bytes)
{
if (bytes.Length == 0)
return string.Empty;
ArgumentOutOfRangeException.ThrowIfGreaterThan(bytes.Length, int.MaxValue / 2, nameof(bytes));

return HexConverter.ToString(bytes, HexConverter.Casing.Lower);
}

/// <summary>
/// Converts a span of 8-bit unsigned integers to its equivalent span representation that is encoded with lowercase hex characters.
/// </summary>
/// <param name="source">A span of 8-bit unsigned integers.</param>
/// <param name="destination">The span representation in hex of the elements in <paramref name="source"/>.</param>
/// <param name="charsWritten">When this method returns, contains the number of chars that were written in <paramref name="destination"/>.</param>
/// <returns>true if the conversion was successful; otherwise, false.</returns>
public static bool TryToHexStringLower(ReadOnlySpan<byte> source, Span<char> destination, out int charsWritten)
{
if (source.Length == 0)
{
charsWritten = 0;
return true;
}
else if (source.Length > int.MaxValue / 2 || destination.Length > source.Length * 2)
{
charsWritten = 0;
return false;
}

HexConverter.EncodeToUtf16(source, destination, HexConverter.Casing.Lower);
charsWritten = source.Length * 2;
return true;
}
} // class Convert
Expand Down
6 changes: 5 additions & 1 deletion src/libraries/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1284,7 +1284,9 @@ 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<byte> bytes) { throw null; }
public static bool TryToHexString(System.ReadOnlySpan<byte> source, System.Span<char> destination, out int charsWritten) { throw null; }
public static string ToHexStringLower(byte[] inArray) { throw null; }
public static string ToHexStringLower(byte[] inArray, int offset, int length) { throw null; }
public static string ToHexStringLower(System.ReadOnlySpan<byte> bytes) { throw null; }
public static short ToInt16(bool value) { throw null; }
public static short ToInt16(byte value) { throw null; }
determ1ne marked this conversation as resolved.
Show resolved Hide resolved
public static short ToInt16(char value) { throw null; }
Expand Down Expand Up @@ -1577,6 +1579,8 @@ public static partial class Convert
public static bool TryFromBase64Chars(System.ReadOnlySpan<char> chars, System.Span<byte> bytes, out int bytesWritten) { throw null; }
public static bool TryFromBase64String(string s, System.Span<byte> bytes, out int bytesWritten) { throw null; }
public static bool TryToBase64Chars(System.ReadOnlySpan<byte> bytes, System.Span<char> chars, out int charsWritten, System.Base64FormattingOptions options = System.Base64FormattingOptions.None) { throw null; }
public static bool TryToHexString(System.ReadOnlySpan<byte> source, System.Span<char> destination, out int charsWritten) { throw null; }
public static bool TryToHexStringLower(System.ReadOnlySpan<byte> source, System.Span<char> destination, out int charsWritten) { throw null; }
}
public delegate TOutput Converter<in TInput, out TOutput>(TInput input);
public readonly partial struct DateOnly : System.IComparable, System.IComparable<System.DateOnly>, System.IEquatable<System.DateOnly>, System.IFormattable, System.IParsable<System.DateOnly>, System.ISpanFormattable, System.ISpanParsable<System.DateOnly>, System.IUtf8SpanFormattable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ public static void ToHexFromHexRoundtrip()
{
const int loopCount = 50;
Span<char> buffer = stackalloc char[loopCount * 2];
Span<char> bufferLower = stackalloc char[loopCount * 2];
for (int i = 1; i < loopCount; i++)
{
byte[] data = Security.Cryptography.RandomNumberGenerator.GetBytes(i);
Expand All @@ -97,6 +98,12 @@ public static void ToHexFromHexRoundtrip()
AssertExtensions.SequenceEqual(hex.AsSpan(), currentBuffer);
Assert.Equal(hex.Length, written);

Span<char> currentBufferLower = bufferLower.Slice(0, i * 2);
tryHex = Convert.TryToHexStringLower(data, currentBufferLower, out written);
Assert.True(tryHex);
AssertExtensions.SequenceEqual(hex.ToLowerInvariant().AsSpan(), currentBufferLower);
Assert.Equal(hex.Length, written);

TestSequence(data, hex);
TestSequence(data, hex.ToLowerInvariant());
TestSequence(data, hex.ToUpperInvariant());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using System.Collections.Generic;

namespace System.Tests
{
{
public class ConvertToHexStringTests
{
[Fact]
Expand All @@ -16,6 +16,13 @@ public static void KnownByteSequence()
Assert.Equal("000102FDFEFF", Convert.ToHexString(inputBytes));
}

[Fact]
public static void KnownByteSequenceLower()
{
byte[] inputBytes = new byte[] { 0x00, 0x01, 0x02, 0xFD, 0xFE, 0xFF };
Assert.Equal("000102fdfeff", Convert.ToHexStringLower(inputBytes));
}

[Fact]
public static void CompleteValueRange()
{
Expand All @@ -30,18 +37,35 @@ public static void CompleteValueRange()
Assert.Equal(sb.ToString(), Convert.ToHexString(values));
}

[Fact]
public static void CompleteValueRangeLower()
{
byte[] values = new byte[256];
StringBuilder sb = new StringBuilder(256);
for (int i = 0; i < values.Length; i++)
{
values[i] = (byte)i;
sb.Append($"{i:x2}");
}

Assert.Equal(sb.ToString(), Convert.ToHexStringLower(values));
}

[Fact]
public static void ZeroLength()
{
byte[] inputBytes = Convert.FromHexString("000102FDFEFF");
Assert.Same(string.Empty, Convert.ToHexString(inputBytes, 0, 0));
Assert.Same(string.Empty, Convert.ToHexStringLower(inputBytes, 0, 0));
}

[Fact]
public static void InvalidInputBuffer()
{
AssertExtensions.Throws<ArgumentNullException>("inArray", () => Convert.ToHexString(null));
AssertExtensions.Throws<ArgumentNullException>("inArray", () => Convert.ToHexString(null, 0, 0));
AssertExtensions.Throws<ArgumentNullException>("inArray", () => Convert.ToHexStringLower(null));
AssertExtensions.Throws<ArgumentNullException>("inArray", () => Convert.ToHexStringLower(null, 0, 0));
}

[Fact]
Expand All @@ -50,6 +74,8 @@ public static void InvalidOffset()
byte[] inputBytes = Convert.FromHexString("000102FDFEFF");
AssertExtensions.Throws<ArgumentOutOfRangeException>("offset", () => Convert.ToHexString(inputBytes, -1, inputBytes.Length));
AssertExtensions.Throws<ArgumentOutOfRangeException>("offset", () => Convert.ToHexString(inputBytes, inputBytes.Length, inputBytes.Length));
AssertExtensions.Throws<ArgumentOutOfRangeException>("offset", () => Convert.ToHexStringLower(inputBytes, -1, inputBytes.Length));
AssertExtensions.Throws<ArgumentOutOfRangeException>("offset", () => Convert.ToHexStringLower(inputBytes, inputBytes.Length, inputBytes.Length));
}

[Fact]
Expand All @@ -59,12 +85,16 @@ public static void InvalidLength()
AssertExtensions.Throws<ArgumentOutOfRangeException>("length", () => Convert.ToHexString(inputBytes, 0, -1));
AssertExtensions.Throws<ArgumentOutOfRangeException>("offset", () => Convert.ToHexString(inputBytes, 0, inputBytes.Length + 1));
AssertExtensions.Throws<ArgumentOutOfRangeException>("offset", () => Convert.ToHexString(inputBytes, 1, inputBytes.Length));
AssertExtensions.Throws<ArgumentOutOfRangeException>("length", () => Convert.ToHexStringLower(inputBytes, 0, -1));
AssertExtensions.Throws<ArgumentOutOfRangeException>("offset", () => Convert.ToHexStringLower(inputBytes, 0, inputBytes.Length + 1));
AssertExtensions.Throws<ArgumentOutOfRangeException>("offset", () => Convert.ToHexStringLower(inputBytes, 1, inputBytes.Length));
}

[Fact]
public static unsafe void InputTooLarge()
{
AssertExtensions.Throws<ArgumentOutOfRangeException>("bytes", () => Convert.ToHexString(new ReadOnlySpan<byte>((void*)0, Int32.MaxValue)));
AssertExtensions.Throws<ArgumentOutOfRangeException>("bytes", () => Convert.ToHexStringLower(new ReadOnlySpan<byte>((void*)0, Int32.MaxValue)));
}

public static IEnumerable<object[]> ToHexStringTestData()
Expand Down Expand Up @@ -106,5 +136,13 @@ public static unsafe void ToHexString(byte[] input, string expected)
string actual = Convert.ToHexString(input);
Assert.Equal(expected, actual);
}

[Theory]
[MemberData(nameof(ToHexStringTestData))]
public static unsafe void ToHexStringLower(byte[] input, string expected)
{
string actual = Convert.ToHexStringLower(input);
Assert.Equal(expected.ToLower(), actual);
}
determ1ne marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,12 @@ public static string ToSerialString(this byte[] serialBytes)
return ToUpperHexString(serialBytes);
}

#if NETCOREAPP || NETSTANDARD2_1
#if NET5_0_OR_GREATER
private static string ToUpperHexString(ReadOnlySpan<byte> ba)
{
return Convert.ToHexString(ba);
}
#elif NETCOREAPP || NETSTANDARD2_1
determ1ne marked this conversation as resolved.
Show resolved Hide resolved
private static string ToUpperHexString(ReadOnlySpan<byte> ba)
{
return HexConverter.ToString(ba, HexConverter.Casing.Upper);
Expand Down
Loading
Loading