All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.lwjgl.system.MemoryUtil Maven / Gradle / Ivy

There is a newer version: 3.3.4
Show newest version
/*
 * Copyright LWJGL. All rights reserved.
 * License terms: https://www.lwjgl.org/license
 */
package org.lwjgl.system;

import org.lwjgl.*;
import org.lwjgl.system.MemoryManage.*;
import org.lwjgl.system.MemoryUtil.MemoryAllocationReport.*;
import org.lwjgl.system.jni.*;

import javax.annotation.*;
import java.nio.*;
import java.nio.charset.*;
import java.util.*;
import java.util.function.*;

import static java.lang.Character.*;
import static java.lang.Math.*;
import static org.lwjgl.system.APIUtil.*;
import static org.lwjgl.system.Checks.*;
import static org.lwjgl.system.MathUtil.*;
import static org.lwjgl.system.MemoryUtil.LazyInit.*;
import static org.lwjgl.system.Pointer.*;
import static org.lwjgl.system.jni.JNINativeInterface.*;
import static org.lwjgl.system.libc.LibCString.*;

/**
 * This class provides functionality for managing native memory.
 *
 * 

All methods in this class will make use of {@link sun.misc.Unsafe} if it's available, for performance. If Unsafe is not available, the fallback * implementations make use of reflection and, in the worst-case, JNI.

* *

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

* *

Text encoding/decoding

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

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

* * @see Configuration#MEMORY_ALLOCATOR * @see Configuration#DEBUG_MEMORY_ALLOCATOR */ public final class MemoryUtil { /** Alias for the null pointer address. */ public static final long NULL = 0L; /** The memory page size, in bytes. This value is always a power-of-two. */ public static final int PAGE_SIZE; /** The cache-line size, in bytes. This value is always a power-of-two. */ public static final int CACHE_LINE_SIZE; static final int ARRAY_TLC_SIZE = Configuration.ARRAY_TLC_SIZE.get(8192); static final ThreadLocal ARRAY_TLC_BYTE = ThreadLocal.withInitial(() -> new byte[ARRAY_TLC_SIZE]); static final ThreadLocal ARRAY_TLC_CHAR = ThreadLocal.withInitial(() -> new char[ARRAY_TLC_SIZE]); static final sun.misc.Unsafe UNSAFE; static final ByteOrder NATIVE_ORDER = ByteOrder.nativeOrder(); private static final Charset UTF16 = NATIVE_ORDER == ByteOrder.LITTLE_ENDIAN ? StandardCharsets.UTF_16LE : StandardCharsets.UTF_16BE; static final Class 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 { ADDRESS = getAddressOffset(); MARK = getMarkOffset(); POSITION = getPositionOffset(); LIMIT = getLimitOffset(); CAPACITY = getCapacityOffset(); int oopSize = UNSAFE.arrayIndexScale(Object[].class); PARENT_BYTE = getParentOffset(oopSize, bb, it -> it.duplicate().order(it.order())); PARENT_SHORT = getParentOffset(oopSize, bb.asShortBuffer(), ShortBuffer::duplicate); PARENT_CHAR = getParentOffset(oopSize, bb.asCharBuffer(), CharBuffer::duplicate); PARENT_INT = getParentOffset(oopSize, bb.asIntBuffer(), IntBuffer::duplicate); PARENT_LONG = getParentOffset(oopSize, bb.asLongBuffer(), LongBuffer::duplicate); PARENT_FLOAT = getParentOffset(oopSize, bb.asFloatBuffer(), FloatBuffer::duplicate); PARENT_DOUBLE = getParentOffset(oopSize, bb.asDoubleBuffer(), DoubleBuffer::duplicate); } catch (Throwable t) { throw new UnsupportedOperationException(t); } PAGE_SIZE = UNSAFE.pageSize(); CACHE_LINE_SIZE = 64; // TODO: Can we do better? } static final class LazyInit { private LazyInit() { } static final MemoryAllocator ALLOCATOR_IMPL; static final MemoryAllocator ALLOCATOR; static { ALLOCATOR_IMPL = MemoryManage.getInstance(); ALLOCATOR = Configuration.DEBUG_MEMORY_ALLOCATOR.get(false) ? new DebugAllocator(ALLOCATOR_IMPL) : ALLOCATOR_IMPL; apiLog("MemoryUtil allocator: " + ALLOCATOR.getClass().getSimpleName()); } } private MemoryUtil() { } /* ------------------------------------- ------------------------------------- EXPLICIT MEMORY MANAGEMENT API ------------------------------------- ------------------------------------- */ /** The interface implemented by the memory allocator used by the explicit memory management API ({@link #memAlloc}, {@link #memFree}, etc). */ public interface MemoryAllocator { /** Returns a pointer to the malloc function. */ long getMalloc(); /** Returns a pointer to the calloc function. */ long getCalloc(); /** Returns a pointer to the realloc function. */ long getRealloc(); /** Returns a pointer to the free function. */ long getFree(); /** Returns a pointer to the aligned_alloc function. */ long getAlignedAlloc(); /** Returns a pointer to the aligned_free function. */ long getAlignedFree(); /** Called by {@link MemoryUtil#memAlloc}. */ long malloc(long size); /** Called by {@link MemoryUtil#memCalloc}. */ long calloc(long num, long size); /** Called by {@link MemoryUtil#memRealloc}. */ long realloc(long ptr, long size); /** Called by {@link MemoryUtil#memFree}. */ void free(long ptr); /** Called by {@link MemoryUtil#memAlignedAlloc}. */ long aligned_alloc(long alignment, long size); /** Called by {@link MemoryUtil#memAlignedFree}. */ void aligned_free(long ptr); } /** * Returns the {@link MemoryAllocator} instance used internally by the explicit memory management API ({@link #memAlloc}, {@link #memFree}, etc). * *

Allocations made through the returned instance will not be tracked for memory leaks, even if {@link Configuration#DEBUG_MEMORY_ALLOCATOR} is enabled. * This can be useful for {@code static final} allocations that live throughout the application's lifetime and will never be freed until the process is * terminated. Normally such allocations would be reported as memory leaks by the debug allocator.

* *

The expectation is that this method will rarely be used, so it does not have the {@code mem} prefix to avoid pollution of auto-complete lists.

* * @return the {@link MemoryAllocator} instance */ public static MemoryAllocator getAllocator() { return getAllocator(false); } /** * Returns the {@link MemoryAllocator} instance used internally by the explicit memory management API ({@link #memAlloc}, {@link #memFree}, etc). * * @param tracked whether allocations will be tracked for memory leaks, if {@link Configuration#DEBUG_MEMORY_ALLOCATOR} is enabled. * * @return the {@link MemoryAllocator} instance */ public static MemoryAllocator getAllocator(boolean tracked) { return tracked ? ALLOCATOR : ALLOCATOR_IMPL; } // --- [ memAlloc ] --- /** Unsafe version of {@link #memAlloc}. May return {@link #NULL} if {@code size} is zero or the allocation failed. */ public static long nmemAlloc(long size) { return ALLOCATOR.malloc(size); } /** * Unsafe version of {@link #memAlloc} that checks the returned pointer. * * @return a pointer to the memory block allocated by the function on success. This pointer will never be {@link #NULL}, even if {@code size} is zero. * * @throws OutOfMemoryError if the function failed to allocate the requested block of memory */ public static long nmemAllocChecked(long size) { long address = nmemAlloc(size != 0 ? size : 1L); if (CHECKS && address == NULL) { throw new OutOfMemoryError(); } return address; } private static long getAllocationSize(int elements, int elementShift) { return apiCheckAllocation(elements, Integer.toUnsignedLong(elements) << elementShift, BITS64 ? Long.MAX_VALUE : 0xFFFF_FFFFL); } /** * The standard C malloc function. * *

Allocates a block of {@code size} bytes of memory, returning a pointer to the beginning of the block. The content of the newly allocated block of * memory is not initialized, remaining with indeterminate values.

* *

Memory allocated with this method must be freed with {@link #memFree}.

* * @param size the size of the memory block to allocate, in bytes. If {@code size} is zero, the returned pointer shall not be dereferenced. * * @return on success, a pointer to the memory block allocated by the function * * @throws OutOfMemoryError if the function failed to allocate the requested block of memory */ public static ByteBuffer memAlloc(int size) { return wrap(BUFFER_BYTE, nmemAllocChecked(size), size).order(NATIVE_ORDER); } /** * ShortBuffer version of {@link #memAlloc}. * * @param size the number of short values to allocate. */ public static ShortBuffer memAllocShort(int size) { return wrap(BUFFER_SHORT, nmemAllocChecked(getAllocationSize(size, 1)), size); } /** * IntBuffer version of {@link #memAlloc}. * * @param size the number of int values to allocate. */ public static IntBuffer memAllocInt(int size) { return wrap(BUFFER_INT, nmemAllocChecked(getAllocationSize(size, 2)), size); } /** * FloatBuffer version of {@link #memAlloc}. * * @param size the number of float values to allocate. */ public static FloatBuffer memAllocFloat(int size) { return wrap(BUFFER_FLOAT, nmemAllocChecked(getAllocationSize(size, 2)), size); } /** * LongBuffer version of {@link #memAlloc}. * * @param size the number of long values to allocate. */ public static LongBuffer memAllocLong(int size) { return wrap(BUFFER_LONG, nmemAllocChecked(getAllocationSize(size, 3)), size); } /** * DoubleBuffer version of {@link #memAlloc}. * * @param size the number of double values to allocate. */ public static DoubleBuffer memAllocDouble(int size) { return wrap(BUFFER_DOUBLE, nmemAllocChecked(getAllocationSize(size, 3)), size); } /** * PointerBuffer version of {@link #memAlloc}. * * @param size the number of pointer values to allocate. */ public static PointerBuffer memAllocPointer(int size) { return Pointer.Default.wrap(PointerBuffer.class, nmemAllocChecked(getAllocationSize(size, POINTER_SHIFT)), size); } /** Unsafe version of {@link #memFree}. */ public static void nmemFree(long ptr) { ALLOCATOR.free(ptr); } /** * The standard C free function. * *

A block of memory previously allocated by a call to {@link #memAlloc}, {@link #memCalloc} or {@link #memRealloc} is deallocated, making it available * again for further allocations.

* * @param ptr pointer to a memory block previously allocated with {@link #memAlloc}, {@link #memCalloc} or {@link #memRealloc}. If {@code ptr} does not * point to a block of memory allocated with the above functions, it causes undefined behavior. If {@code ptr} is a {@link #NULL} pointer, the * function does nothing. */ public static void memFree(@Nullable Buffer ptr) { if (ptr != null) { nmemFree(UNSAFE.getLong(ptr, ADDRESS)); } } /** PointerBuffer version of {@link #memFree}. */ public static void memFree(@Nullable PointerBuffer ptr) { if (ptr != null) { nmemFree(ptr.address); } } // --- [ memCalloc ] --- /** Unsafe version of {@link #memCalloc}. May return {@link #NULL} if {@code num} or {@code size} are zero or the allocation failed. */ public static long nmemCalloc(long num, long size) { return ALLOCATOR.calloc(num, size); } /** * Unsafe version of {@link #memCalloc} that checks the returned pointer. * * @return a pointer to the memory block allocated by the function on success. This pointer will never be {@link #NULL}, even if {@code num} or * {@code size} are zero. * * @throws OutOfMemoryError if the function failed to allocate the requested block of memory */ public static long nmemCallocChecked(long num, long size) { if (num == 0L || size == 0L) { num = 1L; size = 1L; } long address = nmemCalloc(num, size); if (CHECKS && address == NULL) { throw new OutOfMemoryError(); } return address; } /** * The standard C calloc function. * *

Allocates a block of memory for an array of {@code num} elements, each of them {@code size} bytes long, and initializes all its bits to zero. The * effective result is the allocation of a zero-initialized memory block of {@code (num*size)} bytes.

* *

Memory allocated with this method must be freed with {@link #memFree}.

* * @param num the number of elements to allocate. * @param size the size of each element. If {@code size} is zero, the return value depends on the particular library implementation (it may or may not be * a null pointer), but the returned pointer shall not be dereferenced. * * @return on success, a pointer to the memory block allocated by the function * * @throws OutOfMemoryError if the function failed to allocate the requested block of memory */ public static ByteBuffer memCalloc(int num, int size) { return wrap(BUFFER_BYTE, nmemCallocChecked(num, size), num * size).order(NATIVE_ORDER); } /** * Alternative version of {@link #memCalloc}. * * @param num the number of bytes to allocate. */ public static ByteBuffer memCalloc(int num) { return wrap(BUFFER_BYTE, nmemCallocChecked(num, 1), num).order(NATIVE_ORDER); } /** * ShortBuffer version of {@link #memCalloc}. * * @param num the number of short values to allocate. */ public static ShortBuffer memCallocShort(int num) { return wrap(BUFFER_SHORT, nmemCallocChecked(num, 2), num); } /** * IntBuffer version of {@link #memCalloc}. * * @param num the number of int values to allocate. */ public static IntBuffer memCallocInt(int num) { return wrap(BUFFER_INT, nmemCallocChecked(num, 4), num); } /** * FloatBuffer version of {@link #memCalloc}. * * @param num the number of float values to allocate. */ public static FloatBuffer memCallocFloat(int num) { return wrap(BUFFER_FLOAT, nmemCallocChecked(num, 4), num); } /** * LongBuffer version of {@link #memCalloc}. * * @param num the number of long values to allocate. */ public static LongBuffer memCallocLong(int num) { return wrap(BUFFER_LONG, nmemCallocChecked(num, 8), num); } /** * DoubleBuffer version of {@link #memCalloc}. * * @param num the number of double values to allocate. */ public static DoubleBuffer memCallocDouble(int num) { return wrap(BUFFER_DOUBLE, nmemCallocChecked(num, 8), num); } /** * PointerBuffer version of {@link #memCalloc}. * * @param num the number of pointer values to allocate. */ public static PointerBuffer memCallocPointer(int num) { return Pointer.Default.wrap(PointerBuffer.class, nmemCallocChecked(num, POINTER_SIZE), num); } // --- [ memRealloc] --- /** Unsafe version of {@link #memRealloc}. May return {@link #NULL} if {@code size} is zero or the allocation failed. */ public static long nmemRealloc(long ptr, long size) { return ALLOCATOR.realloc(ptr, size); } /** * Unsafe version of {@link #memRealloc} that checks the returned pointer. * * @return a pointer to the memory block reallocated by the function on success. This pointer will never be {@link #NULL}, even if {@code size} is zero. * * @throws OutOfMemoryError if the function failed to allocate the requested block of memory */ public static long nmemReallocChecked(long ptr, long size) { long address = nmemRealloc(ptr, size != 0 ? size : 1L); if (CHECKS && address == NULL) { throw new OutOfMemoryError(); } return address; } private static T realloc(@Nullable T old_p, T new_p, int size) { if (old_p != null) { new_p.position(min(old_p.position(), size)); } return new_p; } /** * The standard C realloc function. * *

Changes the size of the memory block pointed to by {@code ptr}. The function may move the memory block to a new location (whose address is returned * by the function). The content of the memory block is preserved up to the lesser of the new and old sizes, even if the block is moved to a new location. * If the new size is larger, the value of the newly allocated portion is indeterminate.

* *

The memory address used is always the address at the start of {@code ptr}, so the current position of {@code ptr} does not need to be set to 0 for * this function to work. The current position is preserved, even if the memory block is moved to a new location, unless {@code size} is less than the * current position in which case position will be equal to capacity. The limit is set to the capacity, and the mark is discarded.

* * @param ptr a pointer to a memory block previously allocated with {@link #memAlloc}, {@link #memCalloc} or {@link #memRealloc}. Alternatively, this can * be a {@link #NULL} pointer, in which case a new block is allocated (as if {@link #memAlloc} was called). * @param size the new size for the memory block, in bytes. * * @return a pointer to the reallocated memory block, which may be either the same as {@code ptr} or a new location * * @throws OutOfMemoryError if the function failed to allocate the requested block of memory. The memory block pointed to by argument {@code ptr} is not * deallocated (it is still valid, and with its contents unchanged). */ public static ByteBuffer memRealloc(@Nullable ByteBuffer ptr, int size) { return realloc(ptr, memByteBuffer(nmemReallocChecked(ptr == null ? NULL : UNSAFE.getLong(ptr, ADDRESS), size), size), size); } /** * ShortBuffer version of {@link #memRealloc}. * * @param size the number of short values to allocate. */ public static ShortBuffer memRealloc(@Nullable ShortBuffer ptr, int size) { return realloc(ptr, memShortBuffer(nmemReallocChecked(ptr == null ? NULL : UNSAFE.getLong(ptr, ADDRESS), getAllocationSize(size, 1)), size), size); } /** * IntBuffer version of {@link #memRealloc}. * * @param size the number of int values to allocate. */ public static IntBuffer memRealloc(@Nullable IntBuffer ptr, int size) { return realloc(ptr, memIntBuffer(nmemReallocChecked(ptr == null ? NULL : UNSAFE.getLong(ptr, ADDRESS), getAllocationSize(size, 2)), size), size); } /** * LongBuffer version of {@link #memRealloc}. * * @param size the number of long values to allocate. */ public static LongBuffer memRealloc(@Nullable LongBuffer ptr, int size) { return realloc(ptr, memLongBuffer(nmemReallocChecked(ptr == null ? NULL : UNSAFE.getLong(ptr, ADDRESS), getAllocationSize(size, 3)), size), size); } /** * FloatBuffer version of {@link #memRealloc}. * * @param size the number of float values to allocate. */ public static FloatBuffer memRealloc(@Nullable FloatBuffer ptr, int size) { return realloc(ptr, memFloatBuffer(nmemReallocChecked(ptr == null ? NULL : UNSAFE.getLong(ptr, ADDRESS), getAllocationSize(size, 2)), size), size); } /** * DoubleBuffer version of {@link #memRealloc}. * * @param size the number of double values to allocate. */ public static DoubleBuffer memRealloc(@Nullable DoubleBuffer ptr, int size) { return realloc(ptr, memDoubleBuffer(nmemReallocChecked(ptr == null ? NULL : UNSAFE.getLong(ptr, ADDRESS), getAllocationSize(size, 3)), size), size); } /** * PointerBuffer version of {@link #memRealloc}. * * @param size the number of pointer values to allocate. */ public static PointerBuffer memRealloc(@Nullable PointerBuffer ptr, int size) { PointerBuffer buffer = memPointerBuffer(nmemReallocChecked(ptr == null ? NULL : ptr.address, getAllocationSize(size, POINTER_SHIFT)), size); if (ptr != null) { buffer.position(min(ptr.position(), size)); } return buffer; } // --- [ memAlignedAlloc ] --- /** Unsafe version of {@link #memAlignedAlloc}. May return {@link #NULL} if {@code size} is zero or the allocation failed. */ public static long nmemAlignedAlloc(long alignment, long size) { return ALLOCATOR.aligned_alloc(alignment, size); } /** * Unsafe version of {@link #memAlignedAlloc} that checks the returned pointer. * * @return a pointer to the memory block allocated by the function on success. This pointer will never be {@link #NULL}, even if {@code size} is zero. * * @throws OutOfMemoryError if the function failed to allocate the requested block of memory */ public static long nmemAlignedAllocChecked(long alignment, long size) { long address = nmemAlignedAlloc(alignment, size != 0 ? size : 1L); if (CHECKS && address == NULL) { throw new OutOfMemoryError(); } return address; } /** * The standard C aligned_alloc function. * *

Allocate {@code size} bytes of uninitialized storage whose alignment is specified by {@code alignment}. The size parameter must be an integral * multiple of alignment. Memory allocated with memAlignedAlloc() must be freed with {@link #memAlignedFree}.

* * @param alignment the alignment. Must be a power of two value and a multiple of {@code sizeof(void *)}. * @param size the number of bytes to allocate. Must be a multiple of {@code alignment}. */ public static ByteBuffer memAlignedAlloc(int alignment, int size) { return wrap(BUFFER_BYTE, nmemAlignedAllocChecked(alignment, size), size).order(NATIVE_ORDER); } // --- [ memAlignedFree ] --- /** Unsafe version of {@link #memAlignedFree}. */ public static void nmemAlignedFree(long ptr) { ALLOCATOR.aligned_free(ptr); } /** * Frees a block of memory that was allocated with {@link #memAlignedAlloc}. If ptr is {@code NULL}, no operation is performed. * * @param ptr the aligned block of memory to free */ public static void memAlignedFree(@Nullable ByteBuffer ptr) { if (ptr != null) { nmemAlignedFree(UNSAFE.getLong(ptr, ADDRESS)); } } // --- [ DebugAllocator ] --- /** The memory allocation report callback. */ public interface MemoryAllocationReport { /** * Reports allocated memory. * * @param address the address of the memory allocated. May be {@link #NULL}. * @param memory the amount of memory allocated, in bytes * @param threadId id of the thread that allocated the memory. May be {@link #NULL}. * @param threadName name of the thread that allocated the memory. May be {@code null}. * @param stacktrace the allocation stacktrace. May be {@code null}. */ void invoke(long address, long memory, long threadId, @Nullable String threadName, @Nullable StackTraceElement... stacktrace); /** Specifies how to aggregate the reported allocations. */ enum Aggregate { /** Allocations are aggregated over the whole process or thread. */ ALL, /** * Allocations are aggregated based on the first stack trace element. This will return an allocation aggregate per method/line number, regardless * of how many different code paths lead to that specific method and line number. */ GROUP_BY_METHOD, /** The allocations are aggregated based on the full stack trace chain. */ GROUP_BY_STACKTRACE } } /** * Reports all live allocations. * *

This method can only be used if the {@link Configuration#DEBUG_MEMORY_ALLOCATOR} option has been set to true.

* * @param report the report callback */ public static void memReport(MemoryAllocationReport report) { DebugAllocator.report(report); } /** * Reports aggregates for the live allocations. * *

This method can only be used if the {@link Configuration#DEBUG_MEMORY_ALLOCATOR} option has been set to true.

* * @param report the report callback * @param groupByStackTrace how to aggregate the reported allocations * @param groupByThread if the reported allocations should be grouped by thread */ public static void memReport(MemoryAllocationReport report, Aggregate groupByStackTrace, boolean groupByThread) { DebugAllocator.report(report, groupByStackTrace, groupByThread); } /* ------------------------------------- ------------------------------------- BUFFER MANAGEMENT API ------------------------------------- ------------------------------------- */ // --- [ memAddress0 ] --- /** * Returns the memory address of the specified buffer. [INTERNAL USE ONLY] * * @param buffer the buffer * * @return the memory address */ public static long memAddress0(Buffer buffer) { return UNSAFE.getLong(buffer, ADDRESS); } // --- [ Buffer address ] --- /** * Returns the memory address at the current position of the specified buffer. This is effectively a pointer value that can be used in native function * calls. * * @param buffer the buffer * * @return the memory address */ public static long memAddress(ByteBuffer buffer) { return buffer.position() + memAddress0(buffer); } /** * Returns the memory address at the specified position of the specified buffer. * * @param buffer the buffer * @param position the buffer position * * @return the memory address * * @see #memAddress(ByteBuffer) */ public static long memAddress(ByteBuffer buffer, int position) { Objects.requireNonNull(buffer); return memAddress0(buffer) + Integer.toUnsignedLong(position); } private static long address(int position, int elementShift, long address) { return address + ((position & 0xFFFF_FFFFL) << elementShift); } /** ShortBuffer version of {@link #memAddress(ByteBuffer)}. */ public static long memAddress(ShortBuffer buffer) { return address(buffer.position(), 1, memAddress0(buffer)); } /** ShortBuffer version of {@link #memAddress(ByteBuffer, int)}. */ public static long memAddress(ShortBuffer buffer, int position) { Objects.requireNonNull(buffer); return address(position, 1, memAddress0(buffer)); } /** CharBuffer version of {@link #memAddress(ByteBuffer)}. */ public static long memAddress(CharBuffer buffer) { return address(buffer.position(), 1, memAddress0(buffer)); } /** CharBuffer version of {@link #memAddress(ByteBuffer, int)}. */ public static long memAddress(CharBuffer buffer, int position) { Objects.requireNonNull(buffer); return address(position, 1, memAddress0(buffer)); } /** IntBuffer version of {@link #memAddress(ByteBuffer)}. */ public static long memAddress(IntBuffer buffer) { return address(buffer.position(), 2, memAddress0(buffer)); } /** IntBuffer version of {@link #memAddress(ByteBuffer, int)}. */ public static long memAddress(IntBuffer buffer, int position) { Objects.requireNonNull(buffer); return address(position, 2, memAddress0(buffer)); } /** FloatBuffer version of {@link #memAddress(ByteBuffer)}. */ public static long memAddress(FloatBuffer buffer) { return address(buffer.position(), 2, memAddress0(buffer)); } /** FloatBuffer version of {@link #memAddress(ByteBuffer, int)}. */ public static long memAddress(FloatBuffer buffer, int position) { Objects.requireNonNull(buffer); return address(position, 2, memAddress0(buffer)); } /** LongBuffer version of {@link #memAddress(ByteBuffer)}. */ public static long memAddress(LongBuffer buffer) { return address(buffer.position(), 3, memAddress0(buffer)); } /** LongBuffer version of {@link #memAddress(ByteBuffer, int)}. */ public static long memAddress(LongBuffer buffer, int position) { Objects.requireNonNull(buffer); return address(position, 3, memAddress0(buffer)); } /** DoubleBuffer version of {@link #memAddress(ByteBuffer)}. */ public static long memAddress(DoubleBuffer buffer) { return address(buffer.position(), 3, memAddress0(buffer)); } /** DoubleBuffer version of {@link #memAddress(ByteBuffer, int)}. */ public static long memAddress(DoubleBuffer buffer, int position) { Objects.requireNonNull(buffer); return address(position, 3, memAddress0(buffer)); } /** Polymorphic version of {@link #memAddress(ByteBuffer)}. */ public static long memAddress(Buffer buffer) { int elementShift; if (buffer instanceof ByteBuffer) { elementShift = 0; } else if (buffer instanceof ShortBuffer || buffer instanceof CharBuffer) { elementShift = 1; } else if (buffer instanceof IntBuffer || buffer instanceof FloatBuffer) { elementShift = 2; } else { elementShift = 3; } return address(buffer.position(), elementShift, memAddress0(buffer)); } /** CustomBuffer version of {@link #memAddress(ByteBuffer)}. */ public static long memAddress(CustomBuffer buffer) { return buffer.address(); } /** CustomBuffer version of {@link #memAddress(ByteBuffer, int)}. */ public static long memAddress(CustomBuffer buffer, int position) { return buffer.address(position); } // --- [ Buffer address - Safe ] --- /** Null-safe version of {@link #memAddress(ByteBuffer)}. Returns {@link #NULL} if the specified buffer is null. */ public static long memAddressSafe(@Nullable ByteBuffer buffer) { return buffer == null ? NULL : memAddress0(buffer) + buffer.position(); } /** ShortBuffer version of {@link #memAddressSafe(ByteBuffer)}. */ public static long memAddressSafe(@Nullable ShortBuffer buffer) { return buffer == null ? NULL : address(buffer.position(), 1, memAddress0(buffer)); } /** CharBuffer version of {@link #memAddressSafe(ByteBuffer)}. */ public static long memAddressSafe(@Nullable CharBuffer buffer) { return buffer == null ? NULL : address(buffer.position(), 1, memAddress0(buffer)); } /** IntBuffer version of {@link #memAddressSafe(ByteBuffer)}. */ public static long memAddressSafe(@Nullable IntBuffer buffer) { return buffer == null ? NULL : address(buffer.position(), 2, memAddress0(buffer)); } /** FloatBuffer version of {@link #memAddressSafe(ByteBuffer)}. */ public static long memAddressSafe(@Nullable FloatBuffer buffer) { return buffer == null ? NULL : address(buffer.position(), 2, memAddress0(buffer)); } /** LongBuffer version of {@link #memAddressSafe(ByteBuffer)}. */ public static long memAddressSafe(@Nullable LongBuffer buffer) { return buffer == null ? NULL : address(buffer.position(), 3, memAddress0(buffer)); } /** DoubleBuffer version of {@link #memAddressSafe(ByteBuffer)}. */ public static long memAddressSafe(@Nullable DoubleBuffer buffer) { return buffer == null ? NULL : address(buffer.position(), 3, memAddress0(buffer)); } /** Pointer version of {@link #memAddressSafe(ByteBuffer)}. */ public static long memAddressSafe(@Nullable Pointer pointer) { return pointer == null ? NULL : pointer.address(); } // --- [ Buffer allocation ] --- /** * Creates a new direct ByteBuffer that starts at the specified memory address and has the specified capacity. The returned ByteBuffer instance will be set * to the native {@link ByteOrder}. * * @param address the starting memory address * @param capacity the buffer capacity * * @return the new ByteBuffer */ public static ByteBuffer memByteBuffer(long address, int capacity) { if (CHECKS) { check(address); } return wrap(BUFFER_BYTE, address, capacity).order(NATIVE_ORDER); } /** Like {@link #memByteBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */ @Nullable public static ByteBuffer memByteBufferSafe(long address, int capacity) { return address == NULL ? null : wrap(BUFFER_BYTE, address, capacity).order(NATIVE_ORDER); } /** * Creates a new direct ShortBuffer that starts at the specified memory address and has the specified capacity. * *

The {@code address} specified must be aligned to 2 bytes. If not, use {@code memByteBuffer(address, capacity * 2).asShortBuffer()}.

* * @param address the starting memory address * @param capacity the buffer capacity * * @return the new ShortBuffer */ public static ShortBuffer memShortBuffer(long address, int capacity) { if (CHECKS) { check(address); } return wrap(BUFFER_SHORT, address, capacity); } /** Like {@link #memShortBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */ @Nullable public static ShortBuffer memShortBufferSafe(long address, int capacity) { return address == NULL ? null : wrap(BUFFER_SHORT, address, capacity); } /** * Creates a new direct CharBuffer that starts at the specified memory address and has the specified capacity. * *

The {@code address} specified must be aligned to 2 bytes. If not, use {@code memByteBuffer(address, capacity * 2).asCharBuffer()}.

* * @param address the starting memory address * @param capacity the buffer capacity * * @return the new CharBuffer */ public static CharBuffer memCharBuffer(long address, int capacity) { if (CHECKS) { check(address); } return wrap(BUFFER_CHAR, address, capacity); } /** Like {@link #memCharBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */ @Nullable public static CharBuffer memCharBufferSafe(long address, int capacity) { return address == NULL ? null : wrap(BUFFER_CHAR, address, capacity); } /** * Creates a new direct IntBuffer that starts at the specified memory address and has the specified capacity. * *

The {@code address} specified must be aligned to 4 bytes. If not, use {@code memByteBuffer(address, capacity * 4).asIntBuffer()}.

* * @param address the starting memory address * @param capacity the buffer capacity * * @return the new IntBuffer */ public static IntBuffer memIntBuffer(long address, int capacity) { if (CHECKS) { check(address); } return wrap(BUFFER_INT, address, capacity); } /** Like {@link #memIntBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */ @Nullable public static IntBuffer memIntBufferSafe(long address, int capacity) { return address == NULL ? null : wrap(BUFFER_INT, address, capacity); } /** * Creates a new direct LongBuffer that starts at the specified memory address and has the specified capacity. * *

The {@code address} specified must be aligned to 8 bytes. If not, use {@code memByteBuffer(address, capacity * 8).asLongBuffer()}.

* * @param address the starting memory address * @param capacity the buffer capacity * * @return the new LongBuffer */ public static LongBuffer memLongBuffer(long address, int capacity) { if (CHECKS) { check(address); } return wrap(BUFFER_LONG, address, capacity); } /** Like {@link #memLongBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */ @Nullable public static LongBuffer memLongBufferSafe(long address, int capacity) { return address == NULL ? null : wrap(BUFFER_LONG, address, capacity); } /** * Creates a new direct FloatBuffer that starts at the specified memory address and has the specified capacity. * *

The {@code address} specified must be aligned to 4 bytes. If not, use {@code memByteBuffer(address, capacity * 4).asFloatBuffer()}.

* * @param address the starting memory address * @param capacity the buffer capacity * * @return the new FloatBuffer */ public static FloatBuffer memFloatBuffer(long address, int capacity) { if (CHECKS) { check(address); } return wrap(BUFFER_FLOAT, address, capacity); } /** Like {@link #memFloatBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */ @Nullable public static FloatBuffer memFloatBufferSafe(long address, int capacity) { return address == NULL ? null : wrap(BUFFER_FLOAT, address, capacity); } /** * Creates a new direct DoubleBuffer that starts at the specified memory address and has the specified capacity. * *

The {@code address} specified must be aligned to 8 bytes. If not, use {@code memByteBuffer(address, capacity * 8).asDoubleBuffer()}.

* * @param address the starting memory address * @param capacity the buffer capacity * * @return the new DoubleBuffer */ public static DoubleBuffer memDoubleBuffer(long address, int capacity) { if (CHECKS) { check(address); } return wrap(BUFFER_DOUBLE, address, capacity); } /** Like {@link #memDoubleBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */ @Nullable public static DoubleBuffer memDoubleBufferSafe(long address, int capacity) { return address == NULL ? null : wrap(BUFFER_DOUBLE, address, capacity); } /** * Creates a new PointerBuffer that starts at the specified memory address and has the specified capacity. * *

The {@code address} specified must be aligned to the pointer size. If not, use {@code PointerBuffer.create(memByteBuffer(address, capacity * * POINTER_SIZE))}.

* * @param address the starting memory address * @param capacity the buffer capacity * * @return the new PointerBuffer */ public static PointerBuffer memPointerBuffer(long address, int capacity) { if (CHECKS) { check(address); } return Pointer.Default.wrap(PointerBuffer.class, address, capacity); } /** Like {@link #memPointerBuffer}, but returns {@code null} if {@code address} is {@link #NULL}. */ @Nullable public static PointerBuffer memPointerBufferSafe(long address, int capacity) { return address == NULL ? null : Pointer.Default.wrap(PointerBuffer.class, address, capacity); } // --- [ Buffer duplication ] --- /** * Duplicates the specified buffer. The returned buffer will have the same {@link ByteOrder} as the source buffer. * *

This method should be preferred over {@link ByteBuffer#duplicate} because it has a much shorter call chain. Long call chains may fail to inline due * to JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).

* * @param buffer the buffer to duplicate * * @return the duplicated buffer */ public static ByteBuffer memDuplicate(ByteBuffer buffer) { return duplicate(BUFFER_BYTE, buffer, PARENT_BYTE).order(buffer.order()); } /** * Duplicates the specified buffer. * *

This method should be preferred over {@link ShortBuffer#duplicate} because it has a much shorter call chain. Long call chains may fail to inline due * to JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).

* * @param buffer the buffer to duplicate * * @return the duplicated buffer */ public static ShortBuffer memDuplicate(ShortBuffer buffer) { return duplicate(BUFFER_SHORT, buffer, PARENT_SHORT); } /** * Duplicates the specified buffer. * *

This method should be preferred over {@link CharBuffer#duplicate} because it has a much shorter call chain. Long call chains may fail to inline due * to JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).

* * @param buffer the buffer to duplicate * * @return the duplicated buffer */ public static CharBuffer memDuplicate(CharBuffer buffer) { return duplicate(BUFFER_CHAR, buffer, PARENT_CHAR); } /** * Duplicates the specified buffer. * *

This method should be preferred over {@link IntBuffer#duplicate} because it has a much shorter call chain. Long call chains may fail to inline due * to JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).

* * @param buffer the buffer to duplicate * * @return the duplicated buffer */ public static IntBuffer memDuplicate(IntBuffer buffer) { return duplicate(BUFFER_INT, buffer, PARENT_INT); } /** * Duplicates the specified buffer. * *

This method should be preferred over {@link LongBuffer#duplicate} because it has a much shorter call chain. Long call chains may fail to inline due * to JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).

* * @param buffer the buffer to duplicate * * @return the duplicated buffer */ public static LongBuffer memDuplicate(LongBuffer buffer) { return duplicate(BUFFER_LONG, buffer, PARENT_LONG); } /** * Duplicates the specified buffer. * *

This method should be preferred over {@link FloatBuffer#duplicate} because it has a much shorter call chain. Long call chains may fail to inline due * to JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).

* * @param buffer the buffer to duplicate * * @return the duplicated buffer */ public static FloatBuffer memDuplicate(FloatBuffer buffer) { return duplicate(BUFFER_FLOAT, buffer, PARENT_FLOAT); } /** * Duplicates the specified buffer. * *

This method should be preferred over {@link DoubleBuffer#duplicate} because it has a much shorter call chain. Long call chains may fail to inline due * to JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).

* * @param buffer the buffer to duplicate * * @return the duplicated buffer */ public static DoubleBuffer memDuplicate(DoubleBuffer buffer) { return duplicate(BUFFER_DOUBLE, buffer, PARENT_DOUBLE); } // --- [ Buffer slicing ] --- /** * Slices the specified buffer. The returned buffer will have the same {@link ByteOrder} as the source buffer. * *

This method should be preferred over {@link ByteBuffer#slice} because it has a much shorter call chain. Long call chains may fail to inline due to * JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).

* * @param buffer the buffer to slice * * @return the sliced buffer */ public static ByteBuffer memSlice(ByteBuffer buffer) { return slice(BUFFER_BYTE, buffer, memAddress0(buffer) + buffer.position(), buffer.remaining(), PARENT_BYTE).order(buffer.order()); } /** * Slices the specified buffer. * *

This method should be preferred over {@link ShortBuffer#slice} because it has a much shorter call chain. Long call chains may fail to inline due to * JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).

* * @param buffer the buffer to slice * * @return the sliced buffer */ public static ShortBuffer memSlice(ShortBuffer buffer) { return slice(BUFFER_SHORT, buffer, address(buffer.position(), 1, memAddress0(buffer)), buffer.remaining(), PARENT_SHORT); } /** * Slices the specified buffer. * *

This method should be preferred over {@link CharBuffer#slice} because it has a much shorter call chain. Long call chains may fail to inline due to * JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).

* * @param buffer the buffer to slice * * @return the sliced buffer */ public static CharBuffer memSlice(CharBuffer buffer) { return slice(BUFFER_CHAR, buffer, address(buffer.position(), 1, memAddress0(buffer)), buffer.remaining(), PARENT_CHAR); } /** * Slices the specified buffer. * *

This method should be preferred over {@link IntBuffer#slice} because it has a much shorter call chain. Long call chains may fail to inline due to * JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).

* * @param buffer the buffer to slice * * @return the sliced buffer */ public static IntBuffer memSlice(IntBuffer buffer) { return slice(BUFFER_INT, buffer, address(buffer.position(), 2, memAddress0(buffer)), buffer.remaining(), PARENT_INT); } /** * Slices the specified buffer. * *

This method should be preferred over {@link LongBuffer#slice} because it has a much shorter call chain. Long call chains may fail to inline due to * JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).

* * @param buffer the buffer to slice * * @return the sliced buffer */ public static LongBuffer memSlice(LongBuffer buffer) { return slice(BUFFER_LONG, buffer, address(buffer.position(), 3, memAddress0(buffer)), buffer.remaining(), PARENT_LONG); } /** * Slices the specified buffer. * *

This method should be preferred over {@link FloatBuffer#slice} because it has a much shorter call chain. Long call chains may fail to inline due to * JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).

* * @param buffer the buffer to slice * * @return the sliced buffer */ public static FloatBuffer memSlice(FloatBuffer buffer) { return slice(BUFFER_FLOAT, buffer, address(buffer.position(), 2, memAddress0(buffer)), buffer.remaining(), PARENT_FLOAT); } /** * Slices the specified buffer. * *

This method should be preferred over {@link DoubleBuffer#slice} because it has a much shorter call chain. Long call chains may fail to inline due to * JVM limits, disabling important optimizations (e.g. scalar replacement via Escape Analysis).

* * @param buffer the buffer to slice * * @return the sliced buffer */ public static DoubleBuffer memSlice(DoubleBuffer buffer) { return slice(BUFFER_DOUBLE, buffer, address(buffer.position(), 3, memAddress0(buffer)), buffer.remaining(), PARENT_DOUBLE); } /** * Returns a slice of the specified buffer between {@code (buffer.position() + offset)} and {@code (buffer.position() + offset + capacity)}. The returned * buffer will have the same {@link ByteOrder} as the original buffer. * *

The position and limit of the original buffer are preserved after a call to this method.

* * @param buffer the buffer to slice * @param offset the slice offset, it must be ≤ {@code buffer.remaining()} * @param capacity the slice length, it must be ≤ {@code buffer.capacity() - (buffer.position() + offset)} * * @return the sliced buffer */ public static ByteBuffer memSlice(ByteBuffer buffer, int offset, int capacity) { int position = buffer.position() + offset; if (offset < 0 || buffer.limit() < position) { throw new IllegalArgumentException(); } if (capacity < 0 || buffer.capacity() - position < capacity) { throw new IllegalArgumentException(); } return slice(BUFFER_BYTE, buffer, memAddress0(buffer) + position, capacity, PARENT_BYTE).order(buffer.order()); } /** * Returns a slice of the specified buffer between {@code (buffer.position() + offset)} and {@code (buffer.position() + offset + capacity)}. * *

The position and limit of the original buffer are preserved after a call to this method.

* * @param buffer the buffer to slice * @param offset the slice offset, it must be ≤ {@code buffer.remaining()} * @param capacity the slice length, it must be ≤ {@code buffer.capacity() - (buffer.position() + offset)} * * @return the sliced buffer */ public static ShortBuffer memSlice(ShortBuffer buffer, int offset, int capacity) { int position = buffer.position() + offset; if (offset < 0 || buffer.limit() < position) { throw new IllegalArgumentException(); } if (capacity < 0 || buffer.capacity() - position < capacity) { throw new IllegalArgumentException(); } return slice(BUFFER_SHORT, buffer, address(position, 1, memAddress0(buffer)), capacity, PARENT_SHORT); } /** * Returns a slice of the specified buffer between {@code (buffer.position() + offset)} and {@code (buffer.position() + offset + capacity)}. * *

The position and limit of the original buffer are preserved after a call to this method.

* * @param buffer the buffer to slice * @param offset the slice offset, it must be ≤ {@code buffer.remaining()} * @param capacity the slice length, it must be ≤ {@code buffer.capacity() - (buffer.position() + offset)} * * @return the sliced buffer */ public static CharBuffer memSlice(CharBuffer buffer, int offset, int capacity) { int position = buffer.position() + offset; if (offset < 0 || buffer.limit() < position) { throw new IllegalArgumentException(); } if (capacity < 0 || buffer.capacity() - position < capacity) { throw new IllegalArgumentException(); } return slice(BUFFER_CHAR, buffer, address(position, 1, memAddress0(buffer)), capacity, PARENT_CHAR); } /** * Returns a slice of the specified buffer between {@code (buffer.position() + offset)} and {@code (buffer.position() + offset + capacity)}. * *

The position and limit of the original buffer are preserved after a call to this method.

* * @param buffer the buffer to slice * @param offset the slice offset, it must be ≤ {@code buffer.remaining()} * @param capacity the slice length, it must be ≤ {@code buffer.capacity() - (buffer.position() + offset)} * * @return the sliced buffer */ public static IntBuffer memSlice(IntBuffer buffer, int offset, int capacity) { int position = buffer.position() + offset; if (offset < 0 || buffer.limit() < position) { throw new IllegalArgumentException(); } if (capacity < 0 || buffer.capacity() - position < capacity) { throw new IllegalArgumentException(); } return slice(BUFFER_INT, buffer, address(position, 2, memAddress0(buffer)), capacity, PARENT_INT); } /** * Returns a slice of the specified buffer between {@code (buffer.position() + offset)} and {@code (buffer.position() + offset + capacity)}. * *

The position and limit of the original buffer are preserved after a call to this method.

* * @param buffer the buffer to slice * @param offset the slice offset, it must be ≤ {@code buffer.remaining()} * @param capacity the slice length, it must be ≤ {@code buffer.capacity() - (buffer.position() + offset)} * * @return the sliced buffer */ public static LongBuffer memSlice(LongBuffer buffer, int offset, int capacity) { int position = buffer.position() + offset; if (offset < 0 || buffer.limit() < position) { throw new IllegalArgumentException(); } if (capacity < 0 || buffer.capacity() - position < capacity) { throw new IllegalArgumentException(); } return slice(BUFFER_LONG, buffer, address(position, 3, memAddress0(buffer)), capacity, PARENT_LONG); } /** * Returns a slice of the specified buffer between {@code (buffer.position() + offset)} and {@code (buffer.position() + offset + capacity)}. * *

The position and limit of the original buffer are preserved after a call to this method.

* * @param buffer the buffer to slice * @param offset the slice offset, it must be ≤ {@code buffer.remaining()} * @param capacity the slice length, it must be ≤ {@code buffer.capacity() - (buffer.position() + offset)} * * @return the sliced buffer */ public static FloatBuffer memSlice(FloatBuffer buffer, int offset, int capacity) { int position = buffer.position() + offset; if (offset < 0 || buffer.limit() < position) { throw new IllegalArgumentException(); } if (capacity < 0 || buffer.capacity() - position < capacity) { throw new IllegalArgumentException(); } return slice(BUFFER_FLOAT, buffer, address(position, 2, memAddress0(buffer)), capacity, PARENT_FLOAT); } /** * Returns a slice of the specified buffer between {@code (buffer.position() + offset)} and {@code (buffer.position() + offset + capacity)}. * *

The position and limit of the original buffer are preserved after a call to this method.

* * @param buffer the buffer to slice * @param offset the slice offset, it must be ≤ {@code buffer.remaining()} * @param capacity the slice length, it must be ≤ {@code buffer.capacity() - (buffer.position() + offset)} * * @return the sliced buffer */ public static DoubleBuffer memSlice(DoubleBuffer buffer, int offset, int capacity) { int position = buffer.position() + offset; if (offset < 0 || buffer.limit() < position) { throw new IllegalArgumentException(); } if (capacity < 0 || buffer.capacity() - position < capacity) { throw new IllegalArgumentException(); } return slice(BUFFER_DOUBLE, buffer, address(position, 3, memAddress0(buffer)), capacity, PARENT_DOUBLE); } /** * Returns a slice of the specified buffer between {@code (buffer.position() + offset)} and {@code (buffer.position() + offset + capacity)}. * *

The position and limit of the original buffer are preserved after a call to this method.

* * @param buffer the buffer to slice * @param offset the slice offset, it must be ≤ {@code buffer.remaining()} * @param capacity the slice length, it must be ≤ {@code buffer.capacity() - (buffer.position() + offset)} * * @return the sliced buffer */ public static > T memSlice(T buffer, int offset, int capacity) { return buffer.slice(offset, capacity); } // --- [ memset ] --- /** * Sets all bytes in a specified block of memory to a fixed value (usually zero). * * @param ptr the starting memory address * @param value the value to set (memSet will convert it to unsigned byte) */ public static void memSet(ByteBuffer ptr, int value) { memSet(memAddress(ptr), value, ptr.remaining()); } /** * Sets all bytes in a specified block of memory to a fixed value (usually zero). * * @param ptr the starting memory address * @param value the value to set (memSet will convert it to unsigned byte) */ public static void memSet(ShortBuffer ptr, int value) { memSet(memAddress(ptr), value, apiGetBytes(ptr.remaining(), 1)); } /** * Sets all bytes in a specified block of memory to a fixed value (usually zero). * * @param ptr the starting memory address * @param value the value to set (memSet will convert it to unsigned byte) */ public static void memSet(CharBuffer ptr, int value) { memSet(memAddress(ptr), value, apiGetBytes(ptr.remaining(), 1)); } /** * Sets all bytes in a specified block of memory to a fixed value (usually zero). * * @param ptr the starting memory address * @param value the value to set (memSet will convert it to unsigned byte) */ public static void memSet(IntBuffer ptr, int value) { memSet(memAddress(ptr), value, apiGetBytes(ptr.remaining(), 2)); } /** * Sets all bytes in a specified block of memory to a fixed value (usually zero). * * @param ptr the starting memory address * @param value the value to set (memSet will convert it to unsigned byte) */ public static void memSet(LongBuffer ptr, int value) { memSet(memAddress(ptr), value, apiGetBytes(ptr.remaining(), 3)); } /** * Sets all bytes in a specified block of memory to a fixed value (usually zero). * * @param ptr the starting memory address * @param value the value to set (memSet will convert it to unsigned byte) */ public static void memSet(FloatBuffer ptr, int value) { memSet(memAddress(ptr), value, apiGetBytes(ptr.remaining(), 2)); } /** * Sets all bytes in a specified block of memory to a fixed value (usually zero). * * @param ptr the starting memory address * @param value the value to set (memSet will convert it to unsigned byte) */ public static void memSet(DoubleBuffer ptr, int value) { memSet(memAddress(ptr), value, apiGetBytes(ptr.remaining(), 3)); } /** * Sets all bytes in a specified block of memory to a fixed value (usually zero). * * @param ptr the starting memory address * @param value the value to set (memSet will convert it to unsigned byte) * @param the buffer type */ public static > void memSet(T ptr, int value) { memSet(memAddress(ptr), value, Integer.toUnsignedLong(ptr.remaining()) * ptr.sizeof()); } /** * Sets all bytes in a specified block of memory to a fixed value (usually zero). * * @param ptr the starting memory address * @param value the value to set (memSet will convert it to unsigned byte) * @param the struct type */ public static void memSet(T ptr, int value) { memSet(ptr.address, value, ptr.sizeof()); } // --- [ memcpy ] --- /** * Sets all bytes in a specified block of memory to a copy of another block. * * @param src the source memory address * @param dst the destination memory address */ public static void memCopy(ByteBuffer src, ByteBuffer dst) { if (CHECKS) { check(dst, src.remaining()); } MultiReleaseMemCopy.copy(memAddress(src), memAddress(dst), src.remaining()); } /** * Sets all bytes in a specified block of memory to a copy of another block. * * @param src the source memory address * @param dst the destination memory address */ public static void memCopy(ShortBuffer src, ShortBuffer dst) { if (CHECKS) { check(dst, src.remaining()); } MultiReleaseMemCopy.copy(memAddress(src), memAddress(dst), apiGetBytes(src.remaining(), 1)); } /** * Sets all bytes in a specified block of memory to a copy of another block. * * @param src the source memory address * @param dst the destination memory address */ public static void memCopy(CharBuffer src, CharBuffer dst) { if (CHECKS) { check((Buffer)dst, src.remaining()); } MultiReleaseMemCopy.copy(memAddress(src), memAddress(dst), apiGetBytes(src.remaining(), 1)); } /** * Sets all bytes in a specified block of memory to a copy of another block. * * @param src the source memory address * @param dst the destination memory address */ public static void memCopy(IntBuffer src, IntBuffer dst) { if (CHECKS) { check(dst, src.remaining()); } MultiReleaseMemCopy.copy(memAddress(src), memAddress(dst), apiGetBytes(src.remaining(), 2)); } /** * Sets all bytes in a specified block of memory to a copy of another block. * * @param src the source memory address * @param dst the destination memory address */ public static void memCopy(LongBuffer src, LongBuffer dst) { if (CHECKS) { check(dst, src.remaining()); } MultiReleaseMemCopy.copy(memAddress(src), memAddress(dst), apiGetBytes(src.remaining(), 3)); } /** * Sets all bytes in a specified block of memory to a copy of another block. * * @param src the source memory address * @param dst the destination memory address */ public static void memCopy(FloatBuffer src, FloatBuffer dst) { if (CHECKS) { check(dst, src.remaining()); } MultiReleaseMemCopy.copy(memAddress(src), memAddress(dst), apiGetBytes(src.remaining(), 2)); } /** * Sets all bytes in a specified block of memory to a copy of another block. * * @param src the source memory address * @param dst the destination memory address */ public static void memCopy(DoubleBuffer src, DoubleBuffer dst) { if (CHECKS) { check(dst, src.remaining()); } MultiReleaseMemCopy.copy(memAddress(src), memAddress(dst), apiGetBytes(src.remaining(), 3)); } /** * Sets all bytes in a specified block of memory to a copy of another block. * * @param src the source memory address * @param dst the destination memory address * @param the buffer type */ public static > void memCopy(T src, T dst) { if (CHECKS) { check(dst, src.remaining()); } MultiReleaseMemCopy.copy(memAddress(src), memAddress(dst), Integer.toUnsignedLong(src.remaining()) * src.sizeof()); } /** * Sets all bytes of a struct to a copy of another struct. * * @param src the source struct * @param dst the destination struct * @param the struct type */ public static void memCopy(T src, T dst) { MultiReleaseMemCopy.copy(src.address, dst.address, src.sizeof()); } /* ------------------------------------- ------------------------------------- UNSAFE MEMORY ACCESS API ------------------------------------- ------------------------------------- */ private interface NativeShift { long left(long value, int bytes); long right(long value, int bytes); } private static final NativeShift SHIFT = NATIVE_ORDER == ByteOrder.BIG_ENDIAN ? new NativeShift() { @Override public long left(long value, int bytes) { return value << (bytes << 3); } @Override public long right(long value, int bytes) { return value >>> (bytes << 3); } } : new NativeShift() { @Override public long left(long value, int bytes) { return value >>> (bytes << 3); } @Override public long right(long value, int bytes) { return value << (bytes << 3); } }; private static final long FILL_PATTERN = Long.divideUnsigned(-1L, 255L); /** * Sets all bytes in a specified block of memory to a fixed value (usually zero). * * @param ptr the starting memory address * @param value the value to set (memSet will convert it to unsigned byte) * @param bytes the number of bytes to set */ public static void memSet(long ptr, int value, long bytes) { if (DEBUG && (ptr == NULL || bytes < 0)) { throw new IllegalArgumentException(); } /* - Unsafe.setMemory is very slow. - A custom Java loop is fastest at small sizes, approximately up to 256 bytes. - The native memset becomes fastest at bigger sizes, when the JNI overhead becomes negligible. */ //UNSAFE.setMemory(dst, bytes, (byte)(value & 0xFF)); if (256L < bytes) { nmemset(ptr, value, bytes); return; } long fill = (value & 0xFF) * FILL_PATTERN; int i = 0, length = (int)bytes & 0xFFFF_FFFF; if (length != 0) { int misalignment = (int)ptr & 7; if (misalignment != 0) { long aligned = ptr - misalignment; UNSAFE.putLong(null, aligned, merge( UNSAFE.getLong(null, aligned), fill, SHIFT.right(SHIFT.left(-1L, max(0, 8 - length)), misalignment) // 0x0000FFFFFFFF0000 )); i += 8 - misalignment; } } // Aligned longs for performance for (; i <= length - 8; i += 8) { UNSAFE.putLong(null, ptr + i, fill); } int tail = length - i; if (0 < tail) { // Aligned tail UNSAFE.putLong(null, ptr + i, merge( fill, UNSAFE.getLong(null, ptr + i), SHIFT.right(-1L, tail) // 0x00000000FFFFFFFF )); } } // Bit from a where mask bit is 0, bit from b where mask bit is 1. static long merge(long a, long b, long mask) { return a ^ ((a ^ b) & mask); } /** * Sets all bytes in a specified block of memory to a copy of another block. * * @param src the source memory address * @param dst the destination memory address * @param bytes the number of bytes to copy */ public static void memCopy(long src, long dst, long bytes) { if (DEBUG && (src == NULL || dst == NULL || bytes < 0)) { throw new IllegalArgumentException(); } MultiReleaseMemCopy.copy(src, dst, bytes); } static void memCopyAligned(long src, long dst, int bytes) { int i = 0; // Aligned longs for performance for (; i <= bytes - 8; i += 8) { UNSAFE.putLong(null, dst + i, UNSAFE.getLong(null, src + i)); } // Aligned tail if (i < bytes) { UNSAFE.putLong(null, dst + i, merge( UNSAFE.getLong(null, src + i), UNSAFE.getLong(null, dst + i), SHIFT.right(-1L, bytes - i) )); } } public static boolean memGetBoolean(long ptr) { return UNSAFE.getByte(null, ptr) != 0; } public static byte memGetByte(long ptr) { return UNSAFE.getByte(null, ptr); } public static short memGetShort(long ptr) { return UNSAFE.getShort(null, ptr); } public static int memGetInt(long ptr) { return UNSAFE.getInt(null, ptr); } public static long memGetLong(long ptr) { return UNSAFE.getLong(null, ptr); } public static float memGetFloat(long ptr) { return UNSAFE.getFloat(null, ptr); } public static double memGetDouble(long ptr) { return UNSAFE.getDouble(null, ptr); } public static long memGetAddress(long ptr) { if (BITS64) { return UNSAFE.getLong(null, ptr); } else { return ((long)UNSAFE.getInt(null, ptr)) & 0xFFFF_FFFFL; } } public static void memPutByte(long ptr, byte value) { UNSAFE.putByte(null, ptr, value); } public static void memPutShort(long ptr, short value) { UNSAFE.putShort(null, ptr, value); } public static void memPutInt(long ptr, int value) { UNSAFE.putInt(null, ptr, value); } public static void memPutLong(long ptr, long value) { UNSAFE.putLong(null, ptr, value); } public static void memPutFloat(long ptr, float value) { UNSAFE.putFloat(null, ptr, value); } public static void memPutDouble(long ptr, double value) { UNSAFE.putDouble(null, ptr, value); } public static void memPutAddress(long ptr, long value) { if (BITS64) { UNSAFE.putLong(null, ptr, value); } else { UNSAFE.putInt(null, ptr, (int)value); } } /* ------------------------------------- ------------------------------------- JNI UTILITIES API ------------------------------------- ------------------------------------- */ /** * Returns the object that the specified global reference points to. * * @param globalRef the global reference * @param the object type * * @return the object pointed to by {@code globalRef} */ public static native T memGlobalRefToObject(long globalRef); /** Deprecated, use {@link JNINativeInterface#NewGlobalRef} instead. */ @Deprecated public static long memNewGlobalRef(Object obj) { return NewGlobalRef(obj); } /** Deprecated, use {@link JNINativeInterface#DeleteGlobalRef} instead. */ @Deprecated public static void memDeleteGlobalRef(long globalRef) { DeleteGlobalRef(globalRef); } /** Deprecated, use {@link JNINativeInterface#NewWeakGlobalRef} instead. */ @Deprecated public static long memNewWeakGlobalRef(Object obj) { return NewWeakGlobalRef(obj); } /** Deprecated, use {@link JNINativeInterface#DeleteWeakGlobalRef} instead. */ @Deprecated public static void memDeleteWeakGlobalRef(long globalRef) { DeleteWeakGlobalRef(globalRef);} /* ------------------------------------- ------------------------------------- TEXT ENCODING API ------------------------------------- ------------------------------------- */ /** * Returns a ByteBuffer containing the specified text ASCII encoded and null-terminated. * * @param text the text to encode * * @return the encoded text. The returned buffer must be deallocated manually with {@link #memFree}. */ public static ByteBuffer memASCII(CharSequence text) { return memASCII(text, true); } /** Like {@link #memASCII(CharSequence) memASCII}, but returns {@code null} if {@code text} is {@code null}. */ @Nullable public static ByteBuffer memASCIISafe(@Nullable CharSequence text) { return text == null ? null : memASCII(text, true); } /** * Returns a ByteBuffer containing the specified text ASCII encoded and optionally null-terminated. * * @param text the text to encode * @param nullTerminated if true, the text will be terminated with a '\0'. * * @return the encoded text. The returned buffer must be deallocated manually with {@link #memFree}. */ public static ByteBuffer memASCII(CharSequence text, boolean nullTerminated) { int length = memLengthASCII(text, nullTerminated); long target = nmemAlloc(length); encodeASCII(text, nullTerminated, target); return wrap(BUFFER_BYTE, target, length).order(NATIVE_ORDER); } /** Like {@link #memASCII(CharSequence, boolean) memASCII}, but returns {@code null} if {@code text} is {@code null}. */ @Nullable public static ByteBuffer memASCIISafe(@Nullable CharSequence text, boolean nullTerminated) { return text == null ? null : memASCII(text, nullTerminated); } /** * Encodes and optionally null-terminates the specified text using ASCII encoding. The encoded text is stored in the specified {@link ByteBuffer}, at the * current buffer position. The current buffer position is not modified by this operation. The {@code target} buffer is assumed to have enough remaining * space to store the encoded text. * * @param text the text to encode * @param nullTerminated if true, the text will be terminated with a '\0'. * * @return the number of bytes of the encoded string */ public static int memASCII(CharSequence text, boolean nullTerminated, ByteBuffer target) { return encodeASCII(text, nullTerminated, memAddress(target)); } /** * Encodes and optionally null-terminates the specified text using ASCII encoding. The encoded text is stored in the specified {@link ByteBuffer} at the * specified {@code position} offset. The current buffer position is not modified by this operation. The {@code target} buffer is assumed to have enough * remaining space to store the encoded text. * * @param text the text to encode * @param nullTerminated if true, the text will be terminated with a '\0'. * @param offset the buffer position to which the string will be encoded * * @return the number of bytes of the encoded string */ public static int memASCII(CharSequence text, boolean nullTerminated, ByteBuffer target, int offset) { return encodeASCII(text, nullTerminated, memAddress(target, offset)); } static int encodeASCII(CharSequence text, boolean nullTerminated, long target) { int len = text.length(); for (int p = 0; p < len; p++) { UNSAFE.putByte(target + p, (byte)text.charAt(p)); } if (nullTerminated) { UNSAFE.putByte(target + len++, (byte)0); } return len; } /** * Returns the number of bytes required to encode the specified text in the ASCII encoding. * * @param value the text to encode * @param nullTerminated if true, add the number of bytes required for null-termination * * @return the number of bytes */ public static int memLengthASCII(CharSequence value, boolean nullTerminated) { return value.length() + (nullTerminated ? 1 : 0); } /** * Returns a ByteBuffer containing the specified text UTF-8 encoded and null-terminated. * * @param text the text to encode * * @return the encoded text. The returned buffer must be deallocated manually with {@link #memFree}. */ public static ByteBuffer memUTF8(CharSequence text) { return memUTF8(text, true); } /** Like {@link #memUTF8(CharSequence) memASCII}, but returns {@code null} if {@code text} is {@code null}. */ @Nullable public static ByteBuffer memUTF8Safe(@Nullable CharSequence text) { return text == null ? null : memUTF8(text, true); } /** * Returns a ByteBuffer containing the specified text UTF-8 encoded and optionally null-terminated. * * @param text the text to encode * @param nullTerminated if true, the text will be terminated with a '\0'. * * @return the encoded text. The returned buffer must be deallocated manually with {@link #memFree}. */ public static ByteBuffer memUTF8(CharSequence text, boolean nullTerminated) { int length = memLengthUTF8(text, nullTerminated); long target = nmemAlloc(length); encodeUTF8(text, nullTerminated, target); return wrap(BUFFER_BYTE, target, length).order(NATIVE_ORDER); } /** Like {@link #memUTF8(CharSequence, boolean) memASCII}, but returns {@code null} if {@code text} is {@code null}. */ @Nullable public static ByteBuffer memUTF8Safe(@Nullable CharSequence text, boolean nullTerminated) { return text == null ? null : memUTF8(text, nullTerminated); } /** * Encodes and optionally null-terminates the specified text using UTF-8 encoding. The encoded text is stored in the specified {@link ByteBuffer}, at the * current buffer position. The current buffer position is not modified by this operation. The {@code target} buffer is assumed to have enough remaining * space to store the encoded text. The specified text is assumed to be a valid UTF-16 string. * * @param text the text to encode * @param nullTerminated if true, the text will be terminated with a '\0'. * @param target the buffer in which to store the encoded text * * @return the number of bytes of the encoded string */ public static int memUTF8(CharSequence text, boolean nullTerminated, ByteBuffer target) { return encodeUTF8(text, nullTerminated, memAddress(target)); } /** * Encodes and optionally null-terminates the specified text using UTF-8 encoding. The encoded text is stored in the specified {@link ByteBuffer}, at the * specified {@code position} offset. The current buffer position is not modified by this operation. The {@code target} buffer is assumed to have enough * remaining space to store the encoded text. The specified text is assumed to be a valid UTF-16 string. * * @param text the text to encode * @param nullTerminated if true, the text will be terminated with a '\0'. * @param target the buffer in which to store the encoded text * @param offset the buffer position to which the string will be encoded * * @return the number of bytes of the encoded string */ public static int memUTF8(CharSequence text, boolean nullTerminated, ByteBuffer target, int offset) { return encodeUTF8(text, nullTerminated, memAddress(target, offset)); } static int encodeUTF8(CharSequence text, boolean nullTerminated, long target) { int i = 0, len = text.length(), p = 0; char c; // ASCII fast path while (i < len && (c = text.charAt(i)) < 0x80) { UNSAFE.putByte(target + p++, (byte)c); i++; } // Slow path while (i < len) { c = text.charAt(i++); if (c < 0x80) { UNSAFE.putByte(target + p++, (byte)c); } else { int cp = c; if (c < 0x800) { UNSAFE.putByte(target + p++, (byte)(0xC0 | cp >> 6)); } else { if (!isHighSurrogate(c)) { UNSAFE.putByte(target + p++, (byte)(0xE0 | cp >> 12)); } else { cp = toCodePoint(c, text.charAt(i++)); UNSAFE.putByte(target + p++, (byte)(0xF0 | cp >> 18)); UNSAFE.putByte(target + p++, (byte)(0x80 | cp >> 12 & 0x3F)); } UNSAFE.putByte(target + p++, (byte)(0x80 | cp >> 6 & 0x3F)); } UNSAFE.putByte(target + p++, (byte)(0x80 | cp & 0x3F)); } } if (nullTerminated) { UNSAFE.putByte(target + p++, (byte)0); // TODO: did we have a bug here? } return p; } /** * Returns the number of bytes required to encode the specified text in the UTF-8 encoding. * * @param value the text to encode * @param nullTerminated if true, add the number of bytes required for null-termination * * @return the number of bytes */ public static int memLengthUTF8(CharSequence value, boolean nullTerminated) { int i, len = value.length(), bytes = len; // start with 1:1 // ASCII fast path for (i = 0; i < len; i++) { if (0x80 <= value.charAt(i)) { break; } } // 1 or 2 bytes fast path for (; i < len; i++) { char c = value.charAt(i); // fallback to slow path if (0x800 <= c) { bytes += encodeUTF8LengthSlow(value, i, len); break; } // c <= 127: 0 // c >= 128: 1 bytes += (0x7F - c) >>> 31; } return bytes + (nullTerminated ? 1 : 0); } private static int encodeUTF8LengthSlow(CharSequence value, int offset, int len) { int bytes = 0; for (int i = offset; i < len; i++) { char c = value.charAt(i); if (c < 0x800) { bytes += (0x7F - c) >>> 31; } else if (c < MIN_SURROGATE || MAX_SURROGATE < c) { bytes += 2; } else { bytes += 2; // the byte count already includes 2 bytes for the surrogate pair, add 2 more i++; } } return bytes; } /** * Returns a ByteBuffer containing the specified text UTF-16 encoded and null-terminated. * * @param text the text to encode * * @return the encoded text. The returned buffer must be deallocated manually with {@link #memFree}. */ public static ByteBuffer memUTF16(CharSequence text) { return memUTF16(text, true); } /** Like {@link #memUTF16(CharSequence) memASCII}, but returns {@code null} if {@code text} is {@code null}. */ @Nullable public static ByteBuffer memUTF16Safe(@Nullable CharSequence text) { return text == null ? null : memUTF16(text, true); } /** * Returns a ByteBuffer containing the specified text UTF-16 encoded and optionally null-terminated. * * @param text the text to encode * @param nullTerminated if true, the text will be terminated with a '\0'. * * @return the encoded text. The returned buffer must be deallocated manually with {@link #memFree}. */ public static ByteBuffer memUTF16(CharSequence text, boolean nullTerminated) { int length = memLengthUTF16(text, nullTerminated); long target = nmemAlloc(length); encodeUTF16(text, nullTerminated, target); return wrap(BUFFER_BYTE, target, length).order(NATIVE_ORDER); } /** Like {@link #memUTF16(CharSequence, boolean) memASCII}, but returns {@code null} if {@code text} is {@code null}. */ @Nullable public static ByteBuffer memUTF16Safe(@Nullable CharSequence text, boolean nullTerminated) { return text == null ? null : memUTF16(text, nullTerminated); } /** * Encodes and optionally null-terminates the specified text using UTF-16 encoding. The encoded text is stored in the specified {@link ByteBuffer}, at the * current buffer position. The current buffer position is not modified by this operation. The {@code target} buffer is assumed to have enough remaining * space to store the encoded text. * * @param text the text to encode * @param nullTerminated if true, the text will be terminated with a '\0'. * @param target the buffer in which to store the encoded text * * @return the number of bytes of the encoded string */ public static int memUTF16(CharSequence text, boolean nullTerminated, ByteBuffer target) { return encodeUTF16(text, nullTerminated, memAddress(target)); } /** * Encodes and optionally null-terminates the specified text using UTF-16 encoding. The encoded text is stored in the specified {@link ByteBuffer} at the * specified {@code position} offset. The current buffer position is not modified by this operation. The {@code target} buffer is assumed to have enough * remaining space to store the encoded text. * * @param text the text to encode * @param nullTerminated if true, the text will be terminated with a '\0'. * @param target the buffer in which to store the encoded text * @param offset the buffer position to which the string will be encoded * * @return the number of bytes of the encoded string */ public static int memUTF16(CharSequence text, boolean nullTerminated, ByteBuffer target, int offset) { return encodeUTF16(text, nullTerminated, memAddress(target, offset)); } static int encodeUTF16(CharSequence text, boolean nullTerminated, long target) { int len = text.length(); for (int i = 0; i < len; i++) { UNSAFE.putShort(target + 2 * i, (short)text.charAt(i)); } if (nullTerminated) { UNSAFE.putShort(target + 2 * len++, (short)0); } return 2 * len; } /** * Returns the number of bytes required to encode the specified text in the UTF-16 encoding. * * @param value the text to encode * @param nullTerminated if true, add the number of bytes required for null-termination * * @return the number of bytes */ public static int memLengthUTF16(CharSequence value, boolean nullTerminated) { return (value.length() + (nullTerminated ? 1 : 0)) << 1; } /* ------------------------------------- ------------------------------------- TEXT DECODING API ------------------------------------- ------------------------------------- */ private static int memLengthNT1(long address, int maxLength) { if (CHECKS) { check(address); } return BITS64 ? strlen64NT1(address, maxLength) : strlen32NT1(address, maxLength); } private static int strlen64NT1(long address, int maxLength) { int i = 0; if (8 <= maxLength) { int misalignment = (int)address & 7; if (misalignment != 0) { // Align to 8 bytes for (int len = 8 - misalignment; i < len; i++) { if (UNSAFE.getByte(null, address + i) == 0) { return i; } } } // Aligned longs for performance while (i <= maxLength - 8) { if (mathHasZeroByte(UNSAFE.getLong(null, address + i))) { break; } i += 8; } } // Tail for (; i < maxLength; i++) { if (UNSAFE.getByte(null, address + i) == 0) { break; } } return i; } private static int strlen32NT1(long address, int maxLength) { int i = 0; if (4 <= maxLength) { int misalignment = (int)address & 3; if (misalignment != 0) { // Align to 4 bytes for (int len = 4 - misalignment; i < len; i++) { if (UNSAFE.getByte(null, address + i) == 0) { return i; } } } // Aligned ints for performance while (i <= maxLength - 4) { if (mathHasZeroByte(UNSAFE.getInt(null, address + i))) { break; } i += 4; } } // Tail for (; i < maxLength; i++) { if (UNSAFE.getByte(null, address + i) == 0) { break; } } return i; } /** * Calculates the length, in bytes, of the null-terminated string that starts at the current position of the specified buffer. A single \0 character will * terminate the string. The returned length will NOT include the \0 byte. * *

This method is useful for reading ASCII and UTF8 encoded text.

* * @param buffer the buffer containing the null-terminated string * * @return the string length, in bytes */ public static int memLengthNT1(ByteBuffer buffer) { return memLengthNT1(memAddress(buffer), buffer.remaining()); } private static int memLengthNT2(long address, int maxLength) { if (CHECKS) { check(address); } return BITS64 ? strlen64NT2(address, maxLength) : strlen32NT2(address, maxLength); } private static int strlen64NT2(long address, int maxLength) { int i = 0; if (8 <= maxLength) { int misalignment = (int)address & 7; if (misalignment != 0) { // Align to 8 bytes for (int len = 8 - misalignment; i < len; i += 2) { if (UNSAFE.getShort(null, address + i) == 0) { return i; } } } // Aligned longs for performance while (i <= maxLength - 8) { if (mathHasZeroShort(UNSAFE.getLong(null, address + i))) { break; } i += 8; } } // Tail for (; i < maxLength; i += 2) { if (UNSAFE.getShort(null, address + i) == 0) { break; } } return i; } private static int strlen32NT2(long address, int maxLength) { int i = 0; if (4 <= maxLength) { int misalignment = (int)address & 3; if (misalignment != 0) { // Align to 4 bytes for (int len = 4 - misalignment; i < len; i += 2) { if (UNSAFE.getShort(null, address + i) == 0) { return i; } } } // Aligned longs for performance while (i <= maxLength - 4) { if (mathHasZeroShort(UNSAFE.getInt(null, address + i))) { break; } i += 4; } } // Tail for (; i < maxLength; i += 2) { if (UNSAFE.getShort(null, address + i) == 0) { break; } } return i; } /** * Calculates the length, in bytes, of the null-terminated string that starts at the current position of the specified buffer. Two \0 characters will * terminate the string. The returned buffer will NOT include the \0 bytes. * *

This method is useful for reading UTF16 encoded text.

* * @param buffer the buffer containing the null-terminated string * * @return the string length, in bytes */ public static int memLengthNT2(ByteBuffer buffer) { return memLengthNT2(memAddress(buffer), buffer.remaining()); } /** * Creates a new direct ByteBuffer that starts at the specified memory address and has capacity equal to the null-terminated string starting at that * address. A single \0 character will terminate the string. The returned buffer will NOT include the \0 byte. * *

This method is useful for reading ASCII and UTF8 encoded text.

* * @param address the starting memory address * * @return the new ByteBuffer */ public static ByteBuffer memByteBufferNT1(long address) { return memByteBuffer(address, memLengthNT1(address, Integer.MAX_VALUE)); } /** * Creates a new direct ByteBuffer that starts at the specified memory address and has capacity equal to the null-terminated string starting at that * address, up to a maximum of {@code maxLength} bytes. A single \0 character will terminate the string. The returned buffer will NOT include the \0 byte. * *

This method is useful for reading ASCII and UTF8 encoded text.

* * @param address the starting memory address * @param maxLength the maximum string length, in bytes * * @return the new ByteBuffer */ public static ByteBuffer memByteBufferNT1(long address, int maxLength) { return memByteBuffer(address, memLengthNT1(address, maxLength)); } /** Like {@link #memByteBufferNT1(long) memByteBufferNT1}, but returns {@code null} if {@code address} is {@link #NULL}. */ @Nullable public static ByteBuffer memByteBufferNT1Safe(long address) { return address == NULL ? null : memByteBuffer(address, memLengthNT1(address, Integer.MAX_VALUE)); } /** Like {@link #memByteBufferNT1(long, int) memByteBufferNT1}, but returns {@code null} if {@code address} is {@link #NULL}. */ @Nullable public static ByteBuffer memByteBufferNT1Safe(long address, int maxLength) { return address == NULL ? null : memByteBuffer(address, memLengthNT1(address, maxLength)); } /** * Creates a new direct ByteBuffer that starts at the specified memory address and has capacity equal to the null-terminated string starting at that * address. Two \0 characters will terminate the string. The returned buffer will NOT include the \0 bytes. * *

This method is useful for reading UTF16 encoded text.

* * @param address the starting memory address * * @return the new ByteBuffer */ public static ByteBuffer memByteBufferNT2(long address) { return memByteBufferNT2(address, Integer.MAX_VALUE - 1); } /** * Creates a new direct ByteBuffer that starts at the specified memory address and has capacity equal to the null-terminated string starting at that * address, up to a maximum of {@code maxLength} bytes. Two \0 characters will terminate the string. The returned buffer will NOT include the \0 bytes. * *

This method is useful for reading UTF16 encoded text.

* * @param address the starting memory address * * @return the new ByteBuffer */ public static ByteBuffer memByteBufferNT2(long address, int maxLength) { if (DEBUG) { if ((maxLength & 1) != 0) { throw new IllegalArgumentException("The maximum length must be an even number."); } } return memByteBuffer(address, memLengthNT2(address, maxLength)); } /** Like {@link #memByteBufferNT2(long) memByteBufferNT2}, but returns {@code null} if {@code address} is {@link #NULL}. */ @Nullable public static ByteBuffer memByteBufferNT2Safe(long address) { return address == NULL ? null : memByteBufferNT2(address, Integer.MAX_VALUE - 1); } /** Like {@link #memByteBufferNT2(long, int) memByteBufferNT2}, but returns {@code null} if {@code address} is {@link #NULL}. */ @Nullable public static ByteBuffer memByteBufferNT2Safe(long address, int maxLength) { return address == NULL ? null : memByteBufferNT2(address, maxLength); } /** * Converts the null-terminated ASCII encoded string at the specified memory address to a {@link String}. * * @param address the string memory address * * @return the decoded {@link String} */ public static String memASCII(long address) { return memASCII(address, memLengthNT1(address, Integer.MAX_VALUE)); } /** * Converts the ASCII encoded string at the specified memory address to a {@link String}. * * @param address the string memory address * @param length the number of bytes to decode * * @return the decoded {@link String} */ @SuppressWarnings("deprecation") private static String memASCII(long address, int length) { if (length <= 0) { return ""; } byte[] ascii = length <= ARRAY_TLC_SIZE ? ARRAY_TLC_BYTE.get() : new byte[length]; memByteBuffer(address, length).get(ascii, 0, length); return new String(ascii, 0, 0, length); } /** * Decodes the bytes with index {@code [position(), position()+remaining()}) in {@code buffer}, as an ASCII string. * *

The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.

* * @param buffer the {@link ByteBuffer} to decode * * @return the decoded {@link String} */ public static String memASCII(ByteBuffer buffer) { return memASCII(memAddress(buffer), buffer.remaining()); } /** Like {@link #memASCII(long) memASCII}, but returns {@code null} if {@code address} is {@link #NULL}. */ @Nullable public static String memASCIISafe(long address) { return address == NULL ? null : memASCII(address, memLengthNT1(address, Integer.MAX_VALUE)); } /** Like {@link #memASCII(long, int) memASCII}, but returns {@code null} if {@code address} is {@link #NULL}. */ @Nullable public static String memASCIISafe(long address, int length) { return address == NULL ? null : memASCII(address, length); } /** Like {@link #memASCII(ByteBuffer) memASCII}, but returns {@code null} if {@code buffer} is {@code null}. */ @Nullable public static String memASCIISafe(@Nullable ByteBuffer buffer) { return buffer == null ? null : memASCII(memAddress(buffer), buffer.remaining()); } /** * Decodes the bytes with index {@code [position(), position()+length}) in {@code buffer}, as an ASCII string. * *

The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.

* * @param buffer the {@link ByteBuffer} to decode * @param length the number of bytes to decode * * @return the decoded {@link String} */ public static String memASCII(ByteBuffer buffer, int length) { return memASCII(memAddress(buffer), length); } /** * Decodes the bytes with index {@code [offset, offset+length}) in {@code buffer}, as an ASCII string. * *

The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.

* * @param buffer the {@link ByteBuffer} to decode * @param length the number of bytes to decode * @param offset the offset at which to start decoding. * * @return the decoded {@link String} */ public static String memASCII(ByteBuffer buffer, int length, int offset) { return memASCII(memAddress(buffer, offset), length); } /** * Converts the null-terminated UTF-8 encoded string at the specified memory address to a {@link String}. * * @param address the string memory address * * @return the decoded {@link String} */ public static String memUTF8(long address) { return MultiReleaseTextDecoding.decodeUTF8(address, memLengthNT1(address, Integer.MAX_VALUE)); } /** * Converts the UTF-8 encoded string at the specified memory address to a {@link String}. * * @param address the string memory address * @param length the number of bytes to decode * * @return the decoded {@link String} */ public static String memUTF8(long address, int length) { return MultiReleaseTextDecoding.decodeUTF8(address, length); } /** * Decodes the bytes with index {@code [position(), position()+remaining()}) in {@code buffer}, as a UTF-8 string. * *

The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.

* * @param buffer the {@link ByteBuffer} to decode * * @return the decoded {@link String} */ public static String memUTF8(ByteBuffer buffer) { return MultiReleaseTextDecoding.decodeUTF8(memAddress(buffer), buffer.remaining()); } /** Like {@link #memUTF8(long) memUTF8}, but returns {@code null} if {@code address} is {@link #NULL}. */ @Nullable public static String memUTF8Safe(long address) { return address == NULL ? null : MultiReleaseTextDecoding.decodeUTF8(address, memLengthNT1(address, Integer.MAX_VALUE)); } /** Like {@link #memUTF8(long, int) memUTF8}, but returns {@code null} if {@code address} is {@link #NULL}. */ @Nullable public static String memUTF8Safe(long address, int length) { return address == NULL ? null : MultiReleaseTextDecoding.decodeUTF8(address, length); } /** Like {@link #memUTF8(ByteBuffer) memUTF8}, but returns {@code null} if {@code buffer} is {@code null}. */ @Nullable public static String memUTF8Safe(@Nullable ByteBuffer buffer) { return buffer == null ? null : MultiReleaseTextDecoding.decodeUTF8(memAddress(buffer), buffer.remaining()); } /** * Decodes the bytes with index {@code [position(), position()+length}) in {@code buffer}, as a UTF-8 string. * *

The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.

* * @param buffer the {@link ByteBuffer} to decode * @param length the number of bytes to decode * * @return the decoded {@link String} */ public static String memUTF8(ByteBuffer buffer, int length) { return MultiReleaseTextDecoding.decodeUTF8(memAddress(buffer), length); } /** * Decodes the bytes with index {@code [offset, offset+length}) in {@code buffer}, as a UTF-8 string. * *

The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.

* * @param buffer the {@link ByteBuffer} to decode * @param length the number of bytes to decode * @param offset the offset at which to start decoding. * * @return the decoded {@link String} */ public static String memUTF8(ByteBuffer buffer, int length, int offset) { return MultiReleaseTextDecoding.decodeUTF8(memAddress(buffer, offset), length); } /** * Converts the null-terminated UTF-16 encoded string at the specified memory address to a {@link String}. * * @param address the string memory address * * @return the decoded {@link String} */ public static String memUTF16(long address) { return memUTF16(address, memLengthNT2(address, Integer.MAX_VALUE - 1) >> 1); } /** * Converts the UTF-16 encoded string at the specified memory address to a {@link String}. * * @param address the string memory address * @param length the number of characters to decode * * @return the decoded {@link String} */ public static String memUTF16(long address, int length) { if (length <= 0) { return ""; } if (DEBUG) { // The implementation below does no codepoint validation. int len = length << 1; byte[] bytes = len <= ARRAY_TLC_SIZE ? ARRAY_TLC_BYTE.get() : new byte[len]; memByteBuffer(address, len).get(bytes, 0, len); return new String(bytes, 0, len, UTF16); } char[] chars = length <= ARRAY_TLC_SIZE ? ARRAY_TLC_CHAR.get() : new char[length]; memCharBuffer(address, length).get(chars, 0, length); return new String(chars, 0, length); } /** * Decodes the bytes with index {@code [position(), position()+remaining()}) in {@code buffer}, as a UTF-16 string. * *

The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.

* * @param buffer the {@link ByteBuffer} to decode * * @return the decoded {@link String} */ public static String memUTF16(ByteBuffer buffer) { return memUTF16(memAddress(buffer), buffer.remaining() >> 1); } /** Like {@link #memUTF16(long) memUTF16}, but returns {@code null} if {@code address} is {@link #NULL}. */ @Nullable public static String memUTF16Safe(long address) { return address == NULL ? null : memUTF16(address, memLengthNT2(address, Integer.MAX_VALUE - 1) >> 1); } /** Like {@link #memUTF16(long, int) memUTF16}, but returns {@code null} if {@code address} is {@link #NULL}. */ @Nullable public static String memUTF16Safe(long address, int length) { return address == NULL ? null : memUTF16(address, length); } /** Like {@link #memUTF16(ByteBuffer) memUTF16}, but returns {@code null} if {@code buffer} is {@code null}. */ @Nullable public static String memUTF16Safe(@Nullable ByteBuffer buffer) { return buffer == null ? null : memUTF16(memAddress(buffer), buffer.remaining() >> 1); } /** * Decodes the bytes with index {@code [position(), position()+(length*2)}) in {@code buffer}, as a UTF-16 string. * *

The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.

* * @param buffer the {@link ByteBuffer} to decode * @param length the number of characters to decode * * @return the decoded {@link String} */ public static String memUTF16(ByteBuffer buffer, int length) { return memUTF16(memAddress(buffer), length); } /** * Decodes the bytes with index {@code [offset, offset+(length*2)}) in {@code buffer}, as a UTF-16 string. * *

The current {@code position} and {@code limit} of the specified {@code buffer} are not affected by this operation.

* * @param buffer the {@link ByteBuffer} to decode * @param length the number of characters to decode * @param offset the offset at which to start decoding, in bytes. * * @return the decoded {@link String} */ public static String memUTF16(ByteBuffer buffer, int length, int offset) { return memUTF16(memAddress(buffer, offset), length); } // ------------------------------------------------- // ------------------------------------------------- // ------------------------------------------------- private static sun.misc.Unsafe getUnsafeInstance() { java.lang.reflect.Field[] fields = sun.misc.Unsafe.class.getDeclaredFields(); /* Different runtimes use different names for the Unsafe singleton, so we cannot use .getDeclaredField and we scan instead. For example: Oracle: theUnsafe PERC : m_unsafe_instance Android: THE_ONE */ for (java.lang.reflect.Field field : fields) { if (!field.getType().equals(sun.misc.Unsafe.class)) { continue; } int modifiers = field.getModifiers(); if (!(java.lang.reflect.Modifier.isStatic(modifiers) && java.lang.reflect.Modifier.isFinal(modifiers))) { continue; } try { field.setAccessible(true); return (sun.misc.Unsafe)field.get(null); } catch (Exception ignored) { } break; } throw new UnsupportedOperationException("LWJGL requires sun.misc.Unsafe to be available."); } private static long getAddressOffset() { long MAGIC_ADDRESS = 0xDEADBEEF8BADF00DL; if (BITS32) { MAGIC_ADDRESS &= 0xFFFFFFFFL; } ByteBuffer bb = Objects.requireNonNull(NewDirectByteBuffer(MAGIC_ADDRESS, 0)); long offset = 8L; // 8 byte aligned, cannot be at 0 while (true) { if (UNSAFE.getLong(bb, offset) == MAGIC_ADDRESS) { return offset; } offset += 8L; } } private static final int MAGIC_CAPACITY = 0x0D15EA5E; private static final int MAGIC_POSITION = 0x00FACADE; private static long getIntFieldOffset(ByteBuffer bb, int magicValue) { long offset = 4L; // 4 byte aligned, cannot be at 0 while (true) { if (UNSAFE.getInt(bb, offset) == magicValue) { return offset; } offset += 4L; } } private static long getMarkOffset() { ByteBuffer bb = Objects.requireNonNull(NewDirectByteBuffer(1L, 0)); return getIntFieldOffset(bb, -1); } private static long getPositionOffset() { ByteBuffer bb = Objects.requireNonNull(NewDirectByteBuffer(-1L, MAGIC_CAPACITY)); bb.position(MAGIC_POSITION); return getIntFieldOffset(bb, MAGIC_POSITION); } private static long getLimitOffset() { ByteBuffer bb = Objects.requireNonNull(NewDirectByteBuffer(-1L, MAGIC_CAPACITY)); bb.limit(MAGIC_POSITION); return getIntFieldOffset(bb, MAGIC_POSITION); } private static long getCapacityOffset() { ByteBuffer bb = Objects.requireNonNull(NewDirectByteBuffer(-1L, MAGIC_CAPACITY)); bb.limit(0); return getIntFieldOffset(bb, MAGIC_CAPACITY); } private static long getParentOffset(int oopSize, T parent, Function childFactory) { T child = childFactory.apply(parent); long offset = oopSize; // pointer aligned, cannot be at 0 switch (oopSize) { case Integer.BYTES: // 32-bit or 64-bit with compressed oops while (true) { if (UNSAFE.getInt(parent, offset) != UNSAFE.getInt(child, offset)) { return offset; } offset += oopSize; } case Long.BYTES: // 64-bit with uncompressed oops while (true) { if (UNSAFE.getLong(parent, offset) != UNSAFE.getLong(child, offset)) { return offset; } offset += oopSize; } default: throw new IllegalStateException(); } } @SuppressWarnings("unchecked") static T wrap(Class clazz, long address, int capacity) { T buffer; try { buffer = (T)UNSAFE.allocateInstance(clazz); } catch (InstantiationException e) { throw new UnsupportedOperationException(e); } UNSAFE.putLong(buffer, ADDRESS, address); UNSAFE.putInt(buffer, MARK, -1); UNSAFE.putInt(buffer, LIMIT, capacity); UNSAFE.putInt(buffer, CAPACITY, capacity); return buffer; } @SuppressWarnings("unchecked") static T slice(Class clazz, T source, long address, int capacity, long attachmentOffset) { T target; try { target = (T)UNSAFE.allocateInstance(clazz); } catch (InstantiationException e) { throw new UnsupportedOperationException(e); } UNSAFE.putLong(target, ADDRESS, address); UNSAFE.putInt(target, MARK, -1); UNSAFE.putInt(target, LIMIT, capacity); UNSAFE.putInt(target, CAPACITY, capacity); // The JDK stores source here UNSAFE.putObject(target, attachmentOffset, UNSAFE.getObject(source, attachmentOffset)); return target; } @SuppressWarnings("unchecked") static T duplicate(Class clazz, T source, long attachmentOffset) { T target; try { target = (T)UNSAFE.allocateInstance(clazz); } catch (InstantiationException e) { throw new UnsupportedOperationException(e); } UNSAFE.putLong(target, ADDRESS, UNSAFE.getLong(source, ADDRESS)); UNSAFE.putInt(target, MARK, UNSAFE.getInt(source, MARK)); UNSAFE.putInt(target, POSITION, UNSAFE.getInt(source, POSITION)); UNSAFE.putInt(target, LIMIT, UNSAFE.getInt(source, LIMIT)); UNSAFE.putInt(target, CAPACITY, UNSAFE.getInt(source, CAPACITY)); // The JDK stores source here UNSAFE.putObject(target, attachmentOffset, UNSAFE.getObject(source, attachmentOffset)); return target; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy