org.lwjgl.system.MemoryUtil Maven / Gradle / Ivy
/*
* Copyright LWJGL. All rights reserved.
* License terms: https://www.lwjgl.org/license
*/
package org.lwjgl.system;
import org.lwjgl.*;
import org.lwjgl.system.MemoryManage.*;
import org.lwjgl.system.MemoryUtil.MemoryAllocationReport.*;
import org.lwjgl.system.jni.*;
import javax.annotation.*;
import java.nio.*;
import java.nio.charset.*;
import java.util.*;
import java.util.function.*;
import static java.lang.Character.*;
import static java.lang.Math.*;
import static org.lwjgl.system.APIUtil.*;
import static org.lwjgl.system.Checks.*;
import static org.lwjgl.system.MathUtil.*;
import static org.lwjgl.system.MemoryUtil.LazyInit.*;
import static org.lwjgl.system.Pointer.*;
import static org.lwjgl.system.jni.JNINativeInterface.*;
import static org.lwjgl.system.libc.LibCString.*;
/**
* This class provides functionality for managing native memory.
*
* All methods in this class will make use of {@link sun.misc.Unsafe} if it's available, for performance. If Unsafe is not available, the fallback
* implementations make use of reflection and, in the worst-case, JNI.
*
* Method names in this class are prefixed with {@code mem} to avoid ambiguities when used with static imports.
*
* Text encoding/decoding
*
* Three codecs are available, each with a different postfix:
*
* - UTF16 - Direct mapping of 2 bytes to Java char and vice versa
* - UTF8 - custom UTF-8 codec without intermediate allocations
* - ASCII - Not the original 7bit ASCII, but any character set with a single byte encoding (ISO 8859-1, Windows-1252, etc.)
*
*
* Methods in bindings that accept/return {@code CharSequence}/{@code String} also support {@code ByteBuffer}, so custom codecs can be used if necessary.
*
* @see Configuration#MEMORY_ALLOCATOR
* @see Configuration#DEBUG_MEMORY_ALLOCATOR
*/
public final class MemoryUtil {
/** Alias for the null pointer address. */
public static final long NULL = 0L;
/** The memory page size, in bytes. This value is always a power-of-two. */
public static final int PAGE_SIZE;
/** The cache-line size, in bytes. This value is always a power-of-two. */
public static final int CACHE_LINE_SIZE;
static final int ARRAY_TLC_SIZE = Configuration.ARRAY_TLC_SIZE.get(8192);
static final ThreadLocal ARRAY_TLC_BYTE = ThreadLocal.withInitial(() -> new byte[ARRAY_TLC_SIZE]);
static final ThreadLocal ARRAY_TLC_CHAR = ThreadLocal.withInitial(() -> new char[ARRAY_TLC_SIZE]);
static final sun.misc.Unsafe UNSAFE;
static final ByteOrder NATIVE_ORDER = ByteOrder.nativeOrder();
private static final Charset UTF16 = NATIVE_ORDER == ByteOrder.LITTLE_ENDIAN
? StandardCharsets.UTF_16LE
: StandardCharsets.UTF_16BE;
static final Class extends ByteBuffer> BUFFER_BYTE;
static final Class extends ShortBuffer> BUFFER_SHORT;
static final Class extends CharBuffer> BUFFER_CHAR;
static final Class extends IntBuffer> BUFFER_INT;
static final Class extends LongBuffer> BUFFER_LONG;
static final Class extends FloatBuffer> BUFFER_FLOAT;
static final Class extends DoubleBuffer> BUFFER_DOUBLE;
private static final long MARK;
private static final long POSITION;
private static final long LIMIT;
private static final long CAPACITY;
private static final long ADDRESS;
private static final long PARENT_BYTE;
private static final long PARENT_SHORT;
private static final long PARENT_CHAR;
private static final long PARENT_INT;
private static final long PARENT_LONG;
private static final long PARENT_FLOAT;
private static final long PARENT_DOUBLE;
static {
Library.initialize();
//ACCESSOR = MemoryAccess.getInstance();
ByteBuffer bb = ByteBuffer.allocateDirect(0).order(NATIVE_ORDER);
BUFFER_BYTE = bb.getClass();
BUFFER_SHORT = bb.asShortBuffer().getClass();
BUFFER_CHAR = bb.asCharBuffer().getClass();
BUFFER_INT = bb.asIntBuffer().getClass();
BUFFER_LONG = bb.asLongBuffer().getClass();
BUFFER_FLOAT = bb.asFloatBuffer().getClass();
BUFFER_DOUBLE = bb.asDoubleBuffer().getClass();
UNSAFE = getUnsafeInstance();
try {
ADDRESS = getAddressOffset();
MARK = getMarkOffset();
POSITION = getPositionOffset();
LIMIT = getLimitOffset();
CAPACITY = getCapacityOffset();
int oopSize = UNSAFE.arrayIndexScale(Object[].class);
PARENT_BYTE = getParentOffset(oopSize, bb, it -> it.duplicate().order(it.order()));
PARENT_SHORT = getParentOffset(oopSize, bb.asShortBuffer(), ShortBuffer::duplicate);
PARENT_CHAR = getParentOffset(oopSize, bb.asCharBuffer(), CharBuffer::duplicate);
PARENT_INT = getParentOffset(oopSize, bb.asIntBuffer(), IntBuffer::duplicate);
PARENT_LONG = getParentOffset(oopSize, bb.asLongBuffer(), LongBuffer::duplicate);
PARENT_FLOAT = getParentOffset(oopSize, bb.asFloatBuffer(), FloatBuffer::duplicate);
PARENT_DOUBLE = getParentOffset(oopSize, bb.asDoubleBuffer(), DoubleBuffer::duplicate);
} catch (Throwable t) {
throw new UnsupportedOperationException(t);
}
PAGE_SIZE = UNSAFE.pageSize();
CACHE_LINE_SIZE = 64; // TODO: Can we do better?
}
static final class LazyInit {
private LazyInit() {
}
static final MemoryAllocator ALLOCATOR_IMPL;
static final MemoryAllocator ALLOCATOR;
static {
ALLOCATOR_IMPL = MemoryManage.getInstance();
ALLOCATOR = Configuration.DEBUG_MEMORY_ALLOCATOR.get(false)
? new DebugAllocator(ALLOCATOR_IMPL)
: ALLOCATOR_IMPL;
apiLog("MemoryUtil allocator: " + ALLOCATOR.getClass().getSimpleName());
}
}
private MemoryUtil() {
}
/* -------------------------------------
-------------------------------------
EXPLICIT MEMORY MANAGEMENT API
-------------------------------------
------------------------------------- */
/** The interface implemented by the memory allocator used by the explicit memory management API ({@link #memAlloc}, {@link #memFree}, etc). */
public interface MemoryAllocator {
/** Returns a pointer to the malloc function. */
long getMalloc();
/** Returns a pointer to the calloc function. */
long getCalloc();
/** Returns a pointer to the realloc function. */
long getRealloc();
/** Returns a pointer to the free function. */
long getFree();
/** Returns a pointer to the aligned_alloc function. */
long getAlignedAlloc();
/** Returns a pointer to the aligned_free function. */
long getAlignedFree();
/** Called by {@link MemoryUtil#memAlloc}. */
long malloc(long size);
/** Called by {@link MemoryUtil#memCalloc}. */
long calloc(long num, long size);
/** Called by {@link MemoryUtil#memRealloc}. */
long realloc(long ptr, long size);
/** Called by {@link MemoryUtil#memFree}. */
void free(long ptr);
/** Called by {@link MemoryUtil#memAlignedAlloc}. */
long aligned_alloc(long alignment, long size);
/** Called by {@link MemoryUtil#memAlignedFree}. */
void aligned_free(long ptr);
}
/**
* Returns the {@link MemoryAllocator} instance used internally by the explicit memory management API ({@link #memAlloc}, {@link #memFree}, etc).
*
* Allocations made through the returned instance will not be tracked for memory leaks, even if {@link Configuration#DEBUG_MEMORY_ALLOCATOR} is enabled.
* This can be useful for {@code static final} allocations that live throughout the application's lifetime and will never be freed until the process is
* terminated. Normally such allocations would be reported as memory leaks by the debug allocator.
*
* The expectation is that this method will rarely be used, so it does not have the {@code mem} prefix to avoid pollution of auto-complete lists.
*
* @return the {@link MemoryAllocator} instance
*/
public static MemoryAllocator getAllocator() {
return getAllocator(false);
}
/**
* Returns the {@link MemoryAllocator} instance used internally by the explicit memory management API ({@link #memAlloc}, {@link #memFree}, etc).
*
* @param tracked whether allocations will be tracked for memory leaks, if {@link Configuration#DEBUG_MEMORY_ALLOCATOR} is enabled.
*
* @return the {@link MemoryAllocator} instance
*/
public static MemoryAllocator getAllocator(boolean tracked) {
return tracked
? ALLOCATOR
: ALLOCATOR_IMPL;
}
// --- [ memAlloc ] ---
/** Unsafe version of {@link #memAlloc}. May return {@link #NULL} if {@code size} is zero or the allocation failed. */
public static long nmemAlloc(long size) {
return ALLOCATOR.malloc(size);
}
/**
* Unsafe version of {@link #memAlloc} that checks the returned pointer.
*
* @return a pointer to the memory block allocated by the function on success. This pointer will never be {@link #NULL}, even if {@code size} is zero.
*
* @throws OutOfMemoryError if the function failed to allocate the requested block of memory
*/
public static long nmemAllocChecked(long size) {
long address = nmemAlloc(size != 0 ? size : 1L);
if (CHECKS && address == NULL) {
throw new OutOfMemoryError();
}
return address;
}
private static long getAllocationSize(int elements, int elementShift) {
return apiCheckAllocation(elements, Integer.toUnsignedLong(elements) << elementShift, BITS64 ? Long.MAX_VALUE : 0xFFFF_FFFFL);
}
/**
* The standard C malloc function.
*
* Allocates a block of {@code size} bytes of memory, returning a pointer to the beginning of the block. The content of the newly allocated block of
* memory is not initialized, remaining with indeterminate values.
*
* Memory allocated with this method must be freed with {@link #memFree}.
*
* @param size the size of the memory block to allocate, in bytes. If {@code size} is zero, the returned pointer shall not be dereferenced.
*
* @return on success, a pointer to the memory block allocated by the function
*
* @throws OutOfMemoryError if the function failed to allocate the requested block of memory
*/
public static ByteBuffer memAlloc(int size) {
return wrap(BUFFER_BYTE, nmemAllocChecked(size), size).order(NATIVE_ORDER);
}
/**
* ShortBuffer version of {@link #memAlloc}.
*
* @param size the number of short values to allocate.
*/
public static ShortBuffer memAllocShort(int size) {
return wrap(BUFFER_SHORT, nmemAllocChecked(getAllocationSize(size, 1)), size);
}
/**
* IntBuffer version of {@link #memAlloc}.
*
* @param size the number of int values to allocate.
*/
public static IntBuffer memAllocInt(int size) {
return wrap(BUFFER_INT, nmemAllocChecked(getAllocationSize(size, 2)), size);
}
/**
* FloatBuffer version of {@link #memAlloc}.
*
* @param size the number of float values to allocate.
*/
public static FloatBuffer memAllocFloat(int size) {
return wrap(BUFFER_FLOAT, nmemAllocChecked(getAllocationSize(size, 2)), size);
}
/**
* LongBuffer version of {@link #memAlloc}.
*
* @param size the number of long values to allocate.
*/
public static LongBuffer memAllocLong(int size) {
return wrap(BUFFER_LONG, nmemAllocChecked(getAllocationSize(size, 3)), size);
}
/**
* DoubleBuffer version of {@link #memAlloc}.
*
* @param size the number of double values to allocate.
*/
public static DoubleBuffer memAllocDouble(int size) {
return wrap(BUFFER_DOUBLE, nmemAllocChecked(getAllocationSize(size, 3)), size);
}
/**
* PointerBuffer version of {@link #memAlloc}.
*
* @param size the number of pointer values to allocate.
*/
public static PointerBuffer memAllocPointer(int size) {
return Pointer.Default.wrap(PointerBuffer.class, nmemAllocChecked(getAllocationSize(size, POINTER_SHIFT)), size);
}
/** Unsafe version of {@link #memFree}. */
public static void nmemFree(long ptr) {
ALLOCATOR.free(ptr);
}
/**
* The standard C free function.
*
* A block of memory previously allocated by a call to {@link #memAlloc}, {@link #memCalloc} or {@link #memRealloc} is deallocated, making it available
* again for further allocations.
*
* @param ptr pointer to a memory block previously allocated with {@link #memAlloc}, {@link #memCalloc} or {@link #memRealloc}. If {@code ptr} does not
* point to a block of memory allocated with the above functions, it causes undefined behavior. If {@code ptr} is a {@link #NULL} pointer, the
* function does nothing.
*/
public static void memFree(@Nullable Buffer ptr) {
if (ptr != null) {
nmemFree(UNSAFE.getLong(ptr, ADDRESS));
}
}
/** PointerBuffer version of {@link #memFree}. */
public static void memFree(@Nullable PointerBuffer ptr) {
if (ptr != null) {
nmemFree(ptr.address);
}
}
// --- [ memCalloc ] ---
/** Unsafe version of {@link #memCalloc}. May return {@link #NULL} if {@code num} or {@code size} are zero or the allocation failed. */
public static long nmemCalloc(long num, long size) {
return ALLOCATOR.calloc(num, size);
}
/**
* Unsafe version of {@link #memCalloc} that checks the returned pointer.
*
* @return a pointer to the memory block allocated by the function on success. This pointer will never be {@link #NULL}, even if {@code num} or
* {@code size} are zero.
*
* @throws OutOfMemoryError if the function failed to allocate the requested block of memory
*/
public static long nmemCallocChecked(long num, long size) {
if (num == 0L || size == 0L) {
num = 1L;
size = 1L;
}
long address = nmemCalloc(num, size);
if (CHECKS && address == NULL) {
throw new OutOfMemoryError();
}
return address;
}
/**
* The standard C calloc function.
*
* Allocates a block of memory for an array of {@code num} elements, each of them {@code size} bytes long, and initializes all its bits to zero. The
* effective result is the allocation of a zero-initialized memory block of {@code (num*size)} bytes.
*
* Memory allocated with this method must be freed with {@link #memFree}.
*
* @param num the number of elements to allocate.
* @param size the size of each element. If {@code size} is zero, the return value depends on the particular library implementation (it may or may not be
* a null pointer), but the returned pointer shall not be dereferenced.
*
* @return on success, a pointer to the memory block allocated by the function
*
* @throws OutOfMemoryError if the function failed to allocate the requested block of memory
*/
public static ByteBuffer memCalloc(int num, int size) {
return wrap(BUFFER_BYTE, nmemCallocChecked(num, size), num * size).order(NATIVE_ORDER);
}
/**
* Alternative version of {@link #memCalloc}.
*
* @param num the number of bytes to allocate.
*/
public static ByteBuffer memCalloc(int num) {
return wrap(BUFFER_BYTE, nmemCallocChecked(num, 1), num).order(NATIVE_ORDER);
}
/**
* ShortBuffer version of {@link #memCalloc}.
*
* @param num the number of short values to allocate.
*/
public static ShortBuffer memCallocShort(int num) {
return wrap(BUFFER_SHORT, nmemCallocChecked(num, 2), num);
}
/**
* IntBuffer version of {@link #memCalloc}.
*
* @param num the number of int values to allocate.
*/
public static IntBuffer memCallocInt(int num) {
return wrap(BUFFER_INT, nmemCallocChecked(num, 4), num);
}
/**
* FloatBuffer version of {@link #memCalloc}.
*
* @param num the number of float values to allocate.
*/
public static FloatBuffer memCallocFloat(int num) {
return wrap(BUFFER_FLOAT, nmemCallocChecked(num, 4), num);
}
/**
* LongBuffer version of {@link #memCalloc}.
*
* @param num the number of long values to allocate.
*/
public static LongBuffer memCallocLong(int num) {
return wrap(BUFFER_LONG, nmemCallocChecked(num, 8), num);
}
/**
* DoubleBuffer version of {@link #memCalloc}.
*
* @param num the number of double values to allocate.
*/
public static DoubleBuffer memCallocDouble(int num) {
return wrap(BUFFER_DOUBLE, nmemCallocChecked(num, 8), num);
}
/**
* PointerBuffer version of {@link #memCalloc}.
*
* @param num the number of pointer values to allocate.
*/
public static PointerBuffer memCallocPointer(int num) {
return Pointer.Default.wrap(PointerBuffer.class, nmemCallocChecked(num, POINTER_SIZE), num);
}
// --- [ memRealloc] ---
/** Unsafe version of {@link #memRealloc}. May return {@link #NULL} if {@code size} is zero or the allocation failed. */
public static long nmemRealloc(long ptr, long size) {
return ALLOCATOR.realloc(ptr, size);
}
/**
* Unsafe version of {@link #memRealloc} that checks the returned pointer.
*
* @return a pointer to the memory block reallocated by the function on success. This pointer will never be {@link #NULL}, even if {@code size} is zero.
*
* @throws OutOfMemoryError if the function failed to allocate the requested block of memory
*/
public static long nmemReallocChecked(long ptr, long size) {
long address = nmemRealloc(ptr, size != 0 ? size : 1L);
if (CHECKS && address == NULL) {
throw new OutOfMemoryError();
}
return address;
}
private static T realloc(@Nullable T old_p, T new_p, int size) {
if (old_p != null) {
new_p.position(min(old_p.position(), size));
}
return new_p;
}
/**
* The standard C realloc function.
*
* Changes the size of the memory block pointed to by {@code ptr}. The function may move the memory block to a new location (whose address is returned
* by the function). The content of the memory block is preserved up to the lesser of the new and old sizes, even if the block is moved to a new location.
* If the new size is larger, the value of the newly allocated portion is indeterminate.
*
* The memory address used is always the address at the start of {@code ptr}, so the current position of {@code ptr} does not need to be set to 0 for
* this function to work. The current position is preserved, even if the memory block is moved to a new location, unless {@code size} is less than the
* current position in which case position will be equal to capacity. The limit is set to the capacity, and the mark is discarded.
*
* @param ptr a pointer to a memory block previously allocated with {@link #memAlloc}, {@link #memCalloc} or {@link #memRealloc}. Alternatively, this can
* be a {@link #NULL} pointer, in which case a new block is allocated (as if {@link #memAlloc} was called).
* @param size the new size for the memory block, in bytes.
*
* @return a pointer to the reallocated memory block, which may be either the same as {@code ptr} or a new location
*
* @throws OutOfMemoryError if the function failed to allocate the requested block of memory. The memory block pointed to by argument {@code ptr} is not
* deallocated (it is still valid, and with its contents unchanged).
*/
public static ByteBuffer memRealloc(@Nullable ByteBuffer ptr, int size) {
return realloc(ptr, memByteBuffer(nmemReallocChecked(ptr == null ? NULL : UNSAFE.getLong(ptr, ADDRESS), size), size), size);
}
/**
* ShortBuffer version of {@link #memRealloc}.
*
* @param size the number of short values to allocate.
*/
public static ShortBuffer memRealloc(@Nullable ShortBuffer ptr, int size) {
return realloc(ptr, memShortBuffer(nmemReallocChecked(ptr == null ? NULL : UNSAFE.getLong(ptr, ADDRESS), getAllocationSize(size, 1)), size), size);
}
/**
* IntBuffer version of {@link #memRealloc}.
*
* @param size the number of int values to allocate.
*/
public static IntBuffer memRealloc(@Nullable IntBuffer ptr, int size) {
return realloc(ptr, memIntBuffer(nmemReallocChecked(ptr == null ? NULL : UNSAFE.getLong(ptr, ADDRESS), getAllocationSize(size, 2)), size), size);
}
/**
* LongBuffer version of {@link #memRealloc}.
*
* @param size the number of long values to allocate.
*/
public static LongBuffer memRealloc(@Nullable LongBuffer ptr, int size) {
return realloc(ptr, memLongBuffer(nmemReallocChecked(ptr == null ? NULL : UNSAFE.getLong(ptr, ADDRESS), getAllocationSize(size, 3)), size), size);
}
/**
* FloatBuffer version of {@link #memRealloc}.
*
* @param size the number of float values to allocate.
*/
public static FloatBuffer memRealloc(@Nullable FloatBuffer ptr, int size) {
return realloc(ptr, memFloatBuffer(nmemReallocChecked(ptr == null ? NULL : UNSAFE.getLong(ptr, ADDRESS), getAllocationSize(size, 2)), size), size);
}
/**
* DoubleBuffer version of {@link #memRealloc}.
*
* @param size the number of double values to allocate.
*/
public static DoubleBuffer memRealloc(@Nullable DoubleBuffer ptr, int size) {
return realloc(ptr, memDoubleBuffer(nmemReallocChecked(ptr == null ? NULL : UNSAFE.getLong(ptr, ADDRESS), getAllocationSize(size, 3)), size), size);
}
/**
* PointerBuffer version of {@link #memRealloc}.
*
* @param size the number of pointer values to allocate.
*/
public static PointerBuffer memRealloc(@Nullable PointerBuffer ptr, int size) {
PointerBuffer buffer = memPointerBuffer(nmemReallocChecked(ptr == null ? NULL : ptr.address, getAllocationSize(size, POINTER_SHIFT)), size);
if (ptr != null) {
buffer.position(min(ptr.position(), size));
}
return buffer;
}
// --- [ memAlignedAlloc ] ---
/** Unsafe version of {@link #memAlignedAlloc}. May return {@link #NULL} if {@code size} is zero or the allocation failed. */
public static long nmemAlignedAlloc(long alignment, long size) {
return ALLOCATOR.aligned_alloc(alignment, size);
}
/**
* Unsafe version of {@link #memAlignedAlloc} that checks the returned pointer.
*
* @return a pointer to the memory block allocated by the function on success. This pointer will never be {@link #NULL}, even if {@code size} is zero.
*
* @throws OutOfMemoryError if the function failed to allocate the requested block of memory
*/
public static long nmemAlignedAllocChecked(long alignment, long size) {
long address = nmemAlignedAlloc(alignment, size != 0 ? size : 1L);
if (CHECKS && address == NULL) {
throw new OutOfMemoryError();
}
return address;
}
/**
* The standard C aligned_alloc function.
*
* Allocate {@code size} bytes of uninitialized storage whose alignment is specified by {@code alignment}. The size parameter must be an integral
* multiple of alignment. Memory allocated with memAlignedAlloc() must be freed with {@link #memAlignedFree}.
*
* @param alignment the alignment. Must be a power of two value and a multiple of {@code sizeof(void *)}.
* @param size the number of bytes to allocate. Must be a multiple of {@code alignment}.
*/
public static ByteBuffer memAlignedAlloc(int alignment, int size) {
return wrap(BUFFER_BYTE, nmemAlignedAllocChecked(alignment, size), size).order(NATIVE_ORDER);
}
// --- [ memAlignedFree ] ---
/** Unsafe version of {@link #memAlignedFree}. */
public static void nmemAlignedFree(long ptr) {
ALLOCATOR.aligned_free(ptr);
}
/**
* Frees a block of memory that was allocated with {@link #memAlignedAlloc}. If ptr is {@code NULL}, no operation is performed.
*
* @param ptr the aligned block of memory to free
*/
public static void memAlignedFree(@Nullable ByteBuffer ptr) {
if (ptr != null) {
nmemAlignedFree(UNSAFE.getLong(ptr, ADDRESS));
}
}
// --- [ DebugAllocator ] ---
/** The memory allocation report callback. */
public interface MemoryAllocationReport {
/**
* Reports allocated memory.
*
* @param address the address of the memory allocated. May be {@link #NULL}.
* @param memory the amount of memory allocated, in bytes
* @param threadId id of the thread that allocated the memory. May be {@link #NULL}.
* @param threadName name of the thread that allocated the memory. May be {@code null}.
* @param stacktrace the allocation stacktrace. May be {@code null}.
*/
void invoke(long address, long memory, long threadId, @Nullable String threadName, @Nullable StackTraceElement... stacktrace);
/** Specifies how to aggregate the reported allocations. */
enum Aggregate {
/** Allocations are aggregated over the whole process or thread. */
ALL,
/**
* Allocations are aggregated based on the first stack trace element. This will return an allocation aggregate per method/line number, regardless
* of how many different code paths lead to that specific method and line number.
*/
GROUP_BY_METHOD,
/** The allocations are aggregated based on the full stack trace chain. */
GROUP_BY_STACKTRACE
}
}
/**
* Reports all live allocations.
*
* This method can only be used if the {@link Configuration#DEBUG_MEMORY_ALLOCATOR} option has been set to true.
*
* @param report the report callback
*/
public static void memReport(MemoryAllocationReport report) {
DebugAllocator.report(report);
}
/**
* Reports aggregates for the live allocations.
*
* This method can only be used if the {@link Configuration#DEBUG_MEMORY_ALLOCATOR} option has been set to true.
*
* @param report the report callback
* @param groupByStackTrace how to aggregate the reported allocations
* @param groupByThread if the reported allocations should be grouped by thread
*/
public static void memReport(MemoryAllocationReport report, Aggregate groupByStackTrace, boolean groupByThread) {
DebugAllocator.report(report, groupByStackTrace, groupByThread);
}
/* -------------------------------------
-------------------------------------
BUFFER MANAGEMENT API
-------------------------------------
------------------------------------- */
// --- [ memAddress0 ] ---
/**
* Returns the memory address of the specified buffer. [INTERNAL USE ONLY]
*
* @param buffer the buffer
*
* @return the memory address
*/
public static long memAddress0(Buffer buffer) { return UNSAFE.getLong(buffer, ADDRESS); }
// --- [ Buffer address ] ---
/**
* Returns the memory address at the current position of the specified buffer. This is effectively a pointer value that can be used in native function
* calls.
*
* @param buffer the buffer
*
* @return the memory address
*/
public static long memAddress(ByteBuffer buffer) { return buffer.position() + memAddress0(buffer); }
/**
* Returns the memory address at the specified position of the specified buffer.
*
* @param buffer the buffer
* @param position the buffer position
*
* @return the memory address
*
* @see #memAddress(ByteBuffer)
*/
public static long memAddress(ByteBuffer buffer, int position) {
Objects.requireNonNull(buffer);
return memAddress0(buffer) + Integer.toUnsignedLong(position);
}
private static long address(int position, int elementShift, long address) {
return address + ((position & 0xFFFF_FFFFL) << elementShift);
}
/** ShortBuffer version of {@link #memAddress(ByteBuffer)}. */
public static long memAddress(ShortBuffer buffer) { return address(buffer.position(), 1, memAddress0(buffer)); }
/** ShortBuffer version of {@link #memAddress(ByteBuffer, int)}. */
public static long memAddress(ShortBuffer buffer, int position) {
Objects.requireNonNull(buffer);
return address(position, 1, memAddress0(buffer));
}
/** CharBuffer version of {@link #memAddress(ByteBuffer)}. */
public static long memAddress(CharBuffer buffer) { return address(buffer.position(), 1, memAddress0(buffer)); }
/** CharBuffer version of {@link #memAddress(ByteBuffer, int)}. */
public static long memAddress(CharBuffer buffer, int position) {
Objects.requireNonNull(buffer);
return address(position, 1, memAddress0(buffer));
}
/** IntBuffer version of {@link #memAddress(ByteBuffer)}. */
public static long memAddress(IntBuffer buffer) { return address(buffer.position(), 2, memAddress0(buffer)); }
/** IntBuffer version of {@link #memAddress(ByteBuffer, int)}. */
public static long memAddress(IntBuffer buffer, int position) {
Objects.requireNonNull(buffer);
return address(position, 2, memAddress0(buffer));
}
/** FloatBuffer version of {@link #memAddress(ByteBuffer)}. */
public static long memAddress(FloatBuffer buffer) { return address(buffer.position(), 2, memAddress0(buffer)); }
/** FloatBuffer version of {@link #memAddress(ByteBuffer, int)}. */
public static long memAddress(FloatBuffer buffer, int position) {
Objects.requireNonNull(buffer);
return address(position, 2, memAddress0(buffer));
}
/** LongBuffer version of {@link #memAddress(ByteBuffer)}. */
public static long memAddress(LongBuffer buffer) { return address(buffer.position(), 3, memAddress0(buffer)); }
/** LongBuffer version of {@link #memAddress(ByteBuffer, int)}. */
public static long memAddress(LongBuffer buffer, int position) {
Objects.requireNonNull(buffer);
return address(position, 3, memAddress0(buffer));
}
/** DoubleBuffer version of {@link #memAddress(ByteBuffer)}. */
public static long memAddress(DoubleBuffer buffer) { return address(buffer.position(), 3, memAddress0(buffer)); }
/** DoubleBuffer version of {@link #memAddress(ByteBuffer, int)}. */
public static long memAddress(DoubleBuffer buffer, int position) {
Objects.requireNonNull(buffer);
return address(position, 3, memAddress0(buffer));
}
/** Polymorphic version of {@link #memAddress(ByteBuffer)}. */
public static long memAddress(Buffer buffer) {
int elementShift;
if (buffer instanceof ByteBuffer) {
elementShift = 0;
} else if (buffer instanceof ShortBuffer || buffer instanceof CharBuffer) {
elementShift = 1;
} else if (buffer instanceof IntBuffer || buffer instanceof FloatBuffer) {
elementShift = 2;
} else {
elementShift = 3;
}
return address(buffer.position(), elementShift, memAddress0(buffer));
}
/** CustomBuffer version of {@link #memAddress(ByteBuffer)}. */
public static long memAddress(CustomBuffer> buffer) { return buffer.address(); }
/** CustomBuffer version of {@link #memAddress(ByteBuffer, int)}. */
public static long memAddress(CustomBuffer> buffer, int position) { return buffer.address(position); }
// --- [ Buffer address - Safe ] ---
/** Null-safe version of {@link #memAddress(ByteBuffer)}. Returns {@link #NULL} if the specified buffer is null. */
public static long memAddressSafe(@Nullable ByteBuffer buffer) { return buffer == null ? NULL : memAddress0(buffer) + buffer.position(); }
/** ShortBuffer version of {@link #memAddressSafe(ByteBuffer)}. */
public static long memAddressSafe(@Nullable ShortBuffer buffer) { return buffer == null ? NULL : address(buffer.position(), 1, memAddress0(buffer)); }
/** CharBuffer version of {@link #memAddressSafe(ByteBuffer)}. */
public static long memAddressSafe(@Nullable CharBuffer buffer) { return buffer == null ? NULL : address(buffer.position(), 1, memAddress0(buffer)); }
/** IntBuffer version of {@link #memAddressSafe(ByteBuffer)}. */
public static long memAddressSafe(@Nullable IntBuffer buffer) { return buffer == null ? NULL : address(buffer.position(), 2, memAddress0(buffer)); }
/** FloatBuffer version of {@link #memAddressSafe(ByteBuffer)}. */
public static long memAddressSafe(@Nullable FloatBuffer buffer) { return buffer == null ? NULL : address(buffer.position(), 2, memAddress0(buffer)); }
/** LongBuffer version of {@link #memAddressSafe(ByteBuffer)}. */
public static long memAddressSafe(@Nullable LongBuffer buffer) { return buffer == null ? NULL : address(buffer.position(), 3, memAddress0(buffer)); }
/** DoubleBuffer version of {@link #memAddressSafe(ByteBuffer)}. */
public static long memAddressSafe(@Nullable DoubleBuffer buffer) { return buffer == null ? NULL : address(buffer.position(), 3, memAddress0(buffer)); }
/** Pointer version of {@link #memAddressSafe(ByteBuffer)}. */
public static long memAddressSafe(@Nullable Pointer pointer) { return pointer == null ? NULL : pointer.address(); }
// --- [ Buffer allocation ] ---
/**
* Creates a new direct ByteBuffer that starts at the specified memory address and has the specified capacity. The returned ByteBuffer instance will be set
* to the native {@link ByteOrder}.
*
* @param address the starting memory address
* @param capacity the buffer capacity
*
* @return the new ByteBuffer
*/
public static ByteBuffer memByteBuffer(long address, int capacity) {
if (CHECKS) {
check(address);
}
return wrap(BUFFER_BYTE, address, capacity).order(NATIVE_ORDER);
}
/** Like {@link #memByteBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */
@Nullable
public static ByteBuffer memByteBufferSafe(long address, int capacity) {
return address == NULL ? null : wrap(BUFFER_BYTE, address, capacity).order(NATIVE_ORDER);
}
/**
* Creates a new direct ShortBuffer that starts at the specified memory address and has the specified capacity.
*
* The {@code address} specified must be aligned to 2 bytes. If not, use {@code memByteBuffer(address, capacity * 2).asShortBuffer()}.
*
* @param address the starting memory address
* @param capacity the buffer capacity
*
* @return the new ShortBuffer
*/
public static ShortBuffer memShortBuffer(long address, int capacity) {
if (CHECKS) {
check(address);
}
return wrap(BUFFER_SHORT, address, capacity);
}
/** Like {@link #memShortBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */
@Nullable
public static ShortBuffer memShortBufferSafe(long address, int capacity) {
return address == NULL ? null : wrap(BUFFER_SHORT, address, capacity);
}
/**
* Creates a new direct CharBuffer that starts at the specified memory address and has the specified capacity.
*
* The {@code address} specified must be aligned to 2 bytes. If not, use {@code memByteBuffer(address, capacity * 2).asCharBuffer()}.
*
* @param address the starting memory address
* @param capacity the buffer capacity
*
* @return the new CharBuffer
*/
public static CharBuffer memCharBuffer(long address, int capacity) {
if (CHECKS) {
check(address);
}
return wrap(BUFFER_CHAR, address, capacity);
}
/** Like {@link #memCharBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */
@Nullable
public static CharBuffer memCharBufferSafe(long address, int capacity) {
return address == NULL ? null : wrap(BUFFER_CHAR, address, capacity);
}
/**
* Creates a new direct IntBuffer that starts at the specified memory address and has the specified capacity.
*
* The {@code address} specified must be aligned to 4 bytes. If not, use {@code memByteBuffer(address, capacity * 4).asIntBuffer()}.
*
* @param address the starting memory address
* @param capacity the buffer capacity
*
* @return the new IntBuffer
*/
public static IntBuffer memIntBuffer(long address, int capacity) {
if (CHECKS) {
check(address);
}
return wrap(BUFFER_INT, address, capacity);
}
/** Like {@link #memIntBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */
@Nullable
public static IntBuffer memIntBufferSafe(long address, int capacity) {
return address == NULL ? null : wrap(BUFFER_INT, address, capacity);
}
/**
* Creates a new direct LongBuffer that starts at the specified memory address and has the specified capacity.
*
* The {@code address} specified must be aligned to 8 bytes. If not, use {@code memByteBuffer(address, capacity * 8).asLongBuffer()}.
*
* @param address the starting memory address
* @param capacity the buffer capacity
*
* @return the new LongBuffer
*/
public static LongBuffer memLongBuffer(long address, int capacity) {
if (CHECKS) {
check(address);
}
return wrap(BUFFER_LONG, address, capacity);
}
/** Like {@link #memLongBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */
@Nullable
public static LongBuffer memLongBufferSafe(long address, int capacity) {
return address == NULL ? null : wrap(BUFFER_LONG, address, capacity);
}
/**
* Creates a new direct FloatBuffer that starts at the specified memory address and has the specified capacity.
*
* The {@code address} specified must be aligned to 4 bytes. If not, use {@code memByteBuffer(address, capacity * 4).asFloatBuffer()}.
*
* @param address the starting memory address
* @param capacity the buffer capacity
*
* @return the new FloatBuffer
*/
public static FloatBuffer memFloatBuffer(long address, int capacity) {
if (CHECKS) {
check(address);
}
return wrap(BUFFER_FLOAT, address, capacity);
}
/** Like {@link #memFloatBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */
@Nullable
public static FloatBuffer memFloatBufferSafe(long address, int capacity) {
return address == NULL ? null : wrap(BUFFER_FLOAT, address, capacity);
}
/**
* Creates a new direct DoubleBuffer that starts at the specified memory address and has the specified capacity.
*
* The {@code address} specified must be aligned to 8 bytes. If not, use {@code memByteBuffer(address, capacity * 8).asDoubleBuffer()}.
*
* @param address the starting memory address
* @param capacity the buffer capacity
*
* @return the new DoubleBuffer
*/
public static DoubleBuffer memDoubleBuffer(long address, int capacity) {
if (CHECKS) {
check(address);
}
return wrap(BUFFER_DOUBLE, address, capacity);
}
/** Like {@link #memDoubleBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */
@Nullable
public static DoubleBuffer memDoubleBufferSafe(long address, int capacity) {
return address == NULL ? null : wrap(BUFFER_DOUBLE, address, capacity);
}
/**
* Creates a new PointerBuffer that starts at the specified memory address and has the specified capacity.
*
* The {@code address} specified must be aligned to the pointer size. If not, use {@code PointerBuffer.create(memByteBuffer(address, capacity *
* POINTER_SIZE))}.
*
* @param address the starting memory address
* @param capacity the buffer capacity
*
* @return the new PointerBuffer
*/
public static PointerBuffer memPointerBuffer(long address, int capacity) {
if (CHECKS) {
check(address);
}
return Pointer.Default.wrap(PointerBuffer.class, address, capacity);
}
/** Like {@link #memPointerBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */
@Nullable
public static PointerBuffer memPointerBufferSafe(long address, int capacity) {
return address == NULL ? null : Pointer.Default.wrap(PointerBuffer.class, address, capacity);
}
// --- [ Buffer duplication ] ---
/**
* Duplicates the specified buffer. The returned buffer will have the same {@link ByteOrder} as the source buffer.
*
* This method should be preferred over {@link ByteBuffer#duplicate} because it has a much shorter call chain. Long call chains may fail to inline due
* to JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).
*
* @param buffer the buffer to duplicate
*
* @return the duplicated buffer
*/
public static ByteBuffer memDuplicate(ByteBuffer buffer) { return duplicate(BUFFER_BYTE, buffer, PARENT_BYTE).order(buffer.order()); }
/**
* Duplicates the specified buffer.
*
* This method should be preferred over {@link ShortBuffer#duplicate} because it has a much shorter call chain. Long call chains may fail to inline due
* to JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).
*
* @param buffer the buffer to duplicate
*
* @return the duplicated buffer
*/
public static ShortBuffer memDuplicate(ShortBuffer buffer) { return duplicate(BUFFER_SHORT, buffer, PARENT_SHORT); }
/**
* Duplicates the specified buffer.
*
* This method should be preferred over {@link CharBuffer#duplicate} because it has a much shorter call chain. Long call chains may fail to inline due
* to JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).
*
* @param buffer the buffer to duplicate
*
* @return the duplicated buffer
*/
public static CharBuffer memDuplicate(CharBuffer buffer) { return duplicate(BUFFER_CHAR, buffer, PARENT_CHAR); }
/**
* Duplicates the specified buffer.
*
* This method should be preferred over {@link IntBuffer#duplicate} because it has a much shorter call chain. Long call chains may fail to inline due
* to JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).
*
* @param buffer the buffer to duplicate
*
* @return the duplicated buffer
*/
public static IntBuffer memDuplicate(IntBuffer buffer) { return duplicate(BUFFER_INT, buffer, PARENT_INT); }
/**
* Duplicates the specified buffer.
*
* This method should be preferred over {@link LongBuffer#duplicate} because it has a much shorter call chain. Long call chains may fail to inline due
* to JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).
*
* @param buffer the buffer to duplicate
*
* @return the duplicated buffer
*/
public static LongBuffer memDuplicate(LongBuffer buffer) { return duplicate(BUFFER_LONG, buffer, PARENT_LONG); }
/**
* Duplicates the specified buffer.
*
* This method should be preferred over {@link FloatBuffer#duplicate} because it has a much shorter call chain. Long call chains may fail to inline due
* to JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).
*
* @param buffer the buffer to duplicate
*
* @return the duplicated buffer
*/
public static FloatBuffer memDuplicate(FloatBuffer buffer) { return duplicate(BUFFER_FLOAT, buffer, PARENT_FLOAT); }
/**
* Duplicates the specified buffer.
*
* This method should be preferred over {@link DoubleBuffer#duplicate} because it has a much shorter call chain. Long call chains may fail to inline due
* to JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).
*
* @param buffer the buffer to duplicate
*
* @return the duplicated buffer
*/
public static DoubleBuffer memDuplicate(DoubleBuffer buffer) { return duplicate(BUFFER_DOUBLE, buffer, PARENT_DOUBLE); }
// --- [ Buffer slicing ] ---
/**
* Slices the specified buffer. The returned buffer will have the same {@link ByteOrder} as the source buffer.
*
* This method should be preferred over {@link ByteBuffer#slice} because it has a much shorter call chain. Long call chains may fail to inline due to
* JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).
*
* @param buffer the buffer to slice
*
* @return the sliced buffer
*/
public static ByteBuffer memSlice(ByteBuffer buffer) {
return slice(BUFFER_BYTE, buffer, memAddress0(buffer) + buffer.position(), buffer.remaining(), PARENT_BYTE).order(buffer.order());
}
/**
* Slices the specified buffer.
*
* This method should be preferred over {@link ShortBuffer#slice} because it has a much shorter call chain. Long call chains may fail to inline due to
* JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).
*
* @param buffer the buffer to slice
*
* @return the sliced buffer
*/
public static ShortBuffer memSlice(ShortBuffer buffer) {
return slice(BUFFER_SHORT, buffer, address(buffer.position(), 1, memAddress0(buffer)), buffer.remaining(), PARENT_SHORT);
}
/**
* Slices the specified buffer.
*
* This method should be preferred over {@link CharBuffer#slice} because it has a much shorter call chain. Long call chains may fail to inline due to
* JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).
*
* @param buffer the buffer to slice
*
* @return the sliced buffer
*/
public static CharBuffer memSlice(CharBuffer buffer) {
return slice(BUFFER_CHAR, buffer, address(buffer.position(), 1, memAddress0(buffer)), buffer.remaining(), PARENT_CHAR);
}
/**
* Slices the specified buffer.
*
* This method should be preferred over {@link IntBuffer#slice} because it has a much shorter call chain. Long call chains may fail to inline due to
* JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).
*
* @param buffer the buffer to slice
*
* @return the sliced buffer
*/
public static IntBuffer memSlice(IntBuffer buffer) {
return slice(BUFFER_INT, buffer, address(buffer.position(), 2, memAddress0(buffer)), buffer.remaining(), PARENT_INT);
}
/**
* Slices the specified buffer.
*
* This method should be preferred over {@link LongBuffer#slice} because it has a much shorter call chain. Long call chains may fail to inline due to
* JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).
*
* @param buffer the buffer to slice
*
* @return the sliced buffer
*/
public static LongBuffer memSlice(LongBuffer buffer) {
return slice(BUFFER_LONG, buffer, address(buffer.position(), 3, memAddress0(buffer)), buffer.remaining(), PARENT_LONG);
}
/**
* Slices the specified buffer.
*
* This method should be preferred over {@link FloatBuffer#slice} because it has a much shorter call chain. Long call chains may fail to inline due to
* JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).
*
* @param buffer the buffer to slice
*
* @return the sliced buffer
*/
public static FloatBuffer memSlice(FloatBuffer buffer) {
return slice(BUFFER_FLOAT, buffer, address(buffer.position(), 2, memAddress0(buffer)), buffer.remaining(), PARENT_FLOAT);
}
/**
* Slices the specified buffer.
*
* This method should be preferred over {@link DoubleBuffer#slice} because it has a much shorter call chain. Long call chains may fail to inline due to
* JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).
*
* @param buffer the buffer to slice
*
* @return the sliced buffer
*/
public static DoubleBuffer memSlice(DoubleBuffer buffer) {
return slice(BUFFER_DOUBLE, buffer, address(buffer.position(), 3, memAddress0(buffer)), buffer.remaining(), PARENT_DOUBLE);
}
/**
* Returns a slice of the specified buffer between {@code (buffer.position() + offset)} and {@code (buffer.position() + offset + capacity)}. The returned
* buffer will have the same {@link ByteOrder} as the original buffer.
*
* The position and limit of the original buffer are preserved after a call to this method.
*
* @param buffer the buffer to slice
* @param offset the slice offset, it must be ≤ {@code buffer.remaining()}
* @param capacity the slice length, it must be ≤ {@code buffer.capacity() - (buffer.position() + offset)}
*
* @return the sliced buffer
*/
public static ByteBuffer memSlice(ByteBuffer buffer, int offset, int capacity) {
int position = buffer.position() + offset;
if (offset < 0 || buffer.limit() < position) {
throw new IllegalArgumentException();
}
if (capacity < 0 || buffer.capacity() - position < capacity) {
throw new IllegalArgumentException();
}
return slice(BUFFER_BYTE, buffer, memAddress0(buffer) + position, capacity, PARENT_BYTE).order(buffer.order());
}
/**
* Returns a slice of the specified buffer between {@code (buffer.position() + offset)} and {@code (buffer.position() + offset + capacity)}.
*
* The position and limit of the original buffer are preserved after a call to this method.
*
* @param buffer the buffer to slice
* @param offset the slice offset, it must be ≤ {@code buffer.remaining()}
* @param capacity the slice length, it must be ≤ {@code buffer.capacity() - (buffer.position() + offset)}
*
* @return the sliced buffer
*/
public static ShortBuffer memSlice(ShortBuffer buffer, int offset, int capacity) {
int position = buffer.position() + offset;
if (offset < 0 || buffer.limit() < position) {
throw new IllegalArgumentException();
}
if (capacity < 0 || buffer.capacity() - position < capacity) {
throw new IllegalArgumentException();
}
return slice(BUFFER_SHORT, buffer, address(position, 1, memAddress0(buffer)), capacity, PARENT_SHORT);
}
/**
* Returns a slice of the specified buffer between {@code (buffer.position() + offset)} and {@code (buffer.position() + offset + capacity)}.
*
* The position and limit of the original buffer are preserved after a call to this method.
*
* @param buffer the buffer to slice
* @param offset the slice offset, it must be ≤ {@code buffer.remaining()}
* @param capacity the slice length, it must be ≤ {@code buffer.capacity() - (buffer.position() + offset)}
*
* @return the sliced buffer
*/
public static CharBuffer memSlice(CharBuffer buffer, int offset, int capacity) {
int position = buffer.position() + offset;
if (offset < 0 || buffer.limit() < position) {
throw new IllegalArgumentException();
}
if (capacity < 0 || buffer.capacity() - position < capacity) {
throw new IllegalArgumentException();
}
return slice(BUFFER_CHAR, buffer, address(position, 1, memAddress0(buffer)), capacity, PARENT_CHAR);
}
/**
* Returns a slice of the specified buffer between {@code (buffer.position() + offset)} and {@code (buffer.position() + offset + capacity)}.
*
* The position and limit of the original buffer are preserved after a call to this method.
*
* @param buffer the buffer to slice
* @param offset the slice offset, it must be ≤ {@code buffer.remaining()}
* @param capacity the slice length, it must be ≤ {@code buffer.capacity() - (buffer.position() + offset)}
*
* @return the sliced buffer
*/
public static IntBuffer memSlice(IntBuffer buffer, int offset, int capacity) {
int position = buffer.position() + offset;
if (offset < 0 || buffer.limit() < position) {
throw new IllegalArgumentException();
}
if (capacity < 0 || buffer.capacity() - position < capacity) {
throw new IllegalArgumentException();
}
return slice(BUFFER_INT, buffer, address(position, 2, memAddress0(buffer)), capacity, PARENT_INT);
}
/**
* Returns a slice of the specified buffer between {@code (buffer.position() + offset)} and {@code (buffer.position() + offset + capacity)}.
*
* The position and limit of the original buffer are preserved after a call to this method.
*
* @param buffer the buffer to slice
* @param offset the slice offset, it must be ≤ {@code buffer.remaining()}
* @param capacity the slice length, it must be ≤ {@code buffer.capacity() - (buffer.position() + offset)}
*
* @return the sliced buffer
*/
public static LongBuffer memSlice(LongBuffer buffer, int offset, int capacity) {
int position = buffer.position() + offset;
if (offset < 0 || buffer.limit() < position) {
throw new IllegalArgumentException();
}
if (capacity < 0 || buffer.capacity() - position < capacity) {
throw new IllegalArgumentException();
}
return slice(BUFFER_LONG, buffer, address(position, 3, memAddress0(buffer)), capacity, PARENT_LONG);
}
/**
* Returns a slice of the specified buffer between {@code (buffer.position() + offset)} and {@code (buffer.position() + offset + capacity)}.
*
* The position and limit of the original buffer are preserved after a call to this method.
*
* @param buffer the buffer to slice
* @param offset the slice offset, it must be ≤ {@code buffer.remaining()}
* @param capacity the slice length, it must be ≤ {@code buffer.capacity() - (buffer.position() + offset)}
*
* @return the sliced buffer
*/
public static FloatBuffer memSlice(FloatBuffer buffer, int offset, int capacity) {
int position = buffer.position() + offset;
if (offset < 0 || buffer.limit() < position) {
throw new IllegalArgumentException();
}
if (capacity < 0 || buffer.capacity() - position < capacity) {
throw new IllegalArgumentException();
}
return slice(BUFFER_FLOAT, buffer, address(position, 2, memAddress0(buffer)), capacity, PARENT_FLOAT);
}
/**
* Returns a slice of the specified buffer between {@code (buffer.position() + offset)} and {@code (buffer.position() + offset + capacity)}.
*
* The position and limit of the original buffer are preserved after a call to this method.
*
* @param buffer the buffer to slice
* @param offset the slice offset, it must be ≤ {@code buffer.remaining()}
* @param capacity the slice length, it must be ≤ {@code buffer.capacity() - (buffer.position() + offset)}
*
* @return the sliced buffer
*/
public static DoubleBuffer memSlice(DoubleBuffer buffer, int offset, int capacity) {
int position = buffer.position() + offset;
if (offset < 0 || buffer.limit() < position) {
throw new IllegalArgumentException();
}
if (capacity < 0 || buffer.capacity() - position < capacity) {
throw new IllegalArgumentException();
}
return slice(BUFFER_DOUBLE, buffer, address(position, 3, memAddress0(buffer)), capacity, PARENT_DOUBLE);
}
/**
* Returns a slice of the specified buffer between {@code (buffer.position() + offset)} and {@code (buffer.position() + offset + capacity)}.
*
* The position and limit of the original buffer are preserved after a call to this method.
*
* @param buffer the buffer to slice
* @param offset the slice offset, it must be ≤ {@code buffer.remaining()}
* @param capacity the slice length, it must be ≤ {@code buffer.capacity() - (buffer.position() + offset)}
*
* @return the sliced buffer
*/
public static > T memSlice(T buffer, int offset, int capacity) { return buffer.slice(offset, capacity); }
// --- [ memset ] ---
/**
* Sets all bytes in a specified block of memory to a fixed value (usually zero).
*
* @param ptr the starting memory address
* @param value the value to set (memSet will convert it to unsigned byte)
*/
public static void memSet(ByteBuffer ptr, int value) { memSet(memAddress(ptr), value, ptr.remaining()); }
/**
* Sets all bytes in a specified block of memory to a fixed value (usually zero).
*
* @param ptr the starting memory address
* @param value the value to set (memSet will convert it to unsigned byte)
*/
public static void memSet(ShortBuffer ptr, int value) { memSet(memAddress(ptr), value, apiGetBytes(ptr.remaining(), 1)); }
/**
* Sets all bytes in a specified block of memory to a fixed value (usually zero).
*
* @param ptr the starting memory address
* @param value the value to set (memSet will convert it to unsigned byte)
*/
public static void memSet(CharBuffer ptr, int value) { memSet(memAddress(ptr), value, apiGetBytes(ptr.remaining(), 1)); }
/**
* Sets all bytes in a specified block of memory to a fixed value (usually zero).
*
* @param ptr the starting memory address
* @param value the value to set (memSet will convert it to unsigned byte)
*/
public static void memSet(IntBuffer ptr, int value) { memSet(memAddress(ptr), value, apiGetBytes(ptr.remaining(), 2)); }
/**
* Sets all bytes in a specified block of memory to a fixed value (usually zero).
*
* @param ptr the starting memory address
* @param value the value to set (memSet will convert it to unsigned byte)
*/
public static void memSet(LongBuffer ptr, int value) { memSet(memAddress(ptr), value, apiGetBytes(ptr.remaining(), 3)); }
/**
* Sets all bytes in a specified block of memory to a fixed value (usually zero).
*
* @param ptr the starting memory address
* @param value the value to set (memSet will convert it to unsigned byte)
*/
public static void memSet(FloatBuffer ptr, int value) { memSet(memAddress(ptr), value, apiGetBytes(ptr.remaining(), 2)); }
/**
* Sets all bytes in a specified block of memory to a fixed value (usually zero).
*
* @param ptr the starting memory address
* @param value the value to set (memSet will convert it to unsigned byte)
*/
public static void memSet(DoubleBuffer ptr, int value) { memSet(memAddress(ptr), value, apiGetBytes(ptr.remaining(), 3)); }
/**
* Sets all bytes in a specified block of memory to a fixed value (usually zero).
*
* @param ptr the starting memory address
* @param value the value to set (memSet will convert it to unsigned byte)
* @param the buffer type
*/
public static > void memSet(T ptr, int value) { memSet(memAddress(ptr), value, Integer.toUnsignedLong(ptr.remaining()) * ptr.sizeof()); }
/**
* Sets all bytes in a specified block of memory to a fixed value (usually zero).
*
* @param ptr the starting memory address
* @param value the value to set (memSet will convert it to unsigned byte)
* @param the struct type
*/
public static void memSet(T ptr, int value) { memSet(ptr.address, value, ptr.sizeof()); }
// --- [ memcpy ] ---
/**
* Sets all bytes in a specified block of memory to a copy of another block.
*
* @param src the source memory address
* @param dst the destination memory address
*/
public static void memCopy(ByteBuffer src, ByteBuffer dst) {
if (CHECKS) {
check(dst, src.remaining());
}
MultiReleaseMemCopy.copy(memAddress(src), memAddress(dst), src.remaining());
}
/**
* Sets all bytes in a specified block of memory to a copy of another block.
*
* @param src the source memory address
* @param dst the destination memory address
*/
public static void memCopy(ShortBuffer src, ShortBuffer dst) {
if (CHECKS) {
check(dst, src.remaining());
}
MultiReleaseMemCopy.copy(memAddress(src), memAddress(dst), apiGetBytes(src.remaining(), 1));
}
/**
* Sets all bytes in a specified block of memory to a copy of another block.
*
* @param src the source memory address
* @param dst the destination memory address
*/
public static void memCopy(CharBuffer src, CharBuffer dst) {
if (CHECKS) {
check((Buffer)dst, src.remaining());
}
MultiReleaseMemCopy.copy(memAddress(src), memAddress(dst), apiGetBytes(src.remaining(), 1));
}
/**
* Sets all bytes in a specified block of memory to a copy of another block.
*
* @param src the source memory address
* @param dst the destination memory address
*/
public static void memCopy(IntBuffer src, IntBuffer dst) {
if (CHECKS) {
check(dst, src.remaining());
}
MultiReleaseMemCopy.copy(memAddress(src), memAddress(dst), apiGetBytes(src.remaining(), 2));
}
/**
* Sets all bytes in a specified block of memory to a copy of another block.
*
* @param src the source memory address
* @param dst the destination memory address
*/
public static void memCopy(LongBuffer src, LongBuffer dst) {
if (CHECKS) {
check(dst, src.remaining());
}
MultiReleaseMemCopy.copy(memAddress(src), memAddress(dst), apiGetBytes(src.remaining(), 3));
}
/**
* Sets all bytes in a specified block of memory to a copy of another block.
*
* @param src the source memory address
* @param dst the destination memory address
*/
public static void memCopy(FloatBuffer src, FloatBuffer dst) {
if (CHECKS) {
check(dst, src.remaining());
}
MultiReleaseMemCopy.copy(memAddress(src), memAddress(dst), apiGetBytes(src.remaining(), 2));
}
/**
* Sets all bytes in a specified block of memory to a copy of another block.
*
* @param src the source memory address
* @param dst the destination memory address
*/
public static void memCopy(DoubleBuffer src, DoubleBuffer dst) {
if (CHECKS) {
check(dst, src.remaining());
}
MultiReleaseMemCopy.copy(memAddress(src), memAddress(dst), apiGetBytes(src.remaining(), 3));
}
/**
* Sets all bytes in a specified block of memory to a copy of another block.
*
* @param src the source memory address
* @param dst the destination memory address
* @param the buffer type
*/
public static > void memCopy(T src, T dst) {
if (CHECKS) {
check(dst, src.remaining());
}
MultiReleaseMemCopy.copy(memAddress(src), memAddress(dst), Integer.toUnsignedLong(src.remaining()) * src.sizeof());
}
/**
* Sets all bytes of a struct to a copy of another struct.
*
* @param src the source struct
* @param dst the destination struct
* @param the struct type
*/
public static void memCopy(T src, T dst) {
MultiReleaseMemCopy.copy(src.address, dst.address, src.sizeof());
}
/* -------------------------------------
-------------------------------------
UNSAFE MEMORY ACCESS API
-------------------------------------
------------------------------------- */
private interface NativeShift {
long left(long value, int bytes);
long right(long value, int bytes);
}
private static final NativeShift SHIFT = NATIVE_ORDER == ByteOrder.BIG_ENDIAN ?
new NativeShift() {
@Override public long left(long value, int bytes) { return value << (bytes << 3); }
@Override public long right(long value, int bytes) { return value >>> (bytes << 3); }
} :
new NativeShift() {
@Override public long left(long value, int bytes) { return value >>> (bytes << 3); }
@Override public long right(long value, int bytes) { return value << (bytes << 3); }
};
private static final long FILL_PATTERN = Long.divideUnsigned(-1L, 255L);
/**
* Sets all bytes in a specified block of memory to a fixed value (usually zero).
*
* @param ptr the starting memory address
* @param value the value to set (memSet will convert it to unsigned byte)
* @param bytes the number of bytes to set
*/
public static void memSet(long ptr, int value, long bytes) {
if (DEBUG && (ptr == NULL || bytes < 0)) {
throw new IllegalArgumentException();
}
/*
- Unsafe.setMemory is very slow.
- A custom Java loop is fastest at small sizes, approximately up to 256 bytes.
- The native memset becomes fastest at bigger sizes, when the JNI overhead becomes negligible.
*/
//UNSAFE.setMemory(dst, bytes, (byte)(value & 0xFF));
if (256L < bytes) {
nmemset(ptr, value, bytes);
return;
}
long fill = (value & 0xFF) * FILL_PATTERN;
int i = 0,
length = (int)bytes & 0xFFFF_FFFF;
if (length != 0) {
int misalignment = (int)ptr & 7;
if (misalignment != 0) {
long aligned = ptr - misalignment;
UNSAFE.putLong(null, aligned, merge(
UNSAFE.getLong(null, aligned),
fill,
SHIFT.right(SHIFT.left(-1L, max(0, 8 - length)), misalignment) // 0x0000FFFFFFFF0000
));
i += 8 - misalignment;
}
}
// Aligned longs for performance
for (; i <= length - 8; i += 8) {
UNSAFE.putLong(null, ptr + i, fill);
}
int tail = length - i;
if (0 < tail) {
// Aligned tail
UNSAFE.putLong(null, ptr + i, merge(
fill,
UNSAFE.getLong(null, ptr + i),
SHIFT.right(-1L, tail) // 0x00000000FFFFFFFF
));
}
}
// Bit from a where mask bit is 0, bit from b where mask bit is 1.
static long merge(long a, long b, long mask) {
return a ^ ((a ^ b) & mask);
}
/**
* Sets all bytes in a specified block of memory to a copy of another block.
*
* @param src the source memory address
* @param dst the destination memory address
* @param bytes the number of bytes to copy
*/
public static void memCopy(long src, long dst, long bytes) {
if (DEBUG && (src == NULL || dst == NULL || bytes < 0)) {
throw new IllegalArgumentException();
}
MultiReleaseMemCopy.copy(src, dst, bytes);
}
static void memCopyAligned(long src, long dst, int bytes) {
int i = 0;
// Aligned longs for performance
for (; i <= bytes - 8; i += 8) {
UNSAFE.putLong(null, dst + i, UNSAFE.getLong(null, src + i));
}
// Aligned tail
if (i < bytes) {
UNSAFE.putLong(null, dst + i, merge(
UNSAFE.getLong(null, src + i),
UNSAFE.getLong(null, dst + i),
SHIFT.right(-1L, bytes - i)
));
}
}
public static boolean memGetBoolean(long ptr) { return UNSAFE.getByte(null, ptr) != 0; }
public static byte memGetByte(long ptr) { return UNSAFE.getByte(null, ptr); }
public static short memGetShort(long ptr) { return UNSAFE.getShort(null, ptr); }
public static int memGetInt(long ptr) { return UNSAFE.getInt(null, ptr); }
public static long memGetLong(long ptr) { return UNSAFE.getLong(null, ptr); }
public static float memGetFloat(long ptr) { return UNSAFE.getFloat(null, ptr); }
public static double memGetDouble(long ptr) { return UNSAFE.getDouble(null, ptr); }
public static long memGetAddress(long ptr) {
if (BITS64) {
return UNSAFE.getLong(null, ptr);
} else {
return ((long)UNSAFE.getInt(null, ptr)) & 0xFFFF_FFFFL;
}
}
public static void memPutByte(long ptr, byte value) { UNSAFE.putByte(null, ptr, value); }
public static void memPutShort(long ptr, short value) { UNSAFE.putShort(null, ptr, value); }
public static void memPutInt(long ptr, int value) { UNSAFE.putInt(null, ptr, value); }
public static void memPutLong(long ptr, long value) { UNSAFE.putLong(null, ptr, value); }
public static void memPutFloat(long ptr, float value) { UNSAFE.putFloat(null, ptr, value); }
public static void memPutDouble(long ptr, double value) { UNSAFE.putDouble(null, ptr, value); }
public static void memPutAddress(long ptr, long value) {
if (BITS64) {
UNSAFE.putLong(null, ptr, value);
} else {
UNSAFE.putInt(null, ptr, (int)value);
}
}
/* -------------------------------------
-------------------------------------
JNI UTILITIES API
-------------------------------------
------------------------------------- */
/**
* Returns the object that the specified global reference points to.
*
* @param globalRef the global reference
* @param the object type
*
* @return the object pointed to by {@code globalRef}
*/
public static native T memGlobalRefToObject(long globalRef);
/** Deprecated, use {@link JNINativeInterface#NewGlobalRef} instead. */
@Deprecated public static long memNewGlobalRef(Object obj) { return NewGlobalRef(obj); }
/** Deprecated, use {@link JNINativeInterface#DeleteGlobalRef} instead. */
@Deprecated public static void memDeleteGlobalRef(long globalRef) { DeleteGlobalRef(globalRef); }
/** Deprecated, use {@link JNINativeInterface#NewWeakGlobalRef} instead. */
@Deprecated public static long memNewWeakGlobalRef(Object obj) { return NewWeakGlobalRef(obj); }
/** Deprecated, use {@link JNINativeInterface#DeleteWeakGlobalRef} instead. */
@Deprecated public static void memDeleteWeakGlobalRef(long globalRef) { DeleteWeakGlobalRef(globalRef);}
/* -------------------------------------
-------------------------------------
TEXT ENCODING API
-------------------------------------
------------------------------------- */
/**
* Returns a ByteBuffer containing the specified text ASCII encoded and null-terminated.
*
* @param text the text to encode
*
* @return the encoded text. The returned buffer must be deallocated manually with {@link #memFree}.
*/
public static ByteBuffer memASCII(CharSequence text) {
return memASCII(text, true);
}
/** Like {@link #memASCII(CharSequence) memASCII}, but returns {@code null} if {@code text} is {@code null}. */
@Nullable
public static ByteBuffer memASCIISafe(@Nullable CharSequence text) {
return text == null ? null : memASCII(text, true);
}
/**
* Returns a ByteBuffer containing the specified text ASCII encoded and optionally null-terminated.
*
* @param text the text to encode
* @param nullTerminated if true, the text will be terminated with a '\0'.
*
* @return the encoded text. The returned buffer must be deallocated manually with {@link #memFree}.
*/
public static ByteBuffer memASCII(CharSequence text, boolean nullTerminated) {
int length = memLengthASCII(text, nullTerminated);
long target = nmemAlloc(length);
encodeASCII(text, nullTerminated, target);
return wrap(BUFFER_BYTE, target, length).order(NATIVE_ORDER);
}
/** Like {@link #memASCII(CharSequence, boolean) memASCII}, but returns {@code null} if {@code text} is {@code null}. */
@Nullable
public static ByteBuffer memASCIISafe(@Nullable CharSequence text, boolean nullTerminated) {
return text == null ? null : memASCII(text, nullTerminated);
}
/**
* Encodes and optionally null-terminates the specified text using ASCII encoding. The encoded text is stored in the specified {@link ByteBuffer}, at the
* current buffer position. The current buffer position is not modified by this operation. The {@code target} buffer is assumed to have enough remaining
* space to store the encoded text.
*
* @param text the text to encode
* @param nullTerminated if true, the text will be terminated with a '\0'.
*
* @return the number of bytes of the encoded string
*/
public static int memASCII(CharSequence text, boolean nullTerminated, ByteBuffer target) {
return encodeASCII(text, nullTerminated, memAddress(target));
}
/**
* Encodes and optionally null-terminates the specified text using ASCII encoding. The encoded text is stored in the specified {@link ByteBuffer} at the
* specified {@code position} offset. The current buffer position is not modified by this operation. The {@code target} buffer is assumed to have enough
* remaining space to store the encoded text.
*
* @param text the text to encode
* @param nullTerminated if true, the text will be terminated with a '\0'.
* @param offset the buffer position to which the string will be encoded
*
* @return the number of bytes of the encoded string
*/
public static int memASCII(CharSequence text, boolean nullTerminated, ByteBuffer target, int offset) {
return encodeASCII(text, nullTerminated, memAddress(target, offset));
}
static int encodeASCII(CharSequence text, boolean nullTerminated, long target) {
int len = text.length();
for (int p = 0; p < len; p++) {
UNSAFE.putByte(target + p, (byte)text.charAt(p));
}
if (nullTerminated) {
UNSAFE.putByte(target + len++, (byte)0);
}
return len;
}
/**
* Returns the number of bytes required to encode the specified text in the ASCII encoding.
*
* @param value the text to encode
* @param nullTerminated if true, add the number of bytes required for null-termination
*
* @return the number of bytes
*/
public static int memLengthASCII(CharSequence value, boolean nullTerminated) {
return value.length() + (nullTerminated ? 1 : 0);
}
/**
* Returns a ByteBuffer containing the specified text UTF-8 encoded and null-terminated.
*
* @param text the text to encode
*
* @return the encoded text. The returned buffer must be deallocated manually with {@link #memFree}.
*/
public static ByteBuffer memUTF8(CharSequence text) {
return memUTF8(text, true);
}
/** Like {@link #memUTF8(CharSequence) memASCII}, but returns {@code null} if {@code text} is {@code null}. */
@Nullable
public static ByteBuffer memUTF8Safe(@Nullable CharSequence text) {
return text == null ? null : memUTF8(text, true);
}
/**
* Returns a ByteBuffer containing the specified text UTF-8 encoded and optionally null-terminated.
*
* @param text the text to encode
* @param nullTerminated if true, the text will be terminated with a '\0'.
*
* @return the encoded text. The returned buffer must be deallocated manually with {@link #memFree}.
*/
public static ByteBuffer memUTF8(CharSequence text, boolean nullTerminated) {
int length = memLengthUTF8(text, nullTerminated);
long target = nmemAlloc(length);
encodeUTF8(text, nullTerminated, target);
return wrap(BUFFER_BYTE, target, length).order(NATIVE_ORDER);
}
/** Like {@link #memUTF8(CharSequence, boolean) memASCII}, but returns {@code null} if {@code text} is {@code null}. */
@Nullable
public static ByteBuffer memUTF8Safe(@Nullable CharSequence text, boolean nullTerminated) {
return text == null ? null : memUTF8(text, nullTerminated);
}
/**
* Encodes and optionally null-terminates the specified text using UTF-8 encoding. The encoded text is stored in the specified {@link ByteBuffer}, at the
* current buffer position. The current buffer position is not modified by this operation. The {@code target} buffer is assumed to have enough remaining
* space to store the encoded text. The specified text is assumed to be a valid UTF-16 string.
*
* @param text the text to encode
* @param nullTerminated if true, the text will be terminated with a '\0'.
* @param target the buffer in which to store the encoded text
*
* @return the number of bytes of the encoded string
*/
public static int memUTF8(CharSequence text, boolean nullTerminated, ByteBuffer target) {
return encodeUTF8(text, nullTerminated, memAddress(target));
}
/**
* Encodes and optionally null-terminates the specified text using UTF-8 encoding. The encoded text is stored in the specified {@link ByteBuffer}, at the
* specified {@code position} offset. The current buffer position is not modified by this operation. The {@code target} buffer is assumed to have enough
* remaining space to store the encoded text. The specified text is assumed to be a valid UTF-16 string.
*
* @param text the text to encode
* @param nullTerminated if true, the text will be terminated with a '\0'.
* @param target the buffer in which to store the encoded text
* @param offset the buffer position to which the string will be encoded
*
* @return the number of bytes of the encoded string
*/
public static int memUTF8(CharSequence text, boolean nullTerminated, ByteBuffer target, int offset) {
return encodeUTF8(text, nullTerminated, memAddress(target, offset));
}
static int encodeUTF8(CharSequence text, boolean nullTerminated, long target) {
int i = 0, len = text.length(), p = 0;
char c;
// ASCII fast path
while (i < len && (c = text.charAt(i)) < 0x80) {
UNSAFE.putByte(target + p++, (byte)c);
i++;
}
// Slow path
while (i < len) {
c = text.charAt(i++);
if (c < 0x80) {
UNSAFE.putByte(target + p++, (byte)c);
} else {
int cp = c;
if (c < 0x800) {
UNSAFE.putByte(target + p++, (byte)(0xC0 | cp >> 6));
} else {
if (!isHighSurrogate(c)) {
UNSAFE.putByte(target + p++, (byte)(0xE0 | cp >> 12));
} else {
cp = toCodePoint(c, text.charAt(i++));
UNSAFE.putByte(target + p++, (byte)(0xF0 | cp >> 18));
UNSAFE.putByte(target + p++, (byte)(0x80 | cp >> 12 & 0x3F));
}
UNSAFE.putByte(target + p++, (byte)(0x80 | cp >> 6 & 0x3F));
}
UNSAFE.putByte(target + p++, (byte)(0x80 | cp & 0x3F));
}
}
if (nullTerminated) {
UNSAFE.putByte(target + p++, (byte)0); // TODO: did we have a bug here?
}
return p;
}
/**
* Returns the number of bytes required to encode the specified text in the UTF-8 encoding.
*
* @param value the text to encode
* @param nullTerminated if true, add the number of bytes required for null-termination
*
* @return the number of bytes
*/
public static int memLengthUTF8(CharSequence value, boolean nullTerminated) {
int i, len = value.length(), bytes = len; // start with 1:1
// ASCII fast path
for (i = 0; i < len; i++) {
if (0x80 <= value.charAt(i)) {
break;
}
}
// 1 or 2 bytes fast path
for (; i < len; i++) {
char c = value.charAt(i);
// fallback to slow path
if (0x800 <= c) {
bytes += encodeUTF8LengthSlow(value, i, len);
break;
}
// c <= 127: 0
// c >= 128: 1
bytes += (0x7F - c) >>> 31;
}
return bytes + (nullTerminated ? 1 : 0);
}
private static int encodeUTF8LengthSlow(CharSequence value, int offset, int len) {
int bytes = 0;
for (int i = offset; i < len; i++) {
char c = value.charAt(i);
if (c < 0x800) {
bytes += (0x7F - c) >>> 31;
} else if (c < MIN_SURROGATE || MAX_SURROGATE < c) {
bytes += 2;
} else {
bytes += 2; // the byte count already includes 2 bytes for the surrogate pair, add 2 more
i++;
}
}
return bytes;
}
/**
* Returns a ByteBuffer containing the specified text UTF-16 encoded and null-terminated.
*
* @param text the text to encode
*
* @return the encoded text. The returned buffer must be deallocated manually with {@link #memFree}.
*/
public static ByteBuffer memUTF16(CharSequence text) {
return memUTF16(text, true);
}
/** Like {@link #memUTF16(CharSequence) memASCII}, but returns {@code null} if {@code text} is {@code null}. */
@Nullable
public static ByteBuffer memUTF16Safe(@Nullable CharSequence text) {
return text == null ? null : memUTF16(text, true);
}
/**
* Returns a ByteBuffer containing the specified text UTF-16 encoded and optionally null-terminated.
*
* @param text the text to encode
* @param nullTerminated if true, the text will be terminated with a '\0'.
*
* @return the encoded text. The returned buffer must be deallocated manually with {@link #memFree}.
*/
public static ByteBuffer memUTF16(CharSequence text, boolean nullTerminated) {
int length = memLengthUTF16(text, nullTerminated);
long target = nmemAlloc(length);
encodeUTF16(text, nullTerminated, target);
return wrap(BUFFER_BYTE, target, length).order(NATIVE_ORDER);
}
/** Like {@link #memUTF16(CharSequence, boolean) memASCII}, but returns {@code null} if {@code text} is {@code null}. */
@Nullable
public static ByteBuffer memUTF16Safe(@Nullable CharSequence text, boolean nullTerminated) {
return text == null ? null : memUTF16(text, nullTerminated);
}
/**
* Encodes and optionally null-terminates the specified text using UTF-16 encoding. The encoded text is stored in the specified {@link ByteBuffer}, at the
* current buffer position. The current buffer position is not modified by this operation. The {@code target} buffer is assumed to have enough remaining
* space to store the encoded text.
*
* @param text the text to encode
* @param nullTerminated if true, the text will be terminated with a '\0'.
* @param target the buffer in which to store the encoded text
*
* @return the number of bytes of the encoded string
*/
public static int memUTF16(CharSequence text, boolean nullTerminated, ByteBuffer target) {
return encodeUTF16(text, nullTerminated, memAddress(target));
}
/**
* Encodes and optionally null-terminates the specified text using UTF-16 encoding. The encoded text is stored in the specified {@link ByteBuffer} at the
* specified {@code position} offset. The current buffer position is not modified by this operation. The {@code target} buffer is assumed to have enough
* remaining space to store the encoded text.
*
* @param text the text to encode
* @param nullTerminated if true, the text will be terminated with a '\0'.
* @param target the buffer in which to store the encoded text
* @param offset the buffer position to which the string will be encoded
*
* @return the number of bytes of the encoded string
*/
public static int memUTF16(CharSequence text, boolean nullTerminated, ByteBuffer target, int offset) {
return encodeUTF16(text, nullTerminated, memAddress(target, offset));
}
static int encodeUTF16(CharSequence text, boolean nullTerminated, long target) {
int len = text.length();
for (int i = 0; i < len; i++) {
UNSAFE.putShort(target + 2 * i, (short)text.charAt(i));
}
if (nullTerminated) {
UNSAFE.putShort(target + 2 * len++, (short)0);
}
return 2 * len;
}
/**
* Returns the number of bytes required to encode the specified text in the UTF-16 encoding.
*
* @param value the text to encode
* @param nullTerminated if true, add the number of bytes required for null-termination
*
* @return the number of bytes
*/
public static int memLengthUTF16(CharSequence value, boolean nullTerminated) {
return (value.length() + (nullTerminated ? 1 : 0)) << 1;
}
/* -------------------------------------
-------------------------------------
TEXT DECODING API
-------------------------------------
------------------------------------- */
private static int memLengthNT1(long address, int maxLength) {
if (CHECKS) {
check(address);
}
return BITS64
? strlen64NT1(address, maxLength)
: strlen32NT1(address, maxLength);
}
private static int strlen64NT1(long address, int maxLength) {
int i = 0;
if (8 <= maxLength) {
int misalignment = (int)address & 7;
if (misalignment != 0) {
// Align to 8 bytes
for (int len = 8 - misalignment; i < len; i++) {
if (UNSAFE.getByte(null, address + i) == 0) {
return i;
}
}
}
// Aligned longs for performance
while (i <= maxLength - 8) {
if (mathHasZeroByte(UNSAFE.getLong(null, address + i))) {
break;
}
i += 8;
}
}
// Tail
for (; i < maxLength; i++) {
if (UNSAFE.getByte(null, address + i) == 0) {
break;
}
}
return i;
}
private static int strlen32NT1(long address, int maxLength) {
int i = 0;
if (4 <= maxLength) {
int misalignment = (int)address & 3;
if (misalignment != 0) {
// Align to 4 bytes
for (int len = 4 - misalignment; i < len; i++) {
if (UNSAFE.getByte(null, address + i) == 0) {
return i;
}
}
}
// Aligned ints for performance
while (i <= maxLength - 4) {
if (mathHasZeroByte(UNSAFE.getInt(null, address + i))) {
break;
}
i += 4;
}
}
// Tail
for (; i < maxLength; i++) {
if (UNSAFE.getByte(null, address + i) == 0) {
break;
}
}
return i;
}
/**
* Calculates the length, in bytes, of the null-terminated string that starts at the current position of the specified buffer. A single \0 character will
* terminate the string. The returned length will NOT include the \0 byte.
*
* This method is useful for reading ASCII and UTF8 encoded text.
*
* @param buffer the buffer containing the null-terminated string
*
* @return the string length, in bytes
*/
public static int memLengthNT1(ByteBuffer buffer) {
return memLengthNT1(memAddress(buffer), buffer.remaining());
}
private static int memLengthNT2(long address, int maxLength) {
if (CHECKS) {
check(address);
}
return BITS64
? strlen64NT2(address, maxLength)
: strlen32NT2(address, maxLength);
}
private static int strlen64NT2(long address, int maxLength) {
int i = 0;
if (8 <= maxLength) {
int misalignment = (int)address & 7;
if (misalignment != 0) {
// Align to 8 bytes
for (int len = 8 - misalignment; i < len; i += 2) {
if (UNSAFE.getShort(null, address + i) == 0) {
return i;
}
}
}
// Aligned longs for performance
while (i <= maxLength - 8) {
if (mathHasZeroShort(UNSAFE.getLong(null, address + i))) {
break;
}
i += 8;
}
}
// Tail
for (; i < maxLength; i += 2) {
if (UNSAFE.getShort(null, address + i) == 0) {
break;
}
}
return i;
}
private static int strlen32NT2(long address, int maxLength) {
int i = 0;
if (4 <= maxLength) {
int misalignment = (int)address & 3;
if (misalignment != 0) {
// Align to 4 bytes
for (int len = 4 - misalignment; i < len; i += 2) {
if (UNSAFE.getShort(null, address + i) == 0) {
return i;
}
}
}
// Aligned longs for performance
while (i <= maxLength - 4) {
if (mathHasZeroShort(UNSAFE.getInt(null, address + i))) {
break;
}
i += 4;
}
}
// Tail
for (; i < maxLength; i += 2) {
if (UNSAFE.getShort(null, address + i) == 0) {
break;
}
}
return i;
}
/**
* Calculates the length, in bytes, of the null-terminated string that starts at the current position of the specified buffer. Two \0 characters will
* terminate the string. The returned buffer will NOT include the \0 bytes.
*
* This method is useful for reading UTF16 encoded text.
*
* @param buffer the buffer containing the null-terminated string
*
* @return the string length, in bytes
*/
public static int memLengthNT2(ByteBuffer buffer) {
return memLengthNT2(memAddress(buffer), buffer.remaining());
}
/**
* Creates a new direct ByteBuffer that starts at the specified memory address and has capacity equal to the null-terminated string starting at that
* address. A single \0 character will terminate the string. The returned buffer will NOT include the \0 byte.
*
* This method is useful for reading ASCII and UTF8 encoded text.
*
* @param address the starting memory address
*
* @return the new ByteBuffer
*/
public static ByteBuffer memByteBufferNT1(long address) {
return memByteBuffer(address, memLengthNT1(address, Integer.MAX_VALUE));
}
/**
* Creates a new direct ByteBuffer that starts at the specified memory address and has capacity equal to the null-terminated string starting at that
* address, up to a maximum of {@code maxLength} bytes. A single \0 character will terminate the string. The returned buffer will NOT include the \0 byte.
*
* This method is useful for reading ASCII and UTF8 encoded text.
*
* @param address the starting memory address
* @param maxLength the maximum string length, in bytes
*
* @return the new ByteBuffer
*/
public static ByteBuffer memByteBufferNT1(long address, int maxLength) {
return memByteBuffer(address, memLengthNT1(address, maxLength));
}
/** Like {@link #memByteBufferNT1(long) memByteBufferNT1}, but returns {@code null} if {@code address} is {@link #NULL}. */
@Nullable
public static ByteBuffer memByteBufferNT1Safe(long address) {
return address == NULL ? null : memByteBuffer(address, memLengthNT1(address, Integer.MAX_VALUE));
}
/** Like {@link #memByteBufferNT1(long, int) memByteBufferNT1}, but returns {@code null} if {@code address} is {@link #NULL}. */
@Nullable
public static ByteBuffer memByteBufferNT1Safe(long address, int maxLength) {
return address == NULL ? null : memByteBuffer(address, memLengthNT1(address, maxLength));
}
/**
* Creates a new direct ByteBuffer that starts at the specified memory address and has capacity equal to the null-terminated string starting at that
* address. Two \0 characters will terminate the string. The returned buffer will NOT include the \0 bytes.
*
* This method is useful for reading UTF16 encoded text.
*
* @param address the starting memory address
*
* @return the new ByteBuffer
*/
public static ByteBuffer memByteBufferNT2(long address) {
return memByteBufferNT2(address, Integer.MAX_VALUE - 1);
}
/**
* Creates a new direct ByteBuffer that starts at the specified memory address and has capacity equal to the null-terminated string starting at that
* address, up to a maximum of {@code maxLength} bytes. Two \0 characters will terminate the string. The returned buffer will NOT include the \0 bytes.
*
* This method is useful for reading UTF16 encoded text.
*
* @param address the starting memory address
*
* @return the new ByteBuffer
*/
public static ByteBuffer memByteBufferNT2(long address, int maxLength) {
if (DEBUG) {
if ((maxLength & 1) != 0) {
throw new IllegalArgumentException("The maximum length must be an even number.");
}
}
return memByteBuffer(address, memLengthNT2(address, maxLength));
}
/** Like {@link #memByteBufferNT2(long) memByteBufferNT2}, but returns {@code null} if {@code address} is {@link #NULL}. */
@Nullable
public static ByteBuffer memByteBufferNT2Safe(long address) {
return address == NULL ? null : memByteBufferNT2(address, Integer.MAX_VALUE - 1);
}
/** Like {@link #memByteBufferNT2(long, int) memByteBufferNT2}, but returns {@code null} if {@code address} is {@link #NULL}. */
@Nullable
public static ByteBuffer memByteBufferNT2Safe(long address, int maxLength) {
return address == NULL ? null : memByteBufferNT2(address, maxLength);
}
/**
* Converts the null-terminated ASCII encoded string at the specified memory address to a {@link String}.
*
* @param address the string memory address
*
* @return the decoded {@link String}
*/
public static String memASCII(long address) {
return memASCII(address, memLengthNT1(address, Integer.MAX_VALUE));
}
/**
* Converts the ASCII encoded string at the specified memory address to a {@link String}.
*
* @param address the string memory address
* @param length the number of bytes to decode
*
* @return the decoded {@link String}
*/
@SuppressWarnings("deprecation")
private static String memASCII(long address, int length) {
if (length <= 0) {
return "";
}
byte[] ascii = length <= ARRAY_TLC_SIZE ? ARRAY_TLC_BYTE.get() : new byte[length];
memByteBuffer(address, length).get(ascii, 0, length);
return new String(ascii, 0, 0, length);
}
/**
* Decodes the bytes with index {@code [position(), position()+remaining()}) in {@code buffer}, as an ASCII string.
*
* The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.
*
* @param buffer the {@link ByteBuffer} to decode
*
* @return the decoded {@link String}
*/
public static String memASCII(ByteBuffer buffer) {
return memASCII(memAddress(buffer), buffer.remaining());
}
/** Like {@link #memASCII(long) memASCII}, but returns {@code null} if {@code address} is {@link #NULL}. */
@Nullable
public static String memASCIISafe(long address) {
return address == NULL ? null : memASCII(address, memLengthNT1(address, Integer.MAX_VALUE));
}
/** Like {@link #memASCII(long, int) memASCII}, but returns {@code null} if {@code address} is {@link #NULL}. */
@Nullable
public static String memASCIISafe(long address, int length) {
return address == NULL ? null : memASCII(address, length);
}
/** Like {@link #memASCII(ByteBuffer) memASCII}, but returns {@code null} if {@code buffer} is {@code null}. */
@Nullable
public static String memASCIISafe(@Nullable ByteBuffer buffer) {
return buffer == null ? null : memASCII(memAddress(buffer), buffer.remaining());
}
/**
* Decodes the bytes with index {@code [position(), position()+length}) in {@code buffer}, as an ASCII string.
*
* The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.
*
* @param buffer the {@link ByteBuffer} to decode
* @param length the number of bytes to decode
*
* @return the decoded {@link String}
*/
public static String memASCII(ByteBuffer buffer, int length) {
return memASCII(memAddress(buffer), length);
}
/**
* Decodes the bytes with index {@code [offset, offset+length}) in {@code buffer}, as an ASCII string.
*
* The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.
*
* @param buffer the {@link ByteBuffer} to decode
* @param length the number of bytes to decode
* @param offset the offset at which to start decoding.
*
* @return the decoded {@link String}
*/
public static String memASCII(ByteBuffer buffer, int length, int offset) {
return memASCII(memAddress(buffer, offset), length);
}
/**
* Converts the null-terminated UTF-8 encoded string at the specified memory address to a {@link String}.
*
* @param address the string memory address
*
* @return the decoded {@link String}
*/
public static String memUTF8(long address) {
return MultiReleaseTextDecoding.decodeUTF8(address, memLengthNT1(address, Integer.MAX_VALUE));
}
/**
* Converts the UTF-8 encoded string at the specified memory address to a {@link String}.
*
* @param address the string memory address
* @param length the number of bytes to decode
*
* @return the decoded {@link String}
*/
public static String memUTF8(long address, int length) {
return MultiReleaseTextDecoding.decodeUTF8(address, length);
}
/**
* Decodes the bytes with index {@code [position(), position()+remaining()}) in {@code buffer}, as a UTF-8 string.
*
* The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.
*
* @param buffer the {@link ByteBuffer} to decode
*
* @return the decoded {@link String}
*/
public static String memUTF8(ByteBuffer buffer) {
return MultiReleaseTextDecoding.decodeUTF8(memAddress(buffer), buffer.remaining());
}
/** Like {@link #memUTF8(long) memUTF8}, but returns {@code null} if {@code address} is {@link #NULL}. */
@Nullable
public static String memUTF8Safe(long address) {
return address == NULL ? null : MultiReleaseTextDecoding.decodeUTF8(address, memLengthNT1(address, Integer.MAX_VALUE));
}
/** Like {@link #memUTF8(long, int) memUTF8}, but returns {@code null} if {@code address} is {@link #NULL}. */
@Nullable
public static String memUTF8Safe(long address, int length) {
return address == NULL ? null : MultiReleaseTextDecoding.decodeUTF8(address, length);
}
/** Like {@link #memUTF8(ByteBuffer) memUTF8}, but returns {@code null} if {@code buffer} is {@code null}. */
@Nullable
public static String memUTF8Safe(@Nullable ByteBuffer buffer) {
return buffer == null ? null : MultiReleaseTextDecoding.decodeUTF8(memAddress(buffer), buffer.remaining());
}
/**
* Decodes the bytes with index {@code [position(), position()+length}) in {@code buffer}, as a UTF-8 string.
*
* The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.
*
* @param buffer the {@link ByteBuffer} to decode
* @param length the number of bytes to decode
*
* @return the decoded {@link String}
*/
public static String memUTF8(ByteBuffer buffer, int length) {
return MultiReleaseTextDecoding.decodeUTF8(memAddress(buffer), length);
}
/**
* Decodes the bytes with index {@code [offset, offset+length}) in {@code buffer}, as a UTF-8 string.
*
* The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.
*
* @param buffer the {@link ByteBuffer} to decode
* @param length the number of bytes to decode
* @param offset the offset at which to start decoding.
*
* @return the decoded {@link String}
*/
public static String memUTF8(ByteBuffer buffer, int length, int offset) {
return MultiReleaseTextDecoding.decodeUTF8(memAddress(buffer, offset), length);
}
/**
* Converts the null-terminated UTF-16 encoded string at the specified memory address to a {@link String}.
*
* @param address the string memory address
*
* @return the decoded {@link String}
*/
public static String memUTF16(long address) {
return memUTF16(address, memLengthNT2(address, Integer.MAX_VALUE - 1) >> 1);
}
/**
* Converts the UTF-16 encoded string at the specified memory address to a {@link String}.
*
* @param address the string memory address
* @param length the number of characters to decode
*
* @return the decoded {@link String}
*/
public static String memUTF16(long address, int length) {
if (length <= 0) {
return "";
}
if (DEBUG) {
// The implementation below does no codepoint validation.
int len = length << 1;
byte[] bytes = len <= ARRAY_TLC_SIZE ? ARRAY_TLC_BYTE.get() : new byte[len];
memByteBuffer(address, len).get(bytes, 0, len);
return new String(bytes, 0, len, UTF16);
}
char[] chars = length <= ARRAY_TLC_SIZE ? ARRAY_TLC_CHAR.get() : new char[length];
memCharBuffer(address, length).get(chars, 0, length);
return new String(chars, 0, length);
}
/**
* Decodes the bytes with index {@code [position(), position()+remaining()}) in {@code buffer}, as a UTF-16 string.
*
* The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.
*
* @param buffer the {@link ByteBuffer} to decode
*
* @return the decoded {@link String}
*/
public static String memUTF16(ByteBuffer buffer) {
return memUTF16(memAddress(buffer), buffer.remaining() >> 1);
}
/** Like {@link #memUTF16(long) memUTF16}, but returns {@code null} if {@code address} is {@link #NULL}. */
@Nullable
public static String memUTF16Safe(long address) {
return address == NULL ? null : memUTF16(address, memLengthNT2(address, Integer.MAX_VALUE - 1) >> 1);
}
/** Like {@link #memUTF16(long, int) memUTF16}, but returns {@code null} if {@code address} is {@link #NULL}. */
@Nullable
public static String memUTF16Safe(long address, int length) {
return address == NULL ? null : memUTF16(address, length);
}
/** Like {@link #memUTF16(ByteBuffer) memUTF16}, but returns {@code null} if {@code buffer} is {@code null}. */
@Nullable
public static String memUTF16Safe(@Nullable ByteBuffer buffer) {
return buffer == null ? null : memUTF16(memAddress(buffer), buffer.remaining() >> 1);
}
/**
* Decodes the bytes with index {@code [position(), position()+(length*2)}) in {@code buffer}, as a UTF-16 string.
*
* The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.
*
* @param buffer the {@link ByteBuffer} to decode
* @param length the number of characters to decode
*
* @return the decoded {@link String}
*/
public static String memUTF16(ByteBuffer buffer, int length) {
return memUTF16(memAddress(buffer), length);
}
/**
* Decodes the bytes with index {@code [offset, offset+(length*2)}) in {@code buffer}, as a UTF-16 string.
*
* The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.
*
* @param buffer the {@link ByteBuffer} to decode
* @param length the number of characters to decode
* @param offset the offset at which to start decoding, in bytes.
*
* @return the decoded {@link String}
*/
public static String memUTF16(ByteBuffer buffer, int length, int offset) {
return memUTF16(memAddress(buffer, offset), length);
}
// -------------------------------------------------
// -------------------------------------------------
// -------------------------------------------------
private static sun.misc.Unsafe getUnsafeInstance() {
java.lang.reflect.Field[] fields = sun.misc.Unsafe.class.getDeclaredFields();
/*
Different runtimes use different names for the Unsafe singleton,
so we cannot use .getDeclaredField and we scan instead. For example:
Oracle: theUnsafe
PERC : m_unsafe_instance
Android: THE_ONE
*/
for (java.lang.reflect.Field field : fields) {
if (!field.getType().equals(sun.misc.Unsafe.class)) {
continue;
}
int modifiers = field.getModifiers();
if (!(java.lang.reflect.Modifier.isStatic(modifiers) && java.lang.reflect.Modifier.isFinal(modifiers))) {
continue;
}
try {
field.setAccessible(true);
return (sun.misc.Unsafe)field.get(null);
} catch (Exception ignored) {
}
break;
}
throw new UnsupportedOperationException("LWJGL requires sun.misc.Unsafe to be available.");
}
private static long getAddressOffset() {
long MAGIC_ADDRESS = 0xDEADBEEF8BADF00DL;
if (BITS32) {
MAGIC_ADDRESS &= 0xFFFFFFFFL;
}
ByteBuffer bb = Objects.requireNonNull(NewDirectByteBuffer(MAGIC_ADDRESS, 0));
long offset = 8L; // 8 byte aligned, cannot be at 0
while (true) {
if (UNSAFE.getLong(bb, offset) == MAGIC_ADDRESS) {
return offset;
}
offset += 8L;
}
}
private static final int MAGIC_CAPACITY = 0x0D15EA5E;
private static final int MAGIC_POSITION = 0x00FACADE;
private static long getIntFieldOffset(ByteBuffer bb, int magicValue) {
long offset = 4L; // 4 byte aligned, cannot be at 0
while (true) {
if (UNSAFE.getInt(bb, offset) == magicValue) {
return offset;
}
offset += 4L;
}
}
private static long getMarkOffset() {
ByteBuffer bb = Objects.requireNonNull(NewDirectByteBuffer(1L, 0));
return getIntFieldOffset(bb, -1);
}
private static long getPositionOffset() {
ByteBuffer bb = Objects.requireNonNull(NewDirectByteBuffer(-1L, MAGIC_CAPACITY));
bb.position(MAGIC_POSITION);
return getIntFieldOffset(bb, MAGIC_POSITION);
}
private static long getLimitOffset() {
ByteBuffer bb = Objects.requireNonNull(NewDirectByteBuffer(-1L, MAGIC_CAPACITY));
bb.limit(MAGIC_POSITION);
return getIntFieldOffset(bb, MAGIC_POSITION);
}
private static long getCapacityOffset() {
ByteBuffer bb = Objects.requireNonNull(NewDirectByteBuffer(-1L, MAGIC_CAPACITY));
bb.limit(0);
return getIntFieldOffset(bb, MAGIC_CAPACITY);
}
private static long getParentOffset(int oopSize, T parent, Function childFactory) {
T child = childFactory.apply(parent);
long offset = oopSize; // pointer aligned, cannot be at 0
switch (oopSize) {
case Integer.BYTES: // 32-bit or 64-bit with compressed oops
while (true) {
if (UNSAFE.getInt(parent, offset) != UNSAFE.getInt(child, offset)) {
return offset;
}
offset += oopSize;
}
case Long.BYTES: // 64-bit with uncompressed oops
while (true) {
if (UNSAFE.getLong(parent, offset) != UNSAFE.getLong(child, offset)) {
return offset;
}
offset += oopSize;
}
default:
throw new IllegalStateException();
}
}
@SuppressWarnings("unchecked")
static T wrap(Class extends T> clazz, long address, int capacity) {
T buffer;
try {
buffer = (T)UNSAFE.allocateInstance(clazz);
} catch (InstantiationException e) {
throw new UnsupportedOperationException(e);
}
UNSAFE.putLong(buffer, ADDRESS, address);
UNSAFE.putInt(buffer, MARK, -1);
UNSAFE.putInt(buffer, LIMIT, capacity);
UNSAFE.putInt(buffer, CAPACITY, capacity);
return buffer;
}
@SuppressWarnings("unchecked")
static T slice(Class extends T> clazz, T source, long address, int capacity, long attachmentOffset) {
T target;
try {
target = (T)UNSAFE.allocateInstance(clazz);
} catch (InstantiationException e) {
throw new UnsupportedOperationException(e);
}
UNSAFE.putLong(target, ADDRESS, address);
UNSAFE.putInt(target, MARK, -1);
UNSAFE.putInt(target, LIMIT, capacity);
UNSAFE.putInt(target, CAPACITY, capacity);
// The JDK stores source here
UNSAFE.putObject(target, attachmentOffset, UNSAFE.getObject(source, attachmentOffset));
return target;
}
@SuppressWarnings("unchecked")
static T duplicate(Class extends T> clazz, T source, long attachmentOffset) {
T target;
try {
target = (T)UNSAFE.allocateInstance(clazz);
} catch (InstantiationException e) {
throw new UnsupportedOperationException(e);
}
UNSAFE.putLong(target, ADDRESS, UNSAFE.getLong(source, ADDRESS));
UNSAFE.putInt(target, MARK, UNSAFE.getInt(source, MARK));
UNSAFE.putInt(target, POSITION, UNSAFE.getInt(source, POSITION));
UNSAFE.putInt(target, LIMIT, UNSAFE.getInt(source, LIMIT));
UNSAFE.putInt(target, CAPACITY, UNSAFE.getInt(source, CAPACITY));
// The JDK stores source here
UNSAFE.putObject(target, attachmentOffset, UNSAFE.getObject(source, attachmentOffset));
return target;
}
}