Initial release of mapped objects library (v0.10) in LWJGL.
This commit is contained in:
parent
8e7e212161
commit
7e244edc2b
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Created on Jun 24, 2011
|
||||
*/
|
||||
|
||||
package org.lwjgl.util.mapped;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* @author Riven
|
||||
*/
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MappedField {
|
||||
long byteOffset();
|
||||
|
||||
long byteLength() default -1;
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Created on Jul 4, 2011
|
||||
*/
|
||||
|
||||
package org.lwjgl.util.mapped;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* @author Riven
|
||||
*/
|
||||
|
||||
public class MappedForeach<T extends MappedObject> implements Iterable<T>
|
||||
{
|
||||
final T mapped;
|
||||
final int elementCount;
|
||||
|
||||
MappedForeach(T mapped, int elementCount)
|
||||
{
|
||||
this.mapped = mapped;
|
||||
this.elementCount = elementCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator()
|
||||
{
|
||||
return new Iterator<T>()
|
||||
{
|
||||
private int index = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
return this.index < (MappedForeach.this.elementCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next()
|
||||
{
|
||||
mapped.viewAddress = mapped.baseAddress + (this.index++) * mapped.stride;
|
||||
|
||||
return mapped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove()
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* Created on Jun 28, 2011
|
||||
*/
|
||||
|
||||
package org.lwjgl.util.mapped;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author Riven
|
||||
*/
|
||||
|
||||
public class MappedHelper
|
||||
{
|
||||
public static void setup(MappedObject mo, ByteBuffer buffer, int align, int sizeof)
|
||||
{
|
||||
if (mo.baseAddress != 0L)
|
||||
throw new IllegalStateException("this method should not be called by user-code");
|
||||
|
||||
if (buffer == null)
|
||||
throw new NullPointerException("buffer");
|
||||
if (!buffer.isDirect())
|
||||
throw new IllegalArgumentException("bytebuffer must be direct");
|
||||
mo.preventGC = buffer;
|
||||
|
||||
if (align <= 0)
|
||||
throw new IllegalArgumentException("invalid alignment");
|
||||
mo.align = align;
|
||||
|
||||
if (sizeof % align != 0)
|
||||
throw new IllegalStateException("sizeof not a multiple of alignment");
|
||||
mo.stride = sizeof;
|
||||
|
||||
long addr = MappedObjectUnsafe.getBufferBaseAddress(buffer);
|
||||
if (addr % align != 0)
|
||||
throw new IllegalStateException("buffer address not aligned on " + align + " bytes");
|
||||
|
||||
mo.baseAddress = mo.viewAddress = addr;
|
||||
}
|
||||
|
||||
public static void put_views(MappedSet2 set, int view)
|
||||
{
|
||||
set.view(view);
|
||||
}
|
||||
|
||||
public static void put_views(MappedSet3 set, int view)
|
||||
{
|
||||
set.view(view);
|
||||
}
|
||||
|
||||
public static void put_views(MappedSet4 set, int view)
|
||||
{
|
||||
set.view(view);
|
||||
}
|
||||
|
||||
public static void put_view(MappedObject mapped, int view)
|
||||
{
|
||||
mapped.viewAddress = mapped.baseAddress + view * mapped.stride;
|
||||
}
|
||||
|
||||
public static int get_view(MappedObject mapped)
|
||||
{
|
||||
return (int) (mapped.viewAddress - mapped.baseAddress) / mapped.stride;
|
||||
}
|
||||
|
||||
public static MappedObject dup(MappedObject src, MappedObject dst)
|
||||
{
|
||||
dst.baseAddress = src.baseAddress;
|
||||
dst.viewAddress = src.viewAddress;
|
||||
dst.stride = src.stride;
|
||||
dst.align = src.align;
|
||||
dst.preventGC = src.preventGC;
|
||||
return dst;
|
||||
}
|
||||
|
||||
public static MappedObject slice(MappedObject src, MappedObject dst)
|
||||
{
|
||||
dst.baseAddress = src.viewAddress; // !
|
||||
dst.viewAddress = src.viewAddress;
|
||||
dst.stride = src.stride;
|
||||
dst.align = src.align;
|
||||
dst.preventGC = src.preventGC;
|
||||
return dst;
|
||||
}
|
||||
|
||||
public static void copy(MappedObject src, MappedObject dst, int bytes)
|
||||
{
|
||||
MappedObjectUnsafe.INSTANCE.copyMemory(src.viewAddress, dst.viewAddress, bytes);
|
||||
}
|
||||
|
||||
public static ByteBuffer newBuffer(long address, int capacity)
|
||||
{
|
||||
return MappedObjectUnsafe.newBuffer(address, capacity);
|
||||
}
|
||||
|
||||
// ---- primitive fields read/write
|
||||
|
||||
// byte
|
||||
|
||||
public static void bput(byte value, long addr)
|
||||
{
|
||||
MappedObjectUnsafe.INSTANCE.putByte(addr, value);
|
||||
}
|
||||
|
||||
public static byte bget(long addr)
|
||||
{
|
||||
return MappedObjectUnsafe.INSTANCE.getByte(addr);
|
||||
}
|
||||
|
||||
// short
|
||||
|
||||
public static void sput(short value, long addr)
|
||||
{
|
||||
MappedObjectUnsafe.INSTANCE.putShort(addr, value);
|
||||
}
|
||||
|
||||
public static short sget(long addr)
|
||||
{
|
||||
return MappedObjectUnsafe.INSTANCE.getShort(addr);
|
||||
}
|
||||
|
||||
// char
|
||||
|
||||
public static void cput(char value, long addr)
|
||||
{
|
||||
MappedObjectUnsafe.INSTANCE.putChar(addr, value);
|
||||
}
|
||||
|
||||
public static char cget(long addr)
|
||||
{
|
||||
return MappedObjectUnsafe.INSTANCE.getChar(addr);
|
||||
}
|
||||
|
||||
// int
|
||||
|
||||
public static void iput(int value, long addr)
|
||||
{
|
||||
MappedObjectUnsafe.INSTANCE.putInt(addr, value);
|
||||
}
|
||||
|
||||
public static int iget(long addr)
|
||||
{
|
||||
return MappedObjectUnsafe.INSTANCE.getInt(addr);
|
||||
}
|
||||
|
||||
// float
|
||||
|
||||
public static void fput(float value, long addr)
|
||||
{
|
||||
MappedObjectUnsafe.INSTANCE.putFloat(addr, value);
|
||||
}
|
||||
|
||||
public static float fget(long addr)
|
||||
{
|
||||
return MappedObjectUnsafe.INSTANCE.getFloat(addr);
|
||||
}
|
||||
|
||||
// long
|
||||
|
||||
public static void jput(long value, long addr)
|
||||
{
|
||||
MappedObjectUnsafe.INSTANCE.putLong(addr, value);
|
||||
}
|
||||
|
||||
public static long jget(long addr)
|
||||
{
|
||||
return MappedObjectUnsafe.INSTANCE.getLong(addr);
|
||||
}
|
||||
|
||||
// double
|
||||
|
||||
public static void dput(double value, long addr)
|
||||
{
|
||||
MappedObjectUnsafe.INSTANCE.putDouble(addr, value);
|
||||
}
|
||||
|
||||
public static double dget(long addr)
|
||||
{
|
||||
return MappedObjectUnsafe.INSTANCE.getDouble(addr);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Created on Jun 25, 2011
|
||||
*/
|
||||
|
||||
package org.lwjgl.util.mapped;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author Riven
|
||||
*/
|
||||
|
||||
public class MappedObject
|
||||
{
|
||||
public MappedObject()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
// these fields are not assignable/writable by user-code
|
||||
public long baseAddress;
|
||||
public long viewAddress;
|
||||
public int stride;
|
||||
public int align;
|
||||
|
||||
/**
|
||||
* Holds the value of sizeof of the sub-type of this MappedObject<br>
|
||||
* <br>
|
||||
* The behavior of this (transformed) method does not follow the normal Java behavior.<br>
|
||||
* <code>Vec2.SIZEOF</code> will yield 8 (2 floats)<br>
|
||||
* <code>Vec3.SIZEOF</code> will yield 12 (3 floats)<br>
|
||||
* This (required) notation might cause compiler warnings, which can be suppressed with @SuppressWarnings("static-access").<br>
|
||||
* Using Java 5.0's static-import on this method will break functionality.
|
||||
*/
|
||||
|
||||
// any method that calls these field will have its call-site modified
|
||||
public static int SIZEOF = -1; // 'final' per subtype
|
||||
public int view; // read/write
|
||||
|
||||
public final void next()
|
||||
{
|
||||
this.viewAddress += this.stride;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a MappedObject instance, mapping the memory region of the specified direct ByteBuffer.<br>
|
||||
* <br>
|
||||
* The behavior of this (transformed) method does not follow the normal Java behavior.<br>
|
||||
* <code>Vec2.map(buffer)</code> will return a mapped Vec2 instance.<br>
|
||||
* <code>Vec3.map(buffer)</code> will return a mapped Vec3 instance.<br>
|
||||
* This (required) notation might cause compiler warnings, which can be suppressed with @SuppressWarnings("static-access").<br>
|
||||
* Using Java 5.0's static-import on this method will break functionality.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static <T extends MappedObject> T map(ByteBuffer bb)
|
||||
{
|
||||
// any method that calls this method will have its call-site modified
|
||||
throw new InternalError("type not registered");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static <T extends MappedObject> T map(long address, int capacity)
|
||||
{
|
||||
// any method that calls this method will have its call-site modified
|
||||
throw new InternalError("type not registered");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a MappedObject instance, mapping the memory region of an allocated direct ByteBuffer with a capacity of <code>elementCount*SIZEOF</code>
|
||||
* <br>
|
||||
* The behavior of this (transformed) method does not follow the normal Java behavior.<br>
|
||||
* <code>Vec2.malloc(int)</code> will return a mapped Vec2 instance.<br>
|
||||
* <code>Vec3.malloc(int)</code> will return a mapped Vec3 instance.<br>
|
||||
* This (required) notation might cause compiler warnings, which can be suppressed with @SuppressWarnings("static-access").<br>
|
||||
* Using Java 5.0's static-import on this method will break functionality.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static <T extends MappedObject> T malloc(int elementCount)
|
||||
{
|
||||
// any method that calls this method will have its call-site modified
|
||||
throw new InternalError("type not registered");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an identical new MappedObject instance, comparable to the
|
||||
* contract of ByteBuffer.duplicate()
|
||||
*/
|
||||
|
||||
public final <T extends MappedObject> T dup()
|
||||
{
|
||||
// any method that calls this method will have its call-site modified
|
||||
throw new InternalError("type not registered");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new MappedObject instance, with a base offset equal to
|
||||
* the offset of the current view, comparable to the contract of ByteBuffer.slice()
|
||||
*/
|
||||
|
||||
public final <T extends MappedObject> T slice()
|
||||
{
|
||||
// any method that calls this method will have its call-site modified
|
||||
throw new InternalError("type not registered");
|
||||
}
|
||||
|
||||
public final <T extends MappedObject> void runViewConstructor()
|
||||
{
|
||||
// any method that calls this method will have its call-site modified
|
||||
throw new InternalError("type not registered");
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies and amount of <code>SIZEOF</code> bytes, from the current
|
||||
* mapped object, to the specified mapped object.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final <T extends MappedObject> void copyTo(T target)
|
||||
{
|
||||
// any method that calls this method will have its call-site modified
|
||||
throw new InternalError("type not registered");
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies and amount of <code>SIZEOF*instances<c/ode> bytes, from the
|
||||
* current mapped object, to the specified mapped object.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final <T extends MappedObject> void copyRange(T target, int instances)
|
||||
{
|
||||
// any method that calls this method will have its call-site modified
|
||||
throw new InternalError("type not registered");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Iterable<MappedObject> that will step through
|
||||
* <code>elementCount</code> views, leaving the <code>view</code> at
|
||||
* the last valid value.<br>
|
||||
* <br>
|
||||
* For convenience you are encouraged to static-import this specific method:
|
||||
* <code>import static org.lwjgl.util.mapped.MappedObject.foreach;</code>
|
||||
*/
|
||||
|
||||
public static <T extends MappedObject> MappedForeach<T> foreach(T mapped, int elementCount)
|
||||
{
|
||||
return new MappedForeach<T>(mapped, elementCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a newly initiated mapped object with the specified stride and offset.
|
||||
* @throws IllegalStateException if view is not at index 0
|
||||
*/
|
||||
|
||||
public static final <T extends MappedObject> T configure(T mapped, int stride, int offset)
|
||||
{
|
||||
if (mapped.baseAddress != mapped.viewAddress)
|
||||
throw new IllegalStateException("view must be zero");
|
||||
|
||||
if (offset < 0)
|
||||
throw new IllegalStateException("offset must not be negative: " + offset);
|
||||
if (offset % mapped.align != 0)
|
||||
throw new IllegalStateException("offset not a multiple of alignment: " + offset);
|
||||
|
||||
if (stride < mapped.stride)
|
||||
throw new IllegalStateException("new stride must not be smaller than current stride: " + stride);
|
||||
if (stride % mapped.align != 0)
|
||||
throw new IllegalStateException("stride not a multiple of alignment: " + stride);
|
||||
|
||||
mapped.baseAddress += offset;
|
||||
mapped.viewAddress += offset;
|
||||
mapped.stride = stride;
|
||||
|
||||
return mapped;
|
||||
}
|
||||
|
||||
ByteBuffer preventGC;
|
||||
|
||||
public ByteBuffer backingByteBuffer()
|
||||
{
|
||||
return this.preventGC;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Created on Jun 24, 2011
|
||||
*/
|
||||
|
||||
package org.lwjgl.util.mapped;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author Riven
|
||||
*/
|
||||
|
||||
public class MappedObjectClassLoader extends URLClassLoader
|
||||
{
|
||||
static final String MAPPEDOBJECT_PACKAGE_PREFIX;
|
||||
|
||||
static
|
||||
{
|
||||
MAPPEDOBJECT_PACKAGE_PREFIX = MappedObjectClassLoader.class.getPackage().getName() + ".";
|
||||
}
|
||||
|
||||
static boolean FORKED = false;
|
||||
|
||||
public static boolean fork(Class< ? > mainClass, String[] args)
|
||||
{
|
||||
if (FORKED)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FORKED = true;
|
||||
|
||||
try
|
||||
{
|
||||
URLClassLoader loader = new MappedObjectClassLoader(mainClass);
|
||||
Class< ? > replacedMainClass = loader.loadClass(mainClass.getName());
|
||||
Method mainMethod = replacedMainClass.getMethod("main", String[].class);
|
||||
mainMethod.invoke(null, new Object[] { args });
|
||||
}
|
||||
catch (InvocationTargetException exc)
|
||||
{
|
||||
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), exc.getCause());
|
||||
}
|
||||
catch (Throwable cause)
|
||||
{
|
||||
throw new Error("failed to fork", cause);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private MappedObjectClassLoader(Class< ? > mainClass)
|
||||
{
|
||||
super(((URLClassLoader) mainClass.getClassLoader()).getURLs());
|
||||
}
|
||||
|
||||
private static long total_time_transforming;
|
||||
|
||||
@Override
|
||||
protected synchronized Class< ? > loadClass(String name, boolean resolve) throws ClassNotFoundException
|
||||
{
|
||||
if (name.startsWith("java."))
|
||||
return super.loadClass(name, resolve);
|
||||
if (name.startsWith("javax."))
|
||||
return super.loadClass(name, resolve);
|
||||
|
||||
if (name.startsWith("sun."))
|
||||
return super.loadClass(name, resolve);
|
||||
if (name.startsWith("sunw."))
|
||||
return super.loadClass(name, resolve);
|
||||
|
||||
// never transform classes in this very package, sub-packages should be transformed
|
||||
if (name.startsWith(MAPPEDOBJECT_PACKAGE_PREFIX))
|
||||
if (name.substring(MAPPEDOBJECT_PACKAGE_PREFIX.length()).indexOf('.') == -1)
|
||||
return super.loadClass(name, resolve);
|
||||
|
||||
String className = name.replace('.', '/');
|
||||
|
||||
if (MappedObjectTransformer.PRINT_ACTIVITY)
|
||||
System.out.println(MappedObjectClassLoader.class.getSimpleName() + ": " + className);
|
||||
|
||||
byte[] bytecode = readStream(this.getResourceAsStream(className.concat(".class")));
|
||||
|
||||
long t0 = System.nanoTime();
|
||||
bytecode = MappedObjectTransformer.transformFieldAccess(className, bytecode);
|
||||
long t1 = System.nanoTime();
|
||||
total_time_transforming += (t1 - t0);
|
||||
|
||||
if (MappedObjectTransformer.PRINT_TIMING)
|
||||
System.out.println("transforming " + className + " took " + (t1 - t0) / 1000 + " micros (total: " + (total_time_transforming / 1000 / 1000) + "ms)");
|
||||
|
||||
Class< ? > clazz = super.defineClass(name, bytecode, 0, bytecode.length);
|
||||
if (resolve)
|
||||
resolveClass(clazz);
|
||||
return clazz;
|
||||
}
|
||||
|
||||
static byte[] readStream(InputStream in)
|
||||
{
|
||||
byte[] bytecode = new byte[256];
|
||||
int len = 0;
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (bytecode.length == len)
|
||||
bytecode = Arrays.copyOf(bytecode, len * 2);
|
||||
int got = in.read(bytecode, len, bytecode.length - len);
|
||||
if (got == -1)
|
||||
break;
|
||||
len += got;
|
||||
}
|
||||
}
|
||||
catch (IOException exc)
|
||||
{
|
||||
// stop!
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
in.close();
|
||||
}
|
||||
catch (IOException exc)
|
||||
{
|
||||
// ignore...
|
||||
}
|
||||
}
|
||||
return Arrays.copyOf(bytecode, len);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,588 @@
|
|||
/*
|
||||
* Created on Jun 23, 2011
|
||||
*/
|
||||
|
||||
package org.lwjgl.util.mapped;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.objectweb.asm.*;
|
||||
import org.objectweb.asm.util.TraceClassVisitor;
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
/**
|
||||
* @author Riven
|
||||
*/
|
||||
|
||||
public class MappedObjectTransformer
|
||||
{
|
||||
static final boolean PRINT_TIMING = true;
|
||||
static final boolean PRINT_ACTIVITY = false;
|
||||
static final boolean PRINT_BYTECODE = false;
|
||||
static final Map<String, MappedSubtypeInfo> className_to_subtype;
|
||||
|
||||
static
|
||||
{
|
||||
className_to_subtype = new HashMap<String, MappedSubtypeInfo>();
|
||||
|
||||
{
|
||||
// HACK: required for mapped.view++
|
||||
//
|
||||
// because the compiler generates:
|
||||
// => GETFIELD MappedObject.view
|
||||
// => ICONST_1
|
||||
// => IADD
|
||||
// => PUTFIELD MyMappedType.view
|
||||
//
|
||||
// instead of:
|
||||
// => GETFIELD MyMappedType.view
|
||||
// => ICONST_1
|
||||
// => IADD
|
||||
// => PUTFIELD MyMappedType.view
|
||||
//
|
||||
MappedSubtypeInfo info = new MappedSubtypeInfo(jvmClassName(MappedObject.class), -1, -1);
|
||||
className_to_subtype.put(info.className, info);
|
||||
}
|
||||
|
||||
String vmName = System.getProperty("java.vm.name");
|
||||
if (vmName != null && !vmName.contains("Server"))
|
||||
{
|
||||
System.err.println("Warning: " + MappedObject.class.getSimpleName() + "s have inferiour performance on Client VMs, please consider switching to a Server VM.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void register(Class< ? > type)
|
||||
{
|
||||
if (MappedObjectClassLoader.FORKED)
|
||||
return;
|
||||
|
||||
MappedType mapped = type.getAnnotation(MappedType.class);
|
||||
if (mapped == null)
|
||||
throw new InternalError("missing " + MappedType.class.getName() + " annotation");
|
||||
|
||||
String className = jvmClassName(type);
|
||||
|
||||
MappedSubtypeInfo mappedType = new MappedSubtypeInfo(className, mapped.sizeof(), mapped.align());
|
||||
|
||||
int advancingOffset = 0;
|
||||
|
||||
for (Field field : type.getDeclaredFields())
|
||||
{
|
||||
// static fields are never mapped
|
||||
if ((field.getModifiers() & Modifier.STATIC) != 0)
|
||||
continue;
|
||||
|
||||
// we only support primitives and ByteBuffers
|
||||
if (!field.getType().isPrimitive() && field.getType() != ByteBuffer.class)
|
||||
throw new InternalError("field '" + className + "." + field.getName() + "' not supported: " + field.getType());
|
||||
|
||||
MappedField meta = field.getAnnotation(MappedField.class);
|
||||
if (meta == null && !mapped.autoGenerateOffsets())
|
||||
throw new InternalError("field '" + className + "." + field.getName() + "' missing annotation " + MappedField.class.getName() + ": " + className);
|
||||
|
||||
// quick hack
|
||||
long byteOffset = meta == null ? advancingOffset : meta.byteOffset();
|
||||
long byteLength;
|
||||
if (field.getType() == long.class || field.getType() == double.class)
|
||||
byteLength = 8;
|
||||
else if (field.getType() == int.class || field.getType() == float.class)
|
||||
byteLength = 4;
|
||||
else if (field.getType() == char.class || field.getType() == short.class)
|
||||
byteLength = 2;
|
||||
else if (field.getType() == byte.class)
|
||||
byteLength = 1;
|
||||
else if (field.getType() == ByteBuffer.class)
|
||||
byteLength = meta.byteLength();
|
||||
else
|
||||
throw new IllegalStateException(field.getType().getName());
|
||||
|
||||
if (field.getType() != ByteBuffer.class)
|
||||
if ((advancingOffset % byteLength) != 0)
|
||||
throw new IllegalStateException("misaligned mapped type: " + className + "." + field.getName());
|
||||
|
||||
if (PRINT_ACTIVITY)
|
||||
System.out.println(MappedObjectTransformer.class.getSimpleName() + ": " + className + "." + field.getName() + " [type=" + field.getType().getSimpleName() + ", offset=" + byteOffset + "]");
|
||||
|
||||
mappedType.fieldToOffset.put(field.getName(), Long.valueOf(byteOffset));
|
||||
mappedType.fieldToLength.put(field.getName(), Long.valueOf(byteLength));
|
||||
|
||||
advancingOffset += byteLength;
|
||||
}
|
||||
|
||||
if (className_to_subtype.put(className, mappedType) != null)
|
||||
{
|
||||
throw new InternalError("duplicate mapped type: " + className);
|
||||
}
|
||||
}
|
||||
|
||||
public static final String view_constructor_method = "_construct_view_";
|
||||
|
||||
public static byte[] transformFieldAccess(final String className, byte[] bytecode)
|
||||
{
|
||||
int flags = ClassWriter.COMPUTE_FRAMES;
|
||||
|
||||
ClassWriter writer = new ClassWriter(flags);
|
||||
|
||||
ClassAdapter adapter = new ClassAdapter(writer)
|
||||
{
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)
|
||||
{
|
||||
{
|
||||
MappedSubtypeInfo mappedSubtype = className_to_subtype.get(className);
|
||||
|
||||
if (mappedSubtype != null && !mappedSubtype.className.equals(jvmClassName(MappedObject.class)))
|
||||
{
|
||||
if (name.equals("<init>"))
|
||||
{
|
||||
if (!desc.equals("()V"))
|
||||
throw new IllegalStateException(className + " can only have a default constructor, found: " + desc);
|
||||
|
||||
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, jvmClassName(MappedObject.class), "<init>", "()V");
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
|
||||
// put the method body in another method
|
||||
name = view_constructor_method;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new MappedInstanceMethodAdapter(className, name, desc, super.visitMethod(access, name, desc, signature, exceptions));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String fieldName, String typeName, String dunno, Object value)
|
||||
{
|
||||
// remove redirected fields
|
||||
MappedSubtypeInfo mappedSubtype = className_to_subtype.get(className);
|
||||
|
||||
if (mappedSubtype != null && mappedSubtype.fieldToOffset.containsKey(fieldName))
|
||||
{
|
||||
if (PRINT_ACTIVITY)
|
||||
System.out.println(MappedObjectTransformer.class.getSimpleName() + ": discarding field: " + className + "." + fieldName + ":" + typeName);
|
||||
return null;
|
||||
}
|
||||
|
||||
return super.visitField(access, fieldName, typeName, dunno, value);
|
||||
}
|
||||
};
|
||||
|
||||
new ClassReader(bytecode).accept(adapter, 0);
|
||||
bytecode = writer.toByteArray();
|
||||
|
||||
if (PRINT_BYTECODE)
|
||||
printBytecode(bytecode, adapter);
|
||||
|
||||
return bytecode;
|
||||
}
|
||||
|
||||
static void printBytecode(byte[] bytecode, ClassAdapter adapter)
|
||||
{
|
||||
StringWriter sw = new StringWriter();
|
||||
ClassVisitor tracer = new TraceClassVisitor(adapter, new PrintWriter(sw));
|
||||
new ClassReader(bytecode).accept(tracer, 0);
|
||||
String dump = sw.toString();
|
||||
|
||||
System.out.println(dump);
|
||||
}
|
||||
|
||||
static void throwException0(MethodVisitor visitor, Class< ? > type, String msg)
|
||||
{
|
||||
String errorType = jvmClassName(type);
|
||||
|
||||
visitor.visitTypeInsn(NEW, errorType);
|
||||
visitor.visitInsn(DUP);
|
||||
visitor.visitLdcInsn(msg);
|
||||
visitor.visitMethodInsn(INVOKESPECIAL, errorType, "<init>", "(Ljava/lang/String;)V");
|
||||
visitor.visitInsn(ATHROW);
|
||||
}
|
||||
|
||||
static class MappedInstanceMethodAdapter extends MethodAdapter
|
||||
{
|
||||
private final String className;
|
||||
private final String methodName;
|
||||
private final String descr;
|
||||
|
||||
public MappedInstanceMethodAdapter(String className, String methodName, String descr, MethodVisitor backing)
|
||||
{
|
||||
super(backing);
|
||||
this.className = className;
|
||||
this.methodName = methodName;
|
||||
this.descr = descr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTypeInsn(int opcode, String typeName)
|
||||
{
|
||||
if (opcode == NEW && className_to_subtype.containsKey(typeName))
|
||||
{
|
||||
throw new IllegalAccessError("must not manually create instances of " + typeName + " in method " + this.className + "." + this.methodName + this.descr);
|
||||
}
|
||||
|
||||
super.visitTypeInsn(opcode, typeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String className, String methodName, String signature)
|
||||
{
|
||||
if (opcode == INVOKESPECIAL && className.equals(jvmClassName(MappedObject.class)) && methodName.equals("<init>") && signature.equals("()V"))
|
||||
{
|
||||
// stack: instance
|
||||
visitInsn(POP);
|
||||
// stack: -
|
||||
return;
|
||||
}
|
||||
|
||||
for (MappedSubtypeInfo mappedType : className_to_subtype.values())
|
||||
{
|
||||
boolean isMapDirectMethod = (opcode == INVOKESTATIC && methodName.equals("map") && className.equals(mappedType.className) && signature.equals("(JI)L" + jvmClassName(MappedObject.class) + ";"));
|
||||
boolean isMapBufferMethod = (opcode == INVOKESTATIC && methodName.equals("map") && className.equals(mappedType.className) && signature.equals("(Ljava/nio/ByteBuffer;)L" + jvmClassName(MappedObject.class) + ";"));
|
||||
boolean isMallocMethod = (opcode == INVOKESTATIC && methodName.equals("malloc") && className.equals(mappedType.className) && signature.equals("(I)L" + jvmClassName(MappedObject.class) + ";"));
|
||||
|
||||
if ((isMapDirectMethod || isMapBufferMethod) || isMallocMethod)
|
||||
{
|
||||
if (isMallocMethod)
|
||||
{
|
||||
// stack: count
|
||||
pushInt(super.mv, mappedType.sizeof);
|
||||
// stack: sizeof, count
|
||||
super.visitInsn(IMUL);
|
||||
// stack: bytes
|
||||
super.visitMethodInsn(INVOKESTATIC, jvmClassName(ByteBuffer.class), "allocateDirect", "(I)L" + jvmClassName(ByteBuffer.class) + ";");
|
||||
// stack: buffer
|
||||
}
|
||||
else if (isMapDirectMethod)
|
||||
{
|
||||
// stack: capacity, address
|
||||
super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "newBuffer", "(JI)L" + jvmClassName(ByteBuffer.class) + ";");
|
||||
// stack: buffer
|
||||
}
|
||||
|
||||
// stack: buffer
|
||||
super.visitTypeInsn(NEW, className);
|
||||
// stack: new, buffer
|
||||
super.visitInsn(DUP);
|
||||
// stack: new, new, buffer
|
||||
super.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V");
|
||||
// stack: new, buffer
|
||||
super.visitInsn(DUP_X1);
|
||||
// stack: new, buffer, new
|
||||
super.visitInsn(SWAP);
|
||||
// stack: buffer, new, new
|
||||
pushInt(super.mv, mappedType.align);
|
||||
// stack: int, buffer, new, new
|
||||
pushInt(super.mv, mappedType.sizeof);
|
||||
// stack: int, int, buffer, new, new
|
||||
super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "setup", "(L" + jvmClassName(MappedObject.class) + ";Ljava/nio/ByteBuffer;II)V");
|
||||
// stack: new
|
||||
return;
|
||||
}
|
||||
|
||||
if (opcode == INVOKEVIRTUAL && methodName.equals("dup") && className.equals(mappedType.className) && signature.equals("()L" + jvmClassName(MappedObject.class) + ";"))
|
||||
{
|
||||
// stack: this
|
||||
super.visitTypeInsn(NEW, className);
|
||||
// stack: new, this
|
||||
super.visitInsn(DUP);
|
||||
// stack: new, new, this
|
||||
super.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V");
|
||||
// stack: new, this
|
||||
super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "dup", "(L" + jvmClassName(MappedObject.class) + ";L" + jvmClassName(MappedObject.class) + ";)L" + jvmClassName(MappedObject.class) + ";");
|
||||
// stack: new
|
||||
return;
|
||||
}
|
||||
|
||||
if (opcode == INVOKEVIRTUAL && methodName.equals("slice") && className.equals(mappedType.className) && signature.equals("()L" + jvmClassName(MappedObject.class) + ";"))
|
||||
{
|
||||
// stack: this
|
||||
super.visitTypeInsn(NEW, className);
|
||||
// stack: new, this
|
||||
super.visitInsn(DUP);
|
||||
// stack: new, new, this
|
||||
super.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V");
|
||||
// stack: new, this
|
||||
super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "slice", "(L" + jvmClassName(MappedObject.class) + ";L" + jvmClassName(MappedObject.class) + ";)L" + jvmClassName(MappedObject.class) + ";");
|
||||
// stack: new
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
if (opcode == INVOKEVIRTUAL && methodName.equals("runViewConstructor") && className.equals(mappedType.className) && signature.equals("()V"))
|
||||
{
|
||||
// stack: this
|
||||
super.visitInsn(DUP);
|
||||
// stack: this, this
|
||||
super.visitMethodInsn(INVOKEVIRTUAL, className, view_constructor_method, "()V");
|
||||
// stack: this
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
if (opcode == INVOKEVIRTUAL && methodName.equals("copyTo") && className.equals(mappedType.className) && signature.equals("(L" + jvmClassName(MappedObject.class) + ";)V"))
|
||||
{
|
||||
// stack: target, this
|
||||
pushInt(super.mv, mappedType.sizeof);
|
||||
// stack: sizeof, target, this
|
||||
super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "copy", "(L" + jvmClassName(MappedObject.class) + ";L" + jvmClassName(MappedObject.class) + ";I)V");
|
||||
// stack: -
|
||||
return;
|
||||
}
|
||||
|
||||
if (opcode == INVOKEVIRTUAL && methodName.equals("copyRange") && className.equals(mappedType.className) && signature.equals("(L" + jvmClassName(MappedObject.class) + ";I)V"))
|
||||
{
|
||||
// stack: instances, target, this
|
||||
pushInt(super.mv, mappedType.sizeof);
|
||||
// stack: sizeof, instances, target, this
|
||||
super.visitInsn(IMUL);
|
||||
// stack: bytes, target, this
|
||||
super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "copy", "(L" + jvmClassName(MappedObject.class) + ";L" + jvmClassName(MappedObject.class) + ";I)V");
|
||||
// stack: -
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
super.visitMethodInsn(opcode, className, methodName, signature);
|
||||
}
|
||||
|
||||
static void throwAccessErrorOnReadOnlyField(String className, String fieldName)
|
||||
{
|
||||
throw new IllegalAccessError("field '" + className + "." + fieldName + "' is final");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldInsn(int opcode, String className, String fieldName, String typeName)
|
||||
{
|
||||
MappedSubtypeInfo mappedSubtype = className_to_subtype.get(className);
|
||||
if (mappedSubtype == null)
|
||||
{
|
||||
String mappedSetPrefix = jvmClassName(MappedSet.class);
|
||||
|
||||
outer: if (fieldName.equals("view") && className.startsWith(mappedSetPrefix))
|
||||
{
|
||||
if (opcode == GETFIELD)
|
||||
throwAccessErrorOnReadOnlyField(className, fieldName);
|
||||
if (opcode != PUTFIELD)
|
||||
break outer;
|
||||
|
||||
// stack: index, this
|
||||
if (false)
|
||||
break outer;
|
||||
else if (className.equals(jvmClassName(MappedSet2.class)))
|
||||
super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "put_views", "(L" + jvmClassName(MappedSet2.class) + ";I)V");
|
||||
else if (className.equals(jvmClassName(MappedSet3.class)))
|
||||
super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "put_views", "(L" + jvmClassName(MappedSet3.class) + ";I)V");
|
||||
else if (className.equals(jvmClassName(MappedSet4.class)))
|
||||
super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "put_views", "(L" + jvmClassName(MappedSet4.class) + ";I)V");
|
||||
else
|
||||
break outer;
|
||||
// stack: -
|
||||
return;
|
||||
}
|
||||
|
||||
// early out
|
||||
super.visitFieldInsn(opcode, className, fieldName, typeName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fieldName.equals("SIZEOF"))
|
||||
{
|
||||
if (!typeName.equals("I"))
|
||||
throw new IllegalStateException();
|
||||
|
||||
if (opcode == GETSTATIC)
|
||||
{
|
||||
pushInt(super.mv, mappedSubtype.sizeof);
|
||||
return;
|
||||
}
|
||||
if (opcode == PUTSTATIC)
|
||||
{
|
||||
throwAccessErrorOnReadOnlyField(className, fieldName);
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldName.equals("view"))
|
||||
{
|
||||
if (!typeName.equals("I"))
|
||||
throw new IllegalStateException();
|
||||
|
||||
if (opcode == GETFIELD)
|
||||
{
|
||||
// stack: instance
|
||||
super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "get_view", "(L" + jvmClassName(MappedObject.class) + ";)I");
|
||||
return;
|
||||
}
|
||||
if (opcode == PUTFIELD)
|
||||
{
|
||||
// stack: int, instance
|
||||
super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "put_view", "(L" + jvmClassName(MappedObject.class) + ";I)V");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldName.equals("align"))
|
||||
{
|
||||
if (!typeName.equals("I"))
|
||||
throw new IllegalStateException();
|
||||
|
||||
if (opcode == GETFIELD)
|
||||
{
|
||||
// stack: instance
|
||||
super.visitInsn(POP);
|
||||
// stack: -
|
||||
pushInt(super.mv, mappedSubtype.align);
|
||||
// stack: int
|
||||
return;
|
||||
}
|
||||
if (opcode == PUTFIELD)
|
||||
{
|
||||
throwAccessErrorOnReadOnlyField(className, fieldName);
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldName.equals("stride"))
|
||||
{
|
||||
if (!typeName.equals("I"))
|
||||
throw new IllegalStateException();
|
||||
|
||||
if (opcode == GETFIELD)
|
||||
{
|
||||
// do not change a thing
|
||||
}
|
||||
if (opcode == PUTFIELD)
|
||||
{
|
||||
throwAccessErrorOnReadOnlyField(className, fieldName);
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldName.equals("baseAddress") || fieldName.equals("viewAddress"))
|
||||
{
|
||||
if (!typeName.equals("J"))
|
||||
throw new IllegalStateException();
|
||||
|
||||
if (opcode == GETFIELD)
|
||||
{
|
||||
// do not change a thing
|
||||
}
|
||||
if (opcode == PUTFIELD)
|
||||
{
|
||||
throwAccessErrorOnReadOnlyField(className, fieldName);
|
||||
}
|
||||
}
|
||||
|
||||
Long fieldOffset = mappedSubtype.fieldToOffset.get(fieldName);
|
||||
if (fieldOffset == null)
|
||||
{
|
||||
// early out
|
||||
super.visitFieldInsn(opcode, className, fieldName, typeName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeName.equals("L" + jvmClassName(ByteBuffer.class) + ";"))
|
||||
{
|
||||
if (opcode == PUTFIELD)
|
||||
{
|
||||
throwAccessErrorOnReadOnlyField(className, fieldName);
|
||||
}
|
||||
if (opcode == GETFIELD)
|
||||
{
|
||||
Long fieldLength = mappedSubtype.fieldToLength.get(fieldName);
|
||||
|
||||
super.visitFieldInsn(GETFIELD, mappedSubtype.className, "viewAddress", "J");
|
||||
super.visitLdcInsn(fieldOffset);
|
||||
super.visitInsn(LADD);
|
||||
super.visitLdcInsn(fieldLength);
|
||||
super.visitInsn(L2I);
|
||||
super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), "newBuffer", "(JI)L" + jvmClassName(ByteBuffer.class) + ";");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (opcode == PUTFIELD)
|
||||
{
|
||||
super.visitInsn(SWAP);
|
||||
super.visitFieldInsn(GETFIELD, mappedSubtype.className, "viewAddress", "J");
|
||||
super.visitLdcInsn(fieldOffset);
|
||||
super.visitInsn(LADD);
|
||||
super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), typeName.toLowerCase() + "put", "(" + typeName + "J)V");
|
||||
return;
|
||||
}
|
||||
if (opcode == GETFIELD)
|
||||
{
|
||||
super.visitFieldInsn(GETFIELD, mappedSubtype.className, "viewAddress", "J");
|
||||
super.visitLdcInsn(fieldOffset);
|
||||
super.visitInsn(LADD);
|
||||
super.visitMethodInsn(INVOKESTATIC, jvmClassName(MappedHelper.class), typeName.toLowerCase() + "get", "(J)" + typeName);
|
||||
return;
|
||||
}
|
||||
|
||||
// original field access
|
||||
super.visitFieldInsn(opcode, className, fieldName, typeName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void pushInt(MethodVisitor mv, int value)
|
||||
{
|
||||
if (value == -1)
|
||||
mv.visitInsn(ICONST_M1);
|
||||
else if (value == 0)
|
||||
mv.visitInsn(ICONST_0);
|
||||
else if (value == 1)
|
||||
mv.visitInsn(ICONST_1);
|
||||
else if (value == 2)
|
||||
mv.visitInsn(ICONST_2);
|
||||
else if (value == 3)
|
||||
mv.visitInsn(ICONST_3);
|
||||
else if (value == 4)
|
||||
mv.visitInsn(ICONST_4);
|
||||
else if (value == 5)
|
||||
mv.visitInsn(ICONST_5);
|
||||
else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE)
|
||||
mv.visitIntInsn(BIPUSH, value);
|
||||
else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE)
|
||||
mv.visitIntInsn(SIPUSH, value);
|
||||
else
|
||||
mv.visitLdcInsn(Integer.valueOf(value));
|
||||
}
|
||||
|
||||
static String jvmClassName(Class< ? > type)
|
||||
{
|
||||
return type.getName().replace('.', '/');
|
||||
}
|
||||
|
||||
static class MappedSubtypeInfo
|
||||
{
|
||||
public final String className;
|
||||
|
||||
public int sizeof;
|
||||
public int align;
|
||||
|
||||
public Map<String, Long> fieldToOffset;
|
||||
public Map<String, Long> fieldToLength;
|
||||
|
||||
public MappedSubtypeInfo(String className, int sizeof, int align)
|
||||
{
|
||||
this.className = className;
|
||||
|
||||
this.sizeof = sizeof;
|
||||
this.align = align;
|
||||
|
||||
this.fieldToOffset = new HashMap<String, Long>();
|
||||
this.fieldToLength = new HashMap<String, Long>();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Created on Jun 24, 2011
|
||||
*/
|
||||
|
||||
package org.lwjgl.util.mapped;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
/**
|
||||
* @author Riven
|
||||
*/
|
||||
|
||||
public class MappedObjectUnsafe
|
||||
{
|
||||
public static final Unsafe INSTANCE = getUnsafeInstance();
|
||||
public static final int ADDRESS_SIZE = INSTANCE.addressSize();
|
||||
private static final Object[] ARRAY = new Object[1];
|
||||
private static final long ARRAY_ELEMENT_OFFSET = INSTANCE.arrayBaseOffset(ARRAY.getClass());
|
||||
|
||||
private static final long BUFFER_ADDRESS_OFFSET = getObjectFieldOffset(ByteBuffer.class, "address");
|
||||
private static final long BUFFER_CAPACITY_OFFSET = getObjectFieldOffset(ByteBuffer.class, "capacity");
|
||||
|
||||
//
|
||||
|
||||
public static long getBufferBaseAddress(Buffer buffer)
|
||||
{
|
||||
return INSTANCE.getLong(buffer, BUFFER_ADDRESS_OFFSET);
|
||||
}
|
||||
|
||||
private static final ByteBuffer global = ByteBuffer.allocateDirect(4 * 1024);
|
||||
|
||||
public static ByteBuffer newBuffer(long address, int capacity)
|
||||
{
|
||||
if (address <= 0L || capacity < 0)
|
||||
throw new IllegalStateException("you almost crashed the jvm");
|
||||
|
||||
ByteBuffer buffer = global.duplicate();
|
||||
INSTANCE.putLong(buffer, BUFFER_ADDRESS_OFFSET, address);
|
||||
INSTANCE.putInt(buffer, BUFFER_CAPACITY_OFFSET, capacity);
|
||||
buffer.position(0);
|
||||
buffer.limit(capacity);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static long getObjectFieldOffset(Class< ? > type, String fieldName)
|
||||
{
|
||||
while (type != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return INSTANCE.objectFieldOffset(type.getDeclaredField(fieldName));
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
type = type.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new InternalError();
|
||||
}
|
||||
|
||||
private static Unsafe getUnsafeInstance()
|
||||
{
|
||||
try
|
||||
{
|
||||
ByteBuffer buffer = ByteBuffer.allocateDirect(1);
|
||||
Field unsafeField = buffer.getClass().getDeclaredField("unsafe");
|
||||
unsafeField.setAccessible(true);
|
||||
Unsafe instance = (Unsafe) unsafeField.get(buffer);
|
||||
buffer.flip(); // prevented 'buffer' from being gc'ed
|
||||
return instance;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Created on Jul 11, 2011
|
||||
*/
|
||||
|
||||
package org.lwjgl.util.mapped;
|
||||
|
||||
public class MappedSet
|
||||
{
|
||||
public static MappedSet2 create(MappedObject a, MappedObject b)
|
||||
{
|
||||
return new MappedSet2(a, b);
|
||||
}
|
||||
|
||||
public static MappedSet3 create(MappedObject a, MappedObject b, MappedObject c)
|
||||
{
|
||||
return new MappedSet3(a, b, c);
|
||||
}
|
||||
|
||||
public static MappedSet4 create(MappedObject a, MappedObject b, MappedObject c, MappedObject d)
|
||||
{
|
||||
return new MappedSet4(a, b, c, d);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Created on Jul 11, 2011
|
||||
*/
|
||||
|
||||
package org.lwjgl.util.mapped;
|
||||
|
||||
public class MappedSet2
|
||||
{
|
||||
private final MappedObject a, b;
|
||||
|
||||
MappedSet2(MappedObject a, MappedObject b)
|
||||
{
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
public int view;
|
||||
|
||||
void view(int view)
|
||||
{
|
||||
this.a.viewAddress = this.a.baseAddress + this.a.stride * view;
|
||||
this.b.viewAddress = this.b.baseAddress + this.b.stride * view;
|
||||
}
|
||||
|
||||
public void next()
|
||||
{
|
||||
this.a.viewAddress += this.a.stride;
|
||||
this.b.viewAddress += this.b.stride;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Created on Jul 11, 2011
|
||||
*/
|
||||
|
||||
package org.lwjgl.util.mapped;
|
||||
|
||||
public class MappedSet3
|
||||
{
|
||||
private final MappedObject a, b, c;
|
||||
|
||||
MappedSet3(MappedObject a, MappedObject b, MappedObject c)
|
||||
{
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
this.c = c;
|
||||
}
|
||||
|
||||
public int view;
|
||||
|
||||
void view(int view)
|
||||
{
|
||||
this.a.viewAddress = this.a.baseAddress + this.a.stride * view;
|
||||
this.b.viewAddress = this.b.baseAddress + this.b.stride * view;
|
||||
this.c.viewAddress = this.c.baseAddress + this.c.stride * view;
|
||||
}
|
||||
|
||||
public void next()
|
||||
{
|
||||
this.a.viewAddress += this.a.stride;
|
||||
this.b.viewAddress += this.b.stride;
|
||||
this.c.viewAddress += this.c.stride;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Created on Jul 11, 2011
|
||||
*/
|
||||
|
||||
package org.lwjgl.util.mapped;
|
||||
|
||||
public class MappedSet4
|
||||
{
|
||||
private final MappedObject a, b, c, d;
|
||||
|
||||
MappedSet4(MappedObject a, MappedObject b, MappedObject c, MappedObject d)
|
||||
{
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
this.c = c;
|
||||
this.d = d;
|
||||
}
|
||||
|
||||
public int view;
|
||||
|
||||
void view(int view)
|
||||
{
|
||||
this.a.viewAddress = this.a.baseAddress + this.a.stride * view;
|
||||
this.b.viewAddress = this.b.baseAddress + this.b.stride * view;
|
||||
this.c.viewAddress = this.c.baseAddress + this.c.stride * view;
|
||||
this.d.viewAddress = this.d.baseAddress + this.d.stride * view;
|
||||
}
|
||||
|
||||
public void next()
|
||||
{
|
||||
this.a.viewAddress += this.a.stride;
|
||||
this.b.viewAddress += this.b.stride;
|
||||
this.c.viewAddress += this.c.stride;
|
||||
this.d.viewAddress += this.d.stride;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Created on Jun 24, 2011
|
||||
*/
|
||||
|
||||
package org.lwjgl.util.mapped;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* @author Riven
|
||||
*/
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MappedType {
|
||||
int sizeof();
|
||||
|
||||
int align() default 4;
|
||||
|
||||
boolean autoGenerateOffsets() default true;
|
||||
}
|
Loading…
Reference in New Issue