package org.apache.cassandra.utils.memory;
import java.lang.reflect.Field;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import com.sun.jna.Native;
import org.apache.cassandra.utils.Architecture;
import sun.misc.Unsafe;
import sun.nio.ch.DirectBuffer;
public abstract class MemoryUtil
{
private static final long UNSAFE_COPY_THRESHOLD = 1024 * 1024L;
private static final Unsafe unsafe;
private static final Class<?> DIRECT_BYTE_BUFFER_CLASS, RO_DIRECT_BYTE_BUFFER_CLASS;
private static final long DIRECT_BYTE_BUFFER_ADDRESS_OFFSET;
private static final long DIRECT_BYTE_BUFFER_CAPACITY_OFFSET;
private static final long DIRECT_BYTE_BUFFER_LIMIT_OFFSET;
private static final long DIRECT_BYTE_BUFFER_POSITION_OFFSET;
private static final long DIRECT_BYTE_BUFFER_ATTACHMENT_OFFSET;
private static final Class<?> BYTE_BUFFER_CLASS;
private static final long BYTE_BUFFER_OFFSET_OFFSET;
private static final long BYTE_BUFFER_HB_OFFSET;
private static final long BYTE_ARRAY_BASE_OFFSET;
private static final boolean BIG_ENDIAN = ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN);
public static final boolean INVERTED_ORDER = Architecture.IS_UNALIGNED && !BIG_ENDIAN;
static
{
try
{
Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (sun.misc.Unsafe) field.get(null);
Class<?> clazz = ByteBuffer.allocateDirect(0).getClass();
DIRECT_BYTE_BUFFER_ADDRESS_OFFSET = unsafe.objectFieldOffset(Buffer.class.getDeclaredField("address"));
DIRECT_BYTE_BUFFER_CAPACITY_OFFSET = unsafe.objectFieldOffset(Buffer.class.getDeclaredField("capacity"));
DIRECT_BYTE_BUFFER_LIMIT_OFFSET = unsafe.objectFieldOffset(Buffer.class.getDeclaredField("limit"));
DIRECT_BYTE_BUFFER_POSITION_OFFSET = unsafe.objectFieldOffset(Buffer.class.getDeclaredField("position"));
DIRECT_BYTE_BUFFER_ATTACHMENT_OFFSET = unsafe.objectFieldOffset(clazz.getDeclaredField("att"));
DIRECT_BYTE_BUFFER_CLASS = clazz;
RO_DIRECT_BYTE_BUFFER_CLASS = ByteBuffer.allocateDirect(0).asReadOnlyBuffer().getClass();
clazz = ByteBuffer.allocate(0).getClass();
BYTE_BUFFER_OFFSET_OFFSET = unsafe.objectFieldOffset(ByteBuffer.class.getDeclaredField("offset"));
BYTE_BUFFER_HB_OFFSET = unsafe.objectFieldOffset(ByteBuffer.class.getDeclaredField("hb"));
BYTE_BUFFER_CLASS = clazz;
BYTE_ARRAY_BASE_OFFSET = unsafe.arrayBaseOffset(byte[].class);
}
catch (Exception e)
{
throw new AssertionError(e);
}
}
public static int pageSize()
{
return unsafe.pageSize();
}
public static long getAddress(ByteBuffer buffer)
{
assert buffer.getClass() == DIRECT_BYTE_BUFFER_CLASS;
return unsafe.getLong(buffer, DIRECT_BYTE_BUFFER_ADDRESS_OFFSET);
}
public static long allocate(long size)
{
return Native.malloc(size);
}
public static void free(long peer)
{
Native.free(peer);
}
public static void setByte(long address, byte b)
{
unsafe.putByte(address, b);
}
public static void setByte(long address, int count, byte b)
{
unsafe.setMemory(address, count, b);
}
public static void setShort(long address, short s)
{
unsafe.putShort(address, s);
}
public static void setInt(long address, int l)
{
if (Architecture.IS_UNALIGNED)
unsafe.putInt(address, l);
else
putIntByByte(address, l);
}
public static void setLong(long address, long l)
{
if (Architecture.IS_UNALIGNED)
unsafe.putLong(address, l);
else
putLongByByte(address, l);
}
public static byte getByte(long address)
{
return unsafe.getByte(address);
}
public static int getShort(long address)
{
return (Architecture.IS_UNALIGNED ? unsafe.getShort(address) : getShortByByte(address)) & 0xffff;
}
public static int getInt(long address)
{
return Architecture.IS_UNALIGNED ? unsafe.getInt(address) : getIntByByte(address);
}
public static long getLong(long address)
{
return Architecture.IS_UNALIGNED ? unsafe.getLong(address) : getLongByByte(address);
}
public static ByteBuffer getByteBuffer(long address, int length)
{
return getByteBuffer(address, length, ByteOrder.nativeOrder());
}
public static ByteBuffer getByteBuffer(long address, int length, ByteOrder order)
{
ByteBuffer instance = getHollowDirectByteBuffer(order);
setByteBuffer(instance, address, length);
return instance;
}
public static ByteBuffer getHollowDirectByteBuffer()
{
return getHollowDirectByteBuffer(ByteOrder.nativeOrder());
}
public static ByteBuffer getHollowDirectByteBuffer(ByteOrder order)
{
ByteBuffer instance;
try
{
instance = (ByteBuffer) unsafe.allocateInstance(DIRECT_BYTE_BUFFER_CLASS);
}
catch (InstantiationException e)
{
throw new AssertionError(e);
}
instance.order(order);
return instance;
}
public static ByteBuffer getHollowByteBuffer()
{
ByteBuffer instance;
try
{
instance = (ByteBuffer) unsafe.allocateInstance(BYTE_BUFFER_CLASS);
}
catch (InstantiationException e)
{
throw new AssertionError(e);
}
instance.order(ByteOrder.nativeOrder());
return instance;
}
public static void setByteBuffer(ByteBuffer instance, long address, int length)
{
unsafe.putLong(instance, DIRECT_BYTE_BUFFER_ADDRESS_OFFSET, address);
unsafe.putInt(instance, DIRECT_BYTE_BUFFER_CAPACITY_OFFSET, length);
unsafe.putInt(instance, DIRECT_BYTE_BUFFER_LIMIT_OFFSET, length);
}
public static Object getAttachment(ByteBuffer instance)
{
assert instance.getClass() == DIRECT_BYTE_BUFFER_CLASS;
return unsafe.getObject(instance, DIRECT_BYTE_BUFFER_ATTACHMENT_OFFSET);
}
public static void setAttachment(ByteBuffer instance, Object next)
{
assert instance.getClass() == DIRECT_BYTE_BUFFER_CLASS;
unsafe.putObject(instance, DIRECT_BYTE_BUFFER_ATTACHMENT_OFFSET, next);
}
public static ByteBuffer duplicateDirectByteBuffer(ByteBuffer source, ByteBuffer hollowBuffer)
{
assert source.getClass() == DIRECT_BYTE_BUFFER_CLASS || source.getClass() == RO_DIRECT_BYTE_BUFFER_CLASS;
unsafe.putLong(hollowBuffer, DIRECT_BYTE_BUFFER_ADDRESS_OFFSET, unsafe.getLong(source, DIRECT_BYTE_BUFFER_ADDRESS_OFFSET));
unsafe.putInt(hollowBuffer, DIRECT_BYTE_BUFFER_POSITION_OFFSET, unsafe.getInt(source, DIRECT_BYTE_BUFFER_POSITION_OFFSET));
unsafe.putInt(hollowBuffer, DIRECT_BYTE_BUFFER_LIMIT_OFFSET, unsafe.getInt(source, DIRECT_BYTE_BUFFER_LIMIT_OFFSET));
unsafe.putInt(hollowBuffer, DIRECT_BYTE_BUFFER_CAPACITY_OFFSET, unsafe.getInt(source, DIRECT_BYTE_BUFFER_CAPACITY_OFFSET));
return hollowBuffer;
}
public static long getLongByByte(long address)
{
if (BIG_ENDIAN)
{
return (((long) unsafe.getByte(address ) ) << 56) |
(((long) unsafe.getByte(address + 1) & 0xff) << 48) |
(((long) unsafe.getByte(address + 2) & 0xff) << 40) |
(((long) unsafe.getByte(address + 3) & 0xff) << 32) |
(((long) unsafe.getByte(address + 4) & 0xff) << 24) |
(((long) unsafe.getByte(address + 5) & 0xff) << 16) |
(((long) unsafe.getByte(address + 6) & 0xff) << 8) |
(((long) unsafe.getByte(address + 7) & 0xff) );
}
else
{
return (((long) unsafe.getByte(address + 7) ) << 56) |
(((long) unsafe.getByte(address + 6) & 0xff) << 48) |
(((long) unsafe.getByte(address + 5) & 0xff) << 40) |
(((long) unsafe.getByte(address + 4) & 0xff) << 32) |
(((long) unsafe.getByte(address + 3) & 0xff) << 24) |
(((long) unsafe.getByte(address + 2) & 0xff) << 16) |
(((long) unsafe.getByte(address + 1) & 0xff) << 8) |
(((long) unsafe.getByte(address ) & 0xff) );
}
}
public static int getIntByByte(long address)
{
if (BIG_ENDIAN)
{
return (((int) unsafe.getByte(address ) ) << 24) |
(((int) unsafe.getByte(address + 1) & 0xff) << 16) |
(((int) unsafe.getByte(address + 2) & 0xff) << 8 ) |
(((int) unsafe.getByte(address + 3) & 0xff) );
}
else
{
return (((int) unsafe.getByte(address + 3) ) << 24) |
(((int) unsafe.getByte(address + 2) & 0xff) << 16) |
(((int) unsafe.getByte(address + 1) & 0xff) << 8) |
(((int) unsafe.getByte(address ) & 0xff) );
}
}
public static int getShortByByte(long address)
{
if (BIG_ENDIAN)
{
return (((int) unsafe.getByte(address ) ) << 8) |
(((int) unsafe.getByte(address + 1) & 0xff) );
}
else
{
return (((int) unsafe.getByte(address + 1) ) << 8) |
(((int) unsafe.getByte(address ) & 0xff) );
}
}
public static void putLongByByte(long address, long value)
{
if (BIG_ENDIAN)
{
unsafe.putByte(address, (byte) (value >> 56));
unsafe.putByte(address + 1, (byte) (value >> 48));
unsafe.putByte(address + 2, (byte) (value >> 40));
unsafe.putByte(address + 3, (byte) (value >> 32));
unsafe.putByte(address + 4, (byte) (value >> 24));
unsafe.putByte(address + 5, (byte) (value >> 16));
unsafe.putByte(address + 6, (byte) (value >> 8));
unsafe.putByte(address + 7, (byte) (value));
}
else
{
unsafe.putByte(address + 7, (byte) (value >> 56));
unsafe.putByte(address + 6, (byte) (value >> 48));
unsafe.putByte(address + 5, (byte) (value >> 40));
unsafe.putByte(address + 4, (byte) (value >> 32));
unsafe.putByte(address + 3, (byte) (value >> 24));
unsafe.putByte(address + 2, (byte) (value >> 16));
unsafe.putByte(address + 1, (byte) (value >> 8));
unsafe.putByte(address, (byte) (value));
}
}
public static void putIntByByte(long address, int value)
{
if (BIG_ENDIAN)
{
unsafe.putByte(address, (byte) (value >> 24));
unsafe.putByte(address + 1, (byte) (value >> 16));
unsafe.putByte(address + 2, (byte) (value >> 8));
unsafe.putByte(address + 3, (byte) (value));
}
else
{
unsafe.putByte(address + 3, (byte) (value >> 24));
unsafe.putByte(address + 2, (byte) (value >> 16));
unsafe.putByte(address + 1, (byte) (value >> 8));
unsafe.putByte(address, (byte) (value));
}
}
public static void setBytes(long address, ByteBuffer buffer)
{
int start = buffer.position();
int count = buffer.limit() - start;
if (count == 0)
return;
if (buffer.isDirect())
setBytes(((DirectBuffer)buffer).address() + start, address, count);
else
setBytes(address, buffer.array(), buffer.arrayOffset() + start, count);
}
public static void setBytes(long address, byte[] buffer, int bufferOffset, int count)
{
assert buffer != null;
assert !(bufferOffset < 0 || count < 0 || bufferOffset + count > buffer.length);
setBytes(buffer, bufferOffset, address, count);
}
public static void setBytes(long src, long trg, long count)
{
while (count > 0)
{
long size = (count> UNSAFE_COPY_THRESHOLD) ? UNSAFE_COPY_THRESHOLD : count;
unsafe.copyMemory(src, trg, size);
count -= size;
src += size;
trg+= size;
}
}
public static void setBytes(byte[] src, int offset, long trg, long count)
{
while (count > 0)
{
long size = (count> UNSAFE_COPY_THRESHOLD) ? UNSAFE_COPY_THRESHOLD : count;
unsafe.copyMemory(src, BYTE_ARRAY_BASE_OFFSET + offset, null, trg, size);
count -= size;
offset += size;
trg += size;
}
}
public static void getBytes(long address, byte[] buffer, int bufferOffset, int count)
{
if (buffer == null)
throw new NullPointerException();
else if (bufferOffset < 0 || count < 0 || count > buffer.length - bufferOffset)
throw new IndexOutOfBoundsException();
else if (count == 0)
return;
unsafe.copyMemory(null, address, buffer, BYTE_ARRAY_BASE_OFFSET + bufferOffset, count);
}
}