diff --git a/jme3-lwjgl3/src/main/java/com/jme3/lwjgl3/utils/APIBuffer.java b/jme3-lwjgl3/src/main/java/com/jme3/lwjgl3/utils/APIBuffer.java index 2f3e0996b..32b169bec 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/lwjgl3/utils/APIBuffer.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/lwjgl3/utils/APIBuffer.java @@ -16,526 +16,715 @@ import org.lwjgl.system.MemoryUtil; import static org.lwjgl.system.MemoryUtil.*; /** - * Helper class for alternative API functions. Instead of the user - * passing their own buffer, thread-local instances of this class - * are used internally instead. + * Helper class for alternative API functions. Instead of the user passing their + * own buffer, thread-local instances of this class are used internally instead. */ public class APIBuffer { - private static final int DEFAULT_CAPACITY = 128; - - private ByteBuffer buffer; - private long address; - - private int offset; - - private int stackDepth; - private int[] stack = new int[4]; - - public APIBuffer() { - buffer = BufferUtils.createByteBuffer(DEFAULT_CAPACITY); - address = memAddress(buffer); - } - - /** Resets the parameter offset to 0. */ - public APIBuffer reset() { - offset = 0; - return this; - } - - /** Pushes the current parameter offset to a stack. */ - public APIBuffer push() { - if ( stackDepth == stack.length ) - stack = Arrays.copyOf(stack, stack.length << 1); - - stack[stackDepth++] = offset; - - // Upward align the current offset to the pointer size. - offset = (offset + (POINTER_SIZE - 1)) & -POINTER_SIZE; - - return this; - } - - /** Restores the last pushed parameter offset. */ - public APIBuffer pop() { - offset = stack[--stackDepth]; - return this; - } - - /** Returns the current parameter offset. */ - public int getOffset() { - return offset; - } - - /** Sets the current parameter offset. */ - public void setOffset(int offset) { - this.offset = offset; - } - - /** Returns the memory address of the internal {@link ByteBuffer}. This address may change after a call to one of the {@code Param()} methods. */ - public long address() { - return address; - } - - /** Returns the memory address of the specified {@code offset}. This address may change after a call to one of the {@code Param()} methods. */ - public long address(int offset) { - return address + offset; - } - - /** - * Returns the memory address of the specified {@code offset} or {@link MemoryUtil#NULL NULL} if the specified {@code value} is null. This address may - * change after a call to one of the {@code Param()} methods. - */ - public long addressSafe(Object value, int offset) { - return value == null ? NULL : address(offset); - } - - /** Returns the {@link ByteBuffer} that backs this {@link APIBuffer}. */ - public ByteBuffer buffer() { - return buffer; - } - - private void ensureCapacity(int capacity) { - if ( capacity <= buffer.capacity() ) - return; - - ByteBuffer resized = BufferUtils.createByteBuffer(mathRoundPoT(capacity)); - - resized.put(buffer); - resized.clear(); - - buffer = resized; - address = memAddress(resized); - } - - // --------------------------------------------------------------------------------------------------------------------- - - private int param(int bytes) { - return param(bytes, bytes); - } - - private int param(int bytes, int alignment) { - // Upward align the current offset to the specified alignment - int param = (offset + (alignment - 1)) & -alignment; - ensureCapacity(offset = param + bytes); - return param; - } - - /** Ensures space for an additional boolean value and returns the address offset. */ - public int booleanParam() { return param(1); } - - /** Ensures space for an additional byte value and returns the address offset. */ - public int byteParam() { return param(1); } - - /** Ensures space for an additional short value and returns the address offset. */ - public int shortParam() { return param(2); } - - /** Ensures space for an additional int value and returns the address offset. */ - public int intParam() { return param(4); } - - /** Ensures space for an additional long value and returns the address offset. */ - public int longParam() { return param(8); } - - /** Ensures space for an additional float value and returns the address offset. */ - public int floatParam() { return param(4); } - - /** Ensures space for an additional double value and returns the address offset. */ - public int doubleParam() { return param(8); } - - /** Ensures space for an additional pointer value and returns the address offset. */ - public int pointerParam() { return param(POINTER_SIZE); } - - /** Ensures space for an additional buffer with the specified size (in bytes) and returns the address offset. */ - public int bufferParam(int size) { return param(size, POINTER_SIZE); } - - // --------------------------------------------------------------------------------------------------------------------- - - /** Ensures space for an additional boolean value, sets the specified value at the allocated offset and returns that offset. */ - public int booleanParam(boolean value) { - int offset = booleanParam(); - buffer.put(offset, value ? (byte)1 : (byte)0); - return offset; - } - - /** Ensures space for an additional byte value, sets the specified value at the allocated offset and returns that offset. */ - public int byteParam(byte value) { - int offset = byteParam(); - buffer.put(offset, value); - return offset; - } - - /** Ensures space for an additional short value, sets the specified value at the allocated offset and returns that offset. */ - public int shortParam(short value) { - int offset = shortParam(); - buffer.putShort(offset, value); - return offset; - } - - /** Ensures space for an additional int value, sets the specified value at the allocated offset and returns that offset. */ - public int intParam(int value) { - int offset = intParam(); - buffer.putInt(offset, value); - return offset; - } - - /** Ensures space for an additional long value, sets the specified value at the allocated offset and returns that offset. */ - public int longParam(long value) { - int offset = longParam(); - buffer.putLong(offset, value); - return offset; - } - - /** Ensures space for an additional float value, sets the specified value at the allocated offset and returns that offset. */ - public int floatParam(float value) { - int offset = floatParam(); - buffer.putFloat(offset, value); - return offset; - } - - /** Ensures space for an additional double value, sets the specified value at the allocated offset and returns that offset. */ - public int doubleParam(double value) { - int offset = doubleParam(); - buffer.putDouble(offset, value); - return offset; - } - - /** Ensures space for an additional pointer value, sets the specified value at the allocated offset and returns that offset. */ - public int pointerParam(long value) { - int offset = pointerParam(); - PointerBuffer.put(buffer, offset, value); - return offset; - } - // ---- - - /** Ensures space for an additional pointer buffer, sets the specified memory addresses and returns the address offset. */ - public int pointerArrayParam(long... pointers) { - int buffersAddress = bufferParam(pointers.length << POINTER_SHIFT); - for ( int i = 0; i < pointers.length; i++ ) - pointerParam(buffersAddress, i, pointers[i]); - - return buffersAddress; - } - - /** Ensures space for an additional pointer buffer, sets the memory addresses of the specified buffers and returns the address offset. */ - public int pointerArrayParam(ByteBuffer... buffers) { - int buffersAddress = bufferParam(buffers.length << POINTER_SHIFT); - for ( int i = 0; i < buffers.length; i++ ) - pointerParam(buffersAddress, i, memAddress(buffers[i])); - - return buffersAddress; - } - - /** - * Ensures space for two additional pointer buffers, sets the memory addresses and remaining bytes of the specified buffers and returns the address - * offset. - */ - public int pointerArrayParamp(ByteBuffer... buffers) { - int buffersAddress = pointerArrayParam(buffers); - - int buffersLengths = bufferParam(buffers.length << POINTER_SHIFT); - for ( int i = 0; i < buffers.length; i++ ) - pointerParam(buffersLengths, i, buffers[i].remaining()); - - return buffersAddress; - } - - // --------------------------------------------------------------------------------------------------------------------- - - /** - * ASCII encodes the specified strings with a null-terminator and ensures space for a buffer filled with the memory addresses of the encoded strings. - * - *

The encoded buffers must be later freed with {@link #pointerArrayFree(int, int)}.

- * - * @return the offset to the memory address buffer - */ - public int pointerArrayParamASCII(CharSequence... strings) { - int buffersAddress = bufferParam(strings.length << POINTER_SHIFT); - for ( int i = 0; i < strings.length; i++ ) { - ByteBuffer buffer = MemoryUtil.memASCII(strings[i]); - - pointerParam(buffersAddress, i, memAddress(buffer)); - } - - return buffersAddress; - } - - /** - * ASCII encodes the specified strings and ensures space for two additional buffers filled with the lengths and memory addresses of the encoded strings, - * respectively. The lengths are 4-bytes integers and the memory address buffer starts immediately after the lengths buffer. - * - *

The encoded buffers must be later freed with {@link #pointerArrayFree(int, int)}.

- * - * @return the offset to the lengths buffer - */ - public int pointerArrayParamASCIIi(CharSequence... strings) { - int buffersAddress = bufferParam(strings.length << POINTER_SHIFT); - int lengthsAddress = bufferParam(strings.length << 2); - - for ( int i = 0; i < strings.length; i++ ) { - ByteBuffer buffer = MemoryUtil.memASCII(strings[i]); - - pointerParam(buffersAddress, i, memAddress(buffer)); - intParam(lengthsAddress, i, buffer.remaining()); - } - - return buffersAddress; - } - - /** - * ASCII encodes the specified strings and ensures space for two additional buffers filled with the lengths and memory addresses of the encoded strings, - * respectively. The lengths are pointer-sized integers and the memory address buffer starts immediately after the lengths buffer. - * - *

The encoded buffers must be later freed with {@link #pointerArrayFree(int, int)}.

- * - * @return the offset to the lengths buffer - */ - public int pointerArrayParamASCIIp(CharSequence... strings) { - int buffersAddress = bufferParam(strings.length << POINTER_SHIFT); - int lengthsAddress = bufferParam(strings.length << POINTER_SHIFT); - - for ( int i = 0; i < strings.length; i++ ) { - ByteBuffer buffer = MemoryUtil.memASCII(strings[i]); - - pointerParam(buffersAddress, i, memAddress(buffer)); - pointerParam(lengthsAddress, i, buffer.remaining()); - } - - return buffersAddress; - } - - /** - * UTF8 encodes the specified strings with a null-terminator and ensures space for a buffer filled with the memory addresses of the encoded strings. - * - *

The encoded buffers must be later freed with {@link #pointerArrayFree(int, int)}.

- * - * @return the offset to the memory address buffer - */ - public int pointerArrayParamUTF8(CharSequence... strings) { - int buffersAddress = bufferParam(strings.length << POINTER_SHIFT); - for ( int i = 0; i < strings.length; i++ ) { - ByteBuffer buffer = MemoryUtil.memUTF8(strings[i]); - - pointerParam(buffersAddress, i, memAddress(buffer)); - } - - return buffersAddress; - } - - /** - * UTF8 encodes the specified strings and ensures space for two additional buffers filled with the lengths and memory addresses of the encoded strings, - * respectively. The lengths are 4-bytes integers and the memory address buffer starts immediately after the lengths buffer. - * - *

The encoded buffers must be later freed with {@link #pointerArrayFree(int, int)}.

- * - * @return the offset to the lengths buffer - */ - public int pointerArrayParamUTF8i(CharSequence... strings) { - int buffersAddress = bufferParam(strings.length << POINTER_SHIFT); - int lengthsAddress = bufferParam(strings.length << 2); - - for ( int i = 0; i < strings.length; i++ ) { - ByteBuffer buffer = MemoryUtil.memUTF8(strings[i]); - - pointerParam(buffersAddress, i, memAddress(buffer)); - intParam(lengthsAddress, i, buffer.remaining()); - } - - return buffersAddress; - } - - /** - * UTF8 encodes the specified strings and ensures space for two additional buffers filled with the lengths and memory addresses of the encoded strings, - * respectively. The lengths are pointer-sized integers and the memory address buffer starts immediately after the lengths buffer. - * - *

The encoded buffers must be later freed with {@link #pointerArrayFree(int, int)}.

- * - * @return the offset to the lengths buffer - */ - public int pointerArrayParamUTF8p(CharSequence... strings) { - int buffersAddress = bufferParam(strings.length << POINTER_SHIFT); - int lengthsAddress = bufferParam(strings.length << POINTER_SHIFT); - - for ( int i = 0; i < strings.length; i++ ) { - ByteBuffer buffer = MemoryUtil.memUTF8(strings[i]); - - pointerParam(buffersAddress, i, memAddress(buffer)); - pointerParam(lengthsAddress, i, buffer.remaining()); - } - - return buffersAddress; - } - - /** - * UTF16 encodes the specified strings with a null-terminator and ensures space for a buffer filled with the memory addresses of the encoded strings. - * - *

The encoded buffers must be later freed with {@link #pointerArrayFree(int, int)}.

- * - * @return the offset to the memory address buffer - */ - public int pointerArrayParamUTF16(CharSequence... strings) { - int buffersAddress = bufferParam(strings.length << POINTER_SHIFT); - for ( int i = 0; i < strings.length; i++ ) { - ByteBuffer buffer = MemoryUtil.memUTF16(strings[i]); - - pointerParam(buffersAddress, i, memAddress(buffer)); - } - - return buffersAddress; - } - - /** - * UTF16 encodes the specified strings and ensures space for two additional buffers filled with the lengths and memory addresses of the encoded strings, - * respectively. The lengths are 4-bytes integers and the memory address buffer starts immediately after the lengths buffer. - * - *

The encoded buffers must be later freed with {@link #pointerArrayFree(int, int)}.

- * - * @return the offset to the lengths buffer - */ - public int pointerArrayParamUTF16i(CharSequence... strings) { - int buffersAddress = bufferParam(strings.length << POINTER_SHIFT); - int lengthsAddress = bufferParam(strings.length << 2); - - for ( int i = 0; i < strings.length; i++ ) { - ByteBuffer buffer = MemoryUtil.memUTF16(strings[i]); - - pointerParam(buffersAddress, i, memAddress(buffer)); - intParam(lengthsAddress, i, buffer.remaining()); - } - - return buffersAddress; - } - - /** - * UTF16 encodes the specified strings and ensures space for two additional buffers filled with the lengths and memory addresses of the encoded strings, - * respectively. The lengths are pointer-sized integers and the memory address buffer starts immediately after the lengths buffer. - * - *

The encoded buffers must be later freed with {@link #pointerArrayFree(int, int)}.

- * - * @return the offset to the lengths buffer - */ - public int pointerArrayParamUTF16p(CharSequence... strings) { - int buffersAddress = bufferParam(strings.length << POINTER_SHIFT); - int lengthsAddress = bufferParam(strings.length << POINTER_SHIFT); - - for ( int i = 0; i < strings.length; i++ ) { - ByteBuffer buffer = MemoryUtil.memUTF16(strings[i]); - - pointerParam(buffersAddress, i, memAddress(buffer)); - pointerParam(lengthsAddress, i, buffer.remaining()); - } - - return buffersAddress; - } - - // --------------------------------------------------------------------------------------------------------------------- - - /** Frees {@code length} memory blocks stored in the APIBuffer, starting at the specified {@code offset}. */ - public void pointerArrayFree(int offset, int length) { - for ( int i = 0; i < length; i++ ) - nmemFree(pointerValue(offset + (i << POINTER_SHIFT))); - } - - // --------------------------------------------------------------------------------------------------------------------- - - /** Sets an int value at the specified index of the int buffer that starts at the specified offset. */ - public void intParam(int offset, int index, int value) { - buffer.putInt(offset + (index << 2), value); - } - - /** Sets a pointer value at the specified index of the pointer buffer that starts at the specified offset. */ - public void pointerParam(int offset, int index, long value) { - PointerBuffer.put(buffer, offset + (index << POINTER_SHIFT), value); - } - - // --------------------------------------------------------------------------------------------------------------------- - - /** Ensures space for the specified string encoded in ASCII, encodes the string at the allocated offset and returns that offset. */ - public int stringParamASCII(CharSequence value, boolean nullTerminated) { - if ( value == null ) - return -1; - - int offset = bufferParam(value.length() + (nullTerminated ? 1 : 0)); - MemoryUtil.memASCII(value, nullTerminated, buffer, offset); - return offset; - } - - /** Ensures space for the specified string encoded in UTF-8, encodes the string at the allocated offset and returns that offset. */ - public int stringParamUTF8(CharSequence value, boolean nullTerminated) { - if ( value == null ) - return -1; - - int encodedLen = MemoryUtil.memLengthUTF8(value, nullTerminated); - int offset = bufferParam(encodedLen); - MemoryUtil.memUTF8(value, nullTerminated, buffer, offset); - return offset; - } - - /** Ensures space for the specified string encoded in UTF-16, encodes the string at the allocated offset and returns that offset. */ - public int stringParamUTF16(CharSequence value, boolean nullTerminated) { - if ( value == null ) - return -1; - - int offset = bufferParam((value.length() + (nullTerminated ? 1 : 0)) << 1); - MemoryUtil.memUTF16(value, nullTerminated, buffer, offset); - return offset; - } - - // --------------------------------------------------------------------------------------------------------------------- - - /** Returns the boolean value at the specified offset. */ - public boolean booleanValue(int offset) { return buffer.get(offset) != 0; } - - /** Returns the boolean value at the specified offset. */ - public byte byteValue(int offset) { return buffer.get(offset); } - - /** Returns the short value at the specified offset. */ - public short shortValue(int offset) { return buffer.getShort(offset); } - - /** Returns the int value at the specified offset. */ - public int intValue(int offset) { return buffer.getInt(offset); } - - /** Returns the long value at the specified offset. */ - public long longValue(int offset) { return buffer.getLong(offset); } - - /** Returns the float value at the specified offset. */ - public float floatValue(int offset) { return buffer.getFloat(offset); } - - /** Returns the double value at the specified offset. */ - public double doubleValue(int offset) { return buffer.getDouble(offset); } - - /** Returns the pointer value at the specified offset. */ - public long pointerValue(int offset) { return PointerBuffer.get(buffer, offset); } - - /** Returns the ASCII string value at the specified byte range. */ - public String stringValueASCII(int offset, int limit) { - buffer.position(offset); - buffer.limit(limit); - try { - return MemoryUtil.memASCII(buffer); - } finally { - buffer.clear(); - } - } - - /** Returns the UTF8 string value at the specified byte range. */ - public String stringValueUTF8(int offset, int limit) { - buffer.position(offset); - buffer.limit(limit); - try { - return MemoryUtil.memUTF8(buffer); - } finally { - buffer.clear(); - } - } - - /** Returns the UTF16 string value at the specified byte range. */ - public String stringValueUTF16(int offset, int limit) { - buffer.position(offset); - buffer.limit(limit); - try { - return MemoryUtil.memUTF16(buffer); - } finally { - buffer.clear(); - } - } -} \ No newline at end of file + private static final int DEFAULT_CAPACITY = 128; + + private ByteBuffer buffer; + private long address; + + private int offset; + + private int stackDepth; + private int[] stack = new int[4]; + + public APIBuffer() { + buffer = BufferUtils.createByteBuffer(DEFAULT_CAPACITY); + address = memAddress(buffer); + } + + /** + * Resets the parameter offset to 0. + */ + public APIBuffer reset() { + offset = 0; + return this; + } + + /** + * Pushes the current parameter offset to a stack. + */ + public APIBuffer push() { + if (stackDepth == stack.length) { + stack = Arrays.copyOf(stack, stack.length << 1); + } + + stack[stackDepth++] = offset; + + // Upward align the current offset to the pointer size. + offset = (offset + (POINTER_SIZE - 1)) & -POINTER_SIZE; + + return this; + } + + /** + * Restores the last pushed parameter offset. + */ + public APIBuffer pop() { + offset = stack[--stackDepth]; + return this; + } + + /** + * Returns the current parameter offset. + */ + public int getOffset() { + return offset; + } + + /** + * Sets the current parameter offset. + */ + public void setOffset(int offset) { + this.offset = offset; + } + + /** + * Returns the memory address of the internal {@link ByteBuffer}. This + * address may change after a call to one of the {@code Param()} + * methods. + */ + public long address() { + return address; + } + + /** + * Returns the memory address of the specified {@code offset}. This address + * may change after a call to one of the {@code Param()} methods. + */ + public long address(int offset) { + return address + offset; + } + + /** + * Returns the memory address of the specified {@code offset} or + * {@link MemoryUtil#NULL NULL} if the specified {@code value} is null. This + * address may change after a call to one of the {@code Param()} + * methods. + */ + public long addressSafe(Object value, int offset) { + return value == null ? NULL : address(offset); + } + + /** + * Returns the {@link ByteBuffer} that backs this {@link APIBuffer}. + */ + public ByteBuffer buffer() { + return buffer; + } + + private void ensureCapacity(int capacity) { + if (capacity <= buffer.capacity()) { + return; + } + + ByteBuffer resized = BufferUtils.createByteBuffer(mathRoundPoT(capacity)); + + resized.put(buffer); + resized.clear(); + + buffer = resized; + address = memAddress(resized); + } + + // --------------------------------------------------------------------------------------------------------------------- + private int param(int bytes) { + return param(bytes, bytes); + } + + private int param(int bytes, int alignment) { + // Upward align the current offset to the specified alignment + int param = (offset + (alignment - 1)) & -alignment; + ensureCapacity(offset = param + bytes); + return param; + } + + /** + * Ensures space for an additional boolean value and returns the address + * offset. + */ + public int booleanParam() { + return param(1); + } + + /** + * Ensures space for an additional byte value and returns the address + * offset. + */ + public int byteParam() { + return param(1); + } + + /** + * Ensures space for an additional short value and returns the address + * offset. + */ + public int shortParam() { + return param(2); + } + + /** + * Ensures space for an additional int value and returns the address offset. + */ + public int intParam() { + return param(4); + } + + /** + * Ensures space for an additional long value and returns the address + * offset. + */ + public int longParam() { + return param(8); + } + + /** + * Ensures space for an additional float value and returns the address + * offset. + */ + public int floatParam() { + return param(4); + } + + /** + * Ensures space for an additional double value and returns the address + * offset. + */ + public int doubleParam() { + return param(8); + } + + /** + * Ensures space for an additional pointer value and returns the address + * offset. + */ + public int pointerParam() { + return param(POINTER_SIZE); + } + + /** + * Ensures space for an additional buffer with the specified size (in bytes) + * and returns the address offset. + */ + public int bufferParam(int size) { + return param(size, POINTER_SIZE); + } + + // --------------------------------------------------------------------------------------------------------------------- + /** + * Ensures space for an additional boolean value, sets the specified value + * at the allocated offset and returns that offset. + */ + public int booleanParam(boolean value) { + int offset = booleanParam(); + buffer.put(offset, value ? (byte) 1 : (byte) 0); + return offset; + } + + /** + * Ensures space for an additional byte value, sets the specified value at + * the allocated offset and returns that offset. + */ + public int byteParam(byte value) { + int offset = byteParam(); + buffer.put(offset, value); + return offset; + } + + /** + * Ensures space for an additional short value, sets the specified value at + * the allocated offset and returns that offset. + */ + public int shortParam(short value) { + int offset = shortParam(); + buffer.putShort(offset, value); + return offset; + } + + /** + * Ensures space for an additional int value, sets the specified value at + * the allocated offset and returns that offset. + */ + public int intParam(int value) { + int offset = intParam(); + buffer.putInt(offset, value); + return offset; + } + + /** + * Ensures space for an additional long value, sets the specified value at + * the allocated offset and returns that offset. + */ + public int longParam(long value) { + int offset = longParam(); + buffer.putLong(offset, value); + return offset; + } + + /** + * Ensures space for an additional float value, sets the specified value at + * the allocated offset and returns that offset. + */ + public int floatParam(float value) { + int offset = floatParam(); + buffer.putFloat(offset, value); + return offset; + } + + /** + * Ensures space for an additional double value, sets the specified value at + * the allocated offset and returns that offset. + */ + public int doubleParam(double value) { + int offset = doubleParam(); + buffer.putDouble(offset, value); + return offset; + } + + /** + * Ensures space for an additional pointer value, sets the specified value + * at the allocated offset and returns that offset. + */ + public int pointerParam(long value) { + int offset = pointerParam(); + PointerBuffer.put(buffer, offset, value); + return offset; + } + // ---- + + /** + * Ensures space for an additional pointer buffer, sets the specified memory + * addresses and returns the address offset. + */ + public int pointerArrayParam(long... pointers) { + int buffersAddress = bufferParam(pointers.length << POINTER_SHIFT); + for (int i = 0; i < pointers.length; i++) { + pointerParam(buffersAddress, i, pointers[i]); + } + + return buffersAddress; + } + + /** + * Ensures space for an additional pointer buffer, sets the memory addresses + * of the specified buffers and returns the address offset. + */ + public int pointerArrayParam(ByteBuffer... buffers) { + int buffersAddress = bufferParam(buffers.length << POINTER_SHIFT); + for (int i = 0; i < buffers.length; i++) { + pointerParam(buffersAddress, i, memAddress(buffers[i])); + } + + return buffersAddress; + } + + /** + * Ensures space for two additional pointer buffers, sets the memory + * addresses and remaining bytes of the specified buffers and returns the + * address offset. + */ + public int pointerArrayParamp(ByteBuffer... buffers) { + int buffersAddress = pointerArrayParam(buffers); + + int buffersLengths = bufferParam(buffers.length << POINTER_SHIFT); + for (int i = 0; i < buffers.length; i++) { + pointerParam(buffersLengths, i, buffers[i].remaining()); + } + + return buffersAddress; + } + + // --------------------------------------------------------------------------------------------------------------------- + /** + * ASCII encodes the specified strings with a null-terminator and ensures + * space for a buffer filled with the memory addresses of the encoded + * strings. + * + *

+ * The encoded buffers must be later freed with + * {@link #pointerArrayFree(int, int)}.

+ * + * @return the offset to the memory address buffer + */ + public int pointerArrayParamASCII(CharSequence... strings) { + int buffersAddress = bufferParam(strings.length << POINTER_SHIFT); + for (int i = 0; i < strings.length; i++) { + ByteBuffer buffer = MemoryUtil.memASCII(strings[i]); + + pointerParam(buffersAddress, i, memAddress(buffer)); + } + + return buffersAddress; + } + + /** + * ASCII encodes the specified strings and ensures space for two additional + * buffers filled with the lengths and memory addresses of the encoded + * strings, respectively. The lengths are 4-bytes integers and the memory + * address buffer starts immediately after the lengths buffer. + * + *

+ * The encoded buffers must be later freed with + * {@link #pointerArrayFree(int, int)}.

+ * + * @return the offset to the lengths buffer + */ + public int pointerArrayParamASCIIi(CharSequence... strings) { + int buffersAddress = bufferParam(strings.length << POINTER_SHIFT); + int lengthsAddress = bufferParam(strings.length << 2); + + for (int i = 0; i < strings.length; i++) { + ByteBuffer buffer = MemoryUtil.memASCII(strings[i]); + + pointerParam(buffersAddress, i, memAddress(buffer)); + intParam(lengthsAddress, i, buffer.remaining()); + } + + return buffersAddress; + } + + /** + * ASCII encodes the specified strings and ensures space for two additional + * buffers filled with the lengths and memory addresses of the encoded + * strings, respectively. The lengths are pointer-sized integers and the + * memory address buffer starts immediately after the lengths buffer. + * + *

+ * The encoded buffers must be later freed with + * {@link #pointerArrayFree(int, int)}.

+ * + * @return the offset to the lengths buffer + */ + public int pointerArrayParamASCIIp(CharSequence... strings) { + int buffersAddress = bufferParam(strings.length << POINTER_SHIFT); + int lengthsAddress = bufferParam(strings.length << POINTER_SHIFT); + + for (int i = 0; i < strings.length; i++) { + ByteBuffer buffer = MemoryUtil.memASCII(strings[i]); + + pointerParam(buffersAddress, i, memAddress(buffer)); + pointerParam(lengthsAddress, i, buffer.remaining()); + } + + return buffersAddress; + } + + /** + * UTF8 encodes the specified strings with a null-terminator and ensures + * space for a buffer filled with the memory addresses of the encoded + * strings. + * + *

+ * The encoded buffers must be later freed with + * {@link #pointerArrayFree(int, int)}.

+ * + * @return the offset to the memory address buffer + */ + public int pointerArrayParamUTF8(CharSequence... strings) { + int buffersAddress = bufferParam(strings.length << POINTER_SHIFT); + for (int i = 0; i < strings.length; i++) { + ByteBuffer buffer = MemoryUtil.memUTF8(strings[i]); + + pointerParam(buffersAddress, i, memAddress(buffer)); + } + + return buffersAddress; + } + + /** + * UTF8 encodes the specified strings and ensures space for two additional + * buffers filled with the lengths and memory addresses of the encoded + * strings, respectively. The lengths are 4-bytes integers and the memory + * address buffer starts immediately after the lengths buffer. + * + *

+ * The encoded buffers must be later freed with + * {@link #pointerArrayFree(int, int)}.

+ * + * @return the offset to the lengths buffer + */ + public int pointerArrayParamUTF8i(CharSequence... strings) { + int buffersAddress = bufferParam(strings.length << POINTER_SHIFT); + int lengthsAddress = bufferParam(strings.length << 2); + + for (int i = 0; i < strings.length; i++) { + ByteBuffer buffer = MemoryUtil.memUTF8(strings[i]); + + pointerParam(buffersAddress, i, memAddress(buffer)); + intParam(lengthsAddress, i, buffer.remaining()); + } + + return buffersAddress; + } + + /** + * UTF8 encodes the specified strings and ensures space for two additional + * buffers filled with the lengths and memory addresses of the encoded + * strings, respectively. The lengths are pointer-sized integers and the + * memory address buffer starts immediately after the lengths buffer. + * + *

+ * The encoded buffers must be later freed with + * {@link #pointerArrayFree(int, int)}.

+ * + * @return the offset to the lengths buffer + */ + public int pointerArrayParamUTF8p(CharSequence... strings) { + int buffersAddress = bufferParam(strings.length << POINTER_SHIFT); + int lengthsAddress = bufferParam(strings.length << POINTER_SHIFT); + + for (int i = 0; i < strings.length; i++) { + ByteBuffer buffer = MemoryUtil.memUTF8(strings[i]); + + pointerParam(buffersAddress, i, memAddress(buffer)); + pointerParam(lengthsAddress, i, buffer.remaining()); + } + + return buffersAddress; + } + + /** + * UTF16 encodes the specified strings with a null-terminator and ensures + * space for a buffer filled with the memory addresses of the encoded + * strings. + * + *

+ * The encoded buffers must be later freed with + * {@link #pointerArrayFree(int, int)}.

+ * + * @return the offset to the memory address buffer + */ + public int pointerArrayParamUTF16(CharSequence... strings) { + int buffersAddress = bufferParam(strings.length << POINTER_SHIFT); + for (int i = 0; i < strings.length; i++) { + ByteBuffer buffer = MemoryUtil.memUTF16(strings[i]); + + pointerParam(buffersAddress, i, memAddress(buffer)); + } + + return buffersAddress; + } + + /** + * UTF16 encodes the specified strings and ensures space for two additional + * buffers filled with the lengths and memory addresses of the encoded + * strings, respectively. The lengths are 4-bytes integers and the memory + * address buffer starts immediately after the lengths buffer. + * + *

+ * The encoded buffers must be later freed with + * {@link #pointerArrayFree(int, int)}.

+ * + * @return the offset to the lengths buffer + */ + public int pointerArrayParamUTF16i(CharSequence... strings) { + int buffersAddress = bufferParam(strings.length << POINTER_SHIFT); + int lengthsAddress = bufferParam(strings.length << 2); + + for (int i = 0; i < strings.length; i++) { + ByteBuffer buffer = MemoryUtil.memUTF16(strings[i]); + + pointerParam(buffersAddress, i, memAddress(buffer)); + intParam(lengthsAddress, i, buffer.remaining()); + } + + return buffersAddress; + } + + /** + * UTF16 encodes the specified strings and ensures space for two additional + * buffers filled with the lengths and memory addresses of the encoded + * strings, respectively. The lengths are pointer-sized integers and the + * memory address buffer starts immediately after the lengths buffer. + * + *

+ * The encoded buffers must be later freed with + * {@link #pointerArrayFree(int, int)}.

+ * + * @return the offset to the lengths buffer + */ + public int pointerArrayParamUTF16p(CharSequence... strings) { + int buffersAddress = bufferParam(strings.length << POINTER_SHIFT); + int lengthsAddress = bufferParam(strings.length << POINTER_SHIFT); + + for (int i = 0; i < strings.length; i++) { + ByteBuffer buffer = MemoryUtil.memUTF16(strings[i]); + + pointerParam(buffersAddress, i, memAddress(buffer)); + pointerParam(lengthsAddress, i, buffer.remaining()); + } + + return buffersAddress; + } + + // --------------------------------------------------------------------------------------------------------------------- + /** + * Frees {@code length} memory blocks stored in the APIBuffer, starting at + * the specified {@code offset}. + */ + public void pointerArrayFree(int offset, int length) { + for (int i = 0; i < length; i++) { + nmemFree(pointerValue(offset + (i << POINTER_SHIFT))); + } + } + + // --------------------------------------------------------------------------------------------------------------------- + /** + * Sets an int value at the specified index of the int buffer that starts at + * the specified offset. + */ + public void intParam(int offset, int index, int value) { + buffer.putInt(offset + (index << 2), value); + } + + /** + * Sets a pointer value at the specified index of the pointer buffer that + * starts at the specified offset. + */ + public void pointerParam(int offset, int index, long value) { + PointerBuffer.put(buffer, offset + (index << POINTER_SHIFT), value); + } + + // --------------------------------------------------------------------------------------------------------------------- + /** + * Ensures space for the specified string encoded in ASCII, encodes the + * string at the allocated offset and returns that offset. + */ + public int stringParamASCII(CharSequence value, boolean nullTerminated) { + if (value == null) { + return -1; + } + + int offset = bufferParam(value.length() + (nullTerminated ? 1 : 0)); + MemoryUtil.memASCII(value, nullTerminated, buffer, offset); + return offset; + } + + /** + * Ensures space for the specified string encoded in UTF-8, encodes the + * string at the allocated offset and returns that offset. + */ + public int stringParamUTF8(CharSequence value, boolean nullTerminated) { + if (value == null) { + return -1; + } + + int encodedLen = MemoryUtil.memLengthUTF8(value, nullTerminated); + int offset = bufferParam(encodedLen); + MemoryUtil.memUTF8(value, nullTerminated, buffer, offset); + return offset; + } + + /** + * Ensures space for the specified string encoded in UTF-16, encodes the + * string at the allocated offset and returns that offset. + */ + public int stringParamUTF16(CharSequence value, boolean nullTerminated) { + if (value == null) { + return -1; + } + + int offset = bufferParam((value.length() + (nullTerminated ? 1 : 0)) << 1); + MemoryUtil.memUTF16(value, nullTerminated, buffer, offset); + return offset; + } + + // --------------------------------------------------------------------------------------------------------------------- + /** + * Returns the boolean value at the specified offset. + */ + public boolean booleanValue(int offset) { + return buffer.get(offset) != 0; + } + + /** + * Returns the boolean value at the specified offset. + */ + public byte byteValue(int offset) { + return buffer.get(offset); + } + + /** + * Returns the short value at the specified offset. + */ + public short shortValue(int offset) { + return buffer.getShort(offset); + } + + /** + * Returns the int value at the specified offset. + */ + public int intValue(int offset) { + return buffer.getInt(offset); + } + + /** + * Returns the long value at the specified offset. + */ + public long longValue(int offset) { + return buffer.getLong(offset); + } + + /** + * Returns the float value at the specified offset. + */ + public float floatValue(int offset) { + return buffer.getFloat(offset); + } + + /** + * Returns the double value at the specified offset. + */ + public double doubleValue(int offset) { + return buffer.getDouble(offset); + } + + /** + * Returns the pointer value at the specified offset. + */ + public long pointerValue(int offset) { + return PointerBuffer.get(buffer, offset); + } + + /** + * Returns the ASCII string value at the specified byte range. + */ + public String stringValueASCII(int offset, int limit) { + buffer.position(offset); + buffer.limit(limit); + try { + return MemoryUtil.memASCII(buffer); + } finally { + buffer.clear(); + } + } + + /** + * Returns the UTF8 string value at the specified byte range. + */ + public String stringValueUTF8(int offset, int limit) { + buffer.position(offset); + buffer.limit(limit); + try { + return MemoryUtil.memUTF8(buffer); + } finally { + buffer.clear(); + } + } + + /** + * Returns the UTF16 string value at the specified byte range. + */ + public String stringValueUTF16(int offset, int limit) { + buffer.position(offset); + buffer.limit(limit); + try { + return MemoryUtil.memUTF16(buffer); + } finally { + buffer.clear(); + } + } +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/lwjgl3/utils/APIUtil.java b/jme3-lwjgl3/src/main/java/com/jme3/lwjgl3/utils/APIUtil.java index 871db1ffa..689659e3b 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/lwjgl3/utils/APIUtil.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/lwjgl3/utils/APIUtil.java @@ -4,12 +4,6 @@ */ package com.jme3.lwjgl3.utils; -import com.jme3.lwjgl3.utils.APIBuffer; -import org.lwjgl.system.linux.LinuxLibrary; -import org.lwjgl.system.macosx.MacOSXLibrary; -import org.lwjgl.system.windows.WindowsLibrary; - -import java.io.PrintStream; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.HashMap; @@ -20,173 +14,205 @@ import java.util.regex.Pattern; /** * Utility class useful to API bindings. [INTERNAL USE ONLY] * - *

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

+ *

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

*/ public final class APIUtil { - private static final ThreadLocal API_BUFFERS = new ThreadLocal() { - @Override - protected APIBuffer initialValue() { - return new APIBuffer(); - } - }; - - private APIUtil() { - } - - - /** Returns a thread-local {@link APIBuffer} that has been reset. */ - public static APIBuffer apiBuffer() { - return API_BUFFERS.get().reset(); - } - - /** - * Returns a thread-local {@link APIBuffer}, without resetting it. This makes the APIBuffer work like a stack when used in nested API calls. The user is - * responsible for resetting the {@link APIBuffer} to an appropriate state before the nested call returns. - * - * @see APIBuffer#pop - */ - public static APIBuffer apiStack() { - return API_BUFFERS.get().push(); - } - - /** A data class for API versioning information. */ - public static class APIVersion { - - /** Returns the API major version. */ - public final int major; - /** Returns the API minor version. */ - public final int minor; - - /** Returns the API revision. May be null. */ - public final String revision; - /** Returns the API implementation-specific versioning information. May be null. */ - public final String implementation; - - public APIVersion(int major, int minor) { - this(major, minor, null, null); - } - - public APIVersion(int major, int minor, String revision, String implementation) { - this.major = major; - this.minor = minor; - this.revision = revision; - this.implementation = implementation; - } - - } - - /** - * Parses a version string. The version string must have the format {@code MAJOR.MINOR.REVISION IMPL}, where {@code MAJOR} is the major version (integer), - * {@code MINOR} is the minor version (integer), {@code REVISION} is the revision version (string, optional) and {@code IMPL} is implementation-specific - * information (string, optional). - * - * @param version the API version string - * - * @return the parsed {@link APIVersion} - */ - public static APIVersion apiParseVersion(String version) { - return apiParseVersion(version, null); - } - - /** - * Parses a version string. The version string must have the format {@code PREFIX MAJOR.MINOR.REVISION IMPL}, where {@code PREFIX} is the specified prefix - * (string, optional), {@code MAJOR} is the major version (integer), {@code MINOR} is the minor version (integer), {@code REVISION} is the revision version - * (string, optional) and {@code IMPL} is implementation-specific information (string, optional). - * - * @param version the version string - * @param prefix the version string prefix, may be null - * - * @return the parsed {@link APIVersion} - */ - public static APIVersion apiParseVersion(String version, String prefix) { - String pattern = "([0-9]+)[.]([0-9]+)([.]\\S+)?\\s*(.+)?"; - if ( prefix != null ) - pattern = prefix + "\\s+" + pattern; - - Matcher matcher = Pattern.compile(pattern).matcher(version); - if ( !matcher.matches() ) - throw new IllegalArgumentException(String.format("Malformed API version string [%s]", version)); - - return new APIVersion( - Integer.parseInt(matcher.group(1)), - Integer.parseInt(matcher.group(2)), - matcher.group(3), - matcher.group(4) - ); - } - - public static String apiUnknownToken(int token) { - return apiUnknownToken("Unknown", token); - } - - public static String apiUnknownToken(String description, int token) { - return String.format("%s [0x%X]", description, token); - } - - /** - * Returns a map of public static final integer fields in the specified classes, to their String representations. An optional filter can be specified to - * only include specific fields. The target map may be null, in which case a new map is allocated and returned. - * - *

This method is useful when debugging to quickly identify values returned from an API.

- * - * @param filter the filter to use (optional) - * @param target the target map (optional) - * @param tokenClasses the classes to get tokens from - * - * @return the token map - */ - public static Map apiClassTokens(TokenFilter filter, Map target, Class... tokenClasses) { - if ( target == null ) - target = new HashMap(64); - - int TOKEN_MODIFIERS = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL; - - for ( Class tokenClass : tokenClasses ) { - if ( tokenClass == null ) - continue; - - for ( Field field : tokenClass.getDeclaredFields() ) { - // Get only fields. - if ( (field.getModifiers() & TOKEN_MODIFIERS) == TOKEN_MODIFIERS && field.getType() == int.class ) { - try { - int value = field.getInt(null); - if ( filter != null && !filter.accept(field, value) ) - continue; - - String name = target.get(value); - target.put(value, name == null ? field.getName() : name + "|" + field.getName()); - } catch (IllegalAccessException e) { - // Ignore - } - } - } - } - - return target; - } - - public static Class apiOptionalClass(String className) { - try { - return Class.forName(className); - } catch (ClassNotFoundException e) { - return null; - } - } - - /** Simple interface for Field filtering. */ - public interface TokenFilter { - - /** - * Should return true if the specified Field passes the filter. - * - * @param field the Field to test - * @param value the integer value of the field - * - * @return true if the Field is accepted - */ - boolean accept(Field field, int value); - - } - -} \ No newline at end of file + private static final ThreadLocal API_BUFFERS = new ThreadLocal() { + @Override + protected APIBuffer initialValue() { + return new APIBuffer(); + } + }; + + private APIUtil() { + } + + /** + * Returns a thread-local {@link APIBuffer} that has been reset. + */ + public static APIBuffer apiBuffer() { + return API_BUFFERS.get().reset(); + } + + /** + * Returns a thread-local {@link APIBuffer}, without resetting it. This + * makes the APIBuffer work like a stack when used in nested API calls. The + * user is responsible for resetting the {@link APIBuffer} to an appropriate + * state before the nested call returns. + * + * @see APIBuffer#pop + */ + public static APIBuffer apiStack() { + return API_BUFFERS.get().push(); + } + + /** + * A data class for API versioning information. + */ + public static class APIVersion { + + /** + * Returns the API major version. + */ + public final int major; + /** + * Returns the API minor version. + */ + public final int minor; + + /** + * Returns the API revision. May be null. + */ + public final String revision; + /** + * Returns the API implementation-specific versioning information. May + * be null. + */ + public final String implementation; + + public APIVersion(int major, int minor) { + this(major, minor, null, null); + } + + public APIVersion(int major, int minor, String revision, String implementation) { + this.major = major; + this.minor = minor; + this.revision = revision; + this.implementation = implementation; + } + + } + + /** + * Parses a version string. The version string must have the format + * {@code MAJOR.MINOR.REVISION IMPL}, where {@code MAJOR} is the major + * version (integer), {@code MINOR} is the minor version (integer), + * {@code REVISION} is the revision version (string, optional) and + * {@code IMPL} is implementation-specific information (string, optional). + * + * @param version the API version string + * + * @return the parsed {@link APIVersion} + */ + public static APIVersion apiParseVersion(String version) { + return apiParseVersion(version, null); + } + + /** + * Parses a version string. The version string must have the format + * {@code PREFIX MAJOR.MINOR.REVISION IMPL}, where {@code PREFIX} is the + * specified prefix (string, optional), {@code MAJOR} is the major version + * (integer), {@code MINOR} is the minor version (integer), {@code REVISION} + * is the revision version (string, optional) and {@code IMPL} is + * implementation-specific information (string, optional). + * + * @param version the version string + * @param prefix the version string prefix, may be null + * + * @return the parsed {@link APIVersion} + */ + public static APIVersion apiParseVersion(String version, String prefix) { + String pattern = "([0-9]+)[.]([0-9]+)([.]\\S+)?\\s*(.+)?"; + if (prefix != null) { + pattern = prefix + "\\s+" + pattern; + } + + Matcher matcher = Pattern.compile(pattern).matcher(version); + if (!matcher.matches()) { + throw new IllegalArgumentException(String.format("Malformed API version string [%s]", version)); + } + + return new APIVersion( + Integer.parseInt(matcher.group(1)), + Integer.parseInt(matcher.group(2)), + matcher.group(3), + matcher.group(4) + ); + } + + public static String apiUnknownToken(int token) { + return apiUnknownToken("Unknown", token); + } + + public static String apiUnknownToken(String description, int token) { + return String.format("%s [0x%X]", description, token); + } + + /** + * Returns a map of public static final integer fields in the specified + * classes, to their String representations. An optional filter can be + * specified to only include specific fields. The target map may be null, in + * which case a new map is allocated and returned. + * + *

+ * This method is useful when debugging to quickly identify values returned + * from an API.

+ * + * @param filter the filter to use (optional) + * @param target the target map (optional) + * @param tokenClasses the classes to get tokens from + * + * @return the token map + */ + public static Map apiClassTokens(TokenFilter filter, Map target, Class... tokenClasses) { + if (target == null) { + target = new HashMap(64); + } + + int TOKEN_MODIFIERS = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL; + + for (Class tokenClass : tokenClasses) { + if (tokenClass == null) { + continue; + } + + for (Field field : tokenClass.getDeclaredFields()) { + // Get only fields. + if ((field.getModifiers() & TOKEN_MODIFIERS) == TOKEN_MODIFIERS && field.getType() == int.class) { + try { + int value = field.getInt(null); + if (filter != null && !filter.accept(field, value)) { + continue; + } + + String name = target.get(value); + target.put(value, name == null ? field.getName() : name + "|" + field.getName()); + } catch (IllegalAccessException e) { + // Ignore + } + } + } + } + + return target; + } + + public static Class apiOptionalClass(String className) { + try { + return Class.forName(className); + } catch (ClassNotFoundException e) { + return null; + } + } + + /** + * Simple interface for Field filtering. + */ + public interface TokenFilter { + + /** + * Should return true if the specified Field passes the filter. + * + * @param field the Field to test + * @param value the integer value of the field + * + * @return true if the Field is accepted + */ + boolean accept(Field field, int value); + + } + +}