From 3990c30efab1fe1f4772bb1a640ab1d47edbaee6 Mon Sep 17 00:00:00 2001 From: nan01ab Date: Tue, 5 Nov 2024 00:55:18 +0800 Subject: [PATCH 1/6] fea: optimize ToHexString --- src/Neo.Extensions/ByteExtensions.cs | 54 ++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/src/Neo.Extensions/ByteExtensions.cs b/src/Neo.Extensions/ByteExtensions.cs index 013a8ef1cc..f41c3ac4b8 100644 --- a/src/Neo.Extensions/ByteExtensions.cs +++ b/src/Neo.Extensions/ByteExtensions.cs @@ -10,23 +10,36 @@ // modifications are permitted. using System; +using System.Runtime.CompilerServices; using System.Text; namespace Neo.Extensions { public static class ByteExtensions { + private const string HexChars = "0123456789abcdef"; + /// /// Converts a byte array to hex . /// /// The byte array to convert. /// The converted hex . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string ToHexString(this byte[] value) { - StringBuilder sb = new(); - foreach (var b in value) - sb.AppendFormat("{0:x2}", b); - return sb.ToString(); +#if NET9_0_OR_GREATER + return Convert.ToHexStringLower(value); +#else + return string.Create(value.Length * 2, value, (span, bytes) => + { + for (int i = 0; i < bytes.Length; i++) + { + byte b = bytes[i]; + span[i * 2] = HexChars[b >> 4]; + span[i * 2 + 1] = HexChars[b & 0xF]; + } + }); +#endif } /// @@ -35,12 +48,21 @@ public static string ToHexString(this byte[] value) /// The byte array to convert. /// Indicates whether it should be converted in the reversed byte order. /// The converted hex . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string ToHexString(this byte[] value, bool reverse = false) { - StringBuilder sb = new(); - for (var i = 0; i < value.Length; i++) - sb.AppendFormat("{0:x2}", value[reverse ? value.Length - i - 1 : i]); - return sb.ToString(); + if (!reverse) + return ToHexString(value); + + return string.Create(value.Length * 2, value, (span, bytes) => + { + for (int i = 0; i < bytes.Length; i++) + { + byte b = bytes[bytes.Length - i - 1]; + span[i * 2] = HexChars[b >> 4]; + span[i * 2 + 1] = HexChars[b & 0xF]; + } + }); } /// @@ -48,12 +70,22 @@ public static string ToHexString(this byte[] value, bool reverse = false) /// /// The byte array to convert. /// The converted hex . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string ToHexString(this ReadOnlySpan value) { - StringBuilder sb = new(); - foreach (var b in value) - sb.AppendFormat("{0:x2}", b); +#if NET9_0_OR_GREATER + return Convert.ToHexStringLower(value); +#else + // string.Create with ReadOnlySpan not supported in NET5 or lower + var sb = new StringBuilder(value.Length * 2); + for (int i = 0; i < value.Length; i++) + { + byte b = value[i]; + sb.Append(HexChars[b >> 4]); + sb.Append(HexChars[b & 0xF]); + } return sb.ToString(); +#endif } } } From ac15bd3924ba0f9b679a221451957d8fca96a1fe Mon Sep 17 00:00:00 2001 From: Fernando Diaz Toledano Date: Mon, 4 Nov 2024 19:05:11 +0100 Subject: [PATCH 2/6] use char array instead string --- src/Neo.Extensions/ByteExtensions.cs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Neo.Extensions/ByteExtensions.cs b/src/Neo.Extensions/ByteExtensions.cs index f41c3ac4b8..236c81b473 100644 --- a/src/Neo.Extensions/ByteExtensions.cs +++ b/src/Neo.Extensions/ByteExtensions.cs @@ -17,7 +17,7 @@ namespace Neo.Extensions { public static class ByteExtensions { - private const string HexChars = "0123456789abcdef"; + private static readonly char[] s_hexChars = "0123456789abcdef".ToCharArray(); /// /// Converts a byte array to hex . @@ -32,11 +32,11 @@ public static string ToHexString(this byte[] value) #else return string.Create(value.Length * 2, value, (span, bytes) => { - for (int i = 0; i < bytes.Length; i++) + for (var i = 0; i < bytes.Length; i++) { - byte b = bytes[i]; - span[i * 2] = HexChars[b >> 4]; - span[i * 2 + 1] = HexChars[b & 0xF]; + var b = bytes[i]; + span[i * 2] = s_hexChars[b >> 4]; + span[i * 2 + 1] = s_hexChars[b & 0xF]; } }); #endif @@ -56,11 +56,11 @@ public static string ToHexString(this byte[] value, bool reverse = false) return string.Create(value.Length * 2, value, (span, bytes) => { - for (int i = 0; i < bytes.Length; i++) + for (var i = 0; i < bytes.Length; i++) { - byte b = bytes[bytes.Length - i - 1]; - span[i * 2] = HexChars[b >> 4]; - span[i * 2 + 1] = HexChars[b & 0xF]; + var b = bytes[bytes.Length - i - 1]; + span[i * 2] = s_hexChars[b >> 4]; + span[i * 2 + 1] = s_hexChars[b & 0xF]; } }); } @@ -78,11 +78,11 @@ public static string ToHexString(this ReadOnlySpan value) #else // string.Create with ReadOnlySpan not supported in NET5 or lower var sb = new StringBuilder(value.Length * 2); - for (int i = 0; i < value.Length; i++) + for (var i = 0; i < value.Length; i++) { - byte b = value[i]; - sb.Append(HexChars[b >> 4]); - sb.Append(HexChars[b & 0xF]); + var b = value[i]; + sb.Append(s_hexChars[b >> 4]); + sb.Append(s_hexChars[b & 0xF]); } return sb.ToString(); #endif From 2bbb8fe8842e4034ba2db3f81e45e19784ed23fc Mon Sep 17 00:00:00 2001 From: nan01ab Date: Tue, 5 Nov 2024 09:25:37 +0800 Subject: [PATCH 3/6] 1. const string is faster; 2. Scalar.ToString uses ToHexString --- .../Neo.Cryptography.BLS12_381.csproj | 4 ++++ src/Neo.Cryptography.BLS12_381/Scalar.cs | 9 ++------- src/Neo.Extensions/ByteExtensions.cs | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Neo.Cryptography.BLS12_381/Neo.Cryptography.BLS12_381.csproj b/src/Neo.Cryptography.BLS12_381/Neo.Cryptography.BLS12_381.csproj index f74c9f1ec0..cbed7d23b8 100644 --- a/src/Neo.Cryptography.BLS12_381/Neo.Cryptography.BLS12_381.csproj +++ b/src/Neo.Cryptography.BLS12_381/Neo.Cryptography.BLS12_381.csproj @@ -13,4 +13,8 @@ + + + + diff --git a/src/Neo.Cryptography.BLS12_381/Scalar.cs b/src/Neo.Cryptography.BLS12_381/Scalar.cs index ef40e9b817..ccea2bb448 100644 --- a/src/Neo.Cryptography.BLS12_381/Scalar.cs +++ b/src/Neo.Cryptography.BLS12_381/Scalar.cs @@ -9,11 +9,11 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Cryptography; -using System.Text; using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; using static Neo.Cryptography.BLS12_381.MathUtility; using static Neo.Cryptography.BLS12_381.ScalarConstants; @@ -119,12 +119,7 @@ private Span GetSpanU64() public override string ToString() { - byte[] bytes = ToArray(); - StringBuilder sb = new(); - sb.Append("0x"); - for (int i = bytes.Length - 1; i >= 0; i--) - sb.AppendFormat("{0:x2}", bytes[i]); - return sb.ToString(); + return "0x" + ToArray().ToHexString(true); } public static bool operator ==(in Scalar a, in Scalar b) diff --git a/src/Neo.Extensions/ByteExtensions.cs b/src/Neo.Extensions/ByteExtensions.cs index 236c81b473..73aed3e388 100644 --- a/src/Neo.Extensions/ByteExtensions.cs +++ b/src/Neo.Extensions/ByteExtensions.cs @@ -17,7 +17,7 @@ namespace Neo.Extensions { public static class ByteExtensions { - private static readonly char[] s_hexChars = "0123456789abcdef".ToCharArray(); + private const string s_hexChars = "0123456789abcdef"; /// /// Converts a byte array to hex . From d89e67807d1fc8748a9754c1937d3f2be7f0caad Mon Sep 17 00:00:00 2001 From: Fernando Diaz Toledano Date: Tue, 5 Nov 2024 09:36:15 +0100 Subject: [PATCH 4/6] Use StringBuilder --- src/Neo.Cryptography.BLS12_381/Scalar.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Neo.Cryptography.BLS12_381/Scalar.cs b/src/Neo.Cryptography.BLS12_381/Scalar.cs index ccea2bb448..7a00486b66 100644 --- a/src/Neo.Cryptography.BLS12_381/Scalar.cs +++ b/src/Neo.Cryptography.BLS12_381/Scalar.cs @@ -14,6 +14,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Cryptography; +using System.Text; using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; using static Neo.Cryptography.BLS12_381.MathUtility; using static Neo.Cryptography.BLS12_381.ScalarConstants; @@ -119,7 +120,10 @@ private Span GetSpanU64() public override string ToString() { - return "0x" + ToArray().ToHexString(true); + StringBuilder sb = new(); + sb.Append("0x"); + sb.Append(ToArray().ToHexString(true)); + return sb.ToString(); } public static bool operator ==(in Scalar a, in Scalar b) From d977dfcf4b31074113d12dec3c4a88647fb7c897 Mon Sep 17 00:00:00 2001 From: Fernando Diaz Toledano Date: Tue, 5 Nov 2024 09:42:52 +0100 Subject: [PATCH 5/6] Optimize capacity --- src/Neo.Cryptography.BLS12_381/Scalar.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Neo.Cryptography.BLS12_381/Scalar.cs b/src/Neo.Cryptography.BLS12_381/Scalar.cs index 7a00486b66..ffcc074f8f 100644 --- a/src/Neo.Cryptography.BLS12_381/Scalar.cs +++ b/src/Neo.Cryptography.BLS12_381/Scalar.cs @@ -120,9 +120,11 @@ private Span GetSpanU64() public override string ToString() { - StringBuilder sb = new(); + var data = ToArray(); + + StringBuilder sb = new(2 + (data.Length * 2)); sb.Append("0x"); - sb.Append(ToArray().ToHexString(true)); + sb.Append(data.ToHexString(true)); return sb.ToString(); } From d8cb4d90289af8ac192d6a79a942eb23c9140178 Mon Sep 17 00:00:00 2001 From: Fernando Diaz Toledano Date: Tue, 5 Nov 2024 09:43:07 +0100 Subject: [PATCH 6/6] Reduce changes --- src/Neo.Cryptography.BLS12_381/Scalar.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Neo.Cryptography.BLS12_381/Scalar.cs b/src/Neo.Cryptography.BLS12_381/Scalar.cs index ffcc074f8f..a4e93efe55 100644 --- a/src/Neo.Cryptography.BLS12_381/Scalar.cs +++ b/src/Neo.Cryptography.BLS12_381/Scalar.cs @@ -120,11 +120,11 @@ private Span GetSpanU64() public override string ToString() { - var data = ToArray(); + var bytes = ToArray(); - StringBuilder sb = new(2 + (data.Length * 2)); + StringBuilder sb = new(2 + (bytes.Length * 2)); sb.Append("0x"); - sb.Append(data.ToHexString(true)); + sb.Append(bytes.ToHexString(true)); return sb.ToString(); }