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

Base58 perfomance patch #3186

Merged
merged 26 commits into from
Dec 19, 2024
Merged
Changes from 22 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d9379be
Base58 perfomance improvments
kzorin52 Mar 28, 2024
66bbd57
Fix usings
kzorin52 Mar 28, 2024
8769111
Update src/Neo/Cryptography/Base58.cs
Jim8y Mar 28, 2024
fb882ca
Base58 improvments #2
kzorin52 Mar 29, 2024
194c456
Update src/Neo/Cryptography/Base58.cs
shargon Mar 29, 2024
a1ce6ab
Merge branch 'master' into base58-patch
Jim8y Apr 4, 2024
6b76b85
Merge branch 'master' into base58-patch
Jim8y Apr 11, 2024
0b06541
Merge branch 'master' into base58-patch
cschuchardt88 May 16, 2024
696318c
Merge branch 'master' into base58-patch
Jim8y May 17, 2024
e437511
Merge branch 'master' into base58-patch
shargon May 20, 2024
f8c7bc8
Merge branch 'master' into base58-patch
cschuchardt88 Jun 9, 2024
9a338e6
Merge branch 'master' into base58-patch
cschuchardt88 Jun 27, 2024
faeb909
fix perfomance
kzorin52 Oct 14, 2024
1b641d8
remove Ext, further optimisations
kzorin52 Oct 14, 2024
1a3c0c2
Merge branch 'master' into base58-patch
kzorin52 Oct 16, 2024
bd3a205
Clean
shargon Oct 17, 2024
2682e8b
format
kzorin52 Oct 18, 2024
babf4a4
Merge branch 'master' into base58-patch
Jim8y Nov 6, 2024
a1ff531
Merge branch 'master' into base58-patch
shargon Nov 6, 2024
8c2c577
Reduce array
shargon Nov 6, 2024
ea3f6d8
reuse var
shargon Nov 6, 2024
8840fb3
same name as before
shargon Nov 6, 2024
bfd9ae1
Merge branch 'master' into base58-patch
shargon Dec 5, 2024
3163eb3
Merge branch 'master' into base58-patch
cschuchardt88 Dec 9, 2024
4e598fb
Merge branch 'master' into base58-patch
Jim8y Dec 19, 2024
260c9fe
Merge branch 'master' into base58-patch
NGDAdmin Dec 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 54 additions & 14 deletions src/Neo/Cryptography/Base58.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using System;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;

namespace Neo.Cryptography
Expand All @@ -25,6 +26,21 @@ public static class Base58
/// Represents the alphabet of the base-58 encoder.
/// </summary>
public const string Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
private const char ZeroChar = '1';
private static readonly BigInteger s_alphabetLength = Alphabet.Length;

#pragma warning disable format
private static readonly sbyte[] s_decodeMap =
[
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6,
7, 8, -1, -1, -1, -1, -1, -1, -1, 9, 10, 11, 12, 13, 14, 15, 16, -1, 17,
18, 19, 20, 21, -1, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1, -1,
-1, -1, -1, -1, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, -1, 44, 45,
46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57
];
#pragma warning restore format

/// <summary>
/// Converts the specified <see cref="string"/>, which encodes binary data as base-58 digits, to an equivalent byte array. The encoded <see cref="string"/> contains the checksum of the binary data.
Expand Down Expand Up @@ -71,21 +87,30 @@ public static byte[] Decode(string input)
{
// Decode Base58 string to BigInteger
var bi = BigInteger.Zero;
for (int i = 0; i < input.Length; i++)
sbyte digit;
for (var i = 0; i < input.Length; i++)
{
int digit = Alphabet.IndexOf(input[i]);
if (digit < 0)
if (input[i] >= 123)
throw new FormatException($"Invalid Base58 character '{input[i]}' at position {i}");
digit = s_decodeMap[input[i]];
if (digit == -1)
throw new FormatException($"Invalid Base58 character '{input[i]}' at position {i}");
bi = bi * Alphabet.Length + digit;
bi = bi * s_alphabetLength + digit;
}

// Encode BigInteger to byte[]
// Leading zero bytes get encoded as leading `1` characters
int leadingZeroCount = input.TakeWhile(c => c == Alphabet[0]).Count();
var leadingZeros = new byte[leadingZeroCount];
if (bi.IsZero) return leadingZeros;
var bytesWithoutLeadingZeros = bi.ToByteArray(isUnsigned: true, isBigEndian: true);
return [.. leadingZeros, .. bytesWithoutLeadingZeros];

var leadingZeroCount = LeadingBase58Zeros(input);
if (bi.IsZero)
{
return new byte[leadingZeroCount];
}

var result = new byte[leadingZeroCount + bi.GetByteCount(true)];

_ = bi.TryWriteBytes(result.AsSpan(leadingZeroCount), out _, true, true);
return result;
}

/// <summary>
Expand All @@ -99,20 +124,35 @@ public static string Encode(ReadOnlySpan<byte> input)
BigInteger value = new(input, isUnsigned: true, isBigEndian: true);

// Encode BigInteger to Base58 string
var sb = new StringBuilder();
var sb = new StringBuilder(input.Length * 138 / 100 + 5);
shargon marked this conversation as resolved.
Show resolved Hide resolved

while (value > 0)
{
value = BigInteger.DivRem(value, Alphabet.Length, out var remainder);
sb.Insert(0, Alphabet[(int)remainder]);
value = BigInteger.DivRem(value, s_alphabetLength, out var remainder);
sb.Append(Alphabet[(int)remainder]);
}

// Append `1` for each leading 0 byte
for (int i = 0; i < input.Length && input[i] == 0; i++)
{
sb.Insert(0, Alphabet[0]);
sb.Append(ZeroChar);
}
return sb.ToString();

Span<char> copy = stackalloc char[sb.Length];
sb.CopyTo(0, copy, sb.Length);
copy.Reverse();
shargon marked this conversation as resolved.
Show resolved Hide resolved

return copy.ToString();
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int LeadingBase58Zeros(string collection)
shargon marked this conversation as resolved.
Show resolved Hide resolved
{
var i = 0;
var len = collection.Length;
for (; i < len && collection[i] == ZeroChar; i++) { }

return i;
}
}
}
Loading