8234049: Implementation of Memory Access API (Incubator)
Co-authored-by: Vlaidmir Ivanov <vladimir.x.ivanov@oracle.com> Reviewed-by: alanb, psandoz, chegar, rriggs, plevart, briangoetz, jrose, adinn, vlivanov
This commit is contained in:
parent
7cdecd8981
commit
8f4f088a12
@ -58,6 +58,7 @@ BOOT_MODULES += \
|
||||
java.rmi \
|
||||
java.security.sasl \
|
||||
java.xml \
|
||||
jdk.incubator.foreign \
|
||||
jdk.internal.vm.ci \
|
||||
jdk.jfr \
|
||||
jdk.management \
|
||||
@ -162,6 +163,7 @@ DOCS_MODULES += \
|
||||
jdk.jsobject \
|
||||
jdk.jshell \
|
||||
jdk.jstatd \
|
||||
jdk.incubator.foreign \
|
||||
jdk.localedata \
|
||||
jdk.management \
|
||||
jdk.management.agent \
|
||||
|
@ -159,6 +159,108 @@ endef
|
||||
|
||||
################################################################################
|
||||
|
||||
################################################################################
|
||||
# Setup a rule for generating a VarHandleMemoryAddress java class
|
||||
# Param 1 - Variable declaration prefix
|
||||
# Param 2 - Type with first letter capitalized
|
||||
define GenerateVarHandleMemoryAddress
|
||||
|
||||
$1_Type := $2
|
||||
|
||||
$1_FILENAME := $(VARHANDLES_GENSRC_DIR)/VarHandleMemoryAddressAs$$($1_Type)s.java
|
||||
|
||||
ifeq ($$($1_Type), Byte)
|
||||
$1_type := byte
|
||||
$1_BoxType := $$($1_Type)
|
||||
|
||||
$1_rawType := $$($1_type)
|
||||
$1_RawType := $$($1_Type)
|
||||
$1_RawBoxType := $$($1_BoxType)
|
||||
|
||||
$1_ARGS += -Kbyte
|
||||
endif
|
||||
|
||||
ifeq ($$($1_Type), Short)
|
||||
$1_type := short
|
||||
$1_BoxType := $$($1_Type)
|
||||
|
||||
$1_rawType := $$($1_type)
|
||||
$1_RawType := $$($1_Type)
|
||||
$1_RawBoxType := $$($1_BoxType)
|
||||
endif
|
||||
|
||||
ifeq ($$($1_Type), Char)
|
||||
$1_type := char
|
||||
$1_BoxType := Character
|
||||
|
||||
$1_rawType := $$($1_type)
|
||||
$1_RawType := $$($1_Type)
|
||||
$1_RawBoxType := $$($1_BoxType)
|
||||
endif
|
||||
|
||||
ifeq ($$($1_Type), Int)
|
||||
$1_type := int
|
||||
$1_BoxType := Integer
|
||||
|
||||
$1_rawType := $$($1_type)
|
||||
$1_RawType := $$($1_Type)
|
||||
$1_RawBoxType := $$($1_BoxType)
|
||||
|
||||
$1_ARGS += -KCAS
|
||||
$1_ARGS += -KAtomicAdd
|
||||
$1_ARGS += -KBitwise
|
||||
endif
|
||||
|
||||
ifeq ($$($1_Type), Long)
|
||||
$1_type := long
|
||||
$1_BoxType := $$($1_Type)
|
||||
|
||||
$1_rawType := $$($1_type)
|
||||
$1_RawType := $$($1_Type)
|
||||
$1_RawBoxType := $$($1_BoxType)
|
||||
|
||||
$1_ARGS += -KCAS
|
||||
$1_ARGS += -KAtomicAdd
|
||||
$1_ARGS += -KBitwise
|
||||
endif
|
||||
|
||||
ifeq ($$($1_Type), Float)
|
||||
$1_type := float
|
||||
$1_BoxType := $$($1_Type)
|
||||
|
||||
$1_rawType := int
|
||||
$1_RawType := Int
|
||||
$1_RawBoxType := Integer
|
||||
|
||||
$1_ARGS += -KCAS
|
||||
$1_ARGS += -KfloatingPoint
|
||||
endif
|
||||
|
||||
ifeq ($$($1_Type), Double)
|
||||
$1_type := double
|
||||
$1_BoxType := $$($1_Type)
|
||||
|
||||
$1_rawType := long
|
||||
$1_RawType := Long
|
||||
$1_RawBoxType := Long
|
||||
|
||||
$1_ARGS += -KCAS
|
||||
$1_ARGS += -KfloatingPoint
|
||||
endif
|
||||
|
||||
$$($1_FILENAME): $(VARHANDLES_SRC_DIR)/X-VarHandleMemoryAddressView.java.template $(BUILD_TOOLS_JDK)
|
||||
$$(call MakeDir, $$(@D))
|
||||
$(RM) $$@
|
||||
$(TOOL_SPP) -nel -K$$($1_type) \
|
||||
-Dtype=$$($1_type) -DType=$$($1_Type) -DBoxType=$$($1_BoxType) \
|
||||
-DrawType=$$($1_rawType) -DRawType=$$($1_RawType) -DRawBoxType=$$($1_RawBoxType) \
|
||||
$$($1_ARGS) -i$$< -o$$@
|
||||
|
||||
GENSRC_VARHANDLES += $$($1_FILENAME)
|
||||
endef
|
||||
|
||||
################################################################################
|
||||
|
||||
# List the types to generate source for, with capitalized first letter
|
||||
VARHANDLES_TYPES := Boolean Byte Short Char Int Long Float Double Reference
|
||||
$(foreach t, $(VARHANDLES_TYPES), \
|
||||
@ -169,6 +271,11 @@ VARHANDLES_BYTE_ARRAY_TYPES := Short Char Int Long Float Double
|
||||
$(foreach t, $(VARHANDLES_BYTE_ARRAY_TYPES), \
|
||||
$(eval $(call GenerateVarHandleByteArray,VAR_HANDLE_BYTE_ARRAY_$t,$t)))
|
||||
|
||||
# List the types to generate source for, with capitalized first letter
|
||||
VARHANDLES_MEMORY_ADDRESS_TYPES := Byte Short Char Int Long Float Double
|
||||
$(foreach t, $(VARHANDLES_MEMORY_ADDRESS_TYPES), \
|
||||
$(eval $(call GenerateVarHandleMemoryAddress,VAR_HANDLE_MEMORY_ADDRESS_$t,$t)))
|
||||
|
||||
GENSRC_JAVA_BASE += $(GENSRC_VARHANDLES)
|
||||
|
||||
# Include custom extension post hook
|
||||
|
@ -219,7 +219,9 @@ static bool trust_final_non_static_fields(ciInstanceKlass* holder) {
|
||||
// Never trust strangely unstable finals: System.out, etc.
|
||||
return false;
|
||||
// Even if general trusting is disabled, trust system-built closures in these packages.
|
||||
if (holder->is_in_package("java/lang/invoke") || holder->is_in_package("sun/invoke"))
|
||||
if (holder->is_in_package("java/lang/invoke") || holder->is_in_package("sun/invoke") ||
|
||||
holder->is_in_package("jdk/internal/foreign") || holder->is_in_package("jdk/incubator/foreign") ||
|
||||
holder->is_in_package("java/lang"))
|
||||
return true;
|
||||
// Trust VM unsafe anonymous classes. They are private API (jdk.internal.misc.Unsafe)
|
||||
// and can't be serialized, so there is no hacking of finals going on with them.
|
||||
|
@ -0,0 +1,494 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package java.lang.invoke;
|
||||
|
||||
import jdk.internal.access.foreign.MemoryAddressProxy;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
import sun.security.action.GetBooleanAction;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.BIPUSH;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.CHECKCAST;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.GETFIELD;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_0;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_1;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_2;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_3;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_4;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_5;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_M1;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.LADD;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.LALOAD;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.LASTORE;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.LLOAD;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.LMUL;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.NEWARRAY;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.DUP;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.SIPUSH;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.T_LONG;
|
||||
|
||||
class AddressVarHandleGenerator {
|
||||
private static final String DEBUG_DUMP_CLASSES_DIR_PROPERTY = "jdk.internal.foreign.ClassGenerator.DEBUG_DUMP_CLASSES_DIR";
|
||||
|
||||
private static final boolean DEBUG =
|
||||
GetBooleanAction.privilegedGetProperty("jdk.internal.foreign.ClassGenerator.DEBUG");
|
||||
|
||||
private static final Class<?> BASE_CLASS = VarHandleMemoryAddressBase.class;
|
||||
|
||||
private static final HashMap<Class<?>, Class<?>> helperClassCache;
|
||||
|
||||
static {
|
||||
helperClassCache = new HashMap<>();
|
||||
helperClassCache.put(byte.class, VarHandleMemoryAddressAsBytes.class);
|
||||
helperClassCache.put(short.class, VarHandleMemoryAddressAsShorts.class);
|
||||
helperClassCache.put(char.class, VarHandleMemoryAddressAsChars.class);
|
||||
helperClassCache.put(int.class, VarHandleMemoryAddressAsInts.class);
|
||||
helperClassCache.put(long.class, VarHandleMemoryAddressAsLongs.class);
|
||||
helperClassCache.put(float.class, VarHandleMemoryAddressAsFloats.class);
|
||||
helperClassCache.put(double.class, VarHandleMemoryAddressAsDoubles.class);
|
||||
}
|
||||
|
||||
private static final File DEBUG_DUMP_CLASSES_DIR;
|
||||
|
||||
static {
|
||||
String path = GetPropertyAction.privilegedGetProperty(DEBUG_DUMP_CLASSES_DIR_PROPERTY);
|
||||
if (path == null) {
|
||||
DEBUG_DUMP_CLASSES_DIR = null;
|
||||
} else {
|
||||
DEBUG_DUMP_CLASSES_DIR = new File(path);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
|
||||
private final String implClassName;
|
||||
private final int dimensions;
|
||||
private final Class<?> carrier;
|
||||
private final Class<?> helperClass;
|
||||
private final VarForm form;
|
||||
|
||||
AddressVarHandleGenerator(Class<?> carrier, int dims) {
|
||||
this.dimensions = dims;
|
||||
this.carrier = carrier;
|
||||
Class<?>[] components = new Class<?>[dimensions];
|
||||
Arrays.fill(components, long.class);
|
||||
this.form = new VarForm(BASE_CLASS, MemoryAddressProxy.class, carrier, components);
|
||||
this.helperClass = helperClassCache.get(carrier);
|
||||
this.implClassName = helperClass.getName().replace('.', '/') + dimensions;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a VarHandle memory access factory.
|
||||
* The factory has type (ZJJ[J)VarHandle.
|
||||
*/
|
||||
MethodHandle generateHandleFactory() {
|
||||
Class<?> implCls = generateClass();
|
||||
try {
|
||||
Class<?>[] components = new Class<?>[dimensions];
|
||||
Arrays.fill(components, long.class);
|
||||
|
||||
VarForm form = new VarForm(implCls, MemoryAddressProxy.class, carrier, components);
|
||||
|
||||
MethodType constrType = MethodType.methodType(void.class, VarForm.class, boolean.class, long.class, long.class, long.class, long[].class);
|
||||
MethodHandle constr = MethodHandles.Lookup.IMPL_LOOKUP.findConstructor(implCls, constrType);
|
||||
constr = MethodHandles.insertArguments(constr, 0, form);
|
||||
return constr;
|
||||
} catch (Throwable ex) {
|
||||
throw new AssertionError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a specialized VarHandle class for given carrier
|
||||
* and access coordinates.
|
||||
*/
|
||||
Class<?> generateClass() {
|
||||
BinderClassWriter cw = new BinderClassWriter();
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("Generating header implementation class");
|
||||
}
|
||||
|
||||
cw.visit(52, ACC_PUBLIC | ACC_SUPER, implClassName, null, Type.getInternalName(BASE_CLASS), null);
|
||||
|
||||
//add dimension fields
|
||||
for (int i = 0; i < dimensions; i++) {
|
||||
cw.visitField(ACC_PRIVATE | ACC_FINAL, "dim" + i, "J", null, null);
|
||||
}
|
||||
|
||||
addConstructor(cw);
|
||||
|
||||
addAccessModeTypeMethod(cw);
|
||||
|
||||
addStridesAccessor(cw);
|
||||
|
||||
addCarrierAccessor(cw);
|
||||
|
||||
for (VarHandle.AccessMode mode : VarHandle.AccessMode.values()) {
|
||||
addAccessModeMethodIfNeeded(mode, cw);
|
||||
}
|
||||
|
||||
|
||||
cw.visitEnd();
|
||||
byte[] classBytes = cw.toByteArray();
|
||||
return defineClass(cw, classBytes);
|
||||
}
|
||||
|
||||
void addConstructor(BinderClassWriter cw) {
|
||||
MethodType constrType = MethodType.methodType(void.class, VarForm.class, boolean.class, long.class, long.class, long.class, long[].class);
|
||||
MethodVisitor mv = cw.visitMethod(0, "<init>", constrType.toMethodDescriptorString(), null, null);
|
||||
mv.visitCode();
|
||||
//super call
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(VarForm.class));
|
||||
mv.visitVarInsn(ILOAD, 2);
|
||||
mv.visitVarInsn(LLOAD, 3);
|
||||
mv.visitVarInsn(LLOAD, 5);
|
||||
mv.visitVarInsn(LLOAD, 7);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(BASE_CLASS), "<init>",
|
||||
MethodType.methodType(void.class, VarForm.class, boolean.class, long.class, long.class, long.class).toMethodDescriptorString(), false);
|
||||
//init dimensions
|
||||
for (int i = 0 ; i < dimensions ; i++) {
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 9);
|
||||
mv.visitLdcInsn(i);
|
||||
mv.visitInsn(LALOAD);
|
||||
mv.visitFieldInsn(PUTFIELD, implClassName, "dim" + i, "J");
|
||||
}
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
void addAccessModeTypeMethod(BinderClassWriter cw) {
|
||||
MethodType modeMethType = MethodType.methodType(MethodType.class, VarHandle.AccessMode.class);
|
||||
MethodVisitor mv = cw.visitMethod(ACC_FINAL, "accessModeTypeUncached", modeMethType.toMethodDescriptorString(), null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitFieldInsn(GETFIELD, Type.getInternalName(VarHandle.AccessMode.class), "at", Type.getDescriptor(VarHandle.AccessType.class));
|
||||
mv.visitLdcInsn(cw.makeConstantPoolPatch(MemoryAddressProxy.class));
|
||||
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class.class));
|
||||
mv.visitLdcInsn(cw.makeConstantPoolPatch(carrier));
|
||||
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class.class));
|
||||
|
||||
Class<?>[] dims = new Class<?>[dimensions];
|
||||
Arrays.fill(dims, long.class);
|
||||
mv.visitLdcInsn(cw.makeConstantPoolPatch(dims));
|
||||
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class[].class));
|
||||
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(VarHandle.AccessType.class),
|
||||
"accessModeType", MethodType.methodType(MethodType.class, Class.class, Class.class, Class[].class).toMethodDescriptorString(), false);
|
||||
|
||||
mv.visitInsn(ARETURN);
|
||||
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
void addAccessModeMethodIfNeeded(VarHandle.AccessMode mode, BinderClassWriter cw) {
|
||||
String methName = mode.methodName();
|
||||
MethodType methType = form.getMethodType(mode.at.ordinal())
|
||||
.insertParameterTypes(0, BASE_CLASS);
|
||||
|
||||
try {
|
||||
MethodType helperType = methType.insertParameterTypes(2, long.class);
|
||||
if (dimensions > 0) {
|
||||
helperType = helperType.dropParameterTypes(3, 3 + dimensions);
|
||||
}
|
||||
//try to resolve...
|
||||
String helperMethodName = methName + "0";
|
||||
MethodHandles.Lookup.IMPL_LOOKUP
|
||||
.findStatic(helperClass,
|
||||
helperMethodName,
|
||||
helperType);
|
||||
|
||||
|
||||
MethodVisitor mv = cw.visitMethod(ACC_STATIC, methName, methType.toMethodDescriptorString(), null, null);
|
||||
mv.visitAnnotation(Type.getDescriptor(ForceInline.class), true);
|
||||
mv.visitCode();
|
||||
|
||||
mv.visitVarInsn(ALOAD, 0); // handle impl
|
||||
mv.visitVarInsn(ALOAD, 1); // receiver
|
||||
|
||||
// offset calculation
|
||||
int slot = 2;
|
||||
mv.visitVarInsn(ALOAD, 0); // load recv
|
||||
mv.visitFieldInsn(GETFIELD, Type.getInternalName(BASE_CLASS), "offset", "J");
|
||||
for (int i = 0 ; i < dimensions ; i++) {
|
||||
mv.visitVarInsn(ALOAD, 0); // load recv
|
||||
mv.visitTypeInsn(CHECKCAST, implClassName);
|
||||
mv.visitFieldInsn(GETFIELD, implClassName, "dim" + i, "J");
|
||||
mv.visitVarInsn(LLOAD, slot);
|
||||
mv.visitInsn(LMUL);
|
||||
mv.visitInsn(LADD);
|
||||
slot += 2;
|
||||
}
|
||||
|
||||
for (int i = 2 + dimensions; i < methType.parameterCount() ; i++) {
|
||||
Class<?> param = methType.parameterType(i);
|
||||
mv.visitVarInsn(loadInsn(param), slot); // load index
|
||||
slot += getSlotsForType(param);
|
||||
}
|
||||
|
||||
//call helper
|
||||
mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(helperClass), helperMethodName,
|
||||
helperType.toMethodDescriptorString(), false);
|
||||
|
||||
mv.visitInsn(returnInsn(helperType.returnType()));
|
||||
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
//not found, skip
|
||||
}
|
||||
}
|
||||
|
||||
void addStridesAccessor(BinderClassWriter cw) {
|
||||
MethodVisitor mv = cw.visitMethod(ACC_FINAL, "strides", "()[J", null, null);
|
||||
mv.visitCode();
|
||||
iConstInsn(mv, dimensions);
|
||||
mv.visitIntInsn(NEWARRAY, T_LONG);
|
||||
|
||||
for (int i = 0 ; i < dimensions ; i++) {
|
||||
mv.visitInsn(DUP);
|
||||
iConstInsn(mv, i);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, implClassName, "dim" + i, "J");
|
||||
mv.visitInsn(LASTORE);
|
||||
}
|
||||
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
void addCarrierAccessor(BinderClassWriter cw) {
|
||||
MethodVisitor mv = cw.visitMethod(ACC_FINAL, "carrier", "()Ljava/lang/Class;", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitLdcInsn(cw.makeConstantPoolPatch(carrier));
|
||||
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class.class));
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
//where
|
||||
private Class<?> defineClass(BinderClassWriter cw, byte[] classBytes) {
|
||||
try {
|
||||
if (DEBUG_DUMP_CLASSES_DIR != null) {
|
||||
debugWriteClassToFile(classBytes);
|
||||
}
|
||||
Object[] patches = cw.resolvePatches(classBytes);
|
||||
Class<?> c = U.defineAnonymousClass(BASE_CLASS, classBytes, patches);
|
||||
return c;
|
||||
} catch (Throwable e) {
|
||||
debugPrintClass(classBytes);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// shared code generation helpers
|
||||
|
||||
private static int getSlotsForType(Class<?> c) {
|
||||
if (c == long.class || c == double.class) {
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an actual return instruction conforming to the given return type.
|
||||
*/
|
||||
private int returnInsn(Class<?> type) {
|
||||
return switch (LambdaForm.BasicType.basicType(type)) {
|
||||
case I_TYPE -> Opcodes.IRETURN;
|
||||
case J_TYPE -> Opcodes.LRETURN;
|
||||
case F_TYPE -> Opcodes.FRETURN;
|
||||
case D_TYPE -> Opcodes.DRETURN;
|
||||
case L_TYPE -> Opcodes.ARETURN;
|
||||
case V_TYPE -> RETURN;
|
||||
};
|
||||
}
|
||||
|
||||
private int loadInsn(Class<?> type) {
|
||||
return switch (LambdaForm.BasicType.basicType(type)) {
|
||||
case I_TYPE -> Opcodes.ILOAD;
|
||||
case J_TYPE -> LLOAD;
|
||||
case F_TYPE -> Opcodes.FLOAD;
|
||||
case D_TYPE -> Opcodes.DLOAD;
|
||||
case L_TYPE -> Opcodes.ALOAD;
|
||||
case V_TYPE -> throw new IllegalStateException("Cannot load void");
|
||||
};
|
||||
}
|
||||
|
||||
private static void iConstInsn(MethodVisitor mv, int i) {
|
||||
switch (i) {
|
||||
case -1, 0, 1, 2, 3, 4, 5:
|
||||
mv.visitInsn(ICONST_0 + i);
|
||||
break;
|
||||
default:
|
||||
if(i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(BIPUSH, i);
|
||||
} else if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(SIPUSH, i);
|
||||
} else {
|
||||
mv.visitLdcInsn(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// debug helpers
|
||||
|
||||
private static String debugPrintClass(byte[] classFile) {
|
||||
ClassReader cr = new ClassReader(classFile);
|
||||
StringWriter sw = new StringWriter();
|
||||
cr.accept(new TraceClassVisitor(new PrintWriter(sw)), 0);
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
private void debugWriteClassToFile(byte[] classFile) {
|
||||
File file = new File(DEBUG_DUMP_CLASSES_DIR, implClassName + ".class");
|
||||
|
||||
if (DEBUG) {
|
||||
System.err.println("Dumping class " + implClassName + " to " + file);
|
||||
}
|
||||
|
||||
try {
|
||||
debugWriteDataToFile(classFile, file);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to write class " + implClassName + " to file " + file);
|
||||
}
|
||||
}
|
||||
|
||||
private void debugWriteDataToFile(byte[] data, File file) {
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
if (file.exists()) {
|
||||
throw new RuntimeException("Failed to remove pre-existing file " + file);
|
||||
}
|
||||
|
||||
File parent = file.getParentFile();
|
||||
if (!parent.exists()) {
|
||||
parent.mkdirs();
|
||||
}
|
||||
if (!parent.exists()) {
|
||||
throw new RuntimeException("Failed to create " + parent);
|
||||
}
|
||||
if (!parent.isDirectory()) {
|
||||
throw new RuntimeException(parent + " is not a directory");
|
||||
}
|
||||
|
||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||
fos.write(data);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to write class " + implClassName + " to file " + file);
|
||||
}
|
||||
}
|
||||
|
||||
static class BinderClassWriter extends ClassWriter {
|
||||
|
||||
private final ArrayList<ConstantPoolPatch> cpPatches = new ArrayList<>();
|
||||
private int curUniquePatchIndex = 0;
|
||||
|
||||
BinderClassWriter() {
|
||||
super(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
}
|
||||
|
||||
public String makeConstantPoolPatch(Object o) {
|
||||
int myUniqueIndex = curUniquePatchIndex++;
|
||||
String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + myUniqueIndex;
|
||||
int index = newConst(cpPlaceholder);
|
||||
cpPatches.add(new ConstantPoolPatch(index, cpPlaceholder, o));
|
||||
return cpPlaceholder;
|
||||
}
|
||||
|
||||
public Object[] resolvePatches(byte[] classFile) {
|
||||
if (cpPatches.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int size = ((classFile[8] & 0xFF) << 8) | (classFile[9] & 0xFF);
|
||||
|
||||
Object[] patches = new Object[size];
|
||||
for (ConstantPoolPatch p : cpPatches) {
|
||||
if (p.index >= size) {
|
||||
throw new InternalError("Failed to resolve constant pool patch entries");
|
||||
}
|
||||
patches[p.index] = p.value;
|
||||
}
|
||||
|
||||
return patches;
|
||||
}
|
||||
|
||||
static class ConstantPoolPatch {
|
||||
final int index;
|
||||
final String placeholder;
|
||||
final Object value;
|
||||
|
||||
ConstantPoolPatch(int index, String placeholder, Object value) {
|
||||
this.index = index;
|
||||
this.placeholder = placeholder;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CpPatch/index="+index+",placeholder="+placeholder+",value="+value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -41,6 +41,7 @@ import sun.invoke.util.VerifyType;
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@ -1790,6 +1791,44 @@ abstract class MethodHandleImpl {
|
||||
invokerMethodTypes, callSiteMethodTypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VarHandle memoryAddressViewVarHandle(Class<?> carrier, long alignmentMask,
|
||||
ByteOrder order, long offset, long[] strides) {
|
||||
return VarHandles.makeMemoryAddressViewHandle(carrier, alignmentMask, order, offset, strides);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> memoryAddressCarrier(VarHandle handle) {
|
||||
return checkMemAccessHandle(handle).carrier();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long memoryAddressAlignmentMask(VarHandle handle) {
|
||||
return checkMemAccessHandle(handle).alignmentMask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteOrder memoryAddressByteOrder(VarHandle handle) {
|
||||
return checkMemAccessHandle(handle).be ?
|
||||
ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long memoryAddressOffset(VarHandle handle) {
|
||||
return checkMemAccessHandle(handle).offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long[] memoryAddressStrides(VarHandle handle) {
|
||||
return checkMemAccessHandle(handle).strides();
|
||||
}
|
||||
|
||||
private VarHandleMemoryAddressBase checkMemAccessHandle(VarHandle handle) {
|
||||
if (!(handle instanceof VarHandleMemoryAddressBase)) {
|
||||
throw new IllegalArgumentException("Not a memory access varhandle: " + handle);
|
||||
}
|
||||
return (VarHandleMemoryAddressBase) handle;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package java.lang.invoke;
|
||||
|
||||
/**
|
||||
* Base class for memory access var handle implementations.
|
||||
*/
|
||||
abstract class VarHandleMemoryAddressBase extends VarHandle {
|
||||
|
||||
/** endianness **/
|
||||
final boolean be;
|
||||
|
||||
/** access size (in bytes, computed from var handle carrier type) **/
|
||||
final long length;
|
||||
|
||||
/** access offset (in bytes); must be compatible with {@code alignmentMask} **/
|
||||
final long offset;
|
||||
|
||||
/** alignment constraint (in bytes, expressed as a bit mask) **/
|
||||
final long alignmentMask;
|
||||
|
||||
VarHandleMemoryAddressBase(VarForm form, boolean be, long length, long offset, long alignmentMask) {
|
||||
super(form);
|
||||
this.be = be;
|
||||
this.length = length;
|
||||
this.offset = offset;
|
||||
this.alignmentMask = alignmentMask;
|
||||
}
|
||||
|
||||
static IllegalStateException newIllegalStateExceptionForMisalignedAccess(long address) {
|
||||
return new IllegalStateException("Misaligned access at address: " + address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strides used for multi-dimensional access; each stride must be compatible with {@code alignmentMask}.
|
||||
*/
|
||||
abstract long[] strides();
|
||||
|
||||
abstract Class<?> carrier();
|
||||
}
|
@ -25,13 +25,26 @@
|
||||
|
||||
package java.lang.invoke;
|
||||
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
|
||||
|
||||
final class VarHandles {
|
||||
|
||||
static ClassValue<ConcurrentMap<Integer, MethodHandle>> ADDRESS_FACTORIES = new ClassValue<>() {
|
||||
@Override
|
||||
protected ConcurrentMap<Integer, MethodHandle> computeValue(Class<?> type) {
|
||||
return new ConcurrentHashMap<>();
|
||||
}
|
||||
};
|
||||
|
||||
static VarHandle makeFieldHandle(MemberName f, Class<?> refc, Class<?> type, boolean isWriteAllowedOnFinalFields) {
|
||||
if (!f.isStatic()) {
|
||||
long foffset = MethodHandleNatives.objectFieldOffset(f);
|
||||
@ -279,6 +292,43 @@ final class VarHandles {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a memory access VarHandle.
|
||||
*
|
||||
* Resulting VarHandle will take a memory address as first argument,
|
||||
* and a certain number of coordinate {@code long} parameters, depending on the length
|
||||
* of the {@code strides} argument array.
|
||||
*
|
||||
* Coordinates are multiplied with corresponding scale factors ({@code strides}) and added
|
||||
* to a single fixed offset to compute an effective offset from the given MemoryAddress for the access.
|
||||
*
|
||||
* @param carrier the Java carrier type.
|
||||
* @param alignmentMask alignment requirement to be checked upon access. In bytes. Expressed as a mask.
|
||||
* @param byteOrder the byte order.
|
||||
* @param offset a constant offset for the access.
|
||||
* @param strides the scale factors with which to multiply given access coordinates.
|
||||
* @return the created VarHandle.
|
||||
*/
|
||||
static VarHandle makeMemoryAddressViewHandle(Class<?> carrier, long alignmentMask,
|
||||
ByteOrder byteOrder, long offset, long[] strides) {
|
||||
if (!carrier.isPrimitive() || carrier == void.class || carrier == boolean.class) {
|
||||
throw new IllegalArgumentException("Invalid carrier: " + carrier.getName());
|
||||
}
|
||||
long size = Wrapper.forPrimitiveType(carrier).bitWidth() / 8;
|
||||
boolean be = byteOrder == ByteOrder.BIG_ENDIAN;
|
||||
|
||||
Map<Integer, MethodHandle> carrierFactory = ADDRESS_FACTORIES.get(carrier);
|
||||
MethodHandle fac = carrierFactory.computeIfAbsent(strides.length,
|
||||
dims -> new AddressVarHandleGenerator(carrier, dims)
|
||||
.generateHandleFactory());
|
||||
|
||||
try {
|
||||
return (VarHandle)fac.invoke(be, size, offset, alignmentMask, strides);
|
||||
} catch (Throwable ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * A helper program to generate the VarHandleGuards class with a set of
|
||||
// * static guard methods each of which corresponds to a particular shape and
|
||||
|
@ -24,6 +24,8 @@
|
||||
*/
|
||||
package java.lang.invoke;
|
||||
|
||||
import jdk.internal.access.JavaNioAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.util.Preconditions;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
@ -38,6 +40,8 @@ import static java.lang.invoke.MethodHandleStatics.UNSAFE;
|
||||
|
||||
final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
|
||||
|
||||
static JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
|
||||
|
||||
static final int ALIGN = $BoxType$.BYTES - 1;
|
||||
|
||||
#if[floatingPoint]
|
||||
@ -529,6 +533,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
|
||||
|
||||
@ForceInline
|
||||
static int index(ByteBuffer bb, int index) {
|
||||
nioAccess.checkSegment(bb);
|
||||
return Preconditions.checkIndex(index, UNSAFE.getInt(bb, BUFFER_LIMIT) - ALIGN, null);
|
||||
}
|
||||
|
||||
@ -536,7 +541,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
|
||||
static int indexRO(ByteBuffer bb, int index) {
|
||||
if (UNSAFE.getBoolean(bb, BYTE_BUFFER_IS_READ_ONLY))
|
||||
throw new ReadOnlyBufferException();
|
||||
return Preconditions.checkIndex(index, UNSAFE.getInt(bb, BUFFER_LIMIT) - ALIGN, null);
|
||||
return index(bb, index);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
|
@ -0,0 +1,512 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package java.lang.invoke;
|
||||
|
||||
import jdk.internal.access.foreign.MemoryAddressProxy;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
|
||||
|
||||
#warn
|
||||
|
||||
final class VarHandleMemoryAddressAs$Type$s {
|
||||
|
||||
static final boolean BE = UNSAFE.isBigEndian();
|
||||
|
||||
static final int VM_ALIGN = $BoxType$.BYTES - 1;
|
||||
|
||||
#if[floatingPoint]
|
||||
@ForceInline
|
||||
static $rawType$ convEndian(boolean big, $type$ v) {
|
||||
$rawType$ rv = $Type$.$type$ToRaw$RawType$Bits(v);
|
||||
return big == BE ? rv : $RawBoxType$.reverseBytes(rv);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ convEndian(boolean big, $rawType$ rv) {
|
||||
rv = big == BE ? rv : $RawBoxType$.reverseBytes(rv);
|
||||
return $Type$.$rawType$BitsTo$Type$(rv);
|
||||
}
|
||||
#else[floatingPoint]
|
||||
#if[byte]
|
||||
@ForceInline
|
||||
static $type$ convEndian(boolean big, $type$ n) {
|
||||
return n;
|
||||
}
|
||||
#else[byte]
|
||||
@ForceInline
|
||||
static $type$ convEndian(boolean big, $type$ n) {
|
||||
return big == BE ? n : $BoxType$.reverseBytes(n);
|
||||
}
|
||||
#end[byte]
|
||||
#end[floatingPoint]
|
||||
|
||||
@ForceInline
|
||||
static MemoryAddressProxy checkAddress(Object obb, long offset, long length, boolean ro) {
|
||||
MemoryAddressProxy oo = (MemoryAddressProxy)Objects.requireNonNull(obb);
|
||||
oo.checkAccess(offset, length, ro);
|
||||
return oo;
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static long offset(MemoryAddressProxy bb, long offset, long alignmentMask) {
|
||||
long address = offsetNoVMAlignCheck(bb, offset, alignmentMask);
|
||||
if ((address & VM_ALIGN) != 0) {
|
||||
throw VarHandleMemoryAddressBase.newIllegalStateExceptionForMisalignedAccess(address);
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static long offsetNoVMAlignCheck(MemoryAddressProxy bb, long offset, long alignmentMask) {
|
||||
long base = bb.unsafeGetOffset();
|
||||
long address = base + offset;
|
||||
//note: the offset portion has already been aligned-checked, by construction
|
||||
if ((base & alignmentMask) != 0) {
|
||||
throw VarHandleMemoryAddressBase.newIllegalStateExceptionForMisalignedAccess(address);
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ get0(VarHandleMemoryAddressBase handle, Object obb, long base) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true);
|
||||
#if[floatingPoint]
|
||||
$rawType$ rawValue = UNSAFE.get$RawType$Unaligned(
|
||||
bb.unsafeGetBase(),
|
||||
offsetNoVMAlignCheck(bb, base, handle.alignmentMask),
|
||||
handle.be);
|
||||
return $Type$.$rawType$BitsTo$Type$(rawValue);
|
||||
#else[floatingPoint]
|
||||
#if[byte]
|
||||
return UNSAFE.get$Type$(
|
||||
bb.unsafeGetBase(),
|
||||
offsetNoVMAlignCheck(bb, base, handle.alignmentMask));
|
||||
#else[byte]
|
||||
return UNSAFE.get$Type$Unaligned(
|
||||
bb.unsafeGetBase(),
|
||||
offsetNoVMAlignCheck(bb, base, handle.alignmentMask),
|
||||
handle.be);
|
||||
#end[byte]
|
||||
#end[floatingPoint]
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static void set0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
#if[floatingPoint]
|
||||
UNSAFE.put$RawType$Unaligned(
|
||||
bb.unsafeGetBase(),
|
||||
offsetNoVMAlignCheck(bb, base, handle.alignmentMask),
|
||||
$Type$.$type$ToRaw$RawType$Bits(value),
|
||||
handle.be);
|
||||
#else[floatingPoint]
|
||||
#if[byte]
|
||||
UNSAFE.put$Type$(
|
||||
bb.unsafeGetBase(),
|
||||
offsetNoVMAlignCheck(bb, base, handle.alignmentMask),
|
||||
value);
|
||||
#else[byte]
|
||||
UNSAFE.put$Type$Unaligned(
|
||||
bb.unsafeGetBase(),
|
||||
offsetNoVMAlignCheck(bb, base, handle.alignmentMask),
|
||||
value,
|
||||
handle.be);
|
||||
#end[byte]
|
||||
#end[floatingPoint]
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ getVolatile0(VarHandleMemoryAddressBase handle, Object obb, long base) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true);
|
||||
return convEndian(handle.be,
|
||||
UNSAFE.get$RawType$Volatile(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask)));
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static void setVolatile0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
UNSAFE.put$RawType$Volatile(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
convEndian(handle.be, value));
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ getAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true);
|
||||
return convEndian(handle.be,
|
||||
UNSAFE.get$RawType$Acquire(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask)));
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static void setRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
UNSAFE.put$RawType$Release(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
convEndian(handle.be, value));
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ getOpaque0(VarHandleMemoryAddressBase handle, Object obb, long base) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true);
|
||||
return convEndian(handle.be,
|
||||
UNSAFE.get$RawType$Opaque(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask)));
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static void setOpaque0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
UNSAFE.put$RawType$Opaque(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
convEndian(handle.be, value));
|
||||
}
|
||||
#if[CAS]
|
||||
|
||||
@ForceInline
|
||||
static boolean compareAndSet0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
return UNSAFE.compareAndSet$RawType$(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
convEndian(handle.be, expected), convEndian(handle.be, value));
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ compareAndExchange0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
return convEndian(handle.be,
|
||||
UNSAFE.compareAndExchange$RawType$(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
convEndian(handle.be, expected), convEndian(handle.be, value)));
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ compareAndExchangeAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
return convEndian(handle.be,
|
||||
UNSAFE.compareAndExchange$RawType$Acquire(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
convEndian(handle.be, expected), convEndian(handle.be, value)));
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ compareAndExchangeRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
return convEndian(handle.be,
|
||||
UNSAFE.compareAndExchange$RawType$Release(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
convEndian(handle.be, expected), convEndian(handle.be, value)));
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static boolean weakCompareAndSetPlain0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
return UNSAFE.weakCompareAndSet$RawType$Plain(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
convEndian(handle.be, expected), convEndian(handle.be, value));
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static boolean weakCompareAndSet0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
return UNSAFE.weakCompareAndSet$RawType$(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
convEndian(handle.be, expected), convEndian(handle.be, value));
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static boolean weakCompareAndSetAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
return UNSAFE.weakCompareAndSet$RawType$Acquire(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
convEndian(handle.be, expected), convEndian(handle.be, value));
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static boolean weakCompareAndSetRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
return UNSAFE.weakCompareAndSet$RawType$Release(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
convEndian(handle.be, expected), convEndian(handle.be, value));
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ getAndSet0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
return convEndian(handle.be,
|
||||
UNSAFE.getAndSet$RawType$(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
convEndian(handle.be, value)));
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ getAndSetAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
return convEndian(handle.be,
|
||||
UNSAFE.getAndSet$RawType$Acquire(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
convEndian(handle.be, value)));
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ getAndSetRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
return convEndian(handle.be,
|
||||
UNSAFE.getAndSet$RawType$Release(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
convEndian(handle.be, value)));
|
||||
}
|
||||
#end[CAS]
|
||||
#if[AtomicAdd]
|
||||
|
||||
@ForceInline
|
||||
static $type$ getAndAdd0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ delta) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
if (handle.be == BE) {
|
||||
return UNSAFE.getAndAdd$RawType$(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
delta);
|
||||
} else {
|
||||
return getAndAddConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), delta);
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ getAndAddAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ delta) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
if (handle.be == BE) {
|
||||
return UNSAFE.getAndAdd$RawType$Acquire(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
delta);
|
||||
} else {
|
||||
return getAndAddConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), delta);
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ getAndAddRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ delta) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
if (handle.be == BE) {
|
||||
return UNSAFE.getAndAdd$RawType$Release(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
delta);
|
||||
} else {
|
||||
return getAndAddConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), delta);
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ getAndAddConvEndianWithCAS(MemoryAddressProxy bb, long offset, $type$ delta) {
|
||||
$type$ nativeExpectedValue, expectedValue;
|
||||
Object base = bb.unsafeGetBase();
|
||||
do {
|
||||
nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset);
|
||||
expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue);
|
||||
} while (!UNSAFE.weakCompareAndSet$RawType$(base, offset,
|
||||
nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue + delta)));
|
||||
return expectedValue;
|
||||
}
|
||||
#end[AtomicAdd]
|
||||
#if[Bitwise]
|
||||
|
||||
@ForceInline
|
||||
static $type$ getAndBitwiseOr0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
if (handle.be == BE) {
|
||||
return UNSAFE.getAndBitwiseOr$RawType$(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
value);
|
||||
} else {
|
||||
return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ getAndBitwiseOrRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
if (handle.be == BE) {
|
||||
return UNSAFE.getAndBitwiseOr$RawType$Release(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
value);
|
||||
} else {
|
||||
return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ getAndBitwiseOrAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
if (handle.be == BE) {
|
||||
return UNSAFE.getAndBitwiseOr$RawType$Acquire(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
value);
|
||||
} else {
|
||||
return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ getAndBitwiseOrConvEndianWithCAS(MemoryAddressProxy bb, long offset, $type$ value) {
|
||||
$type$ nativeExpectedValue, expectedValue;
|
||||
Object base = bb.unsafeGetBase();
|
||||
do {
|
||||
nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset);
|
||||
expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue);
|
||||
} while (!UNSAFE.weakCompareAndSet$RawType$(base, offset,
|
||||
nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue | value)));
|
||||
return expectedValue;
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ getAndBitwiseAnd0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
if (handle.be == BE) {
|
||||
return UNSAFE.getAndBitwiseAnd$RawType$(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
value);
|
||||
} else {
|
||||
return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ getAndBitwiseAndRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
if (handle.be == BE) {
|
||||
return UNSAFE.getAndBitwiseAnd$RawType$Release(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
value);
|
||||
} else {
|
||||
return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ getAndBitwiseAndAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
if (handle.be == BE) {
|
||||
return UNSAFE.getAndBitwiseAnd$RawType$Acquire(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
value);
|
||||
} else {
|
||||
return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ getAndBitwiseAndConvEndianWithCAS(MemoryAddressProxy bb, long offset, $type$ value) {
|
||||
$type$ nativeExpectedValue, expectedValue;
|
||||
Object base = bb.unsafeGetBase();
|
||||
do {
|
||||
nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset);
|
||||
expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue);
|
||||
} while (!UNSAFE.weakCompareAndSet$RawType$(base, offset,
|
||||
nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue & value)));
|
||||
return expectedValue;
|
||||
}
|
||||
|
||||
|
||||
@ForceInline
|
||||
static $type$ getAndBitwiseXor0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
if (handle.be == BE) {
|
||||
return UNSAFE.getAndBitwiseXor$RawType$(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
value);
|
||||
} else {
|
||||
return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ getAndBitwiseXorRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
if (handle.be == BE) {
|
||||
return UNSAFE.getAndBitwiseXor$RawType$Release(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
value);
|
||||
} else {
|
||||
return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ getAndBitwiseXorAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
|
||||
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
|
||||
if (handle.be == BE) {
|
||||
return UNSAFE.getAndBitwiseXor$RawType$Acquire(
|
||||
bb.unsafeGetBase(),
|
||||
offset(bb, base, handle.alignmentMask),
|
||||
value);
|
||||
} else {
|
||||
return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
static $type$ getAndBitwiseXorConvEndianWithCAS(MemoryAddressProxy bb, long offset, $type$ value) {
|
||||
$type$ nativeExpectedValue, expectedValue;
|
||||
Object base = bb.unsafeGetBase();
|
||||
do {
|
||||
nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset);
|
||||
expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue);
|
||||
} while (!UNSAFE.weakCompareAndSet$RawType$(base, offset,
|
||||
nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue ^ value)));
|
||||
return expectedValue;
|
||||
}
|
||||
#end[Bitwise]
|
||||
}
|
@ -28,7 +28,9 @@ package java.nio;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
import jdk.internal.access.JavaNioAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.access.foreign.MemorySegmentProxy;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
|
||||
import java.util.Spliterator;
|
||||
|
||||
@ -213,13 +215,26 @@ public abstract class Buffer {
|
||||
// NOTE: hoisted here for speed in JNI GetDirectBufferAddress
|
||||
long address;
|
||||
|
||||
// Used by buffers generated by the memory access API (JEP-370)
|
||||
final MemorySegmentProxy segment;
|
||||
|
||||
|
||||
// Creates a new buffer with given address and capacity.
|
||||
//
|
||||
Buffer(long addr, int cap, MemorySegmentProxy segment) {
|
||||
this.address = addr;
|
||||
this.capacity = cap;
|
||||
this.segment = segment;
|
||||
}
|
||||
|
||||
// Creates a new buffer with the given mark, position, limit, and capacity,
|
||||
// after checking invariants.
|
||||
//
|
||||
Buffer(int mark, int pos, int lim, int cap) { // package-private
|
||||
Buffer(int mark, int pos, int lim, int cap, MemorySegmentProxy segment) { // package-private
|
||||
if (cap < 0)
|
||||
throw createCapacityException(cap);
|
||||
this.capacity = cap;
|
||||
this.segment = segment;
|
||||
limit(lim);
|
||||
position(pos);
|
||||
if (mark >= 0) {
|
||||
@ -731,6 +746,13 @@ public abstract class Buffer {
|
||||
mark = -1;
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
final void checkSegment() {
|
||||
if (segment != null) {
|
||||
segment.checkValidState();
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
// setup access to this package in SharedSecrets
|
||||
SharedSecrets.setJavaNioAccess(
|
||||
@ -739,6 +761,31 @@ public abstract class Buffer {
|
||||
public JavaNioAccess.BufferPool getDirectBufferPool() {
|
||||
return Bits.BUFFER_POOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer newDirectByteBuffer(long addr, int cap, Object obj, MemorySegmentProxy segment) {
|
||||
return new DirectByteBuffer(addr, cap, obj, segment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer newHeapByteBuffer(byte[] hb, int offset, int capacity, MemorySegmentProxy segment) {
|
||||
return new HeapByteBuffer(hb, offset, capacity, segment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBufferBase(ByteBuffer bb) {
|
||||
return bb.base();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getBufferAddress(ByteBuffer bb) {
|
||||
return bb.address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkSegment(Buffer buffer) {
|
||||
buffer.checkSegment();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
package java.nio;
|
||||
|
||||
import java.util.Objects;
|
||||
import jdk.internal.access.foreign.MemorySegmentProxy;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
|
||||
@ -40,11 +41,11 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
|
||||
|
||||
#end[rw]
|
||||
|
||||
ByteBufferAs$Type$Buffer$RW$$BO$(ByteBuffer bb) { // package-private
|
||||
ByteBufferAs$Type$Buffer$RW$$BO$(ByteBuffer bb, MemorySegmentProxy segment) { // package-private
|
||||
#if[rw]
|
||||
super(-1, 0,
|
||||
bb.remaining() >> $LG_BYTES_PER_VALUE$,
|
||||
bb.remaining() >> $LG_BYTES_PER_VALUE$);
|
||||
bb.remaining() >> $LG_BYTES_PER_VALUE$, segment);
|
||||
this.bb = bb;
|
||||
// enforce limit == capacity
|
||||
int cap = this.capacity();
|
||||
@ -53,21 +54,21 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
|
||||
assert (pos <= cap);
|
||||
address = bb.address;
|
||||
#else[rw]
|
||||
super(bb);
|
||||
super(bb, segment);
|
||||
#end[rw]
|
||||
}
|
||||
|
||||
ByteBufferAs$Type$Buffer$RW$$BO$(ByteBuffer bb,
|
||||
int mark, int pos, int lim, int cap,
|
||||
long addr)
|
||||
long addr, MemorySegmentProxy segment)
|
||||
{
|
||||
#if[rw]
|
||||
super(mark, pos, lim, cap);
|
||||
super(mark, pos, lim, cap, segment);
|
||||
this.bb = bb;
|
||||
address = addr;
|
||||
assert address >= bb.address;
|
||||
#else[rw]
|
||||
super(bb, mark, pos, lim, cap, addr);
|
||||
super(bb, mark, pos, lim, cap, addr, segment);
|
||||
#end[rw]
|
||||
}
|
||||
|
||||
@ -82,7 +83,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
|
||||
assert (pos <= lim);
|
||||
int rem = (pos <= lim ? lim - pos : 0);
|
||||
long addr = byteOffset(pos);
|
||||
return new ByteBufferAs$Type$Buffer$RW$$BO$(bb, -1, 0, rem, rem, addr);
|
||||
return new ByteBufferAs$Type$Buffer$RW$$BO$(bb, -1, 0, rem, rem, addr, segment);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -93,7 +94,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
|
||||
0,
|
||||
length,
|
||||
length,
|
||||
byteOffset(index));
|
||||
byteOffset(index), segment);
|
||||
}
|
||||
|
||||
public $Type$Buffer duplicate() {
|
||||
@ -102,7 +103,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
|
||||
this.position(),
|
||||
this.limit(),
|
||||
this.capacity(),
|
||||
address);
|
||||
address, segment);
|
||||
}
|
||||
|
||||
public $Type$Buffer asReadOnlyBuffer() {
|
||||
@ -112,7 +113,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
|
||||
this.position(),
|
||||
this.limit(),
|
||||
this.capacity(),
|
||||
address);
|
||||
address, segment);
|
||||
#else[rw]
|
||||
return duplicate();
|
||||
#end[rw]
|
||||
@ -130,12 +131,14 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
|
||||
}
|
||||
|
||||
public $type$ get() {
|
||||
checkSegment();
|
||||
$memtype$ x = UNSAFE.get$Memtype$Unaligned(bb.hb, byteOffset(nextGetIndex()),
|
||||
{#if[boB]?true:false});
|
||||
return $fromBits$(x);
|
||||
}
|
||||
|
||||
public $type$ get(int i) {
|
||||
checkSegment();
|
||||
$memtype$ x = UNSAFE.get$Memtype$Unaligned(bb.hb, byteOffset(checkIndex(i)),
|
||||
{#if[boB]?true:false});
|
||||
return $fromBits$(x);
|
||||
@ -153,6 +156,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
|
||||
|
||||
public $Type$Buffer put($type$ x) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
$memtype$ y = $toBits$(x);
|
||||
UNSAFE.put$Memtype$Unaligned(bb.hb, byteOffset(nextPutIndex()), y,
|
||||
{#if[boB]?true:false});
|
||||
@ -164,6 +168,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
|
||||
|
||||
public $Type$Buffer put(int i, $type$ x) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
$memtype$ y = $toBits$(x);
|
||||
UNSAFE.put$Memtype$Unaligned(bb.hb, byteOffset(checkIndex(i)), y,
|
||||
{#if[boB]?true:false});
|
||||
@ -237,7 +242,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
|
||||
pos + start,
|
||||
pos + end,
|
||||
capacity(),
|
||||
address);
|
||||
address, segment);
|
||||
}
|
||||
|
||||
#end[char]
|
||||
|
@ -33,6 +33,7 @@ class XXX {
|
||||
|
||||
private $type$ get$Type$(long a) {
|
||||
try {
|
||||
checkSegment();
|
||||
$memtype$ x = UNSAFE.get$Memtype$Unaligned(null, a, bigEndian);
|
||||
return $fromBits$(x);
|
||||
} finally {
|
||||
@ -61,6 +62,7 @@ class XXX {
|
||||
private ByteBuffer put$Type$(long a, $type$ x) {
|
||||
#if[rw]
|
||||
try {
|
||||
checkSegment();
|
||||
$memtype$ y = $toBits$(x);
|
||||
UNSAFE.put$Memtype$Unaligned(null, a, y, bigEndian);
|
||||
} finally {
|
||||
@ -104,13 +106,13 @@ class XXX {
|
||||
0,
|
||||
size,
|
||||
size,
|
||||
address + off))
|
||||
address + off, segment))
|
||||
: ($Type$Buffer)(new ByteBufferAs$Type$Buffer$RW$L(this,
|
||||
-1,
|
||||
0,
|
||||
size,
|
||||
size,
|
||||
address + off)));
|
||||
address + off, segment)));
|
||||
} else {
|
||||
return (nativeByteOrder
|
||||
? ($Type$Buffer)(new Direct$Type$Buffer$RW$U(this,
|
||||
@ -118,13 +120,13 @@ class XXX {
|
||||
0,
|
||||
size,
|
||||
size,
|
||||
off))
|
||||
off, segment))
|
||||
: ($Type$Buffer)(new Direct$Type$Buffer$RW$S(this,
|
||||
-1,
|
||||
0,
|
||||
size,
|
||||
size,
|
||||
off)));
|
||||
off, segment)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@ package java.nio;
|
||||
import java.io.FileDescriptor;
|
||||
import java.lang.ref.Reference;
|
||||
import java.util.Objects;
|
||||
import jdk.internal.access.foreign.MemorySegmentProxy;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.ref.Cleaner;
|
||||
import sun.nio.ch.DirectBuffer;
|
||||
@ -112,7 +113,7 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
//
|
||||
Direct$Type$Buffer$RW$(int cap) { // package-private
|
||||
#if[rw]
|
||||
super(-1, 0, cap, cap);
|
||||
super(-1, 0, cap, cap, null);
|
||||
boolean pa = VM.isDirectMemoryPageAligned();
|
||||
int ps = Bits.pageSize();
|
||||
long size = Math.max(1L, (long)cap + (pa ? ps : 0));
|
||||
@ -145,8 +146,8 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
// Invoked to construct a direct ByteBuffer referring to the block of
|
||||
// memory. A given arbitrary object may also be attached to the buffer.
|
||||
//
|
||||
Direct$Type$Buffer(long addr, int cap, Object ob) {
|
||||
super(-1, 0, cap, cap);
|
||||
Direct$Type$Buffer(long addr, int cap, Object ob, MemorySegmentProxy segment) {
|
||||
super(-1, 0, cap, cap, segment);
|
||||
address = addr;
|
||||
cleaner = null;
|
||||
att = ob;
|
||||
@ -156,7 +157,7 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
// Invoked only by JNI: NewDirectByteBuffer(void*, long)
|
||||
//
|
||||
private Direct$Type$Buffer(long addr, int cap) {
|
||||
super(-1, 0, cap, cap);
|
||||
super(-1, 0, cap, cap, null);
|
||||
address = addr;
|
||||
cleaner = null;
|
||||
att = null;
|
||||
@ -169,15 +170,15 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
protected Direct$Type$Buffer$RW$(int cap, long addr,
|
||||
FileDescriptor fd,
|
||||
Runnable unmapper,
|
||||
boolean isSync)
|
||||
boolean isSync, MemorySegmentProxy segment)
|
||||
{
|
||||
#if[rw]
|
||||
super(-1, 0, cap, cap, fd, isSync);
|
||||
super(-1, 0, cap, cap, fd, isSync, segment);
|
||||
address = addr;
|
||||
cleaner = Cleaner.create(this, unmapper);
|
||||
att = null;
|
||||
#else[rw]
|
||||
super(cap, addr, fd, unmapper, isSync);
|
||||
super(cap, addr, fd, unmapper, isSync, segment);
|
||||
this.isReadOnly = true;
|
||||
#end[rw]
|
||||
}
|
||||
@ -188,10 +189,10 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
//
|
||||
Direct$Type$Buffer$RW$$BO$(DirectBuffer db, // package-private
|
||||
int mark, int pos, int lim, int cap,
|
||||
int off)
|
||||
int off, MemorySegmentProxy segment)
|
||||
{
|
||||
#if[rw]
|
||||
super(mark, pos, lim, cap);
|
||||
super(mark, pos, lim, cap, segment);
|
||||
address = db.address() + off;
|
||||
#if[byte]
|
||||
cleaner = null;
|
||||
@ -199,7 +200,7 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
Object attachment = db.attachment();
|
||||
att = (attachment == null ? db : attachment);
|
||||
#else[rw]
|
||||
super(db, mark, pos, lim, cap, off);
|
||||
super(db, mark, pos, lim, cap, off, segment);
|
||||
this.isReadOnly = true;
|
||||
#end[rw]
|
||||
}
|
||||
@ -216,7 +217,7 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
int rem = (pos <= lim ? lim - pos : 0);
|
||||
int off = (pos << $LG_BYTES_PER_VALUE$);
|
||||
assert (off >= 0);
|
||||
return new Direct$Type$Buffer$RW$$BO$(this, -1, 0, rem, rem, off);
|
||||
return new Direct$Type$Buffer$RW$$BO$(this, -1, 0, rem, rem, off, segment);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -227,7 +228,7 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
0,
|
||||
length,
|
||||
length,
|
||||
index);
|
||||
index, segment);
|
||||
}
|
||||
|
||||
public $Type$Buffer duplicate() {
|
||||
@ -236,7 +237,7 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
this.position(),
|
||||
this.limit(),
|
||||
this.capacity(),
|
||||
0);
|
||||
0, segment);
|
||||
}
|
||||
|
||||
public $Type$Buffer asReadOnlyBuffer() {
|
||||
@ -246,7 +247,7 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
this.position(),
|
||||
this.limit(),
|
||||
this.capacity(),
|
||||
0);
|
||||
0, segment);
|
||||
#else[rw]
|
||||
return duplicate();
|
||||
#end[rw]
|
||||
@ -264,6 +265,7 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
|
||||
public $type$ get() {
|
||||
try {
|
||||
checkSegment();
|
||||
return $fromBits$($swap$(UNSAFE.get$Swaptype$(ix(nextGetIndex()))));
|
||||
} finally {
|
||||
Reference.reachabilityFence(this);
|
||||
@ -272,6 +274,7 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
|
||||
public $type$ get(int i) {
|
||||
try {
|
||||
checkSegment();
|
||||
return $fromBits$($swap$(UNSAFE.get$Swaptype$(ix(checkIndex(i)))));
|
||||
} finally {
|
||||
Reference.reachabilityFence(this);
|
||||
@ -290,6 +293,7 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
|
||||
public $Type$Buffer get($type$[] dst, int offset, int length) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_TO_ARRAY_THRESHOLD) {
|
||||
Objects.checkFromIndexSize(offset, length, dst.length);
|
||||
int pos = position();
|
||||
@ -331,6 +335,7 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
|
||||
public $Type$Buffer get(int index, $type$[] dst, int offset, int length) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_TO_ARRAY_THRESHOLD) {
|
||||
Objects.checkFromIndexSize(index, length, limit());
|
||||
Objects.checkFromIndexSize(offset, length, dst.length);
|
||||
@ -368,6 +373,7 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
public $Type$Buffer put($type$ x) {
|
||||
#if[rw]
|
||||
try {
|
||||
checkSegment();
|
||||
UNSAFE.put$Swaptype$(ix(nextPutIndex()), $swap$($toBits$(x)));
|
||||
} finally {
|
||||
Reference.reachabilityFence(this);
|
||||
@ -381,6 +387,7 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
public $Type$Buffer put(int i, $type$ x) {
|
||||
#if[rw]
|
||||
try {
|
||||
checkSegment();
|
||||
UNSAFE.put$Swaptype$(ix(checkIndex(i)), $swap$($toBits$(x)));
|
||||
} finally {
|
||||
Reference.reachabilityFence(this);
|
||||
@ -393,6 +400,7 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
|
||||
public $Type$Buffer put($Type$Buffer src) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
if (src instanceof Direct$Type$Buffer$BO$) {
|
||||
if (src == this)
|
||||
throw createSameBufferException();
|
||||
@ -439,6 +447,7 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
|
||||
public $Type$Buffer put($type$[] src, int offset, int length) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_FROM_ARRAY_THRESHOLD) {
|
||||
Objects.checkFromIndexSize(offset, length, src.length);
|
||||
int pos = position();
|
||||
@ -480,6 +489,7 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
|
||||
public $Type$Buffer put(int index, $type$[] src, int offset, int length) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_FROM_ARRAY_THRESHOLD) {
|
||||
Objects.checkFromIndexSize(index, length, limit());
|
||||
Objects.checkFromIndexSize(offset, length, src.length);
|
||||
@ -577,7 +587,7 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
pos + start,
|
||||
pos + end,
|
||||
capacity(),
|
||||
offset);
|
||||
offset, segment);
|
||||
}
|
||||
|
||||
#end[char]
|
||||
|
@ -28,6 +28,7 @@
|
||||
package java.nio;
|
||||
|
||||
import java.util.Objects;
|
||||
import jdk.internal.access.foreign.MemorySegmentProxy;
|
||||
|
||||
/**
|
||||
#if[rw]
|
||||
@ -58,47 +59,47 @@ class Heap$Type$Buffer$RW$
|
||||
#end[rw]
|
||||
*/
|
||||
|
||||
Heap$Type$Buffer$RW$(int cap, int lim) { // package-private
|
||||
Heap$Type$Buffer$RW$(int cap, int lim, MemorySegmentProxy segment) { // package-private
|
||||
#if[rw]
|
||||
super(-1, 0, lim, cap, new $type$[cap], 0);
|
||||
super(-1, 0, lim, cap, new $type$[cap], 0, segment);
|
||||
/*
|
||||
hb = new $type$[cap];
|
||||
offset = 0;
|
||||
*/
|
||||
this.address = ARRAY_BASE_OFFSET;
|
||||
#else[rw]
|
||||
super(cap, lim);
|
||||
super(cap, lim, segment);
|
||||
this.isReadOnly = true;
|
||||
#end[rw]
|
||||
}
|
||||
|
||||
Heap$Type$Buffer$RW$($type$[] buf, int off, int len) { // package-private
|
||||
Heap$Type$Buffer$RW$($type$[] buf, int off, int len, MemorySegmentProxy segment) { // package-private
|
||||
#if[rw]
|
||||
super(-1, off, off + len, buf.length, buf, 0);
|
||||
super(-1, off, off + len, buf.length, buf, 0, segment);
|
||||
/*
|
||||
hb = buf;
|
||||
offset = 0;
|
||||
*/
|
||||
this.address = ARRAY_BASE_OFFSET;
|
||||
#else[rw]
|
||||
super(buf, off, len);
|
||||
super(buf, off, len, segment);
|
||||
this.isReadOnly = true;
|
||||
#end[rw]
|
||||
}
|
||||
|
||||
protected Heap$Type$Buffer$RW$($type$[] buf,
|
||||
int mark, int pos, int lim, int cap,
|
||||
int off)
|
||||
int off, MemorySegmentProxy segment)
|
||||
{
|
||||
#if[rw]
|
||||
super(mark, pos, lim, cap, buf, off);
|
||||
super(mark, pos, lim, cap, buf, off, segment);
|
||||
/*
|
||||
hb = buf;
|
||||
offset = off;
|
||||
*/
|
||||
this.address = ARRAY_BASE_OFFSET + off * ARRAY_INDEX_SCALE;
|
||||
#else[rw]
|
||||
super(buf, mark, pos, lim, cap, off);
|
||||
super(buf, mark, pos, lim, cap, off, segment);
|
||||
this.isReadOnly = true;
|
||||
#end[rw]
|
||||
}
|
||||
@ -110,7 +111,7 @@ class Heap$Type$Buffer$RW$
|
||||
0,
|
||||
rem,
|
||||
rem,
|
||||
this.position() + offset);
|
||||
this.position() + offset, segment);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -121,7 +122,7 @@ class Heap$Type$Buffer$RW$
|
||||
0,
|
||||
length,
|
||||
length,
|
||||
index + offset);
|
||||
index + offset, segment);
|
||||
}
|
||||
|
||||
public $Type$Buffer duplicate() {
|
||||
@ -130,7 +131,7 @@ class Heap$Type$Buffer$RW$
|
||||
this.position(),
|
||||
this.limit(),
|
||||
this.capacity(),
|
||||
offset);
|
||||
offset, segment);
|
||||
}
|
||||
|
||||
public $Type$Buffer asReadOnlyBuffer() {
|
||||
@ -140,7 +141,7 @@ class Heap$Type$Buffer$RW$
|
||||
this.position(),
|
||||
this.limit(),
|
||||
this.capacity(),
|
||||
offset);
|
||||
offset, segment);
|
||||
#else[rw]
|
||||
return duplicate();
|
||||
#end[rw]
|
||||
@ -159,20 +160,23 @@ class Heap$Type$Buffer$RW$
|
||||
#end[byte]
|
||||
|
||||
public $type$ get() {
|
||||
checkSegment();
|
||||
return hb[ix(nextGetIndex())];
|
||||
}
|
||||
|
||||
public $type$ get(int i) {
|
||||
checkSegment();
|
||||
return hb[ix(checkIndex(i))];
|
||||
}
|
||||
|
||||
#if[streamableType]
|
||||
$type$ getUnchecked(int i) {
|
||||
return hb[ix(i)];
|
||||
return hb[ix(i)];
|
||||
}
|
||||
#end[streamableType]
|
||||
|
||||
public $Type$Buffer get($type$[] dst, int offset, int length) {
|
||||
checkSegment();
|
||||
Objects.checkFromIndexSize(offset, length, dst.length);
|
||||
int pos = position();
|
||||
if (length > limit() - pos)
|
||||
@ -183,6 +187,7 @@ class Heap$Type$Buffer$RW$
|
||||
}
|
||||
|
||||
public $Type$Buffer get(int index, $type$[] dst, int offset, int length) {
|
||||
checkSegment();
|
||||
Objects.checkFromIndexSize(index, length, limit());
|
||||
Objects.checkFromIndexSize(offset, length, dst.length);
|
||||
System.arraycopy(hb, ix(index), dst, offset, length);
|
||||
@ -201,6 +206,7 @@ class Heap$Type$Buffer$RW$
|
||||
|
||||
public $Type$Buffer put($type$ x) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
hb[ix(nextPutIndex())] = x;
|
||||
return this;
|
||||
#else[rw]
|
||||
@ -210,6 +216,7 @@ class Heap$Type$Buffer$RW$
|
||||
|
||||
public $Type$Buffer put(int i, $type$ x) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
hb[ix(checkIndex(i))] = x;
|
||||
return this;
|
||||
#else[rw]
|
||||
@ -219,6 +226,7 @@ class Heap$Type$Buffer$RW$
|
||||
|
||||
public $Type$Buffer put($type$[] src, int offset, int length) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
Objects.checkFromIndexSize(offset, length, src.length);
|
||||
int pos = position();
|
||||
if (length > limit() - pos)
|
||||
@ -233,6 +241,7 @@ class Heap$Type$Buffer$RW$
|
||||
|
||||
public $Type$Buffer put($Type$Buffer src) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
if (src instanceof Heap$Type$Buffer) {
|
||||
if (src == this)
|
||||
throw createSameBufferException();
|
||||
@ -264,6 +273,7 @@ class Heap$Type$Buffer$RW$
|
||||
|
||||
public $Type$Buffer put(int index, $type$[] src, int offset, int length) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
Objects.checkFromIndexSize(index, length, limit());
|
||||
Objects.checkFromIndexSize(offset, length, src.length);
|
||||
System.arraycopy(src, offset, hb, ix(index), length);
|
||||
@ -276,6 +286,7 @@ class Heap$Type$Buffer$RW$
|
||||
#if[char]
|
||||
|
||||
public $Type$Buffer put(String src, int start, int end) {
|
||||
checkSegment();
|
||||
int length = end - start;
|
||||
Objects.checkFromIndexSize(start, length, src.length());
|
||||
if (isReadOnly())
|
||||
@ -327,6 +338,7 @@ class Heap$Type$Buffer$RW$
|
||||
#if[rw]
|
||||
|
||||
public char getChar() {
|
||||
checkSegment();
|
||||
return UNSAFE.getCharUnaligned(hb, byteOffset(nextGetIndex(2)), bigEndian);
|
||||
}
|
||||
|
||||
@ -338,6 +350,7 @@ class Heap$Type$Buffer$RW$
|
||||
|
||||
public $Type$Buffer putChar(char x) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
UNSAFE.putCharUnaligned(hb, byteOffset(nextPutIndex(2)), x, bigEndian);
|
||||
return this;
|
||||
#else[rw]
|
||||
@ -347,6 +360,7 @@ class Heap$Type$Buffer$RW$
|
||||
|
||||
public $Type$Buffer putChar(int i, char x) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
UNSAFE.putCharUnaligned(hb, byteOffset(checkIndex(i, 2)), x, bigEndian);
|
||||
return this;
|
||||
#else[rw]
|
||||
@ -364,13 +378,13 @@ class Heap$Type$Buffer$RW$
|
||||
0,
|
||||
size,
|
||||
size,
|
||||
addr))
|
||||
addr, segment))
|
||||
: (CharBuffer)(new ByteBufferAsCharBuffer$RW$L(this,
|
||||
-1,
|
||||
0,
|
||||
size,
|
||||
size,
|
||||
addr)));
|
||||
addr, segment)));
|
||||
}
|
||||
|
||||
|
||||
@ -379,10 +393,12 @@ class Heap$Type$Buffer$RW$
|
||||
#if[rw]
|
||||
|
||||
public short getShort() {
|
||||
checkSegment();
|
||||
return UNSAFE.getShortUnaligned(hb, byteOffset(nextGetIndex(2)), bigEndian);
|
||||
}
|
||||
|
||||
public short getShort(int i) {
|
||||
checkSegment();
|
||||
return UNSAFE.getShortUnaligned(hb, byteOffset(checkIndex(i, 2)), bigEndian);
|
||||
}
|
||||
|
||||
@ -390,6 +406,7 @@ class Heap$Type$Buffer$RW$
|
||||
|
||||
public $Type$Buffer putShort(short x) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
UNSAFE.putShortUnaligned(hb, byteOffset(nextPutIndex(2)), x, bigEndian);
|
||||
return this;
|
||||
#else[rw]
|
||||
@ -399,6 +416,7 @@ class Heap$Type$Buffer$RW$
|
||||
|
||||
public $Type$Buffer putShort(int i, short x) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
UNSAFE.putShortUnaligned(hb, byteOffset(checkIndex(i, 2)), x, bigEndian);
|
||||
return this;
|
||||
#else[rw]
|
||||
@ -416,13 +434,13 @@ class Heap$Type$Buffer$RW$
|
||||
0,
|
||||
size,
|
||||
size,
|
||||
addr))
|
||||
addr, segment))
|
||||
: (ShortBuffer)(new ByteBufferAsShortBuffer$RW$L(this,
|
||||
-1,
|
||||
0,
|
||||
size,
|
||||
size,
|
||||
addr)));
|
||||
addr, segment)));
|
||||
}
|
||||
|
||||
|
||||
@ -431,10 +449,12 @@ class Heap$Type$Buffer$RW$
|
||||
#if[rw]
|
||||
|
||||
public int getInt() {
|
||||
checkSegment();
|
||||
return UNSAFE.getIntUnaligned(hb, byteOffset(nextGetIndex(4)), bigEndian);
|
||||
}
|
||||
|
||||
public int getInt(int i) {
|
||||
checkSegment();
|
||||
return UNSAFE.getIntUnaligned(hb, byteOffset(checkIndex(i, 4)), bigEndian);
|
||||
}
|
||||
|
||||
@ -442,6 +462,7 @@ class Heap$Type$Buffer$RW$
|
||||
|
||||
public $Type$Buffer putInt(int x) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
UNSAFE.putIntUnaligned(hb, byteOffset(nextPutIndex(4)), x, bigEndian);
|
||||
return this;
|
||||
#else[rw]
|
||||
@ -451,6 +472,7 @@ class Heap$Type$Buffer$RW$
|
||||
|
||||
public $Type$Buffer putInt(int i, int x) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
UNSAFE.putIntUnaligned(hb, byteOffset(checkIndex(i, 4)), x, bigEndian);
|
||||
return this;
|
||||
#else[rw]
|
||||
@ -468,13 +490,13 @@ class Heap$Type$Buffer$RW$
|
||||
0,
|
||||
size,
|
||||
size,
|
||||
addr))
|
||||
addr, segment))
|
||||
: (IntBuffer)(new ByteBufferAsIntBuffer$RW$L(this,
|
||||
-1,
|
||||
0,
|
||||
size,
|
||||
size,
|
||||
addr)));
|
||||
addr, segment)));
|
||||
}
|
||||
|
||||
|
||||
@ -483,10 +505,12 @@ class Heap$Type$Buffer$RW$
|
||||
#if[rw]
|
||||
|
||||
public long getLong() {
|
||||
checkSegment();
|
||||
return UNSAFE.getLongUnaligned(hb, byteOffset(nextGetIndex(8)), bigEndian);
|
||||
}
|
||||
|
||||
public long getLong(int i) {
|
||||
checkSegment();
|
||||
return UNSAFE.getLongUnaligned(hb, byteOffset(checkIndex(i, 8)), bigEndian);
|
||||
}
|
||||
|
||||
@ -494,6 +518,7 @@ class Heap$Type$Buffer$RW$
|
||||
|
||||
public $Type$Buffer putLong(long x) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
UNSAFE.putLongUnaligned(hb, byteOffset(nextPutIndex(8)), x, bigEndian);
|
||||
return this;
|
||||
#else[rw]
|
||||
@ -503,6 +528,7 @@ class Heap$Type$Buffer$RW$
|
||||
|
||||
public $Type$Buffer putLong(int i, long x) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
UNSAFE.putLongUnaligned(hb, byteOffset(checkIndex(i, 8)), x, bigEndian);
|
||||
return this;
|
||||
#else[rw]
|
||||
@ -520,13 +546,13 @@ class Heap$Type$Buffer$RW$
|
||||
0,
|
||||
size,
|
||||
size,
|
||||
addr))
|
||||
addr, segment))
|
||||
: (LongBuffer)(new ByteBufferAsLongBuffer$RW$L(this,
|
||||
-1,
|
||||
0,
|
||||
size,
|
||||
size,
|
||||
addr)));
|
||||
addr, segment)));
|
||||
}
|
||||
|
||||
|
||||
@ -535,11 +561,13 @@ class Heap$Type$Buffer$RW$
|
||||
#if[rw]
|
||||
|
||||
public float getFloat() {
|
||||
checkSegment();
|
||||
int x = UNSAFE.getIntUnaligned(hb, byteOffset(nextGetIndex(4)), bigEndian);
|
||||
return Float.intBitsToFloat(x);
|
||||
}
|
||||
|
||||
public float getFloat(int i) {
|
||||
checkSegment();
|
||||
int x = UNSAFE.getIntUnaligned(hb, byteOffset(checkIndex(i, 4)), bigEndian);
|
||||
return Float.intBitsToFloat(x);
|
||||
}
|
||||
@ -548,6 +576,7 @@ class Heap$Type$Buffer$RW$
|
||||
|
||||
public $Type$Buffer putFloat(float x) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
int y = Float.floatToRawIntBits(x);
|
||||
UNSAFE.putIntUnaligned(hb, byteOffset(nextPutIndex(4)), y, bigEndian);
|
||||
return this;
|
||||
@ -558,6 +587,7 @@ class Heap$Type$Buffer$RW$
|
||||
|
||||
public $Type$Buffer putFloat(int i, float x) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
int y = Float.floatToRawIntBits(x);
|
||||
UNSAFE.putIntUnaligned(hb, byteOffset(checkIndex(i, 4)), y, bigEndian);
|
||||
return this;
|
||||
@ -576,13 +606,13 @@ class Heap$Type$Buffer$RW$
|
||||
0,
|
||||
size,
|
||||
size,
|
||||
addr))
|
||||
addr, segment))
|
||||
: (FloatBuffer)(new ByteBufferAsFloatBuffer$RW$L(this,
|
||||
-1,
|
||||
0,
|
||||
size,
|
||||
size,
|
||||
addr)));
|
||||
addr, segment)));
|
||||
}
|
||||
|
||||
|
||||
@ -591,11 +621,13 @@ class Heap$Type$Buffer$RW$
|
||||
#if[rw]
|
||||
|
||||
public double getDouble() {
|
||||
checkSegment();
|
||||
long x = UNSAFE.getLongUnaligned(hb, byteOffset(nextGetIndex(8)), bigEndian);
|
||||
return Double.longBitsToDouble(x);
|
||||
}
|
||||
|
||||
public double getDouble(int i) {
|
||||
checkSegment();
|
||||
long x = UNSAFE.getLongUnaligned(hb, byteOffset(checkIndex(i, 8)), bigEndian);
|
||||
return Double.longBitsToDouble(x);
|
||||
}
|
||||
@ -604,6 +636,7 @@ class Heap$Type$Buffer$RW$
|
||||
|
||||
public $Type$Buffer putDouble(double x) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
long y = Double.doubleToRawLongBits(x);
|
||||
UNSAFE.putLongUnaligned(hb, byteOffset(nextPutIndex(8)), y, bigEndian);
|
||||
return this;
|
||||
@ -614,6 +647,7 @@ class Heap$Type$Buffer$RW$
|
||||
|
||||
public $Type$Buffer putDouble(int i, double x) {
|
||||
#if[rw]
|
||||
checkSegment();
|
||||
long y = Double.doubleToRawLongBits(x);
|
||||
UNSAFE.putLongUnaligned(hb, byteOffset(checkIndex(i, 8)), y, bigEndian);
|
||||
return this;
|
||||
@ -632,13 +666,13 @@ class Heap$Type$Buffer$RW$
|
||||
0,
|
||||
size,
|
||||
size,
|
||||
addr))
|
||||
addr, segment))
|
||||
: (DoubleBuffer)(new ByteBufferAsDoubleBuffer$RW$L(this,
|
||||
-1,
|
||||
0,
|
||||
size,
|
||||
size,
|
||||
addr)));
|
||||
addr, segment)));
|
||||
}
|
||||
|
||||
|
||||
@ -666,7 +700,7 @@ class Heap$Type$Buffer$RW$
|
||||
pos + start,
|
||||
pos + end,
|
||||
capacity(),
|
||||
offset);
|
||||
offset, segment);
|
||||
}
|
||||
|
||||
#end[char]
|
||||
|
@ -28,6 +28,8 @@ package java.nio;
|
||||
import java.io.FileDescriptor;
|
||||
import java.lang.ref.Reference;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.internal.access.foreign.MemorySegmentProxy;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
|
||||
@ -88,21 +90,21 @@ public abstract class MappedByteBuffer
|
||||
// This should only be invoked by the DirectByteBuffer constructors
|
||||
//
|
||||
MappedByteBuffer(int mark, int pos, int lim, int cap, // package-private
|
||||
FileDescriptor fd, boolean isSync) {
|
||||
super(mark, pos, lim, cap);
|
||||
FileDescriptor fd, boolean isSync, MemorySegmentProxy segment) {
|
||||
super(mark, pos, lim, cap, segment);
|
||||
this.fd = fd;
|
||||
this.isSync = isSync;
|
||||
}
|
||||
|
||||
MappedByteBuffer(int mark, int pos, int lim, int cap, // package-private
|
||||
boolean isSync) {
|
||||
super(mark, pos, lim, cap);
|
||||
boolean isSync, MemorySegmentProxy segment) {
|
||||
super(mark, pos, lim, cap, segment);
|
||||
this.fd = null;
|
||||
this.isSync = isSync;
|
||||
}
|
||||
|
||||
MappedByteBuffer(int mark, int pos, int lim, int cap) { // package-private
|
||||
super(mark, pos, lim, cap);
|
||||
MappedByteBuffer(int mark, int pos, int lim, int cap, MemorySegmentProxy segment) { // package-private
|
||||
super(mark, pos, lim, cap, segment);
|
||||
this.fd = null;
|
||||
this.isSync = false;
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ class StringCharBuffer // package-private
|
||||
CharSequence str;
|
||||
|
||||
StringCharBuffer(CharSequence s, int start, int end) { // package-private
|
||||
super(-1, start, end, s.length());
|
||||
super(-1, start, end, s.length(), null);
|
||||
int n = s.length();
|
||||
Objects.checkFromToIndex(start, end, n);
|
||||
str = s;
|
||||
@ -68,7 +68,7 @@ class StringCharBuffer // package-private
|
||||
int limit,
|
||||
int cap,
|
||||
int offset) {
|
||||
super(mark, pos, limit, cap, null, offset);
|
||||
super(mark, pos, limit, cap, null, offset, null);
|
||||
str = s;
|
||||
this.isReadOnly = true;
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ import java.util.stream.$Streamtype$Stream;
|
||||
#end[streamableType]
|
||||
|
||||
import java.util.Objects;
|
||||
import jdk.internal.access.foreign.MemorySegmentProxy;
|
||||
import jdk.internal.util.ArraysSupport;
|
||||
|
||||
/**
|
||||
@ -279,17 +280,25 @@ public abstract class $Type$Buffer
|
||||
// backing array, and array offset
|
||||
//
|
||||
$Type$Buffer(int mark, int pos, int lim, int cap, // package-private
|
||||
$type$[] hb, int offset)
|
||||
$type$[] hb, int offset, MemorySegmentProxy segment)
|
||||
{
|
||||
super(mark, pos, lim, cap);
|
||||
super(mark, pos, lim, cap, segment);
|
||||
this.hb = hb;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
// Creates a new buffer with the given mark, position, limit, and capacity
|
||||
//
|
||||
$Type$Buffer(int mark, int pos, int lim, int cap) { // package-private
|
||||
this(mark, pos, lim, cap, null, 0);
|
||||
$Type$Buffer(int mark, int pos, int lim, int cap, MemorySegmentProxy segment) { // package-private
|
||||
this(mark, pos, lim, cap, null, 0, segment);
|
||||
}
|
||||
|
||||
// Creates a new buffer with given base, address and capacity
|
||||
//
|
||||
$Type$Buffer($type$[] hb, long addr, int cap, MemorySegmentProxy segment) { // package-private
|
||||
super(addr, cap, segment);
|
||||
this.hb = hb;
|
||||
this.offset = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -348,7 +357,7 @@ public abstract class $Type$Buffer
|
||||
public static $Type$Buffer allocate(int capacity) {
|
||||
if (capacity < 0)
|
||||
throw createCapacityException(capacity);
|
||||
return new Heap$Type$Buffer(capacity, capacity);
|
||||
return new Heap$Type$Buffer(capacity, capacity, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -393,7 +402,7 @@ public abstract class $Type$Buffer
|
||||
int offset, int length)
|
||||
{
|
||||
try {
|
||||
return new Heap$Type$Buffer(array, offset, length);
|
||||
return new Heap$Type$Buffer(array, offset, length, null);
|
||||
} catch (IllegalArgumentException x) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
@ -26,6 +26,8 @@
|
||||
package jdk.internal.access;
|
||||
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Map;
|
||||
|
||||
public interface JavaLangInvokeAccess {
|
||||
@ -106,4 +108,41 @@ public interface JavaLangInvokeAccess {
|
||||
MethodType[] invokerMethodTypes,
|
||||
MethodType[] callSiteMethodTypes);
|
||||
|
||||
/**
|
||||
* Returns a var handle view of a given memory address.
|
||||
* Used by {@code jdk.internal.foreign.LayoutPath} and
|
||||
* {@code jdk.incubator.foreign.MemoryHandles}.
|
||||
*/
|
||||
VarHandle memoryAddressViewVarHandle(Class<?> carrier, long alignmentMask,
|
||||
ByteOrder order, long offset, long[] strides);
|
||||
|
||||
/**
|
||||
* Returns the carrier associated with a memory access var handle.
|
||||
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
|
||||
*/
|
||||
Class<?> memoryAddressCarrier(VarHandle handle);
|
||||
|
||||
/**
|
||||
* Returns the alignment mask associated with a memory access var handle.
|
||||
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
|
||||
*/
|
||||
long memoryAddressAlignmentMask(VarHandle handle);
|
||||
|
||||
/**
|
||||
* Returns the byte order associated with a memory access var handle.
|
||||
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
|
||||
*/
|
||||
ByteOrder memoryAddressByteOrder(VarHandle handle);
|
||||
|
||||
/**
|
||||
* Returns the offset associated with a memory access var handle.
|
||||
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
|
||||
*/
|
||||
long memoryAddressOffset(VarHandle handle);
|
||||
|
||||
/**
|
||||
* Returns the strides associated with a memory access var handle.
|
||||
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
|
||||
*/
|
||||
long[] memoryAddressStrides(VarHandle handle);
|
||||
}
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
package jdk.internal.access;
|
||||
|
||||
import jdk.internal.access.foreign.MemorySegmentProxy;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
@ -39,4 +41,34 @@ public interface JavaNioAccess {
|
||||
long getMemoryUsed();
|
||||
}
|
||||
BufferPool getDirectBufferPool();
|
||||
|
||||
/**
|
||||
* Constructs a direct ByteBuffer referring to the block of memory starting
|
||||
* at the given memory address and extending {@code cap} bytes.
|
||||
* The {@code ob} parameter is an arbitrary object that is attached
|
||||
* to the resulting buffer.
|
||||
* Used by {@code jdk.internal.foreignMemorySegmentImpl}.
|
||||
*/
|
||||
ByteBuffer newDirectByteBuffer(long addr, int cap, Object obj, MemorySegmentProxy segment);
|
||||
|
||||
/**
|
||||
* Constructs an heap ByteBuffer with given backing array, offset, capacity and segment.
|
||||
* Used by {@code jdk.internal.foreignMemorySegmentImpl}.
|
||||
*/
|
||||
ByteBuffer newHeapByteBuffer(byte[] hb, int offset, int capacity, MemorySegmentProxy segment);
|
||||
|
||||
/**
|
||||
* Used by {@code jdk.internal.foreign.Utils}.
|
||||
*/
|
||||
Object getBufferBase(ByteBuffer bb);
|
||||
|
||||
/**
|
||||
* Used by {@code jdk.internal.foreign.Utils}.
|
||||
*/
|
||||
long getBufferAddress(ByteBuffer bb);
|
||||
|
||||
/**
|
||||
* Used by byte buffer var handle views.
|
||||
*/
|
||||
void checkSegment(Buffer buffer);
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
package jdk.internal.access.foreign;
|
||||
|
||||
/**
|
||||
* This proxy interface is required to allow instances of the {@code MemoryAddress} interface (which is defined inside
|
||||
* an incubating module) to be accessed from the memory access var handles.
|
||||
*/
|
||||
public interface MemoryAddressProxy {
|
||||
/**
|
||||
* Check that memory access is within spatial and temporal bounds.
|
||||
* @throws IllegalStateException if underlying segment has been closed already.
|
||||
* @throws IndexOutOfBoundsException if access is out-of-bounds.
|
||||
*/
|
||||
void checkAccess(long offset, long length, boolean readOnly);
|
||||
long unsafeGetOffset();
|
||||
Object unsafeGetBase();
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
package jdk.internal.access.foreign;
|
||||
|
||||
/**
|
||||
* This proxy interface is required to allow instances of the {@code MemorySegment} interface (which is defined inside
|
||||
* an incubating module) to be accessed from the memory access var handles.
|
||||
*/
|
||||
public interface MemorySegmentProxy {
|
||||
void checkValidState();
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
package jdk.internal.access.foreign;
|
||||
|
||||
/**
|
||||
* This proxy interface is required to allow instances of the {@code FileChannelImpl.Unmapper} interface (which is a non-public class
|
||||
* inside the {@code sun.nio.ch} package) to be accessed from the mapped memory segment factory.
|
||||
*/
|
||||
public interface UnmapperProxy {
|
||||
long address();
|
||||
void unmap();
|
||||
}
|
@ -134,7 +134,8 @@ module java.base {
|
||||
// see make/gensrc/GenModuleInfo.gmk
|
||||
|
||||
exports sun.invoke.util to
|
||||
jdk.compiler;
|
||||
jdk.compiler,
|
||||
jdk.incubator.foreign;
|
||||
exports com.sun.security.ntlm to
|
||||
java.security.sasl;
|
||||
exports jdk.internal to
|
||||
@ -149,7 +150,10 @@ module java.base {
|
||||
java.naming,
|
||||
java.rmi,
|
||||
jdk.jlink,
|
||||
jdk.net;
|
||||
jdk.net,
|
||||
jdk.incubator.foreign;
|
||||
exports jdk.internal.access.foreign to
|
||||
jdk.incubator.foreign;
|
||||
exports jdk.internal.event to
|
||||
jdk.jfr;
|
||||
exports jdk.internal.jimage to
|
||||
@ -202,7 +206,8 @@ module java.base {
|
||||
jdk.scripting.nashorn,
|
||||
jdk.scripting.nashorn.shell,
|
||||
jdk.unsupported,
|
||||
jdk.internal.vm.ci;
|
||||
jdk.internal.vm.ci,
|
||||
jdk.incubator.foreign;
|
||||
exports jdk.internal.module to
|
||||
java.instrument,
|
||||
java.management.rmi,
|
||||
@ -260,7 +265,8 @@ module java.base {
|
||||
java.management,
|
||||
jdk.crypto.cryptoki,
|
||||
jdk.net,
|
||||
jdk.sctp;
|
||||
jdk.sctp,
|
||||
jdk.incubator.foreign;
|
||||
exports sun.nio.cs to
|
||||
jdk.charsets;
|
||||
exports sun.reflect.annotation to
|
||||
@ -276,7 +282,8 @@ module java.base {
|
||||
java.sql.rowset;
|
||||
exports sun.security.action to
|
||||
java.desktop,
|
||||
java.security.jgss;
|
||||
java.security.jgss,
|
||||
jdk.incubator.foreign;
|
||||
exports sun.security.internal.interfaces to
|
||||
jdk.crypto.cryptoki;
|
||||
exports sun.security.internal.spec to
|
||||
|
@ -42,6 +42,7 @@ import java.nio.channels.NonWritableChannelException;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.SelectableChannel;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.internal.access.JavaIOFileDescriptorAccess;
|
||||
import jdk.internal.access.JavaNioAccess;
|
||||
@ -51,6 +52,8 @@ import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.ref.Cleaner;
|
||||
import jdk.internal.ref.CleanerFactory;
|
||||
|
||||
import jdk.internal.access.foreign.UnmapperProxy;
|
||||
|
||||
public class FileChannelImpl
|
||||
extends FileChannel
|
||||
{
|
||||
@ -863,27 +866,39 @@ public class FileChannelImpl
|
||||
// -- Memory-mapped buffers --
|
||||
|
||||
private static abstract class Unmapper
|
||||
implements Runnable
|
||||
implements Runnable, UnmapperProxy
|
||||
{
|
||||
// may be required to close file
|
||||
private static final NativeDispatcher nd = new FileDispatcherImpl();
|
||||
|
||||
private volatile long address;
|
||||
protected final long size;
|
||||
protected final int cap;
|
||||
protected final long cap;
|
||||
private final FileDescriptor fd;
|
||||
private final int pagePosition;
|
||||
|
||||
private Unmapper(long address, long size, int cap,
|
||||
FileDescriptor fd)
|
||||
private Unmapper(long address, long size, long cap,
|
||||
FileDescriptor fd, int pagePosition)
|
||||
{
|
||||
assert (address != 0);
|
||||
this.address = address;
|
||||
this.size = size;
|
||||
this.cap = cap;
|
||||
this.fd = fd;
|
||||
this.pagePosition = pagePosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long address() {
|
||||
return address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
unmap();
|
||||
}
|
||||
|
||||
public void unmap() {
|
||||
if (address == 0)
|
||||
return;
|
||||
unmap0(address, size);
|
||||
@ -911,9 +926,9 @@ public class FileChannelImpl
|
||||
static volatile long totalSize;
|
||||
static volatile long totalCapacity;
|
||||
|
||||
public DefaultUnmapper(long address, long size, int cap,
|
||||
FileDescriptor fd) {
|
||||
super(address, size, cap, fd);
|
||||
public DefaultUnmapper(long address, long size, long cap,
|
||||
FileDescriptor fd, int pagePosition) {
|
||||
super(address, size, cap, fd, pagePosition);
|
||||
incrementStats();
|
||||
}
|
||||
|
||||
@ -940,9 +955,9 @@ public class FileChannelImpl
|
||||
static volatile long totalSize;
|
||||
static volatile long totalCapacity;
|
||||
|
||||
public SyncUnmapper(long address, long size, int cap,
|
||||
FileDescriptor fd) {
|
||||
super(address, size, cap, fd);
|
||||
public SyncUnmapper(long address, long size, long cap,
|
||||
FileDescriptor fd, int pagePosition) {
|
||||
super(address, size, cap, fd, pagePosition);
|
||||
incrementStats();
|
||||
}
|
||||
|
||||
@ -968,11 +983,44 @@ public class FileChannelImpl
|
||||
cl.clean();
|
||||
}
|
||||
|
||||
private static final int MAP_INVALID = -1;
|
||||
private static final int MAP_RO = 0;
|
||||
private static final int MAP_RW = 1;
|
||||
private static final int MAP_PV = 2;
|
||||
|
||||
public MappedByteBuffer map(MapMode mode, long position, long size)
|
||||
public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException {
|
||||
if (size > Integer.MAX_VALUE)
|
||||
throw new IllegalArgumentException("Size exceeds Integer.MAX_VALUE");
|
||||
boolean isSync = isSync(Objects.requireNonNull(mode, "Mode is null"));
|
||||
int prot = toProt(mode);
|
||||
Unmapper unmapper = mapInternal(mode, position, size, prot, isSync);
|
||||
if (unmapper == null) {
|
||||
// a valid file descriptor is not required
|
||||
FileDescriptor dummy = new FileDescriptor();
|
||||
if ((!writable) || (prot == MAP_RO))
|
||||
return Util.newMappedByteBufferR(0, 0, dummy, null, isSync);
|
||||
else
|
||||
return Util.newMappedByteBuffer(0, 0, dummy, null, isSync);
|
||||
} else if ((!writable) || (prot == MAP_RO)) {
|
||||
return Util.newMappedByteBufferR((int)unmapper.cap,
|
||||
unmapper.address + unmapper.pagePosition,
|
||||
unmapper.fd,
|
||||
unmapper, isSync);
|
||||
} else {
|
||||
return Util.newMappedByteBuffer((int)unmapper.cap,
|
||||
unmapper.address + unmapper.pagePosition,
|
||||
unmapper.fd,
|
||||
unmapper, isSync);
|
||||
}
|
||||
}
|
||||
|
||||
public Unmapper mapInternal(MapMode mode, long position, long size) throws IOException {
|
||||
boolean isSync = isSync(Objects.requireNonNull(mode, "Mode is null"));
|
||||
int prot = toProt(mode);
|
||||
return mapInternal(mode, position, size, prot, isSync);
|
||||
}
|
||||
|
||||
private Unmapper mapInternal(MapMode mode, long position, long size, int prot, boolean isSync)
|
||||
throws IOException
|
||||
{
|
||||
ensureOpen();
|
||||
@ -984,35 +1032,8 @@ public class FileChannelImpl
|
||||
throw new IllegalArgumentException("Negative size");
|
||||
if (position + size < 0)
|
||||
throw new IllegalArgumentException("Position + size overflow");
|
||||
if (size > Integer.MAX_VALUE)
|
||||
throw new IllegalArgumentException("Size exceeds Integer.MAX_VALUE");
|
||||
|
||||
int imode;
|
||||
boolean isSync = false;
|
||||
if (mode == MapMode.READ_ONLY)
|
||||
imode = MAP_RO;
|
||||
else if (mode == MapMode.READ_WRITE)
|
||||
imode = MAP_RW;
|
||||
else if (mode == MapMode.PRIVATE)
|
||||
imode = MAP_PV;
|
||||
else if (mode == ExtendedMapMode.READ_ONLY_SYNC) {
|
||||
imode = MAP_RO;
|
||||
isSync = true;
|
||||
} else if (mode == ExtendedMapMode.READ_WRITE_SYNC) {
|
||||
imode = MAP_RW;
|
||||
isSync = true;
|
||||
} else {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
if ((mode != MapMode.READ_ONLY) && mode != ExtendedMapMode.READ_ONLY_SYNC && !writable)
|
||||
throw new NonWritableChannelException();
|
||||
if (!readable)
|
||||
throw new NonReadableChannelException();
|
||||
// reject SYNC request if writeback is not enabled for this platform
|
||||
if (isSync && !Unsafe.isWritebackEnabled()) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
checkMode(mode, prot, isSync);
|
||||
long addr = -1;
|
||||
int ti = -1;
|
||||
try {
|
||||
@ -1045,13 +1066,7 @@ public class FileChannelImpl
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
addr = 0;
|
||||
// a valid file descriptor is not required
|
||||
FileDescriptor dummy = new FileDescriptor();
|
||||
if ((!writable) || (imode == MAP_RO))
|
||||
return Util.newMappedByteBufferR(0, 0, dummy, null, isSync);
|
||||
else
|
||||
return Util.newMappedByteBuffer(0, 0, dummy, null, isSync);
|
||||
return null;
|
||||
}
|
||||
|
||||
pagePosition = (int)(position % allocationGranularity);
|
||||
@ -1059,7 +1074,7 @@ public class FileChannelImpl
|
||||
mapSize = size + pagePosition;
|
||||
try {
|
||||
// If map0 did not throw an exception, the address is valid
|
||||
addr = map0(imode, mapPosition, mapSize, isSync);
|
||||
addr = map0(prot, mapPosition, mapSize, isSync);
|
||||
} catch (OutOfMemoryError x) {
|
||||
// An OutOfMemoryError may indicate that we've exhausted
|
||||
// memory so force gc and re-attempt map
|
||||
@ -1070,7 +1085,7 @@ public class FileChannelImpl
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
try {
|
||||
addr = map0(imode, mapPosition, mapSize, isSync);
|
||||
addr = map0(prot, mapPosition, mapSize, isSync);
|
||||
} catch (OutOfMemoryError y) {
|
||||
// After a second OOME, fail
|
||||
throw new IOException("Map failed", y);
|
||||
@ -1090,29 +1105,53 @@ public class FileChannelImpl
|
||||
|
||||
assert (IOStatus.checkAll(addr));
|
||||
assert (addr % allocationGranularity == 0);
|
||||
int isize = (int)size;
|
||||
Unmapper um = (isSync
|
||||
? new SyncUnmapper(addr, mapSize, isize, mfd)
|
||||
: new DefaultUnmapper(addr, mapSize, isize, mfd));
|
||||
if ((!writable) || (imode == MAP_RO)) {
|
||||
return Util.newMappedByteBufferR(isize,
|
||||
addr + pagePosition,
|
||||
mfd,
|
||||
um,
|
||||
isSync);
|
||||
} else {
|
||||
return Util.newMappedByteBuffer(isize,
|
||||
addr + pagePosition,
|
||||
mfd,
|
||||
um,
|
||||
isSync);
|
||||
}
|
||||
? new SyncUnmapper(addr, mapSize, size, mfd, pagePosition)
|
||||
: new DefaultUnmapper(addr, mapSize, size, mfd, pagePosition));
|
||||
return um;
|
||||
} finally {
|
||||
threads.remove(ti);
|
||||
endBlocking(IOStatus.checkAll(addr));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSync(MapMode mode) {
|
||||
return mode == ExtendedMapMode.READ_ONLY_SYNC ||
|
||||
mode == ExtendedMapMode.READ_WRITE_SYNC;
|
||||
}
|
||||
|
||||
private int toProt(MapMode mode) {
|
||||
int prot;
|
||||
if (mode == MapMode.READ_ONLY) {
|
||||
prot = MAP_RO;
|
||||
} else if (mode == MapMode.READ_WRITE) {
|
||||
prot = MAP_RW;
|
||||
} else if (mode == MapMode.PRIVATE) {
|
||||
prot = MAP_PV;
|
||||
} else if (mode == ExtendedMapMode.READ_ONLY_SYNC) {
|
||||
prot = MAP_RO;
|
||||
} else if (mode == ExtendedMapMode.READ_WRITE_SYNC) {
|
||||
prot = MAP_RW;
|
||||
} else {
|
||||
prot = MAP_INVALID;
|
||||
}
|
||||
return prot;
|
||||
}
|
||||
|
||||
private void checkMode(MapMode mode, int prot, boolean isSync) {
|
||||
if (prot == MAP_INVALID) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
if ((mode != MapMode.READ_ONLY) && mode != ExtendedMapMode.READ_ONLY_SYNC && !writable)
|
||||
throw new NonWritableChannelException();
|
||||
if (!readable)
|
||||
throw new NonReadableChannelException();
|
||||
// reject SYNC request if writeback is not enabled for this platform
|
||||
if (isSync && !Unsafe.isWritebackEnabled()) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by sun.management.ManagementFactoryHelper to create the management
|
||||
* interface for mapped buffers.
|
||||
|
@ -37,6 +37,7 @@ import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.internal.access.foreign.MemorySegmentProxy;
|
||||
import jdk.internal.misc.TerminatingThreadLocal;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
@ -416,7 +417,7 @@ public class Util {
|
||||
long.class,
|
||||
FileDescriptor.class,
|
||||
Runnable.class,
|
||||
boolean.class });
|
||||
boolean.class, MemorySegmentProxy.class});
|
||||
ctor.setAccessible(true);
|
||||
directByteBufferConstructor = ctor;
|
||||
} catch (ClassNotFoundException |
|
||||
@ -443,7 +444,7 @@ public class Util {
|
||||
addr,
|
||||
fd,
|
||||
unmapper,
|
||||
isSync});
|
||||
isSync, null});
|
||||
} catch (InstantiationException |
|
||||
IllegalAccessException |
|
||||
InvocationTargetException e) {
|
||||
@ -464,7 +465,7 @@ public class Util {
|
||||
long.class,
|
||||
FileDescriptor.class,
|
||||
Runnable.class,
|
||||
boolean.class });
|
||||
boolean.class, MemorySegmentProxy.class });
|
||||
ctor.setAccessible(true);
|
||||
directByteBufferRConstructor = ctor;
|
||||
} catch (ClassNotFoundException |
|
||||
@ -491,7 +492,7 @@ public class Util {
|
||||
addr,
|
||||
fd,
|
||||
unmapper,
|
||||
isSync});
|
||||
isSync, null});
|
||||
} catch (InstantiationException |
|
||||
IllegalAccessException |
|
||||
InvocationTargetException e) {
|
||||
|
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
package jdk.incubator.foreign;
|
||||
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.ConstantDesc;
|
||||
import java.lang.constant.ConstantDescs;
|
||||
import java.lang.constant.DirectMethodHandleDesc;
|
||||
import java.lang.constant.DynamicConstantDesc;
|
||||
import java.lang.constant.MethodHandleDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
|
||||
abstract class AbstractLayout implements MemoryLayout {
|
||||
private final OptionalLong size;
|
||||
final long alignment;
|
||||
private final Optional<String> name;
|
||||
|
||||
public AbstractLayout(OptionalLong size, long alignment, Optional<String> name) {
|
||||
this.size = size;
|
||||
this.alignment = alignment;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
Optional<String> optName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractLayout withName(String name) {
|
||||
return dup(alignment, Optional.of(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Optional<String> name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
abstract AbstractLayout dup(long alignment, Optional<String> name);
|
||||
|
||||
@Override
|
||||
public AbstractLayout withBitAlignment(long alignmentBits) {
|
||||
checkAlignment(alignmentBits);
|
||||
return dup(alignmentBits, name);
|
||||
}
|
||||
|
||||
void checkAlignment(long alignmentBitCount) {
|
||||
if (((alignmentBitCount & (alignmentBitCount - 1)) != 0L) || //alignment must be a power of two
|
||||
(alignmentBitCount < 8)) { //alignment must be greater than 8
|
||||
throw new IllegalArgumentException("Invalid alignment: " + alignmentBitCount);
|
||||
}
|
||||
}
|
||||
|
||||
static void checkSize(long size) {
|
||||
checkSize(size, false);
|
||||
}
|
||||
|
||||
static void checkSize(long size, boolean includeZero) {
|
||||
if (size < 0 || (!includeZero && size == 0)) {
|
||||
throw new IllegalArgumentException("Invalid size for layout: " + size);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final long bitAlignment() {
|
||||
return alignment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long bitSize() {
|
||||
return size.orElseThrow(this::badSizeException);
|
||||
}
|
||||
|
||||
static OptionalLong optSize(MemoryLayout layout) {
|
||||
return ((AbstractLayout)layout).size;
|
||||
}
|
||||
|
||||
private UnsupportedOperationException badSizeException() {
|
||||
return new UnsupportedOperationException("Cannot compute size of a layout which is, or depends on a sequence layout with unspecified size");
|
||||
}
|
||||
|
||||
String decorateLayoutString(String s) {
|
||||
if (name.isPresent()) {
|
||||
s = String.format("%s(%s)", s, name.get());
|
||||
}
|
||||
if (!hasNaturalAlignment()) {
|
||||
s = alignment + "%" + s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
boolean hasNaturalAlignment() {
|
||||
return size.isPresent() && size.getAsLong() == alignment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name.hashCode() << Long.hashCode(alignment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(other instanceof AbstractLayout)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Objects.equals(name, ((AbstractLayout)other).name) &&
|
||||
Objects.equals(alignment, ((AbstractLayout)other).alignment);
|
||||
}
|
||||
|
||||
/*** Helper constants for implementing Layout::describeConstable ***/
|
||||
|
||||
public static final DirectMethodHandleDesc BSM_GET_STATIC_FINAL
|
||||
= ConstantDescs.ofConstantBootstrap(ConstantDescs.CD_ConstantBootstraps, "getStaticFinal",
|
||||
ConstantDescs.CD_Object, ConstantDescs.CD_Class);
|
||||
|
||||
static final ClassDesc CD_LAYOUT = MemoryLayout.class.describeConstable().get();
|
||||
|
||||
static final ClassDesc CD_VALUE_LAYOUT = ValueLayout.class.describeConstable().get();
|
||||
|
||||
static final ClassDesc CD_SEQUENCE_LAYOUT = SequenceLayout.class.describeConstable().get();
|
||||
|
||||
static final ClassDesc CD_GROUP_LAYOUT = GroupLayout.class.describeConstable().get();
|
||||
|
||||
static final ClassDesc CD_BYTEORDER = ByteOrder.class.describeConstable().get();
|
||||
|
||||
static final ConstantDesc BIG_ENDIAN = DynamicConstantDesc.ofNamed(BSM_GET_STATIC_FINAL, "BIG_ENDIAN", CD_BYTEORDER, CD_BYTEORDER);
|
||||
|
||||
static final ConstantDesc LITTLE_ENDIAN = DynamicConstantDesc.ofNamed(BSM_GET_STATIC_FINAL, "LITTLE_ENDIAN", CD_BYTEORDER, CD_BYTEORDER);
|
||||
|
||||
static final MethodHandleDesc MH_PADDING = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_LAYOUT, "ofPaddingBits",
|
||||
MethodTypeDesc.of(CD_LAYOUT, ConstantDescs.CD_long));
|
||||
|
||||
static final MethodHandleDesc MH_VALUE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_LAYOUT, "ofValueBits",
|
||||
MethodTypeDesc.of(CD_VALUE_LAYOUT, ConstantDescs.CD_long, CD_BYTEORDER));
|
||||
|
||||
static final MethodHandleDesc MH_SIZED_SEQUENCE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_LAYOUT, "ofSequence",
|
||||
MethodTypeDesc.of(CD_SEQUENCE_LAYOUT, ConstantDescs.CD_long, CD_LAYOUT));
|
||||
|
||||
static final MethodHandleDesc MH_UNSIZED_SEQUENCE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_LAYOUT, "ofSequence",
|
||||
MethodTypeDesc.of(CD_SEQUENCE_LAYOUT, CD_LAYOUT));
|
||||
|
||||
static final MethodHandleDesc MH_STRUCT = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_LAYOUT, "ofStruct",
|
||||
MethodTypeDesc.of(CD_GROUP_LAYOUT, CD_LAYOUT.arrayType()));
|
||||
|
||||
static final MethodHandleDesc MH_UNION = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_LAYOUT, "ofUnion",
|
||||
MethodTypeDesc.of(CD_GROUP_LAYOUT, CD_LAYOUT.arrayType()));
|
||||
}
|
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
package jdk.incubator.foreign;
|
||||
|
||||
import java.lang.constant.ConstantDesc;
|
||||
import java.lang.constant.ConstantDescs;
|
||||
import java.lang.constant.DynamicConstantDesc;
|
||||
import java.lang.constant.MethodHandleDesc;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.function.LongBinaryOperator;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A group layout is used to combine together multiple <em>member layouts</em>. There are two ways in which member layouts
|
||||
* can be combined: if member layouts are laid out one after the other, the resulting group layout is said to be a <em>struct</em>
|
||||
* (see {@link MemoryLayout#ofStruct(MemoryLayout...)}); conversely, if all member layouts are laid out at the same starting offset,
|
||||
* the resulting group layout is said to be a <em>union</em> (see {@link MemoryLayout#ofUnion(MemoryLayout...)}).
|
||||
*
|
||||
* <p>
|
||||
* This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
|
||||
* class; use of identity-sensitive operations (including reference equality
|
||||
* ({@code ==}), identity hash code, or synchronization) on instances of
|
||||
* {@code GroupLayout} may have unpredictable results and should be avoided.
|
||||
* The {@code equals} method should be used for comparisons.
|
||||
*
|
||||
* @implSpec
|
||||
* This class is immutable and thread-safe.
|
||||
*/
|
||||
public final class GroupLayout extends AbstractLayout {
|
||||
|
||||
/**
|
||||
* The group kind.
|
||||
*/
|
||||
enum Kind {
|
||||
/**
|
||||
* A 'struct' kind.
|
||||
*/
|
||||
STRUCT("", MH_STRUCT, Long::sum),
|
||||
/**
|
||||
* A 'union' kind.
|
||||
*/
|
||||
UNION("|", MH_UNION, Math::max);
|
||||
|
||||
final String delimTag;
|
||||
final MethodHandleDesc mhDesc;
|
||||
final LongBinaryOperator sizeOp;
|
||||
|
||||
Kind(String delimTag, MethodHandleDesc mhDesc, LongBinaryOperator sizeOp) {
|
||||
this.delimTag = delimTag;
|
||||
this.mhDesc = mhDesc;
|
||||
this.sizeOp = sizeOp;
|
||||
}
|
||||
|
||||
OptionalLong sizeof(List<MemoryLayout> elems) {
|
||||
long size = 0;
|
||||
for (MemoryLayout elem : elems) {
|
||||
if (AbstractLayout.optSize(elem).isPresent()) {
|
||||
size = sizeOp.applyAsLong(size, elem.bitSize());
|
||||
} else {
|
||||
return OptionalLong.empty();
|
||||
}
|
||||
}
|
||||
return OptionalLong.of(size);
|
||||
}
|
||||
|
||||
long alignof(List<MemoryLayout> elems) {
|
||||
return elems.stream().mapToLong(MemoryLayout::bitAlignment).max() // max alignment in case we have member layouts
|
||||
.orElse(1); // or minimal alignment if no member layout is given
|
||||
}
|
||||
}
|
||||
|
||||
private final Kind kind;
|
||||
private final List<MemoryLayout> elements;
|
||||
|
||||
GroupLayout(Kind kind, List<MemoryLayout> elements) {
|
||||
this(kind, elements, kind.alignof(elements), Optional.empty());
|
||||
}
|
||||
|
||||
GroupLayout(Kind kind, List<MemoryLayout> elements, long alignment, Optional<String> name) {
|
||||
super(kind.sizeof(elements), alignment, name);
|
||||
this.kind = kind;
|
||||
this.elements = elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the member layouts associated with this group.
|
||||
*
|
||||
* @apiNote the order in which member layouts are returned is the same order in which member layouts have
|
||||
* been passed to one of the group layout factory methods (see {@link MemoryLayout#ofStruct(MemoryLayout...)},
|
||||
* {@link MemoryLayout#ofUnion(MemoryLayout...)}).
|
||||
*
|
||||
* @return the member layouts associated with this group.
|
||||
*/
|
||||
public List<MemoryLayout> memberLayouts() {
|
||||
return Collections.unmodifiableList(elements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return decorateLayoutString(elements.stream()
|
||||
.map(Object::toString)
|
||||
.collect(Collectors.joining(kind.delimTag, "[", "]")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this group layout a <em>struct</em>?
|
||||
*
|
||||
* @return true, if this group layout is a <em>struct</em>.
|
||||
*/
|
||||
public boolean isStruct() {
|
||||
return kind == Kind.STRUCT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this group layout a <em>union</em>?
|
||||
*
|
||||
* @return true, if this group layout is a <em>union</em>.
|
||||
*/
|
||||
public boolean isUnion() {
|
||||
return kind == Kind.UNION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
if (!(other instanceof GroupLayout)) {
|
||||
return false;
|
||||
}
|
||||
GroupLayout g = (GroupLayout)other;
|
||||
return kind.equals(g.kind) && elements.equals(g.elements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), kind, elements);
|
||||
}
|
||||
|
||||
@Override
|
||||
GroupLayout dup(long alignment, Optional<String> name) {
|
||||
return new GroupLayout(kind, elements, alignment, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasNaturalAlignment() {
|
||||
return alignment == kind.alignof(elements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DynamicConstantDesc<GroupLayout>> describeConstable() {
|
||||
ConstantDesc[] constants = new ConstantDesc[1 + elements.size()];
|
||||
constants[0] = kind.mhDesc;
|
||||
for (int i = 0 ; i < elements.size() ; i++) {
|
||||
constants[i + 1] = elements.get(i).describeConstable().get();
|
||||
}
|
||||
return Optional.of(DynamicConstantDesc.ofNamed(
|
||||
ConstantDescs.BSM_INVOKE, kind.name().toLowerCase(),
|
||||
CD_GROUP_LAYOUT, constants));
|
||||
}
|
||||
|
||||
//hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
|
||||
//but that causes issues with javadoc, see JDK-8224052
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GroupLayout withName(String name) {
|
||||
return (GroupLayout)super.withName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GroupLayout withBitAlignment(long alignmentBits) {
|
||||
return (GroupLayout)super.withBitAlignment(alignmentBits);
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
package jdk.incubator.foreign;
|
||||
|
||||
import jdk.internal.foreign.MemoryAddressImpl;
|
||||
|
||||
/**
|
||||
* A memory address encodes an offset within a given {@link MemorySegment}. Memory addresses are typically obtained
|
||||
* using the {@link MemorySegment#baseAddress()} method; such addresses can then be adjusted as required,
|
||||
* using {@link MemoryAddress#offset(long)}.
|
||||
* <p>
|
||||
* A memory address is typically used as the first argument in a memory access var handle call, to perform some operation
|
||||
* on the underlying memory backing a given memory segment. Since a memory address is always associated with a memory segment,
|
||||
* such access operations are always subject to spatial and temporal checks as enforced by the address' owning memory region.
|
||||
* <p>
|
||||
* All implementations of this interface must be <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>;
|
||||
* use of identity-sensitive operations (including reference equality ({@code ==}), identity hash code, or synchronization) on
|
||||
* instances of {@code MemoryAddress} may have unpredictable results and should be avoided. The {@code equals} method should
|
||||
* be used for comparisons.
|
||||
* <p>
|
||||
* Non-platform classes should not implement {@linkplain MemoryAddress} directly.
|
||||
*
|
||||
* @apiNote In the future, if the Java language permits, {@link MemoryAddress}
|
||||
* may become a {@code sealed} interface, which would prohibit subclassing except by
|
||||
* explicitly permitted types.
|
||||
*
|
||||
* @implSpec
|
||||
* Implementations of this interface are immutable and thread-safe.
|
||||
*/
|
||||
public interface MemoryAddress {
|
||||
/**
|
||||
* Creates a new memory address with given offset (in bytes) from current one.
|
||||
* @param l specified offset (in bytes), relative to this address, which should be used to create the new address.
|
||||
* @return a new memory address with given offset from current one.
|
||||
*/
|
||||
MemoryAddress offset(long l);
|
||||
|
||||
/**
|
||||
* The offset of this memory address into the underlying segment.
|
||||
*
|
||||
* @return the offset
|
||||
*/
|
||||
long offset();
|
||||
|
||||
/**
|
||||
* The memory segment this address belongs to.
|
||||
* @return The memory segment this address belongs to.
|
||||
*/
|
||||
MemorySegment segment();
|
||||
|
||||
/**
|
||||
* Compares the specified object with this address for equality. Returns {@code true} if and only if the specified
|
||||
* object is also a address, and it is equal to this address.
|
||||
*
|
||||
* @param that the object to be compared for equality with this address.
|
||||
* @return {@code true} if the specified object is equal to this address.
|
||||
*/
|
||||
@Override
|
||||
boolean equals(Object that);
|
||||
|
||||
/**
|
||||
* Returns the hash code value for this address.
|
||||
* @return the hash code value for this address.
|
||||
*/
|
||||
@Override
|
||||
int hashCode();
|
||||
|
||||
/**
|
||||
* Perform bulk copy from source address to target address. More specifically, the bytes at addresses {@code src}
|
||||
* through {@code src.offset(bytes - 1)} are copied into addresses {@code dst} through {@code dst.offset(bytes - 1)}.
|
||||
* If the source and address ranges overlap, then the copying is performed as if the bytes at addresses {@code src}
|
||||
* through {@code src.offset(bytes - 1)} were first copied into a temporary segment with size {@code bytes},
|
||||
* and then the contents of the temporary segment were copied into the bytes at addresses {@code dst} through {@code dst.offset(bytes - 1)}.
|
||||
* @param src the source address.
|
||||
* @param dst the target address.
|
||||
* @param bytes the number of bytes to be copied.
|
||||
* @throws IndexOutOfBoundsException if {@code bytes < 0}, or if it is greater than the size of the segments
|
||||
* associated with either {@code src} or {@code dst}.
|
||||
* @throws IllegalStateException if either the source address or the target address belong to memory segments
|
||||
* which have been already closed, or if access occurs from a thread other than the thread owning either segment.
|
||||
* @throws UnsupportedOperationException if {@code dst} is associated with a read-only segment (see {@link MemorySegment#isReadOnly()}).
|
||||
*/
|
||||
static void copy(MemoryAddress src, MemoryAddress dst, long bytes) {
|
||||
MemoryAddressImpl.copy((MemoryAddressImpl)src, (MemoryAddressImpl)dst, bytes);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
package jdk.incubator.foreign;
|
||||
|
||||
import jdk.internal.access.JavaLangInvokeAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.foreign.Utils;
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* This class defines several factory methods for constructing and combining memory access var handles.
|
||||
* To obtain a memory access var handle, clients must start from one of the <em>leaf</em> methods
|
||||
* (see {@link MemoryHandles#varHandle(Class, ByteOrder)},
|
||||
* {@link MemoryHandles#varHandle(Class, long, ByteOrder)}). This determines the variable type
|
||||
* (all primitive types but {@code void} and {@code boolean} are supported), as well as the alignment constraint and the
|
||||
* byte order associated to a memory access var handle. The resulting memory access var handle can then be combined in various ways
|
||||
* to emulate different addressing modes. The var handles created by this class feature a <em>mandatory</em> coordinate type
|
||||
* (of type {@link MemoryAddress}), and zero or more {@code long} coordinate types, which can be used to emulate
|
||||
* multi-dimensional array indexing.
|
||||
* <p>
|
||||
* As an example, consider the memory layout expressed by a {@link SequenceLayout} instance constructed as follows:
|
||||
* <blockquote><pre>{@code
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(5,
|
||||
MemoryLayout.ofStruct(
|
||||
MemoryLayout.ofPaddingBits(32),
|
||||
MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN).withName("value")
|
||||
));
|
||||
* }</pre></blockquote>
|
||||
* To access the member layout named {@code value}, we can construct a memory access var handle as follows:
|
||||
* <blockquote><pre>{@code
|
||||
VarHandle handle = MemoryHandles.varHandle(int.class, ByteOrder.BIG_ENDIAN); //(MemoryAddress) -> int
|
||||
handle = MemoryHandles.withOffset(handle, 4); //(MemoryAddress) -> int
|
||||
handle = MemoryHandles.withStride(handle, 8); //(MemoryAddress, long) -> int
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* <h2>Addressing mode</h2>
|
||||
*
|
||||
* The final memory location accessed by a memory access var handle can be computed as follows:
|
||||
*
|
||||
* <blockquote><pre>{@code
|
||||
address = base + offset
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* where {@code base} denotes the address expressed by the {@link MemoryAddress} access coordinate, and {@code offset}
|
||||
* can be expressed in the following form:
|
||||
*
|
||||
* <blockquote><pre>{@code
|
||||
offset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* where {@code x_1}, {@code x_2}, ... {@code x_n} are <em>dynamic</em> values provided as optional {@code long}
|
||||
* access coordinates, whereas {@code c_1}, {@code c_2}, ... {@code c_m} and {@code s_0}, {@code s_1}, ... {@code s_n} are
|
||||
* <em>static</em> constants which are can be acquired through the {@link MemoryHandles#withOffset(VarHandle, long)}
|
||||
* and the {@link MemoryHandles#withStride(VarHandle, long)} combinators, respectively.
|
||||
*
|
||||
* <h2><a id="memaccess-mode"></a>Alignment and access modes</h2>
|
||||
*
|
||||
* A memory access var handle is associated with an access size {@code S} and an alignment constraint {@code B}
|
||||
* (both expressed in bytes). We say that a memory access operation is <em>fully aligned</em> if it occurs
|
||||
* at a memory address {@code A} which is compatible with both alignment constraints {@code S} and {@code B}.
|
||||
* If access is fully aligned then following access modes are supported and are
|
||||
* guaranteed to support atomic access:
|
||||
* <ul>
|
||||
* <li>read write access modes for all {@code T}, with the exception of
|
||||
* access modes {@code get} and {@code set} for {@code long} and
|
||||
* {@code double} on 32-bit platforms.
|
||||
* <li>atomic update access modes for {@code int}, {@code long},
|
||||
* {@code float} or {@code double}.
|
||||
* (Future major platform releases of the JDK may support additional
|
||||
* types for certain currently unsupported access modes.)
|
||||
* <li>numeric atomic update access modes for {@code int} and {@code long}.
|
||||
* (Future major platform releases of the JDK may support additional
|
||||
* numeric types for certain currently unsupported access modes.)
|
||||
* <li>bitwise atomic update access modes for {@code int} and {@code long}.
|
||||
* (Future major platform releases of the JDK may support additional
|
||||
* numeric types for certain currently unsupported access modes.)
|
||||
* </ul>
|
||||
*
|
||||
* If {@code T} is {@code float} or {@code double} then atomic
|
||||
* update access modes compare values using their bitwise representation
|
||||
* (see {@link Float#floatToRawIntBits} and
|
||||
* {@link Double#doubleToRawLongBits}, respectively).
|
||||
* <p>
|
||||
* Alternatively, a memory access operation is <em>partially aligned</em> if it occurs at a memory address {@code A}
|
||||
* which is only compatible with the alignment constraint {@code B}; in such cases, access for anything other than the
|
||||
* {@code get} and {@code set} access modes will result in an {@code IllegalStateException}. If access is partially aligned,
|
||||
* atomic access is only guaranteed with respect to the largest power of two that divides the GCD of {@code A} and {@code S}.
|
||||
* <p>
|
||||
* Finally, in all other cases, we say that a memory access operation is <em>misaligned</em>; in such cases an
|
||||
* {@code IllegalStateException} is thrown, irrespective of the access mode being used.
|
||||
*/
|
||||
public final class MemoryHandles {
|
||||
|
||||
private final static JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess();
|
||||
|
||||
private MemoryHandles() {
|
||||
//sorry, just the one!
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a memory access var handle with the given carrier type and byte order.
|
||||
*
|
||||
* The resulting memory access var handle features a single {@link MemoryAddress} access coordinate,
|
||||
* and its variable type is set by the given carrier type.
|
||||
*
|
||||
* The alignment constraint for the resulting memory access var handle is the same as the in memory size of the
|
||||
* carrier type, and the accessed offset is set at zero.
|
||||
*
|
||||
* @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>,
|
||||
* which are common to all memory access var handles.
|
||||
*
|
||||
* @param carrier the carrier type. Valid carriers are {@code byte}, {@code short}, {@code char}, {@code int},
|
||||
* {@code float}, {@code long}, and {@code double}.
|
||||
* @param byteOrder the required byte order.
|
||||
* @return the new memory access var handle.
|
||||
* @throws IllegalArgumentException when an illegal carrier type is used
|
||||
*/
|
||||
public static VarHandle varHandle(Class<?> carrier, ByteOrder byteOrder) {
|
||||
checkCarrier(carrier);
|
||||
return varHandle(carrier,
|
||||
carrierSize(carrier),
|
||||
byteOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a memory access var handle with the given carrier type, alignment constraint, and byte order.
|
||||
*
|
||||
* The resulting memory access var handle features a single {@link MemoryAddress} access coordinate,
|
||||
* and its variable type is set by the given carrier type.
|
||||
*
|
||||
* The accessed offset is zero.
|
||||
*
|
||||
* @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>,
|
||||
* which are common to all memory access var handles.
|
||||
*
|
||||
* @param carrier the carrier type. Valid carriers are {@code byte}, {@code short}, {@code char}, {@code int},
|
||||
* {@code float}, {@code long}, and {@code double}.
|
||||
* @param alignmentBytes the alignment constraint (in bytes). Must be a power of two.
|
||||
* @param byteOrder the required byte order.
|
||||
* @return the new memory access var handle.
|
||||
* @throws IllegalArgumentException if an illegal carrier type is used, or if {@code alignmentBytes} is not a power of two.
|
||||
*/
|
||||
public static VarHandle varHandle(Class<?> carrier, long alignmentBytes, ByteOrder byteOrder) {
|
||||
checkCarrier(carrier);
|
||||
|
||||
if (alignmentBytes <= 0
|
||||
|| (alignmentBytes & (alignmentBytes - 1)) != 0) { // is power of 2?
|
||||
throw new IllegalArgumentException("Bad alignment: " + alignmentBytes);
|
||||
}
|
||||
|
||||
return JLI.memoryAddressViewVarHandle(carrier, alignmentBytes - 1, byteOrder, 0, new long[]{});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a memory access var handle with a fixed offset added to the accessed offset. That is,
|
||||
* if the target memory access var handle accesses a memory location at offset <em>O</em>, the new memory access var
|
||||
* handle will access a memory location at offset <em>O' + O</em>.
|
||||
*
|
||||
* The resulting memory access var handle will feature the same access coordinates as the ones in the target memory access var handle.
|
||||
*
|
||||
* @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>,
|
||||
* which are common to all memory access var handles.
|
||||
*
|
||||
* @param target the target memory access handle to access after the offset adjustment.
|
||||
* @param bytesOffset the offset, in bytes. Must be positive or zero.
|
||||
* @return the new memory access var handle.
|
||||
* @throws IllegalArgumentException when the target var handle is not a memory access var handle,
|
||||
* or when {@code bytesOffset < 0}, or otherwise incompatible with the alignment constraint.
|
||||
*/
|
||||
public static VarHandle withOffset(VarHandle target, long bytesOffset) {
|
||||
if (bytesOffset < 0) {
|
||||
throw new IllegalArgumentException("Illegal offset: " + bytesOffset);
|
||||
}
|
||||
|
||||
long alignMask = JLI.memoryAddressAlignmentMask(target);
|
||||
|
||||
if ((bytesOffset & alignMask) != 0) {
|
||||
throw new IllegalArgumentException("Offset " + bytesOffset + " does not conform to alignment " + (alignMask + 1));
|
||||
}
|
||||
|
||||
return JLI.memoryAddressViewVarHandle(
|
||||
JLI.memoryAddressCarrier(target),
|
||||
alignMask,
|
||||
JLI.memoryAddressByteOrder(target),
|
||||
JLI.memoryAddressOffset(target) + bytesOffset,
|
||||
JLI.memoryAddressStrides(target));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a memory access var handle with a <em>variable</em> offset added to the accessed offset.
|
||||
* That is, if the target memory access var handle accesses a memory location at offset <em>O</em>,
|
||||
* the new memory access var handle will access a memory location at offset <em>(S * X) + O</em>, where <em>S</em>
|
||||
* is a constant <em>stride</em>, whereas <em>X</em> is a dynamic value that will be provided as an additional access
|
||||
* coordinate (of type {@code long}). The new access coordinate will be <em>prepended</em> to the ones available
|
||||
* in the target memory access var handles (if any).
|
||||
*
|
||||
* @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>,
|
||||
* which are common to all memory access var handles.
|
||||
*
|
||||
* @param target the target memory access handle to access after the scale adjustment.
|
||||
* @param bytesStride the stride, in bytes, by which to multiply the coordinate value. Must be greater than zero.
|
||||
* @return the new memory access var handle.
|
||||
* @throws IllegalArgumentException when the target var handle is not a memory access var handle,
|
||||
* or if {@code bytesStride <= 0}, or otherwise incompatible with the alignment constraint.
|
||||
*/
|
||||
public static VarHandle withStride(VarHandle target, long bytesStride) {
|
||||
if (bytesStride == 0) {
|
||||
throw new IllegalArgumentException("Stride must be positive: " + bytesStride);
|
||||
}
|
||||
|
||||
long alignMask = JLI.memoryAddressAlignmentMask(target);
|
||||
|
||||
if ((bytesStride & alignMask) != 0) {
|
||||
throw new IllegalArgumentException("Stride " + bytesStride + " does not conform to alignment " + (alignMask + 1));
|
||||
}
|
||||
|
||||
long offset = JLI.memoryAddressOffset(target);
|
||||
|
||||
long[] strides = JLI.memoryAddressStrides(target);
|
||||
long[] newStrides = new long[strides.length + 1];
|
||||
System.arraycopy(strides, 0, newStrides, 1, strides.length);
|
||||
newStrides[0] = bytesStride;
|
||||
|
||||
return JLI.memoryAddressViewVarHandle(
|
||||
JLI.memoryAddressCarrier(target),
|
||||
alignMask,
|
||||
JLI.memoryAddressByteOrder(target),
|
||||
offset,
|
||||
newStrides);
|
||||
}
|
||||
|
||||
private static void checkCarrier(Class<?> carrier) {
|
||||
if (!carrier.isPrimitive() || carrier == void.class || carrier == boolean.class) {
|
||||
throw new IllegalArgumentException("Illegal carrier: " + carrier.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
private static long carrierSize(Class<?> carrier) {
|
||||
long bitsAlignment = Math.max(8, Wrapper.forPrimitiveType(carrier).bitWidth());
|
||||
return Utils.bitsToBytesOrThrow(bitsAlignment, IllegalStateException::new);
|
||||
}
|
||||
}
|
@ -0,0 +1,445 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
package jdk.incubator.foreign;
|
||||
|
||||
import jdk.internal.foreign.LayoutPath;
|
||||
import jdk.internal.foreign.Utils;
|
||||
|
||||
import java.lang.constant.Constable;
|
||||
import java.lang.constant.DynamicConstantDesc;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
|
||||
/**
|
||||
* A memory layout can be used to describe the contents of a memory segment in a <em>language neutral</em> fashion.
|
||||
* There are two leaves in the layout hierarchy, <em>value layouts</em>, which are used to represent values of given size and kind (see
|
||||
* {@link ValueLayout}) and <em>padding layouts</em> which are used, as the name suggests, to represent a portion of a memory
|
||||
* segment whose contents should be ignored, and which are primarily present for alignment reasons (see {@link MemoryLayout#ofPaddingBits(long)}).
|
||||
* Some common value layout constants are defined in the {@link MemoryLayouts} class.
|
||||
* <p>
|
||||
* More complex layouts can be derived from simpler ones: a <em>sequence layout</em> denotes a repetition of one or more
|
||||
* element layout (see {@link SequenceLayout}); a <em>group layout</em> denotes an aggregation of (typically) heterogeneous
|
||||
* member layouts (see {@link GroupLayout}).
|
||||
* <p>
|
||||
* All implementations of this interface must be <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>;
|
||||
* use of identity-sensitive operations (including reference equality ({@code ==}), identity hash code, or synchronization) on
|
||||
* instances of {@code MemoryLayout} may have unpredictable results and should be avoided. The {@code equals} method should
|
||||
* be used for comparisons.
|
||||
* <p>
|
||||
* Non-platform classes should not implement {@linkplain MemoryLayout} directly.
|
||||
*
|
||||
* <h2>Size, alignment and byte order</h2>
|
||||
*
|
||||
* All layouts have a size; layout size for value and padding layouts is always explicitly denoted; this means that a layout description
|
||||
* always has the same size in bits, regardless of the platform in which it is used. For derived layouts, the size is computed
|
||||
* as follows:
|
||||
* <ul>
|
||||
* <li>for a <em>finite</em> sequence layout <em>S</em> whose element layout is <em>E</em> and size is L,
|
||||
* the size of <em>S</em> is that of <em>E, multiplied by L</em></li>
|
||||
* <li>the size of an <em>unbounded</em> sequence layout is <em>unknown</em></li>
|
||||
* <li>for a group layout <em>G</em> with member layouts <em>M1</em>, <em>M2</em>, ... <em>Mn</em> whose sizes are
|
||||
* <em>S1</em>, <em>S2</em>, ... <em>Sn</em>, respectively, the size of <em>G</em> is either <em>S1 + S2 + ... + Sn</em> or
|
||||
* <em>max(S1, S2, ... Sn)</em> depending on whether the group is a <em>struct</em> or an <em>union</em>, respectively</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Furthermore, all layouts feature a <em>natural alignment</em> which can be inferred as follows:
|
||||
* <ul>
|
||||
* <li>for value and padding layout <em>L</em> whose size is <em>N</em>, the natural alignment of <em>L</em> is <em>N</em></li>
|
||||
* <li>for a sequence layout <em>S</em> whose element layout is <em>E</em>, the natural alignment of <em>S</em> is that of <em>E</em></li>
|
||||
* <li>for a group layout <em>G</em> with member layouts <em>M1</em>, <em>M2</em>, ... <em>Mn</em> whose alignments are
|
||||
* <em>A1</em>, <em>A2</em>, ... <em>An</em>, respectively, the natural alignment of <em>G</em> is <em>max(A1, A2 ... An)</em></li>
|
||||
* </ul>
|
||||
* A layout's natural alignment can be overridden if needed (see {@link MemoryLayout#withBitAlignment(long)}), which can be useful to describe
|
||||
* hyper-aligned layouts.
|
||||
* <p>
|
||||
* All value layouts have an <em>explicit</em> byte order (see {@link java.nio.ByteOrder}) which is set when the layout is created.
|
||||
*
|
||||
* <h2><a id = "layout-paths">Layout paths</a></h2>
|
||||
*
|
||||
* A <em>layout path</em> originates from a <em>root</em> layout (typically a group or a sequence layout) and terminates
|
||||
* at a layout nested within the root layout - this is the layout <em>selected</em> by the layout path.
|
||||
* Layout paths are typically expressed as a sequence of one or more {@link PathElement} instances.
|
||||
* <p>
|
||||
* Layout paths are useful in order to e.g. to obtain offset of leaf elements inside arbitrarily nested layouts
|
||||
* (see {@link MemoryLayout#offset(PathElement...)}), or to quickly obtain a memory access handle corresponding to the selected
|
||||
* layout (see {@link MemoryLayout#varHandle(Class, PathElement...)}).
|
||||
* <p>
|
||||
* Such <em>layout paths</em> can be constructed programmatically using the methods in this class.
|
||||
* For instance, given a layout constructed as follows:
|
||||
* <blockquote><pre>{@code
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(5,
|
||||
MemoryLayout.ofStruct(
|
||||
MemoryLayout.ofPaddingBits(32),
|
||||
MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN).withName("value")
|
||||
));
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* We can obtain the offset of the member layout named <code>value</code> from <code>seq</code>, as follows:
|
||||
* <blockquote><pre>{@code
|
||||
long valueOffset = seq.offset(PathElement.sequenceElement(), PathElement.groupElement("value"));
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* Layout paths can feature one or more <em>free dimensions</em>. For instance, a layout path traversing
|
||||
* an unspecified sequence element (that is, where one of the path component was obtained with the
|
||||
* {@link PathElement#sequenceElement()} method) features an additional free dimension, which will have to be bound at runtime;
|
||||
* that is, the memory access var handle associated with such a layout path expression will feature an extra {@code long}
|
||||
* access coordinate. The layout path constructed in the above example features exactly one free dimension.
|
||||
*
|
||||
* @apiNote In the future, if the Java language permits, {@link MemoryLayout}
|
||||
* may become a {@code sealed} interface, which would prohibit subclassing except by
|
||||
* explicitly permitted types.
|
||||
*
|
||||
* @implSpec
|
||||
* Implementations of this class are immutable and thread-safe.
|
||||
*/
|
||||
public interface MemoryLayout extends Constable {
|
||||
|
||||
/**
|
||||
* Returns an {@link Optional} containing the nominal descriptor for this
|
||||
* layout, if one can be constructed, or an empty {@link Optional}
|
||||
* if one cannot be constructed.
|
||||
*
|
||||
* @return An {@link Optional} containing the resulting nominal descriptor,
|
||||
* or an empty {@link Optional} if one cannot be constructed.
|
||||
*/
|
||||
@Override
|
||||
Optional<? extends DynamicConstantDesc<? extends MemoryLayout>> describeConstable();
|
||||
|
||||
/**
|
||||
* Computes the layout size, in bits.
|
||||
*
|
||||
* @return the layout size, in bits.
|
||||
* @throws UnsupportedOperationException if the layout is, or contains, a sequence layout with unspecified size (see {@link SequenceLayout}).
|
||||
*/
|
||||
long bitSize();
|
||||
|
||||
/**
|
||||
* Computes the layout size, in bytes.
|
||||
*
|
||||
* @return the layout size, in bytes.
|
||||
* @throws UnsupportedOperationException if the layout is, or contains, a sequence layout with unspecified size (see {@link SequenceLayout}),
|
||||
* or if {@code bitSize()} is not a multiple of 8.
|
||||
*/
|
||||
default long byteSize() {
|
||||
return Utils.bitsToBytesOrThrow(bitSize(),
|
||||
() -> new UnsupportedOperationException("Cannot compute byte size; bit size is not a multiple of 8"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the <em>name</em> (if any) associated with this layout.
|
||||
*
|
||||
* @return the layout <em>name</em> (if any).
|
||||
* @see MemoryLayout#withName(String)
|
||||
*/
|
||||
Optional<String> name();
|
||||
|
||||
/**
|
||||
* Creates a new layout which features the desired layout <em>name</em>.
|
||||
*
|
||||
* @param name the layout name.
|
||||
* @return a new layout which is the same as this layout, except for the <em>name</em> associated to it.
|
||||
* @see MemoryLayout#name()
|
||||
*/
|
||||
MemoryLayout withName(String name);
|
||||
|
||||
/**
|
||||
* Returns the alignment constraint associated with this layout, expressed in bits. Layout alignment defines a power
|
||||
* of two {@code A} which is the bit-wise alignment of the layout. If {@code A <= 8} then {@code A/8} is the number of
|
||||
* bytes that must be aligned for any pointer that correctly points to this layout. Thus:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code A=8} means unaligned (in the usual sense), which is common in packets.</li>
|
||||
* <li>{@code A=64} means word aligned (on LP64), {@code A=32} int aligned, {@code A=16} short aligned, etc.</li>
|
||||
* <li>{@code A=512} is the most strict alignment required by the x86/SV ABI (for AVX-512 data).</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return the layout alignment constraint, in bits.
|
||||
*/
|
||||
long bitAlignment();
|
||||
|
||||
/**
|
||||
* Returns the alignment constraint associated with this layout, expressed in bytes. Layout alignment defines a power
|
||||
* of two {@code A} which is the byte-wise alignment of the layout, where {@code A} is the number of bytes that must be aligned
|
||||
* for any pointer that correctly points to this layout. Thus:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code A=1} means unaligned (in the usual sense), which is common in packets.</li>
|
||||
* <li>{@code A=8} means word aligned (on LP64), {@code A=4} int aligned, {@code A=2} short aligned, etc.</li>
|
||||
* <li>{@code A=64} is the most strict alignment required by the x86/SV ABI (for AVX-512 data).</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return the layout alignment constraint, in bytes.
|
||||
* @throws UnsupportedOperationException if {@code bitAlignment()} is not a multiple of 8.
|
||||
*/
|
||||
default long byteAlignment() {
|
||||
return Utils.bitsToBytesOrThrow(bitAlignment(),
|
||||
() -> new UnsupportedOperationException("Cannot compute byte alignment; bit alignment is not a multiple of 8"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new layout which features the desired alignment constraint.
|
||||
*
|
||||
* @param bitAlignment the layout alignment constraint, expressed in bits.
|
||||
* @return a new layout which is the same as this layout, except for the alignment constraint associated to it.
|
||||
* @throws IllegalArgumentException if {@code bitAlignment} is not a power of two, or if it's less than than 8.
|
||||
*/
|
||||
MemoryLayout withBitAlignment(long bitAlignment);
|
||||
|
||||
/**
|
||||
* Computes the offset of the layout selected by a given layout path, where the path is considered rooted in this
|
||||
* layout.
|
||||
*
|
||||
* @apiNote if the layout path has one (or more) free dimensions,
|
||||
* the offset is computed as if all the indices corresponding to such dimensions were set to {@code 0}.
|
||||
*
|
||||
* @param elements the layout path elements.
|
||||
* @return The offset of layout selected by a the layout path obtained by concatenating the path elements in {@code elements}.
|
||||
* @throws IllegalArgumentException if the layout path obtained by concatenating the path elements in {@code elements}
|
||||
* does not select a valid layout element.
|
||||
*/
|
||||
default long offset(PathElement... elements) {
|
||||
LayoutPath path = LayoutPath.rootPath(this);
|
||||
for (PathElement e : elements) {
|
||||
path = ((LayoutPath.PathElementImpl)e).apply(path);
|
||||
}
|
||||
return path.offset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a memory access var handle that can be used to dereference memory at the layout selected by a given layout path,
|
||||
* where the path is considered rooted in this layout.
|
||||
*
|
||||
* @apiNote the resulting var handle will feature an additional {@code long} access coordinate for every
|
||||
* unspecified sequence access component contained in this layout path. Moreover, the resulting var handle
|
||||
* features certain <a href="MemoryHandles.html#memaccess-mode">access mode restrictions</a>, which are common to all memory access var handles.
|
||||
*
|
||||
* @param carrier the var handle carrier type.
|
||||
* @param elements the layout path elements.
|
||||
* @return a var handle which can be used to dereference memory at the layout denoted by given layout path.
|
||||
* @throws UnsupportedOperationException if the layout path has one or more elements with incompatible alignment constraints.
|
||||
* @throws IllegalArgumentException if the carrier does not represent a primitive type, if the carrier is {@code void},
|
||||
* {@code boolean}, or if the layout path obtained by concatenating the path elements in {@code elements}
|
||||
* does not select a value layout (see {@link ValueLayout}), or if the selected value layout has a size that
|
||||
* that does not match that of the specified carrier type.
|
||||
*/
|
||||
default VarHandle varHandle(Class<?> carrier, PathElement... elements) {
|
||||
LayoutPath path = LayoutPath.rootPath(this);
|
||||
for (PathElement e : elements) {
|
||||
path = ((LayoutPath.PathElementImpl)e).apply(path);
|
||||
}
|
||||
return path.dereferenceHandle(carrier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instances of this class are used to form <a href="MemoryLayout.html#layout-paths"><em>layout paths</em></a>. There
|
||||
* are two kinds of path elements: <em>group path elements</em> and <em>sequence path elements</em>. Group
|
||||
* path elements are used to select a given named member layout within a {@link GroupLayout}. Sequence
|
||||
* path elements are used to select a sequence element layout within a {@link SequenceLayout}; selection
|
||||
* of sequence element layout can be <em>explicit</em> (see {@link PathElement#sequenceElement(long)}) or
|
||||
* <em>implicit</em> (see {@link PathElement#sequenceElement()}). When a path uses one or more implicit
|
||||
* sequence path elements, it acquires additional <em>free dimensions</em>.
|
||||
* <p>
|
||||
* Non-platform classes should not implement {@linkplain PathElement} directly.
|
||||
*
|
||||
* @apiNote In the future, if the Java language permits, {@link PathElement}
|
||||
* may become a {@code sealed} interface, which would prohibit subclassing except by
|
||||
* explicitly permitted types.
|
||||
*
|
||||
* @implSpec
|
||||
* Implementations of this interface are immutable and thread-safe.
|
||||
*/
|
||||
interface PathElement {
|
||||
|
||||
/**
|
||||
* Returns a path element which selects a member layout with given name from a given group layout.
|
||||
* The path element returned by this method does not alter the number of free dimensions of any path
|
||||
* that is combined with such element.
|
||||
*
|
||||
* @implSpec in case multiple group elements with a matching name exist, the path element returned by this
|
||||
* method will select the first one; that is, the group element with lowest offset from current path is selected.
|
||||
*
|
||||
* @param name the name of the group element to be selected.
|
||||
* @return a path element which selects the group element with given name.
|
||||
* @throws NullPointerException if the specified group element name is {@code null}.
|
||||
*/
|
||||
static PathElement groupElement(String name) {
|
||||
Objects.requireNonNull(name);
|
||||
return new LayoutPath.PathElementImpl(path -> path.groupElement(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a path element which selects the element layout at the specified position in a given the sequence layout.
|
||||
* The path element returned by this method does not alter the number of free dimensions of any path
|
||||
* that is combined with such element.
|
||||
*
|
||||
* @param index the index of the sequence element to be selected.
|
||||
* @return a path element which selects the sequence element layout with given index.
|
||||
* @throws IllegalArgumentException if {@code index < 0}.
|
||||
*/
|
||||
static PathElement sequenceElement(long index) {
|
||||
if (index < 0) {
|
||||
throw new IllegalArgumentException("Index must be positive: " + index);
|
||||
}
|
||||
return new LayoutPath.PathElementImpl(path -> path.sequenceElement(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a path element which selects the element layout in a <em>range</em> of positions in a given the sequence layout,
|
||||
* where the range is expressed as a pair of starting index (inclusive) {@code S} and step factor (which can also be negative)
|
||||
* {@code F}.
|
||||
* If a path with free dimensions {@code n} is combined with the path element returned by this method,
|
||||
* the number of free dimensions of the resulting path will be {@code 1 + n}. If the free dimension associated
|
||||
* with this path is bound by an index {@code I}, the resulting accessed offset can be obtained with the following
|
||||
* formula:
|
||||
* <blockquote><pre>{@code
|
||||
E * (S + I * F)
|
||||
* }</pre></blockquote>
|
||||
* where {@code E} is the size (in bytes) of the sequence element layout.
|
||||
*
|
||||
* @param start the index of the first sequence element to be selected.
|
||||
* @param step the step factor at which subsequence sequence elements are to be selected.
|
||||
* @return a path element which selects the sequence element layout with given index.
|
||||
* @throws IllegalArgumentException if {@code start < 0}, or {@code step == 0}.
|
||||
*/
|
||||
static PathElement sequenceElement(long start, long step) {
|
||||
if (start < 0) {
|
||||
throw new IllegalArgumentException("Start index must be positive: " + start);
|
||||
}
|
||||
if (step == 0) {
|
||||
throw new IllegalArgumentException("Step must be != 0: " + step);
|
||||
}
|
||||
return new LayoutPath.PathElementImpl(path -> path.sequenceElement(start, step));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a path element which selects an unspecified element layout from a given sequence layout.
|
||||
* If a path with free dimensions {@code n} is combined with the path element returned by this method,
|
||||
* the number of free dimensions of the resulting path will be {@code 1 + n}.
|
||||
*
|
||||
* @return a path element which selects an unspecified sequence element layout.
|
||||
*/
|
||||
static PathElement sequenceElement() {
|
||||
return new LayoutPath.PathElementImpl(LayoutPath::sequenceElement);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the specified object with this layout for equality. Returns {@code true} if and only if the specified
|
||||
* object is also a layout, and it is equal to this layout.
|
||||
*
|
||||
* @param that the object to be compared for equality with this layout.
|
||||
* @return {@code true} if the specified object is equal to this layout.
|
||||
*/
|
||||
boolean equals(Object that);
|
||||
|
||||
/**
|
||||
* Returns the hash code value for this layout.
|
||||
*
|
||||
* @return the hash code value for this layout.
|
||||
*/
|
||||
int hashCode();
|
||||
|
||||
/**
|
||||
* Returns a string representation of this layout.
|
||||
*
|
||||
* @return a string representation of this layout.
|
||||
*/
|
||||
@Override
|
||||
String toString();
|
||||
|
||||
/**
|
||||
* Create a new padding layout with given size.
|
||||
*
|
||||
* @param size the padding size in bits.
|
||||
* @return the new selector layout.
|
||||
* @throws IllegalArgumentException if {@code size <= 0}.
|
||||
*/
|
||||
static MemoryLayout ofPaddingBits(long size) {
|
||||
AbstractLayout.checkSize(size);
|
||||
return new PaddingLayout(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a value layout of given byte order and size.
|
||||
*
|
||||
* @param size the value layout size.
|
||||
* @param order the value layout's byte order.
|
||||
* @return a new value layout.
|
||||
* @throws IllegalArgumentException if {@code size <= 0}.
|
||||
*/
|
||||
static ValueLayout ofValueBits(long size, ByteOrder order) {
|
||||
AbstractLayout.checkSize(size);
|
||||
return new ValueLayout(order, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new sequence layout with given element layout and element count.
|
||||
*
|
||||
* @param elementCount the sequence element count.
|
||||
* @param elementLayout the sequence element layout.
|
||||
* @return the new sequence layout with given element layout and size.
|
||||
* @throws IllegalArgumentException if {@code elementCount < 0}.
|
||||
*/
|
||||
static SequenceLayout ofSequence(long elementCount, MemoryLayout elementLayout) {
|
||||
AbstractLayout.checkSize(elementCount, true);
|
||||
OptionalLong size = OptionalLong.of(elementCount);
|
||||
return new SequenceLayout(size, elementLayout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new sequence layout, with unbounded element count and given element layout.
|
||||
*
|
||||
* @param elementLayout the element layout of the sequence layout.
|
||||
* @return the new sequence layout with given element layout.
|
||||
*/
|
||||
static SequenceLayout ofSequence(MemoryLayout elementLayout) {
|
||||
return new SequenceLayout(OptionalLong.empty(), elementLayout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new <em>struct</em> group layout with given member layouts.
|
||||
*
|
||||
* @param elements The member layouts of the <em>struct</em> group layout.
|
||||
* @return a new <em>struct</em> group layout with given member layouts.
|
||||
*/
|
||||
static GroupLayout ofStruct(MemoryLayout... elements) {
|
||||
return new GroupLayout(GroupLayout.Kind.STRUCT, List.of(elements));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new <em>union</em> group layout with given member layouts.
|
||||
*
|
||||
* @param elements The member layouts of the <em>union</em> layout.
|
||||
* @return a new <em>union</em> group layout with given member layouts.
|
||||
*/
|
||||
static GroupLayout ofUnion(MemoryLayout... elements) {
|
||||
return new GroupLayout(GroupLayout.Kind.UNION, List.of(elements));
|
||||
}
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
package jdk.incubator.foreign;
|
||||
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* This class defines useful layout constants. Some of the constants defined in this class are explicit in both
|
||||
* size and byte order (see {@link #BITS_64_BE}), and can therefore be used to explicitly and unambiguously specify the
|
||||
* contents of a memory segment. Other constants make implicit byte order assumptions (see
|
||||
* {@link #JAVA_INT}); as such, these constants make it easy to interoperate with other serialization-centric APIs,
|
||||
* such as {@link java.nio.ByteBuffer}.
|
||||
*/
|
||||
public final class MemoryLayouts {
|
||||
|
||||
private MemoryLayouts() {
|
||||
//just the one, please
|
||||
}
|
||||
|
||||
/**
|
||||
* A value layout constant with size of one byte, and byte order set to {@link ByteOrder#LITTLE_ENDIAN}.
|
||||
*/
|
||||
public static final ValueLayout BITS_8_LE = MemoryLayout.ofValueBits(8, ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
/**
|
||||
* A value layout constant with size of two bytes, and byte order set to {@link ByteOrder#LITTLE_ENDIAN}.
|
||||
*/
|
||||
public static final ValueLayout BITS_16_LE = MemoryLayout.ofValueBits(16, ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
/**
|
||||
* A value layout constant with size of four bytes, and byte order set to {@link ByteOrder#LITTLE_ENDIAN}.
|
||||
*/
|
||||
public static final ValueLayout BITS_32_LE = MemoryLayout.ofValueBits(32, ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
/**
|
||||
* A value layout constant with size of eight bytes, and byte order set to {@link ByteOrder#LITTLE_ENDIAN}.
|
||||
*/
|
||||
public static final ValueLayout BITS_64_LE = MemoryLayout.ofValueBits(64, ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
/**
|
||||
* A value layout constant with size of one byte, and byte order set to {@link ByteOrder#BIG_ENDIAN}.
|
||||
*/
|
||||
public static final ValueLayout BITS_8_BE = MemoryLayout.ofValueBits(8, ByteOrder.BIG_ENDIAN);
|
||||
|
||||
/**
|
||||
* A value layout constant with size of two bytes, and byte order set to {@link ByteOrder#BIG_ENDIAN}.
|
||||
*/
|
||||
public static final ValueLayout BITS_16_BE = MemoryLayout.ofValueBits(16, ByteOrder.BIG_ENDIAN);
|
||||
|
||||
/**
|
||||
* A value layout constant with size of four bytes, and byte order set to {@link ByteOrder#BIG_ENDIAN}.
|
||||
*/
|
||||
public static final ValueLayout BITS_32_BE = MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN);
|
||||
|
||||
/**
|
||||
* A value layout constant with size of eight bytes, and byte order set to {@link ByteOrder#BIG_ENDIAN}.
|
||||
*/
|
||||
public static final ValueLayout BITS_64_BE = MemoryLayout.ofValueBits(64, ByteOrder.BIG_ENDIAN);
|
||||
|
||||
/**
|
||||
* A padding layout constant with size of one byte.
|
||||
*/
|
||||
public static final MemoryLayout PAD_8 = MemoryLayout.ofPaddingBits(8);
|
||||
|
||||
/**
|
||||
* A padding layout constant with size of two bytes.
|
||||
*/
|
||||
public static final MemoryLayout PAD_16 = MemoryLayout.ofPaddingBits(16);
|
||||
|
||||
/**
|
||||
* A padding layout constant with size of four bytes.
|
||||
*/
|
||||
public static final MemoryLayout PAD_32 = MemoryLayout.ofPaddingBits(32);
|
||||
|
||||
/**
|
||||
* A padding layout constant with size of eight bytes.
|
||||
*/
|
||||
public static final MemoryLayout PAD_64 = MemoryLayout.ofPaddingBits(64);
|
||||
|
||||
/**
|
||||
* A value layout constant whose size is the same as that of a Java {@code byte}, and byte order set to {@link ByteOrder#nativeOrder()}.
|
||||
*/
|
||||
public static final ValueLayout JAVA_BYTE = MemoryLayout.ofValueBits(8, ByteOrder.nativeOrder());
|
||||
|
||||
/**
|
||||
* A value layout constant whose size is the same as that of a Java {@code char}, and byte order set to {@link ByteOrder#nativeOrder()}.
|
||||
*/
|
||||
public static final ValueLayout JAVA_CHAR = MemoryLayout.ofValueBits(16, ByteOrder.nativeOrder());
|
||||
|
||||
/**
|
||||
* A value layout constant whose size is the same as that of a Java {@code short}, and byte order set to {@link ByteOrder#nativeOrder()}.
|
||||
*/
|
||||
public static final ValueLayout JAVA_SHORT = MemoryLayout.ofValueBits(16, ByteOrder.nativeOrder());
|
||||
|
||||
/**
|
||||
* A value layout constant whose size is the same as that of a Java {@code int}, and byte order set to {@link ByteOrder#nativeOrder()}.
|
||||
*/
|
||||
public static final ValueLayout JAVA_INT = MemoryLayout.ofValueBits(32, ByteOrder.nativeOrder());
|
||||
|
||||
/**
|
||||
* A value layout constant whose size is the same as that of a Java {@code long}, and byte order set to {@link ByteOrder#nativeOrder()}.
|
||||
*/
|
||||
public static final ValueLayout JAVA_LONG = MemoryLayout.ofValueBits(64, ByteOrder.nativeOrder());
|
||||
|
||||
/**
|
||||
* A value layout constant whose size is the same as that of a Java {@code float}, and byte order set to {@link ByteOrder#nativeOrder()}.
|
||||
*/
|
||||
public static final ValueLayout JAVA_FLOAT = MemoryLayout.ofValueBits(32, ByteOrder.nativeOrder());
|
||||
|
||||
/**
|
||||
* A value layout constant whose size is the same as that of a Java {@code double}, and byte order set to {@link ByteOrder#nativeOrder()}.
|
||||
*/
|
||||
public static final ValueLayout JAVA_DOUBLE = MemoryLayout.ofValueBits(64, ByteOrder.nativeOrder());
|
||||
}
|
@ -0,0 +1,430 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
package jdk.incubator.foreign;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import jdk.internal.foreign.Utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* A memory segment models a contiguous region of memory. A memory segment is associated with both spatial
|
||||
* and temporal bounds. Spatial bounds ensure that memory access operations on a memory segment cannot affect a memory location
|
||||
* which falls <em>outside</em> the boundaries of the memory segment being accessed. Temporal checks ensure that memory access
|
||||
* operations on a segment cannot occur after a memory segment has been closed (see {@link MemorySegment#close()}).
|
||||
* <p>
|
||||
* All implementations of this interface must be <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>;
|
||||
* use of identity-sensitive operations (including reference equality ({@code ==}), identity hash code, or synchronization) on
|
||||
* instances of {@code MemorySegment} may have unpredictable results and should be avoided. The {@code equals} method should
|
||||
* be used for comparisons.
|
||||
* <p>
|
||||
* Non-platform classes should not implement {@linkplain MemorySegment} directly.
|
||||
*
|
||||
* <h2>Constructing memory segments from different sources</h2>
|
||||
*
|
||||
* There are multiple ways to obtain a memory segment. First, memory segments backed by off-heap memory can
|
||||
* be allocated using one of the many factory methods provided (see {@link MemorySegment#allocateNative(MemoryLayout)},
|
||||
* {@link MemorySegment#allocateNative(long)} and {@link MemorySegment#allocateNative(long, long)}). Memory segments obtained
|
||||
* in this way are called <em>native memory segments</em>.
|
||||
* <p>
|
||||
* It is also possible to obtain a memory segment backed by an existing heap-allocated Java array,
|
||||
* using one of the provided factory methods (e.g. {@link MemorySegment#ofArray(int[])}). Memory segments obtained
|
||||
* in this way are called <em>array memory segments</em>.
|
||||
* <p>
|
||||
* It is possible to obtain a memory segment backed by an existing Java byte buffer (see {@link ByteBuffer}),
|
||||
* using the factory method {@link MemorySegment#ofByteBuffer(ByteBuffer)}.
|
||||
* Memory segments obtained in this way are called <em>buffer memory segments</em>. Note that buffer memory segments might
|
||||
* be backed by native memory (as in the case of native memory segments) or heap memory (as in the case of array memory segments),
|
||||
* depending on the characteristics of the byte buffer instance the segment is associated with. For instance, a buffer memory
|
||||
* segment obtained from a byte buffer created with the {@link ByteBuffer#allocateDirect(int)} method will be backed
|
||||
* by native memory.
|
||||
* <p>
|
||||
* Finally, it is also possible to obtain a memory segment backed by a memory-mapped file using the factory method
|
||||
* {@link MemorySegment#mapFromPath(Path, long, FileChannel.MapMode)}. Such memory segments are called <em>mapped memory segments</em>.
|
||||
*
|
||||
* <h2>Closing a memory segment</h2>
|
||||
*
|
||||
* Memory segments are closed explicitly (see {@link MemorySegment#close()}). In general when a segment is closed, all off-heap
|
||||
* resources associated with it are released; this has different meanings depending on the kind of memory segment being
|
||||
* considered:
|
||||
* <ul>
|
||||
* <li>closing a native memory segment results in <em>freeing</em> the native memory associated with it</li>
|
||||
* <li>closing a mapped memory segment results in the backing memory-mapped file to be unmapped</li>
|
||||
* <li>closing an acquired memory segment <b>does not</b> result in the release of resources
|
||||
* (see the section on <a href="#thread-confinement">thread confinement</a> for more details)</li>
|
||||
* <li>closing a buffer, or a heap segment does not have any side-effect, other than marking the segment
|
||||
* as <em>not alive</em> (see {@link MemorySegment#isAlive()}). Also, since the buffer and heap segments might keep
|
||||
* strong references to the original buffer or array instance, it is the responsibility of clients to ensure that
|
||||
* these segments are discarded in a timely manner, so as not to prevent garbage collection to reclaim the underlying
|
||||
* objects.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2><a id = "thread-confinement">Thread confinement</a></h2>
|
||||
*
|
||||
* Memory segments support strong thread-confinement guarantees. Upon creation, they are assigned an <em>owner thread</em>,
|
||||
* typically the thread which initiated the creation operation. After creation, only the owner thread will be allowed
|
||||
* to directly manipulate the memory segment (e.g. close the memory segment) or access the underlying memory associated with
|
||||
* the segment using a memory access var handle. Any attempt to perform such operations from a thread other than the
|
||||
* owner thread will result in a runtime failure.
|
||||
* <p>
|
||||
* If a memory segment S owned by thread A needs to be used by thread B, B needs to explicitly <em>acquire</em> S,
|
||||
* which will create a so called <em>acquired</em> memory segment owned by B (see {@link #acquire()}) backed by the same resources
|
||||
* as S. A memory segment can be acquired multiple times by one or more threads; in that case, a memory segment S cannot
|
||||
* be closed until all the acquired memory segments derived from S have been closed. Furthermore, closing an acquired
|
||||
* memory segment does <em>not</em> trigger any deallocation action. It is therefore important that clients remember to
|
||||
* explicitly close the original segment from which the acquired memory segments have been obtained in order to truly
|
||||
* ensure that off-heap resources associated with the memory segment are released.
|
||||
*
|
||||
* <h2>Memory segment views</h2>
|
||||
*
|
||||
* Memory segments support <em>views</em>. It is possible to create an <em>immutable</em> view of a memory segment
|
||||
* (see {@link MemorySegment#asReadOnly()}) which does not support write operations. It is also possible to create views
|
||||
* whose spatial bounds are stricter than the ones of the original segment (see {@link MemorySegment#asSlice(long, long)}).
|
||||
* <p>
|
||||
* Temporal bounds of the original segment are inherited by the view; that is, closing a segment view, such as a sliced
|
||||
* view, will cause the original segment to be closed; as such special care must be taken when sharing views
|
||||
* between multiple clients. If a client want to protect itself against early closure of a segment by
|
||||
* another actor, it is the responsibility of that client to take protective measures, such as calling
|
||||
* {@link MemorySegment#acquire()} before sharing the view with another client.
|
||||
* <p>
|
||||
* To allow for interoperability with existing code, a byte buffer view can be obtained from a memory segment
|
||||
* (see {@link #asByteBuffer()}). This can be useful, for instance, for those clients that want to keep using the
|
||||
* {@link ByteBuffer} API, but need to operate on large memory segments. Byte buffers obtained in such a way support
|
||||
* the same spatial and temporal access restrictions associated to the memory address from which they originated.
|
||||
*
|
||||
* @apiNote In the future, if the Java language permits, {@link MemorySegment}
|
||||
* may become a {@code sealed} interface, which would prohibit subclassing except by
|
||||
* explicitly permitted types.
|
||||
*
|
||||
* @implSpec
|
||||
* Implementations of this interface are immutable and thread-safe.
|
||||
*/
|
||||
public interface MemorySegment extends AutoCloseable {
|
||||
|
||||
/**
|
||||
* The base memory address associated with this memory segment.
|
||||
* @return The base memory address.
|
||||
*/
|
||||
MemoryAddress baseAddress();
|
||||
|
||||
/**
|
||||
* Obtains an <a href="#thread-confinement">acquired</a> memory segment which can be used to access memory associated
|
||||
* with this segment from the current thread. As a side-effect, this segment cannot be closed until the acquired
|
||||
* view has been closed too (see {@link #close()}).
|
||||
* @return an <a href="#thread-confinement">acquired</a> memory segment which can be used to access memory associated
|
||||
* with this segment from the current thread.
|
||||
* @throws IllegalStateException if this segment has been closed.
|
||||
*/
|
||||
MemorySegment acquire();
|
||||
|
||||
/**
|
||||
* Is this segment accessible from the current thread?
|
||||
* @return true, if this segment is accessible from the current thread.
|
||||
*/
|
||||
boolean isAccessible();
|
||||
|
||||
/**
|
||||
* The size (in bytes) of this memory segment.
|
||||
* @return The size (in bytes) of this memory segment.
|
||||
*/
|
||||
long byteSize();
|
||||
|
||||
/**
|
||||
* Obtains a read-only view of this segment. An attempt to write memory associated with a read-only memory segment
|
||||
* will fail with {@link UnsupportedOperationException}.
|
||||
* @return a read-only view of this segment.
|
||||
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
|
||||
* thread owning this segment.
|
||||
*/
|
||||
MemorySegment asReadOnly();
|
||||
|
||||
/**
|
||||
* Obtains a new memory segment view whose base address is the same as the base address of this segment plus a given offset,
|
||||
* and whose new size is specified by the given argument.
|
||||
* @param offset The new segment base offset (relative to the current segment base address), specified in bytes.
|
||||
* @param newSize The new segment size, specified in bytes.
|
||||
* @return a new memory segment view with updated base/limit addresses.
|
||||
* @throws IndexOutOfBoundsException if {@code offset < 0}, {@code offset > byteSize()}, {@code newSize < 0}, or {@code newSize > byteSize() - offset}
|
||||
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
|
||||
* thread owning this segment.
|
||||
*/
|
||||
MemorySegment asSlice(long offset, long newSize);
|
||||
|
||||
/**
|
||||
* Is this segment alive?
|
||||
* @return true, if the segment is alive.
|
||||
* @see MemorySegment#close()
|
||||
*/
|
||||
boolean isAlive();
|
||||
|
||||
/**
|
||||
* Is this segment read-only?
|
||||
* @return true, if the segment is read-only.
|
||||
* @see MemorySegment#asReadOnly()
|
||||
*/
|
||||
boolean isReadOnly();
|
||||
|
||||
/**
|
||||
* Closes this memory segment. Once a memory segment has been closed, any attempt to use the memory segment,
|
||||
* or to access the memory associated with the segment will fail with {@link IllegalStateException}. Depending on
|
||||
* the kind of memory segment being closed, calling this method further trigger deallocation of all the resources
|
||||
* associated with the memory segment.
|
||||
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
|
||||
* thread owning this segment, or if existing acquired views of this segment are still in use (see {@link MemorySegment#acquire()}).
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Wraps this segment in a {@link ByteBuffer}. Some of the properties of the returned buffer are linked to
|
||||
* the properties of this segment. For instance, if this segment is <em>immutable</em>
|
||||
* (see {@link MemorySegment#asReadOnly()}, then the resulting buffer is <em>read-only</em>
|
||||
* (see {@link ByteBuffer#isReadOnly()}. Additionally, if this is a native memory segment, the resulting buffer is
|
||||
* <em>direct</em> (see {@link ByteBuffer#isDirect()}).
|
||||
* <p>
|
||||
* The life-cycle of the returned buffer will be tied to that of this segment. That means that if the this segment
|
||||
* is closed (see {@link MemorySegment#close()}, accessing the returned
|
||||
* buffer will throw an {@link IllegalStateException}.
|
||||
* <p>
|
||||
* The resulting buffer's byte order is {@link java.nio.ByteOrder#BIG_ENDIAN}; this can be changed using
|
||||
* {@link ByteBuffer#order(java.nio.ByteOrder)}.
|
||||
*
|
||||
* @return a {@link ByteBuffer} view of this memory segment.
|
||||
* @throws UnsupportedOperationException if this segment cannot be mapped onto a {@link ByteBuffer} instance,
|
||||
* e.g. because it models an heap-based segment that is not based on a {@code byte[]}), or if its size is greater
|
||||
* than {@link Integer#MAX_VALUE}.
|
||||
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
|
||||
* thread owning this segment.
|
||||
*/
|
||||
ByteBuffer asByteBuffer();
|
||||
|
||||
/**
|
||||
* Copy the contents of this memory segment into a fresh byte array.
|
||||
* @return a fresh byte array copy of this memory segment.
|
||||
* @throws UnsupportedOperationException if this segment's contents cannot be copied into a {@link byte[]} instance,
|
||||
* e.g. its size is greater than {@link Integer#MAX_VALUE}.
|
||||
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
|
||||
* thread owning this segment.
|
||||
*/
|
||||
byte[] toByteArray();
|
||||
|
||||
/**
|
||||
* Creates a new buffer memory segment that models the memory associated with the given byte
|
||||
* buffer. The segment starts relative to the buffer's position (inclusive)
|
||||
* and ends relative to the buffer's limit (exclusive).
|
||||
* <p>
|
||||
* The resulting memory segment keeps a reference to the backing buffer, to ensure it remains <em>reachable</em>
|
||||
* for the life-time of the segment.
|
||||
*
|
||||
* @param bb the byte buffer backing the buffer memory segment.
|
||||
* @return a new buffer memory segment.
|
||||
*/
|
||||
static MemorySegment ofByteBuffer(ByteBuffer bb) {
|
||||
return Utils.makeBufferSegment(bb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new array memory segment that models the memory associated with a given heap-allocated byte array.
|
||||
* <p>
|
||||
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
|
||||
* for the life-time of the segment.
|
||||
*
|
||||
* @param arr the primitive array backing the array memory segment.
|
||||
* @return a new array memory segment.
|
||||
*/
|
||||
static MemorySegment ofArray(byte[] arr) {
|
||||
return Utils.makeArraySegment(arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new array memory segment that models the memory associated with a given heap-allocated char array.
|
||||
* <p>
|
||||
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
|
||||
* for the life-time of the segment.
|
||||
*
|
||||
* @param arr the primitive array backing the array memory segment.
|
||||
* @return a new array memory segment.
|
||||
*/
|
||||
static MemorySegment ofArray(char[] arr) {
|
||||
return Utils.makeArraySegment(arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new array memory segment that models the memory associated with a given heap-allocated short array.
|
||||
* <p>
|
||||
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
|
||||
* for the life-time of the segment.
|
||||
*
|
||||
* @param arr the primitive array backing the array memory segment.
|
||||
* @return a new array memory segment.
|
||||
*/
|
||||
static MemorySegment ofArray(short[] arr) {
|
||||
return Utils.makeArraySegment(arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new array memory segment that models the memory associated with a given heap-allocated int array.
|
||||
* <p>
|
||||
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
|
||||
* for the life-time of the segment.
|
||||
*
|
||||
* @param arr the primitive array backing the array memory segment.
|
||||
* @return a new array memory segment.
|
||||
*/
|
||||
static MemorySegment ofArray(int[] arr) {
|
||||
return Utils.makeArraySegment(arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new array memory segment that models the memory associated with a given heap-allocated float array.
|
||||
* <p>
|
||||
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
|
||||
* for the life-time of the segment.
|
||||
*
|
||||
* @param arr the primitive array backing the array memory segment.
|
||||
* @return a new array memory segment.
|
||||
*/
|
||||
static MemorySegment ofArray(float[] arr) {
|
||||
return Utils.makeArraySegment(arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new array memory segment that models the memory associated with a given heap-allocated long array.
|
||||
* <p>
|
||||
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
|
||||
* for the life-time of the segment.
|
||||
*
|
||||
* @param arr the primitive array backing the array memory segment.
|
||||
* @return a new array memory segment.
|
||||
*/
|
||||
static MemorySegment ofArray(long[] arr) {
|
||||
return Utils.makeArraySegment(arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new array memory segment that models the memory associated with a given heap-allocated double array.
|
||||
* <p>
|
||||
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
|
||||
* for the life-time of the segment.
|
||||
*
|
||||
* @param arr the primitive array backing the array memory segment.
|
||||
* @return a new array memory segment.
|
||||
*/
|
||||
static MemorySegment ofArray(double[] arr) {
|
||||
return Utils.makeArraySegment(arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new native memory segment that models a newly allocated block of off-heap memory with given layout.
|
||||
* <p>
|
||||
* This is equivalent to the following code:
|
||||
* <blockquote><pre>{@code
|
||||
allocateNative(layout.bytesSize(), layout.bytesAlignment());
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* @implNote The initialization state of the contents of the block of off-heap memory associated with the returned native memory
|
||||
* segment is unspecified and should not be relied upon. Moreover, a client is responsible to call the {@link MemorySegment#close()}
|
||||
* on a native memory segment, to make sure the backing off-heap memory block is deallocated accordingly. Failure to do so
|
||||
* will result in off-heap memory leaks.
|
||||
*
|
||||
* @param layout the layout of the off-heap memory block backing the native memory segment.
|
||||
* @return a new native memory segment.
|
||||
* @throws IllegalArgumentException if the specified layout has illegal size or alignment constraint.
|
||||
*/
|
||||
static MemorySegment allocateNative(MemoryLayout layout) {
|
||||
return allocateNative(layout.byteSize(), layout.byteAlignment());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new native memory segment that models a newly allocated block of off-heap memory with given size (in bytes).
|
||||
* <p>
|
||||
* This is equivalent to the following code:
|
||||
* <blockquote><pre>{@code
|
||||
allocateNative(bytesSize, 1);
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* @implNote The initialization state of the contents of the block of off-heap memory associated with the returned native memory
|
||||
* segment is unspecified and should not be relied upon. Moreover, a client is responsible to call the {@link MemorySegment#close()}
|
||||
* on a native memory segment, to make sure the backing off-heap memory block is deallocated accordingly. Failure to do so
|
||||
* will result in off-heap memory leaks.
|
||||
*
|
||||
* @param bytesSize the size (in bytes) of the off-heap memory block backing the native memory segment.
|
||||
* @return a new native memory segment.
|
||||
* @throws IllegalArgumentException if {@code bytesSize < 0}.
|
||||
*/
|
||||
static MemorySegment allocateNative(long bytesSize) {
|
||||
return allocateNative(bytesSize, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new mapped memory segment that models a memory-mapped region of a file from a given path.
|
||||
*
|
||||
* @implNote When obtaining a mapped segment from a newly created file, the initialization state of the contents of the block
|
||||
* of mapped memory associated with the returned mapped memory segment is unspecified and should not be relied upon.
|
||||
*
|
||||
* @param path the path to the file to memory map.
|
||||
* @param bytesSize the size (in bytes) of the mapped memory backing the memory segment.
|
||||
* @param mapMode a file mapping mode, see {@link FileChannel#map(FileChannel.MapMode, long, long)}.
|
||||
* @return a new mapped memory segment.
|
||||
* @throws IllegalArgumentException if {@code bytesSize < 0}.
|
||||
* @throws UnsupportedOperationException if an unsupported map mode is specified.
|
||||
* @throws IOException if the specified path does not point to an existing file, or if some other I/O error occurs.
|
||||
*/
|
||||
static MemorySegment mapFromPath(Path path, long bytesSize, FileChannel.MapMode mapMode) throws IOException {
|
||||
return Utils.makeMappedSegment(path, bytesSize, mapMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new native memory segment that models a newly allocated block of off-heap memory with given size and
|
||||
* alignment constraint (in bytes).
|
||||
*
|
||||
* @implNote The initialization state of the contents of the block of off-heap memory associated with the returned native memory
|
||||
* segment is unspecified and should not be relied upon. Moreover, a client is responsible to call the {@link MemorySegment#close()}
|
||||
* on a native memory segment, to make sure the backing off-heap memory block is deallocated accordingly. Failure to do so
|
||||
* will result in off-heap memory leaks.
|
||||
*
|
||||
* @param bytesSize the size (in bytes) of the off-heap memory block backing the native memory segment.
|
||||
* @param alignmentBytes the alignment constraint (in bytes) of the off-heap memory block backing the native memory segment.
|
||||
* @return a new native memory segment.
|
||||
* @throws IllegalArgumentException if {@code bytesSize < 0}, {@code alignmentBytes < 0}, or if {@code alignmentBytes}
|
||||
* is not a power of 2.
|
||||
*/
|
||||
static MemorySegment allocateNative(long bytesSize, long alignmentBytes) {
|
||||
if (bytesSize <= 0) {
|
||||
throw new IllegalArgumentException("Invalid allocation size : " + bytesSize);
|
||||
}
|
||||
|
||||
if (alignmentBytes < 0 ||
|
||||
((alignmentBytes & (alignmentBytes - 1)) != 0L)) {
|
||||
throw new IllegalArgumentException("Invalid alignment constraint : " + alignmentBytes);
|
||||
}
|
||||
|
||||
return Utils.makeNativeSegment(bytesSize, alignmentBytes);
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
package jdk.incubator.foreign;
|
||||
|
||||
import java.lang.constant.ConstantDescs;
|
||||
import java.lang.constant.DynamicConstantDesc;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
|
||||
/**
|
||||
* A padding layout. A padding layout specifies the size of extra space which is typically not accessed by applications,
|
||||
* and is typically used for aligning member layouts around word boundaries.
|
||||
* <p>
|
||||
* This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
|
||||
* class; use of identity-sensitive operations (including reference equality
|
||||
* ({@code ==}), identity hash code, or synchronization) on instances of
|
||||
* {@code PaddingLayout} may have unpredictable results and should be avoided.
|
||||
* The {@code equals} method should be used for comparisons.
|
||||
*
|
||||
* @implSpec
|
||||
* This class is immutable and thread-safe.
|
||||
*/
|
||||
/* package-private */ final class PaddingLayout extends AbstractLayout implements MemoryLayout {
|
||||
|
||||
PaddingLayout(long size) {
|
||||
this(size, size, Optional.empty());
|
||||
}
|
||||
|
||||
PaddingLayout(long size, long alignment, Optional<String> name) {
|
||||
super(OptionalLong.of(size), alignment, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return decorateLayoutString("x" + bitSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
if (!(other instanceof PaddingLayout)) {
|
||||
return false;
|
||||
}
|
||||
PaddingLayout p = (PaddingLayout)other;
|
||||
return bitSize() == p.bitSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), bitSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
PaddingLayout dup(long alignment, Optional<String> name) {
|
||||
return new PaddingLayout(bitSize(), alignment, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DynamicConstantDesc<MemoryLayout>> describeConstable() {
|
||||
return Optional.of(DynamicConstantDesc.ofNamed(ConstantDescs.BSM_INVOKE, "padding",
|
||||
CD_LAYOUT, MH_PADDING, bitSize()));
|
||||
}
|
||||
|
||||
//hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
|
||||
//but that causes issues with javadoc, see JDK-8224052
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public PaddingLayout withName(String name) {
|
||||
return (PaddingLayout)super.withName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public PaddingLayout withBitAlignment(long alignmentBits) {
|
||||
return (PaddingLayout)super.withBitAlignment(alignmentBits);
|
||||
}
|
||||
}
|
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
package jdk.incubator.foreign;
|
||||
|
||||
import java.lang.constant.ConstantDescs;
|
||||
import java.lang.constant.DynamicConstantDesc;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
|
||||
/**
|
||||
* A sequence layout. A sequence layout is used to denote a repetition of a given layout, also called the sequence layout's <em>element layout</em>.
|
||||
* The repetition count, where it exists (e.g. for <em>finite</em> sequence layouts) is said to be the the sequence layout's <em>element count</em>.
|
||||
* A finite sequence layout can be thought of as a group layout where the sequence layout's element layout is repeated a number of times
|
||||
* that is equal to the sequence layout's element count. In other words this layout:
|
||||
*
|
||||
* <pre>{@code
|
||||
MemoryLayout.ofSequence(3, MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN));
|
||||
* }</pre>
|
||||
*
|
||||
* is equivalent to the following layout:
|
||||
*
|
||||
* <pre>{@code
|
||||
MemoryLayout.ofStruct(
|
||||
MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN),
|
||||
MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN),
|
||||
MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN));
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
|
||||
* class; use of identity-sensitive operations (including reference equality
|
||||
* ({@code ==}), identity hash code, or synchronization) on instances of
|
||||
* {@code SequenceLayout} may have unpredictable results and should be avoided.
|
||||
* The {@code equals} method should be used for comparisons.
|
||||
*
|
||||
* @implSpec
|
||||
* This class is immutable and thread-safe.
|
||||
*/
|
||||
public final class SequenceLayout extends AbstractLayout {
|
||||
|
||||
private final OptionalLong elemCount;
|
||||
private final MemoryLayout elementLayout;
|
||||
|
||||
SequenceLayout(OptionalLong elemCount, MemoryLayout elementLayout) {
|
||||
this(elemCount, elementLayout, elementLayout.bitAlignment(), Optional.empty());
|
||||
}
|
||||
|
||||
SequenceLayout(OptionalLong elemCount, MemoryLayout elementLayout, long alignment, Optional<String> name) {
|
||||
super(elemCount.isPresent() && AbstractLayout.optSize(elementLayout).isPresent() ?
|
||||
OptionalLong.of(elemCount.getAsLong() * elementLayout.bitSize()) :
|
||||
OptionalLong.empty(), alignment, name);
|
||||
this.elemCount = elemCount;
|
||||
this.elementLayout = elementLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the element layout associated with this sequence layout.
|
||||
*
|
||||
* @return The element layout associated with this sequence layout.
|
||||
*/
|
||||
public MemoryLayout elementLayout() {
|
||||
return elementLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the element count of this sequence layout (if any).
|
||||
*
|
||||
* @return the element count of this sequence layout (if any).
|
||||
*/
|
||||
public OptionalLong elementCount() {
|
||||
return elemCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return decorateLayoutString(String.format("[%s:%s]",
|
||||
elemCount.isPresent() ? elemCount.getAsLong() : "", elementLayout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
if (!(other instanceof SequenceLayout)) {
|
||||
return false;
|
||||
}
|
||||
SequenceLayout s = (SequenceLayout)other;
|
||||
return elemCount.equals(s.elemCount) && elementLayout.equals(s.elementLayout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), elemCount, elementLayout);
|
||||
}
|
||||
|
||||
@Override
|
||||
SequenceLayout dup(long alignment, Optional<String> name) {
|
||||
return new SequenceLayout(elementCount(), elementLayout, alignment, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasNaturalAlignment() {
|
||||
return alignment == elementLayout.bitAlignment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DynamicConstantDesc<SequenceLayout>> describeConstable() {
|
||||
return elemCount.isPresent() ?
|
||||
Optional.of(DynamicConstantDesc.ofNamed(ConstantDescs.BSM_INVOKE, "value",
|
||||
CD_SEQUENCE_LAYOUT, MH_SIZED_SEQUENCE, elemCount.getAsLong(), elementLayout.describeConstable().get())) :
|
||||
Optional.of(DynamicConstantDesc.ofNamed(ConstantDescs.BSM_INVOKE, "value",
|
||||
CD_SEQUENCE_LAYOUT, MH_UNSIZED_SEQUENCE, elementLayout.describeConstable().get()));
|
||||
}
|
||||
|
||||
//hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
|
||||
//but that causes issues with javadoc, see JDK-8224052
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public SequenceLayout withName(String name) {
|
||||
return (SequenceLayout)super.withName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public SequenceLayout withBitAlignment(long alignmentBits) {
|
||||
return (SequenceLayout)super.withBitAlignment(alignmentBits);
|
||||
}
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
package jdk.incubator.foreign;
|
||||
|
||||
import java.lang.constant.ConstantDescs;
|
||||
import java.lang.constant.DynamicConstantDesc;
|
||||
import java.lang.constant.MethodHandleDesc;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
|
||||
/**
|
||||
* A value layout. A value layout is used to model the memory layout associated with values of basic data types, such as <em>integral</em> types
|
||||
* (either signed or unsigned) and <em>floating-point</em> types. Each value layout has a size and a byte order (see {@link ByteOrder}).
|
||||
*
|
||||
* <p>
|
||||
* This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
|
||||
* class; use of identity-sensitive operations (including reference equality
|
||||
* ({@code ==}), identity hash code, or synchronization) on instances of
|
||||
* {@code ValueLayout} may have unpredictable results and should be avoided.
|
||||
* The {@code equals} method should be used for comparisons.
|
||||
*
|
||||
* @implSpec
|
||||
* This class is immutable and thread-safe.
|
||||
*/
|
||||
public final class ValueLayout extends AbstractLayout implements MemoryLayout {
|
||||
|
||||
private final ByteOrder order;
|
||||
|
||||
ValueLayout(ByteOrder order, long size) {
|
||||
this(order, size, size, Optional.empty());
|
||||
}
|
||||
|
||||
ValueLayout(ByteOrder order, long size, long alignment, Optional<String> name) {
|
||||
super(OptionalLong.of(size), alignment, name);
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value's byte order.
|
||||
*
|
||||
* @return the value's byte order.
|
||||
*/
|
||||
public ByteOrder order() {
|
||||
return order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new value layout with given byte order.
|
||||
*
|
||||
* @param order the desired byte order.
|
||||
* @return a new value layout with given byte order.
|
||||
*/
|
||||
public ValueLayout withOrder(ByteOrder order) {
|
||||
return new ValueLayout(order, bitSize(), alignment, optName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return decorateLayoutString(String.format("%s%d",
|
||||
order == ByteOrder.BIG_ENDIAN ? "B" : "b",
|
||||
bitSize()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
if (!(other instanceof ValueLayout)) {
|
||||
return false;
|
||||
}
|
||||
ValueLayout v = (ValueLayout)other;
|
||||
return order.equals(v.order) &&
|
||||
bitSize() == v.bitSize() &&
|
||||
alignment == v.alignment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), order, bitSize(), alignment);
|
||||
}
|
||||
|
||||
@Override
|
||||
ValueLayout dup(long alignment, Optional<String> name) {
|
||||
return new ValueLayout(order, bitSize(), alignment, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DynamicConstantDesc<ValueLayout>> describeConstable() {
|
||||
return Optional.of(DynamicConstantDesc.ofNamed(ConstantDescs.BSM_INVOKE, "value",
|
||||
CD_VALUE_LAYOUT, MH_VALUE, bitSize(), order == ByteOrder.BIG_ENDIAN ? BIG_ENDIAN : LITTLE_ENDIAN));
|
||||
}
|
||||
|
||||
//hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
|
||||
//but that causes issues with javadoc, see JDK-8224052
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public ValueLayout withName(String name) {
|
||||
return (ValueLayout)super.withName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public ValueLayout withBitAlignment(long alignmentBits) {
|
||||
return (ValueLayout)super.withBitAlignment(alignmentBits);
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p> Classes to support low-level, safe and efficient memory access. For example:
|
||||
*
|
||||
* <pre>{@code
|
||||
static final VarHandle intHandle = MemoryHandles.varHandle(int.class, ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(10 * 4)) {
|
||||
MemoryAddress base = segment.baseAddress();
|
||||
for (long i = 0 ; i < 10 ; i++) {
|
||||
intHandle.set(base.offset(i * 4), (int)i);
|
||||
}
|
||||
}
|
||||
* }</pre>
|
||||
*
|
||||
* Here we create a var handle, namely {@code intHandle}, to manipulate values of the primitive type {@code int}, at
|
||||
* a given memory location. We then create a <em>native</em> memory segment, that is, a memory segment backed by
|
||||
* off-heap memory; the size of the segment is 40 bytes, enough to store 10 values of the primitive type {@code int}.
|
||||
* The segment is created inside a <em>try-with-resources</em> construct: this idiom ensures that all the memory resources
|
||||
* associated with the segment will be released at the end of the block. Inside the try-with-resources block, we initialize
|
||||
* the contents of the memory segment; more specifically, if we view the memory segment as a set of 10 adjacent slots,
|
||||
* {@code s[i]}, where {@code 0 <= i < 10}, where the size of each slot is exactly 4 bytes, the initialization logic above will set each slot
|
||||
* so that {@code s[i] = i}, again where {@code 0 <= i < 10}.
|
||||
*
|
||||
* <p>
|
||||
* The key abstractions introduced by this package are {@link jdk.incubator.foreign.MemorySegment} and {@link jdk.incubator.foreign.MemoryAddress}.
|
||||
* The first models a contiguous memory region, which can reside either inside or outside the Java heap; the latter models an address - that is,
|
||||
* an offset inside a given segment. A memory address represents the main access coordinate of a memory access var handle, which can be obtained
|
||||
* using the combinator methods defined in the {@link jdk.incubator.foreign.MemoryHandles} class. Finally, the {@link jdk.incubator.foreign.MemoryLayout} class
|
||||
* hierarchy enables description of <em>memory layouts</em> and basic operations such as computing the size in bytes of a given
|
||||
* layout, obtain its alignment requirements, and so on. Memory layouts also provide an alternate, more abstract way, to produce
|
||||
* memory access var handles, e.g. using <a href="MemoryLayout.html#layout-paths"><em>layout paths</em></a>.
|
||||
*
|
||||
* <h2><a id="deallocation"></a>Deterministic deallocation</h2>
|
||||
*
|
||||
* When writing code that manipulates memory segments, especially if backed by memory which resides outside the Java heap, it is
|
||||
* crucial that the resources associated with a memory segment are released when the segment is no longer in use, by calling the {@link jdk.incubator.foreign.MemorySegment#close()}
|
||||
* method either explicitly, or implicitly, by relying on try-with-resources construct (as demonstrated in the example above).
|
||||
* Closing a given memory segment is an <em>atomic</em> operation which can either succeed - and result in the underlying
|
||||
* memory associated with the segment to be released, or <em>fail</em> with an exception.
|
||||
* <p>
|
||||
* The deterministic deallocation model differs significantly from the implicit strategies adopted within other APIs, most
|
||||
* notably the {@link java.nio.ByteBuffer} API: in that case, when a native byte buffer is created (see {@link java.nio.ByteBuffer#allocateDirect(int)}),
|
||||
* the underlying memory is not released until the byte buffer reference becomes <em>unreachable</em>. While implicit deallocation
|
||||
* models such as this can be very convenient - clients do not have to remember to <em>close</em> a direct buffer - such models can also make it
|
||||
* hard for clients to ensure that the memory associated with a direct buffer has indeed been released.
|
||||
*
|
||||
* <h2><a id="safety"></a>Safety</h2>
|
||||
*
|
||||
* This API provides strong safety guarantees when it comes to memory access. First, when dereferencing a memory segment using
|
||||
* a memory address, such an address is validated (upon access), to make sure that it does not point to a memory location
|
||||
* which resides <em>outside</em> the boundaries of the memory segment it refers to. We call this guarantee <em>spatial safety</em>.
|
||||
* <p>
|
||||
* Since memory segments can be closed (see above), a memory address is also validated (upon access) to make sure that
|
||||
* the segment it belongs to has not been closed prematurely. We call this guarantee <em>temporal safety</em>. Note that,
|
||||
* in the general case, guaranteeing temporal safety can be hard, as multiple threads could attempt to access and/or close
|
||||
* the same memory segment concurrently. The memory access API addresses this problem by imposing strong
|
||||
* <a href="MemorySegment.html#thread-confinement"><em>thread-confinement</em></a> guarantees on memory segments: each
|
||||
* memory segment is associated with an owner thread, which is the only thread that can either access or close the segment.
|
||||
* A thread other than the owner thread will have to explicitly <em>acquire</em> a segment in order to be able to use it.
|
||||
* <p>
|
||||
* Together, spatial and temporal safety ensure that each memory access operation either succeeds - and accesses a valid
|
||||
* memory location - or fails.
|
||||
*/
|
||||
package jdk.incubator.foreign;
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
package jdk.incubator.foreign.unsafe;
|
||||
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.internal.foreign.MemoryAddressImpl;
|
||||
|
||||
/**
|
||||
* Unsafe methods to allow interop between sun.misc.unsafe and memory access API.
|
||||
*/
|
||||
public final class ForeignUnsafe {
|
||||
|
||||
private ForeignUnsafe() {
|
||||
//just the one, please
|
||||
}
|
||||
|
||||
// The following methods can be used in conjunction with the java.foreign API.
|
||||
|
||||
/**
|
||||
* Obtain the base object (if any) associated with this address. This can be used in conjunction with
|
||||
* {@link #getUnsafeOffset(MemoryAddress)} in order to obtain a base/offset addressing coordinate pair
|
||||
* to be used with methods like {@link sun.misc.Unsafe#getInt(Object, long)} and the likes.
|
||||
*
|
||||
* @param address the address whose base object is to be obtained.
|
||||
* @return the base object associated with the address, or {@code null}.
|
||||
*/
|
||||
public static Object getUnsafeBase(MemoryAddress address) {
|
||||
return ((MemoryAddressImpl)address).unsafeGetBase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the offset associated with this address. If {@link #getUnsafeBase(MemoryAddress)} returns {@code null} on the passed
|
||||
* address, then the offset is to be interpreted as the (absolute) numerical value associated said address.
|
||||
* Alternatively, the offset represents the displacement of a field or an array element within the containing
|
||||
* base object. This can be used in conjunction with {@link #getUnsafeBase(MemoryAddress)} in order to obtain a base/offset
|
||||
* addressing coordinate pair to be used with methods like {@link sun.misc.Unsafe#getInt(Object, long)} and the likes.
|
||||
*
|
||||
* @param address the address whose offset is to be obtained.
|
||||
* @return the offset associated with the address.
|
||||
*/
|
||||
public static long getUnsafeOffset(MemoryAddress address) {
|
||||
return ((MemoryAddressImpl)address).unsafeGetOffset();
|
||||
}
|
||||
}
|
@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.internal.access.JavaLangInvokeAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
import jdk.incubator.foreign.GroupLayout;
|
||||
import jdk.incubator.foreign.SequenceLayout;
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.stream.LongStream;
|
||||
|
||||
/**
|
||||
* This class provide support for constructing layout paths; that is, starting from a root path (see {@link #rootPath(MemoryLayout)},
|
||||
* a path can be constructed by selecting layout elements using the selector methods provided by this class
|
||||
* (see {@link #sequenceElement()}, {@link #sequenceElement(long)}, {@link #sequenceElement(long, long)}, {@link #groupElement(String)}).
|
||||
* Once a path has been fully constructed, clients can ask for the offset associated with the layout element selected
|
||||
* by the path (see {@link #offset}), or obtain a memory access var handle to access the selected layout element
|
||||
* given an address pointing to a segment associated with the root layout (see {@link #dereferenceHandle(Class)}).
|
||||
*/
|
||||
public class LayoutPath {
|
||||
|
||||
private static JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess();
|
||||
|
||||
private final MemoryLayout layout;
|
||||
private final long offset;
|
||||
private final LayoutPath enclosing;
|
||||
private final long[] strides;
|
||||
|
||||
private LayoutPath(MemoryLayout layout, long offset, long[] strides, LayoutPath enclosing) {
|
||||
this.layout = layout;
|
||||
this.offset = offset;
|
||||
this.strides = strides;
|
||||
this.enclosing = enclosing;
|
||||
}
|
||||
|
||||
// Layout path selector methods
|
||||
|
||||
public LayoutPath sequenceElement() {
|
||||
check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
|
||||
SequenceLayout seq = (SequenceLayout)layout;
|
||||
MemoryLayout elem = seq.elementLayout();
|
||||
return LayoutPath.nestedPath(elem, offset, addStride(elem.bitSize()), this);
|
||||
}
|
||||
|
||||
public LayoutPath sequenceElement(long start, long step) {
|
||||
check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
|
||||
SequenceLayout seq = (SequenceLayout)layout;
|
||||
checkSequenceBounds(seq, start);
|
||||
MemoryLayout elem = seq.elementLayout();
|
||||
long elemSize = elem.bitSize();
|
||||
return LayoutPath.nestedPath(elem, offset + (start * elemSize), addStride(elemSize * step), this);
|
||||
}
|
||||
|
||||
public LayoutPath sequenceElement(long index) {
|
||||
check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
|
||||
SequenceLayout seq = (SequenceLayout)layout;
|
||||
checkSequenceBounds(seq, index);
|
||||
long elemOffset = 0;
|
||||
if (index > 0) {
|
||||
//if index == 0, we do not depend on sequence element size, so skip
|
||||
long elemSize = seq.elementLayout().bitSize();
|
||||
elemOffset = elemSize * index;
|
||||
}
|
||||
return LayoutPath.nestedPath(seq.elementLayout(), offset + elemOffset, strides, this);
|
||||
}
|
||||
|
||||
public LayoutPath groupElement(String name) {
|
||||
check(GroupLayout.class, "attempting to select a group element from a non-group layout");
|
||||
GroupLayout g = (GroupLayout)layout;
|
||||
long offset = 0;
|
||||
MemoryLayout elem = null;
|
||||
for (int i = 0; i < g.memberLayouts().size(); i++) {
|
||||
MemoryLayout l = g.memberLayouts().get(i);
|
||||
if (l.name().isPresent() &&
|
||||
l.name().get().equals(name)) {
|
||||
elem = l;
|
||||
break;
|
||||
} else {
|
||||
offset += l.bitSize();
|
||||
}
|
||||
}
|
||||
if (elem == null) {
|
||||
throw badLayoutPath("cannot resolve '" + name + "' in layout " + layout);
|
||||
}
|
||||
return LayoutPath.nestedPath(elem, this.offset + offset, strides, this);
|
||||
}
|
||||
|
||||
// Layout path projections
|
||||
|
||||
public long offset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public VarHandle dereferenceHandle(Class<?> carrier) {
|
||||
if (!(layout instanceof ValueLayout)) {
|
||||
throw badLayoutPath("layout path does not select a value layout");
|
||||
}
|
||||
|
||||
if (!carrier.isPrimitive() || carrier == void.class || carrier == boolean.class // illegal carrier?
|
||||
|| Wrapper.forPrimitiveType(carrier).bitWidth() != layout.bitSize()) { // carrier has the right size?
|
||||
throw new IllegalArgumentException("Invalid carrier: " + carrier + ", for layout " + layout);
|
||||
}
|
||||
|
||||
checkAlignment(this);
|
||||
|
||||
return JLI.memoryAddressViewVarHandle(
|
||||
carrier,
|
||||
layout.byteAlignment() - 1, //mask
|
||||
((ValueLayout) layout).order(),
|
||||
Utils.bitsToBytesOrThrow(offset, IllegalStateException::new),
|
||||
LongStream.of(strides).map(s -> Utils.bitsToBytesOrThrow(s, IllegalStateException::new)).toArray());
|
||||
}
|
||||
|
||||
// Layout path construction
|
||||
|
||||
public static LayoutPath rootPath(MemoryLayout layout) {
|
||||
return new LayoutPath(layout, 0L, EMPTY_STRIDES, null);
|
||||
}
|
||||
|
||||
private static LayoutPath nestedPath(MemoryLayout layout, long offset, long[] strides, LayoutPath encl) {
|
||||
return new LayoutPath(layout, offset, strides, encl);
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
||||
private void check(Class<?> layoutClass, String msg) {
|
||||
if (!layoutClass.isAssignableFrom(layout.getClass())) {
|
||||
throw badLayoutPath(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkSequenceBounds(SequenceLayout seq, long index) {
|
||||
if (seq.elementCount().isPresent() && index >= seq.elementCount().getAsLong()) {
|
||||
throw badLayoutPath(String.format("Sequence index out of bound; found: %d, size: %d", index, seq.elementCount().getAsLong()));
|
||||
}
|
||||
}
|
||||
|
||||
private static IllegalArgumentException badLayoutPath(String cause) {
|
||||
return new IllegalArgumentException("Bad layout path: " + cause);
|
||||
}
|
||||
|
||||
private static void checkAlignment(LayoutPath path) {
|
||||
MemoryLayout layout = path.layout;
|
||||
long alignment = layout.bitAlignment();
|
||||
if (path.offset % alignment != 0) {
|
||||
throw new UnsupportedOperationException("Invalid alignment requirements for layout " + layout);
|
||||
}
|
||||
for (long stride : path.strides) {
|
||||
if (stride % alignment != 0) {
|
||||
throw new UnsupportedOperationException("Alignment requirements for layout " + layout + " do not match stride " + stride);
|
||||
}
|
||||
}
|
||||
LayoutPath encl = path.enclosing;
|
||||
if (encl != null) {
|
||||
if (encl.layout.bitAlignment() < alignment) {
|
||||
throw new UnsupportedOperationException("Alignment requirements for layout " + layout + " do not match those for enclosing layout " + encl.layout);
|
||||
}
|
||||
checkAlignment(encl);
|
||||
}
|
||||
}
|
||||
|
||||
private long[] addStride(long stride) {
|
||||
long[] newStrides = new long[strides.length + 1];
|
||||
System.arraycopy(strides, 0, newStrides, 0, strides.length);
|
||||
newStrides[strides.length] = stride;
|
||||
return newStrides;
|
||||
}
|
||||
|
||||
private static long[] EMPTY_STRIDES = new long[0];
|
||||
|
||||
/**
|
||||
* This class provides an immutable implementation for the {@code PathElement} interface. A path element implementation
|
||||
* is simply a pointer to one of the selector methods provided by the {@code LayoutPath} class.
|
||||
*/
|
||||
public static class PathElementImpl implements MemoryLayout.PathElement, UnaryOperator<LayoutPath> {
|
||||
|
||||
final UnaryOperator<LayoutPath> pathOp;
|
||||
|
||||
public PathElementImpl(UnaryOperator<LayoutPath> pathOp) {
|
||||
this.pathOp = pathOp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LayoutPath apply(LayoutPath layoutPath) {
|
||||
return pathOp.apply(layoutPath);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import jdk.internal.access.foreign.MemoryAddressProxy;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* This class provides an immutable implementation for the {@code MemoryAddress} interface. This class contains information
|
||||
* about the segment this address is associated with, as well as an offset into such segment.
|
||||
*/
|
||||
public final class MemoryAddressImpl implements MemoryAddress, MemoryAddressProxy {
|
||||
|
||||
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||
|
||||
private final MemorySegmentImpl segment;
|
||||
private final long offset;
|
||||
|
||||
public MemoryAddressImpl(MemorySegmentImpl segment, long offset) {
|
||||
this.segment = Objects.requireNonNull(segment);
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public static void copy(MemoryAddressImpl src, MemoryAddressImpl dst, long size) {
|
||||
src.checkAccess(0, size, true);
|
||||
dst.checkAccess(0, size, false);
|
||||
//check disjoint
|
||||
long offsetSrc = src.unsafeGetOffset();
|
||||
long offsetDst = dst.unsafeGetOffset();
|
||||
Object baseSrc = src.unsafeGetBase();
|
||||
Object baseDst = dst.unsafeGetBase();
|
||||
UNSAFE.copyMemory(baseSrc, offsetSrc, baseDst, offsetDst, size);
|
||||
}
|
||||
|
||||
// MemoryAddress methods
|
||||
|
||||
@Override
|
||||
public long offset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment segment() {
|
||||
return segment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryAddress offset(long bytes) {
|
||||
return new MemoryAddressImpl(segment, offset + bytes);
|
||||
}
|
||||
|
||||
// MemoryAddressProxy methods
|
||||
|
||||
public void checkAccess(long offset, long length, boolean readOnly) {
|
||||
segment.checkRange(this.offset + offset, length, !readOnly);
|
||||
}
|
||||
|
||||
public long unsafeGetOffset() {
|
||||
return segment.min + offset;
|
||||
}
|
||||
|
||||
public Object unsafeGetBase() {
|
||||
return segment.base();
|
||||
}
|
||||
|
||||
// Object methods
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(unsafeGetBase(), unsafeGetOffset());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object that) {
|
||||
if (that instanceof MemoryAddressImpl) {
|
||||
MemoryAddressImpl addr = (MemoryAddressImpl)that;
|
||||
return Objects.equals(unsafeGetBase(), ((MemoryAddressImpl) that).unsafeGetBase()) &&
|
||||
unsafeGetOffset() == addr.unsafeGetOffset();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MemoryAddress{ region: " + segment + " offset=0x" + Long.toHexString(offset) + " }";
|
||||
}
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
|
||||
/**
|
||||
* This class manages the temporal bounds associated with a memory segment. A scope has a liveness bit, which is updated
|
||||
* when the scope is closed (this operation is triggered by {@link MemorySegmentImpl#close()}). Furthermore, a scope is
|
||||
* associated with an <em>atomic</em> counter which can be incremented (upon calling the {@link #acquire()} method),
|
||||
* and is decremented (when a previously acquired segment is later closed).
|
||||
*/
|
||||
public final class MemoryScope {
|
||||
|
||||
//reference to keep hold onto
|
||||
final Object ref;
|
||||
|
||||
int activeCount = UNACQUIRED;
|
||||
|
||||
final static VarHandle COUNT_HANDLE;
|
||||
|
||||
static {
|
||||
try {
|
||||
COUNT_HANDLE = MethodHandles.lookup().findVarHandle(MemoryScope.class, "activeCount", int.class);
|
||||
} catch (Throwable ex) {
|
||||
throw new ExceptionInInitializerError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
final static int UNACQUIRED = 0;
|
||||
final static int CLOSED = -1;
|
||||
final static int MAX_ACQUIRE = Integer.MAX_VALUE;
|
||||
|
||||
final Runnable cleanupAction;
|
||||
|
||||
public MemoryScope(Object ref, Runnable cleanupAction) {
|
||||
this.ref = ref;
|
||||
this.cleanupAction = cleanupAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method performs a full, thread-safe liveness check; can be used outside confinement thread.
|
||||
*/
|
||||
final boolean isAliveThreadSafe() {
|
||||
return ((int)COUNT_HANDLE.getVolatile(this)) != CLOSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method performs a quick liveness check; must be called from the confinement thread.
|
||||
*/
|
||||
final void checkAliveConfined() {
|
||||
if (activeCount == CLOSED) {
|
||||
throw new IllegalStateException("Segment is not alive");
|
||||
}
|
||||
}
|
||||
|
||||
MemoryScope acquire() {
|
||||
int value;
|
||||
do {
|
||||
value = (int)COUNT_HANDLE.getVolatile(this);
|
||||
if (value == CLOSED) {
|
||||
//segment is not alive!
|
||||
throw new IllegalStateException("Segment is not alive");
|
||||
} else if (value == MAX_ACQUIRE) {
|
||||
//overflow
|
||||
throw new IllegalStateException("Segment acquire limit exceeded");
|
||||
}
|
||||
} while (!COUNT_HANDLE.compareAndSet(this, value, value + 1));
|
||||
return new MemoryScope(ref, this::release);
|
||||
}
|
||||
|
||||
private void release() {
|
||||
int value;
|
||||
do {
|
||||
value = (int)COUNT_HANDLE.getVolatile(this);
|
||||
if (value <= UNACQUIRED) {
|
||||
//cannot get here - we can't close segment twice
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
} while (!COUNT_HANDLE.compareAndSet(this, value, value - 1));
|
||||
}
|
||||
|
||||
void close() {
|
||||
if (!COUNT_HANDLE.compareAndSet(this, UNACQUIRED, CLOSED)) {
|
||||
//first check if already closed...
|
||||
checkAliveConfined();
|
||||
//...if not, then we have acquired views that are still active
|
||||
throw new IllegalStateException("Cannot close a segment that has active acquired views");
|
||||
}
|
||||
if (cleanupAction != null) {
|
||||
cleanupAction.run();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.internal.access.JavaNioAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.access.foreign.MemorySegmentProxy;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* This class provides an immutable implementation for the {@code MemorySegment} interface. This class contains information
|
||||
* about the segment's spatial and temporal bounds, as well as the addressing coordinates (base + offset) which allows
|
||||
* unsafe access; each memory segment implementation is associated with an owner thread which is set at creation time.
|
||||
* Access to certain sensitive operations on the memory segment will fail with {@code IllegalStateException} if the
|
||||
* segment is either in an invalid state (e.g. it has already been closed) or if access occurs from a thread other
|
||||
* than the owner thread. See {@link MemoryScope} for more details on management of temporal bounds.
|
||||
*/
|
||||
public final class MemorySegmentImpl implements MemorySegment, MemorySegmentProxy {
|
||||
|
||||
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||
private static final int BYTE_ARR_BASE = UNSAFE.arrayBaseOffset(byte[].class);
|
||||
|
||||
final long length;
|
||||
final int mask;
|
||||
final long min;
|
||||
final Object base;
|
||||
final Thread owner;
|
||||
final MemoryScope scope;
|
||||
|
||||
final static int READ_ONLY = 1;
|
||||
final static long NONCE = new Random().nextLong();
|
||||
|
||||
public MemorySegmentImpl(long min, Object base, long length, int mask, Thread owner, MemoryScope scope) {
|
||||
this.length = length;
|
||||
this.mask = mask;
|
||||
this.min = min;
|
||||
this.base = base;
|
||||
this.owner = owner;
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
// MemorySegment methods
|
||||
|
||||
@Override
|
||||
public final MemorySegmentImpl asSlice(long offset, long newSize) {
|
||||
checkValidState();
|
||||
checkBounds(offset, newSize);
|
||||
return new MemorySegmentImpl(min + offset, base, newSize, mask, owner, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment acquire() {
|
||||
return new MemorySegmentImpl(min, base, length, mask, Thread.currentThread(), scope.acquire());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final MemoryAddress baseAddress() {
|
||||
return new MemoryAddressImpl(this, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final long byteSize() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final MemorySegmentImpl asReadOnly() {
|
||||
checkValidState();
|
||||
return new MemorySegmentImpl(min, base, length, mask | READ_ONLY, owner, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isAlive() {
|
||||
return scope.isAliveThreadSafe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isReadOnly() {
|
||||
return isSet(READ_ONLY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccessible() {
|
||||
return owner == Thread.currentThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void close() {
|
||||
checkValidState();
|
||||
scope.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer asByteBuffer() {
|
||||
checkValidState();
|
||||
checkIntSize("ByteBuffer");
|
||||
JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
|
||||
ByteBuffer _bb;
|
||||
if (base() != null) {
|
||||
if (!(base() instanceof byte[])) {
|
||||
throw new UnsupportedOperationException("Not an address to an heap-allocated byte array");
|
||||
}
|
||||
_bb = nioAccess.newHeapByteBuffer((byte[]) base(), (int)min - BYTE_ARR_BASE, (int) length, this);
|
||||
} else {
|
||||
_bb = nioAccess.newDirectByteBuffer(min, (int) length, null, this);
|
||||
}
|
||||
if (isReadOnly()) {
|
||||
//scope is IMMUTABLE - obtain a RO byte buffer
|
||||
_bb = _bb.asReadOnlyBuffer();
|
||||
}
|
||||
return _bb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
checkValidState();
|
||||
checkIntSize("byte[]");
|
||||
byte[] arr = new byte[(int)length];
|
||||
MemorySegment arrSegment = MemorySegment.ofArray(arr);
|
||||
MemoryAddress.copy(this.baseAddress(), arrSegment.baseAddress(), length);
|
||||
return arr;
|
||||
}
|
||||
|
||||
// MemorySegmentProxy methods
|
||||
|
||||
@Override
|
||||
public final void checkValidState() {
|
||||
if (owner != Thread.currentThread()) {
|
||||
throw new IllegalStateException("Attempt to access segment outside owning thread");
|
||||
}
|
||||
scope.checkAliveConfined();
|
||||
}
|
||||
|
||||
// Object methods
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MemorySegment{ id=0x" + Long.toHexString(id()) + " limit: " + byteSize() + " }";
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
||||
void checkRange(long offset, long length, boolean writeAccess) {
|
||||
checkValidState();
|
||||
if (isReadOnly() && writeAccess) {
|
||||
throw new UnsupportedOperationException("Cannot write to read-only memory segment");
|
||||
}
|
||||
checkBounds(offset, length);
|
||||
}
|
||||
|
||||
Object base() {
|
||||
return base;
|
||||
}
|
||||
|
||||
private boolean isSet(int mask) {
|
||||
return (this.mask & mask) != 0;
|
||||
}
|
||||
|
||||
private void checkIntSize(String typeName) {
|
||||
if (length > (Integer.MAX_VALUE - 8)) { //conservative check
|
||||
throw new UnsupportedOperationException(String.format("Segment is too large to wrap as %s. Size: %d", typeName, length));
|
||||
}
|
||||
}
|
||||
|
||||
private void checkBounds(long offset, long length) {
|
||||
if (length < 0 ||
|
||||
offset < 0 ||
|
||||
offset > this.length - length) { // careful of overflow
|
||||
throw new IndexOutOfBoundsException(String.format("Out of bound access on segment %s; new offset = %d; new length = %d",
|
||||
this, offset, length));
|
||||
}
|
||||
}
|
||||
|
||||
private int id() {
|
||||
//compute a stable and random id for this memory segment
|
||||
return Math.abs(Objects.hash(base, min, NONCE));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.internal.access.JavaNioAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.access.foreign.UnmapperProxy;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import sun.nio.ch.FileChannelImpl;
|
||||
import sun.security.action.GetBooleanAction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.OpenOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* This class contains misc helper functions to support creation of memory segments.
|
||||
*/
|
||||
public final class Utils {
|
||||
|
||||
private static Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
// The maximum alignment supported by malloc - typically 16 on 64-bit platforms.
|
||||
private final static long MAX_ALIGN = 16;
|
||||
|
||||
private static final JavaNioAccess javaNioAccess = SharedSecrets.getJavaNioAccess();
|
||||
|
||||
private static final boolean skipZeroMemory = GetBooleanAction.privilegedGetProperty("jdk.internal.foreign.skipZeroMemory");
|
||||
|
||||
public static long alignUp(long n, long alignment) {
|
||||
return (n + alignment - 1) & -alignment;
|
||||
}
|
||||
|
||||
public static long bitsToBytesOrThrow(long bits, Supplier<RuntimeException> exFactory) {
|
||||
if (bits % 8 == 0) {
|
||||
return bits / 8;
|
||||
} else {
|
||||
throw exFactory.get();
|
||||
}
|
||||
}
|
||||
|
||||
// segment factories
|
||||
|
||||
public static MemorySegment makeNativeSegment(long bytesSize, long alignmentBytes) {
|
||||
long alignedSize = bytesSize;
|
||||
|
||||
if (alignmentBytes > MAX_ALIGN) {
|
||||
alignedSize = bytesSize + (alignmentBytes - 1);
|
||||
}
|
||||
|
||||
long buf = unsafe.allocateMemory(alignedSize);
|
||||
if (!skipZeroMemory) {
|
||||
unsafe.setMemory(buf, alignedSize, (byte)0);
|
||||
}
|
||||
long alignedBuf = Utils.alignUp(buf, alignmentBytes);
|
||||
MemoryScope scope = new MemoryScope(null, () -> unsafe.freeMemory(buf));
|
||||
MemorySegment segment = new MemorySegmentImpl(buf, null, alignedSize, 0, Thread.currentThread(), scope);
|
||||
if (alignedBuf != buf) {
|
||||
long delta = alignedBuf - buf;
|
||||
segment = segment.asSlice(delta, bytesSize);
|
||||
}
|
||||
return segment;
|
||||
}
|
||||
|
||||
public static MemorySegment makeArraySegment(byte[] arr) {
|
||||
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_BYTE_BASE_OFFSET, Unsafe.ARRAY_BYTE_INDEX_SCALE);
|
||||
}
|
||||
|
||||
public static MemorySegment makeArraySegment(char[] arr) {
|
||||
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_CHAR_BASE_OFFSET, Unsafe.ARRAY_CHAR_INDEX_SCALE);
|
||||
}
|
||||
|
||||
public static MemorySegment makeArraySegment(short[] arr) {
|
||||
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_SHORT_BASE_OFFSET, Unsafe.ARRAY_SHORT_INDEX_SCALE);
|
||||
}
|
||||
|
||||
public static MemorySegment makeArraySegment(int[] arr) {
|
||||
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_INT_BASE_OFFSET, Unsafe.ARRAY_INT_INDEX_SCALE);
|
||||
}
|
||||
|
||||
public static MemorySegment makeArraySegment(float[] arr) {
|
||||
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_FLOAT_BASE_OFFSET, Unsafe.ARRAY_FLOAT_INDEX_SCALE);
|
||||
}
|
||||
|
||||
public static MemorySegment makeArraySegment(long[] arr) {
|
||||
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_LONG_BASE_OFFSET, Unsafe.ARRAY_LONG_INDEX_SCALE);
|
||||
}
|
||||
|
||||
public static MemorySegment makeArraySegment(double[] arr) {
|
||||
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
|
||||
}
|
||||
|
||||
private static MemorySegment makeArraySegment(Object arr, int size, int base, int scale) {
|
||||
MemoryScope scope = new MemoryScope(null, null);
|
||||
return new MemorySegmentImpl(base, arr, size * scale, 0, Thread.currentThread(), scope);
|
||||
}
|
||||
|
||||
public static MemorySegment makeBufferSegment(ByteBuffer bb) {
|
||||
long bbAddress = javaNioAccess.getBufferAddress(bb);
|
||||
Object base = javaNioAccess.getBufferBase(bb);
|
||||
|
||||
int pos = bb.position();
|
||||
int limit = bb.limit();
|
||||
|
||||
MemoryScope bufferScope = new MemoryScope(bb, null);
|
||||
return new MemorySegmentImpl(bbAddress + pos, base, limit - pos, 0, Thread.currentThread(), bufferScope);
|
||||
}
|
||||
|
||||
// create and map a file into a fresh segment
|
||||
public static MemorySegment makeMappedSegment(Path path, long bytesSize, FileChannel.MapMode mapMode) throws IOException {
|
||||
if (bytesSize <= 0) throw new IllegalArgumentException("Requested bytes size must be > 0.");
|
||||
try (FileChannelImpl channelImpl = (FileChannelImpl)FileChannel.open(path, openOptions(mapMode))) {
|
||||
UnmapperProxy unmapperProxy = channelImpl.mapInternal(mapMode, 0L, bytesSize);
|
||||
MemoryScope scope = new MemoryScope(null, unmapperProxy::unmap);
|
||||
return new MemorySegmentImpl(unmapperProxy.address(), null, bytesSize, 0, Thread.currentThread(), scope);
|
||||
}
|
||||
}
|
||||
|
||||
private static OpenOption[] openOptions(FileChannel.MapMode mapMode) {
|
||||
if (mapMode == FileChannel.MapMode.READ_ONLY) {
|
||||
return new OpenOption[] { StandardOpenOption.READ };
|
||||
} else if (mapMode == FileChannel.MapMode.READ_WRITE || mapMode == FileChannel.MapMode.PRIVATE) {
|
||||
return new OpenOption[] { StandardOpenOption.READ, StandardOpenOption.WRITE };
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Unsupported map mode: " + mapMode);
|
||||
}
|
||||
}
|
||||
}
|
35
src/jdk.incubator.foreign/share/classes/module-info.java
Normal file
35
src/jdk.incubator.foreign/share/classes/module-info.java
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines the experimental foreign memory access API.
|
||||
*
|
||||
* {@Incubating}
|
||||
*
|
||||
* @moduleGraph
|
||||
*/
|
||||
module jdk.incubator.foreign {
|
||||
exports jdk.incubator.foreign;
|
||||
}
|
@ -40,6 +40,7 @@ tier1_part2 = \
|
||||
tier1_part3 = \
|
||||
:jdk_math \
|
||||
:jdk_svc_sanity \
|
||||
:jdk_foreign \
|
||||
java/nio/Buffer \
|
||||
com/sun/crypto/provider/Cipher \
|
||||
sun/nio/cs/ISO8859x.java
|
||||
@ -332,6 +333,9 @@ jdk_svc = \
|
||||
:jdk_jfr \
|
||||
:svc_tools
|
||||
|
||||
jdk_foreign = \
|
||||
java/foreign
|
||||
|
||||
#############################
|
||||
|
||||
#
|
||||
|
25
test/jdk/java/foreign/TEST.properties
Normal file
25
test/jdk/java/foreign/TEST.properties
Normal file
@ -0,0 +1,25 @@
|
||||
#
|
||||
# Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
#
|
||||
# This code is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License version 2 only, as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
# version 2 for more details (a copy is included in the LICENSE file that
|
||||
# accompanied this code).
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License version
|
||||
# 2 along with this work; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
# or visit www.oracle.com if you need additional information or have any
|
||||
# questions.
|
||||
#
|
||||
#
|
||||
|
||||
modules = jdk.incubator.foreign
|
145
test/jdk/java/foreign/TestArrays.java
Normal file
145
test/jdk/java/foreign/TestArrays.java
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @run testng TestArrays
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemoryLayout.PathElement;
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.SequenceLayout;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.testng.annotations.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class TestArrays {
|
||||
|
||||
static SequenceLayout bytes = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.JAVA_BYTE
|
||||
);
|
||||
|
||||
static SequenceLayout chars = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.JAVA_CHAR
|
||||
);
|
||||
|
||||
static SequenceLayout shorts = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.JAVA_SHORT
|
||||
);
|
||||
|
||||
static SequenceLayout ints = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.JAVA_INT
|
||||
);
|
||||
|
||||
static SequenceLayout floats = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.JAVA_FLOAT
|
||||
);
|
||||
|
||||
static SequenceLayout longs = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.JAVA_LONG
|
||||
);
|
||||
|
||||
static SequenceLayout doubles = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.JAVA_DOUBLE
|
||||
);
|
||||
|
||||
static VarHandle byteHandle = bytes.varHandle(byte.class, PathElement.sequenceElement());
|
||||
static VarHandle charHandle = chars.varHandle(char.class, PathElement.sequenceElement());
|
||||
static VarHandle shortHandle = shorts.varHandle(short.class, PathElement.sequenceElement());
|
||||
static VarHandle intHandle = ints.varHandle(int.class, PathElement.sequenceElement());
|
||||
static VarHandle floatHandle = floats.varHandle(float.class, PathElement.sequenceElement());
|
||||
static VarHandle longHandle = doubles.varHandle(long.class, PathElement.sequenceElement());
|
||||
static VarHandle doubleHandle = longs.varHandle(double.class, PathElement.sequenceElement());
|
||||
|
||||
static void initBytes(MemoryAddress base, SequenceLayout seq, BiConsumer<MemoryAddress, Long> handleSetter) {
|
||||
for (long i = 0; i < seq.elementCount().getAsLong() ; i++) {
|
||||
handleSetter.accept(base, i);
|
||||
}
|
||||
}
|
||||
|
||||
static void checkBytes(MemoryAddress base, SequenceLayout layout) {
|
||||
long nBytes = layout.elementCount().getAsLong() * layout.elementLayout().byteSize();
|
||||
byte[] arr = base.segment().toByteArray();
|
||||
for (long i = 0 ; i < nBytes ; i++) {
|
||||
byte expected = (byte)byteHandle.get(base, i);
|
||||
byte found = arr[(int)i];
|
||||
assertEquals(expected, found);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "arrays")
|
||||
public void testArrays(Consumer<MemoryAddress> init, SequenceLayout layout) {
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(layout)) {
|
||||
init.accept(segment.baseAddress());
|
||||
checkBytes(segment.baseAddress(), layout);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = UnsupportedOperationException.class)
|
||||
public void testTooBigForArray() {
|
||||
MemorySegment.allocateNative((long) Integer.MAX_VALUE * 2).toByteArray();
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalStateException.class)
|
||||
public void testArrayFromClosedSegment() {
|
||||
MemorySegment segment = MemorySegment.allocateNative(8);
|
||||
segment.close();
|
||||
segment.toByteArray();
|
||||
}
|
||||
|
||||
@DataProvider(name = "arrays")
|
||||
public Object[][] nativeAccessOps() {
|
||||
Consumer<MemoryAddress> byteInitializer =
|
||||
(base) -> initBytes(base, bytes, (addr, pos) -> byteHandle.set(addr, pos, (byte)(long)pos));
|
||||
Consumer<MemoryAddress> charInitializer =
|
||||
(base) -> initBytes(base, chars, (addr, pos) -> charHandle.set(addr, pos, (char)(long)pos));
|
||||
Consumer<MemoryAddress> shortInitializer =
|
||||
(base) -> initBytes(base, shorts, (addr, pos) -> shortHandle.set(addr, pos, (short)(long)pos));
|
||||
Consumer<MemoryAddress> intInitializer =
|
||||
(base) -> initBytes(base, ints, (addr, pos) -> intHandle.set(addr, pos, (int)(long)pos));
|
||||
Consumer<MemoryAddress> floatInitializer =
|
||||
(base) -> initBytes(base, floats, (addr, pos) -> floatHandle.set(addr, pos, (float)(long)pos));
|
||||
Consumer<MemoryAddress> longInitializer =
|
||||
(base) -> initBytes(base, longs, (addr, pos) -> longHandle.set(addr, pos, (long)pos));
|
||||
Consumer<MemoryAddress> doubleInitializer =
|
||||
(base) -> initBytes(base, doubles, (addr, pos) -> doubleHandle.set(addr, pos, (double)(long)pos));
|
||||
|
||||
return new Object[][]{
|
||||
{byteInitializer, bytes},
|
||||
{charInitializer, chars},
|
||||
{shortInitializer, shorts},
|
||||
{intInitializer, ints},
|
||||
{floatInitializer, floats},
|
||||
{longInitializer, longs},
|
||||
{doubleInitializer, doubles}
|
||||
};
|
||||
}
|
||||
}
|
555
test/jdk/java/foreign/TestByteBuffer.java
Normal file
555
test/jdk/java/foreign/TestByteBuffer.java
Normal file
@ -0,0 +1,555 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @modules java.base/sun.nio.ch
|
||||
* jdk.incubator.foreign/jdk.internal.foreign
|
||||
* @run testng TestByteBuffer
|
||||
*/
|
||||
|
||||
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.MemoryLayout.PathElement;
|
||||
import jdk.incubator.foreign.SequenceLayout;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.InvalidMarkException;
|
||||
import java.nio.LongBuffer;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.foreign.MemoryAddressImpl;
|
||||
import org.testng.annotations.*;
|
||||
import sun.nio.ch.DirectBuffer;
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class TestByteBuffer {
|
||||
|
||||
static SequenceLayout tuples = MemoryLayout.ofSequence(500,
|
||||
MemoryLayout.ofStruct(
|
||||
MemoryLayouts.BITS_32_BE.withName("index"),
|
||||
MemoryLayouts.BITS_32_BE.withName("value")
|
||||
));
|
||||
|
||||
static SequenceLayout bytes = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.BITS_8_BE
|
||||
);
|
||||
|
||||
static SequenceLayout chars = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.BITS_16_BE
|
||||
);
|
||||
|
||||
static SequenceLayout shorts = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.BITS_16_BE
|
||||
);
|
||||
|
||||
static SequenceLayout ints = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.BITS_32_BE
|
||||
);
|
||||
|
||||
static SequenceLayout floats = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.BITS_32_BE
|
||||
);
|
||||
|
||||
static SequenceLayout longs = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.BITS_64_BE
|
||||
);
|
||||
|
||||
static SequenceLayout doubles = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.BITS_64_BE
|
||||
);
|
||||
|
||||
static VarHandle indexHandle = tuples.varHandle(int.class, PathElement.sequenceElement(), PathElement.groupElement("index"));
|
||||
static VarHandle valueHandle = tuples.varHandle(float.class, PathElement.sequenceElement(), PathElement.groupElement("value"));
|
||||
|
||||
static VarHandle byteHandle = bytes.varHandle(byte.class, PathElement.sequenceElement());
|
||||
static VarHandle charHandle = chars.varHandle(char.class, PathElement.sequenceElement());
|
||||
static VarHandle shortHandle = shorts.varHandle(short.class, PathElement.sequenceElement());
|
||||
static VarHandle intHandle = ints.varHandle(int.class, PathElement.sequenceElement());
|
||||
static VarHandle floatHandle = floats.varHandle(float.class, PathElement.sequenceElement());
|
||||
static VarHandle longHandle = doubles.varHandle(long.class, PathElement.sequenceElement());
|
||||
static VarHandle doubleHandle = longs.varHandle(double.class, PathElement.sequenceElement());
|
||||
|
||||
|
||||
static void initTuples(MemoryAddress base) {
|
||||
for (long i = 0; i < tuples.elementCount().getAsLong() ; i++) {
|
||||
indexHandle.set(base, i, (int)i);
|
||||
valueHandle.set(base, i, (float)(i / 500f));
|
||||
}
|
||||
}
|
||||
|
||||
static void checkTuples(MemoryAddress base, ByteBuffer bb) {
|
||||
for (long i = 0; i < tuples.elementCount().getAsLong() ; i++) {
|
||||
assertEquals(bb.getInt(), (int)indexHandle.get(base, i));
|
||||
assertEquals(bb.getFloat(), (float)valueHandle.get(base, i));
|
||||
}
|
||||
}
|
||||
|
||||
static void initBytes(MemoryAddress base, SequenceLayout seq, BiConsumer<MemoryAddress, Long> handleSetter) {
|
||||
for (long i = 0; i < seq.elementCount().getAsLong() ; i++) {
|
||||
handleSetter.accept(base, i);
|
||||
}
|
||||
}
|
||||
|
||||
static <Z extends Buffer> void checkBytes(MemoryAddress base, SequenceLayout layout,
|
||||
Function<ByteBuffer, Z> bufFactory,
|
||||
BiFunction<MemoryAddress, Long, Object> handleExtractor,
|
||||
Function<Z, Object> bufferExtractor) {
|
||||
long nelems = layout.elementCount().getAsLong();
|
||||
long elemSize = layout.elementLayout().byteSize();
|
||||
for (long i = 0 ; i < nelems ; i++) {
|
||||
long limit = nelems - i;
|
||||
MemorySegment resizedSegment = base.segment().asSlice(i * elemSize, limit * elemSize);
|
||||
ByteBuffer bb = resizedSegment.asByteBuffer();
|
||||
Z z = bufFactory.apply(bb);
|
||||
for (long j = i ; j < limit ; j++) {
|
||||
Object handleValue = handleExtractor.apply(resizedSegment.baseAddress(), j - i);
|
||||
Object bufferValue = bufferExtractor.apply(z);
|
||||
if (handleValue instanceof Number) {
|
||||
assertEquals(((Number)handleValue).longValue(), j);
|
||||
assertEquals(((Number)bufferValue).longValue(), j);
|
||||
} else {
|
||||
assertEquals((long)(char)handleValue, j);
|
||||
assertEquals((long)(char)bufferValue, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOffheap() {
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(tuples)) {
|
||||
MemoryAddress base = segment.baseAddress();
|
||||
initTuples(base);
|
||||
|
||||
ByteBuffer bb = segment.asByteBuffer();
|
||||
checkTuples(base, bb);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeap() {
|
||||
byte[] arr = new byte[(int) tuples.byteSize()];
|
||||
MemorySegment region = MemorySegment.ofArray(arr);
|
||||
MemoryAddress base = region.baseAddress();
|
||||
initTuples(base);
|
||||
|
||||
ByteBuffer bb = region.asByteBuffer();
|
||||
checkTuples(base, bb);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChannel() throws Throwable {
|
||||
File f = new File("test.out");
|
||||
assertTrue(f.createNewFile());
|
||||
f.deleteOnExit();
|
||||
|
||||
//write to channel
|
||||
try (FileChannel channel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
|
||||
withMappedBuffer(channel, FileChannel.MapMode.READ_WRITE, 0, tuples.byteSize(), mbb -> {
|
||||
MemorySegment segment = MemorySegment.ofByteBuffer(mbb);
|
||||
MemoryAddress base = segment.baseAddress();
|
||||
initTuples(base);
|
||||
mbb.force();
|
||||
});
|
||||
}
|
||||
|
||||
//read from channel
|
||||
try (FileChannel channel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) {
|
||||
withMappedBuffer(channel, FileChannel.MapMode.READ_ONLY, 0, tuples.byteSize(), mbb -> {
|
||||
MemorySegment segment = MemorySegment.ofByteBuffer(mbb);
|
||||
MemoryAddress base = segment.baseAddress();
|
||||
checkTuples(base, mbb);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMappedSegment() throws Throwable {
|
||||
File f = new File("test2.out");
|
||||
f.createNewFile();
|
||||
f.deleteOnExit();
|
||||
|
||||
//write to channel
|
||||
try (MemorySegment segment = MemorySegment.mapFromPath(f.toPath(), tuples.byteSize(), FileChannel.MapMode.READ_WRITE)) {
|
||||
MemoryAddress base = segment.baseAddress();
|
||||
initTuples(base);
|
||||
}
|
||||
|
||||
//read from channel
|
||||
try (MemorySegment segment = MemorySegment.mapFromPath(f.toPath(), tuples.byteSize(), FileChannel.MapMode.READ_ONLY)) {
|
||||
MemoryAddress base = segment.baseAddress();
|
||||
checkTuples(base, segment.asByteBuffer());
|
||||
}
|
||||
}
|
||||
|
||||
static void withMappedBuffer(FileChannel channel, FileChannel.MapMode mode, long pos, long size, Consumer<MappedByteBuffer> action) throws Throwable {
|
||||
MappedByteBuffer mbb = channel.map(mode, pos, size);
|
||||
var ref = new WeakReference<>(mbb);
|
||||
action.accept(mbb);
|
||||
mbb = null;
|
||||
//wait for it to be GCed
|
||||
System.gc();
|
||||
while (ref.get() != null) {
|
||||
Thread.sleep(20);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "bufferOps")
|
||||
public void testScopedBuffer(Function<ByteBuffer, Buffer> bufferFactory, Map<Method, Object[]> members) {
|
||||
Buffer bb;
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(bytes)) {
|
||||
MemoryAddress base = segment.baseAddress();
|
||||
bb = bufferFactory.apply(segment.asByteBuffer());
|
||||
}
|
||||
//outside of scope!!
|
||||
for (Map.Entry<Method, Object[]> e : members.entrySet()) {
|
||||
if (!e.getKey().getName().contains("get") &&
|
||||
!e.getKey().getName().contains("put")) {
|
||||
//skip
|
||||
return;
|
||||
}
|
||||
try {
|
||||
e.getKey().invoke(bb, e.getValue());
|
||||
assertTrue(false);
|
||||
} catch (InvocationTargetException ex) {
|
||||
Throwable cause = ex.getCause();
|
||||
if (cause instanceof IllegalStateException) {
|
||||
//all get/set buffer operation should fail because of the scope check
|
||||
assertTrue(ex.getCause().getMessage().contains("not alive"));
|
||||
} else {
|
||||
//all other exceptions were unexpected - fail
|
||||
assertTrue(false);
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
//unexpected exception - fail
|
||||
assertTrue(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "bufferHandleOps")
|
||||
public void testScopedBufferAndVarHandle(VarHandle bufferHandle) {
|
||||
ByteBuffer bb;
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(bytes)) {
|
||||
bb = segment.asByteBuffer();
|
||||
for (Map.Entry<MethodHandle, Object[]> e : varHandleMembers(bb, bufferHandle).entrySet()) {
|
||||
MethodHandle handle = e.getKey().bindTo(bufferHandle)
|
||||
.asSpreader(Object[].class, e.getValue().length);
|
||||
try {
|
||||
handle.invoke(e.getValue());
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
//skip
|
||||
} catch (Throwable ex) {
|
||||
//should not fail - segment is alive!
|
||||
fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Map.Entry<MethodHandle, Object[]> e : varHandleMembers(bb, bufferHandle).entrySet()) {
|
||||
try {
|
||||
MethodHandle handle = e.getKey().bindTo(bufferHandle)
|
||||
.asSpreader(Object[].class, e.getValue().length);
|
||||
handle.invoke(e.getValue());
|
||||
fail();
|
||||
} catch (IllegalStateException ex) {
|
||||
assertTrue(ex.getMessage().contains("not alive"));
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
//skip
|
||||
} catch (Throwable ex) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "bufferOps")
|
||||
public void testDirectBuffer(Function<ByteBuffer, Buffer> bufferFactory, Map<Method, Object[]> members) {
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(bytes)) {
|
||||
MemoryAddress base = segment.baseAddress();
|
||||
Buffer bb = bufferFactory.apply(segment.asByteBuffer());
|
||||
assertTrue(bb.isDirect());
|
||||
DirectBuffer directBuffer = ((DirectBuffer)bb);
|
||||
assertEquals(directBuffer.address(), ((MemoryAddressImpl)base).unsafeGetOffset());
|
||||
assertTrue((directBuffer.attachment() == null) == (bb instanceof ByteBuffer));
|
||||
assertTrue(directBuffer.cleaner() == null);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider="resizeOps")
|
||||
public void testResizeOffheap(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(seq)) {
|
||||
MemoryAddress base = segment.baseAddress();
|
||||
initializer.accept(base);
|
||||
checker.accept(base);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider="resizeOps")
|
||||
public void testResizeHeap(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
|
||||
int capacity = (int)seq.byteSize();
|
||||
MemoryAddress base = MemorySegment.ofArray(new byte[capacity]).baseAddress();
|
||||
initializer.accept(base);
|
||||
checker.accept(base);
|
||||
}
|
||||
|
||||
@Test(dataProvider="resizeOps")
|
||||
public void testResizeBuffer(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
|
||||
int capacity = (int)seq.byteSize();
|
||||
MemoryAddress base = MemorySegment.ofByteBuffer(ByteBuffer.wrap(new byte[capacity])).baseAddress();
|
||||
initializer.accept(base);
|
||||
checker.accept(base);
|
||||
}
|
||||
|
||||
@Test(dataProvider="resizeOps")
|
||||
public void testResizeRoundtripHeap(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
|
||||
int capacity = (int)seq.byteSize();
|
||||
byte[] arr = new byte[capacity];
|
||||
MemorySegment segment = MemorySegment.ofArray(arr);
|
||||
MemoryAddress first = segment.baseAddress();
|
||||
initializer.accept(first);
|
||||
MemoryAddress second = MemorySegment.ofByteBuffer(segment.asByteBuffer()).baseAddress();
|
||||
checker.accept(second);
|
||||
}
|
||||
|
||||
@Test(dataProvider="resizeOps")
|
||||
public void testResizeRoundtripNative(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(seq)) {
|
||||
MemoryAddress first = segment.baseAddress();
|
||||
initializer.accept(first);
|
||||
MemoryAddress second = MemorySegment.ofByteBuffer(segment.asByteBuffer()).baseAddress();
|
||||
checker.accept(second);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalStateException.class)
|
||||
public void testBufferOnClosedScope() {
|
||||
MemorySegment leaked;
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(bytes)) {
|
||||
leaked = segment;
|
||||
}
|
||||
leaked.asByteBuffer();
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = UnsupportedOperationException.class)
|
||||
public void testTooBigForByteBuffer() {
|
||||
MemorySegment.allocateNative((long) Integer.MAX_VALUE * 2).asByteBuffer();
|
||||
}
|
||||
|
||||
@Test(dataProvider="resizeOps")
|
||||
public void testCopyHeapToNative(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
|
||||
int bytes = (int)seq.byteSize();
|
||||
try (MemorySegment nativeArray = MemorySegment.allocateNative(bytes);
|
||||
MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes])) {
|
||||
initializer.accept(heapArray.baseAddress());
|
||||
MemoryAddress.copy(heapArray.baseAddress(), nativeArray.baseAddress(), bytes);
|
||||
checker.accept(nativeArray.baseAddress());
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider="resizeOps")
|
||||
public void testCopyNativeToHeap(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
|
||||
int bytes = (int)seq.byteSize();
|
||||
try (MemorySegment nativeArray = MemorySegment.allocateNative(seq);
|
||||
MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes])) {
|
||||
initializer.accept(nativeArray.baseAddress());
|
||||
MemoryAddress.copy(nativeArray.baseAddress(), heapArray.baseAddress(), bytes);
|
||||
checker.accept(heapArray.baseAddress());
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider(name = "bufferOps")
|
||||
public static Object[][] bufferOps() throws Throwable {
|
||||
return new Object[][]{
|
||||
{ (Function<ByteBuffer, Buffer>) bb -> bb, bufferMembers(ByteBuffer.class)},
|
||||
{ (Function<ByteBuffer, Buffer>) ByteBuffer::asCharBuffer, bufferMembers(CharBuffer.class)},
|
||||
{ (Function<ByteBuffer, Buffer>) ByteBuffer::asShortBuffer, bufferMembers(ShortBuffer.class)},
|
||||
{ (Function<ByteBuffer, Buffer>) ByteBuffer::asIntBuffer, bufferMembers(IntBuffer.class)},
|
||||
{ (Function<ByteBuffer, Buffer>) ByteBuffer::asFloatBuffer, bufferMembers(FloatBuffer.class)},
|
||||
{ (Function<ByteBuffer, Buffer>) ByteBuffer::asLongBuffer, bufferMembers(LongBuffer.class)},
|
||||
{ (Function<ByteBuffer, Buffer>) ByteBuffer::asDoubleBuffer, bufferMembers(DoubleBuffer.class)},
|
||||
};
|
||||
}
|
||||
|
||||
static Map<Method, Object[]> bufferMembers(Class<?> bufferClass) {
|
||||
Map<Method, Object[]> members = new HashMap<>();
|
||||
for (Method m : bufferClass.getMethods()) {
|
||||
//skip statics and method declared in j.l.Object
|
||||
if (m.getDeclaringClass().equals(Object.class) ||
|
||||
(m.getModifiers() & Modifier.STATIC) != 0) continue;
|
||||
Object[] args = Stream.of(m.getParameterTypes())
|
||||
.map(TestByteBuffer::defaultValue)
|
||||
.toArray();
|
||||
members.put(m, args);
|
||||
}
|
||||
return members;
|
||||
}
|
||||
|
||||
@DataProvider(name = "bufferHandleOps")
|
||||
public static Object[][] bufferHandleOps() throws Throwable {
|
||||
return new Object[][]{
|
||||
{ MethodHandles.byteBufferViewVarHandle(char[].class, ByteOrder.nativeOrder()) },
|
||||
{ MethodHandles.byteBufferViewVarHandle(short[].class, ByteOrder.nativeOrder()) },
|
||||
{ MethodHandles.byteBufferViewVarHandle(int[].class, ByteOrder.nativeOrder()) },
|
||||
{ MethodHandles.byteBufferViewVarHandle(long[].class, ByteOrder.nativeOrder()) },
|
||||
{ MethodHandles.byteBufferViewVarHandle(float[].class, ByteOrder.nativeOrder()) },
|
||||
{ MethodHandles.byteBufferViewVarHandle(double[].class, ByteOrder.nativeOrder()) }
|
||||
};
|
||||
}
|
||||
|
||||
static Map<MethodHandle, Object[]> varHandleMembers(ByteBuffer bb, VarHandle handle) {
|
||||
Map<MethodHandle, Object[]> members = new HashMap<>();
|
||||
for (VarHandle.AccessMode mode : VarHandle.AccessMode.values()) {
|
||||
Class<?>[] params = handle.accessModeType(mode).parameterArray();
|
||||
Object[] args = Stream.concat(Stream.of(bb), Stream.of(params).skip(1)
|
||||
.map(TestByteBuffer::defaultValue))
|
||||
.toArray();
|
||||
try {
|
||||
members.put(MethodHandles.varHandleInvoker(mode, handle.accessModeType(mode)), args);
|
||||
} catch (Throwable ex) {
|
||||
throw new AssertionError(ex);
|
||||
}
|
||||
}
|
||||
return members;
|
||||
}
|
||||
|
||||
@DataProvider(name = "resizeOps")
|
||||
public Object[][] resizeOps() {
|
||||
Consumer<MemoryAddress> byteInitializer =
|
||||
(base) -> initBytes(base, bytes, (addr, pos) -> byteHandle.set(addr, pos, (byte)(long)pos));
|
||||
Consumer<MemoryAddress> charInitializer =
|
||||
(base) -> initBytes(base, chars, (addr, pos) -> charHandle.set(addr, pos, (char)(long)pos));
|
||||
Consumer<MemoryAddress> shortInitializer =
|
||||
(base) -> initBytes(base, shorts, (addr, pos) -> shortHandle.set(addr, pos, (short)(long)pos));
|
||||
Consumer<MemoryAddress> intInitializer =
|
||||
(base) -> initBytes(base, ints, (addr, pos) -> intHandle.set(addr, pos, (int)(long)pos));
|
||||
Consumer<MemoryAddress> floatInitializer =
|
||||
(base) -> initBytes(base, floats, (addr, pos) -> floatHandle.set(addr, pos, (float)(long)pos));
|
||||
Consumer<MemoryAddress> longInitializer =
|
||||
(base) -> initBytes(base, longs, (addr, pos) -> longHandle.set(addr, pos, (long)pos));
|
||||
Consumer<MemoryAddress> doubleInitializer =
|
||||
(base) -> initBytes(base, doubles, (addr, pos) -> doubleHandle.set(addr, pos, (double)(long)pos));
|
||||
|
||||
Consumer<MemoryAddress> byteChecker =
|
||||
(base) -> checkBytes(base, bytes, Function.identity(), byteHandle::get, ByteBuffer::get);
|
||||
Consumer<MemoryAddress> charChecker =
|
||||
(base) -> checkBytes(base, chars, ByteBuffer::asCharBuffer, charHandle::get, CharBuffer::get);
|
||||
Consumer<MemoryAddress> shortChecker =
|
||||
(base) -> checkBytes(base, shorts, ByteBuffer::asShortBuffer, shortHandle::get, ShortBuffer::get);
|
||||
Consumer<MemoryAddress> intChecker =
|
||||
(base) -> checkBytes(base, ints, ByteBuffer::asIntBuffer, intHandle::get, IntBuffer::get);
|
||||
Consumer<MemoryAddress> floatChecker =
|
||||
(base) -> checkBytes(base, floats, ByteBuffer::asFloatBuffer, floatHandle::get, FloatBuffer::get);
|
||||
Consumer<MemoryAddress> longChecker =
|
||||
(base) -> checkBytes(base, longs, ByteBuffer::asLongBuffer, longHandle::get, LongBuffer::get);
|
||||
Consumer<MemoryAddress> doubleChecker =
|
||||
(base) -> checkBytes(base, doubles, ByteBuffer::asDoubleBuffer, doubleHandle::get, DoubleBuffer::get);
|
||||
|
||||
return new Object[][]{
|
||||
{byteChecker, byteInitializer, bytes},
|
||||
{charChecker, charInitializer, chars},
|
||||
{shortChecker, shortInitializer, shorts},
|
||||
{intChecker, intInitializer, ints},
|
||||
{floatChecker, floatInitializer, floats},
|
||||
{longChecker, longInitializer, longs},
|
||||
{doubleChecker, doubleInitializer, doubles}
|
||||
};
|
||||
}
|
||||
|
||||
static Object defaultValue(Class<?> c) {
|
||||
if (c.isPrimitive()) {
|
||||
if (c == char.class) {
|
||||
return (char)0;
|
||||
} else if (c == boolean.class) {
|
||||
return false;
|
||||
} else if (c == byte.class) {
|
||||
return (byte)0;
|
||||
} else if (c == short.class) {
|
||||
return (short)0;
|
||||
} else if (c == int.class) {
|
||||
return 0;
|
||||
} else if (c == long.class) {
|
||||
return 0L;
|
||||
} else if (c == float.class) {
|
||||
return 0f;
|
||||
} else if (c == double.class) {
|
||||
return 0d;
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
} else if (c.isArray()) {
|
||||
if (c == char[].class) {
|
||||
return new char[1];
|
||||
} else if (c == boolean[].class) {
|
||||
return new boolean[1];
|
||||
} else if (c == byte[].class) {
|
||||
return new byte[1];
|
||||
} else if (c == short[].class) {
|
||||
return new short[1];
|
||||
} else if (c == int[].class) {
|
||||
return new int[1];
|
||||
} else if (c == long[].class) {
|
||||
return new long[1];
|
||||
} else if (c == float[].class) {
|
||||
return new float[1];
|
||||
} else if (c == double[].class) {
|
||||
return new double[1];
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
96
test/jdk/java/foreign/TestLayoutConstants.java
Normal file
96
test/jdk/java/foreign/TestLayoutConstants.java
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @run testng TestLayoutConstants
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
import org.testng.annotations.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class TestLayoutConstants {
|
||||
|
||||
@Test(dataProvider = "layouts")
|
||||
public void testDescribeResolve(MemoryLayout expected) {
|
||||
try {
|
||||
MemoryLayout actual = expected.describeConstable().get()
|
||||
.resolveConstantDesc(MethodHandles.lookup());
|
||||
assertEquals(actual, expected);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new AssertionError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider(name = "layouts")
|
||||
public Object[][] createLayouts() {
|
||||
return new Object[][] {
|
||||
//padding
|
||||
{ MemoryLayouts.PAD_32 },
|
||||
{ MemoryLayout.ofSequence(MemoryLayouts.PAD_32) },
|
||||
{ MemoryLayout.ofSequence(5, MemoryLayouts.PAD_32) },
|
||||
{ MemoryLayout.ofStruct(MemoryLayouts.PAD_32, MemoryLayouts.PAD_32) },
|
||||
{ MemoryLayout.ofUnion(MemoryLayouts.PAD_32, MemoryLayouts.PAD_32) },
|
||||
//values, big endian
|
||||
{ MemoryLayouts.BITS_32_BE },
|
||||
{ MemoryLayout.ofStruct(
|
||||
MemoryLayouts.BITS_32_BE,
|
||||
MemoryLayouts.BITS_32_BE) },
|
||||
{ MemoryLayout.ofUnion(
|
||||
MemoryLayouts.BITS_32_BE,
|
||||
MemoryLayouts.BITS_32_BE) },
|
||||
//values, little endian
|
||||
{ MemoryLayouts.BITS_32_LE },
|
||||
{ MemoryLayout.ofStruct(
|
||||
MemoryLayouts.BITS_32_LE,
|
||||
MemoryLayouts.BITS_32_LE) },
|
||||
{ MemoryLayout.ofUnion(
|
||||
MemoryLayouts.BITS_32_LE,
|
||||
MemoryLayouts.BITS_32_LE) },
|
||||
//deeply nested
|
||||
{ MemoryLayout.ofStruct(
|
||||
MemoryLayouts.PAD_16,
|
||||
MemoryLayout.ofStruct(
|
||||
MemoryLayouts.PAD_8,
|
||||
MemoryLayouts.BITS_32_BE)) },
|
||||
{ MemoryLayout.ofUnion(
|
||||
MemoryLayouts.PAD_16,
|
||||
MemoryLayout.ofStruct(
|
||||
MemoryLayouts.PAD_8,
|
||||
MemoryLayouts.BITS_32_BE)) },
|
||||
{ MemoryLayout.ofSequence(
|
||||
MemoryLayout.ofStruct(
|
||||
MemoryLayouts.PAD_8,
|
||||
MemoryLayouts.BITS_32_BE)) },
|
||||
{ MemoryLayout.ofSequence(5,
|
||||
MemoryLayout.ofStruct(
|
||||
MemoryLayouts.PAD_8,
|
||||
MemoryLayouts.BITS_32_BE)) },
|
||||
};
|
||||
}
|
||||
}
|
137
test/jdk/java/foreign/TestLayoutPaths.java
Normal file
137
test/jdk/java/foreign/TestLayoutPaths.java
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @run testng TestLayoutPaths
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.GroupLayout;
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemoryLayout.PathElement;
|
||||
import jdk.incubator.foreign.SequenceLayout;
|
||||
|
||||
import org.testng.annotations.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class TestLayoutPaths {
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadSelectFromSeq() {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
|
||||
seq.offset(PathElement.groupElement("foo"));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadSelectFromStruct() {
|
||||
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
|
||||
g.offset(PathElement.sequenceElement());
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadSelectFromValue() {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
|
||||
seq.offset(PathElement.sequenceElement(), PathElement.sequenceElement());
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testUnknownStructField() {
|
||||
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
|
||||
g.offset(PathElement.groupElement("foo"));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class)
|
||||
public void testNullGroupElementName() {
|
||||
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
|
||||
g.offset(PathElement.groupElement(null));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testOutOfBoundsSeqIndex() {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
|
||||
seq.offset(PathElement.sequenceElement(6));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testNegativeSeqIndex() {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
|
||||
seq.offset(PathElement.sequenceElement(-2));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testOutOfBoundsSeqRange() {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
|
||||
seq.offset(PathElement.sequenceElement(6, 2));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testNegativeSeqRange() {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
|
||||
seq.offset(PathElement.sequenceElement(-2, 2));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testIncompleteAccess() {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT));
|
||||
seq.varHandle(int.class, PathElement.sequenceElement());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadContainerAlign() {
|
||||
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT.withBitAlignment(16).withName("foo")).withBitAlignment(8);
|
||||
try {
|
||||
g.offset(PathElement.groupElement("foo"));
|
||||
} catch (Throwable ex) {
|
||||
throw new AssertionError(ex); // should be ok!
|
||||
}
|
||||
try {
|
||||
g.varHandle(int.class, PathElement.groupElement("foo")); //ok
|
||||
assertTrue(false); //should fail!
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
//ok
|
||||
} catch (Throwable ex) {
|
||||
throw new AssertionError(ex); //should fail!
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadAlignOffset() {
|
||||
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.PAD_8, MemoryLayouts.JAVA_INT.withBitAlignment(16).withName("foo"));
|
||||
try {
|
||||
g.offset(PathElement.groupElement("foo"));
|
||||
} catch (Throwable ex) {
|
||||
throw new AssertionError(ex); // should be ok!
|
||||
}
|
||||
try {
|
||||
g.varHandle(int.class, PathElement.groupElement("foo")); //ok
|
||||
assertTrue(false); //should fail!
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
//ok
|
||||
} catch (Throwable ex) {
|
||||
throw new AssertionError(ex); //should fail!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
289
test/jdk/java/foreign/TestLayouts.java
Normal file
289
test/jdk/java/foreign/TestLayouts.java
Normal file
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @run testng TestLayouts
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.function.LongFunction;
|
||||
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import org.testng.annotations.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class TestLayouts {
|
||||
|
||||
@Test(dataProvider = "badLayoutSizes", expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadLayoutSize(SizedLayoutFactory factory, long size) {
|
||||
factory.make(size);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "badAlignments", expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadLayoutAlignment(MemoryLayout layout, long alignment) {
|
||||
layout.withBitAlignment(alignment);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVLAInStruct() {
|
||||
MemoryLayout layout = MemoryLayout.ofStruct(
|
||||
MemoryLayouts.JAVA_INT.withName("size"),
|
||||
MemoryLayout.ofPaddingBits(32),
|
||||
MemoryLayout.ofSequence(MemoryLayouts.JAVA_DOUBLE).withName("arr"));
|
||||
VarHandle size_handle = layout.varHandle(int.class, MemoryLayout.PathElement.groupElement("size"));
|
||||
VarHandle array_elem_handle = layout.varHandle(double.class,
|
||||
MemoryLayout.PathElement.groupElement("arr"),
|
||||
MemoryLayout.PathElement.sequenceElement());
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(8 + 8 * 4)) {
|
||||
size_handle.set(segment.baseAddress(), 4);
|
||||
for (int i = 0 ; i < 4 ; i++) {
|
||||
array_elem_handle.set(segment.baseAddress(), i, (double)i);
|
||||
}
|
||||
//check
|
||||
assertEquals(4, (int)size_handle.get(segment.baseAddress()));
|
||||
for (int i = 0 ; i < 4 ; i++) {
|
||||
assertEquals((double)i, (double)array_elem_handle.get(segment.baseAddress(), i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVLAInSequence() {
|
||||
MemoryLayout layout = MemoryLayout.ofStruct(
|
||||
MemoryLayouts.JAVA_INT.withName("size"),
|
||||
MemoryLayout.ofPaddingBits(32),
|
||||
MemoryLayout.ofSequence(1, MemoryLayout.ofSequence(MemoryLayouts.JAVA_DOUBLE)).withName("arr"));
|
||||
VarHandle size_handle = layout.varHandle(int.class, MemoryLayout.PathElement.groupElement("size"));
|
||||
VarHandle array_elem_handle = layout.varHandle(double.class,
|
||||
MemoryLayout.PathElement.groupElement("arr"),
|
||||
MemoryLayout.PathElement.sequenceElement(0),
|
||||
MemoryLayout.PathElement.sequenceElement());
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(8 + 8 * 4)) {
|
||||
size_handle.set(segment.baseAddress(), 4);
|
||||
for (int i = 0 ; i < 4 ; i++) {
|
||||
array_elem_handle.set(segment.baseAddress(), i, (double)i);
|
||||
}
|
||||
//check
|
||||
assertEquals(4, (int)size_handle.get(segment.baseAddress()));
|
||||
for (int i = 0 ; i < 4 ; i++) {
|
||||
assertEquals((double)i, (double)array_elem_handle.get(segment.baseAddress(), i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexedSequencePath() {
|
||||
MemoryLayout seq = MemoryLayout.ofSequence(10, MemoryLayouts.JAVA_INT);
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(seq)) {
|
||||
VarHandle indexHandle = seq.varHandle(int.class, MemoryLayout.PathElement.sequenceElement());
|
||||
// init segment
|
||||
for (int i = 0 ; i < 10 ; i++) {
|
||||
indexHandle.set(segment.baseAddress(), (long)i, i);
|
||||
}
|
||||
//check statically indexed handles
|
||||
for (int i = 0 ; i < 10 ; i++) {
|
||||
VarHandle preindexHandle = seq.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(i));
|
||||
int expected = (int)indexHandle.get(segment.baseAddress(), (long)i);
|
||||
int found = (int)preindexHandle.get(segment.baseAddress());
|
||||
assertEquals(expected, found);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "unboundLayouts", expectedExceptions = UnsupportedOperationException.class)
|
||||
public void testUnboundSize(MemoryLayout layout, long align) {
|
||||
layout.bitSize();
|
||||
}
|
||||
|
||||
@Test(dataProvider = "unboundLayouts")
|
||||
public void testUnboundAlignment(MemoryLayout layout, long align) {
|
||||
assertEquals(align, layout.bitAlignment());
|
||||
}
|
||||
|
||||
@Test(dataProvider = "unboundLayouts")
|
||||
public void testUnboundEquals(MemoryLayout layout, long align) {
|
||||
assertTrue(layout.equals(layout));
|
||||
}
|
||||
|
||||
@Test(dataProvider = "unboundLayouts")
|
||||
public void testUnboundHash(MemoryLayout layout, long align) {
|
||||
layout.hashCode();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyGroup() {
|
||||
MemoryLayout struct = MemoryLayout.ofStruct();
|
||||
assertEquals(struct.bitSize(), 0);
|
||||
assertEquals(struct.bitAlignment(), 1);
|
||||
|
||||
MemoryLayout union = MemoryLayout.ofUnion();
|
||||
assertEquals(union.bitSize(), 0);
|
||||
assertEquals(union.bitAlignment(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStructSizeAndAlign() {
|
||||
MemoryLayout struct = MemoryLayout.ofStruct(
|
||||
MemoryLayout.ofPaddingBits(8),
|
||||
MemoryLayouts.JAVA_BYTE,
|
||||
MemoryLayouts.JAVA_CHAR,
|
||||
MemoryLayouts.JAVA_INT,
|
||||
MemoryLayouts.JAVA_LONG
|
||||
);
|
||||
assertEquals(struct.byteSize(), 1 + 1 + 2 + 4 + 8);
|
||||
assertEquals(struct.byteAlignment(), 8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnionSizeAndAlign() {
|
||||
MemoryLayout struct = MemoryLayout.ofUnion(
|
||||
MemoryLayouts.JAVA_BYTE,
|
||||
MemoryLayouts.JAVA_CHAR,
|
||||
MemoryLayouts.JAVA_INT,
|
||||
MemoryLayouts.JAVA_LONG
|
||||
);
|
||||
assertEquals(struct.byteSize(), 8);
|
||||
assertEquals(struct.byteAlignment(), 8);
|
||||
}
|
||||
|
||||
@Test(dataProvider="layoutsAndAlignments")
|
||||
public void testAlignmentString(MemoryLayout layout, long bitAlign) {
|
||||
long[] alignments = { 8, 16, 32, 64, 128 };
|
||||
for (long a : alignments) {
|
||||
assertFalse(layout.toString().contains("%"));
|
||||
assertEquals(layout.withBitAlignment(a).toString().contains("%"), a != bitAlign);
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider(name = "badLayoutSizes")
|
||||
public Object[][] factoriesAndSizes() {
|
||||
return new Object[][] {
|
||||
{ SizedLayoutFactory.VALUE_BE, 0 },
|
||||
{ SizedLayoutFactory.VALUE_BE, -1 },
|
||||
{ SizedLayoutFactory.VALUE_LE, 0 },
|
||||
{ SizedLayoutFactory.VALUE_LE, -1 },
|
||||
{ SizedLayoutFactory.PADDING, 0 },
|
||||
{ SizedLayoutFactory.PADDING, -1 },
|
||||
{ SizedLayoutFactory.SEQUENCE, -1 }
|
||||
};
|
||||
}
|
||||
|
||||
@DataProvider(name = "unboundLayouts")
|
||||
public Object[][] unboundLayouts() {
|
||||
return new Object[][] {
|
||||
{ MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT), 32 },
|
||||
{ MemoryLayout.ofSequence(MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT)), 32 },
|
||||
{ MemoryLayout.ofSequence(4, MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT)), 32 },
|
||||
{ MemoryLayout.ofStruct(MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT)), 32 },
|
||||
{ MemoryLayout.ofStruct(MemoryLayout.ofSequence(MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT))), 32 },
|
||||
{ MemoryLayout.ofStruct(MemoryLayout.ofSequence(4, MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT))), 32 },
|
||||
{ MemoryLayout.ofUnion(MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT)), 32 },
|
||||
{ MemoryLayout.ofUnion(MemoryLayout.ofSequence(MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT))), 32 },
|
||||
{ MemoryLayout.ofUnion(MemoryLayout.ofSequence(4, MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT))), 32 },
|
||||
};
|
||||
}
|
||||
|
||||
@DataProvider(name = "badAlignments")
|
||||
public Object[][] layoutsAndBadAlignments() {
|
||||
LayoutKind[] layoutKinds = LayoutKind.values();
|
||||
Object[][] values = new Object[layoutKinds.length * 2][2];
|
||||
for (int i = 0; i < layoutKinds.length ; i++) {
|
||||
values[i * 2] = new Object[] { layoutKinds[i].layout, 3 }; // smaller than 8
|
||||
values[(i * 2) + 1] = new Object[] { layoutKinds[i].layout, 18 }; // not a power of 2
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
enum SizedLayoutFactory {
|
||||
VALUE_LE(size -> MemoryLayout.ofValueBits(size, ByteOrder.LITTLE_ENDIAN)),
|
||||
VALUE_BE(size -> MemoryLayout.ofValueBits(size, ByteOrder.BIG_ENDIAN)),
|
||||
PADDING(MemoryLayout::ofPaddingBits),
|
||||
SEQUENCE(size -> MemoryLayout.ofSequence(size, MemoryLayouts.PAD_8));
|
||||
|
||||
private final LongFunction<MemoryLayout> factory;
|
||||
|
||||
SizedLayoutFactory(LongFunction<MemoryLayout> factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
MemoryLayout make(long size) {
|
||||
return factory.apply(size);
|
||||
}
|
||||
}
|
||||
|
||||
enum LayoutKind {
|
||||
VALUE_LE(MemoryLayouts.BITS_8_LE),
|
||||
VALUE_BE(MemoryLayouts.BITS_8_BE),
|
||||
PADDING(MemoryLayouts.PAD_8),
|
||||
SEQUENCE(MemoryLayout.ofSequence(1, MemoryLayouts.PAD_8)),
|
||||
STRUCT(MemoryLayout.ofStruct(MemoryLayouts.PAD_8, MemoryLayouts.PAD_8)),
|
||||
UNION(MemoryLayout.ofUnion(MemoryLayouts.PAD_8, MemoryLayouts.PAD_8));
|
||||
|
||||
final MemoryLayout layout;
|
||||
|
||||
LayoutKind(MemoryLayout layout) {
|
||||
this.layout = layout;
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider(name = "layoutsAndAlignments")
|
||||
public Object[][] layoutsAndAlignments() {
|
||||
MemoryLayout[] basicLayouts = {
|
||||
MemoryLayouts.JAVA_BYTE,
|
||||
MemoryLayouts.JAVA_CHAR,
|
||||
MemoryLayouts.JAVA_SHORT,
|
||||
MemoryLayouts.JAVA_INT,
|
||||
MemoryLayouts.JAVA_FLOAT,
|
||||
MemoryLayouts.JAVA_LONG,
|
||||
MemoryLayouts.JAVA_DOUBLE,
|
||||
};
|
||||
Object[][] layoutsAndAlignments = new Object[basicLayouts.length * 5][];
|
||||
int i = 0;
|
||||
//add basic layouts
|
||||
for (MemoryLayout l : basicLayouts) {
|
||||
layoutsAndAlignments[i++] = new Object[] { l, l.bitAlignment() };
|
||||
}
|
||||
//add basic layouts wrapped in a sequence with unspecified size
|
||||
for (MemoryLayout l : basicLayouts) {
|
||||
layoutsAndAlignments[i++] = new Object[] { MemoryLayout.ofSequence(l), l.bitAlignment() };
|
||||
}
|
||||
//add basic layouts wrapped in a sequence with given size
|
||||
for (MemoryLayout l : basicLayouts) {
|
||||
layoutsAndAlignments[i++] = new Object[] { MemoryLayout.ofSequence(4, l), l.bitAlignment() };
|
||||
}
|
||||
//add basic layouts wrapped in a struct
|
||||
for (MemoryLayout l : basicLayouts) {
|
||||
layoutsAndAlignments[i++] = new Object[] { MemoryLayout.ofStruct(l), l.bitAlignment() };
|
||||
}
|
||||
//add basic layouts wrapped in a union
|
||||
for (MemoryLayout l : basicLayouts) {
|
||||
layoutsAndAlignments[i++] = new Object[] { MemoryLayout.ofUnion(l), l.bitAlignment() };
|
||||
}
|
||||
return layoutsAndAlignments;
|
||||
}
|
||||
}
|
459
test/jdk/java/foreign/TestMemoryAccess.java
Normal file
459
test/jdk/java/foreign/TestMemoryAccess.java
Normal file
@ -0,0 +1,459 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @run testng/othervm -Xverify:all TestMemoryAccess
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.GroupLayout;
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemoryLayout.PathElement;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.SequenceLayout;
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.testng.annotations.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class TestMemoryAccess {
|
||||
|
||||
@Test(dataProvider = "elements")
|
||||
public void testAccess(Function<MemorySegment, MemorySegment> viewFactory, ValueLayout elemLayout, Class<?> carrier, Checker checker) {
|
||||
ValueLayout layout = elemLayout.withName("elem");
|
||||
testAccessInternal(viewFactory, layout, layout.varHandle(carrier), checker);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "elements")
|
||||
public void testPaddedAccessByName(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, Checker checker) {
|
||||
GroupLayout layout = MemoryLayout.ofStruct(MemoryLayout.ofPaddingBits(elemLayout.bitSize()), elemLayout.withName("elem"));
|
||||
testAccessInternal(viewFactory, layout, layout.varHandle(carrier, PathElement.groupElement("elem")), checker);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "elements")
|
||||
public void testPaddedAccessByIndexSeq(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, Checker checker) {
|
||||
SequenceLayout layout = MemoryLayout.ofSequence(2, elemLayout);
|
||||
testAccessInternal(viewFactory, layout, layout.varHandle(carrier, PathElement.sequenceElement(1)), checker);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "arrayElements")
|
||||
public void testArrayAccess(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, ArrayChecker checker) {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(10, elemLayout.withName("elem"));
|
||||
testArrayAccessInternal(viewFactory, seq, seq.varHandle(carrier, PathElement.sequenceElement()), checker);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "arrayElements")
|
||||
public void testPaddedArrayAccessByName(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, ArrayChecker checker) {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(10, MemoryLayout.ofStruct(MemoryLayout.ofPaddingBits(elemLayout.bitSize()), elemLayout.withName("elem")));
|
||||
testArrayAccessInternal(viewFactory, seq, seq.varHandle(carrier, MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("elem")), checker);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "arrayElements")
|
||||
public void testPaddedArrayAccessByIndexSeq(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, ArrayChecker checker) {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(10, MemoryLayout.ofSequence(2, elemLayout));
|
||||
testArrayAccessInternal(viewFactory, seq, seq.varHandle(carrier, PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement(1)), checker);
|
||||
}
|
||||
|
||||
private void testAccessInternal(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout layout, VarHandle handle, Checker checker) {
|
||||
MemoryAddress outer_address;
|
||||
try (MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(layout))) {
|
||||
MemoryAddress addr = segment.baseAddress();
|
||||
try {
|
||||
checker.check(handle, addr);
|
||||
if (segment.isReadOnly()) {
|
||||
throw new AssertionError(); //not ok, memory should be immutable
|
||||
}
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
if (!segment.isReadOnly()) {
|
||||
throw new AssertionError(); //we should not have failed!
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
checker.check(handle, addr.offset(layout.byteSize()));
|
||||
throw new AssertionError(); //not ok, out of bounds
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
//ok, should fail (out of bounds)
|
||||
}
|
||||
outer_address = addr; //leak!
|
||||
}
|
||||
try {
|
||||
checker.check(handle, outer_address);
|
||||
throw new AssertionError(); //not ok, scope is closed
|
||||
} catch (IllegalStateException ex) {
|
||||
//ok, should fail (scope is closed)
|
||||
}
|
||||
}
|
||||
|
||||
private void testArrayAccessInternal(Function<MemorySegment, MemorySegment> viewFactory, SequenceLayout seq, VarHandle handle, ArrayChecker checker) {
|
||||
MemoryAddress outer_address;
|
||||
try (MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(seq))) {
|
||||
MemoryAddress addr = segment.baseAddress();
|
||||
try {
|
||||
for (int i = 0; i < seq.elementCount().getAsLong(); i++) {
|
||||
checker.check(handle, addr, i);
|
||||
}
|
||||
if (segment.isReadOnly()) {
|
||||
throw new AssertionError(); //not ok, memory should be immutable
|
||||
}
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
if (!segment.isReadOnly()) {
|
||||
throw new AssertionError(); //we should not have failed!
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
checker.check(handle, addr, seq.elementCount().getAsLong());
|
||||
throw new AssertionError(); //not ok, out of bounds
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
//ok, should fail (out of bounds)
|
||||
}
|
||||
outer_address = addr; //leak!
|
||||
}
|
||||
try {
|
||||
checker.check(handle, outer_address, 0);
|
||||
throw new AssertionError(); //not ok, scope is closed
|
||||
} catch (IllegalStateException ex) {
|
||||
//ok, should fail (scope is closed)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "matrixElements")
|
||||
public void testMatrixAccess(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, MatrixChecker checker) {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(20,
|
||||
MemoryLayout.ofSequence(10, elemLayout.withName("elem")));
|
||||
testMatrixAccessInternal(viewFactory, seq, seq.varHandle(carrier,
|
||||
PathElement.sequenceElement(), PathElement.sequenceElement()), checker);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "matrixElements")
|
||||
public void testPaddedMatrixAccessByName(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, MatrixChecker checker) {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(20,
|
||||
MemoryLayout.ofSequence(10, MemoryLayout.ofStruct(MemoryLayout.ofPaddingBits(elemLayout.bitSize()), elemLayout.withName("elem"))));
|
||||
testMatrixAccessInternal(viewFactory, seq,
|
||||
seq.varHandle(carrier,
|
||||
PathElement.sequenceElement(), PathElement.sequenceElement(), PathElement.groupElement("elem")),
|
||||
checker);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "matrixElements")
|
||||
public void testPaddedMatrixAccessByIndexSeq(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout elemLayout, Class<?> carrier, MatrixChecker checker) {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(20,
|
||||
MemoryLayout.ofSequence(10, MemoryLayout.ofSequence(2, elemLayout)));
|
||||
testMatrixAccessInternal(viewFactory, seq,
|
||||
seq.varHandle(carrier,
|
||||
PathElement.sequenceElement(), PathElement.sequenceElement(), PathElement.sequenceElement(1)),
|
||||
checker);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "badCarriers",
|
||||
expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadCarriers(Class<?> carrier) {
|
||||
ValueLayout l = MemoryLayouts.BITS_32_LE.withName("elem");
|
||||
l.varHandle(carrier);
|
||||
}
|
||||
|
||||
private void testMatrixAccessInternal(Function<MemorySegment, MemorySegment> viewFactory, SequenceLayout seq, VarHandle handle, MatrixChecker checker) {
|
||||
MemoryAddress outer_address;
|
||||
try (MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(seq))) {
|
||||
MemoryAddress addr = segment.baseAddress();
|
||||
try {
|
||||
for (int i = 0; i < seq.elementCount().getAsLong(); i++) {
|
||||
for (int j = 0; j < ((SequenceLayout) seq.elementLayout()).elementCount().getAsLong(); j++) {
|
||||
checker.check(handle, addr, i, j);
|
||||
}
|
||||
}
|
||||
if (segment.isReadOnly()) {
|
||||
throw new AssertionError(); //not ok, memory should be immutable
|
||||
}
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
if (!segment.isReadOnly()) {
|
||||
throw new AssertionError(); //we should not have failed!
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
checker.check(handle, addr, seq.elementCount().getAsLong(),
|
||||
((SequenceLayout)seq.elementLayout()).elementCount().getAsLong());
|
||||
throw new AssertionError(); //not ok, out of bounds
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
//ok, should fail (out of bounds)
|
||||
}
|
||||
outer_address = addr; //leak!
|
||||
}
|
||||
try {
|
||||
checker.check(handle, outer_address, 0, 0);
|
||||
throw new AssertionError(); //not ok, scope is closed
|
||||
} catch (IllegalStateException ex) {
|
||||
//ok, should fail (scope is closed)
|
||||
}
|
||||
}
|
||||
|
||||
static Function<MemorySegment, MemorySegment> ID = Function.identity();
|
||||
static Function<MemorySegment, MemorySegment> IMMUTABLE = MemorySegment::asReadOnly;
|
||||
|
||||
@DataProvider(name = "elements")
|
||||
public Object[][] createData() {
|
||||
return new Object[][] {
|
||||
//BE, RW
|
||||
{ ID, MemoryLayouts.BITS_8_BE, byte.class, Checker.BYTE },
|
||||
{ ID, MemoryLayouts.BITS_16_BE, short.class, Checker.SHORT },
|
||||
{ ID, MemoryLayouts.BITS_16_BE, char.class, Checker.CHAR },
|
||||
{ ID, MemoryLayouts.BITS_32_BE, int.class, Checker.INT },
|
||||
{ ID, MemoryLayouts.BITS_64_BE, long.class, Checker.LONG },
|
||||
{ ID, MemoryLayouts.BITS_32_BE, float.class, Checker.FLOAT },
|
||||
{ ID, MemoryLayouts.BITS_64_BE, double.class, Checker.DOUBLE },
|
||||
//BE, RO
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_8_BE, byte.class, Checker.BYTE },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_BE, short.class, Checker.SHORT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_BE, char.class, Checker.CHAR },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_BE, int.class, Checker.INT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_BE, long.class, Checker.LONG },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_BE, float.class, Checker.FLOAT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_BE, double.class, Checker.DOUBLE },
|
||||
//LE, RW
|
||||
{ ID, MemoryLayouts.BITS_8_LE, byte.class, Checker.BYTE },
|
||||
{ ID, MemoryLayouts.BITS_16_LE, short.class, Checker.SHORT },
|
||||
{ ID, MemoryLayouts.BITS_16_LE, char.class, Checker.CHAR },
|
||||
{ ID, MemoryLayouts.BITS_32_LE, int.class, Checker.INT },
|
||||
{ ID, MemoryLayouts.BITS_64_LE, long.class, Checker.LONG },
|
||||
{ ID, MemoryLayouts.BITS_32_LE, float.class, Checker.FLOAT },
|
||||
{ ID, MemoryLayouts.BITS_64_LE, double.class, Checker.DOUBLE },
|
||||
//LE, RO
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_8_LE, byte.class, Checker.BYTE },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_LE, short.class, Checker.SHORT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_LE, char.class, Checker.CHAR },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_LE, int.class, Checker.INT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_LE, long.class, Checker.LONG },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_LE, float.class, Checker.FLOAT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_LE, double.class, Checker.DOUBLE },
|
||||
};
|
||||
}
|
||||
|
||||
interface Checker {
|
||||
void check(VarHandle handle, MemoryAddress addr);
|
||||
|
||||
Checker BYTE = (handle, addr) -> {
|
||||
handle.set(addr, (byte)42);
|
||||
assertEquals(42, (byte)handle.get(addr));
|
||||
};
|
||||
|
||||
Checker SHORT = (handle, addr) -> {
|
||||
handle.set(addr, (short)42);
|
||||
assertEquals(42, (short)handle.get(addr));
|
||||
};
|
||||
|
||||
Checker CHAR = (handle, addr) -> {
|
||||
handle.set(addr, (char)42);
|
||||
assertEquals(42, (char)handle.get(addr));
|
||||
};
|
||||
|
||||
Checker INT = (handle, addr) -> {
|
||||
handle.set(addr, 42);
|
||||
assertEquals(42, (int)handle.get(addr));
|
||||
};
|
||||
|
||||
Checker LONG = (handle, addr) -> {
|
||||
handle.set(addr, (long)42);
|
||||
assertEquals(42, (long)handle.get(addr));
|
||||
};
|
||||
|
||||
Checker FLOAT = (handle, addr) -> {
|
||||
handle.set(addr, (float)42);
|
||||
assertEquals((float)42, (float)handle.get(addr));
|
||||
};
|
||||
|
||||
Checker DOUBLE = (handle, addr) -> {
|
||||
handle.set(addr, (double)42);
|
||||
assertEquals((double)42, (double)handle.get(addr));
|
||||
};
|
||||
}
|
||||
|
||||
@DataProvider(name = "arrayElements")
|
||||
public Object[][] createArrayData() {
|
||||
return new Object[][] {
|
||||
//BE, RW
|
||||
{ ID, MemoryLayouts.BITS_8_BE, byte.class, ArrayChecker.BYTE },
|
||||
{ ID, MemoryLayouts.BITS_16_BE, short.class, ArrayChecker.SHORT },
|
||||
{ ID, MemoryLayouts.BITS_16_BE, char.class, ArrayChecker.CHAR },
|
||||
{ ID, MemoryLayouts.BITS_32_BE, int.class, ArrayChecker.INT },
|
||||
{ ID, MemoryLayouts.BITS_64_BE, long.class, ArrayChecker.LONG },
|
||||
{ ID, MemoryLayouts.BITS_32_BE, float.class, ArrayChecker.FLOAT },
|
||||
{ ID, MemoryLayouts.BITS_64_BE, double.class, ArrayChecker.DOUBLE },
|
||||
//BE, RO
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_8_BE, byte.class, ArrayChecker.BYTE },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_BE, short.class, ArrayChecker.SHORT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_BE, char.class, ArrayChecker.CHAR },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_BE, int.class, ArrayChecker.INT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_BE, long.class, ArrayChecker.LONG },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_BE, float.class, ArrayChecker.FLOAT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_BE, double.class, ArrayChecker.DOUBLE },
|
||||
//LE, RW
|
||||
{ ID, MemoryLayouts.BITS_8_LE, byte.class, ArrayChecker.BYTE },
|
||||
{ ID, MemoryLayouts.BITS_16_LE, short.class, ArrayChecker.SHORT },
|
||||
{ ID, MemoryLayouts.BITS_16_LE, char.class, ArrayChecker.CHAR },
|
||||
{ ID, MemoryLayouts.BITS_32_LE, int.class, ArrayChecker.INT },
|
||||
{ ID, MemoryLayouts.BITS_64_LE, long.class, ArrayChecker.LONG },
|
||||
{ ID, MemoryLayouts.BITS_32_LE, float.class, ArrayChecker.FLOAT },
|
||||
{ ID, MemoryLayouts.BITS_64_LE, double.class, ArrayChecker.DOUBLE },
|
||||
//LE, RO
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_8_LE, byte.class, ArrayChecker.BYTE },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_LE, short.class, ArrayChecker.SHORT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_LE, char.class, ArrayChecker.CHAR },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_LE, int.class, ArrayChecker.INT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_LE, long.class, ArrayChecker.LONG },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_LE, float.class, ArrayChecker.FLOAT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_LE, double.class, ArrayChecker.DOUBLE },
|
||||
};
|
||||
}
|
||||
|
||||
interface ArrayChecker {
|
||||
void check(VarHandle handle, MemoryAddress addr, long index);
|
||||
|
||||
ArrayChecker BYTE = (handle, addr, i) -> {
|
||||
handle.set(addr, i, (byte)i);
|
||||
assertEquals(i, (byte)handle.get(addr, i));
|
||||
};
|
||||
|
||||
ArrayChecker SHORT = (handle, addr, i) -> {
|
||||
handle.set(addr, i, (short)i);
|
||||
assertEquals(i, (short)handle.get(addr, i));
|
||||
};
|
||||
|
||||
ArrayChecker CHAR = (handle, addr, i) -> {
|
||||
handle.set(addr, i, (char)i);
|
||||
assertEquals(i, (char)handle.get(addr, i));
|
||||
};
|
||||
|
||||
ArrayChecker INT = (handle, addr, i) -> {
|
||||
handle.set(addr, i, (int)i);
|
||||
assertEquals(i, (int)handle.get(addr, i));
|
||||
};
|
||||
|
||||
ArrayChecker LONG = (handle, addr, i) -> {
|
||||
handle.set(addr, i, (long)i);
|
||||
assertEquals(i, (long)handle.get(addr, i));
|
||||
};
|
||||
|
||||
ArrayChecker FLOAT = (handle, addr, i) -> {
|
||||
handle.set(addr, i, (float)i);
|
||||
assertEquals((float)i, (float)handle.get(addr, i));
|
||||
};
|
||||
|
||||
ArrayChecker DOUBLE = (handle, addr, i) -> {
|
||||
handle.set(addr, i, (double)i);
|
||||
assertEquals((double)i, (double)handle.get(addr, i));
|
||||
};
|
||||
}
|
||||
|
||||
@DataProvider(name = "matrixElements")
|
||||
public Object[][] createMatrixData() {
|
||||
return new Object[][] {
|
||||
//BE, RW
|
||||
{ ID, MemoryLayouts.BITS_8_BE, byte.class, MatrixChecker.BYTE },
|
||||
{ ID, MemoryLayouts.BITS_16_BE, short.class, MatrixChecker.SHORT },
|
||||
{ ID, MemoryLayouts.BITS_16_BE, char.class, MatrixChecker.CHAR },
|
||||
{ ID, MemoryLayouts.BITS_32_BE, int.class, MatrixChecker.INT },
|
||||
{ ID, MemoryLayouts.BITS_64_BE, long.class, MatrixChecker.LONG },
|
||||
{ ID, MemoryLayouts.BITS_32_BE, float.class, MatrixChecker.FLOAT },
|
||||
{ ID, MemoryLayouts.BITS_64_BE, double.class, MatrixChecker.DOUBLE },
|
||||
//BE, RO
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_8_BE, byte.class, MatrixChecker.BYTE },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_BE, short.class, MatrixChecker.SHORT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_BE, char.class, MatrixChecker.CHAR },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_BE, int.class, MatrixChecker.INT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_BE, long.class, MatrixChecker.LONG },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_BE, float.class, MatrixChecker.FLOAT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_BE, double.class, MatrixChecker.DOUBLE },
|
||||
//LE, RW
|
||||
{ ID, MemoryLayouts.BITS_8_LE, byte.class, MatrixChecker.BYTE },
|
||||
{ ID, MemoryLayouts.BITS_16_LE, short.class, MatrixChecker.SHORT },
|
||||
{ ID, MemoryLayouts.BITS_16_LE, char.class, MatrixChecker.CHAR },
|
||||
{ ID, MemoryLayouts.BITS_32_LE, int.class, MatrixChecker.INT },
|
||||
{ ID, MemoryLayouts.BITS_64_LE, long.class, MatrixChecker.LONG },
|
||||
{ ID, MemoryLayouts.BITS_32_LE, float.class, MatrixChecker.FLOAT },
|
||||
{ ID, MemoryLayouts.BITS_64_LE, double.class, MatrixChecker.DOUBLE },
|
||||
//LE, RO
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_8_LE, byte.class, MatrixChecker.BYTE },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_LE, short.class, MatrixChecker.SHORT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_16_LE, char.class, MatrixChecker.CHAR },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_LE, int.class, MatrixChecker.INT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_LE, long.class, MatrixChecker.LONG },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_32_LE, float.class, MatrixChecker.FLOAT },
|
||||
{ IMMUTABLE, MemoryLayouts.BITS_64_LE, double.class, MatrixChecker.DOUBLE },
|
||||
};
|
||||
}
|
||||
|
||||
interface MatrixChecker {
|
||||
void check(VarHandle handle, MemoryAddress addr, long row, long col);
|
||||
|
||||
MatrixChecker BYTE = (handle, addr, r, c) -> {
|
||||
handle.set(addr, r, c, (byte)(r + c));
|
||||
assertEquals(r + c, (byte)handle.get(addr, r, c));
|
||||
};
|
||||
|
||||
MatrixChecker SHORT = (handle, addr, r, c) -> {
|
||||
handle.set(addr, r, c, (short)(r + c));
|
||||
assertEquals(r + c, (short)handle.get(addr, r, c));
|
||||
};
|
||||
|
||||
MatrixChecker CHAR = (handle, addr, r, c) -> {
|
||||
handle.set(addr, r, c, (char)(r + c));
|
||||
assertEquals(r + c, (char)handle.get(addr, r, c));
|
||||
};
|
||||
|
||||
MatrixChecker INT = (handle, addr, r, c) -> {
|
||||
handle.set(addr, r, c, (int)(r + c));
|
||||
assertEquals(r + c, (int)handle.get(addr, r, c));
|
||||
};
|
||||
|
||||
MatrixChecker LONG = (handle, addr, r, c) -> {
|
||||
handle.set(addr, r, c, r + c);
|
||||
assertEquals(r + c, (long)handle.get(addr, r, c));
|
||||
};
|
||||
|
||||
MatrixChecker FLOAT = (handle, addr, r, c) -> {
|
||||
handle.set(addr, r, c, (float)(r + c));
|
||||
assertEquals((float)(r + c), (float)handle.get(addr, r, c));
|
||||
};
|
||||
|
||||
MatrixChecker DOUBLE = (handle, addr, r, c) -> {
|
||||
handle.set(addr, r, c, (double)(r + c));
|
||||
assertEquals((double)(r + c), (double)handle.get(addr, r, c));
|
||||
};
|
||||
}
|
||||
|
||||
@DataProvider(name = "badCarriers")
|
||||
public Object[][] createBadCarriers() {
|
||||
return new Object[][] {
|
||||
{ void.class },
|
||||
{ boolean.class },
|
||||
{ Object.class },
|
||||
{ int[].class }
|
||||
};
|
||||
}
|
||||
}
|
137
test/jdk/java/foreign/TestMemoryAlignment.java
Normal file
137
test/jdk/java/foreign/TestMemoryAlignment.java
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @run testng TestMemoryAlignment
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
|
||||
import jdk.incubator.foreign.GroupLayout;
|
||||
import jdk.incubator.foreign.MemoryLayout.PathElement;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.SequenceLayout;
|
||||
import jdk.incubator.foreign.ValueLayout;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.stream.LongStream;
|
||||
|
||||
import org.testng.annotations.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class TestMemoryAlignment {
|
||||
|
||||
@Test(dataProvider = "alignments")
|
||||
public void testAlignedAccess(long align) {
|
||||
ValueLayout layout = MemoryLayouts.BITS_32_BE;
|
||||
assertEquals(layout.bitAlignment(), 32);
|
||||
ValueLayout aligned = layout.withBitAlignment(align);
|
||||
assertEquals(aligned.bitAlignment(), align); //unreasonable alignment here, to make sure access throws
|
||||
VarHandle vh = aligned.varHandle(int.class);
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(aligned)) {
|
||||
MemoryAddress addr = segment.baseAddress();
|
||||
vh.set(addr, -42);
|
||||
int val = (int)vh.get(addr);
|
||||
assertEquals(val, -42);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "alignments")
|
||||
public void testUnalignedAccess(long align) {
|
||||
ValueLayout layout = MemoryLayouts.BITS_32_BE;
|
||||
assertEquals(layout.bitAlignment(), 32);
|
||||
ValueLayout aligned = layout.withBitAlignment(align);
|
||||
MemoryLayout alignedGroup = MemoryLayout.ofStruct(MemoryLayouts.PAD_8, aligned);
|
||||
assertEquals(alignedGroup.bitAlignment(), align);
|
||||
VarHandle vh = aligned.varHandle(int.class);
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(alignedGroup)) {
|
||||
MemoryAddress addr = segment.baseAddress();
|
||||
vh.set(addr.offset(1L), -42);
|
||||
assertEquals(align, 8); //this is the only case where access is aligned
|
||||
} catch (IllegalStateException ex) {
|
||||
assertNotEquals(align, 8); //if align != 8, access is always unaligned
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "alignments")
|
||||
public void testUnalignedPath(long align) {
|
||||
MemoryLayout layout = MemoryLayouts.BITS_32_BE;
|
||||
MemoryLayout aligned = layout.withBitAlignment(align).withName("value");
|
||||
GroupLayout alignedGroup = MemoryLayout.ofStruct(MemoryLayouts.PAD_8, aligned);
|
||||
try {
|
||||
alignedGroup.varHandle(int.class, PathElement.groupElement("value"));
|
||||
assertEquals(align, 8); //this is the only case where path is aligned
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
assertNotEquals(align, 8); //if align != 8, path is always unaligned
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "alignments")
|
||||
public void testUnalignedSequence(long align) {
|
||||
SequenceLayout layout = MemoryLayout.ofSequence(5, MemoryLayouts.BITS_32_BE.withBitAlignment(align));
|
||||
try {
|
||||
VarHandle vh = layout.varHandle(int.class, PathElement.sequenceElement());
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(layout)) {
|
||||
MemoryAddress addr = segment.baseAddress();
|
||||
for (long i = 0 ; i < 5 ; i++) {
|
||||
vh.set(addr, i, -42);
|
||||
}
|
||||
}
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
assertTrue(align > 32); //if align > 32, access is always unaligned (for some elements)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPackedAccess() {
|
||||
ValueLayout vChar = MemoryLayouts.BITS_8_BE;
|
||||
ValueLayout vShort = MemoryLayouts.BITS_16_BE;
|
||||
ValueLayout vInt = MemoryLayouts.BITS_32_BE;
|
||||
//mimic pragma pack(1)
|
||||
GroupLayout g = MemoryLayout.ofStruct(vChar.withBitAlignment(8).withName("a"),
|
||||
vShort.withBitAlignment(8).withName("b"),
|
||||
vInt.withBitAlignment(8).withName("c"));
|
||||
assertEquals(g.bitAlignment(), 8);
|
||||
VarHandle vh_c = g.varHandle(byte.class, PathElement.groupElement("a"));
|
||||
VarHandle vh_s = g.varHandle(short.class, PathElement.groupElement("b"));
|
||||
VarHandle vh_i = g.varHandle(int.class, PathElement.groupElement("c"));
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(g)) {
|
||||
MemoryAddress addr = segment.baseAddress();
|
||||
vh_c.set(addr, Byte.MIN_VALUE);
|
||||
assertEquals(vh_c.get(addr), Byte.MIN_VALUE);
|
||||
vh_s.set(addr, Short.MIN_VALUE);
|
||||
assertEquals(vh_s.get(addr), Short.MIN_VALUE);
|
||||
vh_i.set(addr, Integer.MIN_VALUE);
|
||||
assertEquals(vh_i.get(addr), Integer.MIN_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider(name = "alignments")
|
||||
public Object[][] createAlignments() {
|
||||
return LongStream.range(3, 32)
|
||||
.mapToObj(v -> new Object[] { 1L << v })
|
||||
.toArray(Object[][]::new);
|
||||
}
|
||||
}
|
127
test/jdk/java/foreign/TestMemoryCopy.java
Normal file
127
test/jdk/java/foreign/TestMemoryCopy.java
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @run testng TestMemoryCopy
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryHandles;
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class TestMemoryCopy {
|
||||
|
||||
final static VarHandle BYTE_HANDLE = MemoryLayouts.JAVA_BYTE.varHandle(byte.class);
|
||||
|
||||
@Test(dataProvider = "slices")
|
||||
public void testCopy(SegmentSlice s1, SegmentSlice s2) {
|
||||
MemoryAddress addr1 = s1.segment.baseAddress();
|
||||
MemoryAddress addr2 = s2.segment.baseAddress();
|
||||
int size = Math.min(s1.size(), s2.size());
|
||||
//prepare source and target segments
|
||||
for (int i = 0 ; i < size ; i++) {
|
||||
BYTE_HANDLE.set(addr2.offset(i), (byte)0);
|
||||
}
|
||||
for (int i = 0 ; i < size ; i++) {
|
||||
BYTE_HANDLE.set(addr1.offset(i), (byte) i);
|
||||
}
|
||||
//perform copy
|
||||
MemoryAddress.copy(addr1, addr2, size);
|
||||
//check that copy actually worked
|
||||
for (int i = 0 ; i < size ; i++) {
|
||||
assertEquals((byte)i, BYTE_HANDLE.get(addr2.offset(i)));
|
||||
}
|
||||
}
|
||||
|
||||
static class SegmentSlice {
|
||||
|
||||
enum Kind {
|
||||
NATIVE(MemorySegment::allocateNative),
|
||||
ARRAY(i -> MemorySegment.ofArray(new byte[i]));
|
||||
|
||||
final IntFunction<MemorySegment> segmentFactory;
|
||||
|
||||
Kind(IntFunction<MemorySegment> segmentFactory) {
|
||||
this.segmentFactory = segmentFactory;
|
||||
}
|
||||
|
||||
MemorySegment makeSegment(int elems) {
|
||||
return segmentFactory.apply(elems);
|
||||
}
|
||||
}
|
||||
|
||||
final Kind kind;
|
||||
final int first;
|
||||
final int last;
|
||||
final MemorySegment segment;
|
||||
|
||||
public SegmentSlice(Kind kind, int first, int last, MemorySegment segment) {
|
||||
this.kind = kind;
|
||||
this.first = first;
|
||||
this.last = last;
|
||||
this.segment = segment;
|
||||
}
|
||||
|
||||
int size() {
|
||||
return last - first + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider(name = "slices")
|
||||
static Object[][] slices() {
|
||||
int[] sizes = { 16, 8, 4, 2, 1 };
|
||||
List<SegmentSlice> slices = new ArrayList<>();
|
||||
for (SegmentSlice.Kind kind : SegmentSlice.Kind.values()) {
|
||||
MemorySegment segment = kind.makeSegment(16);
|
||||
//compute all slices
|
||||
for (int size : sizes) {
|
||||
for (int index = 0 ; index < 16 ; index += size) {
|
||||
MemorySegment slice = segment.asSlice(index, size);
|
||||
slices.add(new SegmentSlice(kind, index, index + size - 1, slice));
|
||||
}
|
||||
}
|
||||
}
|
||||
Object[][] sliceArray = new Object[slices.size() * slices.size()][];
|
||||
for (int i = 0 ; i < slices.size() ; i++) {
|
||||
for (int j = 0 ; j < slices.size() ; j++) {
|
||||
sliceArray[i * slices.size() + j] = new Object[] { slices.get(i), slices.get(j) };
|
||||
}
|
||||
}
|
||||
return sliceArray;
|
||||
}
|
||||
}
|
232
test/jdk/java/foreign/TestNative.java
Normal file
232
test/jdk/java/foreign/TestNative.java
Normal file
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* jdk.incubator.foreign/jdk.incubator.foreign.unsafe
|
||||
* @run testng TestNative
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemoryLayout.PathElement;
|
||||
import jdk.incubator.foreign.unsafe.ForeignUnsafe;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import org.testng.annotations.*;
|
||||
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.SequenceLayout;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.LongBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class TestNative {
|
||||
|
||||
static Unsafe UNSAFE;
|
||||
|
||||
static {
|
||||
UNSAFE = Unsafe.getUnsafe();
|
||||
}
|
||||
|
||||
static SequenceLayout bytes = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.JAVA_BYTE.withOrder(ByteOrder.nativeOrder())
|
||||
);
|
||||
|
||||
static SequenceLayout chars = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.JAVA_CHAR.withOrder(ByteOrder.nativeOrder())
|
||||
);
|
||||
|
||||
static SequenceLayout shorts = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.JAVA_SHORT.withOrder(ByteOrder.nativeOrder())
|
||||
);
|
||||
|
||||
static SequenceLayout ints = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.JAVA_INT.withOrder(ByteOrder.nativeOrder())
|
||||
);
|
||||
|
||||
static SequenceLayout floats = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.JAVA_FLOAT.withOrder(ByteOrder.nativeOrder())
|
||||
);
|
||||
|
||||
static SequenceLayout longs = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.JAVA_LONG.withOrder(ByteOrder.nativeOrder())
|
||||
);
|
||||
|
||||
static SequenceLayout doubles = MemoryLayout.ofSequence(100,
|
||||
MemoryLayouts.JAVA_DOUBLE.withOrder(ByteOrder.nativeOrder())
|
||||
);
|
||||
|
||||
static VarHandle byteHandle = bytes.varHandle(byte.class, PathElement.sequenceElement());
|
||||
static VarHandle charHandle = chars.varHandle(char.class, PathElement.sequenceElement());
|
||||
static VarHandle shortHandle = shorts.varHandle(short.class, PathElement.sequenceElement());
|
||||
static VarHandle intHandle = ints.varHandle(int.class, PathElement.sequenceElement());
|
||||
static VarHandle floatHandle = floats.varHandle(float.class, PathElement.sequenceElement());
|
||||
static VarHandle longHandle = doubles.varHandle(long.class, PathElement.sequenceElement());
|
||||
static VarHandle doubleHandle = longs.varHandle(double.class, PathElement.sequenceElement());
|
||||
|
||||
static void initBytes(MemoryAddress base, SequenceLayout seq, BiConsumer<MemoryAddress, Long> handleSetter) {
|
||||
for (long i = 0; i < seq.elementCount().getAsLong() ; i++) {
|
||||
handleSetter.accept(base, i);
|
||||
}
|
||||
}
|
||||
|
||||
static <Z extends Buffer> void checkBytes(MemoryAddress base, SequenceLayout layout,
|
||||
BiFunction<MemoryAddress, Long, Object> handleExtractor,
|
||||
Function<ByteBuffer, Z> bufferFactory,
|
||||
BiFunction<Z, Integer, Object> nativeBufferExtractor,
|
||||
BiFunction<Long, Integer, Object> nativeRawExtractor) {
|
||||
long nelems = layout.elementCount().getAsLong();
|
||||
ByteBuffer bb = base.segment().asSlice(base.offset(), (int)layout.byteSize()).asByteBuffer();
|
||||
Z z = bufferFactory.apply(bb);
|
||||
for (long i = 0 ; i < nelems ; i++) {
|
||||
Object handleValue = handleExtractor.apply(base, i);
|
||||
Object bufferValue = nativeBufferExtractor.apply(z, (int)i);
|
||||
Object rawValue = nativeRawExtractor.apply(ForeignUnsafe.getUnsafeOffset(base), (int)i);
|
||||
if (handleValue instanceof Number) {
|
||||
assertEquals(((Number)handleValue).longValue(), i);
|
||||
assertEquals(((Number)bufferValue).longValue(), i);
|
||||
assertEquals(((Number)rawValue).longValue(), i);
|
||||
} else {
|
||||
assertEquals((long)(char)handleValue, i);
|
||||
assertEquals((long)(char)bufferValue, i);
|
||||
assertEquals((long)(char)rawValue, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static native byte getByteBuffer(ByteBuffer buf, int index);
|
||||
public static native char getCharBuffer(CharBuffer buf, int index);
|
||||
public static native short getShortBuffer(ShortBuffer buf, int index);
|
||||
public static native int getIntBuffer(IntBuffer buf, int index);
|
||||
public static native float getFloatBuffer(FloatBuffer buf, int index);
|
||||
public static native long getLongBuffer(LongBuffer buf, int index);
|
||||
public static native double getDoubleBuffer(DoubleBuffer buf, int index);
|
||||
|
||||
public static native byte getByteRaw(long addr, int index);
|
||||
public static native char getCharRaw(long addr, int index);
|
||||
public static native short getShortRaw(long addr, int index);
|
||||
public static native int getIntRaw(long addr, int index);
|
||||
public static native float getFloatRaw(long addr, int index);
|
||||
public static native long getLongRaw(long addr, int index);
|
||||
public static native double getDoubleRaw(long addr, int index);
|
||||
|
||||
public static native long getCapacity(Buffer buffer);
|
||||
|
||||
@Test(dataProvider="nativeAccessOps")
|
||||
public void testNativeAccess(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(seq)) {
|
||||
MemoryAddress address = segment.baseAddress();
|
||||
initializer.accept(address);
|
||||
checker.accept(address);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider="buffers")
|
||||
public void testNativeCapacity(Function<ByteBuffer, Buffer> bufferFunction, int elemSize) {
|
||||
int capacity = (int)doubles.byteSize();
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(doubles)) {
|
||||
ByteBuffer bb = segment.asByteBuffer();
|
||||
Buffer buf = bufferFunction.apply(bb);
|
||||
int expected = capacity / elemSize;
|
||||
assertEquals(buf.capacity(), expected);
|
||||
assertEquals(getCapacity(buf), expected);
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
System.loadLibrary("NativeAccess");
|
||||
}
|
||||
|
||||
@DataProvider(name = "nativeAccessOps")
|
||||
public Object[][] nativeAccessOps() {
|
||||
Consumer<MemoryAddress> byteInitializer =
|
||||
(base) -> initBytes(base, bytes, (addr, pos) -> byteHandle.set(addr, pos, (byte)(long)pos));
|
||||
Consumer<MemoryAddress> charInitializer =
|
||||
(base) -> initBytes(base, chars, (addr, pos) -> charHandle.set(addr, pos, (char)(long)pos));
|
||||
Consumer<MemoryAddress> shortInitializer =
|
||||
(base) -> initBytes(base, shorts, (addr, pos) -> shortHandle.set(addr, pos, (short)(long)pos));
|
||||
Consumer<MemoryAddress> intInitializer =
|
||||
(base) -> initBytes(base, ints, (addr, pos) -> intHandle.set(addr, pos, (int)(long)pos));
|
||||
Consumer<MemoryAddress> floatInitializer =
|
||||
(base) -> initBytes(base, floats, (addr, pos) -> floatHandle.set(addr, pos, (float)(long)pos));
|
||||
Consumer<MemoryAddress> longInitializer =
|
||||
(base) -> initBytes(base, longs, (addr, pos) -> longHandle.set(addr, pos, (long)pos));
|
||||
Consumer<MemoryAddress> doubleInitializer =
|
||||
(base) -> initBytes(base, doubles, (addr, pos) -> doubleHandle.set(addr, pos, (double)(long)pos));
|
||||
|
||||
Consumer<MemoryAddress> byteChecker =
|
||||
(base) -> checkBytes(base, bytes, byteHandle::get, bb -> bb, TestNative::getByteBuffer, TestNative::getByteRaw);
|
||||
Consumer<MemoryAddress> charChecker =
|
||||
(base) -> checkBytes(base, chars, charHandle::get, ByteBuffer::asCharBuffer, TestNative::getCharBuffer, TestNative::getCharRaw);
|
||||
Consumer<MemoryAddress> shortChecker =
|
||||
(base) -> checkBytes(base, shorts, shortHandle::get, ByteBuffer::asShortBuffer, TestNative::getShortBuffer, TestNative::getShortRaw);
|
||||
Consumer<MemoryAddress> intChecker =
|
||||
(base) -> checkBytes(base, ints, intHandle::get, ByteBuffer::asIntBuffer, TestNative::getIntBuffer, TestNative::getIntRaw);
|
||||
Consumer<MemoryAddress> floatChecker =
|
||||
(base) -> checkBytes(base, floats, floatHandle::get, ByteBuffer::asFloatBuffer, TestNative::getFloatBuffer, TestNative::getFloatRaw);
|
||||
Consumer<MemoryAddress> longChecker =
|
||||
(base) -> checkBytes(base, longs, longHandle::get, ByteBuffer::asLongBuffer, TestNative::getLongBuffer, TestNative::getLongRaw);
|
||||
Consumer<MemoryAddress> doubleChecker =
|
||||
(base) -> checkBytes(base, doubles, doubleHandle::get, ByteBuffer::asDoubleBuffer, TestNative::getDoubleBuffer, TestNative::getDoubleRaw);
|
||||
|
||||
return new Object[][]{
|
||||
{byteChecker, byteInitializer, bytes},
|
||||
{charChecker, charInitializer, chars},
|
||||
{shortChecker, shortInitializer, shorts},
|
||||
{intChecker, intInitializer, ints},
|
||||
{floatChecker, floatInitializer, floats},
|
||||
{longChecker, longInitializer, longs},
|
||||
{doubleChecker, doubleInitializer, doubles}
|
||||
};
|
||||
}
|
||||
|
||||
@DataProvider(name = "buffers")
|
||||
public Object[][] buffers() {
|
||||
return new Object[][] {
|
||||
{ (Function<ByteBuffer, Buffer>)bb -> bb, 1 },
|
||||
{ (Function<ByteBuffer, Buffer>)ByteBuffer::asCharBuffer, 2 },
|
||||
{ (Function<ByteBuffer, Buffer>)ByteBuffer::asShortBuffer, 2 },
|
||||
{ (Function<ByteBuffer, Buffer>)ByteBuffer::asIntBuffer, 4 },
|
||||
{ (Function<ByteBuffer, Buffer>)ByteBuffer::asFloatBuffer, 4 },
|
||||
{ (Function<ByteBuffer, Buffer>)ByteBuffer::asLongBuffer, 8 },
|
||||
{ (Function<ByteBuffer, Buffer>)ByteBuffer::asDoubleBuffer, 8 },
|
||||
};
|
||||
}
|
||||
}
|
196
test/jdk/java/foreign/TestSegments.java
Normal file
196
test/jdk/java/foreign/TestSegments.java
Normal file
@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @run testng TestSegments
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
|
||||
import java.awt.font.LayoutPath;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.LongFunction;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.incubator.foreign.SequenceLayout;
|
||||
import org.testng.annotations.*;
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class TestSegments {
|
||||
|
||||
@Test(dataProvider = "badSizeAndAlignments", expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadAllocateAlign(long size, long align) {
|
||||
MemorySegment.allocateNative(size, align);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "badLayouts", expectedExceptions = UnsupportedOperationException.class)
|
||||
public void testBadAllocateLayout(MemoryLayout layout) {
|
||||
MemorySegment.allocateNative(layout);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = OutOfMemoryError.class)
|
||||
public void testAllocateTooBig() {
|
||||
MemorySegment.allocateNative(Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "segmentOperations")
|
||||
public void testOpOutsideConfinement(SegmentMember member) throws Throwable {
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(4)) {
|
||||
AtomicBoolean failed = new AtomicBoolean(false);
|
||||
Thread t = new Thread(() -> {
|
||||
try {
|
||||
Object o = member.method.invoke(segment, member.params);
|
||||
if (member.method.getName().equals("acquire")) {
|
||||
((MemorySegment)o).close();
|
||||
}
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
});
|
||||
t.setUncaughtExceptionHandler((thread, ex) -> failed.set(true));
|
||||
t.start();
|
||||
t.join();
|
||||
assertEquals(failed.get(), member.isConfined());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNativeSegmentIsZeroed() {
|
||||
VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE)
|
||||
.varHandle(byte.class, MemoryLayout.PathElement.sequenceElement());
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(1000)) {
|
||||
for (long i = 0 ; i < segment.byteSize() ; i++) {
|
||||
assertEquals(0, (byte)byteHandle.get(segment.baseAddress(), i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider(name = "badSizeAndAlignments")
|
||||
public Object[][] sizesAndAlignments() {
|
||||
return new Object[][] {
|
||||
{ -1, 8 },
|
||||
{ 1, 15 },
|
||||
{ 1, -15 }
|
||||
};
|
||||
}
|
||||
|
||||
@DataProvider(name = "badLayouts")
|
||||
public Object[][] layouts() {
|
||||
SizedLayoutFactory[] layoutFactories = SizedLayoutFactory.values();
|
||||
Object[][] values = new Object[layoutFactories.length * 2][2];
|
||||
for (int i = 0; i < layoutFactories.length ; i++) {
|
||||
values[i * 2] = new Object[] { MemoryLayout.ofStruct(layoutFactories[i].make(7), MemoryLayout.ofPaddingBits(9)) }; // good size, bad align
|
||||
values[(i * 2) + 1] = new Object[] { layoutFactories[i].make(15).withBitAlignment(16) }; // bad size, good align
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
enum SizedLayoutFactory {
|
||||
VALUE_BE(size -> MemoryLayout.ofValueBits(size, ByteOrder.BIG_ENDIAN)),
|
||||
VALUE_LE(size -> MemoryLayout.ofValueBits(size, ByteOrder.LITTLE_ENDIAN)),
|
||||
PADDING(MemoryLayout::ofPaddingBits);
|
||||
|
||||
private final LongFunction<MemoryLayout> factory;
|
||||
|
||||
SizedLayoutFactory(LongFunction<MemoryLayout> factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
MemoryLayout make(long size) {
|
||||
return factory.apply(size);
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider(name = "segmentOperations")
|
||||
static Object[][] segmentMembers() {
|
||||
List<SegmentMember> members = new ArrayList<>();
|
||||
for (Method m : MemorySegment.class.getDeclaredMethods()) {
|
||||
//skip statics and method declared in j.l.Object
|
||||
if (m.getDeclaringClass().equals(Object.class) ||
|
||||
(m.getModifiers() & Modifier.STATIC) != 0) continue;
|
||||
Object[] args = Stream.of(m.getParameterTypes())
|
||||
.map(TestSegments::defaultValue)
|
||||
.toArray();
|
||||
members.add(new SegmentMember(m, args));
|
||||
}
|
||||
return members.stream().map(ms -> new Object[] { ms }).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
static class SegmentMember {
|
||||
final Method method;
|
||||
final Object[] params;
|
||||
|
||||
public SegmentMember(Method method, Object[] params) {
|
||||
this.method = method;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
boolean isConfined() {
|
||||
return method.getName().startsWith("as") ||
|
||||
method.getName().startsWith("to") ||
|
||||
method.getName().equals("close") ||
|
||||
method.getName().equals("slice");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return method.getName();
|
||||
}
|
||||
}
|
||||
|
||||
static Object defaultValue(Class<?> c) {
|
||||
if (c.isPrimitive()) {
|
||||
if (c == char.class) {
|
||||
return (char)0;
|
||||
} else if (c == boolean.class) {
|
||||
return false;
|
||||
} else if (c == byte.class) {
|
||||
return (byte)0;
|
||||
} else if (c == short.class) {
|
||||
return (short)0;
|
||||
} else if (c == int.class) {
|
||||
return 0;
|
||||
} else if (c == long.class) {
|
||||
return 0L;
|
||||
} else if (c == float.class) {
|
||||
return 0f;
|
||||
} else if (c == double.class) {
|
||||
return 0d;
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
82
test/jdk/java/foreign/TestSharedAccess.java
Normal file
82
test/jdk/java/foreign/TestSharedAccess.java
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @run testng TestSharedAccess
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import org.testng.annotations.*;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class TestSharedAccess {
|
||||
|
||||
static final VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
|
||||
|
||||
@Test
|
||||
public void testShared() throws Throwable {
|
||||
try (MemorySegment s = MemorySegment.allocateNative(4)) {
|
||||
setInt(s, 42);
|
||||
assertEquals(getInt(s), 42);
|
||||
List<Thread> threads = new ArrayList<>();
|
||||
for (int i = 0 ; i < 1000 ; i++) {
|
||||
threads.add(new Thread(() -> {
|
||||
try (MemorySegment local = s.acquire()) {
|
||||
assertEquals(getInt(local), 42);
|
||||
}
|
||||
}));
|
||||
}
|
||||
threads.forEach(Thread::start);
|
||||
threads.forEach(t -> {
|
||||
try {
|
||||
t.join();
|
||||
} catch (Throwable e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expectedExceptions=IllegalStateException.class)
|
||||
public void testBadCloseWithPendingAcquire() {
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(8)) {
|
||||
segment.acquire();
|
||||
} //should fail here!
|
||||
}
|
||||
|
||||
static int getInt(MemorySegment handle) {
|
||||
return (int)intHandle.getVolatile(handle.baseAddress());
|
||||
}
|
||||
|
||||
static void setInt(MemorySegment handle, int value) {
|
||||
intHandle.setVolatile(handle.baseAddress(), value);
|
||||
}
|
||||
}
|
97
test/jdk/java/foreign/TestSlices.java
Normal file
97
test/jdk/java/foreign/TestSlices.java
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.MemoryHandles;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
|
||||
import org.testng.annotations.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @run testng/othervm -Xverify:all TestSlices
|
||||
*/
|
||||
public class TestSlices {
|
||||
|
||||
static MemoryLayout LAYOUT = MemoryLayout.ofSequence(2,
|
||||
MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT));
|
||||
|
||||
static VarHandle VH_ALL = LAYOUT.varHandle(int.class,
|
||||
MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement());
|
||||
|
||||
static VarHandle VH_INT = MemoryLayouts.JAVA_INT.varHandle(int.class);
|
||||
|
||||
@Test(dataProvider = "slices")
|
||||
public void testSlices(VarHandle handle, int lo, int hi, int[] values) {
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(LAYOUT)) {
|
||||
//init
|
||||
for (long i = 0 ; i < 2 ; i++) {
|
||||
for (long j = 0 ; j < 5 ; j++) {
|
||||
VH_ALL.set(segment.baseAddress(), i, j, (int)j + 1 + ((int)i * 5));
|
||||
}
|
||||
}
|
||||
|
||||
checkSlice(segment, handle, lo, hi, values);
|
||||
}
|
||||
}
|
||||
|
||||
static void checkSlice(MemorySegment segment, VarHandle handle, long i_max, long j_max, int... values) {
|
||||
int index = 0;
|
||||
for (long i = 0 ; i < i_max ; i++) {
|
||||
for (long j = 0 ; j < j_max ; j++) {
|
||||
int x = (int) handle.get(segment.baseAddress(), i, j);
|
||||
assertEquals(x, values[index++]);
|
||||
}
|
||||
}
|
||||
assertEquals(index, values.length);
|
||||
}
|
||||
|
||||
@DataProvider(name = "slices")
|
||||
static Object[][] slices() {
|
||||
return new Object[][] {
|
||||
// x
|
||||
{ VH_ALL, 2, 5, new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } },
|
||||
// x[0::2]
|
||||
{ LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(),
|
||||
MemoryLayout.PathElement.sequenceElement(0, 2)), 2, 3, new int[] { 1, 3, 5, 6, 8, 10 } },
|
||||
{ MemoryHandles.withStride(MemoryHandles.withStride(VH_INT, 8), 20), 2, 3, new int[] { 1, 3, 5, 6, 8, 10 } },
|
||||
// x[1::2]
|
||||
{ LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(),
|
||||
MemoryLayout.PathElement.sequenceElement(1, 2)), 2, 2, new int[] { 2, 4, 7, 9 } },
|
||||
{ MemoryHandles.withOffset(MemoryHandles.withStride(MemoryHandles.withStride(VH_INT, 8), 20), 4), 2, 2, new int[] { 2, 4, 7, 9 } },
|
||||
// x[4::-2]
|
||||
{ LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(),
|
||||
MemoryLayout.PathElement.sequenceElement(4, -2)), 2, 3, new int[] { 5, 3, 1, 10, 8, 6 } },
|
||||
{ MemoryHandles.withOffset(MemoryHandles.withStride(MemoryHandles.withStride(VH_INT, -8), 20), 16), 2, 3, new int[] { 5, 3, 1, 10, 8, 6 } },
|
||||
// x[3::-2]
|
||||
{ LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(),
|
||||
MemoryLayout.PathElement.sequenceElement(3, -2)), 2, 2, new int[] { 4, 2, 9, 7 } },
|
||||
{ MemoryHandles.withOffset(MemoryHandles.withStride(MemoryHandles.withStride(VH_INT, -8), 20), 12), 2, 2, new int[] { 4, 2, 9, 7 } },
|
||||
};
|
||||
}
|
||||
}
|
189
test/jdk/java/foreign/TestVarHandleCombinators.java
Normal file
189
test/jdk/java/foreign/TestVarHandleCombinators.java
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @run testng TestVarHandleCombinators
|
||||
*/
|
||||
|
||||
import jdk.incubator.foreign.MemoryHandles;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
public class TestVarHandleCombinators {
|
||||
|
||||
@Test
|
||||
public void testElementAccess() {
|
||||
VarHandle vh = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder());
|
||||
vh = MemoryHandles.withStride(vh, 1);
|
||||
|
||||
byte[] arr = { 0, 0, -1, 0 };
|
||||
MemorySegment segment = MemorySegment.ofArray(arr);
|
||||
MemoryAddress addr = segment.baseAddress();
|
||||
|
||||
assertEquals((byte) vh.get(addr, 2), (byte) -1);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testUnalignedElement() {
|
||||
VarHandle vh = MemoryHandles.varHandle(byte.class, 4, ByteOrder.nativeOrder());
|
||||
MemoryHandles.withStride(vh, 2);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadStrideElement() {
|
||||
VarHandle vh = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
|
||||
MemoryHandles.withStride(vh, 0); //scale factor cant be zero
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testAlignNotPowerOf2() {
|
||||
VarHandle vh = MemoryHandles.varHandle(byte.class, 3, ByteOrder.nativeOrder());
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testAlignNegative() {
|
||||
VarHandle vh = MemoryHandles.varHandle(byte.class, -1, ByteOrder.nativeOrder());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlign() {
|
||||
VarHandle vh = MemoryHandles.varHandle(byte.class, 2, ByteOrder.nativeOrder());
|
||||
|
||||
MemorySegment segment = MemorySegment.allocateNative(1, 2);
|
||||
MemoryAddress address = segment.baseAddress();
|
||||
|
||||
vh.set(address, (byte) 10); // fine, memory region is aligned
|
||||
assertEquals((byte) vh.get(address), (byte) 10);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testAlignBadAccess() {
|
||||
VarHandle vh = MemoryHandles.varHandle(byte.class, 2, ByteOrder.nativeOrder());
|
||||
vh = MemoryHandles.withOffset(vh, 1); // offset by 1 byte
|
||||
|
||||
MemorySegment segment = MemorySegment.allocateNative(2, 2);
|
||||
MemoryAddress address = segment.baseAddress();
|
||||
|
||||
vh.set(address, (byte) 10); // should be bad align
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testOffsetNegative() {
|
||||
VarHandle vh = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder());
|
||||
MemoryHandles.withOffset(vh, -1);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testUnalignedOffset() {
|
||||
VarHandle vh = MemoryHandles.varHandle(byte.class, 4, ByteOrder.nativeOrder());
|
||||
MemoryHandles.withOffset(vh, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOffset() {
|
||||
VarHandle vh = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder());
|
||||
vh = MemoryHandles.withOffset(vh, 1);
|
||||
|
||||
MemorySegment segment = MemorySegment.ofArray(new byte[2]);
|
||||
MemoryAddress address = segment.baseAddress();
|
||||
|
||||
vh.set(address, (byte) 10);
|
||||
assertEquals((byte) vh.get(address), (byte) 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testByteOrderLE() {
|
||||
VarHandle vh = MemoryHandles.varHandle(short.class, 2, ByteOrder.LITTLE_ENDIAN);
|
||||
byte[] arr = new byte[2];
|
||||
MemorySegment segment = MemorySegment.ofArray(arr);
|
||||
MemoryAddress address = segment.baseAddress();
|
||||
|
||||
vh.set(address, (short) 0xFF);
|
||||
assertEquals(arr[0], (byte) 0xFF);
|
||||
assertEquals(arr[1], (byte) 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testByteOrderBE() {
|
||||
VarHandle vh = MemoryHandles.varHandle(short.class, 2, ByteOrder.BIG_ENDIAN);
|
||||
byte[] arr = new byte[2];
|
||||
MemorySegment segment = MemorySegment.ofArray(arr);
|
||||
MemoryAddress address = segment.baseAddress();
|
||||
|
||||
vh.set(address, (short) 0xFF);
|
||||
assertEquals(arr[0], (byte) 0);
|
||||
assertEquals(arr[1], (byte) 0xFF);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedSequenceAccess() {
|
||||
int outer_size = 10;
|
||||
int inner_size = 5;
|
||||
|
||||
//[10 : [5 : [x32 i32]]]
|
||||
|
||||
VarHandle vh = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
|
||||
vh = MemoryHandles.withOffset(vh, 4);
|
||||
VarHandle inner_vh = MemoryHandles.withStride(vh, 8);
|
||||
VarHandle outer_vh = MemoryHandles.withStride(inner_vh, 5 * 8);
|
||||
int count = 0;
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(inner_size * outer_size * 8)) {
|
||||
for (long i = 0; i < outer_size; i++) {
|
||||
for (long j = 0; j < inner_size; j++) {
|
||||
outer_vh.set(segment.baseAddress(), i, j, count);
|
||||
assertEquals(
|
||||
(int)inner_vh.get(segment.baseAddress().offset(i * inner_size * 8), j),
|
||||
count);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "badCarriers", expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadCarrier(Class<?> carrier) {
|
||||
MemoryHandles.varHandle(carrier, ByteOrder.nativeOrder());
|
||||
}
|
||||
|
||||
@DataProvider(name = "badCarriers")
|
||||
public Object[][] createBadCarriers() {
|
||||
return new Object[][] {
|
||||
{ void.class },
|
||||
{ boolean.class },
|
||||
{ Object.class },
|
||||
{ int[].class },
|
||||
{ MemoryAddress.class }
|
||||
};
|
||||
}
|
||||
|
||||
}
|
115
test/jdk/java/foreign/libNativeAccess.c
Normal file
115
test/jdk/java/foreign/libNativeAccess.c
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "jni.h"
|
||||
#include <stdio.h>
|
||||
|
||||
JNIEXPORT jbyte JNICALL
|
||||
Java_TestNative_getByteRaw(JNIEnv *env, jclass cls, jlong addr, jint index) {
|
||||
jbyte *arr = (jbyte*)addr;
|
||||
return arr[index];
|
||||
}
|
||||
|
||||
JNIEXPORT jbyte JNICALL
|
||||
Java_TestNative_getByteBuffer(JNIEnv *env, jclass cls, jobject buf, jint index) {
|
||||
jlong addr = (jlong)(*env)->GetDirectBufferAddress(env, buf);
|
||||
return Java_TestNative_getByteRaw(env, cls, addr, index);
|
||||
}
|
||||
|
||||
JNIEXPORT jchar JNICALL
|
||||
Java_TestNative_getCharRaw(JNIEnv *env, jclass cls, jlong addr, jint index) {
|
||||
jchar *arr = (jchar*)addr;
|
||||
return arr[index];
|
||||
}
|
||||
|
||||
JNIEXPORT jchar JNICALL
|
||||
Java_TestNative_getCharBuffer(JNIEnv *env, jclass cls, jobject buf, jint index) {
|
||||
jlong addr = (jlong)(*env)->GetDirectBufferAddress(env, buf);
|
||||
return Java_TestNative_getCharRaw(env, cls, addr, index);
|
||||
}
|
||||
|
||||
JNIEXPORT jshort JNICALL
|
||||
Java_TestNative_getShortRaw(JNIEnv *env, jclass cls, jlong addr, jint index) {
|
||||
jshort *arr = (jshort*)addr;
|
||||
return arr[index];
|
||||
}
|
||||
|
||||
JNIEXPORT jshort JNICALL
|
||||
Java_TestNative_getShortBuffer(JNIEnv *env, jclass cls, jobject buf, jint index) {
|
||||
jlong addr = (jlong)(*env)->GetDirectBufferAddress(env, buf);
|
||||
return Java_TestNative_getShortRaw(env, cls, addr, index);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_TestNative_getIntRaw(JNIEnv *env, jclass cls, jlong addr, jint index) {
|
||||
jint *arr = (jint*)addr;
|
||||
return arr[index];
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_TestNative_getIntBuffer(JNIEnv *env, jclass cls, jobject buf, jint index) {
|
||||
jlong addr = (jlong)(*env)->GetDirectBufferAddress(env, buf);
|
||||
return Java_TestNative_getIntRaw(env, cls, addr, index);
|
||||
}
|
||||
|
||||
JNIEXPORT jfloat JNICALL
|
||||
Java_TestNative_getFloatRaw(JNIEnv *env, jclass cls, jlong addr, jint index) {
|
||||
jfloat *arr = (jfloat*)addr;
|
||||
return arr[index];
|
||||
}
|
||||
|
||||
JNIEXPORT jfloat JNICALL
|
||||
Java_TestNative_getFloatBuffer(JNIEnv *env, jclass cls, jobject buf, jint index) {
|
||||
jlong addr = (jlong)(*env)->GetDirectBufferAddress(env, buf);
|
||||
return Java_TestNative_getFloatRaw(env, cls, addr, index);
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_TestNative_getLongRaw(JNIEnv *env, jclass cls, jlong addr, jint index) {
|
||||
jlong *arr = (jlong*)addr;
|
||||
return arr[index];
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_TestNative_getLongBuffer(JNIEnv *env, jclass cls, jobject buf, jint index) {
|
||||
jlong addr = (jlong)(*env)->GetDirectBufferAddress(env, buf);
|
||||
return Java_TestNative_getLongRaw(env, cls, addr, index);
|
||||
}
|
||||
|
||||
JNIEXPORT jdouble JNICALL
|
||||
Java_TestNative_getDoubleRaw(JNIEnv *env, jclass cls, jlong addr, jint index) {
|
||||
jdouble *arr = (jdouble*)addr;
|
||||
return arr[index];
|
||||
}
|
||||
|
||||
JNIEXPORT jdouble JNICALL
|
||||
Java_TestNative_getDoubleBuffer(JNIEnv *env, jclass cls, jobject buf, jint index) {
|
||||
jlong addr = (jlong)(*env)->GetDirectBufferAddress(env, buf);
|
||||
return Java_TestNative_getDoubleRaw(env, cls, addr, index);
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_TestNative_getCapacity(JNIEnv *env, jclass cls, jobject buf) {
|
||||
return (*env)->GetDirectBufferCapacity(env, buf);
|
||||
}
|
Loading…
Reference in New Issue
Block a user