diff --git a/community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/LongArrayHash.java b/community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/LongArrayHash.java index a79b07741dc6..77f75f00b88b 100644 --- a/community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/LongArrayHash.java +++ b/community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/LongArrayHash.java @@ -59,25 +59,4 @@ static boolean validValue( long[] arr, int width ) return true; } - static Pair doubleInSize( LongArrayHashTable srcTable, Object[] srcValues ) - { - LongArrayHashTable dstTable = new LongArrayHashTable( srcTable.capacity * 2, srcTable.width ); - Object[] dstValues = new Object[srcTable.capacity * 2]; - long[] srcKeys = srcTable.keys; - long[] dstKeys = dstTable.keys; - dstTable.numberOfEntries = srcTable.numberOfEntries; - for ( int fromSlot = 0; fromSlot < srcTable.capacity; fromSlot = fromSlot + 1 ) - { - int width = srcTable.width; - int fromOffset = fromSlot * width; - if ( srcKeys[fromOffset] != NOT_IN_USE ) - { - int toSlot = LongArrayHash.hashCode( srcKeys, fromOffset, width ) & dstTable.tableMask; - toSlot = dstTable.findUnusedSlot( toSlot ); - System.arraycopy( srcKeys, fromOffset, dstKeys, toSlot * width, width ); - dstValues[toSlot] = srcValues[fromSlot]; - } - } - return Pair.of( dstTable, dstValues ); - } } diff --git a/community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/LongArrayHashMap.java b/community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/LongArrayHashMap.java index 6f915213e83b..f110134516ec 100644 --- a/community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/LongArrayHashMap.java +++ b/community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/LongArrayHashMap.java @@ -128,7 +128,7 @@ public boolean isEmpty() private void resize() { - Pair resized = LongArrayHash.doubleInSize( table, values ); + Pair resized = table.doubleCapacity( values ); table = resized.first(); values = resized.other(); } diff --git a/community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/LongArrayHashMultiMap.java b/community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/LongArrayHashMultiMap.java index b4a22adc6f42..53c5604802c1 100644 --- a/community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/LongArrayHashMultiMap.java +++ b/community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/LongArrayHashMultiMap.java @@ -117,7 +117,7 @@ public boolean isEmpty() private void resize() { - Pair resized = LongArrayHash.doubleInSize( table, values ); + Pair resized = table.doubleCapacity( values ); table = resized.first(); values = resized.other(); } diff --git a/community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/LongArrayHashSet.java b/community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/LongArrayHashSet.java index ea93eb5f85a4..083d4434db40 100644 --- a/community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/LongArrayHashSet.java +++ b/community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/LongArrayHashSet.java @@ -22,7 +22,6 @@ import org.opencypher.v9_0.util.InternalException; import static org.neo4j.cypher.internal.runtime.LongArrayHash.CONTINUE_PROBING; -import static org.neo4j.cypher.internal.runtime.LongArrayHash.NOT_IN_USE; import static org.neo4j.cypher.internal.runtime.LongArrayHash.SLOT_EMPTY; import static org.neo4j.cypher.internal.runtime.LongArrayHash.VALUE_FOUND; @@ -72,7 +71,7 @@ public boolean add( long[] value ) if ( table.timeToResize() ) { // We know we need to add the value to the set, but there is no space left - resize(); + table = table.doubleCapacity(); // Need to restart linear probe after resizing slotNr = slotFor( value ); } @@ -117,26 +116,6 @@ public boolean contains( long[] value ) return result == VALUE_FOUND; } - private void resize() - { - int oldSize = table.capacity; - int oldNumberEntries = table.numberOfEntries; - long[] srcArray = table.keys; - table = new LongArrayHashTable( oldSize * 2, width ); - long[] dstArray = table.keys; - table.numberOfEntries = oldNumberEntries; - - for ( int fromOffset = 0; fromOffset < oldSize * width; fromOffset = fromOffset + width ) - { - if ( srcArray[fromOffset] != NOT_IN_USE ) - { - int toSlot = LongArrayHash.hashCode( srcArray, fromOffset, width ) & table.tableMask; - toSlot = table.findUnusedSlot( toSlot ); - System.arraycopy( srcArray, fromOffset, dstArray, toSlot * width, width ); - } - } - } - private int slotFor( long[] value ) { return LongArrayHash.hashCode( value, 0, width ) & table.tableMask; diff --git a/community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/LongArrayHashTable.java b/community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/LongArrayHashTable.java index f9907175bee0..7189ea987575 100644 --- a/community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/LongArrayHashTable.java +++ b/community/cypher/runtime-util/src/main/java/org/neo4j/cypher/internal/runtime/LongArrayHashTable.java @@ -19,6 +19,8 @@ */ package org.neo4j.cypher.internal.runtime; +import org.neo4j.helpers.collection.Pair; + import static org.neo4j.cypher.internal.runtime.LongArrayHash.CONTINUE_PROBING; import static org.neo4j.cypher.internal.runtime.LongArrayHash.NOT_IN_USE; import static org.neo4j.cypher.internal.runtime.LongArrayHash.SLOT_EMPTY; @@ -32,7 +34,7 @@ class LongArrayHashTable public final long[] keys; public final int width; public final int capacity; - int numberOfEntries; + private int numberOfEntries; private int resizeLimit; private static final double LOAD_FACTOR = 0.75; @@ -52,6 +54,7 @@ class LongArrayHashTable /** * Signals whether it's time to size up or not. The actual resizing is done slightly differently depending on if this is a map or set. Maps have, in * addition to a hash table also a separate array for the values. + * * @return true if the number of keys in the hash table has reached capacity. */ boolean timeToResize() @@ -91,6 +94,7 @@ int checkSlot( int slot, long[] key ) /** * Writes the key to this slot. + * * @param slot The slot to write to. * @param key Le key. */ @@ -115,9 +119,10 @@ public boolean isEmpty() /** * Finds an slot not already claimed, starting from a given slot. + * * @return First unused slot after fromSlot */ - int findUnusedSlot( int fromSlot ) + private int findUnusedSlot( int fromSlot ) { while ( true ) { @@ -129,4 +134,41 @@ int findUnusedSlot( int fromSlot ) } } + LongArrayHashTable doubleCapacity() + { + LongArrayHashTable newTable = new LongArrayHashTable( capacity * 2, width ); + newTable.numberOfEntries = numberOfEntries; + + for ( int fromOffset = 0; fromOffset < capacity * width; fromOffset = fromOffset + width ) + { + if ( keys[fromOffset] != NOT_IN_USE ) + { + int toSlot = LongArrayHash.hashCode( keys, fromOffset, width ) & newTable.tableMask; + toSlot = newTable.findUnusedSlot( toSlot ); + System.arraycopy( keys, fromOffset, newTable.keys, toSlot * width, width ); + } + } + + return newTable; + } + + Pair doubleCapacity( Object[] srcValues ) + { + LongArrayHashTable dstTable = new LongArrayHashTable( capacity * 2, width ); + Object[] dstValues = new Object[capacity * 2]; + long[] srcKeys = keys; + dstTable.numberOfEntries = numberOfEntries; + for ( int fromSlot = 0; fromSlot < capacity; fromSlot = fromSlot + 1 ) + { + int fromOffset = fromSlot * width; + if ( srcKeys[fromOffset] != NOT_IN_USE ) + { + int toSlot = LongArrayHash.hashCode( srcKeys, fromOffset, width ) & dstTable.tableMask; + toSlot = dstTable.findUnusedSlot( toSlot ); + System.arraycopy( srcKeys, fromOffset, dstTable.keys, toSlot * width, width ); + dstValues[toSlot] = srcValues[fromSlot]; + } + } + return Pair.of( dstTable, dstValues ); + } } diff --git a/community/cypher/runtime-util/src/test/scala/org/neo4j/cypher/internal/runtime/LongArrayHashMapTest.scala b/community/cypher/runtime-util/src/test/scala/org/neo4j/cypher/internal/runtime/LongArrayHashMapTest.scala index b6910d1cbdfa..5447d497af0b 100644 --- a/community/cypher/runtime-util/src/test/scala/org/neo4j/cypher/internal/runtime/LongArrayHashMapTest.scala +++ b/community/cypher/runtime-util/src/test/scala/org/neo4j/cypher/internal/runtime/LongArrayHashMapTest.scala @@ -42,7 +42,7 @@ class LongArrayHashMapTest extends FunSuite with Matchers { resultAsSet(map) should equal(Set.empty) } - test("fill and resize") { + test("fill and doubleCapacity") { val map = new LongArrayHashMap[String](8, 3) map.getOrCreateAndAdd(Array(0L, 8L, 1L), () => "hello") map.getOrCreateAndAdd(Array(0L, 7L, 2L), () => "is") diff --git a/community/cypher/runtime-util/src/test/scala/org/neo4j/cypher/internal/runtime/LongArrayHashMultiMapTest.scala b/community/cypher/runtime-util/src/test/scala/org/neo4j/cypher/internal/runtime/LongArrayHashMultiMapTest.scala index d8de72e4f93b..e5c03d88d9ac 100644 --- a/community/cypher/runtime-util/src/test/scala/org/neo4j/cypher/internal/runtime/LongArrayHashMultiMapTest.scala +++ b/community/cypher/runtime-util/src/test/scala/org/neo4j/cypher/internal/runtime/LongArrayHashMultiMapTest.scala @@ -41,7 +41,7 @@ class LongArrayHashMultiMapTest extends FunSuite with Matchers { map.isEmpty should equal(true) } - test("fill and resize") { + test("fill and doubleCapacity") { val map = new LongArrayHashMultiMap[String](8, 3) map.add(Array(0L, 8L, 1L), "hello") map.add(Array(0L, 7L, 2L), "is")