Skip to content

Commit

Permalink
HPPC-186: Protect the iteration order of KTypeHashSet
Browse files Browse the repository at this point in the history
  • Loading branch information
bruno-roustant committed Aug 18, 2020
1 parent 826cd9b commit 6696dbf
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 15 deletions.
6 changes: 6 additions & 0 deletions hppc/src/main/java/com/carrotsearch/hppc/BitMixer.java
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,10 @@ public static int mixPhi(Object k, int seed) {
final int h = (k == null ? 0 : (k.hashCode() ^ seed) * PHI_C32);
return h ^ (h >>> 16);
}

/**
* Prime increment to kind of "shuffle" the iteration order to avoid collision avalanches during
* hash containers copies.
*/
public static final int ITERATION_ORDER_INCREMENT = 29;
}
30 changes: 15 additions & 15 deletions hppc/src/main/templates/com/carrotsearch/hppc/KTypeHashSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ public Object[] toArray() {
}

final KType[] keys = Intrinsics.<KType[]> cast(this.keys);
for (int slot = 0, max = mask; slot <= max; slot++) {
for (int i = 0, slot = 0, mask = this.mask; i <= mask; i++, slot = (slot + BitMixer.ITERATION_ORDER_INCREMENT) & mask) {
KType existing;
if (!Intrinsics.isEmpty(existing = keys[slot])) {
cloned[j++] = existing;
Expand Down Expand Up @@ -478,30 +478,30 @@ public long ramBytesUsed() {
*/
protected final class EntryIterator extends AbstractIterator<KTypeCursor<KType>> {
private final KTypeCursor<KType> cursor;
private final int max = mask + 1;
private int slot = -1;
private int index;
private int slot;

public EntryIterator() {
cursor = new KTypeCursor<KType>();
}

@Override
protected KTypeCursor<KType> fetch() {
if (slot < max) {
final int mask = KTypeHashSet.this.mask;
while (index <= mask) {
KType existing;
for (slot++; slot < max; slot++) {
if (!Intrinsics.isEmpty(existing = Intrinsics.<KType> cast(keys[slot]))) {
cursor.index = slot;
cursor.value = existing;
return cursor;
}
index++;
slot = (slot + BitMixer.ITERATION_ORDER_INCREMENT) & mask;
if (!Intrinsics.isEmpty(existing = Intrinsics.<KType> cast(keys[slot]))) {
cursor.index = slot;
cursor.value = existing;
return cursor;
}
}

if (slot == max && hasEmptyKey) {
cursor.index = slot;
if (index == mask + 1 && hasEmptyKey) {
cursor.index = index++;
cursor.value = Intrinsics.empty();
slot++;
return cursor;
}

Expand All @@ -519,7 +519,7 @@ public <T extends KTypeProcedure<? super KType>> T forEach(T procedure) {
}

final KType[] keys = Intrinsics.<KType[]> cast(this.keys);
for (int slot = 0, max = this.mask; slot <= max; slot++) {
for (int i = 0, slot = 0, mask = this.mask; i <= mask; i++, slot = (slot + BitMixer.ITERATION_ORDER_INCREMENT) & mask) {
KType existing;
if (!Intrinsics.isEmpty(existing = keys[slot])) {
procedure.apply(existing);
Expand All @@ -541,7 +541,7 @@ public <T extends KTypePredicate<? super KType>> T forEach(T predicate) {
}

final KType[] keys = Intrinsics.<KType[]> cast(this.keys);
for (int slot = 0, max = this.mask; slot <= max; slot++) {
for (int i = 0, slot = 0, mask = this.mask; i <= mask; i++, slot = (slot + BitMixer.ITERATION_ORDER_INCREMENT) & mask) {
KType existing;
if (!Intrinsics.isEmpty(existing = keys[slot])) {
if (!predicate.apply(existing)) {
Expand Down

0 comments on commit 6696dbf

Please sign in to comment.