Skip to content

Commit

Permalink
[Fix] StackItem.GetHashCode for CompoundType (#3549)
Browse files Browse the repository at this point in the history
* Fixed #3544

* Fixed SubItems of CompoundType

* Reused `Unsafe.HashBytes` for hashing function in compoundtype

* Fixed test

* simple hashcode for compoundtype

* Test circular reference

* Fixed compoundType

* Add one more test case

* Fixed test

* changed to use subclass

* add comment

* Add more to GetHashCode for CompoundType

* Clean code

---------

Co-authored-by: Jimmy <[email protected]>
Co-authored-by: Fernando Diaz Toledano <[email protected]>
Co-authored-by: NGD Admin <[email protected]>
  • Loading branch information
4 people authored Nov 6, 2024
1 parent 25554df commit 42f0efd
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 9 deletions.
26 changes: 23 additions & 3 deletions src/Neo.VM/Types/CompoundType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,32 @@ public sealed override bool GetBoolean()
}

/// <summary>
/// The operation is not supported. Always throw <see cref="NotSupportedException"/>.
///
/// This method provides a hash code for the <see cref="CompoundType"/> based on its item's span.
/// It is used for efficient storage and retrieval in hash-based collections.
///
/// Use this method when you need a hash code for a <see cref="CompoundType"/>.
/// </summary>
/// <exception cref="NotSupportedException">This method always throws the exception.</exception>
/// <returns>The hash code for the <see cref="CompoundType"/>.</returns>
public override int GetHashCode()
{
throw new NotSupportedException();
var h = new HashCode();
h.Add(Count);
h.Add(Type);
foreach (var item in SubItems)
{
// This isn't prefect and leaves somethings unsolved.
if (item is CompoundType cItem)
{
h.Add(cItem.Count);
h.Add(cItem.Type);
}
else
{
h.Add(item.GetHashCode());
}
}
return h.ToHashCode();
}

public override string ToString()
Expand Down
45 changes: 39 additions & 6 deletions tests/Neo.VM.Tests/UT_StackItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@ namespace Neo.Test
[TestClass]
public class UT_StackItem
{
[TestMethod]
public void TestCircularReference()
{
var itemA = new Struct { true, false };
var itemB = new Struct { true, false };
var itemC = new Struct { false, false };

itemA[1] = itemA;
itemB[1] = itemB;
itemC[1] = itemC;

Assert.AreEqual(itemA.GetHashCode(), itemB.GetHashCode());
Assert.AreNotEqual(itemA.GetHashCode(), itemC.GetHashCode());
}

[TestMethod]
public void TestHashCode()
{
Expand Down Expand Up @@ -62,17 +77,35 @@ public void TestHashCode()

Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode());

itemA = new VM.Types.Array();
itemA = new Array { true, false, 0 };
itemB = new Array { true, false, 0 };
itemC = new Array { true, false, 1 };

Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode());
Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode());

itemA = new Struct { true, false, 0 };
itemB = new Struct { true, false, 0 };
itemC = new Struct { true, false, 1 };

Assert.ThrowsException<System.NotSupportedException>(() => itemA.GetHashCode());
Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode());
Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode());

itemA = new Struct();
itemA = new Map { [true] = false, [0] = 1 };
itemB = new Map { [true] = false, [0] = 1 };
itemC = new Map { [true] = false, [0] = 2 };

Assert.ThrowsException<System.NotSupportedException>(() => itemA.GetHashCode());
Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode());
Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode());

itemA = new Map();
// Test CompoundType GetHashCode for subitems
var junk = new Array { true, false, 0 };
itemA = new Map { [true] = junk, [0] = junk };
itemB = new Map { [true] = junk, [0] = junk };
itemC = new Map { [true] = junk, [0] = 2 };

Assert.ThrowsException<System.NotSupportedException>(() => itemA.GetHashCode());
Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode());
Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode());

itemA = new InteropInterface(123);
itemB = new InteropInterface(123);
Expand Down

0 comments on commit 42f0efd

Please sign in to comment.