From 585549f1f144fb9883f21c535f61bf7c7fa6aefb Mon Sep 17 00:00:00 2001 From: Ioannis Tsakpinis Date: Tue, 12 Jul 2011 13:29:04 +0000 Subject: [PATCH] Mapped object code improvements: added license, additional documentation, reformatted code, now using LWJGLUtil.log instead of System.err. Added system properties for bytecode transformer debug output. (org.lwjgl.util.mapped.PrintTiming & org.lwjgl.util.mapped.PrintActivity) Added support for bounds checking the view of mapped objects. Enabled with org.lwjgl.util.mapped.Checks Added tests for mapped objects. (org.lwjgl.test.mapped package) Added "[LWJGL] " prefix to all LWJGL generated debug messages. --- src/java/org/lwjgl/LWJGLUtil.java | 4 +- .../org/lwjgl/test/mapped/MappedFloat.java | 56 + .../lwjgl/test/mapped/MappedObjectBench.java | 237 ++++ .../lwjgl/test/mapped/MappedObjectTests1.java | 159 +++ .../lwjgl/test/mapped/MappedObjectTests2.java | 130 ++ .../lwjgl/test/mapped/MappedObjectTests3.java | 123 ++ .../lwjgl/test/mapped/MappedSomething.java | 55 + .../org/lwjgl/test/mapped/MappedVec2.java | 50 + .../org/lwjgl/test/mapped/MappedVec3.java | 52 + .../lwjgl/test/mapped/TestMappedObject.java | 75 ++ .../org/lwjgl/util/mapped/MappedField.java | 58 +- .../org/lwjgl/util/mapped/MappedForeach.java | 92 +- .../org/lwjgl/util/mapped/MappedHelper.java | 275 +++-- .../org/lwjgl/util/mapped/MappedObject.java | 359 +++--- .../util/mapped/MappedObjectClassLoader.java | 227 ++-- .../util/mapped/MappedObjectTransformer.java | 1088 ++++++++--------- .../lwjgl/util/mapped/MappedObjectUnsafe.java | 133 +- src/java/org/lwjgl/util/mapped/MappedSet.java | 80 +- .../org/lwjgl/util/mapped/MappedSet2.java | 70 +- .../org/lwjgl/util/mapped/MappedSet3.java | 76 +- .../org/lwjgl/util/mapped/MappedSet4.java | 82 +- .../org/lwjgl/util/mapped/MappedType.java | 79 +- 22 files changed, 2404 insertions(+), 1156 deletions(-) create mode 100644 src/java/org/lwjgl/test/mapped/MappedFloat.java create mode 100644 src/java/org/lwjgl/test/mapped/MappedObjectBench.java create mode 100644 src/java/org/lwjgl/test/mapped/MappedObjectTests1.java create mode 100644 src/java/org/lwjgl/test/mapped/MappedObjectTests2.java create mode 100644 src/java/org/lwjgl/test/mapped/MappedObjectTests3.java create mode 100644 src/java/org/lwjgl/test/mapped/MappedSomething.java create mode 100644 src/java/org/lwjgl/test/mapped/MappedVec2.java create mode 100644 src/java/org/lwjgl/test/mapped/MappedVec3.java create mode 100644 src/java/org/lwjgl/test/mapped/TestMappedObject.java diff --git a/src/java/org/lwjgl/LWJGLUtil.java b/src/java/org/lwjgl/LWJGLUtil.java index 86c6d615..e57392be 100644 --- a/src/java/org/lwjgl/LWJGLUtil.java +++ b/src/java/org/lwjgl/LWJGLUtil.java @@ -448,7 +448,7 @@ public class LWJGLUtil { /** * Gets a boolean property as a privileged action. */ - private static boolean getPrivilegedBoolean(final String property_name) { + public static boolean getPrivilegedBoolean(final String property_name) { Boolean value = AccessController.doPrivileged(new PrivilegedAction() { public Boolean run() { return Boolean.getBoolean(property_name); @@ -464,7 +464,7 @@ public class LWJGLUtil { */ public static void log(String msg) { if (DEBUG) { - System.err.println(msg); + System.err.println("[LWJGL] " + msg); } } diff --git a/src/java/org/lwjgl/test/mapped/MappedFloat.java b/src/java/org/lwjgl/test/mapped/MappedFloat.java new file mode 100644 index 00000000..acce62ec --- /dev/null +++ b/src/java/org/lwjgl/test/mapped/MappedFloat.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2002-2011 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.lwjgl.test.mapped; + +import org.lwjgl.util.mapped.MappedObject; +import org.lwjgl.util.mapped.MappedType; + +/** @author Riven */ +@MappedType(sizeof = 4) +public class MappedFloat extends MappedObject { + + public MappedFloat() { + this.test(); + } + + public float value; + + public void test() { + this.value = 4; + } + + @Override + public String toString() { + return "MappedFloat[" + value + "]"; + } + +} \ No newline at end of file diff --git a/src/java/org/lwjgl/test/mapped/MappedObjectBench.java b/src/java/org/lwjgl/test/mapped/MappedObjectBench.java new file mode 100644 index 00000000..87bf883e --- /dev/null +++ b/src/java/org/lwjgl/test/mapped/MappedObjectBench.java @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2002-2011 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.lwjgl.test.mapped; + +import org.lwjgl.util.mapped.MappedObjectUnsafe; + +import java.nio.ByteBuffer; +import java.util.Arrays; + +import static org.lwjgl.util.mapped.MappedHelper.*; + +/** @author Riven */ +@SuppressWarnings("static-access") +public class MappedObjectBench { + + static class InstanceVec3 { + + float x, y, z; + + @Override + public String toString() { + return "InstanceVec3[" + x + ", " + y + ", " + z + "]"; + } + } + + static class ArrayVec3 { + + float[] a; + int i; + + @Override + public String toString() { + return "ArrayVec3[" + a[i * 3 + 0] + ", " + a[i * 3 + 1] + ", " + a[i * 3 + 2] + "]"; + } + } + + static void benchmarkInstances() { + final int runs = 64; + final int iterations = 64 * 1024; + + InstanceVec3 vec1 = new InstanceVec3(); + InstanceVec3 vec2 = new InstanceVec3(); + InstanceVec3 vec3 = new InstanceVec3(); + + long[] took = new long[runs]; + for ( int run = 0; run < runs; run++ ) { + long t0 = System.nanoTime(); + for ( int iteration = 0; iteration < iterations; iteration++ ) { + vec1.x = 13; + vec1.y += vec1.y * vec1.x + 0.3f; + vec1.z += vec2.y + vec1.x + 0.3f; + vec2.z += vec2.y + vec1.x; + vec3.z += vec2.z + vec1.y; + } + long t1 = System.nanoTime(); + took[run] = t1 - t0; + } + + Arrays.sort(took); + System.out.println("instance took: " + took[took.length / 2] / 1024 + "us"); + + System.out.println(vec1); + System.out.println(vec2); + System.out.println(vec3); + } + + static void benchmarkMapped() { + final int runs = 64; + final int iterations = 64 * 1024; + + ByteBuffer bb = ByteBuffer.allocateDirect(200); + + MappedVec3 vecs = MappedVec3.map(bb); + + MappedVec3 vec1 = vecs.dup(); + MappedVec3 vec2 = vecs.dup(); + MappedVec3 vec3 = vecs.dup(); + + vec1.view = 0; + vec2.view = 1; + vec3.view = 2; + + long[] took = new long[runs]; + for ( int run = 0; run < runs; run++ ) { + long t0 = System.nanoTime(); + for ( int iteration = 0; iteration < iterations; iteration += 2 ) { + vec1.x = 13; + vec1.y += vec1.y * vec1.x + 0.3f; + vec1.z += vec2.y + vec1.x + 0.3f; + vec2.z += vec2.y + vec1.x; + vec3.z += vec2.z + vec1.y; + + vec1.x = 13; + vec1.y += vec1.y * vec1.x + 0.3f; + vec1.z += vec2.y + vec1.x + 0.3f; + vec2.z += vec2.y + vec1.x; + vec3.z += vec2.z + vec1.y; + } + long t1 = System.nanoTime(); + took[run] = t1 - t0; + } + + Arrays.sort(took); + System.out.println("mapped took: " + took[took.length / 2] / 1024 + "us"); + + System.out.println(vec1); + System.out.println(vec2); + System.out.println(vec3); + + System.out.println(bb); + } + + static void benchmarkIndirectArray() { + final int runs = 64; + final int iterations = 64 * 1024; + + float[] bb = new float[200]; + + ArrayVec3 vec1 = new ArrayVec3(); + ArrayVec3 vec2 = new ArrayVec3(); + ArrayVec3 vec3 = new ArrayVec3(); + + vec1.a = bb; + vec2.a = bb; + vec3.a = bb; + + vec1.i = 0; + vec2.i = 1; + vec3.i = 2; + + long[] took = new long[runs]; + for ( int run = 0; run < runs; run++ ) { + long t0 = System.nanoTime(); + for ( int iteration = 0; iteration < iterations; iteration++ ) { + vec1.a[vec1.i * 3 + 0] = 13; + vec1.a[vec1.i * 3 + 1] += vec1.a[vec1.i * 3 + 1] * vec1.a[vec1.i * 3 + 0] + 0.3f; + vec1.a[vec1.i * 3 + 2] += vec2.a[vec2.i * 3 + 1] + vec1.a[vec1.i * 3 + 0] + 0.3f; + vec2.a[vec2.i * 3 + 2] += vec2.a[vec2.i * 3 + 1] + vec1.a[vec1.i * 3 + 0]; + vec3.a[vec3.i * 3 + 2] += vec2.a[vec2.i * 3 + 2] + vec2.a[vec2.i * 3 + 1]; + } + long t1 = System.nanoTime(); + took[run] = t1 - t0; + } + + Arrays.sort(took); + System.out.println("array took: " + took[took.length / 2] / 1024 + "us"); + + System.out.println(vec1); + System.out.println(vec2); + System.out.println(vec3); + + System.out.println(bb); + } + + static void benchmarkDirectArray() { + final int runs = 64; + final int iterations = 64 * 1024; + + float[] bb = new float[200]; + + long[] took = new long[runs]; + for ( int run = 0; run < runs; run++ ) { + long t0 = System.nanoTime(); + for ( int iteration = 0; iteration < iterations; iteration++ ) { + bb[1 * 3 + 0] = 13; + bb[1 * 3 + 1] += bb[1 * 3 + 1] * bb[1 * 3 + 0] + 0.3f; + bb[1 * 3 + 2] += bb[2 * 3 + 1] + bb[1 * 3 + 0] + 0.3f; + bb[2 * 3 + 2] += bb[2 * 3 + 1] + bb[1 * 3 + 0]; + bb[3 * 3 + 2] += bb[2 * 3 + 2] + bb[2 * 3 + 1]; + } + long t1 = System.nanoTime(); + took[run] = t1 - t0; + } + + Arrays.sort(took); + System.out.println("array2 took: " + took[took.length / 2] / 1024 + "us"); + + System.out.println(bb); + } + + static void benchmarkUnsafe() { + final int runs = 64; + final int iterations = 64 * 1024; + + ByteBuffer bb = ByteBuffer.allocateDirect(200); + long addr = MappedObjectUnsafe.getBufferBaseAddress(bb); + + long[] took = new long[runs]; + for ( int run = 0; run < runs; run++ ) { + long t0 = System.nanoTime(); + for ( int iteration = 0; iteration < iterations; iteration++ ) { + fput(13, addr + (1 * 3 + 0) * 4); + fput(fget(addr + (1 * 3 + 1) * 4) + fget(addr + (1 * 3 + 1) * 4) * fget(addr + (1 * 3 + 0) * 4) + 0.3f, addr + (1 * 3 + 1) * 4); + fput(fget(addr + (1 * 3 + 2) * 4) + fget(addr + (2 * 3 + 1) * 4) + fget(addr + (1 * 3 + 0) * 4) + 0.3f, addr + (1 * 3 + 2) * 4); + fput(fget(addr + (2 * 3 + 2) * 4) + fget(addr + (2 * 3 + 1) * 4) + fget(addr + (1 * 3 + 0) * 4), addr + (2 * 3 + 2) * 4); + fput(fget(addr + (3 * 3 + 2) * 4) + fget(addr + (2 * 3 + 2) * 4) + fget(addr + (2 * 3 + 1) * 4), addr + (3 * 3 + 2) * 4); + } + long t1 = System.nanoTime(); + took[run] = t1 - t0; + } + + Arrays.sort(took); + System.out.println("unsafe took: " + took[took.length / 2] / 1024 + "us"); + + System.out.println(bb); + } + +} \ No newline at end of file diff --git a/src/java/org/lwjgl/test/mapped/MappedObjectTests1.java b/src/java/org/lwjgl/test/mapped/MappedObjectTests1.java new file mode 100644 index 00000000..8922b06d --- /dev/null +++ b/src/java/org/lwjgl/test/mapped/MappedObjectTests1.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2002-2011 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.lwjgl.test.mapped; + +import org.lwjgl.util.mapped.MappedHelper; +import org.lwjgl.util.mapped.MappedObjectUnsafe; + +import java.nio.ByteBuffer; + +/** @author Riven */ +@SuppressWarnings("static-access") +public class MappedObjectTests1 { + + static class Test { + + int value; + } + + static void testViewField() { + Test test = new Test(); + test.value = 13; + test.value += 1; + test.value++; + System.out.println(test); + + ByteBuffer bb = ByteBuffer.allocateDirect(200); + MappedFloat vecs = MappedFloat.map(bb); + + // verify 'malloc' and SIZEOF + { + MappedFloat vecs1 = MappedFloat.malloc(1234); + + assert (vecs1.stride == MappedFloat.SIZEOF); + assert (vecs1.stride * 1234 == vecs1.backingByteBuffer().capacity()); + assert (MappedFloat.SIZEOF * 1234 == vecs1.backingByteBuffer().capacity()); + } + + // manipulate 'mapped.value' + { + assert (vecs.value == 0.0f); // 4.0 is set in constructor, but runViewConstructor is not called + vecs.value = 1.1f; + assert (vecs.value == 1.1f); + } + + // manipulate 'view' with assignment + { + assert (vecs.view == 0); + vecs.view = 1; + assert (vecs.view == 1); + assert (vecs.value != 1.1f); // old view + vecs.view = 0; + } + + // manipulate 'view' with iinc + { + assert (vecs.view == 0); + vecs.view++; + assert (vecs.view == 1); + assert (vecs.value != 1.1f); // old view + vecs.view = 0; + } + + // test bound check + { + assert (vecs.view == 0); + try { + vecs.view = 50; + System.out.println("org.lwjgl.util.mapped.Checks is false or there is a bug in bounds checking."); + vecs.view = 0; + } catch (IndexOutOfBoundsException e) { + // expected, ignore + } + assert (vecs.view == 0); + } + + // test dup + { + int newStride = 16; + int doOffset = 0; + + vecs.view = 0; + MappedFloat.configure(vecs, newStride, doOffset); + MappedFloat dec2 = vecs.dup(); + MappedFloat.configure(dec2, newStride, doOffset); + + String s1 = vecs.baseAddress + "," + vecs.viewAddress + "," + vecs.stride + "," + vecs.SIZEOF; + String s2 = dec2.baseAddress + "," + dec2.viewAddress + "," + dec2.stride + "," + dec2.SIZEOF; + // System.out.println(s1); + // System.out.println(s2); + assert (s1.equals(s2)); + + dec2.view++; + + String s3 = vecs.baseAddress + "," + vecs.viewAddress + "," + vecs.stride + "," + vecs.SIZEOF; + String s4 = dec2.baseAddress + "," + dec2.viewAddress + "," + dec2.stride + "," + dec2.SIZEOF; + // System.out.println(s3); + // System.out.println(s4); + assert (!s3.equals(s4)); + } + + // test newBuffer + { + long addr1 = MappedObjectUnsafe.getBufferBaseAddress(bb); + ByteBuffer bb2 = MappedHelper.newBuffer(addr1, bb.capacity()); + long addr2 = MappedObjectUnsafe.getBufferBaseAddress(bb); + + System.out.println(bb); + System.out.println(bb2); + System.out.println(addr1); + System.out.println(addr2); + } + + // test 'copy' + { + vecs.value = 13.37f; + MappedFloat dec2 = vecs.dup(); + dec2.view = 1; + System.out.println(vecs); + System.out.println(dec2); + vecs.copyTo(dec2); + System.out.println(vecs); + System.out.println(dec2); + assert (dec2.value == 13.37f); + + vecs.value = 73.31f; + vecs.copyRange(dec2, 1); + assert (dec2.value == 73.31f); + } + } +} diff --git a/src/java/org/lwjgl/test/mapped/MappedObjectTests2.java b/src/java/org/lwjgl/test/mapped/MappedObjectTests2.java new file mode 100644 index 00000000..12574755 --- /dev/null +++ b/src/java/org/lwjgl/test/mapped/MappedObjectTests2.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2002-2011 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.lwjgl.test.mapped; + +import java.nio.ByteBuffer; + +/** @author Riven */ +@SuppressWarnings("static-access") +public class MappedObjectTests2 { + + static void testWriteFieldAccess(MappedVec3 vecs) { + // write access results into a transform-time IllegalAccessException + + System.out.println(vecs.baseAddress); // test read-access + + System.out.println(vecs.viewAddress); // test read-access + + System.out.println(vecs.align); // test read-access + + System.out.println(MappedVec3.SIZEOF); // test read-access + } + + static void testFields() { + ByteBuffer bb = ByteBuffer.allocateDirect(200); + MappedVec3 vecs = MappedVec3.map(bb); + + testWriteFieldAccess(vecs); + + vecs.x = 13.13f; + vecs.y = 14.14f; + vecs.z = 15.15f; + + System.out.println(vecs.viewAddress); + + assert (vecs.x == 13.13f); + assert (vecs.y == 14.14f); + assert (vecs.z == 15.15f); + + vecs.view = 0; + + assert (vecs.x == 13.13f); + assert (vecs.y == 14.14f); + assert (vecs.z == 15.15f); + + System.out.println(vecs); + vecs.view = 1; + System.out.println(vecs); + + assert (vecs.x == 0.0f); + assert (vecs.y == 0.0f); + assert (vecs.z == 0.0f); + + // now it becomes weird: offsets and strides + + vecs.view = 1; + vecs.x = 0.1234f; + vecs.view = 0; + + // test stride & sizeof + { + long a1 = vecs.viewAddress; + vecs.view = 1; + long a2 = vecs.viewAddress; + assert (a2 - a1 == MappedVec3.SIZEOF); + assert (a2 - a1 == vecs.stride); + vecs.view = 0; + } + + int newStride = 16; + + MappedVec3.configure(vecs, newStride, +4); + assert (vecs.z == 0.1234f); // vecs[1].x ended up in vecs[0].z due to 1float offset + + MappedVec3.configure(vecs, newStride, +8); + assert (vecs.z == 0.0000f); // vecs[1].z ended up in vecs[0].z due to 2float offset + + // test new stride + { + long a1 = vecs.viewAddress; + vecs.view = 1; + long a2 = vecs.viewAddress; + assert (a2 - a1 == newStride); + vecs.view = 0; + } + + // example: GPU => VBO => VTN + { + MappedVec3 v = MappedVec3.map(bb); + MappedVec2 t = MappedVec2.map(bb); + MappedVec3 n = MappedVec3.map(bb); + + int stride = MappedVec3.SIZEOF + MappedVec2.SIZEOF + MappedVec3.SIZEOF; + assert (stride == 32); + + MappedVec3.configure(v, stride, 0); + MappedVec2.configure(t, stride, MappedVec3.SIZEOF); + MappedVec3.configure(n, stride, MappedVec3.SIZEOF + MappedVec2.SIZEOF); + } + } + +} \ No newline at end of file diff --git a/src/java/org/lwjgl/test/mapped/MappedObjectTests3.java b/src/java/org/lwjgl/test/mapped/MappedObjectTests3.java new file mode 100644 index 00000000..cc6ea385 --- /dev/null +++ b/src/java/org/lwjgl/test/mapped/MappedObjectTests3.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2002-2011 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.lwjgl.test.mapped; + +import org.lwjgl.util.mapped.*; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import static org.lwjgl.util.mapped.MappedObject.*; + +/** @author Riven */ +@SuppressWarnings("static-access") +public class MappedObjectTests3 { + + static void testMappedBuffer() { + int elementCount = 4; + + MappedSomething some = MappedSomething.malloc(elementCount); + + assert (some.data != some.data); + + long addr1 = MappedObjectUnsafe.getBufferBaseAddress(some.backingByteBuffer()); + + ByteBuffer mapped = some.data; // creates new ByteBuffer instance + long addr2 = MappedObjectUnsafe.getBufferBaseAddress(mapped); + + assert (addr2 - addr1 == 4); + assert (mapped.capacity() == MappedSomething.SIZEOF - 4); + + some.view++; + mapped = some.data; // creates new ByteBuffer instance + + long addr3 = MappedObjectUnsafe.getBufferBaseAddress(mapped); + assert (addr3 - addr1 == 4 + MappedSomething.SIZEOF); + assert (addr3 - addr2 == 0 + MappedSomething.SIZEOF); + assert (mapped.capacity() == MappedSomething.SIZEOF - 4); + } + + static void testForeach() { + int elementCount = 4; + MappedSomething some = MappedSomething.malloc(elementCount); + + int i = 0; + for ( MappedSomething item : foreach(some, elementCount) ) { + assert (item.view == i++); + } + assert (some.view != elementCount); + System.out.println("current.view=" + some.view + ", not " + elementCount + ", as you might expect"); + } + + @MappedType(sizeof = 12) + public static class Xyz extends MappedObject { + + int x, y, z; + } + + static void testConstructor() { + int capacity = 1024; + ByteBuffer bb = ByteBuffer.allocateDirect(capacity).order(ByteOrder.nativeOrder()); + long address = MappedObjectUnsafe.getBufferBaseAddress(bb); + + MappedFloat mf = MappedFloat.map(address, capacity); + + assert (address == mf.baseAddress); + + assert (mf.value == 0.0f); + mf.view = 1; + assert (mf.value == 0.0f); + mf.runViewConstructor(); + assert (mf.value == 4.0f); + + Xyz.malloc(3); + } + + static void testMappedSet() { + MappedVec2 vec2 = MappedVec2.malloc(3); + MappedVec3 vec3 = MappedVec3.malloc(3); + + MappedSet2 set = MappedSet.create(vec2, vec3); + + assert (vec2.view == 0); + assert (vec3.view == 0); + + set.view = 2; + assert (vec2.view == 2); + assert (vec3.view == 2); + + set.view = 0; + assert (vec2.view == 0); + assert (vec3.view == 0); + } + +} \ No newline at end of file diff --git a/src/java/org/lwjgl/test/mapped/MappedSomething.java b/src/java/org/lwjgl/test/mapped/MappedSomething.java new file mode 100644 index 00000000..bae9f502 --- /dev/null +++ b/src/java/org/lwjgl/test/mapped/MappedSomething.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2002-2011 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.lwjgl.test.mapped; + +import org.lwjgl.util.mapped.MappedField; +import org.lwjgl.util.mapped.MappedObject; +import org.lwjgl.util.mapped.MappedType; + +import java.nio.ByteBuffer; + +/** @author Riven */ +@MappedType(sizeof = 64) +public class MappedSomething extends MappedObject { + + @MappedField(byteOffset = 0) + public int used; + + @MappedField(byteOffset = 4, byteLength = 64 - 4) + public ByteBuffer data; + + @Override + public String toString() { + return "MappedSomething[" + used + "]"; + } + +} \ No newline at end of file diff --git a/src/java/org/lwjgl/test/mapped/MappedVec2.java b/src/java/org/lwjgl/test/mapped/MappedVec2.java new file mode 100644 index 00000000..d13125a0 --- /dev/null +++ b/src/java/org/lwjgl/test/mapped/MappedVec2.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2002-2011 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.lwjgl.test.mapped; + +import org.lwjgl.util.mapped.MappedObject; +import org.lwjgl.util.mapped.MappedType; + +/** @author Riven */ +@MappedType(sizeof = 8) +public class MappedVec2 extends MappedObject { + + public float x; + + public float y; + + @Override + public String toString() { + return "MappedVec2[" + x + "," + y + "]"; + } + +} \ No newline at end of file diff --git a/src/java/org/lwjgl/test/mapped/MappedVec3.java b/src/java/org/lwjgl/test/mapped/MappedVec3.java new file mode 100644 index 00000000..6a1e39cf --- /dev/null +++ b/src/java/org/lwjgl/test/mapped/MappedVec3.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2002-2011 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.lwjgl.test.mapped; + +import org.lwjgl.util.mapped.MappedObject; +import org.lwjgl.util.mapped.MappedType; + +/** @author Riven */ +@MappedType(sizeof = 12) +public class MappedVec3 extends MappedObject { + + public float x; + + public float y; + + public float z; + + @Override + public String toString() { + return "[" + x + "," + y + "," + z + "]"; + } + +} \ No newline at end of file diff --git a/src/java/org/lwjgl/test/mapped/TestMappedObject.java b/src/java/org/lwjgl/test/mapped/TestMappedObject.java new file mode 100644 index 00000000..83ce2345 --- /dev/null +++ b/src/java/org/lwjgl/test/mapped/TestMappedObject.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2002-2011 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.lwjgl.test.mapped; + +import org.lwjgl.util.mapped.MappedObjectClassLoader; +import org.lwjgl.util.mapped.MappedObjectTransformer; + +/** @author Riven */ +public class TestMappedObject { + + static { + boolean assertsEnabled = false; + assert assertsEnabled = true; // Intentional side effect!!! + if ( !assertsEnabled ) + throw new RuntimeException("Asserts must be enabled for this test."); + } + + public static void main(String[] args) { + MappedObjectTransformer.register(MappedFloat.class); + MappedObjectTransformer.register(MappedVec2.class); + MappedObjectTransformer.register(MappedVec3.class); + MappedObjectTransformer.register(MappedSomething.class); + + MappedObjectTransformer.register(MappedObjectTests3.Xyz.class); + + if ( MappedObjectClassLoader.fork(TestMappedObject.class, args) ) { + return; + } + + MappedObjectTests1.testViewField(); + + MappedObjectTests2.testFields(); + + // MappedObjectBench.benchmarkMapped(); + // MappedObjectBench.benchmarkInstances(); + // MappedObjectBench.benchmarkIndirectArray(); + // MappedObjectBench.benchmarkDirectArray(); + // MappedObjectBench.benchmarkUnsafe(); + + MappedObjectTests3.testMappedBuffer(); + MappedObjectTests3.testForeach(); + MappedObjectTests3.testConstructor(); + MappedObjectTests3.testMappedSet(); + } + +} \ No newline at end of file diff --git a/src/java/org/lwjgl/util/mapped/MappedField.java b/src/java/org/lwjgl/util/mapped/MappedField.java index 0ffae166..474cf9f4 100644 --- a/src/java/org/lwjgl/util/mapped/MappedField.java +++ b/src/java/org/lwjgl/util/mapped/MappedField.java @@ -1,19 +1,65 @@ /* - * Created on Jun 24, 2011 + * Copyright (c) 2002-2011 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package org.lwjgl.util.mapped; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** + * This annotation can be used on fields of {@link MappedType} classes, + * to manually specify byte offsets and lengths. This is useful when the + * mapped fields require custom alignment. {@link java.nio.ByteBuffer} + * fields are required to have this annotation with a hardcoded byte length. + * * @author Riven */ - @Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) public @interface MappedField { - long byteOffset(); - long byteLength() default -1; -} + /** + * Specifies the field byte offset within the mapped object. + * + * @return the field byte offset + */ + long byteOffset(); + + /** + * Specifies the field byte length. Required for {@link java.nio.ByteBuffer} fields. + * + * @return the field byte length + */ + long byteLength() default -1; + +} \ No newline at end of file diff --git a/src/java/org/lwjgl/util/mapped/MappedForeach.java b/src/java/org/lwjgl/util/mapped/MappedForeach.java index 3c254e5e..f4e7260e 100644 --- a/src/java/org/lwjgl/util/mapped/MappedForeach.java +++ b/src/java/org/lwjgl/util/mapped/MappedForeach.java @@ -1,52 +1,72 @@ /* - * Created on Jul 4, 2011 + * Copyright (c) 2002-2011 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package org.lwjgl.util.mapped; import java.util.Iterator; /** + * Iterable implementation for {@link MappedObject}. + * * @author Riven */ +public class MappedForeach implements Iterable { -public class MappedForeach implements Iterable -{ - final T mapped; - final int elementCount; + private final T mapped; + private final int elementCount; - MappedForeach(T mapped, int elementCount) - { - this.mapped = mapped; - this.elementCount = elementCount; - } + MappedForeach(T mapped, int elementCount) { + this.mapped = mapped; + this.elementCount = elementCount; + } - @Override - public Iterator iterator() - { - return new Iterator() - { - private int index = 0; + public Iterator iterator() { + return new Iterator() { - @Override - public boolean hasNext() - { - return this.index < (MappedForeach.this.elementCount); - } + private int index; - @Override - public T next() - { - mapped.viewAddress = mapped.baseAddress + (this.index++) * mapped.stride; + public boolean hasNext() { + return this.index < (MappedForeach.this.elementCount); + } - return mapped; - } + public T next() { + mapped.viewAddress = mapped.baseAddress + (this.index++) * mapped.stride; - @Override - public void remove() - { - throw new UnsupportedOperationException(); - } - }; - } -} + return mapped; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + +} \ No newline at end of file diff --git a/src/java/org/lwjgl/util/mapped/MappedHelper.java b/src/java/org/lwjgl/util/mapped/MappedHelper.java index ab749f21..21977844 100644 --- a/src/java/org/lwjgl/util/mapped/MappedHelper.java +++ b/src/java/org/lwjgl/util/mapped/MappedHelper.java @@ -1,181 +1,188 @@ /* - * Created on Jun 28, 2011 + * Copyright (c) 2002-2011 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package org.lwjgl.util.mapped; import java.nio.ByteBuffer; /** + * [INTERNAL USE ONLY] + *

+ * Helper class used by the bytecode transformer. + * * @author Riven */ +public class MappedHelper { -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"); + 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 ( 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 ( 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; + 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"); + long addr = MappedObjectUnsafe.getBufferBaseAddress(buffer); + if ( addr % align != 0 ) + throw new IllegalStateException("buffer address not aligned on " + align + " bytes"); - mo.baseAddress = mo.viewAddress = addr; - } + mo.baseAddress = mo.viewAddress = addr; + } - public static void put_views(MappedSet2 set, int view) - { - set.view(view); - } + 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(MappedSet3 set, int view) { + set.view(view); + } - public static void put_views(MappedSet4 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 void put_view(MappedObject mapped, int view) { + mapped.setViewAddress(mapped.baseAddress + view * mapped.stride); + } - public static int get_view(MappedObject mapped) - { - return (int) (mapped.viewAddress - mapped.baseAddress) / 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 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 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 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); - } + public static ByteBuffer newBuffer(long address, int capacity) { + return MappedObjectUnsafe.newBuffer(address, capacity); + } - // ---- primitive fields read/write + // ---- primitive fields read/write - // byte + // byte - public static void bput(byte value, long addr) - { - MappedObjectUnsafe.INSTANCE.putByte(addr, value); - } + public static void bput(byte value, long addr) { + MappedObjectUnsafe.INSTANCE.putByte(addr, value); + } - public static byte bget(long addr) - { - return MappedObjectUnsafe.INSTANCE.getByte(addr); - } + public static byte bget(long addr) { + return MappedObjectUnsafe.INSTANCE.getByte(addr); + } - // short + // short - public static void sput(short value, long addr) - { - MappedObjectUnsafe.INSTANCE.putShort(addr, value); - } + public static void sput(short value, long addr) { + MappedObjectUnsafe.INSTANCE.putShort(addr, value); + } - public static short sget(long addr) - { - return MappedObjectUnsafe.INSTANCE.getShort(addr); - } + public static short sget(long addr) { + return MappedObjectUnsafe.INSTANCE.getShort(addr); + } - // char + // char - public static void cput(char value, long addr) - { - MappedObjectUnsafe.INSTANCE.putChar(addr, value); - } + public static void cput(char value, long addr) { + MappedObjectUnsafe.INSTANCE.putChar(addr, value); + } - public static char cget(long addr) - { - return MappedObjectUnsafe.INSTANCE.getChar(addr); - } + public static char cget(long addr) { + return MappedObjectUnsafe.INSTANCE.getChar(addr); + } - // int + // int - public static void iput(int value, long addr) - { - MappedObjectUnsafe.INSTANCE.putInt(addr, value); - } + public static void iput(int value, long addr) { + MappedObjectUnsafe.INSTANCE.putInt(addr, value); + } - public static int iget(long addr) - { - return MappedObjectUnsafe.INSTANCE.getInt(addr); - } + public static int iget(long addr) { + return MappedObjectUnsafe.INSTANCE.getInt(addr); + } - // float + // float - public static void fput(float value, long addr) - { - MappedObjectUnsafe.INSTANCE.putFloat(addr, value); - } + public static void fput(float value, long addr) { + MappedObjectUnsafe.INSTANCE.putFloat(addr, value); + } - public static float fget(long addr) - { - return MappedObjectUnsafe.INSTANCE.getFloat(addr); - } + public static float fget(long addr) { + return MappedObjectUnsafe.INSTANCE.getFloat(addr); + } - // long + // long - public static void jput(long value, long addr) - { - MappedObjectUnsafe.INSTANCE.putLong(addr, value); - } + public static void jput(long value, long addr) { + MappedObjectUnsafe.INSTANCE.putLong(addr, value); + } - public static long jget(long addr) - { - return MappedObjectUnsafe.INSTANCE.getLong(addr); - } + public static long jget(long addr) { + return MappedObjectUnsafe.INSTANCE.getLong(addr); + } - // double + // double - public static void dput(double value, long addr) - { - MappedObjectUnsafe.INSTANCE.putDouble(addr, value); - } + public static void dput(double value, long addr) { + MappedObjectUnsafe.INSTANCE.putDouble(addr, value); + } + + public static double dget(long addr) { + return MappedObjectUnsafe.INSTANCE.getDouble(addr); + } - public static double dget(long addr) - { - return MappedObjectUnsafe.INSTANCE.getDouble(addr); - } } \ No newline at end of file diff --git a/src/java/org/lwjgl/util/mapped/MappedObject.java b/src/java/org/lwjgl/util/mapped/MappedObject.java index 2d05ce5b..e08ec3ae 100644 --- a/src/java/org/lwjgl/util/mapped/MappedObject.java +++ b/src/java/org/lwjgl/util/mapped/MappedObject.java @@ -1,185 +1,248 @@ /* - * Created on Jun 25, 2011 + * Copyright (c) 2002-2011 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package org.lwjgl.util.mapped; +import org.lwjgl.LWJGLUtil; + import java.nio.ByteBuffer; /** + * Base superclass of all mapped objects. Classes that require + * data mapping should extend this class and also be annotated + * with {@link MappedType}. + *

+ * Subclasses may only specify the default constructor. Any code + * inside that constructor is optional, but will not run when the + * view is instantiated, see {@link #runViewConstructor()}. + *

+ * Bounds checking may be enabled through a JVM system property: org.lwjgl.util.mapped.Checks=true + * * @author Riven */ +public class MappedObject { -public class MappedObject -{ - public MappedObject() - { - // - } + static final boolean CHECKS = LWJGLUtil.getPrivilegedBoolean("org.lwjgl.util.mapped.Checks"); - // these fields are not assignable/writable by user-code - public long baseAddress; - public long viewAddress; - public int stride; - public int align; + public MappedObject() { + // + } - /** - * Holds the value of sizeof of the sub-type of this MappedObject
- *
- * The behavior of this (transformed) method does not follow the normal Java behavior.
- * Vec2.SIZEOF will yield 8 (2 floats)
- * Vec3.SIZEOF will yield 12 (3 floats)
- * This (required) notation might cause compiler warnings, which can be suppressed with @SuppressWarnings("static-access").
- * Using Java 5.0's static-import on this method will break functionality. - */ + /** The mapped object base memory address, in bytes. Read-only. */ + public long baseAddress; - // 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 + /** The mapped object view memory address, in bytes. Read-only. */ + public long viewAddress; - public final void next() - { - this.viewAddress += this.stride; - } + /** The mapped object stride, in bytes. Read-only. */ + public int stride; - /** - * Creates a MappedObject instance, mapping the memory region of the specified direct ByteBuffer.
- *
- * The behavior of this (transformed) method does not follow the normal Java behavior.
- * Vec2.map(buffer) will return a mapped Vec2 instance.
- * Vec3.map(buffer) will return a mapped Vec3 instance.
- * This (required) notation might cause compiler warnings, which can be suppressed with @SuppressWarnings("static-access").
- * Using Java 5.0's static-import on this method will break functionality. - */ + /** The mapped object memory alignment, in bytes. Read-only. */ + public int align; - @SuppressWarnings("unused") - public static T map(ByteBuffer bb) - { - // any method that calls this method will have its call-site modified - throw new InternalError("type not registered"); - } + /** + * Holds the value of sizeof of the sub-type of this MappedObject
+ *
+ * The behavior of this (transformed) method does not follow the normal Java behavior.
+ * Vec2.SIZEOF will yield 8 (2 floats)
+ * Vec3.SIZEOF will yield 12 (3 floats)
+ * This (required) notation might cause compiler warnings, which can be suppressed with @SuppressWarnings("static-access").
+ * Using Java 5.0's static-import on this method will break functionality. + */ + public static int SIZEOF = -1; // any method that calls these field will have its call-site modified ('final' per subtype) - @SuppressWarnings("unused") - public static T map(long address, int capacity) - { - // any method that calls this method will have its call-site modified - throw new InternalError("type not registered"); - } + /** + * The mapped object view offset, in elements. Read/write. + * This is a virtual field, used as a convenient getter/setter for {@see viewAddress}. + */ + public int view; - /** - * Creates a MappedObject instance, mapping the memory region of an allocated direct ByteBuffer with a capacity of elementCount*SIZEOF - *
- * The behavior of this (transformed) method does not follow the normal Java behavior.
- * Vec2.malloc(int) will return a mapped Vec2 instance.
- * Vec3.malloc(int) will return a mapped Vec3 instance.
- * This (required) notation might cause compiler warnings, which can be suppressed with @SuppressWarnings("static-access").
- * Using Java 5.0's static-import on this method will break functionality. - */ + /** Moves the current view to the next element. */ + public final void next() { + setViewAddress(this.viewAddress + this.stride); + } - @SuppressWarnings("unused") - public static T malloc(int elementCount) - { - // any method that calls this method will have its call-site modified - throw new InternalError("type not registered"); - } + final void setViewAddress(final long address) { + if ( CHECKS ) + checkAddress(address); + this.viewAddress = address; + } - /** - * Creates an identical new MappedObject instance, comparable to the - * contract of ByteBuffer.duplicate() - */ + final void checkAddress(final long address) { + if ( preventGC.capacity() < (address + stride - baseAddress) ) + throw new IndexOutOfBoundsException(); + } - public final T dup() - { - // 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 the specified direct ByteBuffer. + *

+ * The behavior of this (transformed) method does not follow the normal Java behavior.
+ * Vec2.map(buffer) will return a mapped Vec2 instance.
+ * Vec3.map(buffer) will return a mapped Vec3 instance.
+ * This (required) notation might cause compiler warnings, which can be suppressed with @SuppressWarnings("static-access").
+ * Using Java 5.0's static-import on this method will break functionality. + */ + @SuppressWarnings("unused") + public static T map(ByteBuffer bb) { + // 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() - */ + /** + * Creates a MappedObject instance, mapping the memory region specified. This is useful for mapping + * arbitrary regions in memory, e.g. OpenCL CLMem objects, without creating a ByteBuffer first. + *

+ * The behavior of this (transformed) method does not follow the normal Java behavior.
+ * Vec2.map(buffer) will return a mapped Vec2 instance.
+ * Vec3.map(buffer) will return a mapped Vec3 instance.
+ * This (required) notation might cause compiler warnings, which can be suppressed with @SuppressWarnings("static-access").
+ * Using Java 5.0's static-import on this method will break functionality. + */ + @SuppressWarnings("unused") + public static T map(long address, int capacity) { + // any method that calls this method will have its call-site modified + throw new InternalError("type not registered"); + } - public final T slice() - { - // 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 elementCount*SIZEOF + *

+ * The behavior of this (transformed) method does not follow the normal Java behavior.
+ * Vec2.malloc(int) will return a mapped Vec2 instance.
+ * Vec3.malloc(int) will return a mapped Vec3 instance.
+ * This (required) notation might cause compiler warnings, which can be suppressed with @SuppressWarnings("static-access").
+ * Using Java 5.0's static-import on this method will break functionality. + */ + @SuppressWarnings("unused") + public static T malloc(int elementCount) { + // any method that calls this method will have its call-site modified + throw new InternalError("type not registered"); + } - public final void runViewConstructor() - { - // 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 {@link ByteBuffer#duplicate}. This is useful when more than one + * views of the mapped object are required at the same time, e.g. in + * multithreaded access. + */ + public final T dup() { + // any method that calls this method will have its call-site modified + throw new InternalError("type not registered"); + } - /** - * Copies and amount of SIZEOF bytes, from the current - * mapped object, to the specified mapped object. - */ + /** + * Creates a new MappedObject instance, with a base offset equal to + * the offset of the current view, comparable to the contract of {@link ByteBuffer#slice}. + */ + public final T slice() { + // any method that calls this method will have its call-site modified + throw new InternalError("type not registered"); + } - @SuppressWarnings("unused") - public final void copyTo(T target) - { - // any method that calls this method will have its call-site modified - throw new InternalError("type not registered"); - } + /** + * Any code in the default constructor will not run automatically. This method + * can be used to run execute that code on the current view. + */ + public final void runViewConstructor() { + // any method that calls this method will have its call-site modified + throw new InternalError("type not registered"); + } - /** - * Copies and amount of SIZEOF*instances bytes, from the - * current mapped object, to the specified mapped object. - */ + /** + * Copies and amount of SIZEOF bytes, from the current + * mapped object, to the specified mapped object. + */ + @SuppressWarnings("unused") + public final void copyTo(T target) { + // any method that calls this method will have its call-site modified + throw new InternalError("type not registered"); + } - @SuppressWarnings("unused") - public final void copyRange(T target, int instances) - { - // any method that calls this method will have its call-site modified - throw new InternalError("type not registered"); - } + /** + * Copies and amount of SIZEOF*instances bytes, from the + * current mapped object, to the specified mapped object. + */ + @SuppressWarnings("unused") + public final 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 that will step through - * elementCount views, leaving the view at - * the last valid value.
- *
- * For convenience you are encouraged to static-import this specific method: - * import static org.lwjgl.util.mapped.MappedObject.foreach; - */ + /** + * Creates an {@link Iterable} that will step through + * elementCount views, leaving the view at + * the last valid value.
+ *

+ * For convenience you are encouraged to static-import this specific method: + * import static org.lwjgl.util.mapped.MappedObject.foreach; + */ + public static MappedForeach foreach(T mapped, int elementCount) { + return new MappedForeach(mapped, elementCount); + } - public static MappedForeach foreach(T mapped, int elementCount) - { - return new MappedForeach(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 T configure(T mapped, int stride, int offset) { + if ( mapped.baseAddress != mapped.viewAddress ) + throw new IllegalStateException("view must be zero"); - /** - * Configures a newly initiated mapped object with the specified stride and offset. - * @throws IllegalStateException if view is not at index 0 - */ + 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); - public static final T configure(T mapped, int stride, int offset) - { - if (mapped.baseAddress != mapped.viewAddress) - throw new IllegalStateException("view must be zero"); + 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); - 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); + mapped.baseAddress += offset; + mapped.viewAddress += offset; + mapped.stride = stride; - 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); + return mapped; + } - mapped.baseAddress += offset; - mapped.viewAddress += offset; - mapped.stride = stride; + ByteBuffer preventGC; - return mapped; - } + /** + * Returns the {@link ByteBuffer} that backs this mapped object. + * + * @return the backing buffer + */ + public ByteBuffer backingByteBuffer() { + return this.preventGC; + } - ByteBuffer preventGC; - - public ByteBuffer backingByteBuffer() - { - return this.preventGC; - } -} +} \ No newline at end of file diff --git a/src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java b/src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java index a8fd803e..0452937f 100644 --- a/src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java +++ b/src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java @@ -1,9 +1,38 @@ /* - * Created on Jun 24, 2011 + * Copyright (c) 2002-2011 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package org.lwjgl.util.mapped; +import org.lwjgl.LWJGLUtil; + import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; @@ -12,125 +41,117 @@ import java.net.URLClassLoader; import java.util.Arrays; /** + * This classloader is responsible for applying the bytecode transformation to mapped objects. + * The transformation can either be applied using a Java agent, or with the convenient {@link #fork} method. + * * @author Riven */ +public class MappedObjectClassLoader extends URLClassLoader { -public class MappedObjectClassLoader extends URLClassLoader -{ - static final String MAPPEDOBJECT_PACKAGE_PREFIX; + static final String MAPPEDOBJECT_PACKAGE_PREFIX; - static - { - MAPPEDOBJECT_PACKAGE_PREFIX = MappedObjectClassLoader.class.getPackage().getName() + "."; - } + static { + MAPPEDOBJECT_PACKAGE_PREFIX = MappedObjectClassLoader.class.getPackage().getName() + "."; + } - static boolean FORKED = false; + static boolean FORKED; - public static boolean fork(Class< ? > mainClass, String[] args) - { - if (FORKED) - { - return false; - } + /** + * Forks the specified class containing a main method, passing the specified arguments. See + * {@link org.lwjgl.test.mapped.TestMappedObject} for example usage. + * + * @param mainClass the class containing the main method + * @param args the arguments to pass + * + * @return true if the fork was successful. + */ + public static boolean fork(Class mainClass, String[] args) { + if ( FORKED ) { + return false; + } - FORKED = true; + 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); - } + 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; - } + return true; + } - private MappedObjectClassLoader(Class< ? > mainClass) - { - super(((URLClassLoader) mainClass.getClassLoader()).getURLs()); - } + private MappedObjectClassLoader(Class mainClass) { + super(((URLClassLoader)mainClass.getClassLoader()).getURLs()); + } - private static long total_time_transforming; + 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); + @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); + 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); + // 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('.', '/'); + String className = name.replace('.', '/'); - if (MappedObjectTransformer.PRINT_ACTIVITY) - System.out.println(MappedObjectClassLoader.class.getSimpleName() + ": " + className); + if ( MappedObjectTransformer.PRINT_ACTIVITY ) + LWJGLUtil.log(MappedObjectClassLoader.class.getSimpleName() + ": " + className); - byte[] bytecode = readStream(this.getResourceAsStream(className.concat(".class"))); + 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); + 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)"); + if ( MappedObjectTransformer.PRINT_TIMING ) + LWJGLUtil.log("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; - } + 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); - } -} + private 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); + } + +} \ No newline at end of file diff --git a/src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java b/src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java index 9ff1f0cb..6e2f5365 100644 --- a/src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java +++ b/src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java @@ -4,6 +4,10 @@ package org.lwjgl.util.mapped; +import org.lwjgl.LWJGLUtil; +import org.objectweb.asm.*; +import org.objectweb.asm.util.TraceClassVisitor; + import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.Field; @@ -12,577 +16,527 @@ 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.*; /** + * This class implements the bytecode transformation that mapped object go through. + * Mapped object classes need to first be registered with the transformer, see {@link #register(Class)}. + *

+ * The transformer supports some debugging tools, enabled through JVM system properties:
+ * org.lwjgl.util.mapped.PrintTiming=true, prints timing information for the transformation step.
+ * org.lwjgl.util.mapped.PrintActivity=true, prints activity information.
+ * org.lwjgl.util.mapped.PrintBytecode=true, prints the transformed bytecode. [not working atm]
+ * org.lwjgl.util.Debug must also be set to true for the above to work. + * * @author Riven */ +public class MappedObjectTransformer { + + static final boolean PRINT_TIMING = LWJGLUtil.DEBUG && LWJGLUtil.getPrivilegedBoolean("org.lwjgl.util.mapped.PrintTiming"); + static final boolean PRINT_ACTIVITY = LWJGLUtil.DEBUG && LWJGLUtil.getPrivilegedBoolean("org.lwjgl.util.mapped.PrintActivity"); + static final boolean PRINT_BYTECODE = false; //LWJGLUtil.DEBUG && LWJGLUtil.getPrivilegedBoolean("org.lwjgl.util.mapped.PrintBytecode"); + + private static final Map className_to_subtype; + + static { + className_to_subtype = new HashMap(); + + { + // 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") ) { + LWJGLUtil.log("Warning: " + MappedObject.class.getSimpleName() + "s have inferiour performance on Client VMs, please consider switching to a Server VM."); + } + } + + /** + * Registers a class as a mapped object. + * The class must extend {@link MappedObject} and be annotated with {@link MappedField}. + * + * @param type the mapped object class. + */ + 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(); + if ( byteLength < 0 ) + throw new IllegalStateException("invalid byte length for mapped ByteBuffer field: " + className + "." + field.getName() + " [length=" + 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 ) + LWJGLUtil.log(MappedObjectTransformer.class.getSimpleName() + ": " + className + "." + field.getName() + " [type=" + field.getType().getSimpleName() + ", offset=" + byteOffset + "]"); + + mappedType.fieldToOffset.put(field.getName(), byteOffset); + mappedType.fieldToLength.put(field.getName(), byteLength); + + advancingOffset += byteLength; + } + + if ( className_to_subtype.put(className, mappedType) != null ) { + throw new InternalError("duplicate mapped type: " + className); + } + } + + private static final String view_constructor_method = "_construct_view_"; + + 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 ( "".equals(name) ) { + if ( !"()V".equals(desc) ) + 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), "", "()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 ) + LWJGLUtil.log(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; + } + + private 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(); + + LWJGLUtil.log(dump); + } + + private static class MappedInstanceMethodAdapter extends MethodAdapter { + + private final String className; + private final String methodName; + private final String descr; + + 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)) && "".equals(methodName) && "()V".equals(signature) ) { + // stack: instance + visitInsn(POP); + // stack: - + return; + } + + for ( MappedSubtypeInfo mappedType : className_to_subtype.values() ) { + boolean isMapDirectMethod = (opcode == INVOKESTATIC && "map".equals(methodName) && className.equals(mappedType.className) && signature.equals("(JI)L" + jvmClassName(MappedObject.class) + ";")); + boolean isMapBufferMethod = (opcode == INVOKESTATIC && "map".equals(methodName) && className.equals(mappedType.className) && signature.equals("(Ljava/nio/ByteBuffer;)L" + jvmClassName(MappedObject.class) + ";")); + boolean isMallocMethod = (opcode == INVOKESTATIC && "malloc".equals(methodName) && 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, "", "()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 && "dup".equals(methodName) && 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, "", "()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 && "slice".equals(methodName) && 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, "", "()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 && "runViewConstructor".equals(methodName) && className.equals(mappedType.className) && "()V".equals(signature) ) { + // stack: this + super.visitInsn(DUP); + // stack: this, this + super.visitMethodInsn(INVOKEVIRTUAL, className, view_constructor_method, "()V"); + // stack: this + return; + } + + // + + if ( opcode == INVOKEVIRTUAL && "copyTo".equals(methodName) && 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 && "copyRange".equals(methodName) && 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); + } + + private 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 ( "view".equals(fieldName) && 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 ( "SIZEOF".equals(fieldName) ) { + if ( !"I".equals(typeName) ) + throw new IllegalStateException(); + + if ( opcode == GETSTATIC ) { + pushInt(super.mv, mappedSubtype.sizeof); + return; + } + if ( opcode == PUTSTATIC ) { + throwAccessErrorOnReadOnlyField(className, fieldName); + } + } + + if ( "view".equals(fieldName) ) { + if ( !"I".equals(typeName) ) + 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 ( "align".equals(fieldName) ) { + if ( !"I".equals(typeName) ) + 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 ( "stride".equals(fieldName) ) { + if ( !"I".equals(typeName) ) + throw new IllegalStateException(); + + if ( opcode == GETFIELD ) { + // do not change a thing + } + if ( opcode == PUTFIELD ) { + throwAccessErrorOnReadOnlyField(className, fieldName); + } + } + + if ( "baseAddress".equals(fieldName) || "viewAddress".equals(fieldName) ) { + if ( !"J".equals(typeName) ) + 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; + } + } + + private 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(value); + } + + private static String jvmClassName(Class type) { + return type.getName().replace('.', '/'); + } + + private static class MappedSubtypeInfo { + + public final String className; + + public int sizeof; + public int align; + + public Map fieldToOffset; + public Map fieldToLength; + + MappedSubtypeInfo(String className, int sizeof, int align) { + this.className = className; + + this.sizeof = sizeof; + this.align = align; + + this.fieldToOffset = new HashMap(); + this.fieldToLength = new HashMap(); + } + } -public class MappedObjectTransformer -{ - static final boolean PRINT_TIMING = true; - static final boolean PRINT_ACTIVITY = false; - static final boolean PRINT_BYTECODE = false; - static final Map className_to_subtype; - - static - { - className_to_subtype = new HashMap(); - - { - // 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("")) - { - 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), "", "()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, "", "(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("") && 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, "", "()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, "", "()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, "", "()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 fieldToOffset; - public Map fieldToLength; - - public MappedSubtypeInfo(String className, int sizeof, int align) - { - this.className = className; - - this.sizeof = sizeof; - this.align = align; - - this.fieldToOffset = new HashMap(); - this.fieldToLength = new HashMap(); - } - } } \ No newline at end of file diff --git a/src/java/org/lwjgl/util/mapped/MappedObjectUnsafe.java b/src/java/org/lwjgl/util/mapped/MappedObjectUnsafe.java index 3cb4cca1..e4fb56b2 100644 --- a/src/java/org/lwjgl/util/mapped/MappedObjectUnsafe.java +++ b/src/java/org/lwjgl/util/mapped/MappedObjectUnsafe.java @@ -1,7 +1,34 @@ /* - * Created on Jun 24, 2011 + * Copyright (c) 2002-2011 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package org.lwjgl.util.mapped; import java.lang.reflect.Field; @@ -11,73 +38,57 @@ import java.nio.ByteBuffer; import sun.misc.Unsafe; /** + * [INTERNAL USE ONLY] + * * @author Riven */ +public class MappedObjectUnsafe { -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()); + public static final Unsafe INSTANCE = getUnsafeInstance(); - private static final long BUFFER_ADDRESS_OFFSET = getObjectFieldOffset(ByteBuffer.class, "address"); - private static final long BUFFER_CAPACITY_OFFSET = getObjectFieldOffset(ByteBuffer.class, "capacity"); + 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); + } - public static long getBufferBaseAddress(Buffer buffer) - { - return INSTANCE.getLong(buffer, BUFFER_ADDRESS_OFFSET); - } + private static final ByteBuffer global = ByteBuffer.allocateDirect(4 * 1024); - private static final ByteBuffer global = ByteBuffer.allocateDirect(4 * 1024); + static ByteBuffer newBuffer(long address, int capacity) { + if ( address <= 0L || capacity < 0 ) + throw new IllegalStateException("you almost crashed the jvm"); - 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; + } - 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(); + } + } - 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(); - } - } -} +} \ No newline at end of file diff --git a/src/java/org/lwjgl/util/mapped/MappedSet.java b/src/java/org/lwjgl/util/mapped/MappedSet.java index 4210f74e..925ac156 100644 --- a/src/java/org/lwjgl/util/mapped/MappedSet.java +++ b/src/java/org/lwjgl/util/mapped/MappedSet.java @@ -1,23 +1,69 @@ /* - * Created on Jul 11, 2011 + * Copyright (c) 2002-2011 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package org.lwjgl.util.mapped; -public class MappedSet -{ - public static MappedSet2 create(MappedObject a, MappedObject b) - { - return new MappedSet2(a, b); - } +/** + * Factory for mapped sets. A mapped set can be used as a Structure of Arrays by + * linking together the view of two or more mapped objects. Changing the view + * of the mapped set, changes the corresponding view of all the mapped objects in + * the set. + */ +public class MappedSet { - public static MappedSet3 create(MappedObject a, MappedObject b, MappedObject c) - { - return new MappedSet3(a, b, c); - } + /** + * Creates a MappedSet by linking the specified MappedObjects. + * + * @return the mapped set. + */ + public static MappedSet2 create(MappedObject a, MappedObject b) { + return new MappedSet2(a, b); + } - public static MappedSet4 create(MappedObject a, MappedObject b, MappedObject c, MappedObject d) - { - return new MappedSet4(a, b, c, d); - } -} + /** + * Creates a MappedSet by linking the specified MappedObjects. + * + * @return the mapped set. + */ + public static MappedSet3 create(MappedObject a, MappedObject b, MappedObject c) { + return new MappedSet3(a, b, c); + } + + /** + * Creates a MappedSet by linking the specified MappedObjects. + * + * @return the mapped set. + */ + public static MappedSet4 create(MappedObject a, MappedObject b, MappedObject c, MappedObject d) { + return new MappedSet4(a, b, c, d); + } + +} \ No newline at end of file diff --git a/src/java/org/lwjgl/util/mapped/MappedSet2.java b/src/java/org/lwjgl/util/mapped/MappedSet2.java index 2a15080a..2fb3d022 100644 --- a/src/java/org/lwjgl/util/mapped/MappedSet2.java +++ b/src/java/org/lwjgl/util/mapped/MappedSet2.java @@ -1,30 +1,56 @@ /* - * Created on Jul 11, 2011 + * Copyright (c) 2002-2011 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package org.lwjgl.util.mapped; -public class MappedSet2 -{ - private final MappedObject a, b; +/** MappedSet implementation for two MappedObjects. */ +public class MappedSet2 { - MappedSet2(MappedObject a, MappedObject b) - { - this.a = a; - this.b = b; - } + private final MappedObject a, b; - public int view; + MappedSet2(MappedObject a, MappedObject b) { + this.a = a; + this.b = b; + } - 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 int view; - public void next() - { - this.a.viewAddress += this.a.stride; - this.b.viewAddress += this.b.stride; - } -} + void view(int view) { + MappedHelper.put_view(this.a, view); + MappedHelper.put_view(this.b, view); + } + + public void next() { + this.a.next(); + this.b.next(); + } + +} \ No newline at end of file diff --git a/src/java/org/lwjgl/util/mapped/MappedSet3.java b/src/java/org/lwjgl/util/mapped/MappedSet3.java index 6adac0cc..c3b43b08 100644 --- a/src/java/org/lwjgl/util/mapped/MappedSet3.java +++ b/src/java/org/lwjgl/util/mapped/MappedSet3.java @@ -1,33 +1,59 @@ /* - * Created on Jul 11, 2011 + * Copyright (c) 2002-2011 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package org.lwjgl.util.mapped; -public class MappedSet3 -{ - private final MappedObject a, b, c; +/** MappedSet implementation for three MappedObjects. */ +public class MappedSet3 { - MappedSet3(MappedObject a, MappedObject b, MappedObject c) - { - this.a = a; - this.b = b; - this.c = c; - } + private final MappedObject a, b, c; - public int view; + MappedSet3(MappedObject a, MappedObject b, MappedObject c) { + this.a = a; + this.b = b; + this.c = c; + } - 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 int view; - public void next() - { - this.a.viewAddress += this.a.stride; - this.b.viewAddress += this.b.stride; - this.c.viewAddress += this.c.stride; - } -} + void view(int view) { + MappedHelper.put_view(this.a, view); + MappedHelper.put_view(this.b, view); + MappedHelper.put_view(this.c, view); + } + + public void next() { + this.a.next(); + this.b.next(); + this.c.next(); + } + +} \ No newline at end of file diff --git a/src/java/org/lwjgl/util/mapped/MappedSet4.java b/src/java/org/lwjgl/util/mapped/MappedSet4.java index a8a98eab..d3020bee 100644 --- a/src/java/org/lwjgl/util/mapped/MappedSet4.java +++ b/src/java/org/lwjgl/util/mapped/MappedSet4.java @@ -1,36 +1,62 @@ /* - * Created on Jul 11, 2011 + * Copyright (c) 2002-2011 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package org.lwjgl.util.mapped; -public class MappedSet4 -{ - private final MappedObject a, b, c, d; +/** MappedSet implementation for four MappedObjects. */ +public class MappedSet4 { - MappedSet4(MappedObject a, MappedObject b, MappedObject c, MappedObject d) - { - this.a = a; - this.b = b; - this.c = c; - this.d = d; - } + private final MappedObject a, b, c, d; - public int view; + MappedSet4(MappedObject a, MappedObject b, MappedObject c, MappedObject d) { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + } - 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 int 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; - } -} + void view(int view) { + MappedHelper.put_view(this.a, view); + MappedHelper.put_view(this.b, view); + MappedHelper.put_view(this.c, view); + MappedHelper.put_view(this.d, view); + } + + public void next() { + this.a.next(); + this.b.next(); + this.c.next(); + this.d.next(); + } + +} \ No newline at end of file diff --git a/src/java/org/lwjgl/util/mapped/MappedType.java b/src/java/org/lwjgl/util/mapped/MappedType.java index e655d066..7036ca92 100644 --- a/src/java/org/lwjgl/util/mapped/MappedType.java +++ b/src/java/org/lwjgl/util/mapped/MappedType.java @@ -1,21 +1,86 @@ /* - * Created on Jun 24, 2011 + * Copyright (c) 2002-2011 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package org.lwjgl.util.mapped; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** + * This annotation marks a class as a mapped object, which will go under bytecode + * transformation at runtime. Mapped objects cannot be instantiated directly; a data + * buffer must be mapped first and the mapped object instance will then be used as a + * view on top of the buffer. Instead of a separate instance per "element" in the buffer, + * only a single instance is used to manage everything. See {@link MappedObject} + * for API details and {@link org.lwjgl.test.mapped.TestMappedObject} for examples. + *

+ * The instance fields of the annotated class should only be limited to primitive types or + * {@link java.nio.ByteBuffer}. Static fields are supported and they can have any type. + *

+ * The purpose of mapped objects is to reduce the memory requirements required for the type + * of data that are often used in OpenGL/OpenCL programming, while at the same time enabling + * clean Java code. There are also performance benefits related to not having to copy data + * between buffers and Java objects and the removal of bounds checking when accessing + * buffer data. + * * @author Riven */ - @Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) public @interface MappedType { - int sizeof(); - int align() default 4; + /** + * The total size of the mapped object, in bytes. + * + * @return the byte size + */ + int sizeof(); - boolean autoGenerateOffsets() default true; -} + /** + * The mapped data memory alignment, in bytes. + * + * @return the memory alignment + */ + int align() default 4; + + /** + * When autoGenerateOffsets is true, byte offsets of the mapped fields will + * be generated automatically. This is convenient for packed data. For manually + * aligned data, autoGenerateOffsets must be set to false and the user needs + * to manually specify byte offsets using the {@link MappedField} annotation. + * + * @return true if automatic byte offset generation is required. + */ + boolean autoGenerateOffsets() default true; + +} \ No newline at end of file