/* For Copyright and License see LICENSE.txt and COPYING.txt in the root directory */ package com.nerdscentral.data; public final class OffHeapArray extends UnsafeProvider implements AutoCloseable { private final long address; private final long length; private volatile boolean closed; public final static long LENGTH_OF_BYTE = 1; public final static long LENGTH_OF_SHORT = 2; public final static long LENGTH_OF_INT = 4; public final static long LENGTH_OF_LONG = 8; public final static long LENGTH_OF_FLOAT = 4; public final static long LENGTH_OF_DOUBLE = 8; private final static Allocator defaultAllocator = new SimpleOffHeapAllocator(); public interface IndexedByte { byte get(long index); } public interface IndexedShort { short get(long index); } public interface IndexedInt { int get(long index); } public interface IndexedLong { long get(long index); } public interface IndexedFloat { float get(long index); } public interface IndexedDouble { double get(long index); } public final static class OutOfOffheapException extends RuntimeException { private static final long serialVersionUID = 1L; public OutOfOffheapException(long size) { super(Messages.getString("OffHeapArray.0") + size + Messages.getString("OffHeapArray.1")); //$NON-NLS-1$ //$NON-NLS-2$ } } public static interface Allocator { long allocate(long bytes); void free(long address); } public final static class SimpleOffHeapAllocator extends UnsafeProvider implements Allocator { @Override public final long allocate(long bytes) { long address = unsafe.allocateMemory(bytes); if (address == 0) { throw new OutOfOffheapException(bytes); } return address; } @Override public final void free(long address) { unsafe.freeMemory(address); } } private final Allocator allocator; private OffHeapArray(long lengthIn, long stepIn, Allocator allocatorIn) { allocator = allocatorIn; closed = true; length = lengthIn * stepIn; address = allocator.allocate(length); closed = false; } private final void checkBounds(long start, long end, long step) { if ((start < 0) || (start * step > length) || (end < 0) || (end * step > length)) { throw new ArrayIndexOutOfBoundsException("" + start + "," + end); //$NON-NLS-1$ //$NON-NLS-2$ } } public final void checkBoundsByte(long start, long end) { checkBounds(start, end, LENGTH_OF_BYTE); } public final void checkBoundsShort(long start, long end) { checkBounds(start, end, LENGTH_OF_SHORT); } public final void checkBoundsInt(long start, long end) { checkBounds(start, end, LENGTH_OF_INT); } public final void checkBoundsLong(long start, long end) { checkBounds(start, end, LENGTH_OF_LONG); } public final void checkBoundsFLoat(long start, long end) { checkBounds(start, end, LENGTH_OF_FLOAT); } public final void checkBoundsDouble(long start, long end) { checkBounds(start, end, LENGTH_OF_DOUBLE); } public final long byteSize() { return length / LENGTH_OF_BYTE; } public final long shortSize() { return length / LENGTH_OF_SHORT; } public final long intSize() { return length / LENGTH_OF_INT; } public final long longSize() { return length / LENGTH_OF_LONG; } public final long floatSize() { return length / LENGTH_OF_FLOAT; } public final long doubleSize() { return length / LENGTH_OF_DOUBLE; } public final void initialise() { initialise((byte) 0); } public final void initialise(byte value) { unsafe.setMemory(address, length, value); } public final void initialiseIByte(IndexedByte provider) { for (long i = 0; i < length; ++i) { setByte(i, provider.get(i)); } } public final void initialiseIShort(IndexedShort provider) { for (long i = 0; i < length; i += LENGTH_OF_SHORT) { setShort(i, provider.get(i)); } } public final void initialiseIInt(IndexedInt provider) { for (long i = 0; i < length; i += LENGTH_OF_INT) { setInt(i, provider.get(i)); } } public final void initialiseILong(IndexedLong provider) { for (long i = 0; i < length; i += LENGTH_OF_LONG) { setLong(i, provider.get(i)); } } public final void initialiseIFloat(IndexedFloat provider) { for (long i = 0; i < length; i += LENGTH_OF_FLOAT) { setFloat(i, provider.get(i)); } } public final void initialiseIDouble(IndexedDouble provider) { for (long i = 0; i < length; i += LENGTH_OF_DOUBLE) { setDouble(i, provider.get(i)); } } public final static OffHeapArray byteArray(long length) { return new OffHeapArray(length, LENGTH_OF_BYTE, defaultAllocator); } public final static OffHeapArray shortArray(long length) { return new OffHeapArray(length, LENGTH_OF_SHORT, defaultAllocator); } public final static OffHeapArray intArray(long length) { return new OffHeapArray(length, LENGTH_OF_INT, defaultAllocator); } public final static OffHeapArray longArray(long length) { return new OffHeapArray(length, LENGTH_OF_LONG, defaultAllocator); } public final static OffHeapArray floatArray(long length) { return new OffHeapArray(length, LENGTH_OF_FLOAT, defaultAllocator); } public final static OffHeapArray doubleArray(long length) { return new OffHeapArray(length, LENGTH_OF_DOUBLE, defaultAllocator); } public final static OffHeapArray byteArray(long length, Allocator alloc) { return new OffHeapArray(length, LENGTH_OF_BYTE, alloc); } public final static OffHeapArray shortArray(long length, Allocator alloc) { return new OffHeapArray(length, LENGTH_OF_SHORT, alloc); } public final static OffHeapArray intArray(long length, Allocator alloc) { return new OffHeapArray(length, LENGTH_OF_INT, alloc); } public final static OffHeapArray longArray(long length, Allocator alloc) { return new OffHeapArray(length, LENGTH_OF_LONG, alloc); } public final static OffHeapArray floatArray(long length, Allocator alloc) { return new OffHeapArray(length, LENGTH_OF_FLOAT, alloc); } public final static OffHeapArray doubleArray(long length, Allocator alloc) { return new OffHeapArray(length, LENGTH_OF_DOUBLE, alloc); } public final void setByte(long index, byte value) { unsafe.putByte(address + index * LENGTH_OF_BYTE, value); } public final void setShort(long index, short value) { unsafe.putShort(address + index * LENGTH_OF_SHORT, value); } public final void setInt(long index, int value) { unsafe.putInt(address + index * LENGTH_OF_INT, value); } public final void setLong(long index, long value) { unsafe.putLong(address + index * LENGTH_OF_LONG, value); } public final void setFloat(long index, float value) { unsafe.putFloat(address + index * LENGTH_OF_FLOAT, value); } public final void setDouble(long index, double value) { unsafe.putDouble(address + index * LENGTH_OF_DOUBLE, value); } public final byte getByte(long index) { return unsafe.getByte(address + index * LENGTH_OF_BYTE); } public final short getShort(long index) { return unsafe.getShort(address + index * LENGTH_OF_SHORT); } public final int getInt(long index) { return unsafe.getInt(address + index * LENGTH_OF_INT); } public final long getLong(long index) { return unsafe.getLong(address + index * LENGTH_OF_LONG); } public final float getFloat(long index) { return unsafe.getFloat(address + index * LENGTH_OF_FLOAT); } public final double getDouble(long index) { return unsafe.getDouble(address + index * LENGTH_OF_DOUBLE); } @Override public final void close() throws RuntimeException { if (!closed) { synchronized (this) { if (!closed) { closed = true; } } allocator.free(address); } } @Override public final void finalize() throws Exception { if (!closed) System.err.println("Mem leak"); //$NON-NLS-1$ close(); } }