Skip to content

Commit

Permalink
[Fix] unalignment memory load in Neo.VM Unsafe.NotZero(ReadOnlySpan…
Browse files Browse the repository at this point in the history
…<byte>) and remove the use of `unsafe` (#3492)

* fix: unalignment load in ReadOnlySpan<byte>.NotZero

* Update .gitignore

* use SequenceEqual to compare bytes for better performace

* use ContainsAnyExcept to get better performace and simply code

* use ContainsAnyExcept to get better performance and simply code

* use ContainsAnyExcept to get better performance and simply code

* fix comment

* merge master and avoid unnecessary change

* move notzero test

* change to block namespace

* Add attribute

---------

Co-authored-by: Shargon <[email protected]>
Co-authored-by: Christopher Schuchardt <[email protected]>
Co-authored-by: Jimmy <[email protected]>
  • Loading branch information
4 people authored Nov 7, 2024
1 parent 42f0efd commit 64e13bb
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 10 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,8 @@ paket-files/
PublishProfiles
/.vscode
launchSettings.json
/coverages
**/.DS_Store

# Benchmarks
**/BenchmarkDotNet.Artifacts/
**/BenchmarkDotNet.Artifacts/
2 changes: 1 addition & 1 deletion src/Neo.VM/Types/ByteString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ internal bool Equals(StackItem? other, ref uint limits)
public override bool GetBoolean()
{
if (Size > Integer.MaxSize) throw new InvalidCastException();
return Unsafe.NotZero(GetSpan());
return GetSpan().NotZero();
}

public override BigInteger GetInteger()
Expand Down
11 changes: 10 additions & 1 deletion src/Neo.VM/Unsafe.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,17 @@ unsafe internal static class Unsafe
{
private const long DefaultXxHash3Seed = 40343;

/// <summary>
/// All bytes are zero or not in a byte array
/// </summary>
/// <param name="x">The byte array</param>
/// <returns>false if all bytes are zero, true otherwise</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool NotZero(ReadOnlySpan<byte> x)
public static bool NotZero(this ReadOnlySpan<byte> x)
{
#if NET7_0_OR_GREATER
return x.IndexOfAnyExcept((byte)0) >= 0;
#else
int len = x.Length;
if (len == 0) return false;
fixed (byte* xp = x)
Expand All @@ -40,6 +48,7 @@ public static bool NotZero(ReadOnlySpan<byte> x)
}
}
return false;
#endif
}

/// <summary>
Expand Down
33 changes: 26 additions & 7 deletions tests/Neo.VM.Tests/UT_Unsafe.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.VM;
using System;

namespace Neo.Test
{
Expand All @@ -20,14 +21,32 @@ public class UT_Unsafe
[TestMethod]
public void TestNotZero()
{
Assert.IsFalse(Unsafe.NotZero(System.Array.Empty<byte>()));
Assert.IsFalse(Unsafe.NotZero(new byte[4]));
Assert.IsFalse(Unsafe.NotZero(new byte[8]));
Assert.IsFalse(Unsafe.NotZero(new byte[11]));
Assert.IsFalse(new ReadOnlySpan<byte>(System.Array.Empty<byte>()).NotZero());
Assert.IsFalse(new ReadOnlySpan<byte>(new byte[4]).NotZero());
Assert.IsFalse(new ReadOnlySpan<byte>(new byte[7]).NotZero());
Assert.IsFalse(new ReadOnlySpan<byte>(new byte[8]).NotZero());
Assert.IsFalse(new ReadOnlySpan<byte>(new byte[9]).NotZero());
Assert.IsFalse(new ReadOnlySpan<byte>(new byte[11]).NotZero());

Assert.IsTrue(Unsafe.NotZero(new byte[4] { 0x00, 0x00, 0x00, 0x01 }));
Assert.IsTrue(Unsafe.NotZero(new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }));
Assert.IsTrue(Unsafe.NotZero(new byte[11] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }));
Assert.IsTrue(new ReadOnlySpan<byte>(new byte[4] { 0x00, 0x00, 0x00, 0x01 }).NotZero());
Assert.IsTrue(new ReadOnlySpan<byte>(new byte[7] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }).NotZero());
Assert.IsTrue(new ReadOnlySpan<byte>(new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }).NotZero());
Assert.IsTrue(new ReadOnlySpan<byte>(new byte[9] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }).NotZero());
Assert.IsTrue(new ReadOnlySpan<byte>(new byte[11] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }).NotZero());

var bytes = new byte[64];
for (int i = 0; i < bytes.Length; i++)
{
ReadOnlySpan<byte> span = bytes.AsSpan();
Assert.IsFalse(span[i..].NotZero());

for (int j = i; j < bytes.Length; j++)
{
bytes[j] = 0x01;
Assert.IsTrue(span[i..].NotZero());
bytes[j] = 0x00;
}
}
}
}
}

0 comments on commit 64e13bb

Please sign in to comment.