blob: 22ecbf543bc9b5f05687a05f22c485256ed2e1e3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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 sun.misc.Unsafe;
import sun.nio.ch.DirectBuffer;
public abstract class MemoryUtil
{
private static final long UNSAFE_COPY_THRESHOLD = 1024 * 1024L; // copied from java.nio.Bits
private static final Unsafe unsafe;
private static final Class<?> 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);
private static final boolean UNALIGNED;
public static final boolean INVERTED_ORDER;
static
{
String arch = System.getProperty("os.arch");
// Note that s390x architecture are not officially supported and adding it here is only done out of convenience
// for those that want to run C* on this architecture at their own risk (see #11214)
UNALIGNED = arch.equals("i386") || arch.equals("x86")
|| arch.equals("amd64") || arch.equals("x86_64") || arch.equals("s390x");
INVERTED_ORDER = UNALIGNED && !BIG_ENDIAN;
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;
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 setShort(long address, short s)
{
unsafe.putShort(address, s);
}
public static void setInt(long address, int l)
{
if (UNALIGNED)
unsafe.putInt(address, l);
else
putIntByByte(address, l);
}
public static void setLong(long address, long l)
{
if (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 (UNALIGNED ? unsafe.getShort(address) : getShortByByte(address)) & 0xffff;
}
public static int getInt(long address)
{
return UNALIGNED ? unsafe.getInt(address) : getIntByByte(address);
}
public static long getLong(long address)
{
return UNALIGNED ? unsafe.getLong(address) : getLongByByte(address);
}
public static ByteBuffer getByteBuffer(long address, int length)
{
ByteBuffer instance = getHollowDirectByteBuffer();
setByteBuffer(instance, address, length);
return instance;
}
public static ByteBuffer getHollowDirectByteBuffer()
{
ByteBuffer instance;
try
{
instance = (ByteBuffer) unsafe.allocateInstance(DIRECT_BYTE_BUFFER_CLASS);
}
catch (InstantiationException e)
{
throw new AssertionError(e);
}
instance.order(ByteOrder.nativeOrder());
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;
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);
}
/**
* Transfers count bytes from buffer to Memory
*
* @param address start offset in the memory
* @param buffer the data buffer
* @param bufferOffset start offset of the buffer
* @param count number of bytes to transfer
*/
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;
}
}
/**
* Transfers count bytes from Memory starting at memoryOffset to buffer starting at bufferOffset
*
* @param address start offset in the memory
* @param buffer the data buffer
* @param bufferOffset start offset of the buffer
* @param count number of bytes to transfer
*/
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);
}
}