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.
+ * + *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 ThreadLocalAllocations 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 staticChanges 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 staticThis 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