diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/BaseNumberArray.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/BaseNumberArray.java
new file mode 100644
index 000000000000..da7af82d6a58
--- /dev/null
+++ b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/BaseNumberArray.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2002-2016 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.unsafe.impl.batchimport.cache;
+
+/**
+ * Contains basic functionality of fixed size number arrays.
+ */
+abstract class BaseNumberArray> implements NumberArray
+{
+ protected final int itemSize;
+ private final long base;
+
+ /**
+ * @param itemSize byte size of each item in this array.
+ * @param base base index to rebase all indexes in accessor methods off of. See {@link #at(long)}.
+ */
+ protected BaseNumberArray( int itemSize, long base )
+ {
+ this.itemSize = itemSize;
+ this.base = base;
+ }
+
+ @SuppressWarnings( "unchecked" )
+ @Override
+ public N at( long index )
+ {
+ return (N)this;
+ }
+
+ /**
+ * Utility for rebasing an external index to internal index.
+ * @param index external index.
+ * @return index into internal data structure.
+ */
+ protected long rebase( long index )
+ {
+ return index - base;
+ }
+}
diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/ByteArray.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/ByteArray.java
new file mode 100644
index 000000000000..c3439acb16c0
--- /dev/null
+++ b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/ByteArray.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2002-2016 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.unsafe.impl.batchimport.cache;
+
+/**
+ * Abstraction of a {@code byte[]} so that different implementations can be plugged in, for example
+ * off-heap, dynamically growing, or other implementations. This interface is slightly different than
+ * {@link IntArray} and {@link LongArray} in that one item in the array isn't necessarily just a byte,
+ * instead item size can be set to any number and the bytes in an item can be read and written as other
+ * number representations like {@link #setInt(long, int, int) int} or {@link #setLong(long, int, long) long},
+ * even a special {@link #set6BLong(long, int, long) 6B long}. More can easily be added on demand.
+ *
+ * @see NumberArrayFactory
+ */
+public interface ByteArray extends NumberArray
+{
+ void get( long index, byte[] into );
+
+ byte getByte( long index, int offset );
+
+ short getShort( long index, int offset );
+
+ int getInt( long index, int offset );
+
+ long get6BLong( long index, int offset );
+
+ long getLong( long index, int offset );
+
+ void set( long index, byte[] value );
+
+ void setByte( long index, int offset, byte value );
+
+ void setShort( long index, int offset, short value );
+
+ void setInt( long index, int offset, int value );
+
+ void set6BLong( long index, int offset, long value );
+
+ void setLong( long index, int offset, long value );
+}
diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/ChunkedNumberArray.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/ChunkedNumberArray.java
deleted file mode 100644
index d54fdfbe7d40..000000000000
--- a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/ChunkedNumberArray.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (c) 2002-2016 "Neo Technology,"
- * Network Engine for Objects in Lund AB [http://neotechnology.com]
- *
- * This file is part of Neo4j.
- *
- * Neo4j is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package org.neo4j.unsafe.impl.batchimport.cache;
-
-/**
- * A {@link NumberArray} base built up out of smaller chunks.
- */
-abstract class ChunkedNumberArray implements NumberArray
-{
- protected final long chunkSize;
- protected NumberArray[] chunks;
-
- ChunkedNumberArray( long chunkSize )
- {
- this.chunkSize = chunkSize;
- }
-
- @Override
- public long length()
- {
- return chunks.length * chunkSize;
- }
-
- @Override
- public void clear()
- {
- for ( NumberArray chunk : chunks )
- {
- chunk.clear();
- }
- }
-
- @Override
- public void acceptMemoryStatsVisitor( MemoryStatsVisitor visitor )
- {
- for ( NumberArray chunk : chunks )
- {
- chunk.acceptMemoryStatsVisitor( visitor );
- }
- }
-
- @SuppressWarnings( "unchecked" )
- protected N chunkAt( long index )
- {
- int chunkIndex = chunkIndex( index );
- return (N) chunks[chunkIndex];
- }
-
- @SuppressWarnings( "unchecked" )
- protected N chunkOrNullAt( long index )
- {
- int chunkIndex = chunkIndex( index );
- return chunkIndex < chunks.length ? (N) chunks[chunkIndex] : null;
- }
-
- protected int chunkIndex( long index )
- {
- return (int) (index/chunkSize);
- }
-
- protected long index( long index )
- {
- return index % chunkSize;
- }
-
- @Override
- public void close()
- {
- for ( NumberArray chunk : chunks )
- {
- chunk.close();
- }
- }
-}
diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/DynamicByteArray.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/DynamicByteArray.java
new file mode 100644
index 000000000000..790d3a4580dc
--- /dev/null
+++ b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/DynamicByteArray.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2002-2016 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.unsafe.impl.batchimport.cache;
+
+import java.nio.ByteBuffer;
+
+import static org.neo4j.unsafe.impl.batchimport.cache.HeapByteArray.get6BLongFromByteBuffer;
+
+public class DynamicByteArray extends DynamicNumberArray implements ByteArray
+{
+ private final byte[] defaultValue;
+ private final ByteBuffer defaultValueConvenienceBuffer;
+
+ public DynamicByteArray( NumberArrayFactory factory, long chunkSize, byte[] defaultValue )
+ {
+ super( factory, chunkSize, new ByteArray[0] );
+ this.defaultValue = defaultValue;
+ this.defaultValueConvenienceBuffer = ByteBuffer.wrap( defaultValue );
+ }
+
+ @Override
+ public void swap( long fromIndex, long toIndex, int numberOfEntries )
+ {
+ // Let's just do this the stupid way. There's room for optimization here
+ byte[] intermediary = defaultValue.clone();
+ byte[] transport = defaultValue.clone();
+ for ( int i = 0; i < numberOfEntries; i++ )
+ {
+ get( fromIndex+i, intermediary );
+ get( toIndex+i, transport );
+ set( fromIndex+i, transport );
+ set( toIndex+i, intermediary );
+ }
+ }
+
+ @Override
+ public void get( long index, byte[] into )
+ {
+ ByteArray chunk = chunkOrNullAt( index );
+ if ( chunk != null )
+ {
+ chunk.get( index, into );
+ }
+ else
+ {
+ System.arraycopy( defaultValue, 0, into, 0, defaultValue.length );
+ }
+ }
+
+ @Override
+ public byte getByte( long index, int offset )
+ {
+ ByteArray chunk = chunkOrNullAt( index );
+ return chunk != null ? chunk.getByte( index, offset ) : defaultValueConvenienceBuffer.get( offset );
+ }
+
+ @Override
+ public short getShort( long index, int offset )
+ {
+ ByteArray chunk = chunkOrNullAt( index );
+ return chunk != null ? chunk.getShort( index, offset ) : defaultValueConvenienceBuffer.getShort( offset );
+ }
+
+ @Override
+ public int getInt( long index, int offset )
+ {
+ ByteArray chunk = chunkOrNullAt( index );
+ return chunk != null ? chunk.getInt( index, offset ) : defaultValueConvenienceBuffer.getInt( offset );
+ }
+
+ @Override
+ public long get6BLong( long index, int offset )
+ {
+ ByteArray chunk = chunkOrNullAt( index );
+ return chunk != null ? chunk.get6BLong( index, offset ) :
+ get6BLongFromByteBuffer( defaultValueConvenienceBuffer, offset );
+ }
+
+ @Override
+ public long getLong( long index, int offset )
+ {
+ ByteArray chunk = chunkOrNullAt( index );
+ return chunk != null ? chunk.getLong( index, offset ) : defaultValueConvenienceBuffer.getLong( offset );
+ }
+
+ @Override
+ public void set( long index, byte[] value )
+ {
+ at( index ).set( index, value );
+ }
+
+ @Override
+ public void setByte( long index, int offset, byte value )
+ {
+ at( index ).setByte( index, offset, value );
+ }
+
+ @Override
+ public void setShort( long index, int offset, short value )
+ {
+ at( index ).setShort( index, offset, value );
+ }
+
+ @Override
+ public void setInt( long index, int offset, int value )
+ {
+ at( index ).setInt( index, offset, value );
+ }
+
+ @Override
+ public void set6BLong( long index, int offset, long value )
+ {
+ at( index ).set6BLong( index, offset, value );
+ }
+
+ @Override
+ public void setLong( long index, int offset, long value )
+ {
+ at( index ).setLong( index, offset, value );
+ }
+
+ @Override
+ protected ByteArray addChunk( long chunkSize, long base )
+ {
+ return factory.newByteArray( chunkSize, defaultValue, base );
+ }
+}
diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/DynamicIntArray.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/DynamicIntArray.java
index 40c6ced89a44..92205fbc56a5 100644
--- a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/DynamicIntArray.java
+++ b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/DynamicIntArray.java
@@ -31,7 +31,7 @@ public class DynamicIntArray extends DynamicNumberArray implements Int
public DynamicIntArray( NumberArrayFactory factory, long chunkSize, int defaultValue )
{
- super( factory, chunkSize );
+ super( factory, chunkSize, new IntArray[0] );
this.defaultValue = defaultValue;
}
@@ -39,13 +39,13 @@ public DynamicIntArray( NumberArrayFactory factory, long chunkSize, int defaultV
public int get( long index )
{
IntArray chunk = chunkOrNullAt( index );
- return chunk != null ? chunk.get( index( index ) ) : defaultValue;
+ return chunk != null ? chunk.get( index ) : defaultValue;
}
@Override
public void set( long index, int value )
{
- ensureChunkAt( index ).set( index( index ), value );
+ at( index ).set( index, value );
}
@Override
@@ -61,8 +61,8 @@ public void swap( long fromIndex, long toIndex, int numberOfEntries )
}
@Override
- protected IntArray addChunk( long chunkSize )
+ protected IntArray addChunk( long chunkSize, long base )
{
- return factory.newIntArray( chunkSize, defaultValue );
+ return factory.newIntArray( chunkSize, defaultValue, base );
}
}
diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/DynamicLongArray.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/DynamicLongArray.java
index d5d477c7b459..91c7d1d9c1b1 100644
--- a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/DynamicLongArray.java
+++ b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/DynamicLongArray.java
@@ -31,7 +31,7 @@ public class DynamicLongArray extends DynamicNumberArray implements L
public DynamicLongArray( NumberArrayFactory factory, long chunkSize, long defaultValue )
{
- super( factory, chunkSize );
+ super( factory, chunkSize, new LongArray[0] );
this.defaultValue = defaultValue;
}
@@ -39,13 +39,13 @@ public DynamicLongArray( NumberArrayFactory factory, long chunkSize, long defaul
public long get( long index )
{
LongArray chunk = chunkOrNullAt( index );
- return chunk != null ? chunk.get( index( index ) ) : defaultValue;
+ return chunk != null ? chunk.get( index ) : defaultValue;
}
@Override
public void set( long index, long value )
{
- ensureChunkAt( index ).set( index( index ), value );
+ at( index ).set( index, value );
}
@Override
@@ -61,8 +61,8 @@ public void swap( long fromIndex, long toIndex, int numberOfEntries )
}
@Override
- protected LongArray addChunk( long chunkSize )
+ protected LongArray addChunk( long chunkSize, long base )
{
- return factory.newLongArray( chunkSize, defaultValue );
+ return factory.newLongArray( chunkSize, defaultValue, base );
}
}
diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/DynamicNumberArray.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/DynamicNumberArray.java
index f8e4c700a488..9e5c5f860412 100644
--- a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/DynamicNumberArray.java
+++ b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/DynamicNumberArray.java
@@ -28,24 +28,64 @@
* @see NumberArrayFactory#newDynamicLongArray(long, long)
* @see NumberArrayFactory#newDynamicIntArray(long, int)
*/
-abstract class DynamicNumberArray extends ChunkedNumberArray
+abstract class DynamicNumberArray> implements NumberArray
{
protected final NumberArrayFactory factory;
+ protected final long chunkSize;
+ protected N[] chunks;
- DynamicNumberArray( NumberArrayFactory factory, long chunkSize )
+ DynamicNumberArray( NumberArrayFactory factory, long chunkSize, N[] initialChunks )
{
- super( chunkSize );
this.factory = factory;
- this.chunks = new NumberArray[0];
+ this.chunkSize = chunkSize;
+ this.chunks = initialChunks;
}
- protected N ensureChunkAt( long index )
+ @Override
+ public long length()
+ {
+ return chunks.length * chunkSize;
+ }
+
+ @Override
+ public void clear()
+ {
+ for ( N chunk : chunks )
+ {
+ chunk.clear();
+ }
+ }
+
+ @Override
+ public void acceptMemoryStatsVisitor( MemoryStatsVisitor visitor )
+ {
+ for ( N chunk : chunks )
+ {
+ chunk.acceptMemoryStatsVisitor( visitor );
+ }
+ }
+
+ protected N chunkOrNullAt( long index )
+ {
+ int chunkIndex = chunkIndex( index );
+ return chunkIndex < chunks.length ? (N) chunks[chunkIndex] : null;
+ }
+
+ protected int chunkIndex( long index )
+ {
+ return (int) (index/chunkSize);
+ }
+
+ @Override
+ public N at( long index )
{
if ( index >= length() )
{
synchronizedAddChunk( index );
}
- return super.chunkAt( index );
+
+ int chunkIndex = chunkIndex( index );
+ return chunks[chunkIndex];
}
private void synchronizedAddChunk( long index )
@@ -54,15 +94,24 @@ private void synchronizedAddChunk( long index )
{
if ( index >= length() )
{
- NumberArray[] newChunks = Arrays.copyOf( chunks, chunkIndex( index )+1 );
+ N[] newChunks = Arrays.copyOf( chunks, chunkIndex( index )+1 );
for ( int i = chunks.length; i < newChunks.length; i++ )
{
- newChunks[i] = addChunk( chunkSize );
+ newChunks[i] = addChunk( chunkSize, chunkSize * i );
}
chunks = newChunks;
}
}
}
- protected abstract NumberArray addChunk( long chunkSize );
+ protected abstract N addChunk( long chunkSize, long base );
+
+ @Override
+ public void close()
+ {
+ for ( N chunk : chunks )
+ {
+ chunk.close();
+ }
+ }
}
diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/HeapByteArray.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/HeapByteArray.java
new file mode 100644
index 000000000000..4f547b038306
--- /dev/null
+++ b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/HeapByteArray.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2002-2016 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.unsafe.impl.batchimport.cache;
+
+import java.nio.ByteBuffer;
+
+import static java.lang.Math.toIntExact;
+
+public class HeapByteArray extends HeapNumberArray implements ByteArray
+{
+ private final int length;
+ private final byte[] array;
+ private final ByteBuffer buffer;
+ private final byte[] defaultValue;
+
+ public HeapByteArray( int length, byte[] defaultValue, int base )
+ {
+ super( defaultValue.length, base );
+ this.length = length;
+ this.defaultValue = defaultValue;
+ this.array = new byte[itemSize * length];
+ this.buffer = ByteBuffer.wrap( array );
+ clear();
+ }
+
+ @Override
+ public long length()
+ {
+ return length;
+ }
+
+ @Override
+ public void swap( long fromIndex, long toIndex, int numberOfEntries )
+ {
+ byte[] intermediary = new byte[numberOfEntries * itemSize];
+ System.arraycopy( array, index( toIndex, 0 ), intermediary, 0, intermediary.length );
+ System.arraycopy( array, index( fromIndex, 0 ), array, index( toIndex, 0 ), intermediary.length );
+ System.arraycopy( intermediary, 0, array, index( fromIndex, 0 ), intermediary.length );
+ }
+
+ @Override
+ public void clear()
+ {
+ for ( int i = 0; i < length; i++ )
+ {
+ System.arraycopy( defaultValue, 0, array, i * itemSize, itemSize );
+ }
+ }
+
+ @Override
+ public void get( long index, byte[] into )
+ {
+ System.arraycopy( array, index( index, 0 ), into, 0, itemSize );
+ }
+
+ @Override
+ public byte getByte( long index, int offset )
+ {
+ return buffer.get( index( index, offset ) );
+ }
+
+ @Override
+ public short getShort( long index, int offset )
+ {
+ return buffer.getShort( index( index, offset ) );
+ }
+
+ @Override
+ public int getInt( long index, int offset )
+ {
+ return buffer.getInt( index( index, offset ) );
+ }
+
+ @Override
+ public long get6BLong( long index, int offset )
+ {
+ return get6BLongFromByteBuffer( buffer, index( index, offset ) );
+ }
+
+ protected static long get6BLongFromByteBuffer( ByteBuffer buffer, int startOffset )
+ {
+ long low4b = buffer.getInt( startOffset ) & 0xFFFFFFFFL;
+ long high2b = buffer.getShort( startOffset + Integer.BYTES );
+ return low4b | (high2b << 32);
+ }
+
+ @Override
+ public long getLong( long index, int offset )
+ {
+ return buffer.getLong( index( index, offset ) );
+ }
+
+ @Override
+ public void set( long index, byte[] value )
+ {
+ System.arraycopy( value, 0, array, index( index, 0 ), itemSize );
+ }
+
+ @Override
+ public void setByte( long index, int offset, byte value )
+ {
+ buffer.put( index( index, offset ), value );
+ }
+
+ @Override
+ public void setShort( long index, int offset, short value )
+ {
+ buffer.putShort( index( index, offset ), value );
+ }
+
+ @Override
+ public void setInt( long index, int offset, int value )
+ {
+ buffer.putInt( index( index, offset ), value );
+ }
+
+ @Override
+ public void set6BLong( long index, int offset, long value )
+ {
+ int absIndex = index( index, offset );
+ buffer.putInt( absIndex, (int) value );
+ buffer.putShort( absIndex + Integer.BYTES, (short) (value >>> 32) );
+ }
+
+ @Override
+ public void setLong( long index, int offset, long value )
+ {
+ buffer.putLong( index( index, offset ), value );
+ }
+
+ private int index( long index, int offset )
+ {
+ return toIntExact( (rebase( index ) * itemSize) + offset );
+ }
+}
diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/HeapIntArray.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/HeapIntArray.java
index e723fbd0bda2..80c89723a379 100644
--- a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/HeapIntArray.java
+++ b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/HeapIntArray.java
@@ -21,19 +21,17 @@
import java.util.Arrays;
-import static org.neo4j.unsafe.impl.batchimport.Utils.safeCastLongToInt;
-
/**
* A {@code long[]} on heap, abstracted into a {@link IntArray}.
*/
-public class HeapIntArray extends HeapNumberArray implements IntArray
+public class HeapIntArray extends HeapNumberArray implements IntArray
{
private final int[] array;
private final int defaultValue;
- public HeapIntArray( int length, int defaultValue )
+ public HeapIntArray( int length, int defaultValue, int base )
{
- super( 4 );
+ super( 4, base );
this.defaultValue = defaultValue;
this.array = new int[length];
clear();
@@ -48,13 +46,13 @@ public long length()
@Override
public int get( long index )
{
- return array[safeCastLongToInt( index )];
+ return array[index( index )];
}
@Override
public void set( long index, int value )
{
- array[safeCastLongToInt( index )] = value;
+ array[index( index )] = value;
}
@Override
diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/HeapLongArray.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/HeapLongArray.java
index a53e69218aa4..1dba54081627 100644
--- a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/HeapLongArray.java
+++ b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/HeapLongArray.java
@@ -21,19 +21,17 @@
import java.util.Arrays;
-import static org.neo4j.unsafe.impl.batchimport.Utils.safeCastLongToInt;
-
/**
* A {@code long[]} on heap, abstracted into a {@link LongArray}.
*/
-public class HeapLongArray extends HeapNumberArray implements LongArray
+public class HeapLongArray extends HeapNumberArray implements LongArray
{
private final long[] array;
private final long defaultValue;
- public HeapLongArray( int length, long defaultValue )
+ public HeapLongArray( int length, long defaultValue, int base )
{
- super( 8 );
+ super( 8, base );
this.defaultValue = defaultValue;
this.array = new long[length];
clear();
@@ -48,13 +46,13 @@ public long length()
@Override
public long get( long index )
{
- return array[safeCastLongToInt( index )];
+ return array[index( index )];
}
@Override
public void set( long index, long value )
{
- array[safeCastLongToInt( index )] = value;
+ array[index( index )] = value;
}
@Override
diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/HeapNumberArray.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/HeapNumberArray.java
index 4e0c0081db7a..ee32d7732975 100644
--- a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/HeapNumberArray.java
+++ b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/HeapNumberArray.java
@@ -19,16 +19,16 @@
*/
package org.neo4j.unsafe.impl.batchimport.cache;
+import static org.neo4j.unsafe.impl.batchimport.Utils.safeCastLongToInt;
+
/**
* Base class for common functionality for any {@link NumberArray} where the data lives inside heap.
*/
-abstract class HeapNumberArray implements NumberArray
+abstract class HeapNumberArray> extends BaseNumberArray
{
- private final int itemSize;
-
- protected HeapNumberArray( int itemSize )
+ protected HeapNumberArray( int itemSize, long base )
{
- this.itemSize = itemSize;
+ super( itemSize, base );
}
@Override
@@ -41,4 +41,9 @@ public void acceptMemoryStatsVisitor( MemoryStatsVisitor visitor )
public void close()
{ // Nothing to close
}
+
+ protected int index( long index )
+ {
+ return safeCastLongToInt( rebase( index ) );
+ }
}
diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/IntArray.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/IntArray.java
index ca5c7720bdad..b41c2fe5cbde 100644
--- a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/IntArray.java
+++ b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/IntArray.java
@@ -25,7 +25,7 @@
*
* @see NumberArrayFactory
*/
-public interface IntArray extends NumberArray
+public interface IntArray extends NumberArray
{
int get( long index );
diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/LongArray.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/LongArray.java
index ce06a8447e9c..30414c1385a9 100644
--- a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/LongArray.java
+++ b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/LongArray.java
@@ -25,7 +25,7 @@
*
* @see NumberArrayFactory
*/
-public interface LongArray extends NumberArray
+public interface LongArray extends NumberArray
{
long get( long index );
diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/NodeRelationshipCache.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/NodeRelationshipCache.java
index b0cd3eb28b20..ca153da4dd04 100644
--- a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/NodeRelationshipCache.java
+++ b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/NodeRelationshipCache.java
@@ -30,23 +30,30 @@
public class NodeRelationshipCache implements MemoryStatsVisitor.Visitable
{
private static final long EMPTY = -1;
+ private static final byte[] DEFAULT_VALUE = new byte[10];
+ private static final long MAX_RELATIONSHIP_ID = (1L << 48/*6B*/) - 2/*reserving -1 as legal default value*/;
+ static
+ {
+ // This looks odd, but we're using the array itself to create a default byte[] for another
+ ByteArray array = NumberArrayFactory.HEAP.newByteArray( 1, DEFAULT_VALUE.clone() );
+ array.set6BLong( 0, 0, EMPTY );
+ array.setInt( 0, 6, 0 );
+ array.get( 0, DEFAULT_VALUE );
+ }
- private final LongArray array;
+ private final ByteArray array;
private final int denseNodeThreshold;
private final RelGroupCache relGroupCache;
- private final long base;
public NodeRelationshipCache( NumberArrayFactory arrayFactory, int denseNodeThreshold )
{
- this( arrayFactory, denseNodeThreshold, 0 );
+ this( arrayFactory, denseNodeThreshold, 1_000_000, 0 );
}
- NodeRelationshipCache( NumberArrayFactory arrayFactory, int denseNodeThreshold, long base )
+ NodeRelationshipCache( NumberArrayFactory arrayFactory, int denseNodeThreshold, int chunkSize, long base )
{
- int chunkSize = 1_000_000;
- this.array = arrayFactory.newDynamicLongArray( chunkSize, IdFieldManipulator.emptyField() );
+ this.array = arrayFactory.newDynamicByteArray( chunkSize, DEFAULT_VALUE );
this.denseNodeThreshold = denseNodeThreshold;
- this.base = base;
this.relGroupCache = new RelGroupCache( arrayFactory, chunkSize, base );
}
@@ -57,30 +64,35 @@ public NodeRelationshipCache( NumberArrayFactory arrayFactory, int denseNodeThre
*/
public int incrementCount( long nodeId )
{
- long field = array.get( nodeId );
- field = IdFieldManipulator.changeCount( field, 1 );
- array.set( nodeId, field );
- return IdFieldManipulator.getCount( field );
+ ByteArray array = this.array.at( nodeId );
+ int count = array.getInt( nodeId, 6 ) + 1;
+ array.setInt( nodeId, 6, count );
+ return count;
}
public boolean isDense( long nodeId )
{
- return fieldIsDense( array.get( nodeId ) );
+ return isDense( array, nodeId );
}
- private boolean fieldIsDense( long field )
+ private boolean isDense( ByteArray array, long nodeId )
{
if ( denseNodeThreshold == EMPTY )
{ // We haven't initialized the rel group cache yet
return false;
}
- return IdFieldManipulator.getCount( field ) >= denseNodeThreshold;
+ return array.getInt( nodeId, 6 ) >= denseNodeThreshold;
}
public long getAndPutRelationship( long nodeId, int type, Direction direction, long firstRelId,
boolean incrementCount )
{
+ if ( firstRelId > MAX_RELATIONSHIP_ID )
+ {
+ throw new IllegalArgumentException( "Illegal relationship id, max is " + MAX_RELATIONSHIP_ID );
+ }
+
/*
* OK so the story about counting goes: there's an initial pass for counting number of relationships
* per node, globally, not per type/direction. After that the relationship group cache is initialized
@@ -88,40 +100,44 @@ public long getAndPutRelationship( long nodeId, int type, Direction direction, l
* not increment the global count, but it should increment the type/direction counts.
*/
- long field = array.get( nodeId );
- long existingId = IdFieldManipulator.getId( field );
- if ( fieldIsDense( field ) )
+ ByteArray array = this.array.at( nodeId );
+ long existingId = all48Bits( array, nodeId, 0 );
+ if ( isDense( array, nodeId ) )
{
if ( existingId == EMPTY )
{
existingId = relGroupCache.allocate( type, direction, firstRelId, incrementCount );
- field = IdFieldManipulator.setId( field, existingId );
- array.set( nodeId, field );
+ array.set6BLong( nodeId, 0, existingId );
return EMPTY;
}
return relGroupCache.putRelationship( existingId, type, direction, firstRelId, incrementCount );
}
- field = IdFieldManipulator.setId( field, firstRelId );
// Don't increment count for sparse node since that has already been done in a previous pass
- array.set( nodeId, field );
+ array.set6BLong( nodeId, 0, firstRelId );
return existingId;
}
+ private static long all48Bits( ByteArray array, long index, int offset )
+ {
+ long raw = array.get6BLong( index, offset );
+ return raw == -1L ? raw : raw & 0xFFFFFFFFFFFFL;
+ }
+
/**
* Used when setting node nextRel fields. Gets the first relationship for this node,
* or the first relationship group id (where it it first visits all the groups before returning the first one).
*/
public long getFirstRel( long nodeId, GroupVisitor visitor )
{
- long field = array.get( nodeId );
- if ( fieldIsDense( field ) )
+ ByteArray array = this.array.at( nodeId );
+ long id = all48Bits( array, nodeId, 0 );
+ if ( isDense( array, nodeId ) )
{ // Indirection into rel group cache
- long relGroupIndex = IdFieldManipulator.getId( field );
- return relGroupCache.visitGroups( nodeId, relGroupIndex, visitor );
+ return relGroupCache.visitGroups( nodeId, id, visitor );
}
- return IdFieldManipulator.getId( field );
+ return id;
}
public void clearRelationships()
@@ -129,11 +145,9 @@ public void clearRelationships()
long length = array.length();
for ( long i = 0; i < length; i++ )
{
- long field = array.get( i );
- if ( !fieldIsDense( field ) )
+ if ( !isDense( i ) )
{
- field = IdFieldManipulator.cleanId( field );
- array.set( i, field );
+ array.set6BLong( i, 0, -1 );
}
}
relGroupCache.clearRelationships();
@@ -141,23 +155,14 @@ public void clearRelationships()
public int getCount( long nodeId, int type, Direction direction )
{
- long field = array.get( nodeId );
- if ( fieldIsDense( field ) )
+ ByteArray array = this.array.at( nodeId );
+ if ( isDense( array, nodeId ) )
{ // Indirection into rel group cache
- long relGroupIndex = IdFieldManipulator.getId( field );
- if ( relGroupIndex == EMPTY )
- {
- return 0;
- }
- relGroupIndex = relGroupCache.findGroupIndexForType( relGroupIndex, type );
- if ( relGroupIndex == EMPTY )
- {
- return 0;
- }
- field = relGroupCache.getField( relGroupIndex, relGroupCache.directionIndex( direction ) );
+ long id = all48Bits( array, nodeId, 0 );
+ return id == EMPTY ? 0 : relGroupCache.getCount( id, type, direction );
}
- return IdFieldManipulator.getCount( field );
+ return array.getInt( nodeId, 6 );
}
public interface GroupVisitor
@@ -180,43 +185,63 @@ public long visit( long nodeId, int type, long next, long out, long in, long loo
private static class RelGroupCache implements AutoCloseable, MemoryStatsVisitor.Visitable
{
- private static final int ENTRY_SIZE = 4;
+ private static final byte[] DEFAULT_VALUE = new byte[6 + 2 + (6 + 4) * Direction.values().length];
+ static
+ {
+ ByteArray defaultArray = NumberArrayFactory.HEAP.newByteArray( 1, DEFAULT_VALUE.clone() );
+ defaultArray.set6BLong( 0, 0, EMPTY );
+ defaultArray.setShort( 0, 6, (short) EMPTY );
+ for ( int i = 0, offsetBase = 8; i < 3; i++, offsetBase += 10 )
+ {
+ defaultArray.set6BLong( 0, offsetBase, EMPTY );
+ defaultArray.setInt( 0, offsetBase + 6, 0 );
+ }
+ defaultArray.get( 0, DEFAULT_VALUE );
+ }
- private static final int INDEX_NEXT_AND_TYPE = 0;
- private static final int INDEX_OUT = 1;
- private static final int INDEX_IN = 2;
- private static final int INDEX_LOOP = 3;
+ private static final int NEXT_OFFSET = 0;
+ private static final int TYPE_OFFSET = 6;
// Used for testing high id values. Should always be zero in production
private final long base;
- private final LongArray array;
+ private final ByteArray array;
private final AtomicLong nextFreeId;
RelGroupCache( NumberArrayFactory arrayFactory, long chunkSize, long base )
{
this.base = base;
assert chunkSize > 0;
- this.array = arrayFactory.newDynamicLongArray( chunkSize, -1 );
+ // We can use this array to have "entries" accommodating one entire group, e.g:
+ // - next
+ // - type
+ // - out
+ // - out degree
+ // - in
+ // - in degree
+ // - loop
+ // - loop degree
+ this.array = arrayFactory.newDynamicByteArray( chunkSize, DEFAULT_VALUE );
this.nextFreeId = new AtomicLong( base );
}
+ public int getCount( long id, int type, Direction direction )
+ {
+ id = findGroupIndexForType( id, type );
+ return id == EMPTY ? 0 : array.getInt( rebase( id ), countOffset( direction ) );
+ }
+
private void clearRelationships()
{
- long length = array.length() / ENTRY_SIZE;
+ long length = array.length();
for ( long i = 0; i < length; i++ )
{
- clearRelationshipId( i, INDEX_OUT );
- clearRelationshipId( i, INDEX_IN );
- clearRelationshipId( i, INDEX_LOOP );
+ ByteArray array = this.array.at( i );
+ array.set6BLong( i, directionOffset( Direction.OUTGOING ), EMPTY );
+ array.set6BLong( i, directionOffset( Direction.INCOMING ), EMPTY );
+ array.set6BLong( i, directionOffset( Direction.BOTH ), EMPTY );
}
}
- private void clearRelationshipId( long relGroupIndex, int fieldIndex )
- {
- long index = index( relGroupIndex, fieldIndex );
- array.set( rebase( index ), IdFieldManipulator.cleanId( array.get( index ) ) );
- }
-
/**
* Compensate for test value of index (to avoid allocating all your RAM)
*/
@@ -230,12 +255,10 @@ private long nextFreeId()
return nextFreeId.getAndIncrement();
}
- private void initializeGroup( long relGroupIndex, int type )
+ private void initializeGroup( ByteArray array, long relGroupIndex, int type )
{
- setField( relGroupIndex, INDEX_NEXT_AND_TYPE, NextFieldManipulator.initialFieldWithType( type ) );
- setField( relGroupIndex, INDEX_OUT, IdFieldManipulator.emptyField() );
- setField( relGroupIndex, INDEX_IN, IdFieldManipulator.emptyField() );
- setField( relGroupIndex, INDEX_LOOP, IdFieldManipulator.emptyField() );
+ array.setShort( rebase( relGroupIndex ), TYPE_OFFSET, (short) type );
+ // All other values are set to defaults automatically
}
private long visitGroups( long nodeId, long relGroupIndex, GroupVisitor visitor )
@@ -244,11 +267,13 @@ private long visitGroups( long nodeId, long relGroupIndex, GroupVisitor visitor
long first = -1;
while ( currentIndex != EMPTY )
{
- int type = NextFieldManipulator.getType( getField( currentIndex, INDEX_NEXT_AND_TYPE ) );
- long out = IdFieldManipulator.getId( getField( currentIndex, INDEX_OUT ) );
- long in = IdFieldManipulator.getId( getField( currentIndex, INDEX_IN ) );
- long loop = IdFieldManipulator.getId( getField( currentIndex, INDEX_LOOP ) );
- long next = NextFieldManipulator.getNext( getField( currentIndex, INDEX_NEXT_AND_TYPE ) );
+ long index = rebase( currentIndex );
+ ByteArray array = this.array.at( index );
+ int type = array.getShort( index, TYPE_OFFSET );
+ long out = all48Bits( array, index, directionOffset( Direction.OUTGOING ) );
+ long in = all48Bits( array, index, directionOffset( Direction.INCOMING ) );
+ long loop = all48Bits( array, index, directionOffset( Direction.BOTH ) );
+ long next = all48Bits( array, index, NEXT_OFFSET );
long id = visitor.visit( nodeId, type, next, out, in, loop );
if ( first == -1 )
{ // This is the one we return
@@ -260,45 +285,37 @@ private long visitGroups( long nodeId, long relGroupIndex, GroupVisitor visitor
return first;
}
- private void setField( long relGroupIndex, int index, long field )
- {
- array.set( index( relGroupIndex, index ), field );
- }
-
- private long getField( long relGroupIndex, int index )
- {
- return array.get( index( relGroupIndex, index ) );
- }
-
- private int directionIndex( Direction direction )
+ private int directionOffset( Direction direction )
{
- return direction.ordinal()+1;
+ return 8 + (direction.ordinal() * 10);
}
- private long index( long relGroupIndex, int fieldIndex )
+ private int countOffset( Direction direction )
{
- return rebase( relGroupIndex ) * ENTRY_SIZE + fieldIndex;
+ return directionOffset( direction ) + 6;
}
public long allocate( int type, Direction direction, long relId, boolean incrementCount )
{
- long logicalPosition = nextFreeId();
- initializeGroup( logicalPosition, type );
- putRelField( logicalPosition, direction, relId, incrementCount );
- return logicalPosition;
+ long index = nextFreeId();
+ ByteArray array = this.array.at( rebase( index ) );
+ initializeGroup( array, index, type );
+ putRelField( array, index, direction, relId, incrementCount );
+ return index;
}
- private long putRelField( long relGroupIndex, Direction direction, long relId, boolean increment )
+ private long putRelField( ByteArray array, long relGroupIndex, Direction direction,
+ long relId, boolean increment )
{
- int directionIndex = directionIndex( direction );
- long field = getField( relGroupIndex, directionIndex );
- long previousId = IdFieldManipulator.getId( field );
- field = IdFieldManipulator.setId( field, relId );
+ long index = rebase( relGroupIndex );
+ int directionOffset = directionOffset( direction );
+ long previousId = all48Bits( array, index, directionOffset );
+ array.set6BLong( index, directionOffset, relId );
if ( increment )
{
- field = IdFieldManipulator.changeCount( field, 1 );
+ int countOffset = countOffset( direction );
+ array.setInt( index, countOffset, array.getInt( index, countOffset ) + 1 );
}
- setField( relGroupIndex, directionIndex, field );
return previousId;
}
@@ -309,47 +326,48 @@ public long putRelationship( long relGroupIndex, int type, Direction direction,
long previousIndex = EMPTY;
while ( currentIndex != EMPTY )
{
- long foundType = NextFieldManipulator.getType( getField( currentIndex, INDEX_NEXT_AND_TYPE ) );
+ long currentIndexRebased = rebase( currentIndex );
+ ByteArray array = this.array.at( currentIndexRebased );
+ long foundType = array.getShort( currentIndexRebased, TYPE_OFFSET );
if ( foundType == type )
{ // Found it
- return putRelField( currentIndex, direction, relId, trueForIncrement );
+ return putRelField( array, currentIndex, direction, relId, trueForIncrement );
}
else if ( foundType > type )
{ // We came too far, create room for it
break;
}
previousIndex = currentIndex;
- currentIndex = NextFieldManipulator.getNext( getField( currentIndex, INDEX_NEXT_AND_TYPE ) );
+ currentIndex = all48Bits( array, currentIndexRebased, NEXT_OFFSET );
}
long newIndex = nextFreeId();
if ( previousIndex == EMPTY )
{ // We are at the start
- array.swap( index( currentIndex, 0 ), index( newIndex, 0 ), ENTRY_SIZE );
+ array.swap( rebase( currentIndex ), rebase( newIndex ), 1 );
long swap = newIndex;
newIndex = currentIndex;
currentIndex = swap;
}
- initializeGroup( newIndex, type );
+ ByteArray array = this.array.at( rebase( newIndex ) );
+ initializeGroup( array, newIndex, type );
if ( currentIndex != EMPTY )
{ // We are NOT at the end
- setNextField( newIndex, currentIndex );
+ setNextField( array, newIndex, currentIndex );
}
if ( previousIndex != EMPTY )
{ // We are NOT at the start
- setNextField( previousIndex, newIndex );
+ setNextField( this.array, previousIndex, newIndex );
}
- return putRelField( newIndex, direction, relId, trueForIncrement );
+ return putRelField( array, newIndex, direction, relId, trueForIncrement );
}
- private void setNextField( long relGroupIndex, long next )
+ private void setNextField( ByteArray array, long relGroupIndex, long next )
{
- long field = getField( relGroupIndex, INDEX_NEXT_AND_TYPE );
- field = NextFieldManipulator.setNext( field, next );
- setField( relGroupIndex, INDEX_NEXT_AND_TYPE, field );
+ array.set6BLong( rebase( relGroupIndex ), NEXT_OFFSET, next );
}
private long findGroupIndexForType( long relGroupIndex, int type )
@@ -357,7 +375,8 @@ private long findGroupIndexForType( long relGroupIndex, int type )
long currentIndex = relGroupIndex;
while ( currentIndex != EMPTY )
{
- int foundType = NextFieldManipulator.getType( getField( currentIndex, INDEX_NEXT_AND_TYPE ) );
+ long index = rebase( currentIndex );
+ int foundType = array.getShort( index, TYPE_OFFSET );
if ( foundType == type )
{ // Found it
return currentIndex;
@@ -366,7 +385,7 @@ else if ( foundType > type )
{ // We came too far, create room for it
break;
}
- currentIndex = NextFieldManipulator.getNext( getField( currentIndex, INDEX_NEXT_AND_TYPE ) );
+ currentIndex = all48Bits( array, index, 0 );
}
return EMPTY;
}
diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/NumberArray.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/NumberArray.java
index 879a40c92489..b07a0a20b152 100644
--- a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/NumberArray.java
+++ b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/NumberArray.java
@@ -24,7 +24,7 @@
*
* @see NumberArrayFactory
*/
-public interface NumberArray extends MemoryStatsVisitor.Visitable, AutoCloseable
+public interface NumberArray> extends MemoryStatsVisitor.Visitable, AutoCloseable
{
/**
* @return length of the array, i.e. the capacity.
@@ -51,4 +51,18 @@ public interface NumberArray extends MemoryStatsVisitor.Visitable, AutoCloseable
*/
@Override
void close();
+
+ /**
+ * Part of the nature of {@link NumberArray} is that {@link #length()} can be dynamically growing.
+ * For that to work some implementations (those coming from e.g
+ * {@link NumberArrayFactory#newDynamicIntArray(long, int)} and such dynamic calls) has an indirection,
+ * one that is a bit costly when comparing to raw array access. In scenarios where there will be two or
+ * more access to the same index in the array it will be more efficient to resolve this indirection once
+ * and return the "raw" array for that given index so that it can be used directly in multiple calls,
+ * knowing that the returned array holds the given index.
+ *
+ * @param index index into the array which the returned array will contain.
+ * @return array sure to hold the given index.
+ */
+ N at( long index );
}
diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/NumberArrayFactory.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/NumberArrayFactory.java
index ed251c45afa9..a2cd43aa0789 100644
--- a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/NumberArrayFactory.java
+++ b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/NumberArrayFactory.java
@@ -23,6 +23,7 @@
import org.neo4j.helpers.Exceptions;
+import static java.lang.Math.toIntExact;
import static java.lang.String.format;
import static org.neo4j.helpers.Format.bytes;
@@ -40,7 +41,18 @@ public interface NumberArrayFactory
* @param defaultValue value which will represent unset values.
* @return a fixed size {@link IntArray}.
*/
- IntArray newIntArray( long length, int defaultValue );
+ default IntArray newIntArray( long length, int defaultValue )
+ {
+ return newIntArray( length, defaultValue, 0 );
+ }
+
+ /**
+ * @param length size of the array.
+ * @param defaultValue value which will represent unset values.
+ * @param base base index to rebase all requested indexes with.
+ * @return a fixed size {@link IntArray}.
+ */
+ IntArray newIntArray( long length, int defaultValue, long base );
/**
* @param chunkSize the size of each array (number of items). Where new chunks are added when needed.
@@ -54,7 +66,18 @@ public interface NumberArrayFactory
* @param defaultValue value which will represent unset values.
* @return a fixed size {@link LongArray}.
*/
- LongArray newLongArray( long length, long defaultValue );
+ default LongArray newLongArray( long length, long defaultValue )
+ {
+ return newLongArray( length, defaultValue, 0 );
+ }
+
+ /**
+ * @param length size of the array.
+ * @param defaultValue value which will represent unset values.
+ * @param base base index to rebase all requested indexes with.
+ * @return a fixed size {@link LongArray}.
+ */
+ LongArray newLongArray( long length, long defaultValue, long base );
/**
* @param chunkSize the size of each array (number of items). Where new chunks are added when needed.
@@ -63,6 +86,31 @@ public interface NumberArrayFactory
*/
LongArray newDynamicLongArray( long chunkSize, long defaultValue );
+ /**
+ * @param length size of the array.
+ * @param defaultValue value which will represent unset values.
+ * @return a fixed size {@link ByteArray}.
+ */
+ default ByteArray newByteArray( long length, byte[] defaultValue )
+ {
+ return newByteArray( length, defaultValue, 0 );
+ }
+
+ /**
+ * @param length size of the array.
+ * @param defaultValue value which will represent unset values.
+ * @param base base index to rebase all requested indexes with.
+ * @return a fixed size {@link ByteArray}.
+ */
+ ByteArray newByteArray( long length, byte[] defaultValue, long base );
+
+ /**
+ * @param chunkSize the size of each array (number of items). Where new chunks are added when needed.
+ * @param defaultValue value which will represent unset values.
+ * @return dynamically growing {@link ByteArray}.
+ */
+ ByteArray newDynamicByteArray( long chunkSize, byte[] defaultValue );
+
/**
* Implements the dynamic array methods, because they are the same in most implementations.
*/
@@ -79,6 +127,12 @@ public LongArray newDynamicLongArray( long chunkSize, long defaultValue )
{
return new DynamicLongArray( this, chunkSize, defaultValue );
}
+
+ @Override
+ public ByteArray newDynamicByteArray( long chunkSize, byte[] defaultValue )
+ {
+ return new DynamicByteArray( this, chunkSize, defaultValue );
+ }
}
/**
@@ -87,15 +141,21 @@ public LongArray newDynamicLongArray( long chunkSize, long defaultValue )
NumberArrayFactory HEAP = new Adapter()
{
@Override
- public IntArray newIntArray( long length, int defaultValue )
+ public IntArray newIntArray( long length, int defaultValue, long base )
{
- return new HeapIntArray( safeCastLongToInt( length ), defaultValue );
+ return new HeapIntArray( safeCastLongToInt( length ), defaultValue, toIntExact( base ) );
}
@Override
- public LongArray newLongArray( long length, long defaultValue )
+ public LongArray newLongArray( long length, long defaultValue, long base )
{
- return new HeapLongArray( safeCastLongToInt( length ), defaultValue );
+ return new HeapLongArray( safeCastLongToInt( length ), defaultValue, toIntExact( base ) );
+ }
+
+ @Override
+ public ByteArray newByteArray( long length, byte[] defaultValue, long base )
+ {
+ return new HeapByteArray( safeCastLongToInt( length ), defaultValue, toIntExact( base ) );
}
@Override
@@ -111,15 +171,21 @@ public String toString()
NumberArrayFactory OFF_HEAP = new Adapter()
{
@Override
- public IntArray newIntArray( long length, int defaultValue )
+ public IntArray newIntArray( long length, int defaultValue, long base )
{
- return new OffHeapIntArray( length, defaultValue );
+ return new OffHeapIntArray( length, defaultValue, base );
}
@Override
- public LongArray newLongArray( long length, long defaultValue )
+ public LongArray newLongArray( long length, long defaultValue, long base )
{
- return new OffHeapLongArray( length, defaultValue );
+ return new OffHeapLongArray( length, defaultValue, base );
+ }
+
+ @Override
+ public ByteArray newByteArray( long length, byte[] defaultValue, long base )
+ {
+ return new OffHeapByteArray( length, defaultValue, base );
}
@Override
@@ -144,14 +210,14 @@ public Auto( NumberArrayFactory... candidates )
}
@Override
- public LongArray newLongArray( long length, long defaultValue )
+ public LongArray newLongArray( long length, long defaultValue, long base )
{
OutOfMemoryError error = null;
for ( NumberArrayFactory candidate : candidates )
{
try
{
- return candidate.newLongArray( length, defaultValue );
+ return candidate.newLongArray( length, defaultValue, base );
}
catch ( OutOfMemoryError e )
{ // Allright let's try the next one
@@ -162,14 +228,14 @@ public LongArray newLongArray( long length, long defaultValue )
}
@Override
- public IntArray newIntArray( long length, int defaultValue )
+ public IntArray newIntArray( long length, int defaultValue, long base )
{
OutOfMemoryError error = null;
for ( NumberArrayFactory candidate : candidates )
{
try
{
- return candidate.newIntArray( length, defaultValue );
+ return candidate.newIntArray( length, defaultValue, base );
}
catch ( OutOfMemoryError e )
{ // Allright let's try the next one
@@ -179,6 +245,24 @@ public IntArray newIntArray( long length, int defaultValue )
throw error( length, 4, error );
}
+ @Override
+ public ByteArray newByteArray( long length, byte[] defaultValue, long base )
+ {
+ OutOfMemoryError error = null;
+ for ( NumberArrayFactory candidate : candidates )
+ {
+ try
+ {
+ return candidate.newByteArray( length, defaultValue, base );
+ }
+ catch ( OutOfMemoryError e )
+ { // Allright let's try the next one
+ error = e;
+ }
+ }
+ throw error( length, defaultValue.length, error );
+ }
+
private OutOfMemoryError error( long length, int itemSize, OutOfMemoryError error )
{
throw Exceptions.withMessage( error, format( "%s: Not enough memory available for allocating %s, tried %s",
@@ -196,24 +280,35 @@ private OutOfMemoryError error( long length, int itemSize, OutOfMemoryError erro
private final NumberArrayFactory delegate = new Auto( OFF_HEAP, HEAP );
@Override
- public LongArray newLongArray( long length, long defaultValue )
+ public LongArray newLongArray( long length, long defaultValue, long base )
+ {
+ // Here we want to have the property of a dynamic array which makes some parts of the array
+ // live on heap, some off. At the same time we want a fixed size array. Therefore first create
+ // the array as a dynamic array and make it grow to the requested length.
+ LongArray array = newDynamicLongArray( fractionOf( length ), defaultValue );
+ array.at( length-1 );
+ return array;
+ }
+
+ @Override
+ public IntArray newIntArray( long length, int defaultValue, long base )
{
// Here we want to have the property of a dynamic array which makes some parts of the array
// live on heap, some off. At the same time we want a fixed size array. Therefore first create
- // the array as a dynamic array, make it grow to the requested length and then fixate.
- DynamicLongArray array = newDynamicLongArray( fractionOf( length ), defaultValue );
- array.ensureChunkAt( length-1 );
+ // the array as a dynamic array and make it grow to the requested length.
+ IntArray array = newDynamicIntArray( fractionOf( length ), defaultValue );
+ array.at( length-1 );
return array;
}
@Override
- public IntArray newIntArray( long length, int defaultValue )
+ public ByteArray newByteArray( long length, byte[] defaultValue, long base )
{
// Here we want to have the property of a dynamic array which makes some parts of the array
// live on heap, some off. At the same time we want a fixed size array. Therefore first create
- // the array as a dynamic array, make it grow to the requested length and then fixate.
- DynamicIntArray array = newDynamicIntArray( fractionOf( length ), defaultValue );
- array.ensureChunkAt( length-1 );
+ // the array as a dynamic array and make it grow to the requested length.
+ ByteArray array = newDynamicByteArray( fractionOf( length ), defaultValue );
+ array.at( length-1 );
return array;
}
@@ -223,17 +318,23 @@ private long fractionOf( long length )
}
@Override
- public DynamicIntArray newDynamicIntArray( long chunkSize, int defaultValue )
+ public IntArray newDynamicIntArray( long chunkSize, int defaultValue )
{
return new DynamicIntArray( delegate, chunkSize, defaultValue );
}
@Override
- public DynamicLongArray newDynamicLongArray( long chunkSize, long defaultValue )
+ public LongArray newDynamicLongArray( long chunkSize, long defaultValue )
{
return new DynamicLongArray( delegate, chunkSize, defaultValue );
}
+ @Override
+ public ByteArray newDynamicByteArray( long chunkSize, byte[] defaultValue )
+ {
+ return new DynamicByteArray( delegate, chunkSize, defaultValue );
+ }
+
@Override
public String toString()
{
diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/OffHeapByteArray.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/OffHeapByteArray.java
new file mode 100644
index 000000000000..e6385a5e92a0
--- /dev/null
+++ b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/OffHeapByteArray.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2002-2016 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.unsafe.impl.batchimport.cache;
+
+import org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil;
+
+public class OffHeapByteArray extends OffHeapNumberArray implements ByteArray
+{
+ private final byte[] defaultValue;
+
+ protected OffHeapByteArray( long length, byte[] defaultValue, long base )
+ {
+ super( length, defaultValue.length, base );
+ this.defaultValue = defaultValue;
+ clear();
+ }
+
+ @Override
+ public void swap( long fromIndex, long toIndex, int numberOfEntries )
+ {
+ int size = numberOfEntries * itemSize;
+ long intermediary = UnsafeUtil.allocateMemory( size );
+ UnsafeUtil.copyMemory( address( fromIndex, 0 ), intermediary, size );
+ UnsafeUtil.copyMemory( address( toIndex, 0 ), address( fromIndex, 0 ), size );
+ UnsafeUtil.copyMemory( intermediary, address( toIndex, 0 ), size );
+ UnsafeUtil.free( intermediary );
+ }
+
+ @Override
+ public void clear()
+ {
+ if ( isByteUniform( defaultValue ) )
+ {
+ UnsafeUtil.setMemory( address, length * itemSize, defaultValue[0] );
+ }
+ else
+ {
+ long intermediary = UnsafeUtil.allocateMemory( itemSize );
+ for ( int i = 0; i < defaultValue.length; i++ )
+ {
+ UnsafeUtil.putByte( intermediary + i, defaultValue[i] );
+ }
+
+ for ( long i = 0, adr = address; i < length; i++, adr += itemSize )
+ {
+ UnsafeUtil.copyMemory( intermediary, adr, itemSize );
+ }
+ UnsafeUtil.free( intermediary );
+ }
+ }
+
+ private boolean isByteUniform( byte[] bytes )
+ {
+ byte reference = bytes[0];
+ for ( int i = 1; i < bytes.length; i++ )
+ {
+ if ( reference != bytes[i] )
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void get( long index, byte[] into )
+ {
+ long address = address( index, 0 );
+ for ( int i = 0; i < itemSize; i++, address++ )
+ {
+ into[i] = UnsafeUtil.getByte( address );
+ }
+ }
+
+ @Override
+ public byte getByte( long index, int offset )
+ {
+ return UnsafeUtil.getByte( address( index, offset ) );
+ }
+
+ @Override
+ public short getShort( long index, int offset )
+ {
+ return UnsafeUtil.getShort( address( index, offset ) );
+ }
+
+ @Override
+ public int getInt( long index, int offset )
+ {
+ return UnsafeUtil.getInt( address( index, offset ) );
+ }
+
+ @Override
+ public long get6BLong( long index, int offset )
+ {
+ long address = address( index, offset );
+ long low4b = (UnsafeUtil.getInt( address )) & 0xFFFFFFFFL;
+ long high2b = UnsafeUtil.getShort( address + Integer.BYTES );
+ return low4b | (high2b << 32);
+ }
+
+ @Override
+ public long getLong( long index, int offset )
+ {
+ return UnsafeUtil.getLong( address( index, offset ) );
+ }
+
+ @Override
+ public void set( long index, byte[] value )
+ {
+ long address = address( index, 0 );
+ for ( int i = 0; i < itemSize; i++, address++ )
+ {
+ UnsafeUtil.putByte( address, value[i] );
+ }
+ }
+
+ @Override
+ public void setByte( long index, int offset, byte value )
+ {
+ UnsafeUtil.putByte( address( index, offset ), value );
+ }
+
+ @Override
+ public void setShort( long index, int offset, short value )
+ {
+ UnsafeUtil.putShort( address( index, offset ), value );
+ }
+
+ @Override
+ public void setInt( long index, int offset, int value )
+ {
+ UnsafeUtil.putInt( address( index, offset ), value );
+ }
+
+ @Override
+ public void set6BLong( long index, int offset, long value )
+ {
+ long address = address( index, offset );
+ UnsafeUtil.putInt( address, (int) value );
+ UnsafeUtil.putShort( address + Integer.BYTES, (short) (value >>> 32) );
+ }
+
+ @Override
+ public void setLong( long index, int offset, long value )
+ {
+ UnsafeUtil.putLong( address( index, offset ), value );
+ }
+
+ private long address( long index, int offset )
+ {
+ return address + (rebase( index ) * itemSize) + offset;
+ }
+}
diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/OffHeapIntArray.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/OffHeapIntArray.java
index d1a0d76c203f..c863196b52ac 100644
--- a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/OffHeapIntArray.java
+++ b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/OffHeapIntArray.java
@@ -25,13 +25,13 @@
* Off-heap version of {@link IntArray} using {@code sun.misc.Unsafe}. Supports arrays with length beyond
* Integer.MAX_VALUE.
*/
-public class OffHeapIntArray extends OffHeapNumberArray implements IntArray
+public class OffHeapIntArray extends OffHeapRegularNumberArray implements IntArray
{
private final int defaultValue;
- public OffHeapIntArray( long length, int defaultValue )
+ public OffHeapIntArray( long length, int defaultValue, long base )
{
- super( length, 2 );
+ super( length, 2, base );
this.defaultValue = defaultValue;
clear();
}
@@ -57,7 +57,7 @@ public void clear()
}
else
{
- for ( long i = 0, adr = address; i < length; i++, adr += stride )
+ for ( long i = 0, adr = address; i < length; i++, adr += itemSize )
{
UnsafeUtil.putInt( adr, defaultValue );
}
@@ -70,7 +70,7 @@ public void swap( long fromIndex, long toIndex, int numberOfEntries )
long fromAddress = addressOf( fromIndex );
long toAddress = addressOf( toIndex );
- for ( int i = 0; i < numberOfEntries; i++, fromAddress += stride, toAddress += stride )
+ for ( int i = 0; i < numberOfEntries; i++, fromAddress += itemSize, toAddress += itemSize )
{
int fromValue = UnsafeUtil.getInt( fromAddress );
UnsafeUtil.putInt( fromAddress, UnsafeUtil.getInt( toAddress ) );
diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/OffHeapLongArray.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/OffHeapLongArray.java
index b3f266f5be2a..ac19676ca664 100644
--- a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/OffHeapLongArray.java
+++ b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/OffHeapLongArray.java
@@ -25,13 +25,13 @@
* Off-heap version of {@link LongArray} using {@code sun.misc.Unsafe}. Supports arrays with length beyond
* Integer.MAX_VALUE.
*/
-public class OffHeapLongArray extends OffHeapNumberArray implements LongArray
+public class OffHeapLongArray extends OffHeapRegularNumberArray implements LongArray
{
private final long defaultValue;
- public OffHeapLongArray( long length, long defaultValue )
+ public OffHeapLongArray( long length, long defaultValue, long base )
{
- super( length, 3 );
+ super( length, 3, base );
this.defaultValue = defaultValue;
clear();
}
@@ -57,7 +57,7 @@ public void clear()
}
else
{
- for ( long i = 0, adr = address; i < length; i++, adr += stride )
+ for ( long i = 0, adr = address; i < length; i++, adr += itemSize )
{
UnsafeUtil.putLong( adr, defaultValue );
}
@@ -70,7 +70,7 @@ public void swap( long fromIndex, long toIndex, int numberOfEntries )
long fromAddress = addressOf( fromIndex );
long toAddress = addressOf( toIndex );
- for ( int i = 0; i < numberOfEntries; i++, fromAddress += stride, toAddress += stride )
+ for ( int i = 0; i < numberOfEntries; i++, fromAddress += itemSize, toAddress += itemSize )
{
long fromValue = UnsafeUtil.getLong( fromAddress );
UnsafeUtil.putLong( fromAddress, UnsafeUtil.getLong( toAddress ) );
diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/OffHeapNumberArray.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/OffHeapNumberArray.java
index e7e24985258d..de499a9cab39 100644
--- a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/OffHeapNumberArray.java
+++ b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/OffHeapNumberArray.java
@@ -21,24 +21,18 @@
import org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil;
-/**
- * Base class for common functionality for any {@link NumberArray} where the data lives off-heap.
- */
-abstract class OffHeapNumberArray implements NumberArray
+public abstract class OffHeapNumberArray> extends BaseNumberArray
{
protected final long address;
protected final long length;
- protected final int shift;
- protected final int stride;
private boolean closed;
- protected OffHeapNumberArray( long length, int shift )
+ protected OffHeapNumberArray( long length, int itemSize, long base )
{
+ super( itemSize, base );
UnsafeUtil.assertHasUnsafe();
this.length = length;
- this.shift = shift;
- this.stride = 1 << shift;
- this.address = UnsafeUtil.allocateMemory( length << shift );
+ this.address = UnsafeUtil.allocateMemory( length * itemSize );
}
@Override
@@ -47,37 +41,10 @@ public long length()
return length;
}
- protected long addressOf( long index )
- {
- if ( index < 0 || index >= length )
- {
- throw new ArrayIndexOutOfBoundsException( "Requested index " + index + ", but length is " + length );
- }
- return address + (index << shift);
- }
-
- protected boolean isByteUniform( long value )
- {
- byte any = 0; // assignment not really needed
- for ( int i = 0; i < stride; i++ )
- {
- byte test = (byte)(value >>> 8*i);
- if ( i == 0 )
- {
- any = test;
- }
- else if ( test != any )
- {
- return false;
- }
- }
- return true;
- }
-
@Override
public void acceptMemoryStatsVisitor( MemoryStatsVisitor visitor )
{
- visitor.offHeapUsage( length * stride );
+ visitor.offHeapUsage( length * itemSize );
}
@Override
diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/OffHeapRegularNumberArray.java b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/OffHeapRegularNumberArray.java
new file mode 100644
index 000000000000..6b65309477d9
--- /dev/null
+++ b/community/kernel/src/main/java/org/neo4j/unsafe/impl/batchimport/cache/OffHeapRegularNumberArray.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2002-2016 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.unsafe.impl.batchimport.cache;
+
+/**
+ * Base class for common functionality for any {@link NumberArray} where the data lives off-heap.
+ */
+abstract class OffHeapRegularNumberArray> extends OffHeapNumberArray
+{
+ protected final int shift;
+
+ protected OffHeapRegularNumberArray( long length, int shift, long base )
+ {
+ super( length, 1 << shift, base );
+ this.shift = shift;
+ }
+
+ protected long addressOf( long index )
+ {
+ index = rebase( index );
+ if ( index < 0 || index >= length )
+ {
+ throw new ArrayIndexOutOfBoundsException( "Requested index " + index + ", but length is " + length );
+ }
+ return address + (index << shift);
+ }
+
+ protected boolean isByteUniform( long value )
+ {
+ byte any = 0; // assignment not really needed
+ for ( int i = 0; i < itemSize; i++ )
+ {
+ byte test = (byte)(value >>> 8*i);
+ if ( i == 0 )
+ {
+ any = test;
+ }
+ else if ( test != any )
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/community/kernel/src/test/java/org/neo4j/unsafe/impl/batchimport/cache/ByteArrayTest.java b/community/kernel/src/test/java/org/neo4j/unsafe/impl/batchimport/cache/ByteArrayTest.java
new file mode 100644
index 000000000000..1729bad13c23
--- /dev/null
+++ b/community/kernel/src/test/java/org/neo4j/unsafe/impl/batchimport/cache/ByteArrayTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2002-2016 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.unsafe.impl.batchimport.cache;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class ByteArrayTest
+{
+ @Test
+ public void shouldSetAndGetBasicTypes() throws Exception
+ {
+ // GIVEN
+ ByteArray array = newArray( 100, new byte[15] );
+
+ // WHEN
+ array.setByte( 0, 0, (byte) 123 );
+ array.setShort( 0, 1, (short) 1234 );
+ array.setInt( 0, 5, 12345 );
+ array.setLong( 0, 9, Long.MAX_VALUE - 100 );
+
+ // THEN
+ assertEquals( (byte) 123, array.getByte( 0, 0 ) );
+ assertEquals( (short) 1234, array.getShort( 0, 1 ) );
+ assertEquals( 12345, array.getInt( 0, 5 ) );
+ assertEquals( Long.MAX_VALUE - 100, array.getLong( 0, 9 ) );
+ }
+
+ @Test
+ public void shouldDetectMinusOne() throws Exception
+ {
+ // GIVEN
+ ByteArray array = newArray( 100, new byte[15] );
+
+ // WHEN
+ array.set6BLong( 10, 2, -1 );
+ array.set6BLong( 10, 8, -1 );
+
+ // THEN
+ assertEquals( -1L, array.get6BLong( 10, 2 ) );
+ assertEquals( -1L, array.get6BLong( 10, 8 ) );
+ }
+
+ private ByteArray newArray( int length, byte[] defaultValue )
+ {
+ return NumberArrayFactory.HEAP.newByteArray( length, defaultValue );
+ }
+}
diff --git a/community/kernel/src/test/java/org/neo4j/unsafe/impl/batchimport/cache/NodeRelationshipCacheTest.java b/community/kernel/src/test/java/org/neo4j/unsafe/impl/batchimport/cache/NodeRelationshipCacheTest.java
index 3e46a1104acd..4e678fbdfa2a 100644
--- a/community/kernel/src/test/java/org/neo4j/unsafe/impl/batchimport/cache/NodeRelationshipCacheTest.java
+++ b/community/kernel/src/test/java/org/neo4j/unsafe/impl/batchimport/cache/NodeRelationshipCacheTest.java
@@ -19,6 +19,7 @@
*/
package org.neo4j.unsafe.impl.batchimport.cache;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -35,6 +36,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -43,11 +45,14 @@
import static java.lang.Math.max;
import static java.lang.System.currentTimeMillis;
+import static org.neo4j.graphdb.Direction.OUTGOING;
+
@RunWith( Parameterized.class )
public class NodeRelationshipCacheTest
{
@Parameterized.Parameter( 0 )
public long base;
+ private NodeRelationshipCache cache;
@Parameterized.Parameters
public static Collection