From fa72a4a4b9399ae325e396ba33b60e94f3ca8ebf Mon Sep 17 00:00:00 2001 From: Duy Tran Khanh <40482367+khanhduytran0@users.noreply.github.com> Date: Tue, 24 Aug 2021 08:33:31 +0700 Subject: [PATCH] Create MemoryUtil.java Add some functions from older LWJGL versions --- .../java/org/lwjgl/system/MemoryUtil.java | 3033 +++++++++++++++++ 1 file changed, 3033 insertions(+) create mode 100644 jre_lwjgl3glfw/src/main/java/org/lwjgl/system/MemoryUtil.java diff --git a/jre_lwjgl3glfw/src/main/java/org/lwjgl/system/MemoryUtil.java b/jre_lwjgl3glfw/src/main/java/org/lwjgl/system/MemoryUtil.java new file mode 100644 index 000000000..0bec229e1 --- /dev/null +++ b/jre_lwjgl3glfw/src/main/java/org/lwjgl/system/MemoryUtil.java @@ -0,0 +1,3033 @@ +/* + * 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 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: + * + * + *

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 BUFFER_BYTE; + static final Class BUFFER_SHORT; + static final Class BUFFER_CHAR; + static final Class BUFFER_INT; + static final Class BUFFER_LONG; + static final Class BUFFER_FLOAT; + static final Class 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 { + MARK = getMarkOffset(); + POSITION = getPositionOffset(); + LIMIT = getLimitOffset(); + CAPACITY = getCapacityOffset(); + + ADDRESS = getAddressOffset(); + + int oopSize = UNSAFE.arrayIndexScale(Object[].class); + + long offset = (max(max(max(MARK, POSITION), LIMIT), CAPACITY) + 4 + (oopSize - 1)) & ~Integer.toUnsignedLong(oopSize - 1); + long a = memAddress(bb); + + PARENT_BYTE = getParentOffset(offset, oopSize, bb, bb.duplicate().order(bb.order())); + PARENT_SHORT = getParentOffset(offset, oopSize, memShortBuffer(a, 0), bb.asShortBuffer()); + PARENT_CHAR = getParentOffset(offset, oopSize, memCharBuffer(a, 0), bb.asCharBuffer()); + PARENT_INT = getParentOffset(offset, oopSize, memIntBuffer(a, 0), bb.asIntBuffer()); + PARENT_LONG = getParentOffset(offset, oopSize, memLongBuffer(a, 0), bb.asLongBuffer()); + PARENT_FLOAT = getParentOffset(offset, oopSize, memFloatBuffer(a, 0), bb.asFloatBuffer()); + PARENT_DOUBLE = getParentOffset(offset, oopSize, memDoubleBuffer(a, 0), bb.asDoubleBuffer()); + } 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); + } + + /** + * {@code CLongBuffer} version of {@link #memAlloc}. + * + * @param size the number of C long values to allocate. + */ + public static CLongBuffer memAllocCLong(int size) { + return Pointer.Default.wrap(CLongBuffer.class, nmemAllocChecked(getAllocationSize(size, CLONG_SHIFT)), 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)); + } + } + + /** {@code CustomBuffer} version of {@link #memFree}. */ + public static void memFree(@Nullable CustomBuffer ptr) { + if (ptr != null) { + nmemFree(ptr.address); + } + } + + // from LWJGL 3.2.2 + /** {@code 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); + } + + /** + * {@code CLongBuffer} version of {@link #memCalloc}. + * + * @param num the number of C long values to allocate. + */ + public static CLongBuffer memCallocCLong(int num) { + return Pointer.Default.wrap(CLongBuffer.class, nmemCallocChecked(num, CLONG_SIZE), 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); + } + + /** + * {@code CLongBuffer} version of {@link #memRealloc}. + * + * @param size the number of C long values to allocate. + */ + public static CLongBuffer memRealloc(@Nullable CLongBuffer ptr, int size) { + CLongBuffer buffer = memCLongBuffer(nmemReallocChecked(ptr == null ? NULL : ptr.address, getAllocationSize(size, CLONG_SIZE)), size); + if (ptr != null) { + buffer.position(min(ptr.position(), size)); + } + return buffer; + } + + /** + * 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 {@link ByteBuffer} instance as a view of the specified {@link ShortBuffer} between its current position and limit. + * + *

This operation is the inverse of {@link ByteBuffer#asShortBuffer()}. The returned {@code ByteBuffer} instance will be set to the native + * {@link ByteOrder}.

+ * + * @param buffer the source buffer + * + * @return the {@code ByteBuffer} view + */ + public static ByteBuffer memByteBuffer(ShortBuffer buffer) { + if (CHECKS && (Integer.MAX_VALUE >> 1) < buffer.remaining()) { + throw new IllegalArgumentException("The source buffer range is too wide"); + } + return wrap(BUFFER_BYTE, memAddress(buffer), buffer.remaining() << 1).order(NATIVE_ORDER); + } + + /** + * Creates a {@link ByteBuffer} instance as a view of the specified {@link CharBuffer} between its current position and limit. + * + *

This operation is the inverse of {@link ByteBuffer#asCharBuffer()}. The returned {@code ByteBuffer} instance will be set to the native + * {@link ByteOrder}.

+ * + * @param buffer the source buffer + * + * @return the {@code ByteBuffer} view + */ + public static ByteBuffer memByteBuffer(CharBuffer buffer) { + if (CHECKS && (Integer.MAX_VALUE >> 1) < buffer.remaining()) { + throw new IllegalArgumentException("The source buffer range is too wide"); + } + return wrap(BUFFER_BYTE, memAddress(buffer), buffer.remaining() << 1).order(NATIVE_ORDER); + } + + /** + * Creates a {@link ByteBuffer} instance as a view of the specified {@link IntBuffer} between its current position and limit. + * + *

This operation is the inverse of {@link ByteBuffer#asIntBuffer()}. The returned {@code ByteBuffer} instance will be set to the native + * {@link ByteOrder}.

+ * + * @param buffer the source buffer + * + * @return the {@code ByteBuffer} view + */ + public static ByteBuffer memByteBuffer(IntBuffer buffer) { + if (CHECKS && (Integer.MAX_VALUE >> 2) < buffer.remaining()) { + throw new IllegalArgumentException("The source buffer range is too wide"); + } + return wrap(BUFFER_BYTE, memAddress(buffer), buffer.remaining() << 2).order(NATIVE_ORDER); + } + + /** + * Creates a {@link ByteBuffer} instance as a view of the specified {@link LongBuffer} between its current position and limit. + * + *

This operation is the inverse of {@link ByteBuffer#asLongBuffer()}. The returned {@code ByteBuffer} instance will be set to the native + * {@link ByteOrder}.

+ * + * @param buffer the source buffer + * + * @return the {@code ByteBuffer} view + */ + public static ByteBuffer memByteBuffer(LongBuffer buffer) { + if (CHECKS && (Integer.MAX_VALUE >> 3) < buffer.remaining()) { + throw new IllegalArgumentException("The source buffer range is too wide"); + } + return wrap(BUFFER_BYTE, memAddress(buffer), buffer.remaining() << 3).order(NATIVE_ORDER); + } + + /** + * Creates a {@link ByteBuffer} instance as a view of the specified {@link FloatBuffer} between its current position and limit. + * + *

This operation is the inverse of {@link ByteBuffer#asFloatBuffer()}. The returned {@code ByteBuffer} instance will be set to the native + * {@link ByteOrder}.

+ * + * @param buffer the source buffer + * + * @return the {@code ByteBuffer} view + */ + public static ByteBuffer memByteBuffer(FloatBuffer buffer) { + if (CHECKS && (Integer.MAX_VALUE >> 2) < buffer.remaining()) { + throw new IllegalArgumentException("The source buffer range is too wide"); + } + return wrap(BUFFER_BYTE, memAddress(buffer), buffer.remaining() << 2).order(NATIVE_ORDER); + } + + /** + * Creates a {@link ByteBuffer} instance as a view of the specified {@link DoubleBuffer} between its current position and limit. + * + *

This operation is the inverse of {@link ByteBuffer#asDoubleBuffer()}. The returned {@code ByteBuffer} instance will be set to the native + * {@link ByteOrder}.

+ * + * @param buffer the source buffer + * + * @return the {@code ByteBuffer} view + */ + public static ByteBuffer memByteBuffer(DoubleBuffer buffer) { + if (CHECKS && (Integer.MAX_VALUE >> 3) < buffer.remaining()) { + throw new IllegalArgumentException("The source buffer range is too wide"); + } + return wrap(BUFFER_BYTE, memAddress(buffer), buffer.remaining() << 3).order(NATIVE_ORDER); + } + + /** + * Creates a {@link ByteBuffer} instance as a view of the specified {@link CustomBuffer} between its current position and limit. + * + *

The returned {@code ByteBuffer} instance will be set to the native {@link ByteOrder}.

+ * + * @param buffer the source buffer + * + * @return the {@code ByteBuffer} view + */ + public static ByteBuffer memByteBuffer(CustomBuffer buffer) { + if (CHECKS && (Integer.MAX_VALUE / buffer.sizeof()) < buffer.remaining()) { + throw new IllegalArgumentException("The source buffer range is too wide"); + } + return wrap(BUFFER_BYTE, memAddress(buffer), buffer.remaining() * buffer.sizeof()).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 {@code CLongBuffer} 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 {@code CLongBuffer} + */ + public static CLongBuffer memCLongBuffer(long address, int capacity) { + if (CHECKS) { + check(address); + } + return Pointer.Default.wrap(CLongBuffer.class, address, capacity); + } + + /** Like {@link #memCLongBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */ + @Nullable + public static CLongBuffer memCLongBufferSafe(long address, int capacity) { + return address == NULL ? null : Pointer.Default.wrap(CLongBuffer.class, 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) { + ByteBuffer target; + try { + target = (ByteBuffer)UNSAFE.allocateInstance(BUFFER_BYTE); + } catch (InstantiationException e) { + throw new UnsupportedOperationException(e); + } + + UNSAFE.putLong(target, ADDRESS, UNSAFE.getLong(buffer, ADDRESS)); + UNSAFE.putInt(target, MARK, UNSAFE.getInt(buffer, MARK)); + UNSAFE.putInt(target, POSITION, UNSAFE.getInt(buffer, POSITION)); + UNSAFE.putInt(target, LIMIT, UNSAFE.getInt(buffer, LIMIT)); + UNSAFE.putInt(target, CAPACITY, UNSAFE.getInt(buffer, CAPACITY)); + + Object attachment = UNSAFE.getObject(buffer, PARENT_BYTE); + UNSAFE.putObject(target, PARENT_BYTE, attachment == null ? buffer : attachment); + + return target.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, memAddress0(buffer) + buffer.position(), buffer.remaining()); + } + + /** + * 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, memAddress0(buffer) + position, capacity); + } + + /** + * 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 & 0xFF; + + 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 + */ + // from LWJGL 3.1.2 + public static void memCopy(long src, long dst, int bytes) { + memCopy(src, dst, (long)bytes); + } + + /** + * 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 memGetCLong(long ptr) { + return CLONG_SIZE == 8 + ? UNSAFE.getLong(null, ptr) + : UNSAFE.getInt(null, ptr); + } + + public static long memGetAddress(long ptr) { + return BITS64 + ? UNSAFE.getLong(null, ptr) + : 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 memPutCLong(long ptr, long value) { + if (CLONG_SIZE == 8) { + UNSAFE.putLong(null, ptr, value); + } else { + UNSAFE.putInt(null, ptr, (int)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 + Integer.toUnsignedLong(i) * 2, (short)text.charAt(i)); + } + if (nullTerminated) { + UNSAFE.putShort(target + Integer.toUnsignedLong(len++) * 2, (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") + public 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(long offset, int oopSize, T buffer, T bufferWithAttachment) { + switch (oopSize) { + case Integer.BYTES: // 32-bit or 64-bit with compressed oops + while (true) { + if (UNSAFE.getInt(buffer, offset) != UNSAFE.getInt(bufferWithAttachment, offset)) { + return offset; + } + offset += oopSize; + } + case Long.BYTES: // 64-bit with uncompressed oops + while (true) { + if (UNSAFE.getLong(buffer, offset) != UNSAFE.getLong(bufferWithAttachment, offset)) { + return offset; + } + offset += oopSize; + } + default: + throw new IllegalStateException(); + } + } + + @SuppressWarnings("unchecked") + static T wrap(Class 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; + } + + static ByteBuffer slice(ByteBuffer source, long address, int capacity) { + ByteBuffer target; + try { + target = (ByteBuffer)UNSAFE.allocateInstance(BUFFER_BYTE); + } 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); + + Object attachment = UNSAFE.getObject(source, PARENT_BYTE); + UNSAFE.putObject(target, PARENT_BYTE, attachment == null ? source : attachment); + + return target.order(source.order()); + } + + @SuppressWarnings("unchecked") + static T slice(Class 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); + + UNSAFE.putObject(target, attachmentOffset, UNSAFE.getObject(source, attachmentOffset)); + + return target; + } + + @SuppressWarnings("unchecked") + static T duplicate(Class 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)); + + UNSAFE.putObject(target, attachmentOffset, UNSAFE.getObject(source, attachmentOffset)); + + return target; + } + +}