Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 13 additions & 0 deletions src/Base58Encoding.Benchmarks/CountLeadingZerosBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,19 @@ public int Combined()
return totalCount;
}

[Benchmark(Description = "IndexOfAnyExcept")]
public int IndexOfAnyExceptScan()
{
var totalCount = 0;
foreach (var data in TestData)
{
var index = data.AsSpan().IndexOfAnyExcept((byte)0);
totalCount += index < 0 ? data.Length : index;
}

return totalCount;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int CountLeadingZerosArray(ReadOnlySpan<byte> data)
{
Expand Down
43 changes: 17 additions & 26 deletions src/Base58Encoding.Tests/SimpleLeadingZerosTest.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
using System.Runtime.Intrinsics;

namespace Base58Encoding.Tests;

public class SimpleLeadingZerosTest
{
[Fact]
public void BitcoinAddress_CountLeadingZerosMultipleWays_SameResult()
public void BitcoinAddress_CountLeadingZeros_MatchesManualCount()
{
var address = "1111111111111111111114oLvT2";
var decoded = Base58.Bitcoin.Decode(address);
Expand All @@ -18,16 +16,7 @@ public void BitcoinAddress_CountLeadingZerosMultipleWays_SameResult()
manualCount++;
}

// Test SIMD
int simdCount = Base58.CountLeadingZerosSimd(decoded, out int processed);
var simdScalarCount = 0;
if (simdCount >= processed)
{
int remaining = Base58.CountLeadingZerosScalar(decoded.AsSpan(simdCount));
simdScalarCount = simdCount + remaining;
}

Assert.Equal(simdScalarCount, manualCount);
Assert.Equal(manualCount, Base58.CountLeadingZeros(decoded));
}

[Theory]
Expand All @@ -38,33 +27,35 @@ public void BitcoinAddress_CountLeadingZerosMultipleWays_SameResult()
[InlineData(31)]
public void CountLeadingZeros_32Size_ReturnsCorrectNumber(int zerosCount)
{
Assert.SkipUnless(Vector256.IsHardwareAccelerated, "Requires Vector256 hardware acceleration");

// Arrange
var data = new byte[32];
data.AsSpan(0, zerosCount).Fill(0x00);
Random.Shared.NextBytes(data.AsSpan(zerosCount));
data[zerosCount] = 1;

// Act
var result = Base58.CountLeadingZerosSimd(data, out var processed);

Assert.Equal(zerosCount, result);
Assert.Equal(data.Length, processed);
// Act / Assert
Assert.Equal(zerosCount, Base58.CountLeadingZeros(data));
}

[Fact]
public void CountLeadingZeros_512Size_ReturnsCorrectNumber()
{
Assert.SkipUnless(Vector256.IsHardwareAccelerated, "Requires Vector256 hardware acceleration");

// Arrange
var zerosCount = 123;
var data = new byte[512];
data.AsSpan(0, zerosCount).Fill(0x00);
Random.Shared.NextBytes(data.AsSpan(zerosCount));
// Act
var result = Base58.CountLeadingZerosSimd(data, out var processed);
Assert.Equal(zerosCount, result);
Assert.Equal(Vector256<byte>.Count * 4, processed); // Vector256 used 4 times
data[zerosCount] = 1;

// Act / Assert
Assert.Equal(zerosCount, Base58.CountLeadingZeros(data));
}

[Fact]
public void CountLeadingZeros_AllZeros_ReturnsLength()
{
var data = new byte[40];

Assert.Equal(data.Length, Base58.CountLeadingZeros(data));
}
}
1 change: 1 addition & 0 deletions src/Base58Encoding/Base58.CountLeading.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ internal static int CountLeadingZerosScalar(ReadOnlySpan<byte> data)
}
return count;
}

count += sizeof(ulong);
length -= sizeof(ulong);
}
Expand Down
6 changes: 3 additions & 3 deletions src/Base58Encoding/Base58.Decode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public sealed partial class Base58<TAlphabet>
/// <param name="encoded">Base58 encoded input.</param>
/// <returns>Decoded byte array.</returns>
/// <exception cref="ArgumentException">Invalid Base58 character.</exception>
public byte[] Decode(ReadOnlySpan<char> encoded)
public byte[] Decode(scoped ReadOnlySpan<char> encoded)
{
if (encoded.IsEmpty)
{
Expand Down Expand Up @@ -53,7 +53,7 @@ public byte[] Decode(ReadOnlySpan<char> encoded)
/// <exception cref="ArgumentException">
/// Thrown on invalid Base58 character or when <paramref name="destination"/> is too small.
/// </exception>
public int Decode(ReadOnlySpan<char> encoded, Span<byte> destination)
public int Decode(scoped ReadOnlySpan<char> encoded, scoped Span<byte> destination)
{
if (encoded.IsEmpty)
{
Expand All @@ -72,7 +72,7 @@ public int Decode(ReadOnlySpan<char> encoded, Span<byte> destination)
/// <exception cref="ArgumentException">
/// Thrown on invalid Base58 character or when <paramref name="destination"/> is too small.
/// </exception>
public int Decode(ReadOnlySpan<byte> encoded, Span<byte> destination)
public int Decode(scoped ReadOnlySpan<byte> encoded, scoped Span<byte> destination)
{
if (encoded.IsEmpty)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Base58Encoding/Base58.Encode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public sealed partial class Base58<TAlphabet>
/// </summary>
/// <param name="data">Bytes to encode.</param>
/// <returns>Base58 encoded string.</returns>
public string Encode(ReadOnlySpan<byte> data)
public string Encode(scoped ReadOnlySpan<byte> data)
{
if (data.IsEmpty)
{
Expand All @@ -41,7 +41,7 @@ public string Encode(ReadOnlySpan<byte> data)
/// <param name="destination">Destination buffer for ASCII-encoded Base58 characters.</param>
/// <returns>Number of bytes written to <paramref name="destination"/>.</returns>
/// <exception cref="ArgumentException">Thrown if <paramref name="destination"/> is too small.</exception>
public int Encode(ReadOnlySpan<byte> data, Span<byte> destination)
public int Encode(scoped ReadOnlySpan<byte> data, scoped Span<byte> destination)
{
if (data.IsEmpty)
{
Expand Down
Loading