From d949cb4e1b699af891e31070022d24a3ef1ef97e Mon Sep 17 00:00:00 2001 From: Duy Tran Khanh Date: Fri, 20 Aug 2021 15:24:15 +0700 Subject: [PATCH] Fixes Sodium 0.3 crashes on saving settings Sodium 0.3 won't work yet --- JavaApp/libs/lwjgl3-minecraft.jar | Bin 1658175 -> 1636770 bytes .../java/org/lwjgl/system/MemoryUtil.java | 3021 +++++++++++++++++ 2 files changed, 3021 insertions(+) create mode 100644 JavaApp/src/main/java/org/lwjgl/system/MemoryUtil.java diff --git a/JavaApp/libs/lwjgl3-minecraft.jar b/JavaApp/libs/lwjgl3-minecraft.jar index eb8b8e5a8981293bf601bfe21c557ef6af1d3cb1..f76173e4069b0f06d32fb38eb6721e7451bde3f4 100644 GIT binary patch delta 45160 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

0;SU(j8rr!qfD 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 V1e>?;Q$_=!>g+oAR#%5lV--*p#Y^8+8Pj$Y=( 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 methods in this class will make use of {@link sun.misc.Unsafe} if it's available, for performance. If Unsafe is not available, the fallback + * implementations make use of reflection and, in the worst-case, JNI.

+ * + *

Method names in this class are prefixed with {@code mem} to avoid ambiguities when used with static imports.

+ * + *

Text encoding/decoding

+ * + * Three codecs are available, each with a different postfix: + *
    + *
  • UTF16 - Direct mapping of 2 bytes to Java char and vice versa
  • + *
  • UTF8 - custom UTF-8 codec without intermediate allocations
  • + *
  • ASCII - Not the original 7bit ASCII, but any character set with a single byte encoding (ISO 8859-1, Windows-1252, etc.)
  • + *
+ * + *

Methods in bindings that accept/return {@code CharSequence}/{@code String} also support {@code ByteBuffer}, so custom codecs can be used if necessary.

+ * + * @see Configuration#MEMORY_ALLOCATOR + * @see Configuration#DEBUG_MEMORY_ALLOCATOR + */ +public final class MemoryUtil { + + /** Alias for the null pointer address. */ + public static final long NULL = 0L; + + /** The memory page size, in bytes. This value is always a power-of-two. */ + public static final int PAGE_SIZE; + + /** The cache-line size, in bytes. This value is always a power-of-two. */ + public static final int CACHE_LINE_SIZE; + + static final int ARRAY_TLC_SIZE = Configuration.ARRAY_TLC_SIZE.get(8192); + + static final ThreadLocal ARRAY_TLC_BYTE = ThreadLocal.withInitial(() -> new byte[ARRAY_TLC_SIZE]); + static final ThreadLocal ARRAY_TLC_CHAR = ThreadLocal.withInitial(() -> new char[ARRAY_TLC_SIZE]); + + static final sun.misc.Unsafe UNSAFE; + + static final ByteOrder NATIVE_ORDER = ByteOrder.nativeOrder(); + + private static final Charset UTF16 = NATIVE_ORDER == ByteOrder.LITTLE_ENDIAN + ? StandardCharsets.UTF_16LE + : StandardCharsets.UTF_16BE; + + static final Class 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 + */ + 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; + } + +}