/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
******************************************************************************/
package com.badlogic.gdx.utils;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;
/**
* Class with static helper methods to increase the speed of array/direct buffer and direct buffer/direct buffer
* transfers
*
* @author mzechner
*/
public class BufferUtils {
static Array<ByteBuffer> unsafeBuffers = new Array<ByteBuffer>();
static int allocatedUnsafe = 0;
/**
* Copies numFloats floats from src starting at offset to dst. Dst is assumed to be a direct {@link Buffer}. The
* method will crash if that is not the case. The position and limit of the buffer are ignored, the copy is placed
* at position 0 in the buffer. After the copying process the position of the buffer is set to 0 and its limit is
* set to numFloats * 4 if it is a ByteBuffer and numFloats if it is a FloatBuffer. In case the Buffer is neither a
* ByteBuffer nor a FloatBuffer the limit is not set. This is an expert method, use at your own risk.
*
* @param src
* the source array
* @param dst
* the destination buffer, has to be a direct Buffer
* @param numFloats
* the number of floats to copy
* @param offset
* the offset in src to start copying from
*/
public static void copy(float[] src, Buffer dst, int numFloats, int offset) {
copyJni(src, dst, numFloats, offset);
dst.position(0);
if (dst instanceof ByteBuffer)
dst.limit(numFloats << 2);
else if (dst instanceof FloatBuffer)
dst.limit(numFloats);
}
/**
* Copies the contents of src to dst, starting from src[srcOffset], copying numElements elements. The {@link Buffer}
* instance's {@link Buffer#position()} is used to define the offset into the Buffer itself. The position will stay
* the same, the limit will be set to position + numElements. <b>The Buffer must be a direct Buffer with native byte
* order. No error checking is performed</b>.
*
* @param src
* the source array.
* @param srcOffset
* the offset into the source array.
* @param dst
* the destination Buffer, its position is used as an offset.
* @param numElements
* the number of elements to copy.
*/
public static void copy(byte[] src, int srcOffset, Buffer dst, int numElements) {
copyJni(src, srcOffset, dst, positionInBytes(dst), numElements);
dst.limit(dst.position() + bytesToElements(dst, numElements));
}
/**
* Copies the contents of src to dst, starting from src[srcOffset], copying numElements elements. The {@link Buffer}
* instance's {@link Buffer#position()} is used to define the offset into the Buffer itself. The position will stay
* the same, the limit will be set to position + numElements. <b>The Buffer must be a direct Buffer with native byte
* order. No error checking is performed</b>.
*
* @param src
* the source array.
* @param srcOffset
* the offset into the source array.
* @param dst
* the destination Buffer, its position is used as an offset.
* @param numElements
* the number of elements to copy.
*/
public static void copy(short[] src, int srcOffset, Buffer dst, int numElements) {
copyJni(src, srcOffset << 1, dst, positionInBytes(dst), numElements << 1);
dst.limit(dst.position() + bytesToElements(dst, numElements << 1));
}
/**
* Copies the contents of src to dst, starting from src[srcOffset], copying numElements elements. The {@link Buffer}
* instance's {@link Buffer#position()} is used to define the offset into the Buffer itself. The position will stay
* the same, the limit will be set to position + numElements. <b>The Buffer must be a direct Buffer with native byte
* order. No error checking is performed</b>.
*
* @param src
* the source array.
* @param srcOffset
* the offset into the source array.
* @param dst
* the destination Buffer, its position is used as an offset.
* @param numElements
* the number of elements to copy.
*/
public static void copy(char[] src, int srcOffset, Buffer dst, int numElements) {
copyJni(src, srcOffset << 1, dst, positionInBytes(dst), numElements << 1);
dst.limit(dst.position() + bytesToElements(dst, numElements << 1));
}
/**
* Copies the contents of src to dst, starting from src[srcOffset], copying numElements elements. The {@link Buffer}
* instance's {@link Buffer#position()} is used to define the offset into the Buffer itself. The position will stay
* the same, the limit will be set to position + numElements. <b>The Buffer must be a direct Buffer with native byte
* order. No error checking is performed</b>.
*
* @param src
* the source array.
* @param srcOffset
* the offset into the source array.
* @param dst
* the destination Buffer, its position is used as an offset.
* @param numElements
* the number of elements to copy.
*/
public static void copy(int[] src, int srcOffset, Buffer dst, int numElements) {
copyJni(src, srcOffset << 2, dst, positionInBytes(dst), numElements << 2);
dst.limit(dst.position() + bytesToElements(dst, numElements << 2));
}
/**
* Copies the contents of src to dst, starting from src[srcOffset], copying numElements elements. The {@link Buffer}
* instance's {@link Buffer#position()} is used to define the offset into the Buffer itself. The position will stay
* the same, the limit will be set to position + numElements. <b>The Buffer must be a direct Buffer with native byte
* order. No error checking is performed</b>.
*
* @param src
* the source array.
* @param srcOffset
* the offset into the source array.
* @param dst
* the destination Buffer, its position is used as an offset.
* @param numElements
* the number of elements to copy.
*/
public static void copy(long[] src, int srcOffset, Buffer dst, int numElements) {
copyJni(src, srcOffset << 3, dst, positionInBytes(dst), numElements << 3);
dst.limit(dst.position() + bytesToElements(dst, numElements << 3));
}
/**
* Copies the contents of src to dst, starting from src[srcOffset], copying numElements elements. The {@link Buffer}
* instance's {@link Buffer#position()} is used to define the offset into the Buffer itself. The position will stay
* the same, the limit will be set to position + numElements. <b>The Buffer must be a direct Buffer with native byte
* order. No error checking is performed</b>.
*
* @param src
* the source array.
* @param srcOffset
* the offset into the source array.
* @param dst
* the destination Buffer, its position is used as an offset.
* @param numElements
* the number of elements to copy.
*/
public static void copy(float[] src, int srcOffset, Buffer dst, int numElements) {
copyJni(src, srcOffset << 2, dst, positionInBytes(dst), numElements << 2);
dst.limit(dst.position() + bytesToElements(dst, numElements << 2));
}
/**
* Copies the contents of src to dst, starting from src[srcOffset], copying numElements elements. The {@link Buffer}
* instance's {@link Buffer#position()} is used to define the offset into the Buffer itself. The position will stay
* the same, the limit will be set to position + numElements. <b>The Buffer must be a direct Buffer with native byte
* order. No error checking is performed</b>.
*
* @param src
* the source array.
* @param srcOffset
* the offset into the source array.
* @param dst
* the destination Buffer, its position is used as an offset.
* @param numElements
* the number of elements to copy.
*/
public static void copy(double[] src, int srcOffset, Buffer dst, int numElements) {
copyJni(src, srcOffset << 3, dst, positionInBytes(dst), numElements << 3);
dst.limit(dst.position() + bytesToElements(dst, numElements << 3));
}
/**
* Copies the contents of src to dst, starting from the current position of src, copying numElements elements (using
* the data type of src, no matter the datatype of dst). The dst {@link Buffer#position()} is used as the writing
* offset. The position of both Buffers will stay the same. The limit of the src Buffer will stay the same. The
* limit of the dst Buffer will be set to dst.position() + numElements, where numElements are translated to the
* number of elements appropriate for the dst Buffer data type. <b>The Buffers must be direct Buffers with native
* byte order. No error checking is performed</b>.
*
* @param src
* the source Buffer.
* @param dst
* the destination Buffer.
* @param numElements
* the number of elements to copy.
*/
public static void copy(Buffer src, Buffer dst, int numElements) {
int numBytes = elementsToBytes(src, numElements);
copyJni(src, positionInBytes(src), dst, positionInBytes(dst), numBytes);
dst.limit(dst.position() + bytesToElements(dst, numBytes));
}
private static int positionInBytes(Buffer dst) {
if (dst instanceof ByteBuffer)
return dst.position();
else if (dst instanceof ShortBuffer)
return dst.position() << 1;
else if (dst instanceof CharBuffer)
return dst.position() << 1;
else if (dst instanceof IntBuffer)
return dst.position() << 2;
else if (dst instanceof LongBuffer)
return dst.position() << 3;
else if (dst instanceof FloatBuffer)
return dst.position() << 2;
else if (dst instanceof DoubleBuffer)
return dst.position() << 3;
else
throw new GdxRuntimeException("Can't copy to a " + dst.getClass().getName() + " instance");
}
private static int bytesToElements(Buffer dst, int bytes) {
if (dst instanceof ByteBuffer)
return bytes;
else if (dst instanceof ShortBuffer)
return bytes >>> 1;
else if (dst instanceof CharBuffer)
return bytes >>> 1;
else if (dst instanceof IntBuffer)
return bytes >>> 2;
else if (dst instanceof LongBuffer)
return bytes >>> 3;
else if (dst instanceof FloatBuffer)
return bytes >>> 2;
else if (dst instanceof DoubleBuffer)
return bytes >>> 3;
else
throw new GdxRuntimeException("Can't copy to a " + dst.getClass().getName() + " instance");
}
private static int elementsToBytes(Buffer dst, int elements) {
if (dst instanceof ByteBuffer)
return elements;
else if (dst instanceof ShortBuffer)
return elements << 1;
else if (dst instanceof CharBuffer)
return elements << 1;
else if (dst instanceof IntBuffer)
return elements << 2;
else if (dst instanceof LongBuffer)
return elements << 3;
else if (dst instanceof FloatBuffer)
return elements << 2;
else if (dst instanceof DoubleBuffer)
return elements << 3;
else
throw new GdxRuntimeException("Can't copy to a " + dst.getClass().getName() + " instance");
}
public static FloatBuffer newFloatBuffer(int numFloats) {
ByteBuffer buffer = ByteBuffer.allocateDirect(numFloats * 4);
buffer.order(ByteOrder.nativeOrder());
return buffer.asFloatBuffer();
}
public static DoubleBuffer newDoubleBuffer(int numDoubles) {
ByteBuffer buffer = ByteBuffer.allocateDirect(numDoubles * 8);
buffer.order(ByteOrder.nativeOrder());
return buffer.asDoubleBuffer();
}
public static ByteBuffer newByteBuffer(int numBytes) {
ByteBuffer buffer = ByteBuffer.allocateDirect(numBytes);
buffer.order(ByteOrder.nativeOrder());
return buffer;
}
public static ShortBuffer newShortBuffer(int numShorts) {
ByteBuffer buffer = ByteBuffer.allocateDirect(numShorts * 2);
buffer.order(ByteOrder.nativeOrder());
return buffer.asShortBuffer();
}
public static CharBuffer newCharBuffer(int numChars) {
ByteBuffer buffer = ByteBuffer.allocateDirect(numChars * 2);
buffer.order(ByteOrder.nativeOrder());
return buffer.asCharBuffer();
}
public static IntBuffer newIntBuffer(int numInts) {
ByteBuffer buffer = ByteBuffer.allocateDirect(numInts * 4);
buffer.order(ByteOrder.nativeOrder());
return buffer.asIntBuffer();
}
public static LongBuffer newLongBuffer(int numLongs) {
ByteBuffer buffer = ByteBuffer.allocateDirect(numLongs * 8);
buffer.order(ByteOrder.nativeOrder());
return buffer.asLongBuffer();
}
// @off
/*JNI
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
*/
public static void disposeUnsafeByteBuffer(ByteBuffer buffer) {
int size = buffer.capacity();
synchronized (unsafeBuffers) {
if (!unsafeBuffers.removeValue(buffer, true))
throw new IllegalArgumentException("buffer not allocated with newUnsafeByteBuffer or already disposed");
}
allocatedUnsafe -= size;
freeMemory(buffer);
}
/**
* Allocates a new direct ByteBuffer from native heap memory using the native byte order. Needs to be disposed with
* {@link #freeMemory(ByteBuffer)}.
*
* @param numBytes
*/
public static ByteBuffer newUnsafeByteBuffer(int numBytes) {
ByteBuffer buffer = newDisposableByteBuffer(numBytes);
buffer.order(ByteOrder.nativeOrder());
allocatedUnsafe += numBytes;
synchronized (unsafeBuffers) {
unsafeBuffers.add(buffer);
}
return buffer;
}
/**
* Returns the address of the Buffer, it assumes it is an unsafe buffer.
*
* @param buffer
* The Buffer to ask the address for.
* @return the address of the Buffer.
*/
public static long getUnsafeBufferAddress(Buffer buffer) {
return getBufferAddress(buffer) + buffer.position();
}
/**
* Registers the given ByteBuffer as an unsafe ByteBuffer. The ByteBuffer must have been allocated in native code,
* pointing to a memory region allocated via malloc. Needs to be disposed with {@link #freeMemory(ByteBuffer)}.
*
* @param buffer
* the {@link ByteBuffer} to register
* @return the ByteBuffer passed to the method
*/
public static ByteBuffer newUnsafeByteBuffer(ByteBuffer buffer) {
allocatedUnsafe += buffer.capacity();
synchronized (unsafeBuffers) {
unsafeBuffers.add(buffer);
}
return buffer;
}
/**
* @return the number of bytes allocated with {@link #newUnsafeByteBuffer(int)}
*/
public static int getAllocatedBytesUnsafe() {
return allocatedUnsafe;
}
/**
* Frees the memory allocated for the ByteBuffer. DO NOT USE THIS ON BYTEBUFFERS ALLOCATEd VIA METHODS IN THIS CLASS
* OR ByteBuffer.allocateDirect()! IT WILL EXPLODE!
*/
private static native void freeMemory(ByteBuffer buffer); /*
free(buffer);
*/
private static native ByteBuffer newDisposableByteBuffer(int numBytes); /*
char* ptr = (char*)malloc(numBytes);
return env->NewDirectByteBuffer(ptr, numBytes);
*/
private static native long getBufferAddress(Buffer buffer); /*
return (jlong) buffer;
*/
/** Writes the specified number of zeros to the buffer. This is generally faster than reallocating a new buffer. */
public static native void clear(ByteBuffer buffer, int numBytes); /*
memset(buffer, 0, numBytes);
*/
private native static void copyJni(float[] src, Buffer dst, int numFloats, int offset); /*
memcpy(dst, src + offset, numFloats << 2 );
*/
private native static void copyJni(byte[] src, int srcOffset, Buffer dst, int dstOffset, int numBytes); /*
memcpy(dst + dstOffset, src + srcOffset, numBytes);
*/
private native static void copyJni(char[] src, int srcOffset, Buffer dst, int dstOffset, int numBytes); /*
memcpy(dst + dstOffset, src + srcOffset, numBytes);
*/
private native static void copyJni(short[] src, int srcOffset, Buffer dst, int dstOffset, int numBytes); /*
memcpy(dst + dstOffset, src + srcOffset, numBytes);
*/
private native static void copyJni(int[] src, int srcOffset, Buffer dst, int dstOffset, int numBytes); /*
memcpy(dst + dstOffset, src + srcOffset, numBytes);
*/
private native static void copyJni(long[] src, int srcOffset, Buffer dst, int dstOffset, int numBytes); /*
memcpy(dst + dstOffset, src + srcOffset, numBytes);
*/
private native static void copyJni(float[] src, int srcOffset, Buffer dst, int dstOffset, int numBytes); /*
memcpy(dst + dstOffset, src + srcOffset, numBytes);
*/
private native static void copyJni(double[] src, int srcOffset, Buffer dst, int dstOffset, int numBytes); /*
memcpy(dst + dstOffset, src + srcOffset, numBytes);
*/
private native static void copyJni(Buffer src, int srcOffset, Buffer dst, int dstOffset, int numBytes); /*
memcpy(dst + dstOffset, src + srcOffset, numBytes);
*/
}