Skip to content

Commit 48b54d2

Browse files
committed
Adding test and implementation of on hash collision nodes in multi-map.
Refers to issue #16.
1 parent a964308 commit 48b54d2

File tree

7 files changed

+255
-28
lines changed

7 files changed

+255
-28
lines changed

capsule-core/src/main/java/io/usethesource/capsule/core/PersistentTrieSetMultimap.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2315,6 +2315,34 @@ PersistentTrieSet.AbstractSetNode<K> toSetNode(AtomicReference<Thread> mutator)
23152315
// (K[]) collisionContent.stream().map(Map.Entry::getKey).toArray());
23162316
// }
23172317

2318+
@Override
2319+
public boolean equals(final Object other) {
2320+
if (null == other) {
2321+
return false;
2322+
}
2323+
if (this == other) {
2324+
return true;
2325+
}
2326+
if (getClass() != other.getClass()) {
2327+
return false;
2328+
}
2329+
2330+
HashCollisionNode<?, ?> that = (HashCollisionNode<?, ?>) other;
2331+
2332+
if (hash != that.hash) {
2333+
return false;
2334+
}
2335+
2336+
if (collisionContent.size() != that.collisionContent.size()) {
2337+
return false;
2338+
}
2339+
2340+
/*
2341+
* Linear scan for each payload entry due to arbitrary element order.
2342+
*/
2343+
return collisionContent.stream().allMatch(that.collisionContent::contains);
2344+
}
2345+
23182346
@Override
23192347
public byte sizePredicate() {
23202348
return SIZE_MORE_THAN_ONE;

capsule-experimental/src/main/java/io/usethesource/capsule/experimental/multimap/TrieSetMultimap_HHAMT.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2573,6 +2573,34 @@ private static final class HashCollisionNode<K, V> extends AbstractHashCollision
25732573
private static final Supplier<RuntimeException> UOE_NOT_YET_IMPLEMENTED_FACTORY =
25742574
() -> new UnsupportedOperationException("Not yet implemented @ HashCollisionNode.");
25752575

2576+
@Override
2577+
public boolean equals(final Object other) {
2578+
if (null == other) {
2579+
return false;
2580+
}
2581+
if (this == other) {
2582+
return true;
2583+
}
2584+
if (getClass() != other.getClass()) {
2585+
return false;
2586+
}
2587+
2588+
HashCollisionNode<?, ?> that = (HashCollisionNode<?, ?>) other;
2589+
2590+
if (hash != that.hash) {
2591+
return false;
2592+
}
2593+
2594+
if (collisionContent.size() != that.collisionContent.size()) {
2595+
return false;
2596+
}
2597+
2598+
/*
2599+
* Linear scan for each payload entry due to arbitrary element order.
2600+
*/
2601+
return collisionContent.stream().allMatch(that.collisionContent::contains);
2602+
}
2603+
25762604
@Override
25772605
byte sizePredicate() {
25782606
return SIZE_MORE_THAN_ONE;

capsule-experimental/src/main/java/io/usethesource/capsule/experimental/multimap/TrieSetMultimap_HHAMT_Interlinked.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2489,6 +2489,34 @@ public ArrayView<AbstractSetMultimapNode<K, V>> nodeArray() {
24892489
private static final Supplier<RuntimeException> UOE_NOT_YET_IMPLEMENTED_FACTORY =
24902490
() -> new UnsupportedOperationException("Not yet implemented @ HashCollisionNode.");
24912491

2492+
@Override
2493+
public boolean equals(final Object other) {
2494+
if (null == other) {
2495+
return false;
2496+
}
2497+
if (this == other) {
2498+
return true;
2499+
}
2500+
if (getClass() != other.getClass()) {
2501+
return false;
2502+
}
2503+
2504+
HashCollisionNode<?, ?> that = (HashCollisionNode<?, ?>) other;
2505+
2506+
if (hash != that.hash) {
2507+
return false;
2508+
}
2509+
2510+
if (collisionContent.size() != that.collisionContent.size()) {
2511+
return false;
2512+
}
2513+
2514+
/*
2515+
* Linear scan for each payload entry due to arbitrary element order.
2516+
*/
2517+
return collisionContent.stream().allMatch(that.collisionContent::contains);
2518+
}
2519+
24922520
@Override
24932521
public byte sizePredicate() {
24942522
return SIZE_MORE_THAN_ONE;

capsule-experimental/src/main/java/io/usethesource/capsule/experimental/multimap/TrieSetMultimap_HHAMT_Specialized.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2807,6 +2807,34 @@ private static final class HashCollisionNode<K, V> extends AbstractHashCollision
28072807
private static final Supplier<RuntimeException> UOE_NOT_YET_IMPLEMENTED_FACTORY =
28082808
() -> new UnsupportedOperationException("Not yet implemented @ HashCollisionNode.");
28092809

2810+
@Override
2811+
public boolean equals(final Object other) {
2812+
if (null == other) {
2813+
return false;
2814+
}
2815+
if (this == other) {
2816+
return true;
2817+
}
2818+
if (getClass() != other.getClass()) {
2819+
return false;
2820+
}
2821+
2822+
HashCollisionNode<?, ?> that = (HashCollisionNode<?, ?>) other;
2823+
2824+
if (hash != that.hash) {
2825+
return false;
2826+
}
2827+
2828+
if (collisionContent.size() != that.collisionContent.size()) {
2829+
return false;
2830+
}
2831+
2832+
/*
2833+
* Linear scan for each payload entry due to arbitrary element order.
2834+
*/
2835+
return collisionContent.stream().allMatch(that.collisionContent::contains);
2836+
}
2837+
28102838
@Override
28112839
byte sizePredicate() {
28122840
return SIZE_MORE_THAN_ONE;

capsule-experimental/src/main/java/io/usethesource/capsule/experimental/multimap/TrieSetMultimap_HHAMT_Specialized_Interlinked.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2742,6 +2742,34 @@ private static final class HashCollisionNode<K, V> extends AbstractHashCollision
27422742
private static final Supplier<RuntimeException> UOE_NOT_YET_IMPLEMENTED_FACTORY =
27432743
() -> new UnsupportedOperationException("Not yet implemented @ HashCollisionNode.");
27442744

2745+
@Override
2746+
public boolean equals(final Object other) {
2747+
if (null == other) {
2748+
return false;
2749+
}
2750+
if (this == other) {
2751+
return true;
2752+
}
2753+
if (getClass() != other.getClass()) {
2754+
return false;
2755+
}
2756+
2757+
HashCollisionNode<?, ?> that = (HashCollisionNode<?, ?>) other;
2758+
2759+
if (hash != that.hash) {
2760+
return false;
2761+
}
2762+
2763+
if (collisionContent.size() != that.collisionContent.size()) {
2764+
return false;
2765+
}
2766+
2767+
/*
2768+
* Linear scan for each payload entry due to arbitrary element order.
2769+
*/
2770+
return collisionContent.stream().allMatch(that.collisionContent::contains);
2771+
}
2772+
27452773
@Override
27462774
byte sizePredicate() {
27472775
return SIZE_MORE_THAN_ONE;

capsule-experimental/src/main/java/io/usethesource/capsule/experimental/multimap/TrieSetMultimap_HHAMT_Specialized_Path_Interlinked.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2759,6 +2759,34 @@ private static final class HashCollisionNode<K, V> extends AbstractHashCollision
27592759
private static final Supplier<RuntimeException> UOE_NOT_YET_IMPLEMENTED_FACTORY =
27602760
() -> new UnsupportedOperationException("Not yet implemented @ HashCollisionNode.");
27612761

2762+
@Override
2763+
public boolean equals(final Object other) {
2764+
if (null == other) {
2765+
return false;
2766+
}
2767+
if (this == other) {
2768+
return true;
2769+
}
2770+
if (getClass() != other.getClass()) {
2771+
return false;
2772+
}
2773+
2774+
HashCollisionNode<?, ?> that = (HashCollisionNode<?, ?>) other;
2775+
2776+
if (hash != that.hash) {
2777+
return false;
2778+
}
2779+
2780+
if (collisionContent.size() != that.collisionContent.size()) {
2781+
return false;
2782+
}
2783+
2784+
/*
2785+
* Linear scan for each payload entry due to arbitrary element order.
2786+
*/
2787+
return collisionContent.stream().allMatch(that.collisionContent::contains);
2788+
}
2789+
27622790
@Override
27632791
byte sizePredicate() {
27642792
return SIZE_MORE_THAN_ONE;

capsule-veritas/src/test/java/io/usethesource/capsule/SetMultimapSmokeTest.java

Lines changed: 87 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,61 +9,77 @@
99

1010
import java.util.Collection;
1111

12-
import io.usethesource.capsule.experimental.multimap.TrieSetMultimap_HHAMT_Specialized_Path_Interlinked;
13-
import org.junit.Test;
12+
import com.pholser.junit.quickcheck.Property;
13+
import com.pholser.junit.quickcheck.generator.Size;
14+
import com.pholser.junit.quickcheck.runner.JUnitQuickcheck;
15+
import org.junit.runner.RunWith;
1416

1517
import static org.junit.Assert.assertEquals;
1618
import static org.junit.Assert.assertFalse;
19+
import static org.junit.Assert.assertNotEquals;
1720
import static org.junit.Assert.assertTrue;
1821

19-
public class SetMultimapSmokeTest {
22+
@RunWith(JUnitQuickcheck.class)
23+
public class SetMultimapSmokeTest<K, V, CT extends SetMultimap.Immutable<K, V>> {
2024

21-
final static int size = 64;
25+
@Property
26+
public void testInsertTwoTuplesThatShareSameKey(
27+
@Size(min = 0, max = 0) final SetMultimap.Immutable<Integer, String> emptyCollection) {
2228

23-
@Test
24-
public void testInsertTwoTuplesThatShareSameKey() {
25-
SetMultimap.Immutable<Integer, String> map =
26-
TrieSetMultimap_HHAMT_Specialized_Path_Interlinked.<Integer, String>of().__insert(1, "x")
27-
.__insert(1, "y");
29+
SetMultimap.Immutable<Integer, String> map = emptyCollection
30+
.__insert(1, "x")
31+
.__insert(1, "y");
2832

2933
assertEquals(2, map.size());
3034
assertTrue(map.containsKey(1));
3135
}
3236

33-
@Test
34-
public void testInsertTwoTuplesWithOneRemoveThatShareSameKeyX() {
35-
SetMultimap.Immutable<Integer, String> map = TrieSetMultimap_HHAMT_Specialized_Path_Interlinked
36-
.<Integer, String>of().__insert(1, "x").__insert(1, "y").__remove(1, "x");
37+
@Property
38+
public void testInsertTwoTuplesWithOneRemoveThatShareSameKeyX(
39+
@Size(min = 0, max = 0) final SetMultimap.Immutable<Integer, String> emptyCollection) {
40+
41+
SetMultimap.Immutable<Integer, String> map = emptyCollection
42+
.__insert(1, "x")
43+
.__insert(1, "y")
44+
.__remove(1, "x");
3745

3846
assertEquals(1, map.size());
3947
assertTrue(map.containsKey(1));
4048
}
4149

42-
@Test
43-
public void testInsertTwoTuplesWithOneRemoveThatShareSameKeyY() {
44-
SetMultimap.Immutable<Integer, String> map = TrieSetMultimap_HHAMT_Specialized_Path_Interlinked
45-
.<Integer, String>of().__insert(1, "x").__insert(1, "y").__remove(1, "y");
50+
@Property
51+
public void testInsertTwoTuplesWithOneRemoveThatShareSameKeyY(
52+
@Size(min = 0, max = 0) final SetMultimap.Immutable<Integer, String> emptyCollection) {
53+
54+
SetMultimap.Immutable<Integer, String> map = emptyCollection
55+
.__insert(1, "x")
56+
.__insert(1, "y")
57+
.__remove(1, "y");
4658

4759
assertEquals(1, map.size());
4860
assertTrue(map.containsKey(1));
4961
}
5062

51-
@Test
52-
public void testInsertTwoTuplesWithOneRemoveThatShareSameKeyXY() {
53-
SetMultimap.Immutable<Integer, String> map =
54-
TrieSetMultimap_HHAMT_Specialized_Path_Interlinked.<Integer, String>of().__insert(1, "x")
55-
.__insert(1, "y")
56-
.__remove(1, "x").__remove(1, "y");
63+
@Property
64+
public void testInsertTwoTuplesWithOneRemoveThatShareSameKeyXY(
65+
@Size(min = 0, max = 0) final SetMultimap.Immutable<Integer, String> emptyCollection) {
66+
67+
SetMultimap.Immutable<Integer, String> map = emptyCollection
68+
.__insert(1, "x")
69+
.__insert(1, "y")
70+
.__remove(1, "x").__remove(1, "y");
5771

5872
assertEquals(0, map.size());
5973
assertFalse(map.containsKey(1));
6074
}
6175

62-
@Test
63-
public void testInsertTwoTuplesThatShareSameKey_Iterate() {
64-
SetMultimap.Immutable<Integer, String> map =
65-
TrieSetMultimap_HHAMT_Specialized_Path_Interlinked.<Integer, String>of().__insert(1, "x")
66-
.__insert(1, "y");
76+
@Property
77+
public void testInsertTwoTuplesThatShareSameKey_Iterate(
78+
@Size(min = 0, max = 0) final SetMultimap.Immutable<Integer, String> emptyCollection) {
79+
80+
SetMultimap.Immutable<Integer, String> map = emptyCollection
81+
.__insert(1, "x")
82+
.__insert(1, "y");
6783

6884
Collection<String> values = map.values();
6985

@@ -72,4 +88,47 @@ public void testInsertTwoTuplesThatShareSameKey_Iterate() {
7288
assertTrue(values.contains("y"));
7389
}
7490

91+
@Property
92+
public void testHashCollisionReproduction(
93+
@Size(min = 0, max = 0) final SetMultimap.Immutable<Object, String> emptyCollection) {
94+
95+
Object a = new Object() {
96+
public int hashCode() {
97+
return 0;
98+
}
99+
};
100+
101+
Object b = new Object() {
102+
public int hashCode() {
103+
return 0;
104+
}
105+
};
106+
107+
Object c = new Object() {
108+
public int hashCode() {
109+
return 0;
110+
}
111+
};
112+
113+
final SetMultimap.Immutable<Object, String> map =
114+
emptyCollection.__insert(a, "x").__insert(b, "y");
115+
116+
final SetMultimap.Immutable<Object, String> mapDuplicate =
117+
emptyCollection.__insert(a, "x").__insert(b, "y");
118+
119+
final SetMultimap.Immutable<Object, String> mapDuplicateWithDifferentOrder =
120+
emptyCollection.__insert(b, "y").__insert(a, "x");
121+
122+
final SetMultimap.Immutable<Object, String> mapDifferent =
123+
emptyCollection.__insert(a, "x").__insert(c, "z");
124+
125+
assertEquals(map, mapDuplicate);
126+
assertEquals(map, mapDuplicateWithDifferentOrder);
127+
128+
assertEquals(mapDuplicate, map);
129+
assertEquals(mapDuplicateWithDifferentOrder, map);
130+
131+
assertNotEquals(map, mapDifferent);
132+
assertNotEquals(mapDifferent, map);
133+
}
75134
}

0 commit comments

Comments
 (0)